/* 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 , CGO *orthoCGO); /*========================================================================*/ 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(SettingGet(m_G, cSetting_internal_gui_mode) == InternalGUIMode::Default) { 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, orthoCGO); TextSetColor(m_G, TextColor3); TextDrawStrAt(m_G, SettingGetGlobal_s(m_G, cSetting_button_mode_name), x + DIP2PIXEL(88), y, orthoCGO); /* 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, orthoCGO); 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, orthoCGO); y -= cButModeLineHeight; /* glColor3fv(I->Block->TextColor); TextDrawStrAt(m_G,"K",x,y-4); */ TextSetColor(m_G, TextColor3); TextDrawStrAt(m_G, "&", x + DIP2PIXEL(12), y, orthoCGO); TextDrawStrAt(m_G, "Keys", x + DIP2PIXEL(24), y, orthoCGO); 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, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); } mode = Mode[12]; if(mode < 0) TextDrawStr(m_G, BLANK_STR, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); 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, orthoCGO); 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, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); } mode = Mode[13]; if(mode < 0) TextDrawStr(m_G, BLANK_STR, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); 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, orthoCGO); 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, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); } mode = I->Mode[14]; if(mode < 0) TextDrawStr(m_G, BLANK_STR, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); 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, orthoCGO); 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, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); } mode = Mode[15]; if(mode < 0) TextDrawStr(m_G, BLANK_STR, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); y -= cButModeLineHeight; TextSetColor(m_G, TextColor); TextSetColor(m_G, TextColor1); TextDrawStrAt(m_G, " SnglClk", x - DIP2PIXEL(8), y, orthoCGO); 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, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); } TextSetColor(m_G, TextColor); y -= cButModeLineHeight; TextSetColor(m_G, TextColor); TextSetColor(m_G, TextColor1); TextDrawStrAt(m_G, " DblClk", x, y, orthoCGO); 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, orthoCGO); else TextDrawStr(m_G, Code[mode], orthoCGO); } 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, orthoCGO); TextSetColor(m_G, TextColor3); TextDrawStrAt(m_G, "Atoms (and Joints)", x + DIP2PIXEL(64), y, orthoCGO); } else { TextDrawStrAt(m_G, "Selecting ", x, y, orthoCGO); TextSetColor(m_G, TextColor3); switch (SettingGetGlobal_i(m_G, cSetting_mouse_selection_mode)) { case 0: TextDrawStrAt(m_G, "Atoms", x + DIP2PIXEL(80), y, orthoCGO); break; case 1: TextDrawStrAt(m_G, "Residues", x + DIP2PIXEL(80), y, orthoCGO); break; case 2: TextDrawStrAt(m_G, "Chains", x + DIP2PIXEL(80), y, orthoCGO); break; case 3: TextDrawStrAt(m_G, "Segments", x + DIP2PIXEL(80), y, orthoCGO); break; case 4: TextDrawStrAt(m_G, "Objects", x + DIP2PIXEL(80), y, orthoCGO); break; case 5: TextDrawStrAt(m_G, "Molecules", x + DIP2PIXEL(80), y, orthoCGO); break; case 6: TextDrawStrAt(m_G, "C-alphas", x + DIP2PIXEL(80), y, orthoCGO); break; } } } } if (!orthoCGO || !(SettingGetGlobal_b(m_G, cSetting_show_frame_rate) || MoviePlaying(m_G))) { ButModeDrawFastImpl(this, true, orthoCGO); } } bool CButMode::fastDraw(CGO* orthoCGO){ return ButModeDrawFastImpl(this, false, orthoCGO); } static bool ButModeDrawFastImpl(Block * block, short definitely , CGO *orthoCGO) { 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, orthoCGO); } else { TextDrawStrAt(G, "State ", x, y, orthoCGO); } TextSetColor(G, textColor2); sprintf(rateStr, "%4d/%4d ", SceneGetFrame(G) + 1, nf); TextDrawStrAt(G, rateStr, x + DIP2PIXEL(48), y, orthoCGO); if(frame_rate) { sprintf(rateStr,"%5.1f",I->RateShown); TextDrawStrAt(G, rateStr, x + DIP2PIXEL(144), y, orthoCGO); TextSetColor(G, textColor); TextDrawStrAt(G, "Hz ", x + DIP2PIXEL(192), y, orthoCGO); TextSetColor(G, textColor2); } else if(has_movie) { TextSetColor(G, textColor); TextDrawStrAt(G, "State ", x + DIP2PIXEL(128), y, orthoCGO); TextSetColor(G, textColor2); sprintf(rateStr," %4d",SceneGetState(G)+1); TextDrawStrAt(G, rateStr, x + DIP2PIXEL(168), y, orthoCGO); } else if(frame_rate) { } } return true; } /*========================================================================*/ int ButModeInit(PyMOLGlobals * G) { CButMode *I = nullptr; 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]); }