static int SceneDrag()

in layer1/Scene.cpp [4832:5660]


static int SceneDrag(Block * block, int x, int y, int mod, double when)
{
  PyMOLGlobals *G = block->m_G;
  CScene *I = G->Scene;
  float scale, vScale;
  float v1[3], v2[3], n1[3], n2[3], r1, r2, cp[3], v3[3];
  float dx, dy, dt;
  float axis[3], axis2[3], theta, omega;
  float old_front, old_back, old_origin;
  int mode;
  int eff_width;
  int moved_flag;
  int adjust_flag;
  int drag_handled = false;
  int virtual_trackball;
  CObject *obj;

  if(I->PossibleSingleClick) {
    double slowest_single_click_drag = 0.15;
    if((when - I->LastClickTime) > slowest_single_click_drag) {
      I->PossibleSingleClick = 0;
    }
  }

  if(I->LoopFlag) {
    return SceneLoopDrag(block, x, y, mod);
  }
  if(I->ButtonsShown && I->PressMode) {
    if(I->ButtonsValid) {
      SceneElem *elem = I->SceneVLA;
      int i;
      drag_handled = true;
      I->Over = -1;
      for(i = 0; i < I->NScene; i++) {
        if(elem->drawn &&
           (x >= elem->x1) && (y >= elem->y1) && (x < elem->x2) && (y < elem->y2)) {
          I->Over = i;
          OrthoDirty(G);
          break;
        }
        elem++;
      }
      switch (I->PressMode) {
      case 2:
        if(I->Over >= 0) {
          if(I->Pressed != I->Over) {
            const char *cur_name = SettingGetGlobal_s(G, cSetting_scene_current_name);
            if(cur_name && elem->name && (strcmp(cur_name, elem->name))) {
              OrthoLineType buffer;
              int animate = -1;
              if(mod & cOrthoCTRL)
                animate = 0;
              sprintf(buffer, "cmd.scene('''%s''',animate=%d)", elem->name, animate);
              PParse(G, buffer);
              PFlush(G);
              PLog(G, buffer, cPLog_pym);
            }
            I->Pressed = I->Over;
          }
        } else {
          I->Pressed = -1;
        }
      case 3:
        if((I->Over >= 0) && (I->Pressed != I->Over))
          I->PressMode = 4;     /* activate dragging */
        break;
      }

      if(I->PressMode == 4) {   /* dragging */
        if((I->Over >= 0) && (I->Pressed != I->Over) && (I->Pressed >= 0)) {

          SceneElem *pressed = I->SceneVLA + I->Pressed;
          OrthoLineType buffer;

          if(I->Over > 0) {     /* not over the first scene in list */
            SceneElem *first = elem - 1;
            SceneElem *second = pressed;
            if(first >= pressed) {
              first = elem;
              second = pressed;
            }
            sprintf(buffer, "cmd.scene_order('''%s %s''')", first->name, second->name);
          } else {
            sprintf(buffer, "cmd.scene_order('''%s''',location='top')", pressed->name);
          }
          PParse(G, buffer);
          PFlush(G);
          PLog(G, buffer, cPLog_pym);
          I->Pressed = I->Over;
          I->ButtonsValid = false;
	  if(SettingGetGlobal_b(G, cSetting_scene_buttons)){
	    OrthoInvalidateDoDraw(G);
	  }
        }
      }
    }
  }

  if(!drag_handled) {

    mode = ButModeTranslate(G, I->Button, mod);

    y = y - I->margin.bottom;

    scale = (float) I->Height;
    if(scale > I->Width)
      scale = (float) I->Width;
    scale = 0.45F * scale;
    SceneInvalidateCopy(G, false);
    SceneDontCopyNext(G);
    switch (mode) {
    case cButModePickAtom:
      obj = (CObject *) I->LastPicked.context.object;
      if(obj)
        switch (obj->type) {
        case cObjectGadget:
          {
            ObjectGadget *gad;
            gad = (ObjectGadget *) obj;

            ObjectGadgetGetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond,
                                  v1);

            vScale = SceneGetExactScreenVertexScale(G, v1);
            if(stereo_via_adjacent_array(I->StereoMode)) {
              x = get_stereo_x(x, &I->LastX, I->Width, NULL);
            }

            /* transform into model coodinate space */
            switch (obj->Context) {
            case 1:
              {
                float divisor;
                divisor = (float) I->Width;
                if(I->Height < I->Width)
                  divisor = (float) I->Height;
                v2[0] = (x - I->LastX) / divisor;
                v2[1] = (y - I->LastY) / divisor;
                v2[2] = 0;
              }
              break;
            default:
            case 0:
              v2[0] = (x - I->LastX) * vScale;
              v2[1] = (y - I->LastY) * vScale;
              v2[2] = 0;
              MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2);
              break;
            }
            add3f(v1, v2, v2);
            ObjectGadgetSetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond,
                                  v2);
            if (I->LastPicked.src.index){ // pick id on gadget is 1 for band (to change values), 
                                          // 0 for everything else (to move it around)
              SceneChanged(G);   // changing values, need to update gadget text
            } else {
              PyMOL_NeedRedisplay(G->PyMOL);    // moving gadget, just re-draw
            }
          }
          break;
        }
      I->LastX = x;
      I->LastY = y;
      break;
    case cButModeRotDrag:
      eff_width = I->Width;
      if(stereo_via_adjacent_array(I->StereoMode)) {
        eff_width = I->Width / 2;
        x = get_stereo_x(x, &I->LastX, I->Width, NULL);
      }

      virtual_trackball = SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball);

      if (virtual_trackball==2 &&
	  (!I->prev_no_z_rotation1 || !I->prev_no_z_rotation2)) {
	/* when virtual_trackball=2 and twisting, need to set v1,v2 relative to orig */
        v2[0] = (float) (eff_width / 2) - I->orig_x_rotation;
        v2[1] = (float) (I->Height / 2) - I->orig_y_rotation;

        v1[0] = v2[0] - (float) (x - I->LastX);
        v1[1] = v2[1] - (float) (y - I->LastY);

      } else if(virtual_trackball==1){
        v1[0] = (float) (eff_width / 2) - x;
        v1[1] = (float) (I->Height / 2) - y;

        v2[0] = (float) (eff_width / 2) - I->LastX;
        v2[1] = (float) (I->Height / 2) - I->LastY;

      } else {
        v1[0] = (float) (I->LastX) - x;
        v1[1] = (float) (I->LastY) - y;

        v2[0] = 0;
        v2[1] = 0;
      }

      r1 = (float) sqrt1f(v1[0] * v1[0] + v1[1] * v1[1]);
      r2 = (float) sqrt1f(v2[0] * v2[0] + v2[1] * v2[1]);

      {
	short r1lt, r2lt;
	if(virtual_trackball==2) {
	  r1lt = I->prev_no_z_rotation1;
	  r2lt = I->prev_no_z_rotation2;
	} else {
	  r1lt = r1 < scale;
	  r2lt = r2 < scale;
	  I->prev_no_z_rotation1 = r1 < scale;
	  I->prev_no_z_rotation2 = r2 < scale;
	  I->orig_x_rotation = x;
	  I->orig_y_rotation = y;
	}
	if(r1lt) {
	  v1[2] = (float) sqrt1f(scale * scale - r1 * r1);
	} else {
	  v1[2] = 0.0;
	}
	if(r2lt) {
	  v2[2] = (float) sqrt1f(scale * scale - r2 * r2);
	} else {
	  v2[2] = 0.0;
	}
      }
      normalize23f(v1, n1);
      normalize23f(v2, n2);
      cross_product3f(n1, n2, cp);
      theta = (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_scale) *
                       2 * 180 *
                       asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) / cPI);
      dx = (v1[0] - v2[0]);
      dy = (v1[1] - v2[1]);
      dt =
        (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_limit) *
                 sqrt1f(dx * dx + dy * dy) / scale);

      if(theta > dt)
        theta = dt;

      normalize23f(cp, axis);

      axis[2] = -axis[2];

      theta = theta / (1.0F + (float) fabs(axis[2]));

      /* transform into model coodinate space */
      MatrixInvTransformC44fAs33f3f(I->RotMatrix, axis, v2);
      v1[0] = (float) (cPI * theta / 180.0);
      EditorDrag(G, NULL, -1, mode,
                 SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3);
      I->LastX = x;
      I->LastY = y;
      break;
    case cButModeMovDrag:
    case cButModeMovDragZ:
      if(I->Threshold) {
        if((abs(x - I->ThresholdX) > I->Threshold) ||
           (abs(y - I->ThresholdY) > I->Threshold)) {
          I->Threshold = 0;
        }
      }
      if(!I->Threshold) {

        copy3f(I->Origin, v1);
        vScale = SceneGetExactScreenVertexScale(G, v1);
        if(stereo_via_adjacent_array(I->StereoMode)) {
          x = get_stereo_x(x, &I->LastX, I->Width, NULL);
        }

        if(mode == cButModeMovDragZ) {
          v2[0] = 0;
          v2[1] = 0;
          v2[2] = -(y - I->LastY) * vScale;
        } else {
          v2[0] = (x - I->LastX) * vScale;
          v2[1] = (y - I->LastY) * vScale;
          v2[2] = 0;
        }

        v3[0] = 0.0F;
        v3[1] = 0.0F;
        v3[2] = 1.0F;

        /* transform into model coodinate space */
        MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2);
        MatrixInvTransformC44fAs33f3f(I->RotMatrix, v3, v3);

        EditorDrag(G, NULL, -1, mode,
                   SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3);
      }
      I->LastX = x;
      I->LastY = y;
      break;
    case cButModeRotObj:
    case cButModeMovObj:
    case cButModeMovObjZ:
    case cButModeRotView:
    case cButModeMovView:
    case cButModeMovViewZ:
    case cButModeRotFrag:
    case cButModeMovFrag:
    case cButModeMovFragZ:
    case cButModeTorFrag:
    case cButModeMoveAtom:
    case cButModeMoveAtomZ:
    case cButModePkTorBnd:
      obj = (CObject *) I->LastPicked.context.object;
      if(obj) {
        if(I->Threshold) {
          if((abs(x - I->ThresholdX) > I->Threshold) ||
             (abs(y - I->ThresholdY) > I->Threshold)) {
            I->Threshold = 0;
          }
        }
        if(!I->Threshold)
          switch (obj->type) {
          case cObjectGadget:  /* note repeated above */
            {
              ObjectGadget *gad;
              gad = (ObjectGadget *) obj;

              ObjectGadgetGetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond,
                                    v1);

              vScale = SceneGetExactScreenVertexScale(G, v1);
              if(stereo_via_adjacent_array(I->StereoMode)) {
                x = get_stereo_x(x, &I->LastX, I->Width, NULL);
              }

              /* transform into model coodinate space */
              switch (obj->Context) {
              case 1:
                {
                  float divisor;
                  divisor = (float) I->Width;
                  if(I->Height < I->Width)
                    divisor = (float) I->Height;
                  v2[0] = (x - I->LastX) / divisor;
                  v2[1] = (y - I->LastY) / divisor;
                  v2[2] = 0;
                }
                break;
              default:
              case 0:
                v2[0] = (x - I->LastX) * vScale;
                v2[1] = (y - I->LastY) * vScale;
                v2[2] = 0;
                MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2);
                break;
              }
              add3f(v1, v2, v2);
              ObjectGadgetSetVertex(gad, I->LastPicked.src.index, I->LastPicked.src.bond,
                                    v2);
              if (I->LastPicked.src.index){ // pick id on gadget is 1 for band (to change values), 
                                            // 0 for everything else (to move it around)
                SceneChanged(G);   // changing values, need to update gadget text
              } else {
                PyMOL_NeedRedisplay(G->PyMOL);    // moving gadget, just re-draw
              }
            }
            break;
          case cObjectMolecule:
            if(ObjectMoleculeGetAtomTxfVertex((ObjectMolecule *) obj,
                                              I->LastPicked.context.state,
                                              I->LastPicked.src.index, v1)) {
              /* scale properly given the current projection matrix */
              vScale = SceneGetExactScreenVertexScale(G, v1);

              if(stereo_via_adjacent_array(I->StereoMode)) {
                x = get_stereo_x(x, &I->LastX, I->Width, NULL);
              }

              switch (mode) {
              case cButModeMovFragZ:
              case cButModeMovObjZ:
              case cButModeMovViewZ:
              case cButModeMoveAtomZ:
                v2[0] = 0;
                v2[1] = 0;
                v2[2] = -(y - I->LastY) * vScale;
                break;
              default:
                v2[0] = (x - I->LastX) * vScale;
                v2[1] = (y - I->LastY) * vScale;
                v2[2] = 0;
                break;
              }

              v3[0] = 0.0F;
              v3[1] = 0.0F;
              v3[2] = 1.0F;

              /* transform into model coodinate space */
              MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2);
              MatrixInvTransformC44fAs33f3f(I->RotMatrix, v3, v3);

              if(I->LastPicked.src.bond >= cPickableAtom) {
                if((mode != cButModeMoveAtom) && (mode != cButModeMoveAtomZ)) {
                  EditorDrag(G, obj, I->LastPicked.src.index, mode,
                             SettingGetGlobal_i(G, cSetting_state) - 1, v1, v2, v3);
                  switch(mode) {
                  case cButModeMovViewZ:
                  case cButModeRotView:
                  case cButModeMovView:
                    if(SettingGetGlobal_i(G,cSetting_movie_auto_store) && 
                       SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
                      I->ReinterpolateFlag = true;
                      I->ReinterpolateObj = obj;
                    }
                    break;
                  }
                } else {
                  int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations);
                  ObjectMolecule *objOM = (ObjectMolecule *) obj;
                  ObjectMoleculeMoveAtom(objOM,
					 I->LastPicked.context.state,
                                         I->LastPicked.src.index, v2, 1, log_trans);
		  /* -- JV - if this object knows about distances, then move them if necessary */
		  /* check the dynamic_measures setting and make sure the object has a distance measure, first  */
		  /* obviated by new method
		  if (SettingGetGlobal_i(G, cSetting_dynamic_measures)) 
		    ObjectMoleculeMoveDist( (ObjectMolecule *) obj, SettingGetGlobal_i(G, cSetting_state)-1, I->LastPicked.src.index, v2, 1, log_trans);
		  */
                  SceneInvalidate(G);
                }
              } else {
                int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations);
                ObjectMolecule *objOM = (ObjectMolecule *) obj;
		float diffInPixels[2];
		diffInPixels[0] = (x - I->LastX);
		diffInPixels[1] = (y - I->LastY);
                ObjectMoleculeMoveAtomLabel(objOM,
					    I->LastPicked.context.state,
                                            I->LastPicked.src.index, v2, log_trans, diffInPixels);
                SceneInvalidate(G);
              }
            }
            break;
          case cObjectSlice:
            {
              ObjectSlice *slice = (ObjectSlice *) obj;

              if(I->LastPickVertexFlag) {

                copy3f(I->LastPickVertex, v1);

                vScale = SceneGetExactScreenVertexScale(G, v1);

                if(stereo_via_adjacent_array(I->StereoMode)) {
                  x = get_stereo_x(x, &I->LastX, I->Width, NULL);
                }

                v2[0] = (x - I->LastX) * vScale;
                v2[1] = (y - I->LastY) * vScale;
                v2[2] = 0;

                v3[0] = 0.0F;
                v3[1] = 0.0F;
                v3[2] = 1.0F;

                /* transform into model coodinate space */
                MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2);
                MatrixInvTransformC44fAs33f3f(I->RotMatrix, v3, v3);

                ObjectSliceDrag(slice, SceneGetState(G), mode, v1, v2, v3);
              }
            }
            break;
          case cObjectMeasurement:
            if(ObjectDistGetLabelTxfVertex((ObjectDist *) obj,
                                           I->LastPicked.context.state,
                                           I->LastPicked.src.index, v1)) {
              /* scale properly given the current projection matrix */
              vScale = SceneGetExactScreenVertexScale(G, v1);
              if(stereo_via_adjacent_array(I->StereoMode)) {
                x = get_stereo_x(x, &I->LastX, I->Width, NULL);
              }

              switch (mode) {
              case cButModeMovFragZ:
              case cButModeMovObjZ:
              case cButModeMovViewZ:
              case cButModeMoveAtomZ:
                v2[0] = 0;
                v2[1] = 0;
                v2[2] = -(y - I->LastY) * vScale;
                break;
              default:
                v2[0] = (x - I->LastX) * vScale;
                v2[1] = (y - I->LastY) * vScale;
                v2[2] = 0;
                break;
              }

              v3[0] = 0.0F;
              v3[1] = 0.0F;
              v3[2] = 1.0F;

              /* transform into model coodinate space */
              MatrixInvTransformC44fAs33f3f(I->RotMatrix, v2, v2);
              MatrixInvTransformC44fAs33f3f(I->RotMatrix, v3, v3);

              if(I->LastPicked.src.bond == cPickableLabel) {
                int log_trans = SettingGetGlobal_b(G, cSetting_log_conformations);
                ObjectDistMoveLabel((ObjectDist *) obj,
                                    I->LastPicked.context.state,
                                    I->LastPicked.src.index, v2, 1, log_trans);
                SceneInvalidate(G);
              }
            }
            break;
          default:
            break;
          }
      }
      I->LastX = x;
      I->LastY = y;
      break;
    case cButModeTransXY:

      SceneNoteMouseInteraction(G);

      vScale = SceneGetExactScreenVertexScale(G, I->Origin);
      if(stereo_via_adjacent_array(I->StereoMode)) {

        x = get_stereo_x(x, &I->LastX, I->Width, NULL);
      }

      v2[0] = (x - I->LastX) * vScale;
      v2[1] = (y - I->LastY) * vScale;
      v2[2] = 0.0F;

      moved_flag = false;
      if(I->LastX != x) {
        I->Pos[0] += v2[0];
        I->LastX = x;
        SceneInvalidate(G);
        moved_flag = true;
      }
      if(I->LastY != y) {
        I->Pos[1] += v2[1];
        I->LastY = y;
        SceneInvalidate(G);
        moved_flag = true;
      }

      EditorFavorOrigin(G, NULL);
      if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_origin)) {
        SceneGetCenter(G, v2);     /* gets position of center of screen */
        SceneOriginSet(G, v2, true);
      }
      if(moved_flag && SettingGetGlobal_b(G, cSetting_roving_detail)) {
        SceneRovingDirty(G);
      }
      break;
    case cButModeRotXYZ:
    case cButModeRotZ:
    case cButModeInvRotZ:
    case cButModeTransZ:
    case cButModeClipNF:
    case cButModeClipN:
    case cButModeClipF:
    case cButModeRotL:
    case cButModeMovL:
    case cButModeMvzL:

      SceneNoteMouseInteraction(G);
      virtual_trackball = SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball);
      eff_width = I->Width;
      if(stereo_via_adjacent_array(I->StereoMode)) {
        eff_width = I->Width / 2;
        x = get_stereo_x(x, &I->LastX, I->Width, NULL);
      }
      if (virtual_trackball==2 &&
	  (!I->prev_no_z_rotation1 || !I->prev_no_z_rotation2)) {
	/* when virtual_trackball=2 and twisting, need to set v1,v2 relative to orig */
        v2[0] = (float) (eff_width / 2) - I->orig_x_rotation;
        v2[1] = (float) (I->Height / 2) - I->orig_y_rotation;

        v1[0] = v2[0] - (float) (x - I->LastX);
        v1[1] = v2[1] - (float) (y - I->LastY);

      } else if(virtual_trackball==1){
        v1[0] = (float) (eff_width / 2) - x;
        v1[1] = (float) (I->Height / 2) - y;

        v2[0] = (float) (eff_width / 2) - I->LastX;
        v2[1] = (float) (I->Height / 2) - I->LastY;

      } else {
        v1[0] = (float) (I->LastX) - x;
        v1[1] = (float) (I->LastY) - y;

        v2[0] = 0;
        v2[1] = 0;
      }

      r1 = (float) sqrt1f(v1[0] * v1[0] + v1[1] * v1[1]);
      r2 = (float) sqrt1f(v2[0] * v2[0] + v2[1] * v2[1]);

      {
	short r1lt, r2lt;
	if(SettingGet_i(G, NULL, NULL, cSetting_virtual_trackball)==2) {
	  r1lt = I->prev_no_z_rotation1;
	  r2lt = I->prev_no_z_rotation2;
	} else {
	  r1lt = r1 < scale;
	  r2lt = r2 < scale;
	  I->prev_no_z_rotation1 = r1 < scale;
	  I->prev_no_z_rotation2 = r2 < scale;
	  I->orig_x_rotation = x;
	  I->orig_y_rotation = y;
	}
	if(r1lt) {
      float val = scale * scale - r1 * r1;
      short isNeg = val < 0.f;
      v1[2] = (float) sqrt(fabs(val)) * (isNeg ? -1.f : 1.f);
	} else {
	  v1[2] = 0.0;
	}
	if(r2lt) {
      float val = scale * scale - r2 * r2;
      short isNeg = val < 0.f;
      v2[2] = (float) sqrt(fabs(val)) * (isNeg ? -1.f : 1.f);
	} else {
	  v2[2] = 0.0;
	}
      }
      normalize23f(v1, n1);
      normalize23f(v2, n2);
      cross_product3f(n1, n2, cp);
      theta = (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_scale) *
                       2 * 180 *
                       asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) / cPI);

      dx = (v1[0] - v2[0]);
      dy = (v1[1] - v2[1]);
      dt =
        (float) (SettingGet_f(G, NULL, NULL, cSetting_mouse_limit) *
                 sqrt1f(dx * dx + dy * dy) / scale);

      if(theta > dt)
        theta = dt;

      normalize23f(cp, axis);

      theta = theta / (1.0F + (float) fabs(axis[2]));

      v1[2] = 0.0;
      v2[2] = 0.0;
      normalize23f(v1, n1);
      normalize23f(v2, n2);
      cross_product3f(n1, n2, cp);
      omega =
        (float) (2 * 180 * asin(sqrt1f(cp[0] * cp[0] + cp[1] * cp[1] + cp[2] * cp[2])) /
                 cPI);
      normalize23f(cp, axis2);

      old_front = I->Front;
      old_back = I->Back;
      old_origin = -I->Pos[2];

      moved_flag = false;
      adjust_flag = false;
      switch (mode) {
      case cButModeRotXYZ:
        if(I->LastX != x) {
          SceneRotate(G, theta, axis[0], axis[1], -axis[2]);
          I->LastX = x;
          adjust_flag = true;
        }
        if(I->LastY != y) {
          SceneRotate(G, theta, axis[0], axis[1], -axis[2]);
          I->LastY = y;
          adjust_flag = true;
        }
        break;
      case cButModeRotZ:
        if(I->LastX != x) {
          SceneRotate(G, omega, axis2[0], axis2[1], -axis2[2]);
          I->LastX = x;
          adjust_flag = true;
        }
        if(I->LastY != y) {
          SceneRotate(G, omega, axis2[0], axis2[1], -axis2[2]);
          I->LastY = y;
          adjust_flag = true;
        }
        break;
      case cButModeInvRotZ:
        if(I->LastX != x) {
          SceneRotate(G, (I->LastX - x) / 2.0F, 0.0F, 0.0F, 1.0F);
          I->LastX = x;
          adjust_flag = true;
        }
        break;
      case cButModeTransZ:
        // zoom
        if(I->LastY != y) {
          {
            // the 5.0 min depth below is an empirical value
            float factor = SettingGetGlobal_f(G, cSetting_mouse_z_scale) *
              (y - I->LastY) / 400.0 * std::max(5.f, -I->Pos[2]);
            if(!SettingGetGlobal_b(G, cSetting_legacy_mouse_zoom))
              factor = -factor;
            I->Pos[2] += factor;
            I->Front -= factor;
            I->Back -= factor;
            UpdateFrontBackSafe(I);
          }
          I->LastY = y;
          SceneInvalidate(G);
          adjust_flag = true;
        }
        break;
      case cButModeClipNF:
        if(I->LastX != x) {
          I->Back -= (((float) x) - I->LastX) / 10;
          I->LastX = x;
          moved_flag = true;
        }
        if(I->LastY != y) {
          I->Front -= (((float) y) - I->LastY) / 10;
          I->LastY = y;
          moved_flag = true;
        }
        if(moved_flag) {
          SceneClipSet(G, I->Front, I->Back);
        }
        break;
      case cButModeClipN:
        if(I->LastX != x || I->LastY != y) {
          I->Front -= (x - I->LastX + y - I->LastY) / 10.f;
          I->LastX = x;
          I->LastY = y;
          SceneClipSet(G, I->Front, I->Back);
          moved_flag = true;
        }
        break;
      case cButModeClipF:
        if(I->LastX != x || I->LastY != y) {
          I->Back -= (x - I->LastX + y - I->LastY) / 10.f;
          I->LastX = x;
          I->LastY = y;
          SceneClipSet(G, I->Front, I->Back);
          moved_flag = true;
        }
        break;
      case cButModeRotL: 
      case cButModeMovL:
	{
	  /* when light_count == 1, there is an ambient light;
	   * when light_count == 2, there are two lights, the ambient and
	   * a directional, called "light". When there are three, it's the 
	   * ambient, light and the third is light2, and so on.
	   *
	   * Should we use an off-by-one here to make this easier for the
	   * user to understand? light1 is ambient, light2 is first directional
	   * 
	   */
	  float pos[3];
	  int which_light;
	  float ms = 0.01;
	  
	  which_light = light_setting_indices[glm::clamp(
              SettingGetGlobal_i(G, cSetting_edit_light), 1, 9) - 1];
  
	  copy3f(SettingGet<const float *>(G, which_light), pos);
	  
	  pos[0] += (float) dx * ms;
	  pos[1] += (float) dy * ms;
	  
	  SettingSet_3fv(G->Setting, which_light, pos);
          SettingGenerateSideEffects(G, which_light, NULL, 0, 1);
	  I->LastX = x;
	  I->LastY = y;
	}
	break;
      case cButModeMvzL:
	{
	  float pos[3];
	  int which_light;
	  float ms = 0.01;
	  float factor = 0.f;

	  /* when light_count == 1, there is an ambient light;
	   * when light_count == 2, there are two lights, the ambient and
	   * a directional, called "light". When there are three, it's the 
	   * ambient, light and the third is light2, and so on.
	   */
	  
	  which_light = light_setting_indices[glm::clamp(
              SettingGetGlobal_i(G, cSetting_edit_light), 1, 9) - 1];

	  if(I->LastY != y) {
	    factor = 400 / ((I->FrontSafe + I->BackSafe) / 2);
	    if(factor >= 0.0F) {
	      factor = SettingGetGlobal_f(G, cSetting_mouse_z_scale) * 
		(((float) y) - I->LastY) / factor;
	      if(!SettingGetGlobal_b(G, cSetting_legacy_mouse_zoom))
		factor = -factor;
	    }
	  }

	  copy3f(SettingGet<const float *>(G, which_light), pos);
          SettingGenerateSideEffects(G, which_light, NULL, 0, 1);
	  
	  pos[2] -= factor * ms;
	  
	  SettingSet_3fv(G->Setting, which_light, pos);
	  I->LastX = x;
	  I->LastY = y;
	}
	break;
      }
      if(moved_flag)
        SceneDoRoving(G, old_front, old_back, old_origin, adjust_flag, false);
    }
  }
  if(I->PossibleSingleClick) {
    int max_single_click_drag = 4;
    int dx = abs(I->StartX - I->LastX);
    int dy = abs(I->StartY - I->LastY);
    if((dx > max_single_click_drag) || (dy > max_single_click_drag)) {
      I->PossibleSingleClick = false;
    }
  }
  return (1);
}