mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
3172 lines
88 KiB
C++
3172 lines
88 KiB
C++
|
|
/*
|
|
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 <array>
|
|
#include <memory>
|
|
#include <queue>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "os_python.h"
|
|
|
|
#include "os_gl.h"
|
|
#include "os_predef.h"
|
|
#include "os_std.h"
|
|
|
|
#include "ButMode.h"
|
|
#include "CGO.h"
|
|
#include "Control.h"
|
|
#include "Err.h"
|
|
#include "Executive.h"
|
|
#include "Feedback.h"
|
|
#include "File.h"
|
|
#include "ImageUtils.h"
|
|
#include "ListMacros.h"
|
|
#include "MemoryDebug.h"
|
|
#include "Movie.h"
|
|
#include "MyPNG.h"
|
|
#include "Ortho.h"
|
|
#include "P.h"
|
|
#include "Pop.h"
|
|
#include "PyMOL.h"
|
|
#include "PyMOLOptions.h"
|
|
#include "Scene.h"
|
|
#include "Seq.h"
|
|
#include "Setting.h"
|
|
#include "ShaderMgr.h"
|
|
#include "Text.h"
|
|
#include "Util.h"
|
|
#include "Vector.h"
|
|
#include "Version.h"
|
|
#include "Wizard.h"
|
|
#include "main.h"
|
|
|
|
#ifdef _PYMOL_OPENVR
|
|
#include "OpenVRMode.h"
|
|
#endif
|
|
|
|
#define OrthoSaveLines 0xFF
|
|
#define OrthoHistoryLines 0xFF
|
|
|
|
#define cOrthoCharWidth DIP2PIXEL(8)
|
|
#define cOrthoLeftMargin DIP2PIXEL(3)
|
|
#define cOrthoBottomMargin DIP2PIXEL(5)
|
|
|
|
#define CMD_QUEUE_MASK 0x3
|
|
|
|
class COrtho
|
|
{
|
|
public:
|
|
std::vector<Block*> Blocks{};
|
|
Block *GrabbedBy{}, *ClickedIn{};
|
|
int X{}, Y{}, Height{}, Width{};
|
|
int LastX{}, LastY{}, LastModifiers{};
|
|
int ActiveButton{};
|
|
int InputFlag{}; /* whether or not we have active input on the line */
|
|
|
|
OrthoLineType Line[OrthoSaveLines + 1]{};
|
|
OrthoLineType History[OrthoHistoryLines + 1]{};
|
|
int HistoryLine{}, HistoryView{};
|
|
int CurLine{}, CurChar{}, PromptChar{}, CursorChar{};
|
|
int AutoOverlayStopLine{};
|
|
char Prompt[255]{};
|
|
int ShowLines{};
|
|
char Saved[OrthoLineLength]{};
|
|
int SavedPC{}, SavedCC{};
|
|
float TextColor[3]{}, OverlayColor[3]{}, WizardBackColor[3]{},
|
|
WizardTextColor[3]{};
|
|
int DirtyFlag{};
|
|
double BusyLast{}, BusyLastUpdate{};
|
|
int BusyStatus[4]{};
|
|
char BusyMessage[255]{};
|
|
char* WizardPromptVLA{};
|
|
int SplashFlag{};
|
|
int HaveSeqViewer{};
|
|
BlockRect LoopRect{};
|
|
int LoopFlag{};
|
|
int cmdNestLevel{};
|
|
std::array<std::queue<std::string>, CMD_QUEUE_MASK + 1> cmdQueue;
|
|
std::queue<std::string>* cmdActiveQueue;
|
|
int cmdActiveBusy{};
|
|
std::queue<std::string> feedback;
|
|
int Pushed{};
|
|
std::vector<std::function<void()>> deferred; // Ortho manages DeferredObjs
|
|
OrthoRenderMode RenderMode = OrthoRenderMode::Main;
|
|
Rect2D Viewport;
|
|
int WrapXFlag{};
|
|
double DrawTime{}, LastDraw{};
|
|
ClickSide WrapClickSide = ClickSide::None; /* ugly kludge for finding click
|
|
side in geowall stereo mode */
|
|
|
|
/* packing information */
|
|
int WizardHeight{};
|
|
int TextBottom{};
|
|
|
|
int IssueViewportWhenReleased{};
|
|
std::size_t bgTextureID{};
|
|
bool bgTextureNeedsUpdate{};
|
|
CGO* bgCGO{};
|
|
int bgWidth = 0, bgHeight = 0;
|
|
std::shared_ptr<pymol::Image>
|
|
bgData; // this is the image data set from CMol, takes precedence of
|
|
// bg_gradient or bg_image_filename
|
|
CGO *orthoCGO{}, *orthoFastCGO{};
|
|
|
|
/**
|
|
* Finds last block located and coordinate (x, y)
|
|
* @param x cursor X location
|
|
* @param y cursor Y location
|
|
* @return pointer to last block located at (x, y)
|
|
*/
|
|
Block* findBlock(int x, int y);
|
|
|
|
public:
|
|
/**
|
|
* Draws all blocks
|
|
* @param orthoCGO CGO to append to
|
|
*/
|
|
void draw(CGO* orthoCGO);
|
|
|
|
/**
|
|
* Draws all blocks
|
|
* @param orthoCGO CGO to append to
|
|
* @return true if anything was drawn to CGO
|
|
*/
|
|
bool fastDraw(CGO* orthoCGO);
|
|
};
|
|
|
|
bool OrthoBackgroundDataIsSet(const COrtho& ortho)
|
|
{
|
|
return (ortho.bgData && (!ortho.bgData->empty()));
|
|
}
|
|
|
|
std::shared_ptr<pymol::Image> OrthoBackgroundDataGet(const COrtho& ortho)
|
|
{
|
|
return ortho.bgData;
|
|
}
|
|
|
|
std::pair<int, int> OrthoGetSize(const COrtho& ortho)
|
|
{
|
|
return std::make_pair(ortho.Width, ortho.Height);
|
|
}
|
|
|
|
Extent2D OrthoGetExtent(PyMOLGlobals* G)
|
|
{
|
|
auto I = G->Ortho;
|
|
return Extent2D{static_cast<std::uint32_t>(I->Width),
|
|
static_cast<std::uint32_t>(I->Height)};
|
|
}
|
|
|
|
static void OrthoSetExtent(PyMOLGlobals* G, const Extent2D& extent)
|
|
{
|
|
auto I = G->Ortho;
|
|
I->Width = extent.width;
|
|
I->Height = extent.height;
|
|
}
|
|
|
|
std::pair<int, int> OrthoGetBackgroundSize(const COrtho& ortho)
|
|
{
|
|
if (ortho.bgData) {
|
|
return ortho.bgData->getSize();
|
|
} else {
|
|
return std::make_pair(ortho.bgWidth, ortho.bgHeight);
|
|
}
|
|
}
|
|
|
|
static void OrthoParseCurrentLine(PyMOLGlobals* G);
|
|
|
|
#define cBusyWidth 240
|
|
#define cBusyHeight 60
|
|
#define cBusyMargin 10
|
|
#define cBusyBar 10
|
|
#define cBusySpacing 15
|
|
|
|
#define cBusyUpdate 0.2
|
|
|
|
#define cWizardTopMargin 15
|
|
#define cWizardLeftMargin 15
|
|
#define cWizardBorder 7
|
|
|
|
static int get_wrap_x(int x, int* last_x, int width, ClickSide* click_side)
|
|
{
|
|
int width_2 = width / 2;
|
|
int width_3 = width / 3;
|
|
if (!last_x) {
|
|
if (x > width_2) {
|
|
x -= width_2;
|
|
if (click_side)
|
|
*click_side = ClickSide::Right;
|
|
} else {
|
|
if (click_side)
|
|
*click_side = ClickSide::Left;
|
|
}
|
|
} else {
|
|
if ((x - (*last_x)) > width_3) {
|
|
x -= width_2;
|
|
if (click_side)
|
|
*click_side = ClickSide::Right;
|
|
} else if (((*last_x) - x) > width_3) {
|
|
x += width_2;
|
|
if (click_side)
|
|
*click_side = ClickSide::Right;
|
|
} else {
|
|
if (click_side)
|
|
*click_side = ClickSide::Left;
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* [[deprecated("Use CShaderMgr::setDrawBuffer() instead.")]]
|
|
*/
|
|
void OrthoDrawBuffer(PyMOLGlobals* G, GLenum mode)
|
|
{
|
|
G->ShaderMgr->setDrawBuffer(mode);
|
|
}
|
|
|
|
int OrthoGetDirty(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
return I->DirtyFlag;
|
|
}
|
|
|
|
OrthoRenderMode OrthoGetRenderMode(PyMOLGlobals* G)
|
|
{
|
|
return G->Ortho->RenderMode;
|
|
}
|
|
|
|
void OrthoSetLoopRect(PyMOLGlobals* G, int flag, BlockRect* rect)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->LoopRect = (*rect);
|
|
I->LoopFlag = flag;
|
|
OrthoInvalidateDoDraw(G);
|
|
OrthoDirty(G);
|
|
}
|
|
|
|
int OrthoDeferredWaiting(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
return (!I->deferred.empty());
|
|
}
|
|
|
|
void OrthoExecDeferred(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
/* execute all deferred actions that happened to require a
|
|
* valid OpenGL context (such as atom picks, etc.) */
|
|
for (const auto& d : I->deferred) {
|
|
d();
|
|
}
|
|
I->deferred.clear();
|
|
}
|
|
|
|
void OrthoDefer(PyMOLGlobals* G, std::function<void()>&& D)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->deferred.emplace_back(std::move(D));
|
|
OrthoDirty(G);
|
|
}
|
|
|
|
int OrthoGetWidth(PyMOLGlobals* G)
|
|
{
|
|
if (G) {
|
|
COrtho* I = G->Ortho;
|
|
return (I->Width);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int OrthoGetHeight(PyMOLGlobals* G)
|
|
{
|
|
if (G) {
|
|
COrtho* I = G->Ortho;
|
|
return (I->Height);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoFakeDrag(PyMOLGlobals* G)
|
|
{ /* for timing-based events, such as pop-ups */
|
|
COrtho* I = G->Ortho;
|
|
if (I->GrabbedBy)
|
|
OrthoDrag(G, I->LastX, I->LastY, I->LastModifiers);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
void OrthoSetWizardPrompt(PyMOLGlobals* G, char* vla)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
VLAFreeP(I->WizardPromptVLA);
|
|
I->WizardPromptVLA = vla;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoSpecial(PyMOLGlobals* G, int k, int x, int y, int mod)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
int curLine = I->CurLine & OrthoSaveLines;
|
|
int cursorMoved = false;
|
|
|
|
PRINTFB(G, FB_Ortho, FB_Blather)
|
|
" OrthoSpecial: %c (%d), x %d y %d, mod %d\n", k, k, x, y, mod ENDFB(G);
|
|
|
|
switch (k) {
|
|
case P_GLUT_KEY_DOWN:
|
|
if (I->CurChar && (I->HistoryView == I->HistoryLine)) {
|
|
strcpy(I->History[I->HistoryLine], I->Line[curLine] + I->PromptChar);
|
|
}
|
|
I->HistoryView = (I->HistoryView + 1) & OrthoHistoryLines;
|
|
strcpy(I->Line[curLine], I->Prompt);
|
|
I->PromptChar = strlen(I->Prompt);
|
|
if (I->History[I->HistoryView][0]) {
|
|
strcat(I->Line[curLine], I->History[I->HistoryView]);
|
|
I->CurChar = strlen(I->Line[curLine]);
|
|
} else {
|
|
I->CurChar = I->PromptChar;
|
|
}
|
|
I->InputFlag = 1;
|
|
I->CursorChar = -1;
|
|
cursorMoved = true;
|
|
break;
|
|
case P_GLUT_KEY_UP:
|
|
if (I->CurChar && (I->HistoryView == I->HistoryLine)) {
|
|
strcpy(I->History[I->HistoryLine], I->Line[curLine] + I->PromptChar);
|
|
}
|
|
I->HistoryView = (I->HistoryView - 1) & OrthoHistoryLines;
|
|
strcpy(I->Line[curLine], I->Prompt);
|
|
I->PromptChar = strlen(I->Prompt);
|
|
if (I->History[I->HistoryView][0]) {
|
|
strcat(I->Line[curLine], I->History[I->HistoryView]);
|
|
I->CurChar = strlen(I->Line[curLine]);
|
|
} else {
|
|
I->CurChar = I->PromptChar;
|
|
}
|
|
I->CursorChar = -1;
|
|
I->InputFlag = 1;
|
|
cursorMoved = true;
|
|
break;
|
|
case P_GLUT_KEY_LEFT:
|
|
if (I->CursorChar >= 0) {
|
|
I->CursorChar--;
|
|
} else {
|
|
I->CursorChar = I->CurChar - 1;
|
|
}
|
|
if (I->CursorChar < I->PromptChar)
|
|
I->CursorChar = I->PromptChar;
|
|
cursorMoved = true;
|
|
break;
|
|
case P_GLUT_KEY_RIGHT:
|
|
if (I->CursorChar >= 0) {
|
|
I->CursorChar++;
|
|
} else {
|
|
I->CursorChar = I->CurChar - 1;
|
|
}
|
|
if ((unsigned) I->CursorChar > strlen(I->Line[curLine]))
|
|
I->CursorChar = strlen(I->Line[curLine]);
|
|
cursorMoved = true;
|
|
break;
|
|
}
|
|
if (cursorMoved) {
|
|
OrthoInvalidateDoDraw(G);
|
|
}
|
|
OrthoDirty(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int OrthoTextVisible(PyMOLGlobals* G)
|
|
{
|
|
return (SettingGetGlobal_i(G, cSetting_internal_feedback) ||
|
|
SettingGetGlobal_b(G, cSetting_text) ||
|
|
SettingGetGlobal_i(G, cSetting_overlay));
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
int OrthoArrowsGrabbed(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
return ((I->CurChar > I->PromptChar) && OrthoTextVisible(G));
|
|
/* arrows can't be grabbed if text isn't visible */
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int OrthoGetOverlayStatus(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
int overlay = SettingGetGlobal_i(G, cSetting_overlay);
|
|
if (!overlay) {
|
|
if (SettingGetGlobal_i(G, cSetting_auto_overlay) > 0) {
|
|
if (I->CurLine != I->AutoOverlayStopLine) {
|
|
overlay = -1; /* signal auto overlay */
|
|
}
|
|
}
|
|
}
|
|
return overlay;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoRemoveAutoOverlay(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->AutoOverlayStopLine = I->CurLine;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoRemoveSplash(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->SplashFlag = false;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoCommandNest(PyMOLGlobals* G, int dir)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->cmdNestLevel += dir;
|
|
{
|
|
int level = I->cmdNestLevel;
|
|
if (level < 0)
|
|
level = 0;
|
|
if (level > CMD_QUEUE_MASK)
|
|
level = CMD_QUEUE_MASK;
|
|
I->cmdActiveQueue = &I->cmdQueue[level];
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
bool OrthoCommandIsEmpty(COrtho& ortho)
|
|
{
|
|
return ortho.cmdActiveQueue->empty();
|
|
}
|
|
|
|
/*========================================================================*/
|
|
std::string OrthoCommandOut(COrtho& ortho)
|
|
{
|
|
std::string str;
|
|
if (ortho.cmdActiveQueue) {
|
|
str = std::move(ortho.cmdActiveQueue->front());
|
|
ortho.cmdActiveQueue->pop();
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int OrthoCommandWaiting(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
return (I->cmdActiveBusy || !OrthoCommandIsEmpty(*I));
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoClear(PyMOLGlobals* G)
|
|
{
|
|
int a;
|
|
COrtho* I = G->Ortho;
|
|
for (a = 0; a <= OrthoSaveLines; a++)
|
|
I->Line[a][0] = 0;
|
|
OrthoNewLine(G, nullptr, true);
|
|
OrthoRestorePrompt(G);
|
|
OrthoInvalidateDoDraw(G);
|
|
OrthoDirty(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoFeedbackIn(PyMOLGlobals* G, const char* buffer)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
if (G->Option->pmgui) {
|
|
I->feedback.emplace(buffer);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
// For now keep G here for Settings
|
|
std::string OrthoFeedbackOut(PyMOLGlobals* G, COrtho& ortho)
|
|
{
|
|
std::string buffer;
|
|
if (ortho.feedback.empty()) {
|
|
return buffer;
|
|
}
|
|
buffer = std::move(ortho.feedback.front());
|
|
ortho.feedback.pop();
|
|
if (!SettingGetGlobal_b(G, cSetting_colored_feedback)) {
|
|
UtilStripANSIEscapes(buffer);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoDirty(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoDirty: called.\n" ENDFD;
|
|
if (!I->DirtyFlag) {
|
|
I->DirtyFlag = true;
|
|
}
|
|
PyMOL_NeedRedisplay(G->PyMOL);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoBusyMessage(PyMOLGlobals* G, const char* message)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
if (strlen(message) < 255)
|
|
strcpy(I->BusyMessage, message);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoBusySlow(PyMOLGlobals* G, int progress, int total)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
double time_yet = (-I->BusyLastUpdate) + UtilGetSeconds(G);
|
|
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoBusySlow-DEBUG: progress %d total %d\n", progress, total ENDFD;
|
|
I->BusyStatus[0] = progress;
|
|
I->BusyStatus[1] = total;
|
|
if (SettingGetGlobal_b(G, cSetting_show_progress) && (time_yet > 0.15F)) {
|
|
if (PyMOL_GetBusy(G->PyMOL, false)) { /* harmless race condition */
|
|
#ifndef _PYMOL_NOPY
|
|
int blocked = PAutoBlock(G);
|
|
if (PLockStatusAttempt(G)) {
|
|
#endif
|
|
PyMOL_SetProgress(G->PyMOL, PYMOL_PROGRESS_SLOW, progress, total);
|
|
I->BusyLastUpdate = UtilGetSeconds(G);
|
|
|
|
#ifndef _PYMOL_NOPY
|
|
PUnlockStatus(G);
|
|
}
|
|
PAutoUnblock(G, blocked);
|
|
#endif
|
|
}
|
|
OrthoBusyDraw(G, false);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoBusyFast(PyMOLGlobals* G, int progress, int total)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
double time_yet = (-I->BusyLastUpdate) + UtilGetSeconds(G);
|
|
short finished = progress == total;
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoBusyFast-DEBUG: progress %d total %d\n", progress, total ENDFD;
|
|
I->BusyStatus[2] = progress;
|
|
I->BusyStatus[3] = total;
|
|
if (finished ||
|
|
(SettingGetGlobal_b(G, cSetting_show_progress) && (time_yet > 0.15F))) {
|
|
if (PyMOL_GetBusy(G->PyMOL, false) ||
|
|
finished) { /* harmless race condition */
|
|
#ifndef _PYMOL_NOPY
|
|
int blocked = PAutoBlock(G);
|
|
if (PLockStatusAttempt(G)) {
|
|
#endif
|
|
PyMOL_SetProgress(G->PyMOL, PYMOL_PROGRESS_FAST, progress, total);
|
|
I->BusyLastUpdate = UtilGetSeconds(G);
|
|
#ifndef _PYMOL_NOPY
|
|
PUnlockStatus(G);
|
|
}
|
|
PAutoUnblock(G, blocked);
|
|
#endif
|
|
}
|
|
OrthoBusyDraw(G, false);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoBusyPrime(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
int a;
|
|
for (a = 0; a < 4; a++)
|
|
I->BusyStatus[a] = 0;
|
|
I->BusyMessage[0] = 0;
|
|
I->BusyLast = UtilGetSeconds(G);
|
|
I->BusyLastUpdate = UtilGetSeconds(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoBusyDraw(PyMOLGlobals* G, int force)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
double now;
|
|
double busyTime;
|
|
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoBusyDraw: entered.\n" ENDFD;
|
|
now = UtilGetSeconds(G);
|
|
busyTime = (-I->BusyLast) + now;
|
|
if (SettingGetGlobal_b(G, cSetting_show_progress) &&
|
|
(force || (busyTime > cBusyUpdate))) {
|
|
|
|
I->BusyLast = now;
|
|
if (PIsGlutThread()) {
|
|
|
|
if (G->HaveGUI &&
|
|
G->ValidContext
|
|
// only draw into GL_FRONT if default draw buffer is GL_BACK
|
|
// (not the case for QOpenGLWidget)
|
|
&& G->ShaderMgr->defaultBackbuffer.drawBuffer == GL_BACK) {
|
|
char* c;
|
|
int x, y;
|
|
float white[3] = {1, 1, 1};
|
|
int draw_both = SceneMustDrawBoth(G);
|
|
OrthoPushMatrix(G);
|
|
{
|
|
int pass = 0;
|
|
SceneGLClear(G, GL_DEPTH_BUFFER_BIT);
|
|
while (1) {
|
|
if (draw_both) {
|
|
if (!pass)
|
|
OrthoDrawBuffer(G, GL_FRONT_LEFT);
|
|
else
|
|
OrthoDrawBuffer(G, GL_FRONT_RIGHT);
|
|
} else {
|
|
OrthoDrawBuffer(G, GL_FRONT); /* draw into the front buffer */
|
|
}
|
|
|
|
#ifndef PURE_OPENGL_ES_2
|
|
glColor3f(0.f, 0.f, 0.f); // black
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glVertex2i(0, I->Height);
|
|
glVertex2i(cBusyWidth, I->Height);
|
|
glVertex2i(0, I->Height - cBusyHeight);
|
|
glVertex2i(cBusyWidth, I->Height - cBusyHeight);
|
|
glEnd();
|
|
glColor3fv(white);
|
|
#endif
|
|
y = I->Height - cBusyMargin;
|
|
c = I->BusyMessage;
|
|
if (*c) {
|
|
TextSetColor(G, white);
|
|
TextSetPos2i(G, cBusyMargin, y - (cBusySpacing / 2));
|
|
TextDrawStr(G, c, nullptr);
|
|
y -= cBusySpacing;
|
|
}
|
|
|
|
if (I->BusyStatus[1]) {
|
|
glBegin(GL_LINE_LOOP);
|
|
glVertex2i(cBusyMargin, y);
|
|
glVertex2i(cBusyWidth - cBusyMargin, y);
|
|
glVertex2i(cBusyWidth - cBusyMargin, y - cBusyBar);
|
|
glVertex2i(cBusyMargin, y - cBusyBar);
|
|
glEnd();
|
|
glColor3fv(white);
|
|
x = (I->BusyStatus[0] * (cBusyWidth - 2 * cBusyMargin) /
|
|
I->BusyStatus[1]) +
|
|
cBusyMargin;
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glVertex2i(cBusyMargin, y);
|
|
glVertex2i(x, y);
|
|
glVertex2i(cBusyMargin, y - cBusyBar);
|
|
glVertex2i(x, y - cBusyBar);
|
|
glEnd();
|
|
y -= cBusySpacing;
|
|
}
|
|
|
|
if (I->BusyStatus[3]) {
|
|
glColor3fv(white);
|
|
glBegin(GL_LINE_LOOP);
|
|
glVertex2i(cBusyMargin, y);
|
|
glVertex2i(cBusyWidth - cBusyMargin, y);
|
|
glVertex2i(cBusyWidth - cBusyMargin, y - cBusyBar);
|
|
glVertex2i(cBusyMargin, y - cBusyBar);
|
|
glEnd();
|
|
x = (I->BusyStatus[2] * (cBusyWidth - 2 * cBusyMargin) /
|
|
I->BusyStatus[3]) +
|
|
cBusyMargin;
|
|
glColor3fv(white);
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glVertex2i(cBusyMargin, y);
|
|
glVertex2i(x, y);
|
|
glVertex2i(cBusyMargin, y - cBusyBar);
|
|
glVertex2i(x, y - cBusyBar);
|
|
glEnd();
|
|
y -= cBusySpacing;
|
|
}
|
|
if (!draw_both)
|
|
break;
|
|
if (pass > 1)
|
|
break;
|
|
pass++;
|
|
}
|
|
|
|
glFlush();
|
|
glFinish();
|
|
|
|
if (draw_both)
|
|
OrthoDrawBuffer(G, GL_BACK_LEFT);
|
|
else
|
|
OrthoDrawBuffer(G, GL_BACK);
|
|
}
|
|
OrthoPopMatrix(G);
|
|
OrthoDirty(G);
|
|
}
|
|
}
|
|
}
|
|
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoBusyDraw: leaving...\n" ENDFD;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoRestorePrompt(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
int curLine;
|
|
if (!I->InputFlag) {
|
|
if (I->Saved[0]) {
|
|
if (I->CurChar) {
|
|
OrthoNewLine(G, nullptr, true);
|
|
}
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
strcpy(I->Line[curLine], I->Saved);
|
|
I->Saved[0] = 0;
|
|
I->CurChar = I->SavedCC;
|
|
I->PromptChar = I->SavedPC;
|
|
} else {
|
|
if (I->CurChar)
|
|
OrthoNewLine(G, I->Prompt, true);
|
|
else {
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
strcpy(I->Line[curLine], I->Prompt);
|
|
I->CurChar = (I->PromptChar = strlen(I->Prompt));
|
|
}
|
|
}
|
|
I->InputFlag = 1;
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static void OrthoKeyControl(PyMOLGlobals* G, unsigned char k)
|
|
{
|
|
char buffer[OrthoLineLength];
|
|
|
|
/* safer... */
|
|
|
|
sprintf(buffer, "cmd._ctrl(chr(%d))", k);
|
|
/* sprintf(buffer,"_ctrl %c",k); */
|
|
PLog(G, buffer, cPLog_pym);
|
|
PParse(G, buffer);
|
|
PFlush(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static void OrthoKeyCmmd(PyMOLGlobals* G, unsigned char k)
|
|
{
|
|
char buffer[OrthoLineLength];
|
|
|
|
/* safer... */
|
|
|
|
sprintf(buffer, "cmd._cmmd(chr(%d))", k);
|
|
/* sprintf(buffer,"_ctrl %c",k); */
|
|
PLog(G, buffer, cPLog_pym);
|
|
PParse(G, buffer);
|
|
PFlush(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static void OrthoKeyCtSh(PyMOLGlobals* G, unsigned char k)
|
|
{
|
|
char buffer[OrthoLineLength];
|
|
|
|
/* safer... */
|
|
|
|
sprintf(buffer, "cmd._ctsh(chr(%d))", k);
|
|
/* sprintf(buffer,"_ctrl %c",k); */
|
|
PLog(G, buffer, cPLog_pym);
|
|
PParse(G, buffer);
|
|
PFlush(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static void OrthoKeyAlt(PyMOLGlobals* G, unsigned char k)
|
|
{
|
|
char buffer[OrthoLineLength];
|
|
|
|
/* safer... */
|
|
|
|
if (k == '@') {
|
|
/* option G produces '@' on some non-US keyboards, so simply
|
|
ignore the modifier */
|
|
OrthoKey(G, k, 0, 0, 0);
|
|
} else {
|
|
sprintf(buffer, "cmd._alt(chr(%d))", k);
|
|
/* sprintf(buffer,"_alt %c",k); */
|
|
PLog(G, buffer, cPLog_pym);
|
|
PParse(G, buffer);
|
|
PFlush(G);
|
|
}
|
|
}
|
|
|
|
static int add_normal_char(COrtho* I, unsigned char k)
|
|
{
|
|
char buffer[OrthoLineLength];
|
|
int curLine = I->CurLine & OrthoSaveLines;
|
|
if (I->CursorChar >= 0) {
|
|
strcpy(buffer, I->Line[curLine] + I->CursorChar);
|
|
I->Line[curLine][I->CursorChar] = k;
|
|
I->CursorChar++;
|
|
I->CurChar++;
|
|
strcpy(I->Line[curLine] + I->CursorChar, buffer);
|
|
} else {
|
|
I->Line[curLine][I->CurChar] = k;
|
|
I->CurChar++;
|
|
I->Line[curLine][I->CurChar] = 0;
|
|
}
|
|
return curLine;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoKey(PyMOLGlobals* G, unsigned char k, int x, int y, int mod)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
char buffer[OrthoLineLength];
|
|
int curLine;
|
|
|
|
PRINTFB(G, FB_Ortho, FB_Blather)
|
|
" OrthoKey: %c (%d), x %d y %d, mod %d\n", k, k, x, y, mod ENDFB(G);
|
|
|
|
OrthoRestorePrompt(G);
|
|
|
|
if (mod == 4) { /* alt */
|
|
OrthoKeyAlt(G, k);
|
|
} else if (mod == 3) { /* chsh */
|
|
OrthoKeyCtSh(G, (unsigned int) (k + 64));
|
|
} else if ((k > 32) && (k != 127)) {
|
|
curLine = add_normal_char(I, k);
|
|
} else
|
|
switch (k) {
|
|
case 32: /* spacebar */
|
|
if ((!OrthoArrowsGrabbed(G)) &&
|
|
(I->CurChar == I->PromptChar)) { /* no text entered yet... */
|
|
if (SettingGetGlobal_b(G, cSetting_presentation)) {
|
|
if (mod & cOrthoSHIFT) {
|
|
OrthoCommandIn(G, "rewind;mplay");
|
|
} else {
|
|
PParse(G, "cmd.scene('','next')");
|
|
}
|
|
} else {
|
|
if (mod & cOrthoSHIFT) {
|
|
OrthoCommandIn(G, "rewind;mplay");
|
|
} else {
|
|
OrthoCommandIn(G, "mtoggle");
|
|
}
|
|
}
|
|
} else {
|
|
curLine = add_normal_char(I, k);
|
|
}
|
|
break;
|
|
case 127: /* delete */
|
|
if ((!I->CurChar) || (I->CurChar == I->PromptChar) ||
|
|
!OrthoTextVisible(G)) {
|
|
OrthoKeyControl(G, 4 + 64);
|
|
} else {
|
|
if (I->CursorChar > -1 && I->CursorChar < I->CurChar) {
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
strcpy(buffer, I->Line[curLine] + I->CursorChar + 1);
|
|
I->CurChar--;
|
|
strcpy(I->Line[curLine] + I->CursorChar, buffer);
|
|
}
|
|
}
|
|
break;
|
|
case 8: /* backspace */
|
|
if (I->CurChar > I->PromptChar) {
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
if (I->CursorChar >= 0) {
|
|
if (I->CursorChar > I->PromptChar) {
|
|
strcpy(buffer, I->Line[curLine] + I->CursorChar);
|
|
I->Line[curLine][I->CursorChar] = k;
|
|
I->CursorChar--;
|
|
I->CurChar--;
|
|
strcpy(I->Line[curLine] + I->CursorChar, buffer);
|
|
}
|
|
} else {
|
|
I->CurChar--;
|
|
I->Line[curLine][I->CurChar] = 0;
|
|
}
|
|
}
|
|
break;
|
|
case 5: /* CTRL E -- ending */
|
|
if (OrthoArrowsGrabbed(G)) {
|
|
I->CursorChar = -1;
|
|
} else
|
|
OrthoKeyControl(G, (unsigned char) (k + 64));
|
|
break;
|
|
case 1: /* CTRL A -- beginning */
|
|
if (OrthoArrowsGrabbed(G)) {
|
|
if (I->CurChar)
|
|
I->CursorChar = I->PromptChar;
|
|
} else
|
|
OrthoKeyControl(G, (unsigned char) (k + 64));
|
|
break;
|
|
case 4: /* CTRL D */
|
|
if ((!I->CurChar) || (I->CurChar == I->PromptChar) ||
|
|
!OrthoTextVisible(G)) {
|
|
OrthoKeyControl(G, (unsigned char) (4 + 64));
|
|
} else if ((I->CurChar > I->PromptChar) && (I->CursorChar >= 0) &&
|
|
(I->CursorChar < I->CurChar)) { /* deleting */
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
strcpy(buffer, I->Line[curLine] + I->CursorChar + 1);
|
|
I->CurChar--;
|
|
strcpy(I->Line[curLine] + I->CursorChar, buffer);
|
|
} else { /* filename completion query */
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
if (I->PromptChar) {
|
|
strcpy(buffer, I->Line[curLine]);
|
|
PComplete(G, buffer + I->PromptChar,
|
|
sizeof(OrthoLineType) -
|
|
I->PromptChar); /* just print, don't complete */
|
|
}
|
|
}
|
|
break;
|
|
case 9: /* CTRL I -- tab */
|
|
if (mod & cOrthoCTRL) {
|
|
OrthoKeyControl(G, (unsigned char) (k + 64));
|
|
} else {
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
if (I->PromptChar) {
|
|
strcpy(buffer, I->Line[curLine]);
|
|
|
|
if (PComplete(G, buffer + I->PromptChar,
|
|
sizeof(OrthoLineType) - I->PromptChar)) {
|
|
OrthoRestorePrompt(G);
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
strcpy(I->Line[curLine], buffer);
|
|
I->CurChar = strlen(I->Line[curLine]);
|
|
I->CursorChar = -1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 27: /* ESCAPE */
|
|
if (SettingGetGlobal_b(G, cSetting_presentation) &&
|
|
!(mod & (cOrthoCTRL | cOrthoSHIFT))) {
|
|
PParse(G, "_quit");
|
|
} else {
|
|
if (I->SplashFlag) {
|
|
OrthoRemoveSplash(G);
|
|
} else {
|
|
if (mod & cOrthoSHIFT)
|
|
SettingSetGlobal_i(G, cSetting_overlay,
|
|
!(SettingGetGlobal_i(G, cSetting_overlay)));
|
|
else
|
|
SettingSetGlobal_b(
|
|
G, cSetting_text, !(SettingGetGlobal_b(G, cSetting_text)));
|
|
}
|
|
}
|
|
break;
|
|
case 13: /* CTRL M -- carriage return */
|
|
if (I->CurChar > I->PromptChar)
|
|
OrthoParseCurrentLine(G);
|
|
else if (((SettingGetGlobal_b(G, cSetting_movie_panel) ||
|
|
SettingGetGlobal_b(G, cSetting_presentation)) &&
|
|
MovieGetLength(G))) {
|
|
if (mod & cOrthoSHIFT) {
|
|
if (mod & cOrthoCTRL)
|
|
OrthoCommandIn(G, "mview toggle_interp,quiet=1,object=same");
|
|
else
|
|
OrthoCommandIn(G, "mview toggle_interp,quiet=1");
|
|
} else if (mod & cOrthoCTRL) {
|
|
OrthoCommandIn(G, "mview toggle,freeze=1,quiet=1");
|
|
} else {
|
|
if (SettingGetGlobal_b(G, cSetting_presentation)) {
|
|
OrthoCommandIn(G, "mtoggle");
|
|
} else {
|
|
OrthoCommandIn(G, "mview toggle,quiet=1");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 11: /* CTRL K -- truncate */
|
|
if (OrthoArrowsGrabbed(G)) {
|
|
if (I->CursorChar >= 0) {
|
|
I->Line[I->CurLine & OrthoSaveLines][I->CursorChar] = 0;
|
|
I->CurChar = I->CursorChar;
|
|
I->CursorChar = -1;
|
|
}
|
|
} else {
|
|
if (mod & cOrthoCTRL) {
|
|
OrthoKeyControl(G, (unsigned char) (k + 64));
|
|
}
|
|
}
|
|
break;
|
|
case 22: /* CTRL V -- paste */
|
|
#ifndef _PYMOL_NOPY
|
|
if (I->CurChar != I->PromptChar) { /* no text entered yet... */
|
|
PBlockAndUnlockAPI(G);
|
|
PRunStringInstance(G, "cmd.paste()");
|
|
PLockAPIAndUnblock(G);
|
|
} else {
|
|
OrthoKeyControl(G, (unsigned char) (k + 64));
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
OrthoKeyControl(G, (unsigned char) (k + 64));
|
|
break;
|
|
}
|
|
OrthoInvalidateDoDraw(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoParseCurrentLine(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
char buffer[OrthoLineLength];
|
|
int curLine;
|
|
|
|
OrthoRemoveAutoOverlay(G);
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
I->Line[curLine][I->CurChar] = 0;
|
|
strcpy(buffer, I->Line[curLine] + I->PromptChar);
|
|
#ifndef _PYMOL_NOPY
|
|
if (buffer[0]) {
|
|
strcpy(I->History[I->HistoryLine], buffer);
|
|
I->HistoryLine = (I->HistoryLine + 1) & OrthoHistoryLines;
|
|
I->History[I->HistoryLine][0] = 0;
|
|
I->HistoryView = I->HistoryLine;
|
|
|
|
OrthoNewLine(G, nullptr, true);
|
|
if (WordMatch(G, buffer, "quit", true) == 0) /* don't log quit */
|
|
PLog(G, buffer, cPLog_pml);
|
|
OrthoDirty(G); /* this will force a redraw, if necessary */
|
|
PParse(G, buffer);
|
|
OrthoRestorePrompt(G);
|
|
}
|
|
#endif
|
|
I->CursorChar = -1;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoAddOutput(PyMOLGlobals* G, const char* str)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
int curLine;
|
|
const char* p;
|
|
char* q;
|
|
int cc;
|
|
int wrap;
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
if (I->InputFlag) {
|
|
strcpy(I->Saved, I->Line[curLine]);
|
|
I->SavedPC = I->PromptChar;
|
|
I->SavedCC = I->CurChar;
|
|
I->PromptChar = 0;
|
|
I->CurChar = 0;
|
|
I->Line[curLine][0] = 0;
|
|
I->InputFlag = 0;
|
|
}
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
p = str;
|
|
q = I->Line[curLine] + I->CurChar;
|
|
cc = I->CurChar;
|
|
while (*p) {
|
|
if (*p != '\r' && *p != '\n') {
|
|
cc++;
|
|
wrap = SettingGetGlobal_b(G, cSetting_wrap_output);
|
|
|
|
if (wrap > 0) {
|
|
if (cc > wrap) {
|
|
*q = 0;
|
|
I->CurChar = cc;
|
|
OrthoNewLine(G, nullptr, true);
|
|
cc = 0;
|
|
q = I->Line[I->CurLine & OrthoSaveLines];
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
}
|
|
}
|
|
if (cc >= OrthoLineLength - 6) { /* fail safe */
|
|
*q = 0;
|
|
I->CurChar = cc;
|
|
OrthoNewLine(G, nullptr, false);
|
|
cc = 0;
|
|
q = I->Line[I->CurLine & OrthoSaveLines];
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
}
|
|
*q++ = *p++;
|
|
} else {
|
|
*q = 0;
|
|
I->CurChar = cc;
|
|
OrthoNewLine(G, nullptr, true);
|
|
q = I->Line[I->CurLine & OrthoSaveLines];
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
p++;
|
|
cc = 0;
|
|
}
|
|
}
|
|
*q = 0;
|
|
I->CurChar = strlen(I->Line[curLine]);
|
|
if ((SettingGetGlobal_i(G, cSetting_internal_feedback) > 1) ||
|
|
SettingGetGlobal_i(G, cSetting_overlay) ||
|
|
SettingGetGlobal_i(G, cSetting_auto_overlay))
|
|
OrthoDirty(G);
|
|
|
|
OrthoInvalidateDoDraw(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoNewLine(PyMOLGlobals* G, const char* prompt, int crlf)
|
|
{
|
|
int curLine;
|
|
COrtho* I = G->Ortho;
|
|
|
|
/* printf("orthoNewLine: CC: %d CL:%d PC: %d IF:L
|
|
%d\n",I->CurChar,I->CurLine, I->PromptChar,I->InputFlag); */
|
|
/* if(I->CurChar)
|
|
{ */
|
|
if (I->CurChar)
|
|
OrthoFeedbackIn(G, I->Line[I->CurLine & OrthoSaveLines]);
|
|
else
|
|
OrthoFeedbackIn(G, " ");
|
|
|
|
bool do_print = Feedback(G, FB_Python, FB_Output);
|
|
bool do_print_with_escapes = false;
|
|
|
|
#if !defined(_WIN32) && !defined(_WEBGL) && !defined(_PYMOL_LIB)
|
|
do_print_with_escapes = do_print &&
|
|
SettingGetGlobal_b(G, cSetting_colored_feedback) &&
|
|
isatty(STDOUT_FILENO);
|
|
#endif
|
|
|
|
// print as-is if stdout supports ANSI Escape sequences
|
|
if (do_print_with_escapes) {
|
|
printf("%s", I->Line[I->CurLine & OrthoSaveLines]);
|
|
}
|
|
|
|
// strip ANSI Escape sequences (in-place)
|
|
UtilStripANSIEscapes(I->Line[I->CurLine & OrthoSaveLines]);
|
|
|
|
if (do_print) {
|
|
if (!do_print_with_escapes) {
|
|
printf("%s", I->Line[I->CurLine & OrthoSaveLines]);
|
|
}
|
|
|
|
if (crlf) {
|
|
putchar('\n');
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
/* } */
|
|
|
|
/* if(I->Line[I->CurLine&OrthoSaveLines][0]) */
|
|
I->CurLine++;
|
|
curLine = I->CurLine & OrthoSaveLines;
|
|
|
|
if (prompt) {
|
|
strcpy(I->Line[curLine], prompt);
|
|
I->CurChar = (I->PromptChar = strlen(prompt));
|
|
I->InputFlag = 1;
|
|
} else {
|
|
I->CurChar = 0;
|
|
I->Line[curLine][0] = 0;
|
|
I->PromptChar = 0;
|
|
I->InputFlag = 0;
|
|
}
|
|
/*printf("orthoNewLine: CC: %d CL:%d PC: %d IF:L %d\n",I->CurChar,I->CurLine,
|
|
I->PromptChar,I->InputFlag); */
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoGrab(PyMOLGlobals* G, Block* block)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->GrabbedBy = block;
|
|
}
|
|
|
|
int OrthoGrabbedBy(PyMOLGlobals* G, Block* block)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
return I->GrabbedBy == block;
|
|
}
|
|
|
|
void OrthoDoViewportWhenReleased(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
if (!(I->GrabbedBy || I->ClickedIn)) { /* no active UI element? */
|
|
OrthoCommandIn(G, "viewport"); /* then issue viewport refresh */
|
|
OrthoDirty(G);
|
|
} else {
|
|
I->IssueViewportWhenReleased = true; /* otherwise, defer */
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoUngrab(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->GrabbedBy = nullptr;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoAttach(PyMOLGlobals* G, Block* block, int type)
|
|
{
|
|
G->Ortho->Blocks.push_back(block);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoDetach(PyMOLGlobals* G, Block* block)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
if (I->GrabbedBy == block)
|
|
I->GrabbedBy = nullptr;
|
|
auto iter = std::find(I->Blocks.begin(), I->Blocks.end(), block);
|
|
if (iter != I->Blocks.end()) {
|
|
I->Blocks.erase(iter);
|
|
}
|
|
}
|
|
|
|
float* OrthoGetOverlayColor(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
return I->OverlayColor;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
|
|
#ifdef PYMOL_EVAL
|
|
#include "OrthoEvalMessage.h"
|
|
#endif
|
|
#ifdef PYMOL_BETA
|
|
#include "OrthoBetaMessage.h"
|
|
#endif
|
|
#ifdef JYMOL_EVAL
|
|
#include "OrthoJyMolEvalMessage.h"
|
|
#endif
|
|
#ifdef PYMOL_EDU
|
|
#include "OrthoEduMessage.h"
|
|
#endif
|
|
#ifdef PYMOL_COLL
|
|
#include "OrthoCollMessage.h"
|
|
#endif
|
|
#ifdef AXPYMOL_EVAL
|
|
#include "OrthoAxMessage.h"
|
|
#endif
|
|
|
|
/* END PROPRIETARY CODE SEGMENT */
|
|
|
|
/* draw background gradient from bg_rgb_top
|
|
* to bg_rgb_bottom is bg_gradient is set
|
|
*/
|
|
|
|
#define BACKGROUND_TEXTURE_SIZE 256
|
|
|
|
std::size_t OrthoGetBackgroundTextureID(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
return I->bgTextureID;
|
|
}
|
|
|
|
void OrthoInvalidateBackgroundTexture(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
if (I->bgTextureID) {
|
|
G->ShaderMgr->freeGPUBuffer(I->bgTextureID);
|
|
I->bgTextureID = 0;
|
|
I->bgTextureNeedsUpdate = true;
|
|
}
|
|
if (I->bgCGO) {
|
|
CGOFree(I->bgCGO);
|
|
}
|
|
}
|
|
|
|
void OrthoBackgroundTextureNeedsUpdate(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->bgTextureNeedsUpdate = true;
|
|
}
|
|
|
|
/**
|
|
* Make a background image for grid mode.
|
|
*
|
|
* Repurpose the `bg_rgb_top` and `bg_rgb_bottom` colors to color grid cells
|
|
* with alternating colors.
|
|
*/
|
|
static std::unique_ptr<pymol::Image> makeBgGridImage(PyMOLGlobals* G)
|
|
{
|
|
auto const& grid = G->Scene->grid;
|
|
auto tmpImg = std::make_unique<pymol::Image>( //
|
|
grid.n_col > 1 ? grid.n_col : 1, //
|
|
grid.n_row > 1 ? grid.n_row : 1);
|
|
|
|
unsigned char top[4]{0, 0, 0, 0xFF}, bottom[4]{0, 0, 0, 0xFF};
|
|
pymol::scale3(
|
|
ColorGet(G, SettingGet_color(G, cSetting_bg_rgb_top)), 0xFF, top);
|
|
pymol::scale3(
|
|
ColorGet(G, SettingGet_color(G, cSetting_bg_rgb_bottom)), 0xFF, bottom);
|
|
|
|
unsigned char* q = tmpImg->bits();
|
|
|
|
for (unsigned j = 0; j != tmpImg->getHeight(); ++j) {
|
|
for (unsigned i = 0; i != tmpImg->getWidth(); ++i, q += 4) {
|
|
auto color = (i + j) % 2 ? top : bottom;
|
|
copy4(color, q);
|
|
}
|
|
}
|
|
|
|
return tmpImg;
|
|
}
|
|
|
|
static std::unique_ptr<pymol::Image> makeBgGradientImage(PyMOLGlobals* G)
|
|
{
|
|
constexpr unsigned height = BACKGROUND_TEXTURE_SIZE;
|
|
|
|
auto tmpImg = std::make_unique<pymol::Image>(1, height);
|
|
|
|
float top[3], bottom[3], mixed[4]{0, 0, 0, 1};
|
|
copy3f(ColorGet(G, SettingGet_color(G, cSetting_bg_rgb_top)), top);
|
|
copy3f(ColorGet(G, SettingGet_color(G, cSetting_bg_rgb_bottom)), bottom);
|
|
|
|
unsigned char* q = tmpImg->bits();
|
|
|
|
for (unsigned b = 0; b != height; ++b) {
|
|
mix3f(bottom, top, b / float(height - 1), mixed);
|
|
|
|
for (unsigned i = 0; i != 4; ++i) {
|
|
*(q++) = static_cast<unsigned char>(mixed[i] * 0xFF + 0.5);
|
|
}
|
|
}
|
|
|
|
return tmpImg;
|
|
}
|
|
|
|
static CGO* makeBgCGO(PyMOLGlobals* G)
|
|
{
|
|
constexpr float z_value = 0.98f;
|
|
CGO primCgo(G);
|
|
|
|
bool ok = //
|
|
CGOBegin(&primCgo, GL_TRIANGLE_STRIP) && //
|
|
CGOVertex(&primCgo, -1, -1, z_value) && //
|
|
CGOVertex(&primCgo, 1, -1, z_value) && //
|
|
CGOVertex(&primCgo, -1, 1, z_value) && //
|
|
CGOVertex(&primCgo, 1, 1, z_value) && //
|
|
CGOEnd(&primCgo);
|
|
|
|
if (!ok) {
|
|
return nullptr;
|
|
}
|
|
|
|
assert(primCgo.has_begin_end);
|
|
|
|
std::unique_ptr<CGO> bgCgo(CGOOptimizeToVBONotIndexed(&primCgo));
|
|
|
|
CGOChangeShadersTo(
|
|
bgCgo.get(), GL_DEFAULT_SHADER_WITH_SETTINGS, GL_BACKGROUND_SHADER);
|
|
|
|
assert(bgCgo->use_shader);
|
|
|
|
return bgCgo.release();
|
|
}
|
|
|
|
static std::size_t OrthoCreateBgTexture(PyMOLGlobals* G)
|
|
{
|
|
auto const bg_image_mode = SettingGet<int>(G, cSetting_bg_image_mode);
|
|
bool const is_repeat = bg_image_mode > 1;
|
|
auto wrapS = is_repeat ? tex::wrap::REPEAT : tex::wrap::CLAMP_TO_EDGE;
|
|
auto wrapT = is_repeat ? tex::wrap::REPEAT : tex::wrap::CLAMP_TO_EDGE;
|
|
auto const bg_image_linear = SettingGet<bool>(G, cSetting_bg_image_linear);
|
|
auto filter = bg_image_linear ? tex::filter::LINEAR : tex::filter::NEAREST;
|
|
auto texture = G->ShaderMgr->newGPUBuffer<TextureGL>(
|
|
tex::format::RGBA, tex::data_type::UBYTE, filter, filter, wrapS, wrapT);
|
|
return texture->get_hash_id();
|
|
}
|
|
|
|
void bg_grad(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
auto bg_gradient = SettingGet<BgGradient>(G, cSetting_bg_gradient);
|
|
const char* bg_image_filename =
|
|
SettingGetGlobal_s(G, cSetting_bg_image_filename);
|
|
|
|
if (bg_image_filename && !bg_image_filename[0]) {
|
|
bg_image_filename = nullptr;
|
|
}
|
|
|
|
if (bg_gradient == BgGradient::Grid &&
|
|
SettingGet<GridMode>(G, cSetting_grid_mode) == GridMode::NoGrid) {
|
|
bg_gradient = BgGradient::None;
|
|
}
|
|
|
|
if (!(bg_gradient != BgGradient::None || bg_image_filename || I->bgData) ||
|
|
!G->ShaderMgr->ShadersPresent()) {
|
|
const float* bg_rgb =
|
|
ColorGet(G, SettingGet_color(G, nullptr, nullptr, cSetting_bg_rgb));
|
|
SceneGLClearColor(bg_rgb[0], bg_rgb[1], bg_rgb[2], 1.0);
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
|
return;
|
|
}
|
|
|
|
if (!I->bgCGO) {
|
|
I->bgCGO = makeBgCGO(G);
|
|
assert(I->bgCGO);
|
|
}
|
|
|
|
if (!I->bgTextureID || I->bgTextureNeedsUpdate) {
|
|
std::shared_ptr<const pymol::Image> bgImage = I->bgData;
|
|
|
|
if (bgImage) {
|
|
// pass
|
|
} else if (bg_image_filename) {
|
|
// checking to see if bg_image_filename can be loaded into texture
|
|
bgImage = MyPNGRead(bg_image_filename);
|
|
if (bgImage) {
|
|
I->bgWidth = bgImage->getWidth();
|
|
I->bgHeight = bgImage->getHeight();
|
|
} else {
|
|
PRINTFB(G, FB_Ortho, FB_Errors)
|
|
"Ortho: bg_grad: bg_image_filename='%s' cannot be loaded, unset\n",
|
|
bg_image_filename ENDFB(G);
|
|
SettingSetGlobal_s(G, cSetting_bg_image_filename, "");
|
|
G->ShaderMgr->Reload_All_Shaders();
|
|
}
|
|
} else if (bg_gradient == BgGradient::Grid) {
|
|
bgImage = makeBgGridImage(G);
|
|
} else if (bg_gradient == BgGradient::Vertical) {
|
|
bgImage = makeBgGradientImage(G);
|
|
}
|
|
|
|
if (bgImage) {
|
|
if (!I->bgTextureID) {
|
|
I->bgTextureID = OrthoCreateBgTexture(G);
|
|
}
|
|
auto texture =
|
|
G->ShaderMgr->getGPUBuffer<TextureGL>(I->bgTextureID);
|
|
texture->texture_data_2D(
|
|
bgImage->getWidth(), bgImage->getHeight(), bgImage->bits());
|
|
I->bgTextureNeedsUpdate = false;
|
|
}
|
|
}
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
CGORender(I->bgCGO, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
/**
|
|
* Updates the Sequence Viewer if necessary.
|
|
*/
|
|
static void OrthoDoDrawUpdateSeqView(PyMOLGlobals* G)
|
|
{
|
|
auto I = G->Ortho;
|
|
if (SettingGet<bool>(G, cSetting_seq_view)) {
|
|
SeqUpdate(G);
|
|
I->HaveSeqViewer = true;
|
|
} else if (I->HaveSeqViewer) {
|
|
SeqUpdate(G);
|
|
I->HaveSeqViewer = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines the margin size of the internal_gui (legacy contents panel)
|
|
* @param internal_gui_mode the mode of the internal gui
|
|
* @return the size of the right side margin
|
|
*/
|
|
static int OrthoCalculateRightSideMargin(
|
|
PyMOLGlobals* G, InternalGUIMode internal_gui_mode)
|
|
{
|
|
if (!SettingGet<bool>(G, cSetting_internal_gui)) {
|
|
return 0;
|
|
}
|
|
if (internal_gui_mode == InternalGUIMode::Default) {
|
|
return DIP2PIXEL(SettingGet<int>(G, cSetting_internal_gui_width));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Draws the background for the internal Feedback
|
|
* @param orthoCGO the CGO to render into
|
|
* @param rightSceneMargin the size of the right side margin
|
|
* (OrthoCalculateRightSideMargin)
|
|
* @param internal_gui_mode the mode of the internal gui
|
|
*/
|
|
static void OrthoDrawInternalFeedbackBG(PyMOLGlobals* G, CGO* orthoCGO,
|
|
int rightSceneMargin, InternalGUIMode internal_gui_mode)
|
|
{
|
|
auto I = G->Ortho;
|
|
auto* block = SceneGetBlock(G);
|
|
auto height = block->rect.bottom;
|
|
switch (internal_gui_mode) {
|
|
case InternalGUIMode::Default:
|
|
if (orthoCGO) {
|
|
CGOColor(orthoCGO, 0.f, 0.f, 0.f);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, I->Width - rightSceneMargin, height - 1, 0.f);
|
|
CGOVertex(orthoCGO, I->Width - rightSceneMargin, 0, 0.f);
|
|
CGOVertex(orthoCGO, 0.f, height - 1, 0.f);
|
|
CGOVertex(orthoCGO, 0.f, 0.f, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
#ifndef PURE_OPENGL_ES_2
|
|
} else {
|
|
glColor3f(0.0, 0.0, 0.0);
|
|
glBegin(GL_POLYGON);
|
|
glVertex2i(I->Width - rightSceneMargin, height - 1);
|
|
glVertex2i(I->Width - rightSceneMargin, 0);
|
|
glVertex2i(0, 0);
|
|
glVertex2i(0, height - 1);
|
|
glEnd();
|
|
#endif
|
|
}
|
|
/* deliberate fall-through */
|
|
case InternalGUIMode::BG:
|
|
if (orthoCGO) {
|
|
CGOColor(orthoCGO, 0.3f, 0.3f, 0.3f);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, 1 + I->Width - rightSceneMargin, height, 0.f);
|
|
CGOVertex(orthoCGO, 1 + I->Width - rightSceneMargin, height - 1, 0.f);
|
|
CGOVertex(orthoCGO, -1, height, 0.f);
|
|
CGOVertex(orthoCGO, -1, height - 1, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
#ifndef PURE_OPENGL_ES_2
|
|
} else {
|
|
glColor3f(0.3, 0.3, 0.3);
|
|
glBegin(GL_LINES);
|
|
glVertex2i(1 + I->Width - rightSceneMargin, height - 1);
|
|
glVertex2i(-1, height - 1);
|
|
glEnd();
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws the background for the internal GUI (legacy contents panel)
|
|
* @param orthoCGO the CGO to render into
|
|
* @param internal_gui_mode the mode of the internal gui
|
|
*/
|
|
static void OrthoDrawInternalGUIBG(
|
|
PyMOLGlobals* G, CGO* orthoCGO, InternalGUIMode internal_gui_mode)
|
|
{
|
|
auto I = G->Ortho;
|
|
auto internal_gui_width =
|
|
DIP2PIXEL(SettingGet<int>(G, cSetting_internal_gui_width));
|
|
if (internal_gui_mode != InternalGUIMode::Transparent) {
|
|
if (orthoCGO) {
|
|
CGOColor(orthoCGO, 0.3f, 0.3f, 0.3f);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, I->Width - internal_gui_width, 0.f, 0.f);
|
|
CGOVertex(orthoCGO, I->Width - internal_gui_width + 1.f, 0.f, 0.f);
|
|
CGOVertex(orthoCGO, I->Width - internal_gui_width, I->Height, 0.f);
|
|
CGOVertex(orthoCGO, I->Width - internal_gui_width + 1.f, I->Height, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
#ifndef PURE_OPENGL_ES_2
|
|
} else {
|
|
glColor3f(0.3, 0.3, 0.3);
|
|
glBegin(GL_LINES);
|
|
glVertex2i(I->Width - internal_gui_width, 0);
|
|
glVertex2i(I->Width - internal_gui_width, I->Height);
|
|
glEnd();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return the number of overlay lines to display
|
|
*/
|
|
static int OrthoGetNumberOverlayLines(PyMOLGlobals* G)
|
|
{
|
|
auto I = G->Ortho;
|
|
auto overlay = OrthoGetOverlayStatus(G);
|
|
auto internal_feedback = SettingGet<int>(G, cSetting_internal_feedback);
|
|
switch (overlay) {
|
|
case -1: /* auto overlay */
|
|
overlay = I->CurLine - I->AutoOverlayStopLine;
|
|
if (overlay < 0) {
|
|
overlay += (OrthoSaveLines + 1);
|
|
}
|
|
if (internal_feedback > 1) {
|
|
overlay -= (internal_feedback - 1);
|
|
}
|
|
overlay = std::max(overlay, 0);
|
|
break;
|
|
case 1: /* default -- user overlay_lines */
|
|
overlay = SettingGetGlobal_i(G, cSetting_overlay_lines);
|
|
break;
|
|
}
|
|
auto text = SettingGet<bool>(G, cSetting_text);
|
|
return text ? 0 : overlay;
|
|
}
|
|
|
|
/**
|
|
* Draws overlay text
|
|
* @param orthoCGO the CGO to render into
|
|
* @param draw_text whether to draw text
|
|
* @param internal_feedback the number of internal feedback lines
|
|
* @param numOverlayLines the number of overlay lines
|
|
* @param internal_gui_mode the mode of the internal gui
|
|
*/
|
|
static void OrthoDrawText(PyMOLGlobals* G, CGO* orthoCGO, bool draw_text,
|
|
int internal_feedback, int numOverlayLines,
|
|
InternalGUIMode internal_gui_mode)
|
|
{
|
|
auto I = G->Ortho;
|
|
bool skip_prompt =
|
|
SettingGet<int>(G, cSetting_internal_prompt) ? false : true;
|
|
int adjust_at = 0;
|
|
/* now print the text */
|
|
|
|
auto lcount = 0;
|
|
auto x = cOrthoLeftMargin;
|
|
auto y = cOrthoBottomMargin + MovieGetPanelHeight(G);
|
|
|
|
int showLines{};
|
|
if (draw_text || I->SplashFlag)
|
|
showLines = I->ShowLines;
|
|
else {
|
|
showLines = internal_feedback + numOverlayLines;
|
|
}
|
|
if (internal_feedback)
|
|
adjust_at = internal_feedback + 1;
|
|
|
|
auto l = (I->CurLine - (lcount + skip_prompt)) & OrthoSaveLines;
|
|
|
|
if (orthoCGO)
|
|
CGOColorv(orthoCGO, I->TextColor);
|
|
#ifndef PURE_OPENGL_ES_2
|
|
else
|
|
glColor3fv(I->TextColor);
|
|
#endif
|
|
while (l >= 0) {
|
|
lcount++;
|
|
if (lcount > showLines)
|
|
break;
|
|
if (lcount == adjust_at)
|
|
y += 4;
|
|
auto* str = I->Line[l & OrthoSaveLines];
|
|
if (internal_gui_mode != InternalGUIMode::Default) {
|
|
TextSetColor(G, I->OverlayColor);
|
|
} else if (strncmp(str, I->Prompt, 6) == 0) {
|
|
if (lcount < adjust_at)
|
|
TextSetColor(G, I->TextColor);
|
|
else {
|
|
if (length3f(I->OverlayColor) < 0.5)
|
|
TextSetColor(G, I->OverlayColor);
|
|
else
|
|
TextSetColor(G, I->TextColor);
|
|
}
|
|
} else
|
|
TextSetColor(G, I->OverlayColor);
|
|
TextSetPos2i(G, x, y);
|
|
if (str) {
|
|
TextDrawStr(G, str, orthoCGO);
|
|
if ((lcount == 1) && (I->InputFlag)) {
|
|
if (!skip_prompt) {
|
|
if (I->CursorChar >= 0) {
|
|
TextSetPos2i(G, x + cOrthoCharWidth * I->CursorChar, y);
|
|
}
|
|
TextDrawChar(G, '_', orthoCGO);
|
|
}
|
|
}
|
|
}
|
|
l = (I->CurLine - (lcount + skip_prompt)) & OrthoSaveLines;
|
|
y = y + cOrthoLineHeight;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws the Picking Marquee overlay
|
|
* @param orthoCGO the CGO to render into
|
|
*/
|
|
static void OrthoDrawLoop(PyMOLGlobals* G, CGO* orthoCGO)
|
|
{
|
|
auto I = G->Ortho;
|
|
const float* vc = ColorGet(G, cColorFront);
|
|
if (orthoCGO) {
|
|
CGOColor(orthoCGO, vc[0], vc[1], vc[2]);
|
|
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, I->LoopRect.left, I->LoopRect.bottom, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.left, I->LoopRect.top + 1, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.left + 1, I->LoopRect.bottom, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.left + 1, I->LoopRect.top + 1, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, I->LoopRect.left, I->LoopRect.top, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.left, I->LoopRect.top + 1, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right, I->LoopRect.top, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right, I->LoopRect.top + 1, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, I->LoopRect.right, I->LoopRect.bottom, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right, I->LoopRect.top + 1, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right + 1, I->LoopRect.bottom, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right + 1, I->LoopRect.top + 1, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, I->LoopRect.left, I->LoopRect.bottom, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.left, I->LoopRect.bottom + 1, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right, I->LoopRect.bottom, 0.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right, I->LoopRect.bottom + 1, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
|
|
/*
|
|
CGOBegin(orthoCGO, GL_LINE_LOOP);
|
|
CGOVertex(orthoCGO, I->LoopRect.left, I->LoopRect.top, 1.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right, I->LoopRect.top, 1.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.right, I->LoopRect.bottom, 1.f);
|
|
CGOVertex(orthoCGO, I->LoopRect.left, I->LoopRect.bottom, 1.f);
|
|
CGOEnd(orthoCGO);*/
|
|
#ifndef PURE_OPENGL_ES_2
|
|
} else {
|
|
glColor3f(vc[0], vc[1], vc[2]);
|
|
glBegin(GL_LINE_LOOP);
|
|
glVertex2i(I->LoopRect.left, I->LoopRect.top);
|
|
glVertex2i(I->LoopRect.right, I->LoopRect.top);
|
|
glVertex2i(I->LoopRect.right, I->LoopRect.bottom);
|
|
glVertex2i(I->LoopRect.left, I->LoopRect.bottom);
|
|
glVertex2i(I->LoopRect.left, I->LoopRect.top);
|
|
glEnd();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws the PyMOL-product specific messages
|
|
* @param orthoCGO the CGO to render into
|
|
*/
|
|
static void OrthoDrawMessages(PyMOLGlobals* G, CGO* orthoCGO)
|
|
{
|
|
/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
|
|
#ifdef PYMOL_EVAL
|
|
#ifndef _NO_DISPLAY_EVAL
|
|
OrthoDrawEvalMessage(G, orthoCGO);
|
|
#endif
|
|
#endif
|
|
#ifdef PYMOL_BETA
|
|
OrthoDrawBetaMessage(G);
|
|
#endif
|
|
#ifdef JYMOL_EVAL
|
|
OrthoDrawEvalMessage(G);
|
|
#endif
|
|
#ifdef PYMOL_EDU
|
|
OrthoDrawEduMessage(G, orthoCGO);
|
|
#endif
|
|
#ifdef PYMOL_COLL
|
|
OrthoDrawCollMessage(G);
|
|
#endif
|
|
#ifdef AXPYMOL_EVAL
|
|
OrthoDrawAxMessage(G);
|
|
#endif
|
|
|
|
/* END PROPRIETARY CODE SEGMENT */
|
|
}
|
|
|
|
/**
|
|
* Draws the Font Texture for debugging purposes
|
|
* @param orthoCGO the CGO to render into
|
|
*/
|
|
static void OrthoDrawFontTextureDebug(PyMOLGlobals* G, CGO* orthoCGO)
|
|
{
|
|
/* This shows the font texture in the middle of the screen, we might want
|
|
* to debug it */
|
|
float minx = 100.f, maxx = 612.f, miny = 100.f, maxy = 612.f;
|
|
CGOAlpha(orthoCGO, .5f);
|
|
CGOColor(orthoCGO, 0.f, 0.f, 0.f);
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
if (orthoCGO)
|
|
CGOTexCoord2f(orthoCGO, 1.f, 1.f);
|
|
CGOVertex(orthoCGO, maxx, maxy, 0.f);
|
|
if (orthoCGO)
|
|
CGOTexCoord2f(orthoCGO, 1.f, 0.f);
|
|
CGOVertex(orthoCGO, maxx, miny, 0.f);
|
|
if (orthoCGO)
|
|
CGOTexCoord2f(orthoCGO, 0.f, 1.f);
|
|
CGOVertex(orthoCGO, minx, maxy, 0.f);
|
|
if (orthoCGO)
|
|
CGOTexCoord2f(orthoCGO, 0.f, 0.f);
|
|
CGOVertex(orthoCGO, minx, miny, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
CGOStop(orthoCGO);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the backbuffers for the current render mode
|
|
* @param renderMode the render mode to use
|
|
* @return the backbuffer(s)
|
|
* @todo: Temporary solution to use the backbuffers less directly.
|
|
*/
|
|
static std::vector<GLFramebufferConfig> OrthoGetBackbuffers(
|
|
PyMOLGlobals* G, OrthoDrawInfo drawInfo)
|
|
{
|
|
if (drawInfo.offscreenRender) {
|
|
auto extent = OrthoGetExtent(G);
|
|
G->ShaderMgr->bindOffscreenOrtho(extent, drawInfo.clearTarget);
|
|
return {{
|
|
static_cast<std::uint32_t>(G->ShaderMgr->offscreen_ortho_rt),
|
|
GL_COLOR_ATTACHMENT0 //
|
|
}};
|
|
}
|
|
|
|
if (drawInfo.renderMode == OrthoRenderMode::VR) {
|
|
#ifdef _PYMOL_OPENVR
|
|
return {{CShaderMgr::OpenGLDefaultFramebufferID, GL_NONE}};
|
|
#endif
|
|
}
|
|
if (drawInfo.renderMode == OrthoRenderMode::GeoWallRight) {
|
|
return {};
|
|
}
|
|
if (SceneMustDrawBoth(G)) {
|
|
return {
|
|
{CShaderMgr::OpenGLDefaultFramebufferID, GL_BACK_LEFT},
|
|
{CShaderMgr::OpenGLDefaultFramebufferID, GL_BACK_RIGHT},
|
|
};
|
|
} else {
|
|
return {
|
|
{CShaderMgr::OpenGLDefaultFramebufferID, GL_BACK},
|
|
};
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* Top-level function for drawing the PyMOL overlay
|
|
* (includes legacy contents panel, internal feedback, text,
|
|
* 3D scene, and other 2D blocks like Sequence viewer, etc.)
|
|
* @param render_mode the render mode to use
|
|
*/
|
|
void OrthoDoDraw(PyMOLGlobals* G, const OrthoDrawInfo& drawInfo)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
CGO* orthoCGO = nullptr;
|
|
int times = 1, origtimes = 0;
|
|
bool shouldRenderScene = false;
|
|
auto internal_gui_mode =
|
|
SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting);
|
|
#ifdef _PYMOL_OPENVR
|
|
bool offscreen_vr = false;
|
|
int openvr_text = 0;
|
|
#endif
|
|
|
|
bool generate_shader_cgo = false;
|
|
|
|
const auto backbuffers = OrthoGetBackbuffers(G, drawInfo);
|
|
G->ShaderMgr->topLevelConfig = backbuffers.front();
|
|
|
|
I->RenderMode = drawInfo.renderMode;
|
|
OrthoDoDrawUpdateSeqView(G);
|
|
|
|
auto double_pump = SettingGet<bool>(G, cSetting_stereo_double_pump_mono);
|
|
auto bg_color = ColorGet(G, SettingGet_color(G, cSetting_bg_rgb));
|
|
|
|
I->OverlayColor[0] = 1.0F - bg_color[0];
|
|
I->OverlayColor[1] = 1.0F - bg_color[1];
|
|
I->OverlayColor[2] = 1.0F - bg_color[2];
|
|
if (diff3f(I->OverlayColor, bg_color) < 0.25)
|
|
zero3f(I->OverlayColor);
|
|
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoDoDraw: entered.\n" ENDFD;
|
|
if (G->HaveGUI && G->ValidContext) {
|
|
|
|
if (Feedback(G, FB_OpenGL, FB_Debugging))
|
|
PyMOLCheckOpenGLErr("OrthoDoDraw checkpoint 0");
|
|
|
|
auto rightSceneMargin = OrthoCalculateRightSideMargin(G, internal_gui_mode);
|
|
|
|
auto numOverlayLines = OrthoGetNumberOverlayLines(G);
|
|
auto text = SettingGet<bool>(G, cSetting_text);
|
|
|
|
#ifdef PURE_OPENGL_ES_2
|
|
// Workaround for now
|
|
shouldRenderScene = true;
|
|
#else
|
|
if (numOverlayLines || (!text) || drawInfo.renderMode == OrthoRenderMode::VR)
|
|
if (!SceneRenderCached(G))
|
|
shouldRenderScene = true;
|
|
#endif
|
|
|
|
for (const auto& backbuffer : backbuffers) {
|
|
G->ShaderMgr->setDrawBuffer(backbuffer);
|
|
if (drawInfo.clearTarget) {
|
|
SceneGLClear(G, GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
|
}
|
|
}
|
|
|
|
if (drawInfo.renderMode == OrthoRenderMode::VR) {
|
|
#ifdef _PYMOL_OPENVR
|
|
times = 2;
|
|
double_pump = false;
|
|
offscreen_vr = true;
|
|
openvr_text = SettingGet<int>(G, cSetting_openvr_gui_text);
|
|
#endif
|
|
} else if (drawInfo.renderMode != OrthoRenderMode::GeoWallRight) {
|
|
if (SceneMustDrawBoth(G)) {
|
|
times = 2;
|
|
double_pump = true;
|
|
} else {
|
|
times = 1;
|
|
double_pump = false;
|
|
}
|
|
} else {
|
|
times = 1;
|
|
double_pump = false;
|
|
}
|
|
|
|
I->DrawTime = -I->LastDraw;
|
|
I->LastDraw = UtilGetSeconds(G);
|
|
I->DrawTime += I->LastDraw;
|
|
ButModeSetRate(G, (float) I->DrawTime);
|
|
|
|
if (shouldRenderScene && (drawInfo.renderMode != OrthoRenderMode::GeoWallRight)) {
|
|
SceneRenderInfo renderInfo{};
|
|
renderInfo.forceCopy = SettingGet<bool>(G, cSetting_image_copy_always);
|
|
renderInfo.offscreen = drawInfo.offscreenRender;
|
|
SceneRender(G, renderInfo);
|
|
} else if (text) {
|
|
bg_grad(G); // only render the background for text
|
|
}
|
|
SceneGLClearColor(0.0, 0.0, 0.0, 1.0);
|
|
|
|
origtimes = times;
|
|
while (times--) {
|
|
bool draw_text = text;
|
|
G->ShaderMgr->setDrawBuffer(backbuffers[times]);
|
|
|
|
switch (times) {
|
|
case 1:
|
|
#ifdef _PYMOL_OPENVR
|
|
if (offscreen_vr) {
|
|
draw_text = text || (openvr_text == 1);
|
|
OpenVRMenuBufferStart(G, I->Width, I->Height);
|
|
} else
|
|
#endif
|
|
break;
|
|
case 0:
|
|
#ifdef _PYMOL_OPENVR
|
|
if (offscreen_vr) {
|
|
draw_text = text && (openvr_text != 2);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
OrthoPushMatrix(G);
|
|
|
|
if (G->ShaderMgr->ShadersPresent()) {
|
|
if (SettingGet<bool>(G, cSetting_use_shaders)) {
|
|
CGO* orthoFastCGO = CGONew(G);
|
|
CGOFree(I->orthoFastCGO);
|
|
if (G->Ortho->fastDraw(orthoFastCGO)) {
|
|
int ok = true;
|
|
CGO* expandedCGO;
|
|
CGOStop(orthoFastCGO);
|
|
expandedCGO = CGOExpandDrawTextures(orthoFastCGO, 0);
|
|
CHECKOK(ok, expandedCGO);
|
|
if (ok)
|
|
I->orthoFastCGO =
|
|
CGOOptimizeScreenTexturesAndPolygons(expandedCGO, 0);
|
|
CHECKOK(ok, orthoFastCGO);
|
|
CGOFree(orthoFastCGO);
|
|
CGOFree(expandedCGO);
|
|
} else {
|
|
CGOFree(orthoFastCGO);
|
|
}
|
|
if (!I->orthoCGO) {
|
|
orthoCGO = CGONew(G);
|
|
generate_shader_cgo = true;
|
|
} else {
|
|
OrthoRenderCGO(G);
|
|
OrthoPopMatrix(G);
|
|
#ifdef _PYMOL_OPENVR
|
|
if (offscreen_vr && times) {
|
|
OpenVRMenuBufferFinish(G);
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto internal_feedback = SettingGet<int>(G, cSetting_internal_feedback);
|
|
if (internal_feedback) { /* moved to avoid conflict with menus */
|
|
OrthoDrawInternalFeedbackBG(G, orthoCGO, rightSceneMargin, internal_gui_mode);
|
|
}
|
|
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoDoDraw: drawing blocks...\n" ENDFD;
|
|
|
|
if (SettingGet<bool>(G, cSetting_internal_gui)) {
|
|
OrthoDrawInternalGUIBG(G, orthoCGO, internal_gui_mode);
|
|
}
|
|
|
|
OrthoRestorePrompt(G);
|
|
|
|
OrthoDrawText(G, orthoCGO, draw_text, internal_feedback,
|
|
numOverlayLines, internal_gui_mode);
|
|
|
|
OrthoDrawWizardPrompt(G, orthoCGO);
|
|
|
|
if (draw_text || I->SplashFlag) {
|
|
Block* block;
|
|
int active_tmp;
|
|
block = SeqGetBlock(G);
|
|
active_tmp = block->active;
|
|
block->active = false;
|
|
G->Ortho->draw(orthoCGO);
|
|
block->active = active_tmp;
|
|
} else {
|
|
G->Ortho->draw(orthoCGO);
|
|
}
|
|
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoDoDraw: blocks drawn.\n" ENDFD;
|
|
|
|
if (I->LoopFlag) {
|
|
OrthoDrawLoop(G, orthoCGO);
|
|
}
|
|
|
|
OrthoDrawMessages(G, orthoCGO);
|
|
|
|
OrthoPopMatrix(G);
|
|
|
|
#ifdef _PYMOL_OPENVR
|
|
if (offscreen_vr && times) {
|
|
OpenVRMenuBufferFinish(G);
|
|
}
|
|
#endif
|
|
|
|
if (Feedback(G, FB_OpenGL, FB_Debugging))
|
|
PyMOLCheckOpenGLErr("OrthoDoDraw final checkpoint");
|
|
|
|
} /* while */
|
|
}
|
|
|
|
if (generate_shader_cgo) {
|
|
int ok = true;
|
|
|
|
#ifdef SHOW_FONT_TEXTURE
|
|
OrthoDrawFontTextureDebug(G, orthoCGO);
|
|
#endif
|
|
CGOStop(orthoCGO);
|
|
|
|
// Optimize CGO
|
|
CGO* expandedCGO = CGOExpandDrawTextures(orthoCGO, 0);
|
|
CHECKOK(ok, expandedCGO);
|
|
if (ok)
|
|
I->orthoCGO = CGOOptimizeScreenTexturesAndPolygons(expandedCGO, 0);
|
|
CGOFree(orthoCGO);
|
|
CGOFree(expandedCGO);
|
|
|
|
// Render CGO to final buffer (if created anew)
|
|
while (origtimes--) {
|
|
G->ShaderMgr->setDrawBuffer(backbuffers[origtimes]);
|
|
|
|
switch (origtimes) {
|
|
case 1:
|
|
#ifdef _PYMOL_OPENVR
|
|
if (offscreen_vr) {
|
|
OpenVRMenuBufferStart(G, I->Width, I->Height);
|
|
} else
|
|
#endif
|
|
break;
|
|
case 0:
|
|
break;
|
|
}
|
|
OrthoPushMatrix(G);
|
|
OrthoRenderCGO(G);
|
|
OrthoPopMatrix(G);
|
|
#ifdef _PYMOL_OPENVR
|
|
if (offscreen_vr && origtimes) {
|
|
OpenVRMenuBufferFinish(G);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
G->ShaderMgr->topLevelConfig = G->ShaderMgr->defaultBackbuffer;
|
|
|
|
I->DirtyFlag = false;
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoDoDraw: leaving...\n" ENDFD;
|
|
}
|
|
|
|
void OrthoRenderCGO(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
if (I->orthoCGO) {
|
|
SceneDrawImageOverlay(G, 0, nullptr);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glEnable(GL_BLEND);
|
|
if (I->orthoCGO)
|
|
CGORender(I->orthoCGO, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
if (I->orthoFastCGO)
|
|
CGORender(I->orthoFastCGO, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
G->ShaderMgr->Disable_Current_Shader();
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
}
|
|
/*========================================================================*/
|
|
|
|
void OrthoDrawWizardPrompt(PyMOLGlobals* G, CGO* orthoCGO)
|
|
{
|
|
/* assumes PMGUI */
|
|
|
|
COrtho* I = G->Ortho;
|
|
|
|
char *vla, *p;
|
|
int nLine;
|
|
int x, y;
|
|
int nChar, c, ll;
|
|
int maxLen;
|
|
BlockRect rect;
|
|
int prompt_mode = SettingGetGlobal_i(G, cSetting_wizard_prompt_mode);
|
|
auto gui_mode =
|
|
SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting);
|
|
float* text_color = I->WizardTextColor;
|
|
float black[3] = {0.0F, 0.0F, 0.0F};
|
|
|
|
if (I->WizardPromptVLA && prompt_mode) {
|
|
vla = I->WizardPromptVLA;
|
|
|
|
if (gui_mode != InternalGUIMode::Default)
|
|
text_color = black;
|
|
nLine = UtilCountStringVLA(vla);
|
|
if (nLine) {
|
|
nChar = VLAGetSize(I->WizardPromptVLA);
|
|
|
|
/* count max line length; it's strlen - X,
|
|
* where X is 4*n, where n is the number
|
|
* of colors in the text label */
|
|
|
|
maxLen = 0;
|
|
p = vla;
|
|
ll = 0;
|
|
c = nChar;
|
|
while (c > 0) {
|
|
if (!*p) {
|
|
if (maxLen < ll)
|
|
maxLen = ll;
|
|
ll = 0;
|
|
p++;
|
|
c--;
|
|
} else if (TextStartsWithColorCode(p)) {
|
|
p += 4;
|
|
c -= 4;
|
|
} else {
|
|
ll++;
|
|
p++;
|
|
c--;
|
|
}
|
|
}
|
|
|
|
/* determine the coordinates from which to draw the text;
|
|
* need to make adjustments for the sequence viewer */
|
|
|
|
rect.top = I->Height;
|
|
if (I->HaveSeqViewer)
|
|
if (!SettingGetGlobal_b(G, cSetting_seq_view_location)) {
|
|
rect.top -= SeqGetHeight(G);
|
|
}
|
|
|
|
if (prompt_mode != 3) {
|
|
rect.top -= cWizardTopMargin;
|
|
rect.left = cWizardLeftMargin;
|
|
} else {
|
|
rect.top -= 1;
|
|
rect.left = 1;
|
|
}
|
|
|
|
rect.bottom =
|
|
rect.top - (nLine * cOrthoLineHeight + 2 * cWizardBorder) - 2;
|
|
rect.right = rect.left + cOrthoCharWidth * maxLen + 2 * cWizardBorder + 1;
|
|
|
|
if (prompt_mode == 1) {
|
|
if (orthoCGO) {
|
|
if (gui_mode != InternalGUIMode::Default) {
|
|
CGOColor(orthoCGO, 1.0, 1.0F, 1.0F);
|
|
} else {
|
|
CGOColorv(orthoCGO, I->WizardBackColor);
|
|
}
|
|
CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
|
|
CGOVertex(orthoCGO, rect.right, rect.top, 0.f);
|
|
CGOVertex(orthoCGO, rect.right, rect.bottom, 0.f);
|
|
CGOVertex(orthoCGO, rect.left, rect.top, 0.f);
|
|
CGOVertex(orthoCGO, rect.left, rect.bottom, 0.f);
|
|
CGOEnd(orthoCGO);
|
|
} else {
|
|
if (gui_mode != InternalGUIMode::Default) {
|
|
glColor3f(1.0, 1.0F, 1.0F);
|
|
} else {
|
|
glColor3fv(I->WizardBackColor);
|
|
}
|
|
glBegin(GL_POLYGON);
|
|
glVertex2i(rect.right, rect.top);
|
|
glVertex2i(rect.right, rect.bottom);
|
|
glVertex2i(rect.left, rect.bottom);
|
|
glVertex2i(rect.left, rect.top);
|
|
glEnd();
|
|
}
|
|
}
|
|
if (orthoCGO)
|
|
CGOColorv(orthoCGO, text_color);
|
|
else
|
|
glColor3fv(text_color);
|
|
|
|
x = rect.left + cWizardBorder;
|
|
y = rect.top - (cWizardBorder + cOrthoLineHeight);
|
|
|
|
vla = I->WizardPromptVLA;
|
|
|
|
/* count max line length */
|
|
|
|
TextSetColor(G, text_color);
|
|
TextSetPos2i(G, x, y);
|
|
p = vla;
|
|
ll = 0;
|
|
c = nChar;
|
|
/* set the char color, position the characters and draw the text */
|
|
while (c > 0) {
|
|
if (TextSetColorFromCode(G, p, text_color)) {
|
|
p += 4;
|
|
c -= 4;
|
|
}
|
|
if (c--) {
|
|
if (*p) {
|
|
TextDrawChar(G, *p, orthoCGO);
|
|
}
|
|
if (!*(p++)) {
|
|
y = y - cOrthoLineHeight;
|
|
TextSetPos2i(G, x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void OrthoLayoutPanel(
|
|
PyMOLGlobals* G, int m_top, int m_left, int m_bottom, int m_right)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
Block* block = nullptr;
|
|
|
|
int controlHeight = DIP2PIXEL(20);
|
|
int butModeHeight = ButModeGetHeight(G);
|
|
int wizardHeight = I->WizardHeight;
|
|
|
|
int controlBottom = m_bottom;
|
|
int butModeBottom = controlBottom + controlHeight;
|
|
int wizardBottom = butModeBottom + butModeHeight;
|
|
int executiveBottom = wizardBottom + wizardHeight;
|
|
|
|
int height = I->Height;
|
|
|
|
if (SettingGetGlobal_b(G, cSetting_internal_gui)) {
|
|
/* The Executive Block consists of the area in which object entries are
|
|
rendered, if the wizard doesn't exist, then this region extends all the
|
|
way down to the top of the ButMode block */
|
|
block = ExecutiveGetBlock(G);
|
|
block->setMargin(m_top, m_left, executiveBottom, m_right);
|
|
block->active = true;
|
|
|
|
/* The Wizard Block is shown when a wizard is loaded, it is the area between
|
|
the Executive Block and the ButMode Block, and is used for Wizard-related
|
|
info/buttons */
|
|
block = WizardGetBlock(G);
|
|
if (block) {
|
|
block->setMargin(
|
|
height - executiveBottom + 1, m_left, wizardBottom, m_right);
|
|
block->active = false;
|
|
}
|
|
|
|
/* The ButMode block shows info about which Mouse Mode, Selecting Mode,
|
|
State info, and other info like frame rate. It is located under the
|
|
Wizard Block, and above the Control Block */
|
|
block = ButModeGetBlock(G);
|
|
block->setMargin(height - wizardBottom + 1, m_left, butModeBottom, m_right);
|
|
block->active = true;
|
|
|
|
/* Controls are the Movie/Scene arrow buttons at the very bottom */
|
|
block = ControlGetBlock(G);
|
|
block->setMargin(
|
|
height - butModeBottom + 1, m_left, controlBottom, m_right);
|
|
block->active = true;
|
|
} else {
|
|
/* The Executive Block consists of the area in which object entries are
|
|
rendered, if the wizard doesn't exist, then this region extends all the
|
|
way down to the top of the ButMode block */
|
|
block = ExecutiveGetBlock(G);
|
|
block->setMargin(m_right, m_bottom, m_right, m_bottom);
|
|
block->active = false;
|
|
|
|
/* The Wizard Block is shown when a wizard is loaded, it is the area between
|
|
the Executive Block and the ButMode Block, and is used for Wizard-related
|
|
info/buttons */
|
|
block = WizardGetBlock(G);
|
|
if (block) {
|
|
block->setMargin(m_right, m_bottom, m_right, m_bottom);
|
|
block->active = false;
|
|
}
|
|
|
|
/* The ButMode block shows info about which Mouse Mode, Selecting Mode,
|
|
State info, and other info like frame rate. It is located under the
|
|
Wizard Block, and above the Control Block */
|
|
block = ButModeGetBlock(G);
|
|
block->setMargin(m_right, m_bottom, m_right, m_bottom);
|
|
block->active = false;
|
|
|
|
/* Controls are the Movie/Scene arrow buttons at the very bottom */
|
|
block = ControlGetBlock(G);
|
|
block->setMargin(m_right, m_bottom, m_right, m_bottom);
|
|
block->active = false;
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoReshape(PyMOLGlobals* G, int width, int height, int force)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
|
|
if (!G->HaveGUI && width < 0)
|
|
return;
|
|
|
|
Block* block = nullptr;
|
|
int sceneBottom, sceneRight = 0;
|
|
int textBottom = 0;
|
|
int internal_gui_width;
|
|
int internal_feedback;
|
|
int sceneTop = 0;
|
|
|
|
PRINTFD(G, FB_Ortho)
|
|
" OrthoReshape-Debug: %d %d\n", width, height ENDFD;
|
|
|
|
I->WrapXFlag = false;
|
|
if (width > 0) {
|
|
int stereo = SettingGetGlobal_i(G, cSetting_stereo);
|
|
int stereo_mode = SettingGetGlobal_i(G, cSetting_stereo_mode);
|
|
if (stereo) {
|
|
switch (stereo_mode) {
|
|
case cStereo_geowall:
|
|
case cStereo_dynamic:
|
|
width = width / 2;
|
|
I->WrapXFlag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((width != I->Width) || (height != I->Height) || force) {
|
|
if (width < 0)
|
|
width = I->Width;
|
|
if (height < 0)
|
|
height = I->Height;
|
|
|
|
I->Height = height;
|
|
I->Width = width;
|
|
I->ShowLines = height / cOrthoLineHeight;
|
|
|
|
textBottom += MovieGetPanelHeight(G);
|
|
I->TextBottom = textBottom;
|
|
|
|
internal_feedback = SettingGetGlobal_i(G, cSetting_internal_feedback);
|
|
if (internal_feedback)
|
|
sceneBottom = textBottom + (internal_feedback - 1) * cOrthoLineHeight +
|
|
cOrthoBottomSceneMargin;
|
|
else
|
|
sceneBottom = textBottom;
|
|
|
|
internal_gui_width =
|
|
DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_width));
|
|
if (!SettingGetGlobal_b(G, cSetting_internal_gui)) {
|
|
internal_gui_width = 0;
|
|
sceneRight = 0;
|
|
} else {
|
|
auto gui_mode =
|
|
SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting);
|
|
switch (gui_mode) {
|
|
case InternalGUIMode::Transparent:
|
|
sceneRight = 0;
|
|
sceneBottom = 0;
|
|
break;
|
|
default:
|
|
sceneRight = internal_gui_width;
|
|
break;
|
|
}
|
|
}
|
|
|
|
{
|
|
int seqHeight;
|
|
block = SeqGetBlock(G);
|
|
block->active = true;
|
|
|
|
/* reloate the sequence viewer as necessary */
|
|
|
|
if (SettingGetGlobal_b(G, cSetting_seq_view_location)) {
|
|
|
|
block->setMargin(height - sceneBottom - 10, 0, sceneBottom, sceneRight);
|
|
block->reshape(width, height);
|
|
seqHeight = SeqGetHeight(G);
|
|
block->setMargin(
|
|
height - sceneBottom - seqHeight, 0, sceneBottom, sceneRight);
|
|
if (!SettingGetGlobal_b(G, cSetting_seq_view_overlay)) {
|
|
sceneBottom += seqHeight;
|
|
}
|
|
|
|
} else {
|
|
|
|
block->setMargin(0, 0, height - 10, sceneRight);
|
|
block->reshape(width, height);
|
|
seqHeight = SeqGetHeight(G);
|
|
block->setMargin(0, 0, height - seqHeight, sceneRight);
|
|
if (!SettingGetGlobal_b(G, cSetting_seq_view_overlay)) {
|
|
sceneTop = seqHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
OrthoLayoutPanel(G, 0, width - internal_gui_width, textBottom, 0);
|
|
|
|
block = MovieGetBlock(G);
|
|
block->setMargin(height - textBottom, 0, 0, 0);
|
|
block->active = textBottom ? true : false;
|
|
|
|
block = SceneGetBlock(G);
|
|
block->setMargin(sceneTop, 0, sceneBottom, sceneRight);
|
|
|
|
block = nullptr;
|
|
for (auto block : I->Blocks) {
|
|
block->reshape(width, height);
|
|
}
|
|
|
|
WizardRefresh(G); /* safe to call even if no wizard exists */
|
|
}
|
|
SceneInvalidateStencil(G);
|
|
G->ShaderMgr->ResetUniformSet();
|
|
OrthoInvalidateDoDraw(G);
|
|
OrthoDirty(G);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoReshapeWizard(PyMOLGlobals* G, ov_size wizHeight)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->WizardHeight = wizHeight;
|
|
|
|
if (SettingGetGlobal_b(G, cSetting_internal_gui)) {
|
|
Block* block;
|
|
int internal_gui_width =
|
|
DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_width));
|
|
|
|
OrthoLayoutPanel(G, 0, I->Width - internal_gui_width, I->TextBottom, 0);
|
|
|
|
block = ExecutiveGetBlock(G);
|
|
block->reshape(I->Width, I->Height);
|
|
block = WizardGetBlock(G);
|
|
if (block) {
|
|
block->reshape(I->Width, I->Height);
|
|
block->active = wizHeight ? true : false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
ClickSide OrthoGetWrapClickSide(PyMOLGlobals* G)
|
|
{
|
|
return G->Ortho->WrapClickSide;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int OrthoButton(PyMOLGlobals* G, int button, int state, int x, int y, int mod)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
Block* block = nullptr;
|
|
int handled = 0;
|
|
|
|
PRINTFB(G, FB_Ortho, FB_Blather)
|
|
"OrthoButton: button:%d, state=%d, x=%d, y=%d, mod=%d\n", button, state, x, y,
|
|
mod ENDFB(G);
|
|
|
|
switch (button) {
|
|
case P_GLUT_BUTTON_SCROLL_FORWARD:
|
|
case P_GLUT_BUTTON_SCROLL_BACKWARD:
|
|
if ((button != I->ActiveButton) && (I->ActiveButton >= 0) &&
|
|
(I->ActiveButton < 3)) {
|
|
/* suppress wheel events when a button is already pushed */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (I->WrapXFlag) {
|
|
if (state == P_GLUT_DOWN) {
|
|
x = get_wrap_x(x, nullptr, G->Option->winX, &I->WrapClickSide);
|
|
} else {
|
|
x = get_wrap_x(x, &I->LastX, G->Option->winX, &I->WrapClickSide);
|
|
}
|
|
} else {
|
|
I->WrapClickSide = ClickSide::None;
|
|
}
|
|
|
|
OrthoRemoveSplash(G);
|
|
OrthoRemoveAutoOverlay(G);
|
|
I->X = x;
|
|
I->Y = y;
|
|
I->LastX = x;
|
|
I->LastY = y;
|
|
I->LastModifiers = mod;
|
|
|
|
if (state == P_GLUT_DOWN) {
|
|
I->ActiveButton = button;
|
|
if (I->GrabbedBy) {
|
|
block = I->GrabbedBy;
|
|
} else if (!block)
|
|
block = G->Ortho->findBlock(x, y);
|
|
if (block) {
|
|
I->ClickedIn = block;
|
|
handled = block->click(button, x, y, mod);
|
|
}
|
|
} else if (state == P_GLUT_UP) {
|
|
if (I->IssueViewportWhenReleased) {
|
|
OrthoCommandIn(G, "viewport");
|
|
I->IssueViewportWhenReleased = false;
|
|
}
|
|
|
|
if (I->GrabbedBy) {
|
|
block = I->GrabbedBy;
|
|
handled = block->release(button, x, y, mod);
|
|
I->ClickedIn = nullptr;
|
|
}
|
|
if (I->ClickedIn) {
|
|
block = I->ClickedIn;
|
|
handled = block->release(button, x, y, mod);
|
|
I->ClickedIn = nullptr;
|
|
}
|
|
I->ActiveButton = -1;
|
|
}
|
|
if (handled)
|
|
OrthoInvalidateDoDraw(G);
|
|
return (handled);
|
|
}
|
|
|
|
int OrthoButtonDefer(
|
|
PyMOLGlobals* G, int button, int state, int x, int y, int mod)
|
|
{
|
|
std::function<void()> deferred = [=]() {
|
|
OrthoButton(G, button, state, x, y, mod);
|
|
};
|
|
OrthoDefer(G, std::move(deferred));
|
|
return 1;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int OrthoDrag(PyMOLGlobals* G, int x, int y, int mod)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
|
|
Block* block = nullptr;
|
|
int handled = 0;
|
|
|
|
if (I->WrapXFlag) {
|
|
x = get_wrap_x(x, &I->LastX, G->Option->winX, nullptr);
|
|
}
|
|
|
|
I->LastX = x;
|
|
I->LastY = y;
|
|
I->LastModifiers = mod;
|
|
|
|
I->X = x;
|
|
I->Y = y;
|
|
if (I->GrabbedBy) {
|
|
block = I->GrabbedBy;
|
|
handled = block->drag(x, y, mod);
|
|
} else if (I->ClickedIn) {
|
|
block = I->ClickedIn;
|
|
handled = block->drag(x, y, mod);
|
|
}
|
|
if (handled &&
|
|
block !=
|
|
SceneGetBlock(
|
|
G)) // if user is not draging inside scene, then update OrthoCGO
|
|
OrthoInvalidateDoDraw(G);
|
|
return (handled);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoSplash(PyMOLGlobals* G)
|
|
{
|
|
|
|
/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
|
|
#ifdef _PYMOL_IP_SPLASH
|
|
#include "OrthoIPSplash.h"
|
|
#else
|
|
if (G->Option->incentive_product) {
|
|
#ifdef AXPYMOL_EVAL
|
|
PRINTF
|
|
" AxPyMOL(TM) Evaluation Product - Copyright (c) Schrodinger, LLC.\n "
|
|
"\n" ENDF(G);
|
|
PRINTF
|
|
" This Executable Build integrates and extends Open-Source PyMOL " ENDF(G);
|
|
PRINTF _PyMOL_VERSION ENDF(G);
|
|
PRINTF ".\n" ENDF(G);
|
|
#else
|
|
PRINTF " PyMOL(TM) Incentive Product - Copyright (c) Schrodinger, LLC.\n "
|
|
"\n" ENDF(G);
|
|
PRINTF
|
|
" This Executable Build integrates and extends Open-Source PyMOL " ENDF(G);
|
|
PRINTF _PyMOL_VERSION ENDF(G);
|
|
PRINTF ".\n" ENDF(G);
|
|
#endif
|
|
} else
|
|
|
|
/* END PROPRIETARY CODE SEGMENT */
|
|
{
|
|
/* Splash message for unrestricted access open-source versions... */
|
|
PRINTF " PyMOL(TM) Molecular Graphics System, Version " ENDF(G);
|
|
PRINTF _PyMOL_VERSION ENDF(G);
|
|
PRINTF ".\n" ENDF(G);
|
|
PRINTF
|
|
" Copyright (c) Schrodinger, LLC.\n All Rights Reserved.\n \n" ENDF(G);
|
|
|
|
PRINTF " Created by Warren L. DeLano, Ph.D. \n \n" ENDF(G);
|
|
|
|
/* PRINTF " Other Major Authors and Contributors:\n\n" ENDF(G);
|
|
* PRINTF " Ralf W. Grosse-Kunstleve, Ph.D.\n \n" ENDF(G);
|
|
*
|
|
* NOTICE: Enduring thanks to Ralf, but in point of fact, his
|
|
* sglite module is no longer used by PyMOL, and thus we should
|
|
* not mislead everyone by asserting otherwise... */
|
|
|
|
PRINTF " PyMOL is user-supported open-source software. Although some "
|
|
"versions\n" ENDF(G);
|
|
PRINTF " are freely available, PyMOL is not in the public domain.\n "
|
|
"\n" ENDF(G);
|
|
|
|
PRINTF " If PyMOL is helpful in your work or study, then please "
|
|
"volunteer \n" ENDF(G);
|
|
PRINTF
|
|
" support for our ongoing efforts to create open and affordable "
|
|
"scientific\n" ENDF(G);
|
|
PRINTF
|
|
" software by purchasing a PyMOL Maintenance and/or Support "
|
|
"subscription.\n\n" ENDF(G);
|
|
|
|
PRINTF " More information can be found at \"http://www.pymol.org\".\n "
|
|
"\n" ENDF(G);
|
|
|
|
PRINTF " Enter \"help\" for a list of commands.\n" ENDF(G);
|
|
PRINTF
|
|
" Enter \"help <command-name>\" for information on a specific "
|
|
"command.\n\n" ENDF(G);
|
|
|
|
PRINTF " Hit ESC anytime to toggle between text and graphics.\n\n" ENDF(G);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int OrthoInit(PyMOLGlobals* G, int showSplash)
|
|
{
|
|
COrtho* I = nullptr;
|
|
|
|
if ((I = (G->Ortho = new COrtho()))) {
|
|
|
|
I->ActiveButton = -1;
|
|
I->Pushed = 0;
|
|
I->cmdActiveQueue = &(*I->cmdQueue.begin());
|
|
I->cmdNestLevel = 0;
|
|
I->WrapXFlag = false;
|
|
|
|
I->WizardBackColor[0] = 0.2F;
|
|
I->WizardBackColor[1] = 0.2F;
|
|
I->WizardBackColor[2] = 0.2F;
|
|
I->WizardTextColor[0] = 0.2F;
|
|
I->WizardTextColor[1] = 1.0F;
|
|
I->WizardTextColor[2] = 0.2F;
|
|
|
|
I->GrabbedBy = nullptr;
|
|
I->ClickedIn = nullptr;
|
|
I->HaveSeqViewer = false;
|
|
I->TextColor[0] = 0.83F;
|
|
I->TextColor[1] = 0.83F;
|
|
I->TextColor[2] = 1.0;
|
|
I->OverlayColor[0] = 1.0;
|
|
I->OverlayColor[1] = 1.0;
|
|
I->OverlayColor[2] = 1.0;
|
|
I->CurLine = 1000;
|
|
I->PromptChar = 0;
|
|
I->CurChar = 0;
|
|
I->CurLine = 0;
|
|
I->AutoOverlayStopLine = 0;
|
|
I->CursorChar = -1;
|
|
I->HistoryLine = 0;
|
|
I->HistoryView = 0;
|
|
I->Line[I->CurLine & OrthoSaveLines][I->CurChar] = 0;
|
|
I->WizardPromptVLA = nullptr;
|
|
I->SplashFlag = false;
|
|
I->ShowLines = 1;
|
|
I->Saved[0] = 0;
|
|
I->DirtyFlag = true;
|
|
I->LastDraw = UtilGetSeconds(G);
|
|
I->DrawTime = 0.0;
|
|
I->bgCGO = nullptr;
|
|
I->bgData = nullptr;
|
|
I->orthoCGO = nullptr;
|
|
I->orthoFastCGO = nullptr;
|
|
|
|
if (showSplash) {
|
|
OrthoSplash(G);
|
|
I->SplashFlag = true;
|
|
}
|
|
/* OrthoFeedbackIn(G," "); */
|
|
I->CurLine++;
|
|
|
|
#ifndef _PYMOL_LIB
|
|
/* prompt (and typing) should only be shown for PyMOL, not libpymol */
|
|
strcpy(I->Prompt, "PyMOL>");
|
|
#endif
|
|
strcpy(I->Line[I->CurLine], I->Prompt);
|
|
I->CurChar = (I->PromptChar = strlen(I->Prompt));
|
|
I->InputFlag = 1;
|
|
|
|
/*printf("orthoNewLine: CC: %d CL:%d PC: %d IF:L
|
|
%d\n",I->CurChar,I->CurLine, I->PromptChar,I->InputFlag); */
|
|
|
|
PopInit(G);
|
|
{
|
|
int a;
|
|
for (a = 0; a <= OrthoHistoryLines; a++)
|
|
I->History[a][0] = 0;
|
|
}
|
|
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoFree(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
|
|
VLAFreeP(I->WizardPromptVLA);
|
|
PopFree(G);
|
|
{
|
|
I->cmdActiveQueue = nullptr;
|
|
}
|
|
|
|
I->bgData = nullptr;
|
|
|
|
CGOFree(I->bgCGO);
|
|
CGOFree(I->orthoCGO);
|
|
CGOFree(I->orthoFastCGO);
|
|
delete G->Ortho;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoPushMatrix(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
|
|
if (G->HaveGUI && G->ValidContext) {
|
|
if (!I->Pushed) {
|
|
I->Viewport = SceneGetViewport(G);
|
|
}
|
|
auto viewport = I->Viewport;
|
|
if (I->RenderMode == OrthoRenderMode::GeoWallRight) {
|
|
viewport.offset.x += viewport.extent.width;
|
|
}
|
|
SceneSetViewport(G, viewport);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glOrtho(
|
|
0, I->Viewport.extent.width, 0, I->Viewport.extent.height, -100, 100);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glTranslatef(0.33F, 0.33F, 0.0F); /* this generates better
|
|
rasterization on macs */
|
|
|
|
glDisable(GL_ALPHA_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_FOG);
|
|
glDisable(GL_NORMALIZE);
|
|
glDisable(GL_COLOR_MATERIAL);
|
|
glDisable(GL_LINE_SMOOTH);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_DITHER);
|
|
|
|
#ifndef PURE_OPENGL_ES_2
|
|
glShadeModel(
|
|
SettingGetGlobal_b(G, cSetting_pick_shading) ? GL_FLAT : GL_SMOOTH);
|
|
#endif
|
|
if (G->Option->multisample)
|
|
glDisable(0x809D); /* GL_MULTISAMPLE_ARB */
|
|
I->Pushed++;
|
|
}
|
|
/* glDisable(GL_ALPHA_TEST);
|
|
glDisable(GL_CULL_FACE);
|
|
glDisable(GL_POINT_SMOOTH); */
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoPopMatrix(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
if (G->HaveGUI && G->ValidContext) {
|
|
|
|
if (I->Pushed >= 0) {
|
|
SceneSetViewport(G, I->Viewport);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
I->Pushed--;
|
|
}
|
|
}
|
|
}
|
|
|
|
int OrthoGetPushed(PyMOLGlobals* G)
|
|
{
|
|
return G->Ortho->Pushed;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoCommandIn(COrtho& ortho, const char* buffer)
|
|
{
|
|
if (ortho.cmdActiveQueue) {
|
|
ortho.cmdActiveQueue->emplace(buffer);
|
|
}
|
|
}
|
|
|
|
void OrthoCommandIn(PyMOLGlobals* G, const char* buffer)
|
|
{
|
|
OrthoCommandIn(*G->Ortho, buffer);
|
|
}
|
|
|
|
void OrthoCommandSetBusy(PyMOLGlobals* G, int busy)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
I->cmdActiveBusy = busy;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void OrthoPasteIn(PyMOLGlobals* G, const char* buffer)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
int curLine = I->CurLine & OrthoSaveLines;
|
|
int execFlag = false;
|
|
OrthoLineType buf2;
|
|
|
|
if (I->InputFlag) {
|
|
if (I->CursorChar >= 0) {
|
|
strcpy(buf2, I->Line[curLine] + I->CursorChar);
|
|
strcpy(I->Line[curLine] + I->CursorChar, buffer);
|
|
I->CurChar = strlen(I->Line[curLine]);
|
|
I->CursorChar = I->CurChar;
|
|
while ((I->Line[curLine][I->CurChar - 1] == 10) ||
|
|
(I->Line[curLine][I->CurChar - 1] == 13)) {
|
|
execFlag = true;
|
|
I->CurChar--;
|
|
I->Line[curLine][I->CurChar] = 0;
|
|
if (I->CurChar <= I->PromptChar)
|
|
break;
|
|
}
|
|
if (!execFlag) {
|
|
strcpy(I->Line[curLine] + I->CursorChar, buf2);
|
|
I->CurChar = strlen(I->Line[curLine]);
|
|
}
|
|
} else {
|
|
strcat(I->Line[curLine], buffer);
|
|
I->CurChar = strlen(I->Line[curLine]);
|
|
while ((I->Line[curLine][I->CurChar - 1] == 10) ||
|
|
(I->Line[curLine][I->CurChar - 1] == 13)) {
|
|
execFlag = true;
|
|
I->CurChar--;
|
|
I->Line[curLine][I->CurChar] = 0;
|
|
if (I->CurChar <= I->PromptChar)
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
OrthoRestorePrompt(G);
|
|
|
|
while ((I->Line[curLine][I->CurChar - 1] == 10) ||
|
|
(I->Line[curLine][I->CurChar - 1] == 13)) {
|
|
execFlag = true;
|
|
I->CurChar--;
|
|
I->Line[curLine][I->CurChar] = 0;
|
|
if (I->CurChar <= I->PromptChar)
|
|
break;
|
|
}
|
|
}
|
|
if (execFlag) {
|
|
printf("[%s]\n", I->Line[curLine]);
|
|
OrthoParseCurrentLine(G);
|
|
} else
|
|
I->InputFlag = true;
|
|
}
|
|
|
|
/* TODO: Removed. Check in Mobile PyMOL to see if needed - PYMOL-3148*/
|
|
void OrthoSetBackgroundImage(
|
|
PyMOLGlobals* G, const char* image_data, int width, int height)
|
|
{
|
|
#if 0
|
|
COrtho *I = G->Ortho;
|
|
int buff_total = width * height;
|
|
short should_update = 0;
|
|
if (I->bgData){
|
|
FreeP(I->bgData);
|
|
I->bgData = nullptr;
|
|
I->bgWidth = 0;
|
|
I->bgHeight = 0;
|
|
should_update = 1;
|
|
}
|
|
if (buff_total){
|
|
I->bgData = pymol::malloc<unsigned char>(buff_total*4);
|
|
I->bgWidth = width;
|
|
I->bgHeight = height;
|
|
memcpy(I->bgData, image_data, buff_total * 4);
|
|
should_update = 1;
|
|
}
|
|
if (should_update){
|
|
G->ShaderMgr->Reload_All_Shaders();
|
|
I->bgTextureNeedsUpdate = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void OrthoInvalidateDoDraw(PyMOLGlobals* G)
|
|
{
|
|
COrtho* I = G->Ortho;
|
|
if (I->orthoCGO) {
|
|
CGOFree(I->orthoCGO);
|
|
PyMOL_NeedRedisplay(G->PyMOL);
|
|
}
|
|
}
|
|
|
|
void COrtho::draw(CGO* orthoCGO)
|
|
{
|
|
for (auto block : Blocks) {
|
|
block->recursiveDraw(orthoCGO);
|
|
}
|
|
}
|
|
|
|
bool COrtho::fastDraw(CGO* orthoCGO)
|
|
{
|
|
bool ret{false};
|
|
for (auto block : Blocks) {
|
|
ret |= block->recursiveFastDraw(orthoCGO);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Block* COrtho::findBlock(int x, int y)
|
|
{
|
|
for (auto blockIter = Blocks.rbegin(); blockIter != Blocks.rend();
|
|
++blockIter) {
|
|
auto blockFound = (*blockIter)->recursiveFind(x, y);
|
|
if (blockFound != nullptr) {
|
|
return blockFound;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Rect2D OrthoGetRect(PyMOLGlobals* G)
|
|
{
|
|
auto I = G->Ortho;
|
|
auto width = static_cast<std::uint32_t>(I->Width);
|
|
auto height = static_cast<std::uint32_t>(I->Height);
|
|
return {{0, 0}, {width, height}};
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Retrieves Ortho UI aspect ratio
|
|
* @return aspect ratio
|
|
*/
|
|
static float OrthoGetAspectRatio(PyMOLGlobals* G)
|
|
{
|
|
auto I = G->Ortho;
|
|
return static_cast<float>(I->Width) / static_cast<float>(I->Height);
|
|
}
|
|
|
|
/***
|
|
* @brief Draws top-level ortho at a given extent and draws into an offset
|
|
* @param offset offset to copy the drawn rendered ortho image into dstImage at
|
|
* @param extent extent of the ortho to draw
|
|
* @param dstImage image to draw into
|
|
*/
|
|
static void OrthoDrawSizedTile(PyMOLGlobals* G, const Offset2D& offset,
|
|
const Extent2D& extent, pymol::Image& dstImage)
|
|
{
|
|
auto offscreenFBO = G->ShaderMgr->bindOffscreenOrtho(extent, true);
|
|
|
|
G->ShaderMgr->setDrawBuffer(offscreenFBO);
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
|
OrthoReshape(G, extent.width, extent.height, true);
|
|
OrthoInvalidateDoDraw(G);
|
|
OrthoDrawInfo drawInfo{};
|
|
drawInfo.renderScene = false;
|
|
drawInfo.renderMode = OrthoRenderMode::Main;
|
|
drawInfo.viewport = Rect2D{offset.x, offset.y, extent.width, extent.height};
|
|
drawInfo.offscreenRender = true;
|
|
glViewport(drawInfo.viewport->offset.x, drawInfo.viewport->offset.y,
|
|
drawInfo.viewport->extent.width, drawInfo.viewport->extent.height);
|
|
OrthoDoDraw(G, drawInfo);
|
|
auto tileImg = GLImageToPyMOLImage(G, offscreenFBO, SceneGetRect(G));
|
|
|
|
if (!tileImg.empty()) { /* the image into place */
|
|
Rect2D srcRect {{}, extent};
|
|
Rect2D dstRect{offset, OrthoGetExtent(G)};
|
|
PyMOLImageCopy(tileImg, dstImage, srcRect, dstRect);
|
|
} // if tileImg not empty
|
|
}
|
|
|
|
static void OrthoDrawSizedTiles(PyMOLGlobals* G,
|
|
const Extent2D& extent, pymol::Image& dstImage)
|
|
{
|
|
auto I = G->Ortho;
|
|
|
|
int nXStep = (extent.width / (I->Width + 1)) + 1;
|
|
int nYStep = (extent.height / (I->Height + 1)) + 1;
|
|
int total_steps = nXStep * nYStep;
|
|
|
|
OrthoBusyPrime(G);
|
|
|
|
for (int y = 0; y < nYStep; y++) {
|
|
for (int x = 0; x < nXStep; x++) {
|
|
Offset2D offset{x = -(I->Width * x), y = -(I->Height * y)};
|
|
OrthoBusyFast(G, y * nXStep + x, total_steps);
|
|
OrthoDrawSizedTile(G, offset, extent, dstImage);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Ensures that the extent has a valid width and height
|
|
* @param extent extent to calculate
|
|
* @return extent with valid width and height
|
|
* @note If the width or height is 0, it will be calculated based on the
|
|
* current ortho aspect ratio.
|
|
*/
|
|
static Extent2D OrthoCalculateImplicitExtent(PyMOLGlobals* G, Extent2D extent)
|
|
{
|
|
float orthoAspectRatio = OrthoGetAspectRatio(G);
|
|
if (extent.width == 0 && extent.height == 0) {
|
|
extent = OrthoGetExtent(G);
|
|
} else if (extent.width != 0 && extent.height == 0) {
|
|
extent.height = static_cast<std::uint32_t>(extent.width / orthoAspectRatio);
|
|
} else if (extent.height != 0 && extent.width == 0) {
|
|
extent.width = static_cast<std::uint32_t>(extent.height * orthoAspectRatio);
|
|
}
|
|
return extent;
|
|
}
|
|
|
|
/**
|
|
* @brief Makes an image of the current ortho view
|
|
* @param extent extent of the image to make
|
|
* @param quiet suppresses error messages
|
|
*/
|
|
static pymol::Result<pymol::Image> OrthoMakeSizedImage(
|
|
PyMOLGlobals* G, Extent2D extent, bool quiet)
|
|
{
|
|
auto* I = G->Ortho;
|
|
|
|
extent = OrthoCalculateImplicitExtent(G, extent);
|
|
|
|
std::optional<Extent2D> saveExtent;
|
|
if (!((extent.width > 0) && (extent.height > 0) && (I->Width > 0) &&
|
|
(I->Height > 0))) {
|
|
if (saveExtent) {
|
|
OrthoSetExtent(G, *saveExtent);
|
|
}
|
|
return pymol::make_error(
|
|
"OrthoMakeSizedImage-Error: invalid image dimensions");
|
|
}
|
|
|
|
if (!(G->HaveGUI && G->ValidContext)) {
|
|
if (saveExtent) {
|
|
OrthoSetExtent(G, *saveExtent);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
auto maxDim = SceneGLGetMaxDimensions(G);
|
|
auto clampedExtents = ExtentClampByAspectRatio(extent, maxDim);
|
|
auto upscaledExtentInfo = ExtentGetUpscaleInfo(G, clampedExtents, maxDim, 0);
|
|
extent = upscaledExtentInfo.extent;
|
|
|
|
if (!saveExtent) {
|
|
saveExtent = OrthoGetExtent(G);
|
|
}
|
|
|
|
OrthoSetExtent(G, extent);
|
|
G->ShaderMgr->bindOffscreenOrtho(extent, true);
|
|
|
|
pymol::Image final_image(extent.width, extent.height);
|
|
|
|
// Save before we change scene extents in OrthoDrawSizedTile
|
|
auto currSceneExtent = SceneGetExtent(G);
|
|
|
|
OrthoDrawSizedTiles(G, extent, final_image);
|
|
|
|
if (saveExtent) {
|
|
OrthoSetExtent(G, *saveExtent);
|
|
OrthoReshape(G, currSceneExtent.width, currSceneExtent.height, true);
|
|
}
|
|
|
|
OrthoInvalidateDoDraw(G);
|
|
return final_image;
|
|
}
|
|
|
|
pymol::Result<bool> OrthoDeferImage(PyMOLGlobals* G, Extent2D extent, const char* filename,
|
|
int antialias, float dpi, int format, int quiet, pymol::Image* out_img,
|
|
bool with_overlay)
|
|
{
|
|
std::string filename_str = filename ? filename : "";
|
|
std::function<void()> deferred = [=]() {
|
|
auto prior = SceneDeferImage(G, extent, filename_str.c_str(), antialias,
|
|
dpi, format, quiet, out_img);
|
|
if (prior) {
|
|
// Something went bad here. Should fire on deferred.
|
|
return;
|
|
}
|
|
if (!with_overlay) {
|
|
return;
|
|
}
|
|
auto overlay = OrthoMakeSizedImage(G, extent, quiet);
|
|
if (overlay) {
|
|
if (auto composite = PyMOLImageComposite(G, *G->Scene->Image, *overlay)) {
|
|
G->Scene->Image = std::make_shared<pymol::Image>(*composite);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (G->ValidContext) {
|
|
deferred();
|
|
return false;
|
|
}
|
|
|
|
OrthoDefer(G, std::move(deferred));
|
|
return true;
|
|
}
|