layer3/Seeker.cpp (1,877 lines of code) (raw):

/* 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 <algorithm> #include"os_python.h" #include"os_predef.h" #include"os_std.h" #include"os_gl.h" #include"Err.h" #include"Util.h" #include"Seq.h" #include"Seeker.h" #include"MemoryDebug.h" #include"Executive.h" #include"P.h" #include"Selector.h" #include"Wizard.h" #include"Scene.h" #include"Menu.h" #include "Lex.h" #define cTempSeekerSele "_seeker" #define cTempCenterSele "_seeker_center" #define cTempSeekerSele2 "_seeker2" static CSeqRow *SeekerDrag(PyMOLGlobals * G, CSeqRow * rowVLA, int row, int col, int mod); struct _CSeeker { CSeqHandler handler; /* must be first */ int drag_start_col, drag_last_col; int drag_row; int drag_dir, drag_start_toggle; int dragging, drag_setting; int drag_button; double LastClickTime; }; static void SeekerBuildSeleFromAtomList(PyMOLGlobals * G, const char *obj_name, int *atom_list, const char *sele_name, int start_fresh) { ObjectMolecule *obj = ExecutiveFindObjectMoleculeByName(G, obj_name); if(start_fresh) { SelectorCreateFromObjectIndices(G, sele_name, obj, atom_list, -1); } else { OrthoLineType buf1; SelectorCreateFromObjectIndices(G, cTempSeekerSele2, obj, atom_list, -1); sprintf(buf1, "?%s|?%s", sele_name, cTempSeekerSele2); SelectorCreate(G, sele_name, buf1, NULL, true, NULL); ExecutiveDelete(G, cTempSeekerSele2); } } static void SeekerSelectionToggleRange(PyMOLGlobals * G, CSeqRow * rowVLA, int row_num, int col_first, int col_last, int inc_or_excl, int start_over) { char selName[WordLength]; OrthoLineType buf1, buf2; if(row_num >= 0) { CSeqRow *row; CSeqCol *col; char prefix[3] = ""; int logging = SettingGetGlobal_i(G, cSetting_logging); int col_num; int *atom_vla = NULL; int n_at = 0; int at_idx; int *atom_list; ObjectMolecule *obj; if(logging == cPLog_pml) strcpy(prefix, "_ "); row = rowVLA + row_num; if((obj = ExecutiveFindObjectMoleculeByName(G, row->name))) { atom_vla = VLAlloc(int, obj->NAtom / 10); for(col_num = col_first; col_num <= col_last; col_num++) { col = row->col + col_num; if(!col->spacer) { if(!start_over) { if(inc_or_excl) col->inverse = true; else col->inverse = false; } else { col->inverse = true; } atom_list = row->atom_lists + col->atom_at; while((at_idx = (*(atom_list++))) >= 0) { /* build one extra long list so that we only call selector once */ VLACheck(atom_vla, int, n_at); atom_vla[n_at++] = at_idx; } } } VLACheck(atom_vla, int, n_at); atom_vla[n_at] = -1; SeekerBuildSeleFromAtomList(G, row->name, atom_vla, cTempSeekerSele, true); VLAFreeP(atom_vla); { const char *sele_mode_kw; sele_mode_kw = SceneGetSeleModeKeyword(G); if(logging) SelectorLogSele(G, cTempSeekerSele); { ExecutiveGetActiveSeleName(G, selName, true, logging); /* selection or deselecting? */ if(!start_over) { if(inc_or_excl) { sprintf(buf1, "((%s(?%s)) or %s(?%s))", sele_mode_kw, selName, sele_mode_kw, cTempSeekerSele); } else { sprintf(buf1, "((%s(?%s)) and not %s(?%s))", sele_mode_kw, selName, sele_mode_kw, cTempSeekerSele); } } else { sprintf(buf1, "%s(?%s)", sele_mode_kw, cTempSeekerSele); } /* create the new active selection */ SelectorCreate(G, selName, buf1, NULL, true, NULL); { sprintf(buf2, "%scmd.select(\"%s\",\"%s\",enable=1)\n", prefix, selName, buf1); PLog(G, buf2, cPLog_no_flush); } WizardDoSelect(G, selName); } ExecutiveDelete(G, cTempSeekerSele); if(logging) { sprintf(buf2, "%scmd.delete(\"%s\")\n", prefix, cTempSeekerSele); PLog(G, buf2, cPLog_no_flush); PLogFlush(G); } if(SettingGetGlobal_b(G, cSetting_auto_show_selections)) ExecutiveSetObjVisib(G, selName, 1, false); SceneInvalidate(G); } } } } static void SeekerSelectionToggle(PyMOLGlobals * G, CSeqRow * rowVLA, int row_num, int col_num, int inc_or_excl, int start_over) { char selName[WordLength]; OrthoLineType buf1, buf2; if(row_num >= 0) { CSeqRow *row; CSeqCol *col; int *atom_list; char prefix[3] = ""; int logging = SettingGetGlobal_i(G, cSetting_logging); if(logging == cPLog_pml) strcpy(prefix, "_ "); row = rowVLA + row_num; col = row->col + col_num; if(!col->spacer) if(ExecutiveFindObjectByName(G, row->name)) { const char *sele_mode_kw; atom_list = row->atom_lists + col->atom_at; /* build up a selection consisting of residue atoms */ SeekerBuildSeleFromAtomList(G, row->name, atom_list, cTempSeekerSele, true); sele_mode_kw = SceneGetSeleModeKeyword(G); if(logging) SelectorLogSele(G, cTempSeekerSele); { ExecutiveGetActiveSeleName(G, selName, true, logging); /* selection or deselecting? */ if(!start_over) { if(inc_or_excl) { if(!col->spacer) { col->inverse = true; sprintf(buf1, "((%s(?%s)) or %s(%s))", sele_mode_kw, selName, sele_mode_kw, cTempSeekerSele); } } else { if(!col->spacer) { col->inverse = false; sprintf(buf1, "((%s(?%s)) and not %s(%s))", sele_mode_kw, selName, sele_mode_kw, cTempSeekerSele); } } } else { if(!col->spacer) { col->inverse = true; sprintf(buf1, "%s(%s)", sele_mode_kw, cTempSeekerSele); } } /* create the new active selection */ SelectorCreate(G, selName, buf1, NULL, true, NULL); { sprintf(buf2, "%scmd.select(\"%s\",\"%s\",enable=1)\n", prefix, selName, buf1); PLog(G, buf2, cPLog_no_flush); } WizardDoSelect(G, selName); } ExecutiveDelete(G, cTempSeekerSele); if(logging) { sprintf(buf2, "%scmd.delete(\"%s\")\n", prefix, cTempSeekerSele); PLog(G, buf2, cPLog_no_flush); PLogFlush(G); } if(SettingGetGlobal_b(G, cSetting_auto_show_selections)) ExecutiveSetObjVisib(G, selName, 1, false); SceneInvalidate(G); } } } static void SeekerSelectionUpdateCenter(PyMOLGlobals * G, CSeqRow * rowVLA, int row_num, int col_num, int start_over) { { CSeqRow *row; CSeqCol *col; CObject *obj; int *atom_list; char prefix[3] = ""; int logging = SettingGetGlobal_i(G, cSetting_logging); if(logging == cPLog_pml) strcpy(prefix, "_ "); if(row_num >= 0) { row = rowVLA + row_num; col = row->col + col_num; if(!col->spacer) if((obj = ExecutiveFindObjectByName(G, row->name))) { if(col->state && obj) SettingSetSmart_i(G, obj->Setting, NULL, cSetting_state, col->state); atom_list = row->atom_lists + col->atom_at; SeekerBuildSeleFromAtomList(G, row->name, atom_list, cTempCenterSele, start_over); if(logging) SelectorLogSele(G, cTempCenterSele); } } } } static void SeekerSelectionCenter(PyMOLGlobals * G, int action) { OrthoLineType buf2; char prefix[3] = ""; int logging = SettingGetGlobal_i(G, cSetting_logging); if(logging == cPLog_pml) strcpy(prefix, "_ "); switch (action) { case 0: /* center cumulative */ ExecutiveCenter(G, cTempCenterSele, -1, true, -1, NULL, true); if(logging) { sprintf(buf2, "%scmd.center(\"%s\")\n", prefix, cTempCenterSele); PLog(G, buf2, cPLog_no_flush); PLogFlush(G); } break; case 1: /* zoom */ ExecutiveWindowZoom(G, cTempCenterSele, 0.0, -1, false, -1, true); if(logging) { sprintf(buf2, "%scmd.zoom(\"%s\")\n", prefix, cTempCenterSele); PLog(G, buf2, cPLog_no_flush); PLogFlush(G); } break; case 2: /* center seeker */ { char selName[WordLength]; if(ExecutiveGetActiveSeleName(G, selName, true, logging)) { ExecutiveCenter(G, selName, -1, true, -1, NULL, true); if(logging) { sprintf(buf2, "%scmd.center(\"%s\")\n", prefix, selName); PLog(G, buf2, cPLog_no_flush); PLogFlush(G); } } } break; } } #define cDoubleTime 0.35 static CSeqRow *SeekerClick(PyMOLGlobals * G, CSeqRow * rowVLA, int button, int row_num, int col_num, int mod, int x, int y) { CSeqRow *row; CSeqCol *col; /* char selName[WordLength]; */ CSeeker *I = G->Seeker; int logging = SettingGetGlobal_i(G, cSetting_logging); int continuation = false; if((row_num < 0) || (col_num < 0)) { switch (button) { case P_GLUT_LEFT_BUTTON: if((UtilGetSeconds(G) - I->LastClickTime) < cDoubleTime) { OrthoLineType buf2; char name[WordLength]; if(ExecutiveGetActiveSeleName(G, name, false, false)) { SelectorCreate(G, name, "none", NULL, true, NULL); if(logging) { sprintf(buf2, "cmd.select('%s','none', enable=1)", name); PLog(G, buf2, cPLog_no_flush); } SeqDirty(G); } } I->LastClickTime = UtilGetSeconds(G); break; } } else { row = rowVLA + row_num; col = row->col + col_num; I->dragging = false; I->drag_button = button; I->handler.box_row = row_num; I->handler.box_stop_col = col_num; if((I->drag_row == row_num) && (button == P_GLUT_LEFT_BUTTON) && (mod & cOrthoSHIFT)) { continuation = true; } else { I->drag_row = -1; /* invalidate */ I->handler.box_start_col = col_num; } switch (button) { case P_GLUT_RIGHT_BUTTON: { ObjectMolecule *obj; char name[WordLength]; if(ExecutiveGetActiveSeleName(G, name, false, logging) && col->inverse) { MenuActivate2Arg(G, x, y + 16, x, y, false, "pick_sele", name, name); } else if((obj = ExecutiveFindObjectMoleculeByName(G, row->name))) { OrthoLineType buffer; { int *atom_list; char prefix[3] = ""; int logging = SettingGetGlobal_i(G, cSetting_logging); if(logging == cPLog_pml) strcpy(prefix, "_ "); if(ExecutiveFindObjectByName(G, row->name)) { atom_list = row->atom_lists + col->atom_at; /* build up a selection consisting of residue atoms */ if((*atom_list) >= 0) { ObjectMoleculeGetAtomSele(obj, *atom_list, buffer); SeekerBuildSeleFromAtomList(G, row->name, atom_list, cTempSeekerSele, true); if(logging) SelectorLogSele(G, cTempSeekerSele); MenuActivate2Arg(G, x, y + 16, x, y, false, "seq_option", cTempSeekerSele, buffer); } } } } } break; case P_GLUT_MIDDLE_BUTTON: if(!col->spacer) { ObjectMolecule *obj; I->drag_start_col = col_num; I->drag_last_col = col_num; I->drag_row = row_num; I->dragging = true; SeekerSelectionUpdateCenter(G, rowVLA, row_num, col_num, true); if(mod & cOrthoCTRL) SeekerSelectionCenter(G, 1); else SeekerSelectionCenter(G, 0); I->handler.box_active = true; if(col->state && (obj = ExecutiveFindObjectMoleculeByName(G, row->name))) { SettingSetSmart_i(G, obj->Obj.Setting, NULL, cSetting_state, col->state); SceneChanged(G); } } break; case P_GLUT_LEFT_BUTTON: if(!col->spacer) { int start_over = false; int center = 0; ObjectMolecule *obj; if(mod & cOrthoCTRL) { center = 2; } int codes = SettingGet_i(G, row->obj->Obj.Setting, NULL, cSetting_seq_view_format); if(row->obj->DiscreteFlag && SettingGet_b(G, row->obj->Obj.Setting, NULL, cSetting_seq_view_discrete_by_state)) codes = 4; if (codes != 4 || row->obj->DiscreteFlag) { // keep only non-discrete states selectable if(!continuation) { I->drag_start_col = col_num; I->drag_last_col = col_num; I->drag_row = row_num; I->drag_dir = 0; I->drag_start_toggle = true; } else { int tmp; if(((col_num < I->drag_start_col) && (I->drag_last_col > I->drag_start_col)) || ((col_num > I->drag_start_col) && (I->drag_last_col < I->drag_start_col))) { tmp = I->drag_last_col; I->drag_last_col = I->drag_start_col; I->drag_start_col = tmp; I->drag_dir = -I->drag_dir; } } I->dragging = true; I->handler.box_active = true; if(continuation) { SeekerDrag(G, rowVLA, row_num, col_num, mod); } else { if(col->inverse && !start_over) { SeekerSelectionToggle(G, rowVLA, row_num, col_num, false, false); I->drag_setting = false; } else { SeekerSelectionToggle(G, rowVLA, row_num, col_num, true, start_over); I->drag_setting = true; } } } if(center) SeekerSelectionCenter(G, 2); if(col->state && (obj = ExecutiveFindObjectMoleculeByName(G, row->name))) { SettingSetSmart_i(G, obj->Obj.Setting, NULL, cSetting_state, col->state); SceneChanged(G); } } break; } } return NULL; } static void SeekerRefresh(PyMOLGlobals * G, CSeqRow * rowVLA) { if(rowVLA) { CSeqRow *row; CSeqCol *col; int *atom_list; int nRow = VLAGetSize(rowVLA); int sele = ExecutiveGetActiveSele(G); int b; ObjectMolecule *obj; if(sele < 0) sele = SelectorIndexByName(G, "_seeker_hilight"); for(b = 0; b < nRow; b++) { row = rowVLA + b; if((obj = ExecutiveFindObjectMoleculeByName(G, row->name))) { int a; AtomInfoType *atInfo = obj->AtomInfo; int at; int selected; if(sele < 0) { for(a = 0; a < row->nCol; a++) { col = row->col + a; col->inverse = false; } } else { for(a = 0; a < row->nCol; a++) { col = row->col + a; if(!col->spacer) { selected = false; atom_list = row->atom_lists + col->atom_at; while((at = (*atom_list)) >= 0) { atom_list++; if(SelectorIsMember(G, atInfo[at].selEntry, sele)) { selected = true; } } if(selected) col->inverse = true; else col->inverse = false; } else col->inverse = false; } } } } } } static CSeqRow *SeekerDrag(PyMOLGlobals * G, CSeqRow * rowVLA, int row, int col, int mod) { CSeeker *I = G->Seeker; int a; if((row >= 0) && (col >= 0) && (I->dragging)) { I->handler.box_stop_col = col; switch (I->drag_button) { case P_GLUT_LEFT_BUTTON: if(col != I->drag_last_col) { if(I->drag_dir) { if(I->drag_dir > 0) { if(col <= I->drag_start_col) { col = I->drag_start_col; if(I->drag_start_toggle) { SeekerSelectionToggle(G, rowVLA, I->drag_row, I->drag_start_col, !I->drag_setting, false); I->drag_start_toggle = false; } } else if(col > I->drag_start_col) { if(!I->drag_start_toggle) { SeekerSelectionToggle(G, rowVLA, I->drag_row, I->drag_start_col, I->drag_setting, false); I->drag_start_toggle = true; } } } else if(I->drag_dir < 0) { if(col >= I->drag_start_col) { col = I->drag_start_col; if(I->drag_start_toggle) { SeekerSelectionToggle(G, rowVLA, I->drag_row, I->drag_start_col, !I->drag_setting, false); I->drag_start_toggle = false; } } else if(col < I->drag_start_col) { if(!I->drag_start_toggle) { SeekerSelectionToggle(G, rowVLA, I->drag_row, I->drag_start_col, I->drag_setting, false); I->drag_start_toggle = true; } } } } /* if(mod &cOrthoSHIFT) { if(I->drag_start_col == I->drag_last_col) { if(col>I->drag_start_col) { SeekerSelectionCenter(G,rowVLA,I->drag_row,I->drag_start_col+1,false); } else if(col<I->drag_start_col) { SeekerSelectionCenter(G,rowVLA,I->drag_row,I->drag_start_col-1,false); } } if(I->drag_start_col < I->drag_last_col) { if( col > I->drag_last_col ) { for( a=I->drag_last_col+1; a<=col; a++) { SeekerSelectionCenter(G,rowVLA,I->drag_row,a,false); } } } else { if( col < I->drag_last_col) { for(a=I->drag_last_col-1;a>=col;a--) { SeekerSelectionCenter(G,rowVLA,I->drag_row,a,false); } } } SeekerSelectionCenter(G,0); } */ if((I->drag_last_col < I->drag_start_col) && (col > I->drag_start_col)) { /* for(a=I->drag_last_col;a<I->drag_start_col;a++) */ SeekerSelectionToggleRange(G, rowVLA, I->drag_row, I->drag_last_col, I->drag_start_col - 1, !I->drag_setting, false); I->drag_last_col = I->drag_start_col; } if((I->drag_last_col > I->drag_start_col) && (col < I->drag_start_col)) { /* for(a=I->drag_last_col;a>I->drag_start_col;a--) */ SeekerSelectionToggleRange(G, rowVLA, I->drag_row, I->drag_start_col + 1, I->drag_last_col, !I->drag_setting, false); I->drag_last_col = I->drag_start_col; } if(I->drag_start_col == I->drag_last_col) { if(col > I->drag_start_col) { if(!I->drag_dir) I->drag_dir = 1; I->drag_last_col = I->drag_start_col + 1; SeekerSelectionToggle(G, rowVLA, I->drag_row, I->drag_last_col, I->drag_setting, false); } else if(col < I->drag_start_col) { if(!I->drag_dir) I->drag_dir = -1; I->drag_last_col = I->drag_start_col - 1; SeekerSelectionToggle(G, rowVLA, I->drag_row, I->drag_last_col, I->drag_setting, false); } } if(I->drag_start_col < I->drag_last_col) { if(col > I->drag_last_col) { /* for( a=I->drag_last_col+1; a<=col; a++) */ SeekerSelectionToggleRange(G, rowVLA, I->drag_row, I->drag_last_col + 1, col, I->drag_setting, false); } else { /* for(a=I->drag_last_col; a>col ;a--) */ SeekerSelectionToggleRange(G, rowVLA, I->drag_row, col + 1, I->drag_last_col, !I->drag_setting, false); } } else { if(col < I->drag_last_col) { /*for(a=I->drag_last_col-1;a>=col;a--) */ SeekerSelectionToggleRange(G, rowVLA, I->drag_row, col, I->drag_last_col - 1, I->drag_setting, false); } else { /*for(a=I->drag_last_col; a<col ;a++) */ SeekerSelectionToggleRange(G, rowVLA, I->drag_row, I->drag_last_col, col - 1, !I->drag_setting, false); } } I->drag_last_col = col; if(mod & cOrthoCTRL) { SeekerSelectionCenter(G, 2); } } break; case P_GLUT_MIDDLE_BUTTON: if(col != I->drag_last_col) { int action = 0; int start_over = false; if(mod & cOrthoCTRL) { action = 1; } if(!(mod & cOrthoSHIFT)) { start_over = true; I->handler.box_start_col = col; SeekerSelectionUpdateCenter(G, rowVLA, I->drag_row, col, start_over); } else { if(I->drag_start_col == I->drag_last_col) { if(col > I->drag_start_col) { I->drag_last_col = I->drag_start_col + 1; SeekerSelectionUpdateCenter(G, rowVLA, I->drag_row, I->drag_last_col, start_over); } else if(col < I->drag_start_col) { I->drag_last_col = I->drag_start_col - 1; SeekerSelectionUpdateCenter(G, rowVLA, I->drag_row, I->drag_last_col, start_over); } } if(I->drag_start_col < I->drag_last_col) { if(col > I->drag_last_col) { for(a = I->drag_last_col + 1; a <= col; a++) { SeekerSelectionUpdateCenter(G, rowVLA, I->drag_row, a, start_over); } } } else { if(col < I->drag_last_col) { for(a = I->drag_last_col - 1; a >= col; a--) { SeekerSelectionUpdateCenter(G, rowVLA, I->drag_row, a, start_over); } } } } I->drag_last_col = col; SeekerSelectionCenter(G, action); } break; } } return NULL; } static CSeqRow *SeekerRelease(PyMOLGlobals * G, CSeqRow * rowVLA, int button, int row, int col, int mod) { CSeeker *I = G->Seeker; I->dragging = false; I->handler.box_active = false; return NULL; } char SeekerGetAbbr(PyMOLGlobals * G, const char *abbr, char water, char unknown) { switch (abbr[0]) { case 'A': switch (abbr[1]) { case 'L': if(abbr[2] == 'A') return 'A'; break; case 'R': if(abbr[2] == 'G') return 'R'; break; case 'S': switch (abbr[2]) { case 'P': return 'D'; break; case 'N': return 'N'; break; } break; } break; case 'C': switch (abbr[1]) { case 'Y': switch (abbr[2]) { case 'S': case 'X': return 'C'; break; } break; } break; case 'G': switch (abbr[1]) { case 'L': switch (abbr[2]) { case 'N': return 'Q'; break; case 'U': return 'E'; break; case 'Y': return 'G'; break; } } break; case 'H': switch (abbr[1]) { case 'I': switch (abbr[2]) { case 'S': case 'D': case 'E': return 'H'; break; } break; case 'O': switch (abbr[2]) { case 'H': return water; break; } break; case '2': switch (abbr[2]) { case 'O': return water; break; } break; } case 'I': switch (abbr[1]) { case 'L': switch (abbr[2]) { case 'E': return 'I'; break; } } break; case 'L': switch (abbr[1]) { case 'E': switch (abbr[2]) { case 'U': return 'L'; break; } break; case 'Y': switch (abbr[2]) { case 'S': return 'K'; break; } break; } break; case 'M': switch (abbr[1]) { case 'E': switch (abbr[2]) { case 'T': return 'M'; break; } break; case 'S': switch (abbr[2]) { case 'E': // MSE (SELENOMETHIONINE) return 'M'; } } break; case 'P': switch (abbr[1]) { case 'H': switch (abbr[2]) { case 'E': return 'F'; break; } break; case 'R': switch (abbr[2]) { case 'O': return 'P'; break; } break; } break; case 'S': switch (abbr[1]) { case 'E': switch (abbr[2]) { case 'R': return 'S'; break; case 'C': // SEC (SELENOCYSTEINE) return 'U'; break; } break; case 'O': /* SOL -- gromacs solvent residue */ switch (abbr[2]) { case 'L': return water; break; } break; } break; case 'T': switch (abbr[1]) { case 'H': switch (abbr[2]) { case 'R': return 'T'; break; } break; case 'I': switch (abbr[2]) { case 'P': return water; break; } break; case 'R': switch (abbr[2]) { case 'P': return 'W'; break; } break; case 'Y': switch (abbr[2]) { case 'R': return 'Y'; break; } break; } break; case 'V': switch (abbr[1]) { case 'A': switch (abbr[2]) { case 'L': return 'V'; break; } break; } break; case 'W': switch (abbr[1]) { case 'A': switch (abbr[2]) { case 'T': return water; break; } break; } break; } return unknown; } static int SeekerFindColor(PyMOLGlobals * G, AtomInfoType * ai, int n_more_plus_one) { int result = ai->color; /* default -- use first atom color */ AtomInfoType *ai0 = ai; while(1) { if(ai0->flags & cAtomFlag_guide) /* best use guide color */ return ai0->color; if(ai0->protons == cAN_C) /* or use carbon color */ result = ai0->color; n_more_plus_one--; if(n_more_plus_one > 0) { ai0++; if(!AtomInfoSameResidueP(G, ai, ai0)) break; } else break; } return result; } static int SeekerFindTag(PyMOLGlobals * G, AtomInfoType * ai, int sele, int codes, int n_more_plus_one) { int result = 0; /* default -- no tag */ AtomInfoType *ai0 = ai; while(1) { int tag = SelectorIsMember(G, ai0->selEntry, sele); if(tag && (codes < 2) && (ai0->flags & cAtomFlag_guide)) /* use guide atom if present */ return tag; if(result < tag) { if(!result) result = tag; else if((codes < 2) && (ai0->flags & cAtomFlag_guide)) /* residue based and on guide atom */ result = tag; } n_more_plus_one--; if(n_more_plus_one > 0) { int do_break = false; ai0++; switch (codes) { case 0: case 1: if(!AtomInfoSameResidueP(G, ai, ai0)) do_break = true; break; case 2: /* atoms */ do_break = true; break; case 3: /* chains */ if(!AtomInfoSameChainP(G, ai, ai0)) do_break = true; break; } if(do_break) break; } else break; } return result; } PyObject *SeekerGetRawAlignment(PyMOLGlobals * G, int align_sele, int active_only) { #ifdef _PYMOL_NOPY return NULL; #else PyObject *result = NULL; int nRow = 0; int nCol = 0; CSeqRow *row_vla = NULL, *row; void *hidden = NULL; ObjectMolecule *obj; if(align_sele < 0) { align_sele = ExecutiveGetActiveAlignmentSele(G); } if(align_sele >= 0) { row_vla = VLACalloc(CSeqRow, 10); /* first, find out which objects are included in the alignment */ while(ExecutiveIterateObjectMolecule(G, &obj, &hidden)) { if((obj->Obj.Enabled || !active_only) && (obj->Obj.Name[0] != '_')) { int a; AtomInfoType *ai = obj->AtomInfo; for(a = 0; a < obj->NAtom; a++) { if(SelectorIsMember(G, ai->selEntry, align_sele)) { VLACheck(row_vla, CSeqRow, nRow); row = row_vla + nRow; row->obj = obj; row->nCol = obj->NAtom; nRow++; break; } ai++; } } } /* next, figure out how many aligned columns exist */ { int done = false; while(!done) { int a; int min_tag = -1; done = true; for(a = 0; a < nRow; a++) { row = row_vla + a; while(row->cCol < row->nCol) { /* advance to next tag in each row & find lowest */ AtomInfoType *ai = row->obj->AtomInfo + row->cCol; int tag = SelectorIsMember(G, ai->selEntry, align_sele); if(!tag) { row->cCol++; } else { /* we're at a tagged atom... */ if(min_tag > tag) min_tag = tag; else if(min_tag < 0) min_tag = tag; done = false; break; } } } if(min_tag >= 0) { nCol++; for(a = 0; a < nRow; a++) { row = row_vla + a; if(row->cCol < row->nCol) { AtomInfoType *ai = row->obj->AtomInfo + row->cCol; int tag = SelectorIsMember(G, ai->selEntry, align_sele); if(tag == min_tag) { /* advance past this tag */ row->cCol++; } } } } } } /* now populate the table */ result = PyList_New(nCol); if(nCol) { int done = false; nCol = 0; { /* reset start points for our second pass */ int a; for(a = 0; a < nRow; a++) { row = row_vla + a; row->cCol = 0; } } while(!done) { int a; int min_tag = -1; done = true; for(a = 0; a < nRow; a++) { row = row_vla + a; while(row->cCol < row->nCol) { /* advance to next tag in each row & find lowest */ AtomInfoType *ai = row->obj->AtomInfo + row->cCol; int tag = SelectorIsMember(G, ai->selEntry, align_sele); if(!tag) { row->cCol++; } else { /* we're at a tagged atom... */ if(min_tag > tag) min_tag = tag; else if(min_tag < 0) min_tag = tag; done = false; break; } } } if(min_tag >= 0) { int n_member = 0; for(a = 0; a < nRow; a++) { row = row_vla + a; if(row->cCol < row->nCol) { AtomInfoType *ai = row->obj->AtomInfo + row->cCol; int tag = SelectorIsMember(G, ai->selEntry, align_sele); if(tag == min_tag) { /* participates */ n_member++; } } } { PyObject *column_list = PyList_New(n_member); n_member = 0; for(a = 0; a < nRow; a++) { row = row_vla + a; if(row->cCol < row->nCol) { AtomInfoType *ai = row->obj->AtomInfo + row->cCol; int tag = SelectorIsMember(G, ai->selEntry, align_sele); if(tag == min_tag) { /* participates */ PyObject *tup = PyTuple_New(2); PyTuple_SetItem(tup, 0, PyString_FromString(row->obj->Obj.Name)); PyTuple_SetItem(tup, 1, PyInt_FromLong(row->cCol + 1)); /* +1, for 1-based PyMOL atom "index" */ PyList_SetItem(column_list, n_member, tup); row->cCol++; /* advance past this tag */ n_member++; } } } PyList_SetItem(result, nCol, column_list); } nCol++; } } } } VLAFreeP(row_vla); return result; #endif } void SeekerUpdate(PyMOLGlobals * G) { /* CObject *o = NULL; int s; */ void *hidden = NULL; AtomInfoType *ai; ObjectMolecule *obj; int nRow = 0; int label_mode = 0; int codes = 0; int max_row = 50; int default_color = 0; int align_sele = -1; /* alignment selection */ const int MAXCONSECUTIVEGAPS = 10; CSeqRow *row_vla, *row, *lab = NULL; row_vla = VLACalloc(CSeqRow, 10); /* FIRST PASS: get all the residues represented properly */ label_mode = SettingGetGlobal_i(G, cSetting_seq_view_label_mode); align_sele = ExecutiveGetActiveAlignmentSele(G); while(ExecutiveIterateObjectMolecule(G, &obj, &hidden)) { if(obj->Obj.Enabled && (SettingGet_b(G, obj->Obj.Setting, NULL, cSetting_seq_view)) && (obj->Obj.Name[0] != '_')) { int a; AtomInfoType *last = NULL, *last_segi = NULL, *last_chain = NULL; CoordSet *last_disc = NULL; int last_state; int last_abbr = true; int last_spacer = false; int nCol = 0; int nListEntries = 1; /* first list starts at 1 always... */ int est_col = obj->NAtom / 5 + 1; int est_char = obj->NAtom * 4; int first_atom_in_label; int missing_color = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_fill_color); CoordSet *cs = obj->DiscreteFlag ? NULL : ObjectMoleculeGetCoordSet(obj, std::max(0, obj->getState())); bool atom_in_state; int gapMode = SettingGet_i(G, obj->Obj.Setting, nullptr, cSetting_seq_view_gap_mode); int min_pad = -1; CSeqCol *r1 = NULL, *l1 = NULL; /* *col */ if(nRow >= max_row) break; codes = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_format); if(obj->DiscreteFlag && SettingGet_b(G, obj->Obj.Setting, NULL, cSetting_seq_view_discrete_by_state)) codes = 4; default_color = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_color); /* allocate a row for labels, if present the text for the labels and the residues will line up exactly */ VLACheck(row_vla, CSeqRow, nRow); if((label_mode == 2) || ((label_mode == 1) && (!nRow))) { lab = row_vla + nRow++; lab->txt = VLAlloc(char, est_char); lab->col = VLACalloc(CSeqCol, est_col); lab->label_flag = true; } else { lab = NULL; } VLACheck(row_vla, CSeqRow, nRow); row = row_vla + nRow; if(lab) lab = row - 1; /* critical! */ row->txt = VLAlloc(char, est_char); row->col = VLACalloc(CSeqCol, est_col); row->fill = VLACalloc(CSeqCol, est_col / 8); row->atom_lists = VLACalloc(int, obj->NAtom + est_col + 1); row->atom_lists[0] = -1; /* terminate the blank listQ (IMPORTANT!) */ row->char2col = VLACalloc(int, est_char); row->obj = obj; strcpy(row->name, obj->Obj.Name); row->color = obj->Obj.Color; ai = obj->AtomInfo; /* copy object name onto label row */ if(lab) { int st_len; /* copy label text */ VLACheck(lab->col, CSeqCol, nCol); l1 = lab->col + nCol; l1->start = lab->len; UtilConcatVLA(&lab->txt, &lab->len, "/"); UtilConcatVLA(&lab->txt, &lab->len, obj->Obj.Name); l1->stop = lab->len; st_len = l1->stop - l1->start; if(label_mode == 2) { /* blank equivalent text for sequence row below the fixed label */ VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; UtilFillVLA(&row->txt, &row->len, ' ', st_len); r1->stop = row->len; r1->spacer = true; nCol++; } } if(label_mode < 2) { /* no label rows, so put object name into left-hand column */ /* copy label text */ VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; UtilConcatVLA(&row->txt, &row->len, "/"); UtilConcatVLA(&row->txt, &row->len, obj->Obj.Name); r1->stop = row->len; r1->spacer = true; row->column_label_flag = true; row->title_width = row->len; nCol++; } else if(label_mode == 3) { /* otherwise just insert a blank zero-length column */ VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; UtilConcatVLA(&row->txt, &row->len, ""); r1->stop = row->len; r1->spacer = true; nCol++; } if(lab) { int st_len; /* copy label text */ VLACheck(lab->col, CSeqCol, nCol); l1 = lab->col + nCol; l1->start = lab->len; if(obj->NAtom) { UtilConcatVLA(&lab->txt, &lab->len, "/"); UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->segi)); UtilConcatVLA(&lab->txt, &lab->len, "/"); UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->chain)); UtilConcatVLA(&lab->txt, &lab->len, "/"); } else { UtilConcatVLA(&lab->txt, &lab->len, "///"); } l1->stop = lab->len; st_len = l1->stop - l1->start; last_segi = ai; last_chain = ai; /* blank equivalent text for sequence row below the fixed label */ VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; UtilFillVLA(&row->txt, &row->len, ' ', st_len); r1->stop = row->len; r1->spacer = true; nCol++; } else { /* if no labels, just insert a space in row below */ VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; UtilConcatVLA(&row->txt, &row->len, " "); r1->stop = row->len; r1->spacer = true; nCol++; } last_state = -1; for(a = 0; a < obj->NAtom; a++) { first_atom_in_label = false; if(lab && !AtomInfoSameSegmentP(G, last_segi, ai)) { int st_len; if(row->len < min_pad) { row->len = min_pad; } min_pad = -1; /* copy label text */ VLACheck(lab->col, CSeqCol, nCol); l1 = lab->col + nCol; l1->start = lab->len; UtilConcatVLA(&lab->txt, &lab->len, "/"); UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->segi)); UtilConcatVLA(&lab->txt, &lab->len, "/"); UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->chain)); UtilConcatVLA(&lab->txt, &lab->len, "/"); l1->stop = lab->len; st_len = l1->stop - l1->start; /* blank equivalent text for sequence row */ VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; UtilFillVLA(&row->txt, &row->len, ' ', st_len); r1->stop = row->len; r1->spacer = true; nCol++; last_abbr = false; last_spacer = true; last_segi = ai; last_chain = ai; } else if(lab && !AtomInfoSameChainP(G, last_chain, ai)) { int st_len; if(row->len < min_pad) { row->len = min_pad; } min_pad = -1; /* copy label text */ VLACheck(lab->col, CSeqCol, nCol); l1 = lab->col + nCol; l1->start = lab->len; UtilConcatVLA(&lab->txt, &lab->len, "/"); UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->chain)); UtilConcatVLA(&lab->txt, &lab->len, "/"); l1->stop = lab->len; st_len = l1->stop - l1->start; /* blank equivalent text for sequence row */ VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; UtilFillVLA(&row->txt, &row->len, ' ', st_len); r1->stop = row->len; r1->spacer = true; nCol++; last_abbr = false; last_spacer = true; last_chain = ai; } if(min_pad < 0) { min_pad = row->len + 1; // + strlen(resi) for (int v = ai->resv; v; v /= 10) { min_pad++; } if (ai->inscode) { min_pad++; } } atom_in_state = (cs && a < cs->NAtIndex && cs->AtmToIdx[a] >= 0); int gapsNeeded{0}; if(gapMode != GapMode::NONE && AtomInfoSameChainP(G, ai, last) && (ai->flags & last->flags & cAtomFlag_polymer) && align_sele < 0){ gapsNeeded = ai->resv - last->resv - 1; if(gapsNeeded > 1 && gapMode == GapMode::SINGLE){ gapsNeeded = 1; } } auto push_gap = [&](const std::string& str) { UtilConcatVLA(&row->txt, &row->len, str.c_str()); VLACheck(row->col, CSeqCol, nCol + str.size()); r1 = row->col + nCol; for(int i = 0; i < str.size(); i++){ r1->color = missing_color; r1->spacer = true; r1->stop = r1->start + 1; auto lastStop = r1->stop; nCol++; r1 = row->col + nCol; r1->start = lastStop; } }; switch (codes) { case 0: /* one letter residue codes */ if(!AtomInfoSameResidueP(G, last, ai)) { char abbr[2] = "1"; last = ai; VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; //Only include non-consecutive gaps when not doing alignment if(gapsNeeded > 0 && gapsNeeded <= MAXCONSECUTIVEGAPS){ for(int g = 0; g < gapsNeeded; ++g){ push_gap("-"); } } else if(gapsNeeded > MAXCONSECUTIVEGAPS){ push_gap("---...---"); } if(obj->DiscreteFlag) r1->state = ai->discrete_state; first_atom_in_label = true; // single letter codes for polymer/solvent if (!(ai->flags & (cAtomFlag_organic | cAtomFlag_inorganic))) { abbr[0] = SeekerGetAbbr(G, LexStr(G, ai->resn), 'O', 0); } else { abbr[0] = 0; } r1->hint_no_space = last_abbr || last_spacer; if(!abbr[0]) { if(last_abbr) { UtilConcatVLA(&row->txt, &row->len, " "); r1->start = row->len; } if(ai->resn) UtilConcatVLA(&row->txt, &row->len, LexStr(G, ai->resn)); else UtilConcatVLA(&row->txt, &row->len, "''"); r1->stop = row->len; UtilConcatVLA(&row->txt, &row->len, " "); } else { UtilConcatVLA(&row->txt, &row->len, abbr); r1->is_abbr = true; r1->stop = row->len; } if(!atom_in_state) r1->color = missing_color; else if(default_color < 0) r1->color = SeekerFindColor(G, ai, obj->NAtom - a); else r1->color = default_color; if(align_sele >= 0) { r1->tag = SeekerFindTag(G, ai, align_sele, codes, obj->NAtom - a); } else { r1->tag = 0; } nCol++; last_abbr = abbr[0]; } break; case 1: /* explicit residue codes */ if(!AtomInfoSameResidueP(G, last, ai)) { last = ai; VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; if(obj->DiscreteFlag) r1->state = ai->discrete_state; first_atom_in_label = true; //Only include non-consecutive gaps when not doing alignment if(gapsNeeded > 0 && gapsNeeded <= MAXCONSECUTIVEGAPS){ for(int g = 0; g < gapsNeeded; ++g){ push_gap("--- "); } } else if(gapsNeeded > MAXCONSECUTIVEGAPS){ push_gap("---...--- "); } if(ai->resn) UtilConcatVLA(&row->txt, &row->len, LexStr(G, ai->resn)); else UtilConcatVLA(&row->txt, &row->len, "''"); r1->stop = row->len; if(!atom_in_state) r1->color = missing_color; else if(default_color < 0) r1->color = SeekerFindColor(G, ai, obj->NAtom - a); else r1->color = default_color; if(align_sele >= 0) { r1->tag = SeekerFindTag(G, ai, align_sele, codes, obj->NAtom - a); } else { r1->tag = 0; } UtilConcatVLA(&row->txt, &row->len, " "); nCol++; } break; case 2: /* atom names */ VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; first_atom_in_label = true; if(ai->name) UtilConcatVLA(&row->txt, &row->len, LexStr(G, ai->name)); else UtilConcatVLA(&row->txt, &row->len, "''"); r1->stop = row->len; if(!atom_in_state) r1->color = missing_color; else if(default_color < 0) r1->color = ai->color; else r1->color = default_color; if(align_sele >= 0) { r1->tag = SeekerFindTag(G, ai, align_sele, codes, obj->NAtom - a); } else { r1->tag = 0; } UtilConcatVLA(&row->txt, &row->len, " "); nCol++; break; case 3: /* chains */ if(!AtomInfoSameChainP(G, last, ai)) { last = ai; VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; first_atom_in_label = true; if(ai->chain) UtilConcatVLA(&row->txt, &row->len, LexStr(G, ai->chain)); else UtilConcatVLA(&row->txt, &row->len, "''"); r1->stop = row->len; if(default_color < 0) r1->color = SeekerFindColor(G, ai, obj->NAtom - a); else r1->color = default_color; if(align_sele >= 0) { r1->tag = SeekerFindTag(G, ai, align_sele, codes, obj->NAtom - a); } else { r1->tag = 0; } UtilConcatVLA(&row->txt, &row->len, " "); nCol++; } break; case 4: /* state names */ if(obj->DiscreteFlag) { CoordSet *cs; WordType buf1; if((cs = obj->DiscreteCSet[a]) != last_disc) { last_disc = cs; if(cs) { default_color = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_seq_view_color); VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->start = row->len; r1->color = default_color; first_atom_in_label = true; if(cs->Name[0]) UtilConcatVLA(&row->txt, &row->len, cs->Name); else { sprintf(buf1, "%d", ai->discrete_state); UtilConcatVLA(&row->txt, &row->len, buf1); } r1->stop = row->len; r1->state = ai->discrete_state; UtilConcatVLA(&row->txt, &row->len, " "); nCol++; } } } else { /* non-discrete objects simply get their states enumerated without selections */ if(last_state < 0) { int b; CoordSet *cs; WordType buf1; last_state = 1; first_atom_in_label = true; for(b = 0; b < obj->NCSet; b++) { cs = obj->CSet[b]; if(cs) { default_color = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_seq_view_color); VLACheck(row->col, CSeqCol, nCol); r1 = row->col + nCol; r1->state = b + 1; r1->start = row->len; r1->atom_at = nListEntries + 1; /* tricky & dangerous */ r1->color = default_color; if(cs->Name[0]) UtilConcatVLA(&row->txt, &row->len, cs->Name); else { sprintf(buf1, "%d", b + 1); UtilConcatVLA(&row->txt, &row->len, buf1); } r1->stop = row->len; UtilConcatVLA(&row->txt, &row->len, " "); nCol++; } } } } break; case 5: /* movie frames */ break; } if(first_atom_in_label) { if(nCol > 1) { /* terminate current list, if any */ VLACheck(row->atom_lists, int, nListEntries); row->atom_lists[nListEntries] = -1; nListEntries++; } if(r1) { r1->atom_at = nListEntries; } } VLACheck(row->atom_lists, int, nListEntries); row->atom_lists[nListEntries] = a; nListEntries++; ai++; } if(lab) { /* if(lab->len<row->len) { lab->len = row->len; } */ VLASize(lab->txt, char, lab->len + 1); lab->txt[lab->len] = 0; VLACheck(lab->col, CSeqCol, nCol); /* make sure we've got column records for labels too */ lab->nCol = nCol; /*if(row->len<lab->len) { row->len = lab->len; } */ } VLASize(row->txt, char, row->len + 1); row->txt[row->len] = 0; row->nCol = nCol; /* terminate last atom list */ VLACheck(row->atom_lists, int, nListEntries); row->atom_lists[nListEntries] = -1; nListEntries++; nRow++; } } /* SECOND PASS: align columns to reflect current alignment and fixed labels */ if(nRow) { int a, b; int nCol; int maxCol = 0; int done_flag = false; /* find out the maximum number of columns */ for(a = 0; a < nRow; a++) { row = row_vla + a; nCol = row->nCol; row->accum = 0; /* initialize the accumulators */ row->current = 0; if(maxCol < nCol) maxCol = nCol; } if(align_sele < 0) { /* in the simplest mode, just start each sequence in the same column */ b = 0; while(!done_flag) { int max_offset = 0; done_flag = true; for(a = 0; a < nRow; a++) { row = row_vla + a; if(!row->label_flag) { if(b < row->nCol) { CSeqCol *r1 = row->col + b; done_flag = false; r1->offset = r1->start + row->accum; if(max_offset < r1->offset) max_offset = r1->offset; } } } for(a = 0; a < nRow; a++) { row = row_vla + a; if(!row->label_flag) { if(b < row->nCol) { CSeqCol *r1 = row->col + b; if(b < 3) { if(r1->offset < max_offset) { row->accum += max_offset - r1->offset; } } r1->offset = r1->start + row->accum; } } } b++; } } else { /* in alignment mode, line up the tags */ int stagger = false; /* intialize current columns and get the starting character */ int current = 0; int first = true; switch (SettingGetGlobal_i(G, cSetting_seq_view_unaligned_mode)) { case 0: case 1: case 2: stagger = false; break; default: stagger = true; break; } for(a = 0; a < nRow; a++) { row = row_vla + a; row->cCol = 0; if((!row->label_flag) && ((row->cCol < row->nCol))) { if(current < row->accum) current = row->accum; } } done_flag = false; while(!done_flag) { int hint_tagged_no_space = true; done_flag = true; { { /* insert untagged entries into their own columns */ int untagged_flag = true; int saw_untagged_no_abbr = false; int hint_untagged_space = false; while(untagged_flag) { int space_added = false; int max_width = 0; untagged_flag = false; /* first get the spaces in... */ for(a = 0; a < nRow; a++) { row = row_vla + a; if((!row->label_flag) && (row->cCol < row->nCol)) { CSeqCol *r1 = row->col + row->cCol; if(!r1->tag) { /* not aligned */ int text_len = (r1->stop - r1->start); if((!first) && (!space_added) && (row->cCol > 2) && (codes || (((!r1->is_abbr) && (!r1->spacer))) || hint_untagged_space || (r1->is_abbr && (!r1->hint_no_space)))) { /* insert space */ current++; space_added = true; } if(max_width < text_len) max_width = text_len; } } } /* then do the rest */ for(a = 0; a < nRow; a++) { row = row_vla + a; if((!row->label_flag) && (row->cCol < row->nCol)) { CSeqCol *r1 = row->col + row->cCol; if(!r1->tag) { /* not aligned */ int text_len = (r1->stop - r1->start); untagged_flag = true; done_flag = false; saw_untagged_no_abbr |= (!r1->is_abbr) && (!r1->spacer); first = false; r1->offset = current; r1->unaligned = true; if(!r1->spacer) { int aa; for(aa = 0; aa < nRow; aa++) { /* infill populate other rows with dashes */ if(aa != a) { CSeqRow *row2 = row_vla + aa; if(!row2->label_flag) { if(row2->cCol < row2->nCol) { CSeqCol *r2 = row2->col + row2->cCol; if(stagger || r2->tag || r2->spacer) { VLACheck(row2->fill, CSeqCol, row2->nFill); r2 = row2->fill + row2->nFill; r2->stop = text_len; r2->offset = current; row2->nFill++; } } else { CSeqCol *r2 = row2->col + row2->cCol; VLACheck(row2->fill, CSeqCol, row2->nFill); r2 = row2->fill + row2->nFill; r2->stop = text_len; r2->offset = current; row2->nFill++; } } } } } if(stagger) current += text_len; else if(max_width < text_len) max_width = text_len; } } } if(!stagger) current += max_width; if(saw_untagged_no_abbr) { hint_untagged_space = true; hint_tagged_no_space = false; } else { hint_untagged_space = false; hint_tagged_no_space = true; } saw_untagged_no_abbr = false; for(a = 0; a < nRow; a++) { row = row_vla + a; if((!row->label_flag) && (row->cCol < row->nCol)) { CSeqCol *r1 = row->col + row->cCol; if(!r1->tag) { row->cCol++; } } } } } } { /* next insert match-tagged entries into the same column */ int min_tag = 0; for(a = 0; a < nRow; a++) { row = row_vla + a; if((!row->label_flag) && (row->cCol < row->nCol)) { CSeqCol *r1 = row->col + row->cCol; if(r1->tag && ((min_tag > r1->tag) || (!min_tag))) { min_tag = r1->tag; } } } if(min_tag) { int width, max_width = 0; int space_added = false; int rep; for(rep = 0; rep < 2; rep++) for(a = 0; a < nRow; a++) { row = row_vla + a; if((!row->label_flag) && (row->cCol < row->nCol)) { CSeqCol *r1 = row->col + row->cCol; if(r1->tag == min_tag) { if((!first) && (!space_added) && (codes || (((!r1->is_abbr) && (!r1->spacer))) || (r1->is_abbr && (!(r1->hint_no_space || hint_tagged_no_space))))) { /* insert space */ current++; space_added = true; } done_flag = false; first = false; r1->offset = current; width = (r1->stop - r1->start); if(max_width < width) max_width = width; /* row->cCol++; */ } } } { int aa; for(aa = 0; aa < nRow; aa++) { /* infill populate other rows with dashes */ CSeqRow *row2 = row_vla + aa; if(!row2->label_flag) { if(row2->cCol < row2->nCol) { CSeqCol *r1 = row2->col + row2->cCol; if(r1->tag != min_tag) { CSeqCol *r2; VLACheck(row2->fill, CSeqCol, row2->nFill); r2 = row2->fill + row2->nFill; r2->stop = max_width; r2->offset = current; row2->nFill++; } } else { CSeqCol *r2; VLACheck(row2->fill, CSeqCol, row2->nFill); r2 = row2->fill + row2->nFill; r2->stop = max_width; r2->offset = current; row2->nFill++; } } } } for(a = 0; a < nRow; a++) { row = row_vla + a; if((!row->label_flag) && (row->cCol < row->nCol)) { CSeqCol *r1 = row->col + row->cCol; if(r1->tag == min_tag) { row->cCol++; } } } current += max_width; } } } } for(a = 0; a < nRow; a++) { row = row_vla + a; nCol = row->nCol; if(row->label_flag) lab = row; else { for(b = 0; b < nCol; b++) { CSeqCol *r1 = row->col + b, *l1 = NULL; if(lab) { l1 = lab->col + b; /* if a fixed label is present, get the final offset from the residue line */ if(l1->stop) l1->offset = r1->offset; } } lab = NULL; } } } /* THIRD PASS: fill in labels, based on actual residue spacing */ if(nRow && (codes != 4)) { int a, b, c; int nCol; for(a = 0; a < nRow; a++) { lab = row_vla + a; if(lab->label_flag) { int next_open = 0; int *atom_list; int st_len; int div, sub; int draw_it; int n_skipped = 0; int last_resv = -1; AtomInfoType *last_ai = NULL; ObjectMolecule *obj; AtomInfoType *ai; row = lab + 1; nCol = row->nCol; obj = row->obj; div = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_label_spacing); sub = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_label_start); for(b = 0; b < nCol; b++) { CSeqCol *r1 = row->col + b; CSeqCol *l1 = lab->col + b; ai = NULL; if(r1->atom_at) { atom_list = row->atom_lists + r1->atom_at; if(*atom_list >= 0) ai = obj->AtomInfo + (*atom_list); /* get first atom in list */ } if(l1->stop) { /* if label is already present, just line it up */ l1->offset = r1->offset; } else if((r1->offset >= next_open) && ai) { if((div > 1) && (codes != 2)) { if(!((ai->resv - sub) % div)) draw_it = true; else draw_it = false; } else { draw_it = true; } if(ai->resv != (last_resv + 1)) /* gap in sequence? then draw label ASAP */ draw_it = true; if(n_skipped >= (div + div)) /* don't skip too many without a label! */ draw_it = true; if(AtomInfoSameResidueP(G, last_ai, ai)) /* don't ever draw a residue label twice */ draw_it = false; if(draw_it) { n_skipped = 0; last_ai = ai; l1->start = lab->len; if(codes == 2) { UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->resn)); UtilConcatVLA(&lab->txt, &lab->len, "`"); } { char resi[8]; AtomResiFromResv(resi, sizeof(resi), ai); UtilConcatVLA(&lab->txt, &lab->len, resi); } l1->stop = lab->len; st_len = l1->stop - l1->start + 1; l1->offset = r1->offset; next_open = r1->offset + st_len; /* make sure this label doesn't conflict with a fixed label */ for(c = b + 1; c < nCol; c++) { CSeqCol *l2 = lab->col + c; if(l2->offset && (l2->offset < next_open)) { l1->start = 0; l1->stop = 0; break; } if((c - b) > st_len) /* only search as many columns as characters */ break; } } else n_skipped++; } if(ai) last_resv = ai->resv; } } } } /* FOURTH PASS: simply fill in character offsets */ if(nRow) { int a, b; int nCol; int start, stop; for(a = 0; a < nRow; a++) { row = row_vla + a; row->ext_len = 0; if(!row->label_flag) { nCol = row->nCol; for(b = 0; b < nCol; b++) { CSeqCol *r1 = row->col + b; stop = r1->offset + (r1->stop - r1->start); if(row->ext_len < stop) row->ext_len = stop; } VLACheck(row->char2col, int, row->ext_len); UtilZeroMem(row->char2col, row->ext_len); for(b = 0; b < nCol; b++) { CSeqCol *r1 = row->col + b; int c; start = r1->offset; stop = r1->offset + (r1->stop - r1->start); for(c = start; c < stop; c++) row->char2col[c] = b + 1; } } } } G->Seeker->handler.fClick = SeekerClick; G->Seeker->handler.fRelease = SeekerRelease; G->Seeker->handler.fDrag = SeekerDrag; G->Seeker->handler.fRefresh = SeekerRefresh; SeqSetRowVLA(G, row_vla, nRow); SeqSetHandler(G, &G->Seeker->handler); } int SeekerInit(PyMOLGlobals * G) { CSeeker *I = NULL; if((I = (G->Seeker = Calloc(CSeeker, 1)))) { UtilZeroMem(I, sizeof(CSeeker)); I->drag_row = -1; I->LastClickTime = UtilGetSeconds(G) - 1.0F; return 1; } else { return 0; } } void SeekerFree(PyMOLGlobals * G) { FreeP(G->Seeker); }