/* 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_gl.h" #include "os_std.h" #include "Base.h" #include "Seq.h" #include "main.h" #include "MemoryDebug.h" #include "PyMOLObject.h" #include "Scene.h" #include "Text.h" #include "Seeker.h" #include "Menu.h" #include "Executive.h" #include "Ortho.h" #include "CGO.h" static int SeqFindRowCol(PyMOLGlobals * G, int x, int y, int *row_num_ptr, int *col_num_ptr, int fixed_row) { CSeq *I = G->Seq; int result = 0; int row_num = 0; int col_num = 0; if(I->ScrollBarActive) { y -= DIP2PIXEL(I->ScrollBarWidth); } if(fixed_row >= 0) { row_num = fixed_row; } else { row_num = (y - I->rect.bottom) / DIP2PIXEL(I->LineHeight); row_num = (I->NRow - 1) - row_num; } if((row_num >= 0) && (row_num < I->NRow)) { int char_num; CSeqRow *row; row = I->Row.data() + row_num; char_num = (x - I->rect.left - DIP2PIXEL(I->CharMargin)) / DIP2PIXEL(I->CharWidth); if(row->nCol && !row->label_flag) if(char_num < I->VisSize) { char_num += I->NSkip; if((char_num >= 0) && (char_num < row->ext_len) && (row->char2col)) { col_num = row->char2col[char_num]; if(col_num) { col_num--; if(col_num < row->nCol) { result = true; } else if(fixed_row >= 0) { col_num = row->nCol - 1; result = true; } } } else if(char_num == 0) { col_num = 0; result = true; } else { col_num = row->nCol - 1; result = true; } } } if(result) { *row_num_ptr = row_num; *col_num_ptr = col_num; } return result; } void SeqUpdate(PyMOLGlobals * G) { CSeq *I = G->Seq; if(I->Changed) { SeekerUpdate(G); I->Changed = false; I->Dirty = true; OrthoReshape(G, -1, -1, false); /* careful, this is recursive... */ } if(I->Dirty) { I->Handler->refresh(G, I->Row); I->Dirty = false; } } void CSeq::reshape(int width, int height) { PyMOLGlobals *G = m_G; CSeq *I = G->Seq; Block::reshape(width, height); { /* get current sequence sizes */ int a; I->Size = 0; for(a = 0; a < I->NRow; a++) { if(I->Row[a].ext_len > I->Size) I->Size = I->Row[a].ext_len; } } { int extra; I->VisSize = (I->rect.right - I->rect.left - 1) / DIP2PIXEL(I->CharWidth); /* printf("%d %d %d %d %d\n",cw,I->rect.right,I->rect.left,I->VisSize,I->Size); */ if(I->VisSize < 1) I->VisSize = 1; extra = I->Size - I->VisSize; if(extra <= 0) { I->ScrollBarActive = false; } else { I->ScrollBarActive = true; m_ScrollBar.setLimits(I->Size, I->VisSize); } } } void SeqDirty(PyMOLGlobals * G) { CSeq *I = G->Seq; I->Dirty = true; SceneInvalidate(G); } void SeqChanged(PyMOLGlobals * G) { CSeq *I = G->Seq; I->Changed = true; SceneInvalidate(G); } int CSeq::drag(int x, int y, int mod) { PyMOLGlobals *G = m_G; CSeq *I = G->Seq; int pass = 0; int row_num; int col_num; if(!pass) { if(SeqFindRowCol(G, x, y, &row_num, &col_num, I->LastRow)) { if(I->Handler) I->Handler->drag(G, I->Row, row_num, col_num, mod); OrthoDirty(G); } } return (1); } int CSeq::release(int button, int x, int y, int mod) { PyMOLGlobals *G = m_G; CSeq *I = G->Seq; int pass = 0; if(!pass) { int row_num; int col_num; if(SeqFindRowCol(G, x, y, &row_num, &col_num, I->LastRow)) { if(I->Handler) I->Handler->release(G, I->Row, button, row_num, col_num, mod); OrthoDirty(G); } else { if(I->Handler) I->Handler->release(G, I->Row, button, -1, -1, mod); OrthoDirty(G); } } I->DragFlag = false; I->LastRow = -1; return (1); } int SeqGetHeight(PyMOLGlobals * G) { CSeq *I = G->Seq; int height = 0; if(I->NRow) { height = DIP2PIXEL(I->LineHeight * I->NRow + 4); if(I->ScrollBarActive) height += DIP2PIXEL(I->ScrollBarWidth); } return (height); } void SeqSetHandler(PyMOLGlobals * G, CSeqHandler * handler) { CSeq *I = G->Seq; I->Handler = handler; } int CSeq::click(int button, int x, int y, int mod) { PyMOLGlobals *G = m_G; CSeq *I = G->Seq; int pass = 0; int row_num; int col_num; switch(button) { case P_GLUT_BUTTON_SCROLL_FORWARD: I->m_ScrollBar.moveBy(-1); return 1; case P_GLUT_BUTTON_SCROLL_BACKWARD: I->m_ScrollBar.moveBy(1); return 1; } if(I->ScrollBarActive) { if((y - rect.bottom) < DIP2PIXEL(I->ScrollBarWidth)) { pass = 1; I->m_ScrollBar.click(button, x, y, mod); } } if(!pass) { if(SeqFindRowCol(G, x, y, &row_num, &col_num, -1)) { if(I->Handler) I->Handler->click(G, I->Row, button, row_num, col_num, mod, x, y); I->DragFlag = true; I->LastRow = row_num; OrthoDirty(G); } else { switch (button) { case P_GLUT_RIGHT_BUTTON: { ObjectNameType name; if(ExecutiveGetActiveSeleName(G, name, false, false)) { MenuActivate2Arg(G, x, y + DIP2PIXEL(20), x, y, false, "pick_sele", name, name); } } break; case P_GLUT_LEFT_BUTTON: if(I->Handler) I->Handler->click(G, I->Row, button, -1, -1, mod, x, y); break; } } } return (1); } void CSeq::draw(CGO* orthoCGO) { PyMOLGlobals *G = m_G; CSeq *I = G->Seq; if(G->HaveGUI && G->ValidContext) { int x = rect.left; int y = rect.bottom + DIP2PIXEL(I->ScrollBarMargin) + 1; float bg_color[3] = { 0.f, 0.f, 0.f }, overlay_color[3] = { 1.0F, 1.0F, 1.0F }; int label_color_index = SettingGetGlobal_color(G, cSetting_seq_view_label_color); const float *label_color = ColorGet(G, label_color_index); copy3f(label_color, overlay_color); if (SettingGetGlobal_b(G, cSetting_bg_gradient)){ if(SettingGetGlobal_b(G, cSetting_seq_view_location)) { copy3f(ColorGet(G, SettingGetGlobal_color(G, cSetting_bg_rgb_bottom)), bg_color); } else { copy3f(ColorGet(G, SettingGetGlobal_color(G, cSetting_bg_rgb_top)), bg_color); } } else { copy3f(ColorGet(G, SettingGetGlobal_color(G, cSetting_bg_rgb)), bg_color); } if(!SettingGetGlobal_b(G, cSetting_seq_view_overlay)) { if (orthoCGO){ CGOColorv(orthoCGO, bg_color); } else { glColor3fv(bg_color); } fill(orthoCGO); } if(I->ScrollBarActive) { I->m_ScrollBar.setBox(rect.bottom + DIP2PIXEL(I->ScrollBarWidth), rect.left + DIP2PIXEL(I->ScrollBarMargin), rect.bottom + DIP2PIXEL(2), rect.right - DIP2PIXEL(I->ScrollBarMargin)); I->m_ScrollBar.draw(orthoCGO); y += DIP2PIXEL(I->ScrollBarWidth); I->NSkip = static_cast(I->m_ScrollBar.getValue()); } else { I->NSkip = 0; } if(I->NRow) { /* get current sequence sizes */ int a, b; float black[3] = { 0, 0, 0 }; float blue[3] = { 0.5, 0.5, 1.0 }; const float *cur_color; CSeqRow *row; CSeqCol *col; int xx, yy, ch_wid, pix_wid, tot_len; int y1 = y; int max_len = 0; int n_real = 0; int vis_size = I->VisSize; int first_allowed; int max_title_width = 0; char fill_char; const char *fill_str = SettingGetGlobal_s(G, cSetting_seq_view_fill_char); int unaligned_color_index = SettingGetGlobal_color(G, cSetting_seq_view_unaligned_color); int fill_color_index = SettingGetGlobal_color(G, cSetting_seq_view_fill_color); int unaligned_mode = SettingGetGlobal_i(G, cSetting_seq_view_unaligned_mode); const float *unaligned_color; const float *fill_color; if(unaligned_color_index == -1) { switch (unaligned_mode) { case 3: unaligned_color_index = -1; break; default: unaligned_color_index = fill_color_index; break; } } fill_color = ColorGet(G, fill_color_index); if(unaligned_color_index < 0) { unaligned_color = nullptr; } else { unaligned_color = ColorGet(G, unaligned_color_index); } if(fill_str && fill_str[0]) { fill_char = fill_str[0]; if(fill_char == ' ') fill_char = 0; } else fill_char = 0; /* measure titles */ for(a = I->NRow - 1; a >= 0; a--) { row = I->Row.data() + a; col = row->col.data(); if(row->label_flag || row->column_label_flag) { row->title_width = col->offset + (col->stop - col->start); if(max_title_width < row->title_width) max_title_width = row->title_width; } } /* draw titles */ cur_color = overlay_color; TextSetColor(G, cur_color); for(a = I->NRow - 1; a >= 0; a--) { row = I->Row.data() + a; yy = y1 - 2; col = row->col.data(); if((row->label_flag || row->column_label_flag) && row->nCol) { row->title_width = col->offset + (col->stop - col->start); xx = x + DIP2PIXEL(I->CharMargin) + DIP2PIXEL(I->CharWidth) * (col->offset + (max_title_width - row->title_width)); ch_wid = (col->stop - col->start); pix_wid = DIP2PIXEL(I->CharWidth * ch_wid); tot_len = col->offset + ch_wid - I->NSkip; if(tot_len <= vis_size) { TextDrawSubStrFast(G, row->txt, xx, y1, col->start, ch_wid, orthoCGO); } } y1 += DIP2PIXEL(I->LineHeight); } y1 = y; for(a = I->NRow - 1; a >= 0; a--) { row = I->Row.data() + a; cur_color = overlay_color; if (orthoCGO){ CGOColorv(orthoCGO, cur_color); } else { glColor3fv(cur_color); } yy = y1 - 2; if(max_len < row->ext_len) max_len = row->ext_len; if(!row->label_flag) n_real++; if(row->label_flag) { first_allowed = I->NSkip + max_title_width; } else if(row->column_label_flag) { first_allowed = I->NSkip + max_title_width + 1; } else first_allowed = I->NSkip; for(b = 1; b < row->nCol; b++) { if(row->label_flag && (b > 1)) first_allowed = I->NSkip + max_title_width + 1; col = row->col + b; if(col->offset >= first_allowed) { xx = x + DIP2PIXEL(I->CharMargin) + DIP2PIXEL(I->CharWidth) * (col->offset - I->NSkip); ch_wid = (col->stop - col->start); pix_wid = DIP2PIXEL(I->CharWidth * ch_wid); tot_len = col->offset + ch_wid - I->NSkip; if(tot_len <= vis_size) { if(row->label_flag) { TextSetColor(G, cur_color); if (orthoCGO){ CGOColorv(orthoCGO, cur_color); } else { glColor3fv(cur_color); } } else if(col->unaligned && unaligned_color) { float tmp_color[3]; const float *v = ColorGet(G, col->color); switch (unaligned_mode) { case 1: case 4: average3f(v, bg_color, tmp_color); TextSetColor(G, tmp_color); if (orthoCGO){ CGOColorv(orthoCGO, tmp_color); } else { glColor3fv(tmp_color); } break; case 2: case 5: average3f(v, unaligned_color, tmp_color); TextSetColor(G, tmp_color); if (orthoCGO){ CGOColorv(orthoCGO, tmp_color); } else { glColor3fv(tmp_color); } break; default: TextSetColor(G, unaligned_color); if (orthoCGO){ CGOColorv(orthoCGO, unaligned_color); } else { glColor3fv(unaligned_color); } break; } } else { const float *v = ColorGet(G, col->color); TextSetColor(G, v); if (orthoCGO){ CGOColorv(orthoCGO, v); } else { glColor3fv(v); } } if(col->inverse) { if (orthoCGO){ CGOBegin(orthoCGO, GL_TRIANGLE_STRIP); CGOVertex(orthoCGO, xx, yy, 0.f); CGOVertex(orthoCGO, xx, yy + DIP2PIXEL(I->LineHeight) - 1, 0.f); CGOVertex(orthoCGO, xx + pix_wid, yy, 0.f); CGOVertex(orthoCGO, xx + pix_wid, yy + DIP2PIXEL(I->LineHeight) - 1, 0.f); CGOEnd(orthoCGO); } else { glBegin(GL_POLYGON); glVertex2i(xx, yy); glVertex2i(xx, yy + DIP2PIXEL(I->LineHeight) - 1); glVertex2i(xx + pix_wid, yy + DIP2PIXEL(I->LineHeight) - 1); glVertex2i(xx + pix_wid, yy); glEnd(); } TextSetColor(G, black); } TextDrawSubStrFast(G, row->txt, xx, y1, col->start, ch_wid, orthoCGO); } } } if(fill_char) { TextSetColor(G, fill_color); for(b = 0; b < row->nFill; b++) { col = row->fill + b; if(col->offset >= first_allowed) { xx = x + DIP2PIXEL(I->CharMargin) + DIP2PIXEL(I->CharWidth) * (col->offset - I->NSkip); ch_wid = (col->stop - col->start); pix_wid = DIP2PIXEL(I->CharWidth * ch_wid); tot_len = col->offset + ch_wid - I->NSkip; if(tot_len <= vis_size) { TextDrawCharRepeat(G, fill_char, xx, y1, col->start, ch_wid, orthoCGO); } } } } y1 += DIP2PIXEL(I->LineHeight); } if(I->Handler->box_active) { int box_row = I->Handler->box_row; if((box_row >= 0) && (box_row < I->NRow)) { int start_col = I->Handler->box_start_col; int stop_col = I->Handler->box_stop_col; if(start_col > stop_col) { int tmp = stop_col; stop_col = start_col; start_col = tmp; } row = I->Row.data() + box_row; if((start_col >= 0) && (start_col < row->nCol) && (stop_col >= 0) && (stop_col < row->nCol)) { int xx2; CSeqCol *col2; col = row->col + start_col; col2 = row->col + stop_col; /* trim spacers (if any) */ while(col->spacer && (start_col < stop_col)) { start_col++; col = row->col + start_col; } while(col2->spacer && (start_col < stop_col)) { stop_col--; col2 = row->col + stop_col; } yy = y + ((I->NRow - 1) - box_row) * DIP2PIXEL(I->LineHeight) - 2; xx = x + DIP2PIXEL(I->CharMargin) + DIP2PIXEL(I->CharWidth) * (col->offset - I->NSkip); xx2 = x + DIP2PIXEL(I->CharMargin) + DIP2PIXEL(I->CharWidth) * (col2->offset + (col2->stop - col2->start) - I->NSkip); if (orthoCGO){ CGOColorv(orthoCGO, overlay_color); CGOLineAsTriangleStrips(orthoCGO, xx, yy, xx2, yy + DIP2PIXEL(I->LineHeight) - 2); /* TODO: need to convert to triangles CGOBegin(orthoCGO, GL_LINE_LOOP); CGOVertex(orthoCGO, xx, yy); CGOVertex(orthoCGO, xx, yy + I->LineHeight - 2); CGOVertex(orthoCGO, xx2, yy + I->LineHeight - 2); CGOVertex(orthoCGO, xx2, yy); CGOEnd(orthoCGO);*/ } else { glBegin(GL_LINE_LOOP); glVertex2i(xx, yy); glVertex2i(xx, yy + DIP2PIXEL(I->LineHeight) - 2); glVertex2i(xx2, yy + DIP2PIXEL(I->LineHeight) - 2); glVertex2i(xx2, yy); glEnd(); } } } } if(I->ScrollBarActive) { int real_count = n_real; int mode = 0; float width = (float) (rect.right - rect.left); float start = 0, stop; int right = 0; float bot, top, cent; float height = (float) DIP2PIXEL(I->ScrollBarWidth - I->ScrollBarMargin); int last_color = -1; cur_color = blue; for(a = 0; a < I->NRow; a++) { row = I->Row.data() + a; if(!row->label_flag) { top = rect.bottom + DIP2PIXEL(I->ScrollBarMargin) + (height * real_count) / n_real; real_count--; bot = rect.bottom + DIP2PIXEL(I->ScrollBarMargin) + (height * real_count) / n_real; mode = 0; for(b = 0; b < row->nCol; b++) { col = row->col + b; if(col->inverse && (!mode)) { start = (width * col->offset) / max_len; right = col->offset + (col->stop - col->start); mode = 1; last_color = col->color; if(row->label_flag) cur_color = overlay_color; else cur_color = ColorGet(G, col->color); /* is this safe? should be for single-threading */ } else if((!col->inverse) && (mode)) { if(b) { stop = (width * (col[-1].offset + col[-1].stop - col[-1].start)) / max_len; } else { stop = (width * col->offset) / max_len; } if((stop - start) < 1.0F) { cent = (stop + start) * 0.5F; start = cent - 0.5F; stop = cent + 0.5F; } if (orthoCGO){ CGOColorv(orthoCGO, cur_color); CGOBegin(orthoCGO, GL_TRIANGLE_STRIP); CGOVertex(orthoCGO, start, bot, 0.f); CGOVertex(orthoCGO, start, top, 0.f); CGOVertex(orthoCGO, stop, bot, 0.f); CGOVertex(orthoCGO, stop, top, 0.f); CGOEnd(orthoCGO); } else { glColor3fv(cur_color); glBegin(GL_POLYGON); glVertex2f(start, bot); glVertex2f(start, top); glVertex2f(stop, top); glVertex2f(stop, bot); glEnd(); } mode = 0; } else if(col->inverse && mode) { if(last_color != col->color) { if(b) { stop = (width * (col[-1].offset + col[-1].stop - col[-1].start)) / max_len; } else { stop = (width * col->offset) / max_len; } if((stop - start) < 1.0F) { cent = (stop + start) * 0.5F; start = cent - 0.5F; stop = cent + 0.5F; } if (orthoCGO){ CGOColorv(orthoCGO, cur_color); CGOBegin(orthoCGO, GL_TRIANGLE_STRIP); CGOVertex(orthoCGO, start, bot, 0.f); CGOVertex(orthoCGO, start, top, 0.f); CGOVertex(orthoCGO, stop, bot, 0.f); CGOVertex(orthoCGO, stop, top, 0.f); CGOEnd(orthoCGO); } else { glColor3fv(cur_color); glBegin(GL_POLYGON); glVertex2f(start, bot); glVertex2f(start, top); glVertex2f(stop, top); glVertex2f(stop, bot); glEnd(); } start = (width * col->offset) / max_len; last_color = col->color; if(row->label_flag) cur_color = overlay_color; else cur_color = ColorGet(G, col->color); /* is this safe? should be for single-threading */ } right = col->offset + (col->stop - col->start); } } if(mode) { stop = width * right / max_len; if((stop - start) < 1.0F) { cent = (stop + start) * 0.5F; start = cent - 0.5F; stop = cent + 0.5F; } if (orthoCGO){ CGOColorv(orthoCGO, cur_color); CGOBegin(orthoCGO, GL_TRIANGLE_STRIP); CGOVertex(orthoCGO, start, bot, 0.f); CGOVertex(orthoCGO, start, top, 0.f); CGOVertex(orthoCGO, stop, bot, 0.f); CGOVertex(orthoCGO, stop, top, 0.f); CGOEnd(orthoCGO); } else { glColor3fv(cur_color); glBegin(GL_POLYGON); glVertex2f(start, bot); glVertex2f(start, top); glVertex2f(stop, top); glVertex2f(stop, bot); glEnd(); } } } } I->m_ScrollBar.drawHandle(0.35F, orthoCGO); } } } } int SeqInit(PyMOLGlobals * G) { CSeq *I = nullptr; if((I = (G->Seq = new CSeq(G)))) { I->active = true; I->TextColor[0] = 1.0; I->TextColor[1] = 0.75; I->TextColor[2] = 0.75; OrthoAttach(G, I, cOrthoTool); I->m_ScrollBar.setValue(0); return 1; } else return 0; } static void SeqPurgeRowVLA(PyMOLGlobals * G) { G->Seq->Row.clear(); } void SeqSetRow(PyMOLGlobals * G, std::vector&& row, int nRow) { CSeq *I = G->Seq; I->Row = std::move(row); I->NRow = nRow; } void SeqFree(PyMOLGlobals * G) { DeleteP(G->Seq); } Block *SeqGetBlock(PyMOLGlobals * G) { CSeq *I = G->Seq; { return (I); } }