1 /*
2    A* -------------------------------------------------------------------
3    B* This file contains source code for the PyMOL computer program
4    C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
5    D* -------------------------------------------------------------------
6    E* It is unlawful to modify or remove this copyright notice.
7    F* -------------------------------------------------------------------
8    G* Please see the accompanying LICENSE file for further information.
9    H* -------------------------------------------------------------------
10    I* Additional authors of this source file include:
11    -*
12    -*
13    -*
14    Z* -------------------------------------------------------------------
15 */
16 #include"os_python.h"
17 #include"os_predef.h"
18 #include"os_std.h"
19 #include"os_gl.h"
20 
21 #include <set>
22 #include <algorithm>
23 #include <map>
24 #include <cassert>
25 #include <clocale>
26 
27 #include"Version.h"
28 #include"main.h"
29 #include"Base.h"
30 #include"OOMac.h"
31 #include"Executive.h"
32 #include"SpecRec.h"
33 #include"ObjectMap.h"
34 #include"ObjectMesh.h"
35 #include"ObjectDist.h"
36 #include"ObjectSurface.h"
37 #include"ObjectSlice.h"
38 #include"ObjectAlignment.h"
39 #include"ObjectGroup.h"
40 #include"ObjectVolume.h"
41 #include"ObjectCallback.h"
42 #include"ObjectMap.h"
43 #include"ListMacros.h"
44 #include"MyPNG.h"
45 #include"Ortho.h"
46 #include"Scene.h"
47 #include"ScenePicking.h"
48 #include"SceneRay.h"
49 #include"Selector.h"
50 #include"Vector.h"
51 #include"Color.h"
52 #include"Setting.h"
53 #include"Matrix.h"
54 #include"P.h"
55 #include"PConv.h"
56 #include"Match.h"
57 #include"ObjectCGO.h"
58 #include"Util.h"
59 #include"Util2.h"
60 #include"Wizard.h"
61 #include"ScrollBar.h"
62 #include"Movie.h"
63 #include"ObjectGadgetRamp.h"
64 #include"SculptCache.h"
65 #include"Control.h"
66 #include"Menu.h"
67 #include"Map.h"
68 #include"Editor.h"
69 #include"RepDot.h"
70 #include"Seq.h"
71 #include"Text.h"
72 #include"PyMOL.h"
73 #include"PyMOLOptions.h"
74 #include"Tracker.h"
75 #include"TrackerList.h"
76 #include"Word.h"
77 #include"main.h"
78 #include"Parse.h"
79 #include"PlugIOManager.h"
80 #include "Lex.h"
81 #include "List.h"
82 #include "AtomIterators.h"
83 
84 #include"OVContext.h"
85 #include"OVLexicon.h"
86 #include"OVOneToOne.h"
87 #include"OVOneToAny.h"
88 
89 #include"ShaderMgr.h"
90 #include"File.h"
91 #include"FileStream.h"
92 #include"ExecutiveLoad.h"
93 
94 #include "MovieScene.h"
95 #include "Texture.h"
96 
97 #ifdef _PYMOL_OPENVR
98 #include "OpenVRMode.h"
99 #endif
100 
101 #ifndef _PYMOL_NOPY
102 #include "ce_types.h"
103 #endif
104 
105 #ifdef WIN32
106 #define mkstemp _mktemp_s
107 #endif
108 
109 #define cExecObject 0
110 #define cExecSelection 1
111 #define cExecAll 2
112 
113 #define cTempRectSele "_rect"
114 #define cLeftButSele "lb"
115 #define cIndicateSele "indicate"
116 
117 typedef struct PanelRec {
118   SpecRec *spec;
119   int nest_level;
120   int is_group;
121   int is_open;
122   struct PanelRec *next;
123 } PanelRec;
124 
125 typedef struct {
126   int list_id;
127   int next;
128 } ListMember;
129 
130 struct CExecutive : public Block {
131   SpecRec *Spec {};
132   CTracker *Tracker {};
133   int Width {}, Height {}, HowFarDown { 0 };
134   int ScrollBarActive { 0 };
135   int NSkip { 0 };
136   ScrollBar m_ScrollBar;
137   CObject *LastEdited { nullptr };
138   int DragMode { 0 };
139   int Pressed { -1 }, Over { -1 }, LastOver {}, OldVisibility {}, ToggleMode {}, PressedWhat {}, OverWhat {};
140   SpecRec *LastChanged { nullptr }, *LastZoomed { nullptr }, *RecoverPressed { nullptr };
141   int ReorderFlag { false };
142   OrthoLineType ReorderLog {};
143 #ifndef GLUT_FULL_SCREEN
144   // freeglut has glutLeaveFullScreen, no need to remember window dimensions
145   int oldPX {}, oldPY {}, oldWidth {}, oldHeight {};
146 #endif
147   int all_names_list_id {}, all_obj_list_id {}, all_sel_list_id {};
148   OVLexicon *Lex {};
149   OVOneToOne *Key {};
150   bool ValidGroups { false };
151   bool ValidSceneMembers { false };
152   int ValidGridSlots {};
153   PanelRec *Panel {};
154   bool ValidPanel { false };
155 #ifdef _WEBGL
156 #endif
157   int CaptureFlag {};
158   int LastMotionCount {};
159   CGO *selIndicatorsCGO { nullptr };
160   int selectorTexturePosX { 0 }, selectorTexturePosY { 0 }, selectorTextureAllocatedSize { 0 }, selectorTextureSize { 0 };
161   short selectorIsRound { 0 };
162 
163   // AtomInfoType::unique_id -> (object, atom-index)
164   ExecutiveObjectOffset *m_eoo {}; // VLA of (object, atom-index)
165   OVOneToOne *m_id2eoo {}; // unique_id -> m_eoo-index
166 
CExecutiveCExecutive167   CExecutive(PyMOLGlobals * G) : Block(G), m_ScrollBar(G, false) {};
168 
169   int release(int button, int x, int y, int mod) override;
170   int click(int button, int x, int y, int mod) override;
171   int drag(int x, int y, int mod) override;
172   void draw(CGO* orthoCGO) override;
173   void reshape(int width, int height) override;
174 };
175 
176 #ifndef NO_MMLIBS
177 #include "mmpymolx.h"
178 #endif
179 
180 /**
181  * Macro with error handling to get a selection ID from a non-empty expression.
182  * @param expression Selection expression
183  * @param[out] tmpsele Name of the pymol::Result<SelectorTmp> instance
184  * @param[out] seleID Name of the SelectorID_t instance
185  */
186 #define SETUP_SELE(expression, tmpsele, seleID)                                \
187   auto tmpsele = SelectorTmp::make(G, expression);                             \
188   p_return_if_error(tmpsele);                                                  \
189   SelectorID_t seleID = tmpsele->getIndex();                                 \
190   if (seleID < 0) {                                                            \
191     return pymol::Error("This should not happen - PyMOL may have a bug");      \
192   }                                                                            \
193   assert(seleID != cSelectionInvalid)
194 
195 /**
196  * Macro with error handling to get a selection ID from a non-empty expression
197  * `sN`. Assigns `tmpseleN` and `seleN`.
198  */
199 #define SETUP_SELE_DEFAULT(N) SETUP_SELE(s##N, tmpsele##N, sele##N)
200 
201 /**
202  * Macro with error handling to get a selection ID from a non-empty expression
203  * `sN`. Assigns `tmpseleN` and `seleN`.
204  * Adds "Selection N: " prefix to error messages.
205  * @param same_value If `sN` is "same" then use this value for `seleN`
206  */
207 #define SETUP_SELE_DEFAULT_PREFIXED(N, same_value)                             \
208   pymol::Result<SelectorTmp> tmpsele##N;                                       \
209   SelectorID_t sele##N = same_value;                                           \
210   if (!WordMatchExact(G, s##N, cKeywordSame, true)) {                          \
211     tmpsele##N = SelectorTmp::make(G, s##N);                                   \
212     p_return_if_error_prefixed(tmpsele##N, "Selection " #N ": ");              \
213     sele##N = (tmpsele##N)->getIndex();                                        \
214   }                                                                            \
215   if (sele##N == cSelectionInvalid) {                                          \
216     return pymol::Error("Invalid selection " #N);                              \
217   }
218 
219 /* routines that still need to be updated for Tracker list iteration
220 
221 Low priority:
222 
223 ExecutiveSculptIterate
224 ExecutiveSculptActivate
225 ExecutiveSculptDeactivate
226 ExecutiveRenameObjectAtoms
227 ExecutiveSpheroid
228 
229 */
230 
231 static int ExecutiveGetNamesListFromPattern(PyMOLGlobals * G, const char *name,
232                                             int allow_partial, int expand_groups);
233 
234 /**
235  * Retrieves a list of candidates provided by a pattern. Similar to
236  * ExecutiveGetNamesListFromPattern but returns a managed tracker list.
237  *
238  * @param str pattern string provided by user
239  * @param allow_partial allows for partial name matching
240  * @param expand_groups members of the group candidate will be expanded onto the
241  * list.
242  * @return a managed list of candidate records
243  */
ExecutiveGetSpecRecsFromPattern(PyMOLGlobals * G,pymol::zstring_view str,bool allow_partial=true,bool expand_groups=true)244 pymol::TrackerAdapter<SpecRec> ExecutiveGetSpecRecsFromPattern(PyMOLGlobals* G,
245     pymol::zstring_view str, bool allow_partial = true, bool expand_groups = true)
246 {
247   return pymol::TrackerAdapter<SpecRec>(
248       G->Executive->Tracker, ExecutiveGetNamesListFromPattern(
249                                  G, str.c_str(), allow_partial, expand_groups));
250 }
251 
252 /**
253  * Get the list of objects which match the pattern, or all objects
254  * if pattern equals 'all'.
255  * @param str pattern string provided by user
256  * @return List of borrowed object pointers
257  */
ExecutiveGetObjectsFromPattern(PyMOLGlobals * G,pymol::zstring_view pattern)258 static std::vector<CObject*> ExecutiveGetObjectsFromPattern(
259     PyMOLGlobals* G, pymol::zstring_view pattern)
260 {
261   std::vector<CObject*> objects;
262 
263   for (auto& rec : ExecutiveGetSpecRecsFromPattern(G, pattern)) {
264     switch (rec.type) {
265     case cExecObject:
266       objects.push_back(rec.obj);
267       break;
268     case cExecAll:
269       for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
270         if (rec.type == cExecObject) {
271           objects.push_back(rec.obj);
272         }
273       }
274     }
275   }
276 
277   return objects;
278 }
279 
280 static void ExecutiveSpecEnable(PyMOLGlobals * G, SpecRec * rec, int parents, int log);
281 static void ExecutiveSetAllRepVisMask(PyMOLGlobals * G, int repmask, int state);
282 static SpecRec *ExecutiveFindSpec(PyMOLGlobals * G, const char *name);
283 static void ExecutiveSpecSetVisibility(PyMOLGlobals * G, SpecRec * rec,
284                                        int new_vis, int mod, int parents);
285 static int ExecutiveSetObjectMatrix2(PyMOLGlobals * G, CObject * obj, int state,
286                                      double *matrix);
287 static int ExecutiveGetObjectMatrix2(PyMOLGlobals * G, CObject * obj, int state,
288                                      double **matrix, int incl_ttt);
289 static pymol::Result<> ExecutiveTransformObjectSelection2(
290     PyMOLGlobals* G, CObject* obj, int state, const char* s1, int log,
291     const float* matrix, int homogenous, int global);
292 
293 /*
294  * ObjectIterator methods
295  */
reset()296 void ObjectIterator::reset() {
297   rec = G->Executive->Spec;
298 
299   // DEBUG assume first element is always "all"
300   if (rec->type != cExecAll)
301     printf("Error: first SpecRec is not cExecAll\n");
302 }
303 
next()304 bool ObjectIterator::next() {
305   if (!rec || !(rec = rec->next))
306     return false;
307 
308   if (rec->type != cExecObject)
309     return next();
310 
311   return true;
312 }
313 
getObject()314 CObject * ObjectIterator::getObject() {
315   return rec->obj;
316 }
317 
318 /**
319  * Find object of given type, or delete object if it exists but has the wrong
320  * type.
321  * @param name Object name
322  * @return NULL if object can't be found or has the wrong type
323  */
324 template <typename ObjectT>
ExecutiveFindOrDeleteObject(PyMOLGlobals * G,pymol::zstring_view name)325 ObjectT* ExecutiveFindOrDeleteObject(PyMOLGlobals* G, pymol::zstring_view name)
326 {
327   auto anyObj = ExecutiveFindObjectByName(G, name.c_str());
328   auto obj = dynamic_cast<ObjectT*>(anyObj);
329   if (anyObj && !obj) {
330     // incompatible object with the same name
331     ExecutiveDelete(G, name.c_str());
332   }
333   return obj;
334 }
335 
336 /*
337  * True if `rec` and all its parent groups are enabled
338  */
SpecRecIsEnabled(const SpecRec * rec)339 static bool SpecRecIsEnabled(const SpecRec * rec) {
340   while (rec->visible && (rec = rec->group)) {}
341   return !rec;
342 }
343 
ExecutiveIsObjectType(const SpecRec & rec,int cObjectType)344 static bool ExecutiveIsObjectType(const SpecRec& rec, int cObjectType)
345 {
346   return rec.type == cExecObject && rec.obj->type == cObjectType;
347 }
348 
349 /* ======================================================= */
350 
ReportEnabledChange(PyMOLGlobals * G,SpecRec * rec)351 static void ReportEnabledChange(PyMOLGlobals * G, SpecRec *rec){
352 #ifdef _PYMOL_LIB
353   if (G->CallbackObject && G->enabledCallback){
354     G->enabledCallback(G->CallbackObject, rec->name, rec->visible);
355   }
356 #endif
357   OrthoInvalidateDoDraw(G);
358   ExecutiveInvalidateSelectionIndicatorsCGO(G);
359 }
360 
ExecutiveGroupMotionModify(PyMOLGlobals * G,CObject * group,int action,int index,int count,int target,int freeze)361 int ExecutiveGroupMotionModify(PyMOLGlobals *G, CObject *group, int action,
362                                 int index, int count, int target, int freeze)
363 {
364   CExecutive *I = G->Executive;
365   int result = true;
366   CTracker *I_Tracker = I->Tracker;
367   int list_id = ExecutiveGetExpandedGroupList(G,group->Name);
368   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
369   SpecRec *rec;
370   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
371     if(rec) {
372       switch (rec->type) {
373       case cExecObject:
374         if(rec->obj->type != cObjectGroup) {
375           ObjectMotionModify(rec->obj, action, index, count, target, freeze, true);
376         }
377         break;
378       }
379     }
380   }
381   TrackerDelList(I_Tracker, list_id);
382   TrackerDelIter(I_Tracker, iter_id);
383   return result;
384 }
385 
ExecutiveGroupMotion(PyMOLGlobals * G,CObject * group,int action,int first,int last,float power,float bias,int simple,float linear,int wrap,int hand,int window,int cycles,int state,int quiet)386 int ExecutiveGroupMotion(PyMOLGlobals *G, CObject *group,int action, int first,
387                          int last, float power, float bias,
388                          int simple, float linear, int wrap,
389                          int hand, int window, int cycles, int state, int quiet)
390 {
391   CExecutive *I = G->Executive;
392   int result = true;
393   CTracker *I_Tracker = I->Tracker;
394   int list_id = ExecutiveGetExpandedGroupList(G,group->Name);
395   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
396   SpecRec *rec;
397   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
398     if(rec) {
399       switch (rec->type) {
400       case cExecObject:
401         if(rec->obj->type != cObjectGroup) {
402           ObjectMotion(rec->obj,action,first,last,power,bias,simple,linear,wrap,hand,window,cycles,state,quiet);
403         }
404         break;
405       }
406     }
407   }
408   TrackerDelList(I_Tracker, list_id);
409   TrackerDelIter(I_Tracker, iter_id);
410   return result;
411 }
412 
ExecutiveGroupCombineTTT(PyMOLGlobals * G,CObject * group,const float * ttt,int reverse_order,int store)413 int ExecutiveGroupCombineTTT(PyMOLGlobals *G, CObject *group, const float *ttt, int reverse_order, int store)
414 {
415   CExecutive *I = G->Executive;
416   int result = true;
417   CTracker *I_Tracker = I->Tracker;
418   int list_id = ExecutiveGetExpandedGroupList(G,group->Name);
419   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
420   SpecRec *rec;
421   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
422     if(rec) {
423       switch (rec->type) {
424       case cExecObject:
425         if(rec->obj->type != cObjectGroup) {
426           ObjectCombineTTT(rec->obj, ttt, reverse_order, store);
427         }
428         break;
429       }
430     }
431   }
432   TrackerDelList(I_Tracker, list_id);
433   TrackerDelIter(I_Tracker, iter_id);
434   return result;
435 }
436 
ExecutiveGroupTranslateTTT(PyMOLGlobals * G,CObject * group,const float * v,int store)437 int ExecutiveGroupTranslateTTT(PyMOLGlobals *G, CObject *group, const float *v, int store)
438 {
439   CExecutive *I = G->Executive;
440   int result = true;
441   CTracker *I_Tracker = I->Tracker;
442   int list_id = ExecutiveGetExpandedGroupList(G,group->Name);
443   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
444   SpecRec *rec;
445   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
446     if(rec) {
447       switch (rec->type) {
448       case cExecObject:
449         if(rec->obj->type != cObjectGroup) {
450           ObjectTranslateTTT(rec->obj,v, store);
451         }
452         break;
453       }
454     }
455   }
456   TrackerDelList(I_Tracker, list_id);
457   TrackerDelIter(I_Tracker, iter_id);
458   return result;
459 }
460 
461 
ExecutiveMotionView(PyMOLGlobals * G,int action,int first,int last,float power,float bias,int simple,float linear,const char * name,int wrap,int hand,int window,int cycles,const char * scene_name,float scene_cut,int state,int quiet,int autogen)462 int ExecutiveMotionView(PyMOLGlobals *G, int action, int first,
463               int last, float power, float bias,
464               int simple, float linear, const char *name, int wrap,
465               int hand, int window, int cycles,
466               const char *scene_name, float scene_cut, int state, int quiet, int autogen)
467 {
468   int ok = true;
469 
470   CExecutive *I = G->Executive;
471 
472   if(wrap < 0) {
473     wrap = SettingGetGlobal_b(G, cSetting_movie_loop);
474   }
475 
476   if((!name)||(!name[0])||(!strcmp(name,cKeywordNone))||
477      (!strcmp(name,cKeywordAll))||(!strcmp(name,cKeywordSame))) {
478     if(autogen) {
479       /* if autogenerated, then use current settings */
480       power  = SettingGetGlobal_f(G, cSetting_motion_power);
481       bias   = SettingGetGlobal_f(G, cSetting_motion_bias);
482       linear = SettingGetGlobal_f(G, cSetting_motion_linear);
483       hand   = SettingGetGlobal_i(G, cSetting_motion_hand);
484     }
485     /* camera */
486 
487     ok = MovieView(G, action, first, last, power,
488                    bias, true, linear, wrap, hand, window, cycles,
489                    scene_name, scene_cut, state, quiet);
490 
491     if(name && name[0] && strcmp(name, cKeywordNone)) {
492       /* also do all other objects */
493       SpecRec *rec = NULL;
494       while(ListIterate(I->Spec, rec, next)) {
495         switch(rec->type) {
496         case cExecObject:
497           if(autogen) {
498             power  = SettingGet_f(G, NULL, rec->obj->Setting, cSetting_motion_power);
499             bias   = SettingGet_f(G, NULL, rec->obj->Setting, cSetting_motion_bias);
500             simple = SettingGet_i(G, NULL, rec->obj->Setting, cSetting_motion_simple);
501             linear = SettingGet_f(G, NULL, rec->obj->Setting, cSetting_motion_linear);
502             hand   = SettingGet_i(G, NULL, rec->obj->Setting, cSetting_motion_hand);
503           }
504           if((ObjectGetSpecLevel(rec->obj,0)>=0)||(!strcmp(name,cKeywordAll))) {
505             ok = ObjectMotion(rec->obj, action, first, last, power, bias,
506                               simple < 0 ? 0 : 1,
507                               linear, wrap, hand, window, cycles, state, quiet);
508           }
509           break;
510         }
511       }
512     }
513   } else { /* pattern */
514     CTracker *I_Tracker = I->Tracker;
515     SpecRec *rec = NULL;
516     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
517     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
518     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
519       if(rec) {
520 
521         switch (rec->type) {
522         case cExecObject:
523           if(autogen) {
524             power  = SettingGet_f(G, NULL, rec->obj->Setting, cSetting_motion_power);
525             bias   = SettingGet_f(G, NULL, rec->obj->Setting, cSetting_motion_bias);
526             simple = SettingGet_i(G, NULL, rec->obj->Setting, cSetting_motion_simple);
527             linear = SettingGet_f(G, NULL, rec->obj->Setting, cSetting_motion_linear);
528             hand   = SettingGet_i(G, NULL, rec->obj->Setting, cSetting_motion_hand);
529           }
530 
531           ok = ObjectMotion(rec->obj, action, first, last, power, bias,
532                             simple < 0 ? 0 : simple,
533                               linear, wrap, hand, window, cycles, state, quiet);
534           break;
535         }
536       }
537     }
538     TrackerDelList(I_Tracker, list_id);
539     TrackerDelIter(I_Tracker, iter_id);
540 
541     // fix for PYMOL-1465
542     OrthoReshape(G, -1, -1, false);
543   }
544   ExecutiveCountMotions(G);
545   return ok;
546 }
547 
ExecutiveMotionViewModify(PyMOLGlobals * G,int action,int index,int count,int target,const char * name,int freeze,int quiet)548 pymol::Result<> ExecutiveMotionViewModify(PyMOLGlobals* G, int action,
549     int index, int count, int target, const char* name, int freeze, int quiet)
550 {
551   CExecutive *I = G->Executive;
552   if((!name)||(!name[0])||
553      (!strcmp(name,cKeywordNone))||
554      (!strcmp(name,cKeywordSame))||
555      (!strcmp(name,cKeywordAll))) {
556     /* camera */
557     if(MovieGetSpecLevel(G,0)>=0) {
558       MovieViewModify(G, action, index, count, target, true, true);
559     }
560     if((!name) || strcmp(name, cKeywordNone)) {
561       /* also do all other objects */
562       SpecRec *rec = NULL;
563       while(ListIterate(I->Spec, rec, next)) {
564         switch(rec->type) {
565         case cExecObject:
566           if(ObjectGetSpecLevel(rec->obj,0)>=0) {
567             ObjectMotionModify(rec->obj, action, index, count, target, true, true);
568           }
569           break;
570         }
571       }
572       ExecutiveMotionTrim(G);
573     } else {
574       ExecutiveMotionExtend(G,true);
575     }
576 
577     if((!freeze) && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
578       ExecutiveMotionReinterpolate(G);
579     }
580   } else { /* pattern */
581     CTracker *I_Tracker = I->Tracker;
582     SpecRec *rec = NULL;
583     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
584     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
585     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
586       if(rec) {
587         switch (rec->type) {
588         case cExecObject:
589           if(ObjectGetSpecLevel(rec->obj,0)>=0) {
590             /* only modify objects with motion matrices */
591             ObjectMotionModify(rec->obj, action, index, count, target, freeze, false);
592           }
593         }
594       }
595     }
596     TrackerDelList(I_Tracker, list_id);
597     TrackerDelIter(I_Tracker, iter_id);
598   }
599   ExecutiveCountMotions(G);
600   SceneCountFrames(G);
601   return {};
602 }
603 
ExecutiveMotionReinterpolate(PyMOLGlobals * G)604 void ExecutiveMotionReinterpolate(PyMOLGlobals * G)
605 {
606   CExecutive *I = G->Executive;
607   SpecRec *rec = NULL;
608   while(ListIterate(I->Spec, rec, next)) {
609     switch(rec->type) {
610     case cExecAll:
611       if(MovieGetSpecLevel(G,0)>=0) {
612         MovieViewReinterpolate(G);
613       }
614       break;
615     case cExecObject:
616       if(ObjectGetSpecLevel(rec->obj,0)>=0) {
617         ObjectMotionReinterpolate(rec->obj);
618       }
619       break;
620     }
621   }
622 }
623 
ExecutiveMotionTrim(PyMOLGlobals * G)624 void ExecutiveMotionTrim(PyMOLGlobals * G)
625 {
626   int n_frame = MovieGetLength(G);
627   CExecutive *I = G->Executive;
628   SpecRec *rec = NULL;
629   while(ListIterate(I->Spec, rec, next)) {
630     switch(rec->type) {
631     case cExecObject:
632       if(ObjectGetSpecLevel(rec->obj,0)>=0) {
633         ObjectMotionTrim(rec->obj,n_frame);
634       }
635       break;
636     }
637   }
638 }
639 
ExecutiveMotionExtend(PyMOLGlobals * G,int freeze)640 void ExecutiveMotionExtend(PyMOLGlobals * G, int freeze)
641 {
642   int n_frame = 0;
643   int max_length = 0;
644   CExecutive *I = G->Executive;
645   SpecRec *rec = NULL;
646   if(MovieGetSpecLevel(G,-1)>0)
647     n_frame = MovieGetLength(G);
648   while(ListIterate(I->Spec, rec, next)) {
649     switch(rec->type) {
650     case cExecObject:
651       if(ObjectGetSpecLevel(rec->obj,-1)>0) {
652         int length = ObjectMotionGetLength(rec->obj);
653         if(max_length < length)
654           max_length = length;
655       }
656       break;
657     }
658   }
659   if(max_length) {
660     if(n_frame < max_length)
661       MovieViewTrim(G,max_length);
662     while(ListIterate(I->Spec, rec, next)) {
663       switch(rec->type) {
664       case cExecObject:
665         if(ObjectGetSpecLevel(rec->obj,-1)>0) {
666           ObjectMotionTrim(rec->obj,max_length);
667         }
668         break;
669       }
670     }
671   }
672   if((!freeze) && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
673     ExecutiveMotionReinterpolate(G);
674   }
675 
676 }
677 
ExecutiveCountMotions(PyMOLGlobals * G)678 int ExecutiveCountMotions(PyMOLGlobals * G)
679 {
680   int count = 0;
681   CExecutive *I = G->Executive;
682   if(MovieGetLength(G)) {
683     SpecRec *rec = NULL;
684     while(ListIterate(I->Spec, rec, next)) {
685       switch(rec->type) {
686       case cExecAll:
687         if(MovieGetSpecLevel(G,0)>=0)
688           count++;
689         break;
690       case cExecObject:
691         if(ObjectGetSpecLevel(rec->obj,0)>=0)
692           count++;
693         break;
694       }
695     }
696   }
697 
698   if (count < 1 && SceneGetNFrame(G) > 1)
699     count = 1;
700 
701   if(count != I->LastMotionCount) {
702     if(SettingGetGlobal_i(G,cSetting_movie_panel)) {
703       OrthoDoViewportWhenReleased(G);
704     }
705     I->LastMotionCount = count;
706   }
707 
708   return (count);
709 }
710 
ExecutiveMotionDraw(PyMOLGlobals * G,BlockRect * rect,int expected ORTHOCGOARG)711 void ExecutiveMotionDraw(PyMOLGlobals * G, BlockRect *rect, int expected ORTHOCGOARG)
712 {
713   CExecutive *I = G->Executive;
714   SpecRec *rec = NULL;
715   int frames = MovieGetLength(G);
716   BlockRect draw_rect = *rect;
717   int count = 0;
718   int height = rect->top - rect->bottom;
719   while(ListIterate(I->Spec, rec, next)) {
720     switch(rec->type) {
721     case cExecAll:
722       if(MovieGetSpecLevel(G,0)>=0) {
723         int presentation = SettingGetGlobal_b(G, cSetting_presentation);
724         if(presentation) {
725           expected = 1;
726         }
727         draw_rect.top = rect->top - (height * count) / expected;
728         draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
729         MovieDrawViewElem(G,&draw_rect,frames ORTHOCGOARGVAR);
730         count++;
731         if(presentation) {
732           goto done;
733         }
734 
735       }
736       break;
737     case cExecObject:
738       if(ObjectGetSpecLevel(rec->obj,0)>=0) {
739         draw_rect.top = rect->top - (height * count) / expected;
740         draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
741         ObjectDrawViewElem(rec->obj,&draw_rect,frames ORTHOCGOARGVAR);
742         count++;
743       }
744       break;
745     }
746   }
747  done:
748   return;
749 }
750 
ExecutiveMotionMenuActivate(PyMOLGlobals * G,BlockRect * rect,int expected,int passive,int x,int y,int same)751 void ExecutiveMotionMenuActivate(PyMOLGlobals * G, BlockRect *rect, int expected, int passive,
752                                  int x, int y, int same)
753 {
754   CExecutive *I = G->Executive;
755   SpecRec *rec = NULL;
756   BlockRect draw_rect = *rect;
757   int count = 0;
758   int height = rect->top - rect->bottom;
759   if(same) {
760     if(MovieGetSpecLevel(G,0)>=0) {
761       int n_frame = MovieGetLength(G);
762       int frame = MovieXtoFrame(G, &draw_rect, n_frame, x, false);
763       WordType frame_str = "0";
764       if((frame>=0) && (frame<n_frame)) {
765         sprintf(frame_str,"%d",frame+1);
766       }
767       MenuActivate2Arg(
768           G, x, y, x, y, passive, "obj_motion", cKeywordSame, frame_str);
769     }
770   } else {
771   while(ListIterate(I->Spec, rec, next)) {
772     switch(rec->type) {
773     case cExecAll:
774       if(MovieGetSpecLevel(G,0)>=0) {
775         draw_rect.top = rect->top - (height * count) / expected;
776         draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
777         if((y>draw_rect.bottom) && (y<draw_rect.top)) {
778           int n_frame = MovieGetLength(G);
779           int frame = MovieXtoFrame(G, &draw_rect, n_frame, x, false);
780           WordType frame_str = "0";
781           if((frame>=0) && (frame<n_frame)) {
782             sprintf(frame_str,"%d",frame+1);
783           }
784           MenuActivate1Arg(G, x, y, x, y, passive, "camera_motion",frame_str);
785           goto done;
786         }
787         count++;
788       }
789       break;
790     case cExecObject:
791       if(ObjectGetSpecLevel(rec->obj,0)>=0) {
792         draw_rect.top = rect->top - (height * count) / expected;
793         draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
794         if((y>draw_rect.bottom) && (y<draw_rect.top)) {
795           int n_frame = MovieGetLength(G);
796           int frame = MovieXtoFrame(G, &draw_rect, n_frame, x, false);
797           WordType frame_str = "0";
798           if((frame>=0) && (frame<n_frame)) {
799             sprintf(frame_str,"%d",frame+1);
800           }
801           MenuActivate2Arg(G, x, y, x, y, passive, "obj_motion", rec->obj->Name, frame_str);
802           goto done;
803         }
804         count++;
805       }
806       break;
807     }
808   }
809   }
810  done:
811   return;
812 }
813 
ExecutiveMotionClick(PyMOLGlobals * G,BlockRect * rect,int mode,int expected,int x,int y,int nearest)814 void ExecutiveMotionClick(PyMOLGlobals * G, BlockRect *rect,int mode, int expected, int x, int y, int nearest)
815 {
816   CExecutive *I = G->Executive;
817   SpecRec *rec = NULL;
818   BlockRect draw_rect = *rect;
819   int count = 0;
820   int height = rect->top - rect->bottom;
821   while(ListIterate(I->Spec, rec, next)) {
822     switch(rec->type) {
823     case cExecAll:
824       if(MovieGetSpecLevel(G,0)>=0) {
825         draw_rect.top = rect->top - (height * count) / expected;
826         draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
827         if((y>=draw_rect.bottom) && (y<=draw_rect.top)) {
828           MoviePrepareDrag(G,&draw_rect,NULL,mode,x,y,nearest);
829           goto done;
830         }
831         count++;
832       }
833       break;
834     case cExecObject:
835       if(ObjectGetSpecLevel(rec->obj,0)>=0) {
836         MoviePrepareDrag(G,rect,NULL,mode,x,y,nearest);
837         draw_rect.top = rect->top - (height * count) / expected;
838         draw_rect.bottom = rect->top - (height * (count + 1)) / expected;
839         if((y>=draw_rect.bottom) && (y<=draw_rect.top)) {
840           MoviePrepareDrag(G,&draw_rect,rec->obj,mode,x,y,nearest);
841           goto done;
842         }
843         count++;
844       }
845       break;
846     }
847   }
848  done:
849   return;
850 }
851 
ExecutiveReference(PyMOLGlobals * G,int action,const char * sele,int state,int quiet)852 int ExecutiveReference(PyMOLGlobals * G, int action, const char *sele, int state, int quiet)
853 {
854   int result = -1;
855   int s1 = SelectorIndexByName(G, sele);
856   if(s1 >= 0) {
857     ObjectMoleculeOpRec op;
858     ObjectMoleculeOpRecInit(&op);
859 
860     switch (action) {
861     case 1:
862       op.code = OMOP_ReferenceStore;
863       break;
864     case 2:
865       op.code = OMOP_ReferenceRecall;
866       break;
867     case 3:
868       op.code = OMOP_ReferenceValidate;
869       break;
870     case 4:
871       op.code = OMOP_ReferenceSwap;
872       break;
873     }
874     op.i1 = state;
875     op.i2 = 0;
876     ExecutiveObjMolSeleOp(G, s1, &op);
877     result = op.i2;
878   }
879   return result;
880 }
881 
882 /**
883  * Does the object and state validation which is common to isosurface, isomesh,
884  * and volume creation.
885  */
886 template <typename ObjectT>
EtcHelper(PyMOLGlobals * G,const char * obj_name,int & obj_state,ObjectT * & origObj,const char * map_name,int & map_state,ObjectMap * & mapObj,int & multi)887 static pymol::Result<> EtcHelper(PyMOLGlobals* G,             //
888     const char* obj_name, int& obj_state, ObjectT*& origObj,  //
889     const char* map_name, int& map_state, ObjectMap*& mapObj, //
890     int& multi)
891 {
892   if (obj_state < -3) {
893     return pymol::make_error("Invalid state ", obj_state + 1);
894   }
895 
896   if (map_state < -3) {
897     return pymol::make_error("Invalid source_state ", map_state + 1);
898   }
899 
900   mapObj = ExecutiveFindObject<ObjectMap>(G, map_name);
901   if (!mapObj) {
902     return pymol::make_error("Map object \"", map_name, "\" not found");
903   }
904 
905   // if object with same name but different type exists, overwrite it
906   auto anyOrigObj = ExecutiveFindObjectByName(G, obj_name);
907   origObj = dynamic_cast<ObjectT*>(anyOrigObj);
908   if (anyOrigObj && !origObj) {
909     ExecutiveDelete(G, obj_name);
910   }
911 
912   switch (obj_state) {
913   case -1:
914     // all states
915     obj_state = 0;
916     map_state = -1;
917     break;
918   case -2:
919     // current state
920     obj_state = SceneGetState(G);
921     if (map_state < 0)
922       map_state = obj_state;
923     break;
924   case -3:
925     // append mode
926     obj_state = origObj ? origObj->getNFrame() : 0;
927     if (map_state < 0)
928       map_state = obj_state;
929     break;
930   }
931 
932   switch (map_state) {
933   case -1:
934     // all states
935     map_state = 0;
936     multi = true;
937     break;
938   case -2:
939     // current state
940     map_state = SceneGetState(G);
941     break;
942   case -3:
943     // append mode
944     map_state = mapObj->getNFrame() - 1;
945     break;
946   }
947 
948   return {};
949 }
950 
ExecutiveIsosurfaceEtc(PyMOLGlobals * G,const char * surf_name,const char * map_name,float lvl,const char * sele,float fbuf,int state,float carve,int map_state,int side,int quiet,int surf_mode)951 pymol::Result<> ExecutiveIsosurfaceEtc(PyMOLGlobals * G,
952                            const char *surf_name, const char *map_name, float lvl,
953                            const char *sele, float fbuf, int state,
954                            float carve, int map_state, int side,
955                            int quiet, int surf_mode)
956 {
957   int c;
958   ObjectSurface *obj = nullptr, *origObj = nullptr;
959   ObjectMap *mapObj;
960   float mn[3] = { 0, 0, 0 };
961   float mx[3] = { 15, 15, 15 };
962   float *vert_vla = NULL;
963   ObjectMapState *ms;
964   int multi = false;
965 
966   auto res0 = EtcHelper(
967       G, surf_name, state, origObj, map_name, map_state, mapObj, multi);
968   if (!res0) {
969     return res0.error();
970   }
971 
972   {
973     while(1) {
974       ms = ObjectMapStateGetActive(mapObj, map_state);
975       if(ms) {
976         int box_mode = (sele && sele[0]) ? 1 : 0;
977         switch (box_mode) {
978         case 0:                /* using map to get extents */
979           for(c = 0; c < 3; c++) {
980             mn[c] = ms->Corner[c];
981             mx[c] = ms->Corner[3 * 7 + c];
982           }
983           if(!ms->Matrix.empty()) {
984             transform44d3f(ms->Matrix.data(), mn, mn);
985             transform44d3f(ms->Matrix.data(), mx, mx);
986             {
987               float tmp;
988               int a;
989               for(a = 0; a < 3; a++)
990                 if(mn[a] > mx[a]) {
991                   tmp = mn[a];
992                   mn[a] = mx[a];
993                   mx[a] = tmp;
994                 }
995             }
996           }
997           carve = 0.0F;
998           break;
999         case 1:                /* using selection to get extents */
1000           {
1001             auto tmpsele = SelectorTmp2::make(G, sele);
1002             p_return_if_error(tmpsele);
1003             auto s1 = tmpsele->getName();
1004             ExecutiveGetExtent(G, s1, mn, mx, false, -1, false);  /* TODO state */
1005             if(carve != 0.0F) {
1006               vert_vla = ExecutiveGetVertexVLA(G, s1, state);
1007               if(fbuf <= R_SMALL4)
1008                 fbuf = fabs(carve);
1009             }
1010           }
1011           for(c = 0; c < 3; c++) {
1012             mn[c] -= fbuf;
1013             mx[c] += fbuf;
1014           }
1015           break;
1016         }
1017         PRINTFB(G, FB_CCmd, FB_Blather)
1018           " Isosurface: buffer %8.3f carve %8.3f\n", fbuf, carve ENDFB(G);
1019         obj =
1020           ObjectSurfaceFromBox(G, origObj, mapObj,
1021                                            map_state, state, mn, mx, lvl, surf_mode,
1022                                            carve, vert_vla, side, quiet);
1023         /* copy the map's TTT */
1024         ExecutiveMatrixCopy2(G, mapObj, obj, 1, 1, -1, -1, false, 0, quiet);
1025 
1026         if(!origObj) {
1027           ObjectSetName(obj, surf_name);
1028           ExecutiveManageObject(G, (CObject *) obj, -1, quiet);
1029         }
1030         if(SettingGetGlobal_b(G, cSetting_isomesh_auto_state))
1031           if(obj)
1032             ObjectGotoState(obj, state);
1033         if(!quiet) {
1034           PRINTFB(G, FB_ObjectSurface, FB_Actions)
1035             " Isosurface: created \"%s\", setting level to %5.3f\n", surf_name, lvl
1036             ENDFB(G);
1037         }
1038       } else if(!multi) {
1039         return pymol::make_error(
1040             "state ", map_state + 1, " not present in map \"", map_name, "\"");
1041       }
1042       if(multi) {
1043         origObj = obj;
1044         map_state++;
1045         state++;
1046         if(map_state >= mapObj->State.size())
1047           break;
1048       } else {
1049         break;
1050       }
1051     }
1052   }
1053   return {};
1054 }
1055 
ExecutiveIsomeshEtc(PyMOLGlobals * G,const char * mesh_name,const char * map_name,float lvl,const char * sele,float fbuf,int state,float carve,int map_state,int quiet,int mesh_mode,float alt_lvl)1056 pymol::Result<> ExecutiveIsomeshEtc(PyMOLGlobals * G,
1057                         const char *mesh_name, const char *map_name, float lvl,
1058                         const char *sele, float fbuf, int state,
1059                         float carve, int map_state, int quiet,
1060                         int mesh_mode, float alt_lvl)
1061 {
1062   ObjectMesh *obj = nullptr, *origObj = nullptr;
1063   ObjectMap *mapObj;
1064   float mn[3] = { 0, 0, 0 };
1065   float mx[3] = { 15, 15, 15 };
1066   float *vert_vla = NULL;
1067   int multi = false;
1068   ObjectMapState *ms;
1069   ObjectMolecule *sele_obj = NULL;
1070   CSymmetry *symm;
1071 
1072   auto res0 = EtcHelper(
1073       G, mesh_name, state, origObj, map_name, map_state, mapObj, multi);
1074   if (!res0) {
1075     return res0.error();
1076   }
1077 
1078   {
1079     while(1) {
1080       ms = ObjectMapStateGetActive(mapObj, map_state);
1081       if(ms) {
1082         int box_mode = (sele && sele[0]) ? 1 : 0;
1083         switch (box_mode) {
1084         case 0:                /* do the whole map */
1085           {
1086             int c;
1087             for(c = 0; c < 3; c++) {
1088               mn[c] = ms->Corner[c];
1089               mx[c] = ms->Corner[3 * 7 + c];
1090             }
1091           }
1092           if(!ms->Matrix.empty()) {
1093             transform44d3f(ms->Matrix.data(), mn, mn);
1094             transform44d3f(ms->Matrix.data(), mx, mx);
1095             {
1096               float tmp;
1097               int a;
1098               for(a = 0; a < 3; a++)
1099                 if(mn[a] > mx[a]) {
1100                   tmp = mn[a];
1101                   mn[a] = mx[a];
1102                   mx[a] = tmp;
1103                 }
1104             }
1105           }
1106           carve = -0.0;         /* impossible */
1107           break;
1108         case 1:                /* just do area around selection */
1109 	  /* determine the selected object */
1110           {
1111             auto tmpsele = SelectorTmp2::make(G, sele);
1112             p_return_if_error(tmpsele);
1113             int sele1 = tmpsele->getIndex();
1114             if(sele1 >= 0)
1115               sele_obj = SelectorGetSingleObjectMolecule(G, sele1);
1116             auto s1 = tmpsele->getName();
1117             ExecutiveGetExtent(G, s1, mn, mx, false, -1, false);  /* TODO state */
1118             if(carve != 0.0) {
1119               vert_vla = ExecutiveGetVertexVLA(G, s1, state);
1120               if(fbuf <= R_SMALL4)
1121                 fbuf = fabs(carve);
1122             }
1123           }
1124           {
1125             int c;
1126             for(c = 0; c < 3; c++) {
1127               mn[c] -= fbuf;
1128               mx[c] += fbuf;
1129             }
1130           }
1131           break;
1132         }
1133         PRINTFB(G, FB_CCmd, FB_Blather)
1134           " Isomesh: buffer %8.3f carve %8.3f \n", fbuf, carve ENDFB(G);
1135 
1136         symm = NULL;
1137         if(sele_obj &&  ObjectMapValidXtal(mapObj, state)) {
1138           if(SettingGet_b(G, NULL, sele_obj->Setting, cSetting_map_auto_expand_sym)
1139               && (sele_obj->Symmetry)) {
1140             // legacy default: take symmetry from molecular object
1141             symm = sele_obj->Symmetry;
1142           } else if(SettingGet_b(G, NULL, mapObj->Setting, cSetting_map_auto_expand_sym)) {
1143             // fallback: take symmetry from map state
1144             symm = ms->Symmetry.get();
1145           }
1146         }
1147 
1148         if(symm) {
1149           obj = ObjectMeshFromXtalSym(G, origObj, mapObj,
1150                                                   symm,
1151                                                   map_state, state, mn, mx, lvl,
1152                                                   mesh_mode, carve, vert_vla, alt_lvl,
1153                                                   quiet);
1154         } else {
1155           obj = NULL;
1156         }
1157         if(!obj) {
1158           obj = ObjectMeshFromBox(G, origObj, mapObj,
1159                                               map_state, state, mn, mx, lvl, mesh_mode,
1160                                               carve, vert_vla, alt_lvl, quiet);
1161         }
1162         /* copy the map's TTT */
1163         ExecutiveMatrixCopy2(G, mapObj, obj, 1, 1, -1, -1, false, 0, quiet);
1164 
1165         if(!origObj) {
1166           ObjectSetName(obj, mesh_name);
1167           ExecutiveManageObject(G, (CObject *) obj, false, quiet);
1168         }
1169 
1170         if(SettingGetGlobal_b(G, cSetting_isomesh_auto_state))
1171           if(obj)
1172             ObjectGotoState(obj, state);
1173         if(!quiet) {
1174           if(mesh_mode != 3) {
1175             PRINTFB(G, FB_ObjectMesh, FB_Actions)
1176               " Isomesh: created \"%s\", setting level to %5.3f\n", mesh_name, lvl
1177               ENDFB(G);
1178           } else {
1179             PRINTFB(G, FB_ObjectMesh, FB_Actions)
1180               " Gradient: created \"%s\"\n", mesh_name ENDFB(G);
1181           }
1182         }
1183       } else if(!multi) {
1184         return pymol::make_error(
1185             "state ", map_state + 1, " not present in map \"", map_name, "\"");
1186       }
1187       if(multi) {
1188         origObj = obj;
1189         map_state++;
1190         state++;
1191         if(map_state >= mapObj->State.size())
1192           break;
1193       } else {
1194         break;
1195       }
1196     }
1197   }
1198   return {};
1199 }
1200 
1201 pymol::Result<>
ExecutiveVolume(PyMOLGlobals * G,const char * volume_name,const char * map_name,float lvl,const char * sele,float fbuf,int state,float carve,int map_state,int quiet)1202 ExecutiveVolume(PyMOLGlobals * G, const char *volume_name, const char *map_name,
1203                         float lvl,
1204                         const char *sele, float fbuf, int state,
1205                         float carve, int map_state, int quiet)
1206 {
1207   ObjectVolume *obj = nullptr, *origObj = nullptr;
1208   ObjectMap *mapObj;
1209   float mn[3] = { 0, 0, 0 };
1210   float mx[3] = { 15, 15, 15 };
1211   float *vert_vla = NULL;
1212   int multi = false;
1213   ObjectMapState *ms;
1214   ObjectMolecule *sele_obj = NULL;
1215   CSymmetry *symm;
1216 
1217   /* Goal: make a new volume map from the object or selection
1218    *
1219    * If the user specifies an VOLUME OBJECT NAME that already exists, then
1220    * check to make sure it's a volume.  If it is, keep it, else delete it
1221    *
1222    * If the user specifies a MAP OBJECT NAME that already exists, then
1223    * make sure it's really a map.  Keep it if it is (to append the state)
1224    * otherwise, delete it.
1225    *
1226    * Given that the MAP is valid, find the current states for the MAP object
1227    * and the scene/protein.
1228    *
1229    * If the user is loading a map with N-states, into an N-state object, we need
1230    * to loop over each state.  So,
1231    *
1232    * FOR EACH state in the MAP:
1233    *   - determine the extents of the MAP for THIS STATE
1234    *
1235    *   - create the VOLUME object from the MAP/XTAL SYMM
1236    *   - if VOLUME CREATION didn't work, then try creating from a BOX
1237    *
1238    *   - transform (translate/rotate) the VOLUME vis so that it
1239    *     matches the map's ObjectMatrix.
1240    *
1241    *   - if this is a new VOLUME, (eg., origObj==NULL) then set the internal
1242    *     PyMOL state to manage the object and add the name to the object list
1243    *
1244    *   - update the map state for the next loop and re-iterate
1245    */
1246 
1247   auto res0 = EtcHelper(
1248       G, volume_name, state, origObj, map_name, map_state, mapObj, multi);
1249   if (!res0) {
1250     return res0.error();
1251   }
1252 
1253   {
1254     /* do for each state */
1255     while(1) {
1256       ms = ObjectMapStateGetActive(mapObj, map_state);
1257       if(ms) {
1258 	/* determine extents */
1259         int box_mode = (sele && sele[0]) ? 1 : 0;
1260         switch (box_mode) {
1261         case 0:                /* do the whole map */
1262           {
1263             int c;
1264             for(c = 0; c < 3; c++) {
1265               mn[c] = ms->Corner[c];
1266               mx[c] = ms->Corner[3 * 7 + c];
1267             }
1268           }
1269           if(!ms->Matrix.empty()) {
1270             transform44d3f(ms->Matrix.data(), mn, mn);
1271             transform44d3f(ms->Matrix.data(), mx, mx);
1272             {
1273               float tmp;
1274               int a;
1275               for(a = 0; a < 3; a++)
1276                 if(mn[a] > mx[a]) {
1277                   tmp = mn[a];
1278                   mn[a] = mx[a];
1279                   mx[a] = tmp;
1280                 }
1281             }
1282           }
1283           carve = -0.0;         /* impossible */
1284           break;
1285         case 1:                /* just do area around selection */
1286           {
1287             auto tmpsele = SelectorTmp2::make(G, sele);
1288             p_return_if_error(tmpsele);
1289             int sele1 = tmpsele->getIndex();
1290             if(sele1 >= 0)
1291               sele_obj = SelectorGetSingleObjectMolecule(G, sele1);
1292             auto const s1 = tmpsele->getName();
1293             ExecutiveGetExtent(G, s1, mn, mx, false, -1, false);  /* TODO state */
1294             if(carve != 0.0) {
1295               vert_vla = ExecutiveGetVertexVLA(G, s1, state);
1296               if(fbuf <= R_SMALL4)
1297                 fbuf = fabs(carve);
1298             }
1299           }
1300           {
1301             int c;
1302             for(c = 0; c < 3; c++) {
1303               mn[c] -= fbuf;
1304               mx[c] += fbuf;
1305             }
1306           }
1307           break;
1308         }
1309         PRINTFB(G, FB_CCmd, FB_Blather)
1310           " Volume: buffer %8.3f carve %8.3f \n", fbuf, carve ENDFB(G);
1311 
1312         symm = NULL;
1313         if(sele_obj && ObjectMapValidXtal(mapObj, state)) {
1314           if(SettingGet_b(G, NULL, sele_obj->Setting, cSetting_map_auto_expand_sym)
1315               && (sele_obj->Symmetry)) {
1316             // legacy default: take symmetry from molecular object
1317             symm = sele_obj->Symmetry;
1318           } else if(SettingGet_b(G, NULL, mapObj->Setting, cSetting_map_auto_expand_sym)) {
1319             // fallback: take symmetry from map state
1320             symm = ms->Symmetry.get();
1321           }
1322         }
1323 
1324         if(symm) {
1325           obj = ObjectVolumeFromXtalSym(G, origObj, mapObj,
1326                                                   symm,
1327                                                   map_state, state, mn, mx, lvl,
1328                                                   box_mode, carve, vert_vla,
1329                                                   quiet);
1330         } else {
1331           obj = NULL;
1332         }
1333 
1334         if(!obj) {
1335           obj = ObjectVolumeFromBox(G, origObj, mapObj,
1336                                               map_state, state, mn, mx, lvl, box_mode,
1337                                               carve, vert_vla, quiet);
1338         }
1339         /* copy the map's TTT */
1340         ExecutiveMatrixCopy2(G, mapObj, obj, 1, 1, -1, -1, false, 0, quiet);
1341 	/* set the object name
1342 	 * manage the object in the UI */
1343         if(!origObj) {
1344           ObjectSetName(obj, volume_name);
1345           ExecutiveManageObject(G, (CObject *) obj, false, quiet);
1346         }
1347 
1348         if(SettingGetGlobal_b(G, cSetting_isomesh_auto_state))
1349           if(obj)
1350             ObjectGotoState(obj, state);
1351         if(!quiet) {
1352 	  PRINTFB(G, FB_ObjectVolume, FB_Actions)
1353 	    " Volume: created \"%s\"\n", volume_name
1354 	    ENDFB(G);
1355         }
1356       } else if(!multi) {
1357         return pymol::make_error(
1358             "state ", map_state + 1, " not present in map \"", map_name, "\"");
1359       }
1360       if(multi) {
1361         origObj = obj;
1362         map_state++;
1363         state++;
1364         if(map_state >= mapObj->State.size())
1365           break;
1366       } else {
1367         break;
1368       }
1369     }
1370   }
1371   return {};
1372 }
1373 
ExecutivePreparePseudoatomName(PyMOLGlobals * G,pymol::zstring_view object_name)1374 std::string ExecutivePreparePseudoatomName(
1375     PyMOLGlobals* G, pymol::zstring_view object_name)
1376 {
1377   std::string new_object_name;
1378   if (object_name.empty()) {
1379     new_object_name = ExecutiveGetUnusedName(G, "pseudo");
1380   } else {
1381     ObjectNameType valid_name{};
1382     assert(object_name.size() < sizeof(ObjectNameType));
1383     std::copy_n(object_name.c_str(), object_name.size(), valid_name);
1384     ObjectMakeValidName(G, valid_name);
1385     new_object_name = valid_name;
1386   }
1387   return new_object_name;
1388 }
1389 
ExecutivePseudoatom(PyMOLGlobals * G,pymol::zstring_view object_name_view,const char * sele,const char * name,const char * resn,const char * resi,const char * chain,const char * segi,const char * elem,float vdw,int hetatm,float b,float q,const char * label,const float * pos,int color,int state,int mode,int quiet)1390 pymol::Result<> ExecutivePseudoatom(PyMOLGlobals* G, pymol::zstring_view object_name_view,
1391     const char* sele, const char* name, const char* resn, const char* resi,
1392     const char* chain, const char* segi, const char* elem, float vdw,
1393     int hetatm, float b, float q, const char* label, const float* pos, int color,
1394     int state, int mode, int quiet)
1395 {
1396   pymol::Result<SelectorTmp> s1;
1397 
1398   auto object_name = object_name_view.c_str();
1399   auto obj = ExecutiveFindObject<ObjectMolecule>(G, object_name);
1400 
1401   int is_new = false;
1402   int sele_index = -1;
1403   float local_pos[3];
1404 
1405   if(sele && sele[0]) {
1406     if(WordMatchExact(G, cKeywordCenter, sele, true)) {
1407       SceneGetCenter(G, local_pos);
1408       pos = local_pos;
1409     } else if(WordMatchExact(G, cKeywordOrigin, sele, true)) {
1410       SceneOriginGet(G, local_pos);
1411       pos = local_pos;
1412     } else {
1413       s1 = SelectorTmp::make(G, sele);
1414       p_return_if_error(s1);
1415       sele_index = s1->getIndex();
1416       assert(sele_index >= 0);
1417     }
1418   }
1419   if(!obj) {
1420     /* new object */
1421     is_new = true;
1422     obj = new ObjectMolecule(G, false);
1423     ObjectSetName(obj, object_name);
1424   }
1425 
1426 #ifndef _PYMOL_NO_UNDO
1427 #endif
1428     if(ObjectMoleculeAddPseudoatom(obj, sele_index, name, resn, resi, chain,
1429                                    segi, elem, vdw, hetatm, b, q, label, pos, color,
1430                                    state, mode, quiet)) {
1431       if(is_new) {
1432         ExecutiveDelete(G, object_name);        /* just in case */
1433         ExecutiveManageObject(G, obj, false, true);
1434 #ifndef _PYMOL_NO_UNDO
1435 #endif
1436       } else {
1437         ExecutiveUpdateObjectSelection(G, obj);
1438 #ifndef _PYMOL_NO_UNDO
1439 #endif
1440       }
1441     }
1442 #ifndef _PYMOL_NO_UNDO
1443 #endif
1444   return {};
1445 }
1446 
ExecutiveInvalidateGridSlots(PyMOLGlobals * G)1447 static void ExecutiveInvalidateGridSlots(PyMOLGlobals * G)
1448 {
1449   CExecutive *I = G->Executive;
1450   I->ValidGridSlots = false;
1451 }
1452 
ExecutiveSetGridSlot(SpecRec * rec,int new_grid_slot)1453 static void ExecutiveSetGridSlot(SpecRec *rec, int new_grid_slot){
1454   if (rec->grid_slot != new_grid_slot){
1455     CGOFree(rec->gridSlotSelIndicatorsCGO);
1456     rec->gridSlotSelIndicatorsCGO = NULL;
1457     rec->grid_slot = new_grid_slot;
1458   }
1459 }
ExecutiveUpdateGridSlots(PyMOLGlobals * G,int force)1460 static void ExecutiveUpdateGridSlots(PyMOLGlobals * G, int force)
1461 {
1462   CExecutive *I = G->Executive;
1463   int grid_slot_count = 0;
1464   int grid_by_group = 1;        /* grid slots are inherited this many levels */
1465 
1466   ExecutiveUpdateGroups(G, false);
1467   if(force || (!I->ValidGridSlots)) {
1468     CTracker *I_Tracker = I->Tracker;
1469     I->ValidGridSlots = true;
1470     {
1471       SpecRec *rec = NULL;
1472       while(ListIterate(I->Spec, rec, next)) {
1473 	ExecutiveSetGridSlot(rec, 0);
1474         if(rec->type == cExecObject) {
1475           /* make sure every object (potentially) needing a grid slot gets one */
1476           switch (rec->obj->type) {
1477           case cObjectMolecule:
1478           case cObjectMap:
1479           case cObjectMesh:
1480           case cObjectMeasurement:
1481           case cObjectCallback:
1482           case cObjectCGO:
1483           case cObjectSurface:
1484           case cObjectSlice:
1485           case cObjectGadget:
1486           case cObjectGroup:
1487           case cObjectVolume:
1488 	    ExecutiveSetGridSlot(rec, ++grid_slot_count);
1489             break;
1490           }
1491         }
1492       }
1493     }
1494 
1495     if(grid_by_group) {
1496       SpecRec *rec = NULL, *group_rec = NULL;
1497       while(ListIterate(I->Spec, rec, next)) {
1498         OVreturn_word result;
1499         if(OVreturn_IS_OK
1500            ((result = OVLexicon_BorrowFromCString(I->Lex, rec->group_name)))) {
1501           if(OVreturn_IS_OK((result = OVOneToOne_GetForward(I->Key, result.word)))) {
1502             if(TrackerGetCandRef(I_Tracker, result.word,
1503                                  (TrackerRef **) (void *) &group_rec)) {
1504               int grid_slot_group_depth = grid_by_group;
1505               {
1506                 SpecRec *check_rec = group_rec;
1507                 while(check_rec && grid_slot_group_depth) {
1508                   if(grid_slot_group_depth == 1)
1509 		    ExecutiveSetGridSlot(rec, check_rec->grid_slot);
1510                   if(check_rec == rec) {        /* cycle */
1511                     break;
1512                   } else {
1513                     check_rec = check_rec->group;
1514                     grid_slot_group_depth--;
1515                   }
1516                 }
1517               }
1518             }
1519           }
1520         }
1521       }
1522     }
1523 
1524     {
1525       SpecRec *rec = NULL;
1526       while(ListIterate(I->Spec, rec, next)) {
1527         if(rec->type == cExecObject) {
1528           int obj_slot = SettingGet_i(G, rec->obj->Setting, NULL, cSetting_grid_slot);
1529           if(obj_slot == -1) {
1530             rec->obj->grid_slot = rec->grid_slot;
1531           } else
1532             rec->obj->grid_slot = obj_slot;
1533         }
1534       }
1535     }
1536   }
1537 }
1538 
ExecutiveInvalidatePanelList(PyMOLGlobals * G)1539 static void ExecutiveInvalidatePanelList(PyMOLGlobals * G)
1540 {
1541   CExecutive *I = G->Executive;
1542   if(I->ValidPanel) {
1543     if(I->Panel)
1544       ListFree(I->Panel, next, PanelRec);
1545     I->ValidPanel = false;
1546   }
1547   ExecutiveInvalidateGridSlots(G);
1548 }
1549 
PanelListGroup(PyMOLGlobals * G,PanelRec * panel,SpecRec * group,int level,int hide_underscore)1550 static PanelRec *PanelListGroup(PyMOLGlobals * G, PanelRec * panel, SpecRec * group,
1551                                 int level, int hide_underscore)
1552 {
1553   CExecutive *I = G->Executive;
1554   PanelRec *result = NULL;
1555   SpecRec *rec = NULL;
1556   /* set up recursion prevention */
1557   if(level == 0)
1558   while(ListIterate(I->Spec, rec, next)) {
1559     rec->in_panel = false;
1560   }
1561   while(ListIterate(I->Spec, rec, next)) {      /* add all members which belong to this group */
1562 
1563     if((rec->name[0] != '_') || (!hide_underscore)) {   /* not hidden */
1564       if((rec->group == group) && (!rec->in_panel)) {
1565         int group_name_len = strlen(rec->group_name);
1566         if((!hide_underscore)
1567            || !((strncmp(rec->name, rec->group_name, group_name_len) == 0) &&
1568                 /* named with proper group prefix */
1569                 (rec->name[group_name_len] == '.')
1570                 && (rec->name[group_name_len + 1] == '_'))) {
1571           /* and not hidden inside group */
1572 
1573           PanelRec *new_panel = NULL;
1574           ListElemCalloc(G, new_panel, PanelRec);
1575           if(panel)
1576             panel->next = new_panel;
1577           else
1578             result = new_panel;
1579           panel = new_panel;
1580           panel->spec = rec;
1581           panel->nest_level = level;
1582           if(!level)
1583             rec->group_name[0] = 0;     /* force open any cycles which have been created... */
1584           rec->in_panel = true;
1585           if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
1586             ObjectGroup *obj_group = (ObjectGroup *) rec->obj;
1587             panel->is_group = true;
1588             if(obj_group->OpenOrClosed) {
1589               panel->is_open = true;
1590               panel = PanelListGroup(G, panel, rec, level + 1, hide_underscore);
1591             }
1592           }
1593         }
1594       }
1595     }
1596   }
1597   if(!result)
1598     result = panel;
1599   return result;
1600 }
1601 
ExecutiveUpdatePanelList(PyMOLGlobals * G)1602 static void ExecutiveUpdatePanelList(PyMOLGlobals * G)
1603 {
1604   CExecutive *I = G->Executive;
1605   int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
1606   if(!I->ValidPanel) {
1607     /* brute-force & inefficient -- need to optimize algorithm */
1608     I->Panel = PanelListGroup(G, NULL, NULL, 0, hide_underscore);
1609     I->ValidPanel = true;
1610   }
1611 }
1612 
ExecutiveInvalidateSceneMembers(PyMOLGlobals * G)1613 void ExecutiveInvalidateSceneMembers(PyMOLGlobals * G)
1614 {
1615   CExecutive *I = G->Executive;
1616   I->ValidSceneMembers = false;
1617 }
1618 
ExecutiveUpdateSceneMembers(PyMOLGlobals * G)1619 void ExecutiveUpdateSceneMembers(PyMOLGlobals * G)
1620 {
1621   CExecutive *I = G->Executive;
1622   ExecutiveUpdateGroups(G, false);
1623   ExecutiveUpdateGridSlots(G, false);
1624   if(!I->ValidSceneMembers) {
1625     SpecRec *rec = NULL;
1626     while(ListIterate(I->Spec, rec, next)) {
1627       if(rec->type == cExecObject) {
1628         int visible = rec->visible;
1629         SpecRec *group_rec = rec->group;
1630         while(visible && group_rec) {   /* visibility is a group issue... */
1631           if(!group_rec->visible)
1632             visible = false;
1633           else
1634             group_rec = group_rec->group;
1635         }
1636         if(rec->in_scene && !visible) {
1637           rec->in_scene = SceneObjectDel(G, rec->obj, true);
1638         } else if(visible && !rec->in_scene) {
1639           rec->in_scene = SceneObjectAdd(G, rec->obj);
1640         }
1641       }
1642     }
1643     I->ValidSceneMembers = true;
1644   }
1645 }
1646 
ExecutiveInvalidateGroups(PyMOLGlobals * G,int force)1647 void ExecutiveInvalidateGroups(PyMOLGlobals * G, int force)
1648 {
1649   CExecutive *I = G->Executive;
1650   if(force || I->ValidGroups) {
1651     CTracker *I_Tracker = I->Tracker;
1652     SpecRec *rec = NULL;
1653     while(ListIterate(I->Spec, rec, next)) {
1654       rec->group = NULL;
1655       if(rec->type == cExecObject)
1656         if(rec->obj->type == cObjectGroup) {
1657           int list_id = rec->group_member_list_id;
1658           if(list_id)
1659             TrackerDelList(I_Tracker, rec->group_member_list_id);
1660           rec->group_member_list_id = 0;        /* not a list */
1661         }
1662     }
1663     I->ValidGroups = false;
1664     ExecutiveInvalidateSceneMembers(G);
1665     ExecutiveInvalidatePanelList(G);
1666   }
1667   /* any changes to group structure means that we need to check scene
1668      members */
1669 }
1670 
ExecutiveUpdateGroups(PyMOLGlobals * G,int force)1671 void ExecutiveUpdateGroups(PyMOLGlobals * G, int force)
1672 {
1673   CExecutive *I = G->Executive;
1674 
1675   if(force || (!I->ValidGroups)) {
1676     CTracker *I_Tracker = I->Tracker;
1677 
1678     /* first, get rid of existing group lists */
1679 
1680     if(force || I->ValidGroups)
1681       ExecutiveInvalidateGroups(G, true);
1682 
1683     /* create empty lists for each group (also init grid_slot) */
1684 
1685     {
1686       SpecRec *rec = NULL;
1687       while(ListIterate(I->Spec, rec, next)) {
1688         rec->group = NULL;
1689         if(rec->type == cExecObject) {
1690           if(rec->obj->type == cObjectGroup) {
1691             rec->group_member_list_id = TrackerNewList(I_Tracker, NULL);
1692           }
1693         }
1694       }
1695     }
1696 
1697     /* iterate through and populate groups lists with their members */
1698 
1699     {
1700       SpecRec *rec = NULL, *group_rec = NULL;
1701       while(ListIterate(I->Spec, rec, next)) {
1702         OVreturn_word result;
1703         if(OVreturn_IS_OK
1704            ((result = OVLexicon_BorrowFromCString(I->Lex, rec->group_name)))) {
1705           if(OVreturn_IS_OK((result = OVOneToOne_GetForward(I->Key, result.word)))) {
1706             if(TrackerGetCandRef(I_Tracker, result.word,
1707                                  (TrackerRef **) (void *) &group_rec)) {
1708               int cycle = false;
1709               {                 /* don't close infinite loops */
1710                 SpecRec *check_rec = group_rec;
1711                 while(check_rec) {
1712                   if(check_rec == rec) {
1713                     cycle = true;
1714                     break;
1715                   } else {
1716                     check_rec = check_rec->group;
1717                   }
1718                 }
1719               }
1720               if(!cycle) {
1721                 rec->group = group_rec;
1722                 TrackerLink(I_Tracker, rec->cand_id, group_rec->group_member_list_id, 1);
1723               }
1724             }
1725           }
1726         }
1727       }
1728     }
1729 
1730     {
1731       int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
1732       if(hide_underscore) {
1733         SpecRec *rec = NULL;
1734         while(ListIterate(I->Spec, rec, next)) {
1735           rec->is_hidden = false;
1736           if(rec->name[0] == '_')
1737             rec->is_hidden = true;
1738           else if(rec->group) {
1739             int group_name_len = strlen(rec->group_name);
1740             if(rec->group->is_hidden)
1741               rec->is_hidden = true;
1742             else if((strncmp(rec->name, rec->group_name, group_name_len) == 0) &&
1743                     (rec->name[group_name_len] == '.') &&
1744                     (rec->name[group_name_len + 1] == '_'))
1745               rec->is_hidden = true;
1746           }
1747         }
1748         {                       /* sub-optimal propagation of hidden status to group members */
1749           int repeat_flag = true;
1750           while(repeat_flag) {
1751             repeat_flag = false;
1752             while(ListIterate(I->Spec, rec, next)) {
1753               if(rec->group && (!rec->is_hidden)) {
1754                 if(rec->group->is_hidden) {
1755                   rec->is_hidden = true;
1756                   repeat_flag = true;
1757                 }
1758               }
1759             }
1760           }
1761         }
1762       }
1763     }
1764 
1765     /* note that it is possible to have infinite loops -- these must be
1766        allowed for later in the group expansion routine(s) */
1767     I->ValidGroups = true;
1768     ExecutiveInvalidatePanelList(G);
1769   }
1770 }
1771 
ExecutiveGetObjectParentList(PyMOLGlobals * G,SpecRec * child)1772 static int ExecutiveGetObjectParentList(PyMOLGlobals * G, SpecRec * child)
1773 {
1774   int list_id = 0;
1775   ExecutiveUpdateGroups(G, false);
1776   {
1777     CExecutive *I = G->Executive;
1778     CTracker *I_Tracker = I->Tracker;
1779     int priority = 1;           /* generations removed from child */
1780     int repeat_flag = true;
1781     SpecRec *group_rec = NULL;
1782 
1783     list_id = TrackerNewList(I_Tracker, NULL);
1784     while(child && child->group && repeat_flag) {
1785       OVreturn_word result;
1786       repeat_flag = false;
1787       if(OVreturn_IS_OK
1788          ((result = OVLexicon_BorrowFromCString(I->Lex, child->group_name)))) {
1789         if(OVreturn_IS_OK((result = OVOneToOne_GetForward(I->Key, result.word)))) {
1790           if(TrackerGetCandRef(I_Tracker, result.word,
1791                                (TrackerRef **) (void *) &group_rec)) {
1792             if(TrackerLink(I_Tracker, result.word, list_id, priority++)) {
1793               /* checking this prevents infinite loops */
1794               if(group_rec->group) {
1795                 repeat_flag = true;
1796                 child = group_rec;
1797               }
1798             }
1799           }
1800         }
1801       }
1802     }
1803   }
1804   return list_id;
1805 }
1806 
ExecutiveVdwFit(PyMOLGlobals * G,const char * s1,int state1,const char * s2,int state2,float buffer,int quiet)1807 int ExecutiveVdwFit(PyMOLGlobals * G, const char *s1, int state1, const char *s2, int state2,
1808                     float buffer, int quiet)
1809 {
1810   SelectorTmp tmpsele1(G, s1);
1811   SelectorTmp tmpsele2(G, s2);
1812   int sele1 = tmpsele1.getIndex();
1813   int sele2 = tmpsele2.getIndex();
1814 
1815   int ok = true;
1816 
1817   if((sele1 >= 0) && (sele2 >= 0)) {
1818     ok = SelectorVdwFit(G, sele1, state1, sele2, state2, buffer, quiet);
1819   } else {
1820     ok = false;
1821   }
1822   return ok;
1823 }
1824 
get_op_cnt(PyMOLGlobals * G)1825 static int get_op_cnt(PyMOLGlobals * G)
1826 {
1827   int result = 5;
1828   if(!strcmp(SettingGetGlobal_s(G, cSetting_button_mode_name), "3-Button Motions"))
1829     result = 6;
1830   return result;
1831 }
1832 
ExecutiveAddKey(CExecutive * I,SpecRec * rec)1833 static int ExecutiveAddKey(CExecutive * I, SpecRec * rec)
1834 {
1835   int ok = false;
1836   OVreturn_word result;
1837   if(OVreturn_IS_OK((result = OVLexicon_GetFromCString(I->Lex, rec->name)))) {
1838     if(OVreturn_IS_OK(OVOneToOne_Set(I->Key, result.word, rec->cand_id))) {
1839       ok = true;
1840     }
1841   }
1842   return ok;
1843 }
1844 
ExecutiveDelKey(CExecutive * I,SpecRec * rec)1845 static int ExecutiveDelKey(CExecutive * I, SpecRec * rec)
1846 {
1847   int ok = false;
1848   OVreturn_word result;
1849   if(OVreturn_IS_OK((result = OVLexicon_BorrowFromCString(I->Lex, rec->name)))) {
1850     if(OVreturn_IS_OK(OVLexicon_DecRef(I->Lex, result.word)) &&
1851        OVreturn_IS_OK(OVOneToOne_DelForward(I->Key, result.word))) {
1852       ok = true;
1853     }
1854   }
1855   return ok;
1856 }
1857 
ExecutiveUnambiguousNameMatch(PyMOLGlobals * G,const char * name)1858 static SpecRec *ExecutiveUnambiguousNameMatch(PyMOLGlobals * G, const char *name)
1859 {
1860   CExecutive *I = G->Executive;
1861   SpecRec *result = NULL;
1862   SpecRec *rec = NULL;
1863   int best = 0;
1864   int wm;
1865   int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
1866 
1867   while(ListIterate(I->Spec, rec, next)) {
1868     wm = WordMatch(G, name, rec->name, ignore_case);
1869     if(wm < 0) {                /* exact match, so this is valid */
1870       result = rec;
1871       best = wm;
1872       break;
1873     } else if((wm > 0) && (best < wm)) {
1874       result = rec;
1875       best = wm;
1876     } else if((wm > 0) && (best == wm)) {       /* ambiguous match... no good */
1877       result = NULL;
1878     }
1879   }
1880   return (result);
1881 }
1882 
ExecutiveAnyCaseNameMatch(PyMOLGlobals * G,const char * name)1883 static SpecRec *ExecutiveAnyCaseNameMatch(PyMOLGlobals * G, const char *name)
1884 {
1885   CExecutive *I = G->Executive;
1886   SpecRec *result = NULL;
1887   SpecRec *rec = NULL;
1888 
1889   int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
1890   while(ListIterate(I->Spec, rec, next)) {
1891     if(WordMatchExact(G, name, rec->name, ignore_case)) {
1892       result = rec;
1893       break;
1894     }
1895   }
1896   return (result);
1897 }
1898 
1899 /*
1900  * Scroll the i'th match in the object menu panel to the top.
1901  * Scroll to last match if i < 0 and to first match if i > #-1.
1902  * Open groups if hit is inside.
1903  * Highlight the hit (same as mouse click highlight).
1904  *
1905  * Returns the number of hits
1906  */
ExecutiveScrollTo(PyMOLGlobals * G,const char * name,int i)1907 int ExecutiveScrollTo(PyMOLGlobals * G, const char * name, int i) {
1908   CExecutive *I = G->Executive;
1909   PanelRec *panel = NULL;
1910   int pos = 0, numhits = 0;
1911   ObjectGroup *group;
1912   SpecRec *tmp, *spec = NULL, *first = NULL;
1913   int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
1914   int j, lendiff, plen = strlen(name);
1915 
1916   ok_assert(1, I->Spec);
1917 
1918   // i'th substring match, skip the "all" item
1919   for(tmp = I->Spec->next; tmp; tmp = tmp->next) {
1920     lendiff = strlen(tmp->name) - plen;
1921     for(j = 0; j <= lendiff; j++)
1922       if(WordMatchNoWild(G, name, tmp->name + j, ignore_case)) {
1923         if(numhits++ == i || i < 0)
1924           spec = tmp;
1925         if(!first)
1926           first = tmp;
1927         break;
1928       }
1929     tmp->hilight = 0;
1930   }
1931 
1932   // if i was out of range
1933   if(!spec)
1934     spec = first;
1935 
1936   ok_assert(1, spec);
1937 
1938   // flash button until panel is clicked for the next time
1939   spec->hilight = 1;
1940 
1941   // open parent groups
1942   for(tmp = spec->group; tmp; tmp = tmp->group) {
1943     if(!(tmp->type == cExecObject &&
1944          tmp->obj->type == cObjectGroup))
1945       break;
1946     group = (ObjectGroup *) tmp->obj;
1947     if(!group->OpenOrClosed) {
1948       group->OpenOrClosed = 1;
1949       ExecutiveInvalidatePanelList(G);
1950     }
1951   }
1952 
1953   // in case any parent got opened
1954   ExecutiveUpdatePanelList(G);
1955 
1956   // scroll that record to the top
1957   while(ListIterate(I->Panel, panel, next)) {
1958     if(panel->spec == spec) {
1959       I->m_ScrollBar.setValueNoCheck(pos);
1960       return numhits;
1961     }
1962     pos++;
1963   }
1964 
1965 ok_except1:
1966   return numhits;
1967 }
1968 
ExecutiveUpdateColorDepends(PyMOLGlobals * G,ObjectMolecule * mol)1969 void ExecutiveUpdateColorDepends(PyMOLGlobals * G, ObjectMolecule * mol)
1970 {
1971   CExecutive *I = G->Executive;
1972   SpecRec *rec = NULL;
1973 
1974   while(ListIterate(I->Spec, rec, next)) {
1975     if(rec->type == cExecObject) {
1976       if(rec->obj->type == cObjectGadget) {
1977         ObjectGadget *gadget = (ObjectGadget *) rec->obj;
1978         if(gadget->GadgetType == cGadgetRamp) {
1979           ObjectGadgetRamp *ramp = (ObjectGadgetRamp *) gadget;
1980           if(ramp->RampType == cRampMol) {
1981             if(ramp->Mol == mol) {
1982               ExecutiveInvalidateRep(G, cKeywordAll, cRepAll, cRepInvColor);
1983               break;
1984             }
1985           }
1986         }
1987       }
1988     }
1989   }
1990 }
1991 
ExecutiveUpdateCoordDepends(PyMOLGlobals * G,ObjectMolecule * mol)1992 void ExecutiveUpdateCoordDepends(PyMOLGlobals * G, ObjectMolecule * mol)
1993 {                               /* nasty, ugly, inefficient hack */
1994 
1995   CExecutive *I = G->Executive;
1996   SpecRec *rec = NULL;
1997   ObjectGadget *gadget;
1998   int done_inv_all = false;
1999   int dynamic_measures = SettingGet_b(G, mol ? mol->Setting : NULL, NULL,
2000       cSetting_dynamic_measures);
2001 
2002   while(ListIterate(I->Spec, rec, next)) {
2003     if(rec->type == cExecObject) {
2004       switch(rec->obj->type) {
2005       case cObjectGadget:
2006         if(done_inv_all)
2007           break;
2008         gadget = (ObjectGadget *) rec->obj;
2009         if(gadget->GadgetType == cGadgetRamp) {
2010           ObjectGadgetRamp *ramp = (ObjectGadgetRamp *) gadget;
2011           if(ramp->RampType == cRampMol) {
2012             if(ramp->Mol == mol) {
2013               ExecutiveInvalidateRep(G, cKeywordAll, cRepAll, cRepInvColor);
2014               done_inv_all = true;
2015             }
2016           }
2017         }
2018         break;
2019       case cObjectMeasurement:
2020         if(dynamic_measures)
2021           ObjectDistMoveWithObject((ObjectDist*) rec->obj, mol);
2022         break;
2023       case cObjectAlignment:
2024         rec->obj->invalidate(
2025             cRepAll, cRepInvRep, cSelectorUpdateTableAllStates);
2026         break;
2027       }
2028     }
2029   }
2030 }
2031 
ExecutiveValidNamePattern(PyMOLGlobals * G,const char * name)2032 int ExecutiveValidNamePattern(PyMOLGlobals * G, const char *name)
2033 {
2034   int result = false;
2035   CWordMatcher *matcher;
2036   CWordMatchOptions options;
2037   const char *wildcard = SettingGetGlobal_s(G, cSetting_wildcard);
2038 
2039   WordMatchOptionsConfigNameList(&options,
2040                                  *wildcard, SettingGetGlobal_b(G, cSetting_ignore_case));
2041   matcher = WordMatcherNew(G, name, &options, false);
2042   if(matcher) {                 /* this appears to be a pattern */
2043     result = true;
2044     WordMatcherFree(matcher);
2045   } else if(ExecutiveUnambiguousNameMatch(G, name)) {
2046     /* this does not appear to be a pattern, so it is an unambiguous partial name? */
2047     result = true;
2048   }
2049   return result;
2050 
2051 }
2052 
2053 #define cExecNoExpand false
2054 #define cExecExpandGroups true
2055 #define cExecExpandKeepGroups 2
2056 
ExecutiveExpandGroupsInList(PyMOLGlobals * G,int list_id,int expand_groups)2057 static void ExecutiveExpandGroupsInList(PyMOLGlobals * G, int list_id, int expand_groups)
2058 {
2059   CExecutive *I = G->Executive;
2060   CTracker *I_Tracker = I->Tracker;
2061   int new_member_added = true;
2062   SpecRec *rec;
2063   ExecutiveUpdateGroups(G, false);
2064   while(new_member_added) {     /* keep adding til we can't add no more */
2065     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2066     int cand_id;
2067     new_member_added = false;
2068     if(iter_id) {
2069       while((cand_id = TrackerIterNextCandInList(I_Tracker, iter_id,
2070                                                  (TrackerRef **) (void *) &rec))) {
2071         if(rec && (rec->type == cExecObject) &&
2072            rec->group_member_list_id && (rec->obj->type == cObjectGroup)) {
2073           int group_iter_id = TrackerNewIter(I_Tracker, 0, rec->group_member_list_id);
2074           int group_cand_id;
2075           SpecRec *group_rec;
2076           if(group_iter_id) {
2077             while((group_cand_id = TrackerIterNextCandInList(I_Tracker, group_iter_id,
2078                                                              (TrackerRef **) (void *)
2079                                                              &group_rec))) {
2080               if(group_rec && group_cand_id) {
2081                 if(TrackerLink(I_Tracker, group_cand_id, list_id, 1))
2082                   new_member_added = true;
2083               }
2084             }
2085             TrackerDelIter(I_Tracker, group_iter_id);
2086           }
2087         }
2088       }
2089       TrackerDelIter(I_Tracker, iter_id);
2090     }
2091   }
2092   /* now purge all groups from the expanded list */
2093   if(expand_groups != cExecExpandKeepGroups) {
2094     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2095     int cand_id;
2096     while((cand_id = TrackerIterNextCandInList(I_Tracker, iter_id,
2097                                                (TrackerRef **) (void *) &rec))) {
2098       if(rec && (rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
2099         TrackerUnlink(I_Tracker, cand_id, list_id);
2100       }
2101     }
2102   }
2103 }
2104 
2105 
2106 /* DON'T FORGET TO RELEASE LIST WHEN DONE!!! */
ExecutiveGetNamesListFromPattern(PyMOLGlobals * G,const char * name,int allow_partial,int expand_groups)2107 static int ExecutiveGetNamesListFromPattern(PyMOLGlobals * G, const char *name,
2108                                             int allow_partial, int expand_groups)
2109 {
2110   CExecutive *I = G->Executive;
2111   int result = 0;
2112   CWordMatcher *matcher;
2113   CWordMatchOptions options;
2114   CTracker *I_Tracker = I->Tracker;
2115   const char *wildcard = SettingGetGlobal_s(G, cSetting_wildcard);
2116   int iter_id = TrackerNewIter(I_Tracker, 0, I->all_names_list_id);
2117   int cand_id;
2118   int group_found = false;
2119   SpecRec *rec;
2120 
2121   if (!name)
2122     return -1;
2123 
2124   // sanity check: name patterns are not object selections, bail if
2125   // parenthesis or operators in pattern
2126   if (strchr(name, '(') || strchr(name, ')') || strchr(name, '|')) {
2127     PRINTFB(G, FB_Executive, FB_Errors)
2128       " Names-Pattern-Error: Pattern looks like an atom selection"
2129       " (has parenthesis or operators), this is not supported for"
2130       " object name patterns.\n" ENDFB(G);
2131     return -1;
2132   }
2133 
2134   // special case: allow "not ..."
2135   bool match_not = false;
2136   if (WordMatchNoWild(G, "not ", name, false)) {
2137     match_not = true;
2138     name += 4;
2139   } else if (name[0] == '!') {
2140     match_not = true;
2141     name += 1;
2142   }
2143 
2144   // skip whitespace
2145   while (name[0] == ' ') {
2146     ++name;
2147   }
2148 
2149   bool match_enabled = WordMatchExact(G, "enabled", name, false);
2150 
2151   // ignore % and ? prefixes
2152   while(name[0] && (name[0] == '%' || name[0] == '?'))
2153     name++;
2154 
2155   WordMatchOptionsConfigNameList(&options,
2156                                  *wildcard, SettingGetGlobal_b(G, cSetting_ignore_case));
2157   matcher = WordMatcherNew(G, name, &options, /* force= */ match_not);
2158   if(matcher || match_enabled) {
2159     if(iter_id) {
2160       while((cand_id = TrackerIterNextCandInList(I_Tracker, iter_id,
2161                                                  (TrackerRef **) (void *) &rec))) {
2162         if(rec && !(rec->type == cExecAll)) {
2163           bool test = match_enabled ? SpecRecIsEnabled(rec) :
2164             WordMatcherMatchAlpha(matcher, rec->name);
2165           if(test ^ match_not) {
2166             if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup))
2167               group_found = true;
2168             if(!result)
2169               result = TrackerNewList(I_Tracker, NULL);
2170             if(result) {
2171               TrackerLink(I_Tracker, cand_id, result, 1);
2172             }
2173           }
2174         }
2175       }
2176     }
2177   } else if((rec = ExecutiveFindSpec(G, name))) {       /* only one name in list */
2178     if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup))
2179       group_found = true;
2180     result = TrackerNewList(I_Tracker, NULL);
2181     TrackerLink(I_Tracker, rec->cand_id, result, 1);
2182   } else if(allow_partial && (rec = ExecutiveUnambiguousNameMatch(G, name))) {
2183     if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup))
2184       group_found = true;
2185     result = TrackerNewList(I_Tracker, NULL);
2186     TrackerLink(I_Tracker, rec->cand_id, result, 1);
2187   }
2188   if(matcher)
2189     WordMatcherFree(matcher);
2190   if(iter_id)
2191     TrackerDelIter(I->Tracker, iter_id);
2192   if(group_found && expand_groups) {
2193     ExecutiveExpandGroupsInList(G, result, expand_groups);
2194   }
2195   return result;
2196 }
2197 
ExecutiveUngroup(PyMOLGlobals * G,const char * members,int quiet)2198 static int ExecutiveUngroup(PyMOLGlobals* G, const char* members, int quiet)
2199 {
2200   for (auto& rec : ExecutiveGetSpecRecsFromPattern(G, members)) {
2201     rec.group_name[0] = 0;
2202     rec.group = nullptr;
2203   }
2204   ExecutiveInvalidateGroups(G, true);
2205   return true;
2206 }
2207 
ExecutiveGroup(PyMOLGlobals * G,const char * name,const char * members,int action,int quiet)2208 int ExecutiveGroup(PyMOLGlobals * G, const char *name, const char *members, int action, int quiet)
2209 {
2210   if (action == cExecutiveGroupUngroup) {
2211     // Up to PyMOL 2.3 the member argument was ignored and 'name' used for ungrouping
2212     if (name[0]) {
2213       members = name;
2214     }
2215     return ExecutiveUngroup(G, members, quiet);
2216   }
2217 
2218   int ok = true;
2219   CExecutive *I = G->Executive;
2220   auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
2221 
2222   ObjectNameType valid_name;
2223   UtilNCopy(valid_name, name, sizeof(ObjectNameType));
2224   ObjectMakeValidName(G, valid_name);
2225 
2226   CObject *obj = ExecutiveFindObjectByName(G, valid_name);
2227 
2228   if(obj && (obj->type != cObjectGroup)) {
2229       PRINTFB(G, FB_Executive, FB_Errors)
2230         " Group-Error: object '%s' is not a group object.", name ENDFB(G);
2231       ok = false;
2232   } else {
2233     if((!obj) && (action == cExecutiveGroupAdd)) {
2234       obj = (CObject *) new ObjectGroup(G);
2235       if(obj) {
2236         ObjectSetName(obj, valid_name);
2237         ExecutiveManageObject(G, obj, false, true);
2238       }
2239     }
2240   }
2241   if((!members[0]) && ((action == cExecutiveGroupOpen) ||
2242                        (action == cExecutiveGroupClose) ||
2243                        (action == cExecutiveGroupToggle) ||
2244                        (action == cExecutiveGroupEmpty) ||
2245                        (action == cExecutiveGroupPurge) ||
2246                        (action == cExecutiveGroupExcise))) {
2247     ExecutiveUpdateGroups(G, false);
2248     {
2249       CTracker *I_Tracker = I->Tracker;
2250       int list_id = ExecutiveGetNamesListFromPattern(G, name, true, false);
2251       int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2252       SpecRec *rec;
2253 
2254       while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
2255         if(rec) {
2256           ObjectGroup *objGroup = NULL;
2257           if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
2258             objGroup = (ObjectGroup *) rec->obj;
2259           }
2260 
2261           switch (action) {
2262           case cExecutiveGroupOpen:
2263             if(objGroup)
2264               objGroup->OpenOrClosed = 1;
2265             break;
2266           case cExecutiveGroupClose:
2267             if(objGroup)
2268               objGroup->OpenOrClosed = 0;
2269             break;
2270           case cExecutiveGroupToggle:
2271             if(objGroup)
2272               objGroup->OpenOrClosed = !objGroup->OpenOrClosed;
2273             break;
2274           case cExecutiveGroupEmpty:
2275             if(objGroup) {
2276               SpecRec *rec2 = NULL;
2277               while(ListIterate(I->Spec, rec2, next)) {
2278                 if((rec2->group == rec)
2279                    || WordMatchExact(G, rec2->group_name, rec->name, ignore_case)) {
2280                   rec2->group = NULL;
2281                   rec2->group_name[0] = 0;
2282                 }
2283               }
2284             }
2285             break;
2286           case cExecutiveGroupPurge:
2287             if(objGroup) {
2288               SpecRec *rec2 = NULL;
2289               while(ListIterate(I->Spec, rec2, next)) {
2290                 if((rec2->group == rec)
2291                    || WordMatchExact(G, rec2->group_name, rec->name, ignore_case)) {
2292                   ExecutiveDelete(G, rec2->name);
2293                   rec2 = NULL;  /* restart search (danger order N^2) */
2294                 }
2295               }
2296             }
2297             break;
2298           case cExecutiveGroupExcise:
2299             if(objGroup) {
2300 
2301               if(rec->group_name[0]) {
2302                 /* cascade group members up to the surrounding group */
2303                 SpecRec *rec2 = NULL;
2304                 while(ListIterate(I->Spec, rec2, next)) {
2305                   if((rec2->group == rec) ||
2306                      WordMatch(G, rec->name, rec2->group_name, ignore_case)) {
2307                     strcpy(rec2->group_name, rec->group_name);
2308                     rec2->group = rec->group;
2309                   }
2310                 }
2311               } else if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
2312                 /* and/or delete their group membership */
2313                 SpecRec *rec2 = NULL;
2314                 while(ListIterate(I->Spec, rec2, next)) {
2315                   if((rec2->group == rec) ||
2316                      WordMatch(G, rec->name, rec2->group_name, ignore_case)) {
2317                     rec2->group_name[0] = 0;
2318                     rec2->group = nullptr;
2319                   }
2320                 }
2321               }
2322               ExecutiveDelete(G, rec->name);
2323             }
2324             break;
2325           }
2326         }
2327       }
2328       TrackerDelList(I_Tracker, list_id);
2329       TrackerDelIter(I_Tracker, iter_id);
2330       ExecutiveInvalidateGroups(G, true);
2331     }
2332   } else {
2333     if(obj && (obj->type == cObjectGroup)) {
2334       ObjectGroup *objGroup = (ObjectGroup *) obj;
2335       switch (action) {
2336       case cExecutiveGroupOpen:
2337         objGroup->OpenOrClosed = 1;
2338         break;
2339       case cExecutiveGroupClose:
2340         objGroup->OpenOrClosed = 0;
2341         break;
2342       case cExecutiveGroupToggle:
2343         objGroup->OpenOrClosed = !objGroup->OpenOrClosed;
2344         break;
2345       }
2346       if(members[0] && (action != cExecutiveGroupRemove))
2347         action = cExecutiveGroupAdd;
2348 
2349       switch (action) {
2350       case cExecutiveGroupAdd:
2351         {
2352 
2353           CTracker *I_Tracker = I->Tracker;
2354           int list_id = ExecutiveGetNamesListFromPattern(G, members, true, false);
2355           int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2356           SpecRec *rec;
2357 
2358           while(TrackerIterNextCandInList(I_Tracker, iter_id,
2359                                           (TrackerRef **) (void *) &rec)) {
2360             if(rec &&
2361                ((rec->type != cExecObject) ||
2362                 ((rec->type == cExecObject) && (rec->obj != obj)))) {
2363               UtilNCopy(rec->group_name, valid_name, sizeof(WordType));
2364               if(!quiet) {
2365                 PRINTFB(G, FB_Executive, FB_Actions)
2366                   " Executive: adding '%s' to group '%s'.\n", rec->name, rec->group_name
2367                   ENDFB(G);
2368               }
2369             }
2370           }
2371           TrackerDelList(I_Tracker, list_id);
2372           TrackerDelIter(I_Tracker, iter_id);
2373         }
2374         break;
2375       }
2376 
2377       ExecutiveInvalidateGroups(G, true);
2378     }
2379   }
2380   return ok;
2381 }
2382 
ExecutiveGetUniqueIDAtomVLADict(PyMOLGlobals * G,ExecutiveObjectOffset ** return_vla,OVOneToOne ** return_dict)2383 static int ExecutiveGetUniqueIDAtomVLADict(PyMOLGlobals * G,
2384                                             ExecutiveObjectOffset ** return_vla,
2385                                             OVOneToOne ** return_dict)
2386 {
2387   CExecutive *I = G->Executive;
2388   OVOneToOne *o2o = OVOneToOne_New(G->Context->heap);
2389   ExecutiveObjectOffset *vla = VLAlloc(ExecutiveObjectOffset, 1000);
2390   int n_oi = 0;
2391   {
2392     SpecRec *rec = NULL;
2393     while(ListIterate(I->Spec, rec, next)) {
2394       if(rec->type == cExecObject) {
2395         if(rec->obj->type == cObjectMolecule) {
2396           ObjectMolecule *obj = (ObjectMolecule *) rec->obj;
2397           int a, id, n_atom = obj->NAtom;
2398           const AtomInfoType *ai = obj->AtomInfo.data();
2399           for(a = 0; a < n_atom; a++) {
2400             if((id = ai->unique_id)) {
2401               if(OVOneToOne_GetForward(o2o, id).status == OVstatus_NOT_FOUND) {
2402                 if(OVreturn_IS_OK(OVOneToOne_Set(o2o, id, n_oi))) {
2403                   VLACheck(vla, ExecutiveObjectOffset, n_oi);
2404                   vla[n_oi].obj = obj;
2405                   vla[n_oi].atm = a;
2406                   n_oi++;
2407                 }
2408               }
2409             }
2410             ai++;
2411           }
2412         }
2413       }
2414     }
2415   }
2416   *return_dict = o2o;
2417   VLASize(vla, ExecutiveObjectOffset, n_oi);
2418   *return_vla = vla;
2419   return 1;
2420 }
2421 
ExecutiveDrawCmd(PyMOLGlobals * G,int width,int height,int antialias,int entire_window,int quiet)2422 int ExecutiveDrawCmd(PyMOLGlobals * G, int width, int height, int antialias,
2423                      int entire_window, int quiet)
2424 {
2425   CExecutive *I = G->Executive;
2426   if((width <= 0) && (height <= 0)) {
2427     SceneGetWidthHeight(G, &width, &height);
2428   }
2429   if(antialias < 0)
2430     antialias = SettingGetGlobal_i(G, cSetting_antialias);
2431   if(entire_window) {
2432     SceneInvalidateCopy(G, false);
2433     OrthoDirty(G);
2434     I->CaptureFlag = true;
2435   } else {
2436     if(SettingGetGlobal_i(G, cSetting_draw_mode) == -1) {
2437       ExecutiveSetSettingFromString(G, cSetting_draw_mode, "-2", "", -1, true, true);
2438       SceneUpdate(G, false);
2439     }
2440     SceneDeferImage(G, width, height, NULL, antialias, -1.0, cMyPNG_FormatPNG, quiet);
2441   }
2442   return 1;
2443 }
2444 
ExecutiveMatrixCopy2(PyMOLGlobals * G,CObject * source_obj,CObject * target_obj,int source_mode,int target_mode,int source_state,int target_state,int target_undo,int log,int quiet)2445 int ExecutiveMatrixCopy2(PyMOLGlobals * G,
2446                          CObject * source_obj, CObject * target_obj,
2447                          int source_mode, int target_mode,
2448                          int source_state, int target_state,
2449                          int target_undo, int log, int quiet)
2450 {
2451   /*  mode 0: raw coordinates, as per the txf history
2452      mode 1: object TTT matrix
2453      mode 2: state matrix */
2454 
2455   int ok = true;
2456   int copy_ttt_too = false;
2457   int matrix_mode = SettingGetGlobal_i(G, cSetting_matrix_mode);
2458   if(matrix_mode < 0)
2459     matrix_mode = 0; /* for now */
2460 
2461   if((source_mode < 0) && (target_mode < 0)) {
2462     copy_ttt_too = true;
2463   }
2464   if(source_mode < 0)
2465     source_mode = matrix_mode;
2466   if(target_mode < 0)
2467     target_mode = matrix_mode;
2468 
2469   switch (source_mode) {
2470   case 0:                      /* txf history is the source matrix */
2471     {
2472       double *history = NULL;
2473       int found = ExecutiveGetObjectMatrix2(G, source_obj, source_state, &history, false);
2474       if(found) {
2475         switch (target_mode) {
2476         case 0:                /* apply changes to coordinates in the target object */
2477           {
2478             double temp_inverse[16];
2479             if(target_undo) {
2480               double *target_history = NULL;
2481               int target_found = ExecutiveGetObjectMatrix2(G, source_obj,
2482                                                            target_state,
2483                                                            &target_history,
2484                                                            false);
2485               if(target_found && target_history) {
2486                 invert_special44d44d(target_history, temp_inverse);
2487                 if(history) {
2488                   right_multiply44d44d(temp_inverse, history);
2489                   history = temp_inverse;
2490                 } else {
2491                   history = temp_inverse;
2492                 }
2493               }
2494               {
2495                 float historyf[16];
2496                 if(history) {
2497                   convert44d44f(history, historyf);
2498                 } else {
2499                   identity44f(historyf);
2500                 }
2501                 ExecutiveTransformObjectSelection2(G, target_obj, target_state,
2502                                                    "", log, historyf, true, false);
2503               }
2504             }
2505             if(copy_ttt_too) {
2506               const float *tttf;
2507               int found = ObjectGetTTT(source_obj, &tttf, -1);
2508               if(found) {
2509                 ObjectSetTTT(target_obj, tttf, -1, -1);
2510                 target_obj->invalidate(cRepNone, cRepInvExtents, -1);
2511               }
2512             }
2513           }
2514           break;
2515         case 1:                /* applying changes to the object's TTT matrix */
2516           if(history) {
2517             float tttf[16];
2518             convertR44dTTTf(history, tttf);
2519             ObjectSetTTT(target_obj, tttf, -1, -1);
2520           } else {
2521             ObjectSetTTT(target_obj, NULL, -1, -1);
2522           }
2523           target_obj->invalidate(cRepNone, cRepInvExtents, -1);
2524           break;
2525         case 2:                /* applying changes to the state matrix */
2526           ok = ExecutiveSetObjectMatrix2(G, target_obj, target_state, history);
2527           break;
2528         }
2529         break;
2530       }
2531     }
2532     break;
2533   case 1:                      /* from the TTT matrix */
2534     {
2535       /* note that for now we're forcing states to be -1 */
2536       /* in the future, we may have per-state TTTs -- though right now the
2537          view matrices serve that purpose */
2538 
2539       const float *tttf;
2540       int found = ObjectGetTTT(source_obj, &tttf, -1);
2541       if(found) {
2542         switch (target_mode) {
2543         case 0:                /* coordinates & history unsupported.. */
2544           /* should complain */
2545           break;
2546         case 1:                /* TTT */
2547           ObjectSetTTT(target_obj, tttf, -1, -1);
2548           target_obj->invalidate(cRepNone, cRepInvExtents, -1);
2549           break;
2550         case 2:                /* State */
2551           if(tttf) {
2552             double homo[16];
2553             convertTTTfR44d(tttf, homo);
2554             ok = ExecutiveSetObjectMatrix2(G, target_obj, -1, homo);
2555           } else {
2556             ok = ExecutiveSetObjectMatrix2(G, target_obj, -1, NULL);
2557           }
2558           break;
2559         }
2560       }
2561     }
2562     break;
2563   case 2:                      /* from the state matrix */
2564     {
2565       double *homo;
2566       int found = ExecutiveGetObjectMatrix2(G, source_obj, source_state, &homo, false);
2567       if(found) {
2568         switch (target_mode) {
2569         case 0:                /* coordinates & history */
2570           /* TODO */
2571           break;
2572         case 1:                /* TTT */
2573           if(homo) {
2574             float tttf[16];
2575             convertR44dTTTf(homo, tttf);
2576             ObjectSetTTT(target_obj, tttf, -1, -1);
2577             target_obj->invalidate(cRepNone, cRepInvExtents, -1);
2578           } else {
2579             ObjectSetTTT(target_obj, NULL, -1, -1);
2580             target_obj->invalidate(cRepNone, cRepInvExtents, -1);
2581           }
2582           break;
2583         case 2:                /* State */
2584           ok = ExecutiveSetObjectMatrix2(G, target_obj, target_state, homo);
2585           if(copy_ttt_too) {
2586             const float *tttf;
2587             int found = ObjectGetTTT(source_obj, &tttf, -1);
2588             if(found) {
2589               ObjectSetTTT(target_obj, tttf, -1, -1);
2590               target_obj->invalidate(cRepNone, cRepInvExtents, -1);
2591             }
2592           }
2593           break;
2594         }
2595       }
2596     }
2597     break;
2598   }
2599   SceneInvalidate(G);
2600   return ok;
2601 }
2602 
ExecutiveMatrixCopy(PyMOLGlobals * G,const char * source_name,const char * target_name,int source_mode,int target_mode,int source_state,int target_state,int target_undo,int log,int quiet)2603 int ExecutiveMatrixCopy(PyMOLGlobals * G,
2604                         const char *source_name, const char *target_name,
2605                         int source_mode, int target_mode,
2606                         int source_state, int target_state,
2607                         int target_undo, int log, int quiet)
2608 {
2609   /*  mode 0: raw coordinates, as per the txf history
2610      mode 1: object TTT matrix
2611      mode 2: state matrix
2612      mode 3 (source only): camera matrix transformation */
2613 
2614   CExecutive *I = G->Executive;
2615   CTracker *I_Tracker = I->Tracker;
2616   SpecRec *src_rec = NULL;
2617   int ok = true;
2618   int copy_ttt_too = false;
2619   int matrix_mode = SettingGetGlobal_i(G, cSetting_matrix_mode);
2620   if(matrix_mode < 0)
2621     matrix_mode = 0; /* for now */
2622 
2623   if((source_mode < 0) && (target_mode < 0)) {
2624     copy_ttt_too = true;
2625   }
2626 
2627   if(source_mode < 0)
2628     source_mode = matrix_mode;
2629   if(target_mode < 0)
2630     target_mode = matrix_mode;
2631   if(source_name[0] == 0) {
2632     source_mode = 3;
2633     target_undo = 0;
2634   } else
2635     src_rec = ExecutiveFindSpec(G, source_name);
2636 
2637   if (source_mode != 3 && !src_rec) {
2638     PRINTFB(G, FB_Executive, FB_Warnings)
2639       " %s-Warning: Can't find source object '%s'.\n", __FUNCTION__, source_name
2640       ENDFB(G);
2641   }
2642 
2643   int list_id = ExecutiveGetNamesListFromPattern(G, target_name, true, cExecExpandKeepGroups);
2644 
2645   if (!list_id) {
2646     PRINTFB(G, FB_Executive, FB_Warnings)
2647       " %s-Warning: No match for target '%s'.\n", __FUNCTION__, target_name
2648       ENDFB(G);
2649   }
2650 
2651   switch (source_mode) {
2652   case 0:                      /* txf history is the source matrix */
2653     {
2654       double *history = NULL;
2655       int found = ExecutiveGetObjectMatrix(G, source_name, source_state, &history, false);
2656       if(found) {
2657 
2658         int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2659         SpecRec *rec;
2660 
2661         while(TrackerIterNextCandInList(I_Tracker, iter_id,
2662                                         (TrackerRef **) (void *) &rec)) {
2663           if(rec && (rec != src_rec)) {
2664             switch (rec->type) {
2665             case cExecObject:
2666 
2667               switch (target_mode) {
2668               case 0:          /* apply changes to coordinates in the target object */
2669                 {
2670                   double temp_inverse[16];
2671                   if(target_undo) {
2672                     double *target_history = NULL;
2673                     int target_found = ExecutiveGetObjectMatrix(G, rec->name,
2674                                                                 target_state,
2675                                                                 &target_history,
2676                                                                 false);
2677                     if(target_found && target_history) {
2678                       invert_special44d44d(target_history, temp_inverse);
2679                       if(history) {
2680                         right_multiply44d44d(temp_inverse, history);
2681                         history = temp_inverse;
2682                       } else {
2683                         history = temp_inverse;
2684                       }
2685                     }
2686                   }
2687                   {
2688                     float historyf[16];
2689                     if(history) {
2690                       convert44d44f(history, historyf);
2691                     } else {
2692                       identity44f(historyf);
2693                     }
2694                     ExecutiveTransformObjectSelection(G, rec->name, target_state,
2695                                                       "", log, historyf, true, false);
2696                   }
2697                   if(copy_ttt_too) {
2698                     const float *tttf;
2699                     int found = ExecutiveGetObjectTTT(G, source_name, &tttf, -1, quiet);
2700                     if(found) {
2701                       ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
2702                     }
2703                   }
2704                 }
2705                 break;
2706               case 1:          /* applying changes to the object's TTT matrix */
2707                 if(history) {
2708                   float tttf[16];
2709                   convertR44dTTTf(history, tttf);
2710                   ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
2711                 } else {
2712                   ExecutiveSetObjectTTT(G, rec->name, NULL, -1, quiet, -1);
2713                 }
2714                 /* to do: logging, return values, etc. */
2715                 break;
2716               case 2:          /* applying changes to the state matrix */
2717                 ok = ExecutiveSetObjectMatrix(G, rec->name, target_state, history);
2718                 break;
2719               }
2720               break;
2721             }
2722           }
2723         }
2724         TrackerDelIter(I_Tracker, iter_id);
2725       }
2726     }
2727     break;
2728   case 1:                      /* from the TTT matrix */
2729     {
2730       /* note that for now we're forcing states to be -1 */
2731       /* in the future, we may have per-state TTTs -- though right now the
2732          view matrices serve that purpose */
2733 
2734       const float *tttf;
2735       int found = ExecutiveGetObjectTTT(G, source_name, &tttf, -1, quiet);
2736       if(found) {
2737 
2738         int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2739         SpecRec *rec;
2740 
2741         while(TrackerIterNextCandInList(I_Tracker, iter_id,
2742                                         (TrackerRef **) (void *) &rec)) {
2743           if(rec && (rec != src_rec)) {
2744 
2745             switch (rec->type) {
2746             case cExecObject:
2747 
2748               switch (target_mode) {
2749               case 0:          /* coordinates & history unsupported.. */
2750                 /* should complain */
2751                 break;
2752               case 1:          /* TTT */
2753                 ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
2754                 break;
2755               case 2:          /* State */
2756                 if(tttf) {
2757                   double homo[16];
2758                   convertTTTfR44d(tttf, homo);
2759                   ok = ExecutiveSetObjectMatrix(G, rec->name, -1, homo);
2760                 } else {
2761                   ok = ExecutiveSetObjectMatrix(G, rec->name, -1, NULL);
2762                 }
2763                 break;
2764               }
2765             }
2766           }
2767         }
2768 
2769         TrackerDelIter(I_Tracker, iter_id);
2770       }
2771     }
2772     break;
2773   case 2:                      /* from the state matrix */
2774     {
2775       double *homo;
2776       int found = ExecutiveGetObjectMatrix(G, source_name, source_state, &homo, false);
2777       if(found) {
2778 
2779         int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2780         SpecRec *rec;
2781 
2782         while(TrackerIterNextCandInList(I_Tracker, iter_id,
2783                                         (TrackerRef **) (void *) &rec)) {
2784           if(rec && (rec != src_rec)) {
2785             switch (rec->type) {
2786             case cExecObject:
2787 
2788               switch (target_mode) {
2789               case 0:          /* coordinates & history */
2790                 /* TODO */
2791                 break;
2792               case 1:          /* TTT */
2793                 if(homo) {
2794                   float tttf[16];
2795                   convertR44dTTTf(homo, tttf);
2796                   ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
2797                 } else {
2798                   ExecutiveSetObjectTTT(G, rec->name, NULL, -1, quiet, -1);
2799                 }
2800                 break;
2801               case 2:          /* State */
2802                 ok = ExecutiveSetObjectMatrix(G, rec->name, target_state, homo);
2803                 if(copy_ttt_too) {
2804                   const float *tttf;
2805                   int found = ExecutiveGetObjectTTT(G, source_name, &tttf, -1, quiet);
2806                   if(found) {
2807                     ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
2808                   }
2809                 }
2810                 break;
2811               }
2812             }
2813           }
2814         }
2815         TrackerDelIter(I_Tracker, iter_id);
2816       }
2817     }
2818     break;
2819   case 3:                      /* camera */
2820     {
2821       SceneViewType view;
2822       double homo[16], *history;
2823       int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2824       SpecRec *rec;
2825       SceneGetView(G, view);
2826       homo[0] = view[0];
2827       homo[1] = view[4];
2828       homo[2] = view[8];
2829       homo[3] = -(view[0] * view[19] + view[4] * view[20] + view[8] * view[21]);
2830       homo[4] = view[1];
2831       homo[5] = view[5];
2832       homo[6] = view[9];
2833       homo[7] = -(view[1] * view[19] + view[5] * view[20] + view[9] * view[21]);
2834       homo[8] = view[2];
2835       homo[9] = view[6];
2836       homo[10] = view[10];
2837       homo[11] = -(view[2] * view[19] + view[6] * view[20] + view[10] * view[21]);
2838       homo[12] = 0.0;
2839       homo[13] = 0.0;
2840       homo[14] = 0.0;
2841       homo[15] = 1.0;
2842       history = homo;
2843       while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
2844         if(rec && (rec != src_rec)) {
2845           switch (rec->type) {
2846           case cExecObject:
2847 
2848             switch (target_mode) {
2849             case 0:            /* apply changes to coordinates in the target object */
2850               {
2851                 double temp_inverse[16];
2852                 if(target_undo) {
2853                   double *target_history = NULL;
2854                   int target_found = ExecutiveGetObjectMatrix(G, rec->name,
2855                                                               target_state,
2856                                                               &target_history,
2857                                                               false);
2858                   if(target_found && target_history) {
2859                     invert_special44d44d(target_history, temp_inverse);
2860                     if(history) {
2861                       right_multiply44d44d(temp_inverse, history);
2862                       history = temp_inverse;
2863                     } else {
2864                       history = temp_inverse;
2865                     }
2866                   }
2867                 }
2868                 {
2869                   float historyf[16];
2870                   if(history) {
2871                     convert44d44f(history, historyf);
2872                   } else {
2873                     identity44f(historyf);
2874                   }
2875                   ExecutiveTransformObjectSelection(G, rec->name, target_state,
2876                                                     "", log, historyf, true, false);
2877                 }
2878               }
2879               break;
2880             case 1:            /* applying changes to the object's TTT matrix */
2881               if(history) {
2882                 float tttf[16];
2883                 convertR44dTTTf(history, tttf);
2884                 ExecutiveSetObjectTTT(G, rec->name, tttf, -1, quiet, -1);
2885               } else {
2886                 ExecutiveSetObjectTTT(G, rec->name, NULL, -1, quiet, -1);
2887               }
2888               /* to do: logging, return values, etc. */
2889               break;
2890             case 2:            /* applying changes to the state matrix */
2891               ok = ExecutiveSetObjectMatrix(G, rec->name, target_state, history);
2892               break;
2893             }
2894             break;
2895           }
2896         }
2897         TrackerDelIter(I_Tracker, iter_id);
2898       }
2899     }
2900     break;
2901   }
2902 
2903   TrackerDelList(I_Tracker, list_id);
2904 
2905   SceneInvalidate(G);
2906   return ok;
2907 }
2908 
ExecutiveInvalidateMapDependents(PyMOLGlobals * G,const char * map_name,const char * new_name=NULL)2909 static void ExecutiveInvalidateMapDependents(PyMOLGlobals * G, const char *map_name, const char * new_name = NULL)
2910 {
2911   CExecutive *I = G->Executive;
2912   SpecRec *rec = NULL;
2913   while(ListIterate(I->Spec, rec, next)) {
2914     if(rec->type == cExecObject) {
2915       switch (rec->obj->type) {
2916       case cObjectMesh:
2917         ObjectMeshInvalidateMapName((ObjectMesh *) rec->obj, map_name, new_name);
2918         break;
2919       case cObjectSurface:
2920         ObjectSurfaceInvalidateMapName((ObjectSurface *) rec->obj, map_name, new_name);
2921         break;
2922       case cObjectVolume:
2923         ObjectVolumeInvalidateMapName((ObjectVolume *) rec->obj, map_name, new_name);
2924         break;
2925       }
2926     }
2927   }
2928   SceneInvalidate(G);
2929 }
2930 
ExecutiveResetMatrix(PyMOLGlobals * G,const char * name,int mode,int state,int log,int quiet)2931 pymol::Result<> ExecutiveResetMatrix(
2932     PyMOLGlobals* G, const char* name, int mode, int state, int log, int quiet)
2933 {
2934   CExecutive *I = G->Executive;
2935   CTracker *I_Tracker = I->Tracker;
2936   int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
2937   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
2938   SpecRec *rec;
2939   int matrix_mode = SettingGetGlobal_i(G, cSetting_matrix_mode);
2940   if(matrix_mode < 0)
2941     matrix_mode = 0; /* for now */
2942 
2943   if(mode < 0)
2944     mode = matrix_mode;
2945 
2946   bool obj_found = false;
2947   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
2948     if(rec && (rec->type == cExecObject)) {
2949       /*  CObject *obj = ExecutiveFindObjectByName(G,name); */
2950       CObject *obj = rec->obj;
2951       if(obj) {
2952         obj_found = true;
2953         switch (obj->type) {
2954         case cObjectMolecule:
2955           switch (mode) {
2956           case 0:              /* transformations already applied to the coordinates */
2957             {
2958               double *history = NULL;
2959               int found = ExecutiveGetObjectMatrix(G, rec->name, state, &history, false);
2960               if(found && history) {
2961                 double temp_inverse[16];
2962                 float historyf[16];
2963                 invert_special44d44d(history, temp_inverse);
2964                 convert44d44f(temp_inverse, historyf);
2965                 ExecutiveTransformObjectSelection(G, rec->name, state, "",
2966                                                   log, historyf, true, false);
2967               }
2968             }
2969             break;
2970           case 1:              /* operate on the TTT display matrix */
2971             ObjectResetTTT(obj,SettingGetGlobal_b(G,cSetting_movie_auto_store));
2972             obj->invalidate(cRepNone, cRepInvExtents, -1);
2973 
2974             break;
2975           case 2:              /* applying changes to the state matrix */
2976             {
2977               double ident[16];
2978               identity44d(ident);
2979               ExecutiveSetObjectMatrix(G, rec->name, state, ident);
2980             }
2981             break;
2982           }
2983           break;
2984         default:
2985           if (auto* objstate = obj->getObjectState(state)) {
2986             ObjectStateResetMatrix(objstate);
2987             obj->invalidate(cRepNone, cRepInvExtents, state);
2988           }
2989           break;
2990         }
2991       }
2992     }
2993   }
2994   if (!obj_found) {
2995     return pymol::make_error("No object found");
2996   }
2997   return {};
2998 }
2999 
3000 static double ret_mat[16];      /* UGH ..not thread-safe */
3001 
ExecutiveGetObjectMatrix2(PyMOLGlobals * G,CObject * obj,int state,double ** matrix,int incl_ttt)3002 static int ExecutiveGetObjectMatrix2(PyMOLGlobals * G, CObject * obj, int state,
3003                                      double **matrix, int incl_ttt)
3004 {
3005   /* right now, this only makes sense for molecule objects -- but in
3006      time all objects should have per-state matrices */
3007 
3008   int ok = false;
3009   if(state < 0) {
3010     /* to do -- TTT only */
3011   } else {
3012     if (auto* objstate = obj->getObjectState(state)) {
3013       *matrix = ObjectStateGetMatrix(objstate);
3014       ok = true;
3015     }
3016 
3017     if(ok && incl_ttt) {
3018       const float *ttt;
3019       double tttd[16];
3020       if(ObjectGetTTT(obj, &ttt, -1)) {
3021         convertTTTfR44d(ttt, tttd);
3022         if(*matrix) {
3023           copy44d(*matrix, ret_mat);
3024         } else {
3025           identity44d(ret_mat);
3026         }
3027         left_multiply44d44d(tttd, ret_mat);
3028         *matrix = ret_mat;
3029       }
3030     }
3031   }
3032   return ok;
3033 }
3034 
ExecutiveGetObjectMatrix(PyMOLGlobals * G,const char * name,int state,double ** matrix,int incl_ttt)3035 int ExecutiveGetObjectMatrix(PyMOLGlobals * G, const char *name, int state, double **matrix,
3036                              int incl_ttt)
3037 {
3038   int ok = false;
3039   CObject *obj = ExecutiveFindObjectByName(G, name);
3040   if(obj) {
3041     return ExecutiveGetObjectMatrix2(G, obj, state, matrix, incl_ttt);
3042   }
3043   return ok;
3044 }
3045 
ExecutiveSetObjectMatrix2(PyMOLGlobals * G,CObject * obj,int state,double * matrix)3046 static int ExecutiveSetObjectMatrix2(PyMOLGlobals * G, CObject * obj, int state,
3047                                      double *matrix)
3048 {
3049   /* -1 for the TTT matrix, 0 or greater for the state matrix */
3050 
3051   /* right now, this only makes sense for molecule objects -- but in
3052      time all objects should have per-state matrices */
3053   int ok = false;
3054   if(state < 0) {
3055 
3056   } else {
3057     if (auto* objstate = obj->getObjectState(state)) {
3058       ObjectStateSetMatrix(objstate, matrix);
3059       ok = true;
3060     }
3061   }
3062   return ok;
3063 }
3064 
ExecutiveSetObjectMatrix(PyMOLGlobals * G,const char * name,int state,double * matrix)3065 int ExecutiveSetObjectMatrix(PyMOLGlobals * G, const char *name, int state, double *matrix)
3066 {
3067   int ok = false;
3068   CObject *obj = ExecutiveFindObjectByName(G, name);
3069   if(obj) {
3070     return ExecutiveSetObjectMatrix2(G, obj, state, matrix);
3071   }
3072   return ok;
3073 }
3074 
ExecutiveCountNames(PyMOLGlobals * G)3075 static int ExecutiveCountNames(PyMOLGlobals * G)
3076 {
3077   int count = 0;
3078   CExecutive *I = G->Executive;
3079   SpecRec *rec = NULL;
3080   while(ListIterate(I->Spec, rec, next))
3081     count++;
3082 
3083   return (count);
3084 }
3085 
ReorderOrderFn(PyMOLGlobals * G,const SpecRec * const * rec,int l,int r)3086 static int ReorderOrderFn(PyMOLGlobals* G, const SpecRec* const* rec, int l, int r)
3087 {
3088   return (WordCompare(G, rec[l]->name, rec[r]->name, true) <= 0);
3089 }
3090 
SpecRecListPopulate(SpecRec ** list,SpecRec * first,const char * group_name)3091 static int SpecRecListPopulate(SpecRec ** list, SpecRec * first, const char * group_name)
3092 {
3093   /* Add items to list such that group items are ordered behind their group records.
3094    * author: Thomas Holder, 2013-08
3095    * runtime: O(N * G) where N is the number of SpecRecs and G the number of groups
3096    * args: list: pointer to the end of the list
3097    *       first: pointer to G->Executive->Spec (root of linked list)
3098    *       group_name: if empty string, append ungrouped items, otherwise append group items
3099    * returns: number of appended items
3100    */
3101   SpecRec *rec;
3102   int a = 0;
3103   for(rec = first; rec; rec = rec->next) {
3104     if(!strcmp(group_name, rec->group_name)) {
3105       list[a++] = rec;
3106       if(rec->type == cExecObject && rec->obj->type == cObjectGroup)
3107         a += SpecRecListPopulate(list + a, first, rec->name);
3108     }
3109   }
3110   return a;
3111 }
3112 
ExecutiveOrder(PyMOLGlobals * G,const char * s1,int sort,int location)3113 int ExecutiveOrder(PyMOLGlobals * G, const char *s1, int sort, int location)
3114 {
3115   CExecutive *I = G->Executive;
3116   CTracker *I_Tracker = I->Tracker;
3117   int ok = true;
3118   CWordList *word_list = WordListNew(G, s1);
3119   int n_names = ExecutiveCountNames(G);
3120 
3121   if(n_names) {
3122     SpecRec **list, **subset, **sorted;
3123     int *index = NULL;
3124     int n_sel;
3125     int source_row = -1;
3126     int min_row = -1;
3127     list = pymol::malloc<SpecRec *>(n_names);
3128     subset = pymol::calloc<SpecRec *>(n_names);
3129     sorted = pymol::calloc<SpecRec *>(n_names);
3130     index = pymol::malloc<int>(n_names);
3131     if(list && subset) {
3132       /* create an array of current names */
3133       {
3134         int a = 0;
3135         /* copy all names into array */
3136         /* update Thomas Holder 2013-08: order group members behind group.
3137          * fixes PYMOL-1382 (eventually).
3138          * I don't know if this is the best place for the fix, but it's my best guess.
3139          */
3140         SpecRecListPopulate(list, I->Spec, "");
3141         /* unlink them */
3142         for(a = 0; a < n_names; a++) {
3143           list[a]->next = NULL;
3144         }
3145       }
3146       /* transfer matching names to the subset array */
3147       {
3148         int a;
3149         int entry;
3150         int min_entry = word_list->n_word;
3151         const char *word = NULL;
3152         int word_iter = 0;
3153         while(WordListIterate(G, word_list, &word, &word_iter)) {
3154           int list_id = ExecutiveGetNamesListFromPattern(G, word, true, false);
3155           SpecRec *rec = NULL;
3156           entry = word_iter - 1;
3157           for(a = n_names - 1; a > 0; a--) {    /* skipping zeroth */
3158             int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
3159             while(TrackerIterNextCandInList
3160                   (I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
3161               if(rec == list[a]) {
3162                 if((a < min_row) || (min_row < 0))
3163                   min_row = a;
3164                 if(entry <= min_entry) {
3165                   source_row = a;       /* where will new list be inserted... */
3166                   min_entry = entry;
3167                 }
3168                 /* ensure that each record appears only once */
3169                 rec->next = subset[entry];
3170                 subset[entry] = rec;
3171                 list[a] = NULL;
3172               }
3173             }
3174             TrackerDelIter(I_Tracker, iter_id);
3175           }
3176           TrackerDelList(I_Tracker, list_id);
3177         }
3178         if(word_list->n_word && WordMatchExact(G, word_list->start[0], cKeywordAll, true))
3179           location = -1;        /* set to top if "all" is first in list */
3180       }
3181       /* expand the selected entries */
3182       {
3183         SpecRec *rec, *last;
3184         int b;
3185         n_sel = 0;
3186         for(b = 0; b < word_list->n_word; b++) {
3187           rec = subset[b];
3188           while(rec) {
3189             sorted[n_sel++] = rec;
3190             last = rec;
3191             rec = rec->next;
3192             last->next = NULL;
3193           }
3194         }
3195       }
3196       /* sort the selected entries, if requested */
3197       if(sort) {
3198         UtilCopyMem(subset, sorted, sizeof(SpecRec *) * n_sel);
3199         {
3200           int a;
3201           UtilSortIndexGlobals(G, n_sel, subset, index,
3202                                (UtilOrderFnGlobals *) ReorderOrderFn);
3203           for(a = 0; a < n_sel; a++) {
3204             sorted[a] = subset[index[a]];
3205           }
3206         }
3207       }
3208       /* reassemble the list using the new order */
3209       {
3210         SpecRec *spec = NULL;
3211         SpecRec *last = NULL;
3212         int a, b;
3213         int flag;
3214         for(a = 0; a < n_names; a++) {
3215           flag = false;
3216           if(sorted) {          /* not yet added */
3217             switch (location) {
3218             case -1:           /* top */
3219               if(a == 1)
3220                 flag = true;
3221               break;
3222             case -2:           /* upper */
3223               if(min_row >= 0) {
3224                 if(a == min_row)
3225                   flag = true;
3226               } else if(!list[a])
3227                 flag = true;
3228               break;
3229             case 0:            /* current */
3230               if(source_row >= 0) {
3231                 if(a == source_row)
3232                   flag = true;
3233               } else if(!list[a])
3234                 flag = true;
3235               break;
3236             }
3237           }
3238           if(flag) {
3239             for(b = 0; b < n_sel; b++) {
3240               if(sorted[b]) {
3241                 if(last)
3242                   last->next = sorted[b];
3243                 last = sorted[b];
3244                 if(!spec)
3245                   spec = last;
3246               }
3247             }
3248             FreeP(sorted);
3249           }
3250           if(list[a]) {
3251             if(last)
3252               last->next = list[a];
3253             last = list[a];
3254             if(!spec)
3255               spec = last;
3256           }
3257         }
3258         if(sorted) {            /* still not yet readded? */
3259           for(b = 0; b < n_sel; b++) {
3260             if(sorted[b]) {
3261               if(last)
3262                 last->next = sorted[b];
3263               last = sorted[b];
3264               if(!spec)
3265                 spec = last;
3266             }
3267           }
3268         }
3269         I->Spec = spec;
3270         OrthoDirty(G);
3271         SeqChanged(G);
3272       }
3273       FreeP(index);
3274       FreeP(sorted);
3275       FreeP(list);
3276       FreeP(subset);
3277     }
3278     ExecutiveInvalidatePanelList(G);
3279   }
3280   WordListFreeP(word_list);
3281   return (ok);
3282 }
3283 
ExecutiveGetObjectMoleculeVLA(PyMOLGlobals * G,const char * sele)3284 pymol::vla<ObjectMolecule*> ExecutiveGetObjectMoleculeVLA(PyMOLGlobals * G, const char *sele)
3285 {
3286   ObjectMolecule **result = NULL;
3287   int s1 = SelectorIndexByName(G, sele);
3288   if(s1 >= 0) {
3289     ObjectMoleculeOpRec op;
3290     ObjectMoleculeOpRecInit(&op);
3291     op.code = OMOP_GetObjects;
3292     op.obj1VLA = (ObjectMolecule **) VLAlloc(ObjectMolecule *, 10);
3293     op.i1 = 0;
3294     ExecutiveObjMolSeleOp(G, s1, &op);
3295     result = (ObjectMolecule **) op.obj1VLA;
3296     VLASize(result, ObjectMolecule *, op.i1);
3297   }
3298   return pymol::vla_take_ownership(result);
3299 }
3300 
3301 
3302 /* #define ExecLineHeight 18 */
3303 #define ExecClickMargin DIP2PIXEL(2)
3304 #define ExecTopMargin 0
3305 #define ExecToggleMargin DIP2PIXEL(2)
3306 #define ExecLeftMargin DIP2PIXEL(1)
3307 #define ExecRightMargin 0
3308 #define ExecToggleWidth DIP2PIXEL(17)
3309 #define ExecToggleSize DIP2PIXEL(16)
3310 #define ExecToggleTextShift DIP2PIXEL(4)
3311 
ExecutiveSetDrag(PyMOLGlobals * G,const char * name,int quiet,int mode)3312 int ExecutiveSetDrag(PyMOLGlobals * G, const char *name, int quiet,int mode)
3313 {
3314   char drag_name[] = cEditorDrag;
3315   int set_flag = false;
3316   int need_sele = true;
3317   int result = true;
3318   if(name[0]) {
3319     CObject *obj = ExecutiveFindObjectByName(G, name);
3320     if(obj) {
3321       EditorSetDrag(G, obj, -1, quiet, SceneGetState(G));
3322       set_flag = true;
3323     } else {
3324       SpecRec *rec = ExecutiveFindSpec(G, name);
3325       if(rec) {
3326         if(rec->type == cExecSelection) {
3327           SelectorCreate(G, drag_name, name, NULL, true, NULL);
3328           need_sele = false;
3329           {
3330             int sele = SelectorIndexByName(G, drag_name);
3331             ObjectMolecule *objMol = SelectorGetSingleObjectMolecule(G, sele);
3332             if(objMol) {
3333               if(mode>0)
3334                 sele = -1; /* force drag by matrix */
3335               EditorSetDrag(G, objMol, sele, quiet, SceneGetState(G));
3336               set_flag = true;
3337             } else {
3338               PRINTFB(G, FB_Executive, FB_Errors)
3339                 " Drag-Error: selection spans more than one object.\n" ENDFB(G);
3340             }
3341           }
3342         } else if(rec->type == cExecObject) {
3343           switch (rec->obj->type) {
3344           case cObjectGroup:
3345             PRINTFB(G, FB_Executive, FB_Errors)
3346               " Drag-Error: cannot drag group objects yet.\n" ENDFB(G);
3347             break;
3348 
3349           }
3350           result = false;
3351         }
3352       }
3353     }
3354     result = set_flag;
3355     if(!result) {
3356       EditorInactivate(G);
3357       PRINTFB(G, FB_Executive, FB_Errors)
3358         " Drag-Error: invalid or empty selection." ENDFB(G);
3359     } else if(EditorDraggingObjectMatrix(G)) {
3360       SelectorCreate(G, drag_name, "none", NULL, true, NULL);
3361     } else if(need_sele && (obj->type == cObjectMolecule) && (!EditorDraggingObjectMatrix(G))) {
3362       SelectorCreate(G, drag_name, obj->Name, (ObjectMolecule*)(void*)obj, true, NULL);     /* for indication only */
3363     }
3364   } else {
3365     EditorInactivate(G);
3366   }
3367   return result;
3368 }
3369 
ExecutivePop(PyMOLGlobals * G,const char * target,const char * source,int quiet)3370 int ExecutivePop(PyMOLGlobals * G, const char *target, const char *source, int quiet)
3371 {
3372   int ok = true;
3373   int src;
3374   int result = 0;
3375 
3376   ExecutiveDelete(G, target);
3377   if(ExecutiveFindObjectMoleculeByName(G, source)) {
3378     ok = false;
3379     PRINTFB(G, FB_Executive, FB_Errors)
3380       " Pop-Error: source selection '%s' can't be an object.\n", source ENDFB(G);
3381 
3382   } else {
3383     src = SelectorIndexByName(G, source);
3384     if(src < 0)
3385       ok = false;
3386     if(!ok) {
3387       PRINTFB(G, FB_Executive, FB_Errors)
3388         " Pop-Error: invalid source selection name '%s'\n", source ENDFB(G);
3389     } else {
3390       ObjectMoleculeOpRec op;
3391 
3392       ObjectMoleculeOpRecInit(&op);
3393       op.code = OMOP_Pop;
3394       SelectorCreateEmpty(G, target, true);
3395       op.i1 = SelectorIndexByName(G, target);
3396       op.i2 = 1;
3397       op.i3 = 0;
3398       ExecutiveObjMolSeleOp(G, src, &op);
3399       result = op.i3;
3400     }
3401   }
3402   if(!result)
3403     ExecutiveDelete(G, target);
3404   if(!ok)
3405     return -1;
3406   else
3407     return result;
3408 }
3409 
3410 /*
3411  * Return the selector index of the "active" alignment.
3412  */
ExecutiveGetActiveAlignmentSele(PyMOLGlobals * G)3413 int ExecutiveGetActiveAlignmentSele(PyMOLGlobals * G)
3414 {
3415   const char* alignment = ExecutiveGetActiveAlignment(G);
3416   if (alignment && alignment[0]) {
3417     return SelectorIndexByName(G, alignment);
3418   }
3419   return -1;
3420 }
3421 
3422 /*
3423  * Return the name of the "active" alignment. That is:
3424  *
3425  * 1) The "seq_view_alignment" setting if it's set
3426  * 2) or the name of the first enabled alignment object
3427  * 3) or NULL
3428  */
ExecutiveGetActiveAlignment(PyMOLGlobals * G)3429 const char* ExecutiveGetActiveAlignment(PyMOLGlobals* G)
3430 {
3431   const char *alignment = SettingGetGlobal_s(G, cSetting_seq_view_alignment);
3432   if(alignment && alignment[0]) {       /* explicit alignment setting name */
3433     return alignment;
3434   } else {                      /* otherwise, use the first active alignment */
3435     SpecRec *rec = NULL;
3436     CExecutive *I = G->Executive;
3437     while(ListIterate(I->Spec, rec, next)) {
3438       if(rec->visible) {
3439         if(rec->type == cExecObject)
3440           if(rec->obj->type == cObjectAlignment) {
3441             return rec->obj->Name;
3442           }
3443       }
3444     }
3445   }
3446   return nullptr;
3447 }
3448 
ExecutiveGetActiveSele(PyMOLGlobals * G)3449 int ExecutiveGetActiveSele(PyMOLGlobals * G)
3450 {
3451   ObjectNameType name;
3452   if(ExecutiveGetActiveSeleName(G, name, false, false))
3453     return SelectorIndexByName(G, name);
3454   else
3455     return -1;
3456 
3457 }
3458 
ExecutiveGetActiveSeleName(PyMOLGlobals * G,char * name,int create_new,int log)3459 int ExecutiveGetActiveSeleName(PyMOLGlobals * G, char *name, int create_new, int log)
3460 {
3461   /* TODO: cache/optimize to avoid table scan */
3462 
3463   int result = false;
3464   SpecRec *rec = NULL;
3465   CExecutive *I = G->Executive;
3466   while(ListIterate(I->Spec, rec, next)){
3467     if(rec->type == cExecSelection){
3468       if(rec->visible) {
3469         strcpy(name, rec->name);
3470         result = true;
3471       }
3472     }
3473   }
3474   if((!result) && create_new) {
3475     if(SettingGetGlobal_b(G, cSetting_auto_number_selections)) {
3476       int sel_num = SettingGetGlobal_i(G, cSetting_sel_counter) + 1;
3477 
3478       SettingSetGlobal_i(G, cSetting_sel_counter, sel_num);
3479       sprintf(name, "sel%02d", sel_num);
3480       SelectorCreateEmpty(G, name, -1);
3481       if(log) {
3482         if(SettingGetGlobal_i(G, cSetting_logging)) {
3483           OrthoLineType buf2;
3484           sprintf(buf2, "cmd.select('%s','none')\n", name);
3485           PLog(G, buf2, cPLog_no_flush);
3486         }
3487       }
3488     } else {
3489       sprintf(name, "sele");
3490       SelectorCreateEmpty(G, name, -1);
3491       if(log) {
3492         OrthoLineType buf2;
3493         sprintf(buf2, "cmd.select('%s','none')\n", name);
3494         PLog(G, buf2, cPLog_no_flush);
3495       }
3496     }
3497   }
3498   return result;
3499 }
3500 
ExecutiveGetActiveSeleName(PyMOLGlobals * G,std::string & name,int create_new,int log)3501 int ExecutiveGetActiveSeleName(PyMOLGlobals* G, std::string& name, int create_new, int log)
3502 {
3503   ObjectNameType name_arg;
3504   auto result = ExecutiveGetActiveSeleName(G, name_arg, create_new, log);
3505   name = name_arg;
3506   return result;
3507 }
3508 
ExecutiveFixChemistry(PyMOLGlobals * G,const char * s1,const char * s2,int invalidate,int quiet)3509 pymol::Result<> ExecutiveFixChemistry(PyMOLGlobals * G, const char *s1, const char *s2, int invalidate, int quiet)
3510 {
3511   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
3512   SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
3513 
3514   SpecRec *rec = NULL;
3515   CExecutive *I = G->Executive;
3516 
3517   assert((sele1 >= 0) && (sele2 >= 0));
3518   {
3519     while(ListIterate(I->Spec, rec, next)) {
3520       if(rec->type == cExecObject)
3521         if(rec->obj->type == cObjectMolecule) {
3522           ObjectMoleculeFixChemistry((ObjectMolecule *) rec->obj, sele1, sele2,
3523                                      invalidate);
3524         }
3525     }
3526   }
3527   return {};
3528 }
3529 
ExecutiveSetObjectColor(PyMOLGlobals * G,const char * name,const char * color,int quiet)3530 pymol::Result<> ExecutiveSetObjectColor(PyMOLGlobals * G, const char *name, const char *color, int quiet)
3531 {
3532   int col_ind = ColorGetIndex(G, color);
3533   auto obj = ExecutiveFindObjectByName(G, name);
3534   if(obj) {
3535     obj->Color = col_ind;
3536   } else {
3537     return pymol::make_error("Object ", name, " not found.");
3538   }
3539   return {};
3540 }
3541 
ExecutiveGetObjectColorIndex(PyMOLGlobals * G,const char * name)3542 int ExecutiveGetObjectColorIndex(PyMOLGlobals * G, const char *name)
3543 {
3544   int result = -1;
3545   CObject *obj = NULL;
3546   obj = ExecutiveFindObjectByName(G, name);
3547   if(obj) {
3548     result = obj->Color;
3549   }
3550   return (result);
3551 }
3552 
ExecutiveGetAtomVertex(PyMOLGlobals * G,const char * s1,int state,int index)3553 pymol::Result<std::array<float, 3>> ExecutiveGetAtomVertex(PyMOLGlobals * G, const char *s1, int state, int index)
3554 {
3555   auto tmpsele1 = SelectorTmp::make(G, s1);
3556   p_return_if_error(tmpsele1);
3557 
3558   switch (tmpsele1->getAtomCount()) {
3559   case 0:
3560     return pymol::Error("Empty Selection");
3561   case 1:
3562     return SelectorGetSingleAtomVertex(G, tmpsele1->getIndex(), state);
3563   }
3564 
3565   assert(tmpsele1->getAtomCount() > 0);
3566   return pymol::Error("More than one atom found");
3567 }
3568 
ExecutiveMakeUnusedName(PyMOLGlobals * G,char * prefix,int length,bool alwaysnumber,int start,const char * pattern)3569 void ExecutiveMakeUnusedName(PyMOLGlobals * G, char * prefix, int length,
3570                              bool alwaysnumber, int start,
3571                              const char * pattern) {
3572 
3573   if (!prefix[0])
3574     strcpy(prefix, "obj");
3575 
3576   int prefixlen = strlen(prefix);
3577   int suffixlen = length - prefixlen;
3578   char * end = prefix + prefixlen;
3579 
3580   for (int cnt = start; alwaysnumber || ExecutiveValidName(G, prefix); ++cnt) {
3581     snprintf(end, suffixlen, pattern, cnt);
3582     alwaysnumber = false;
3583   }
3584 }
3585 
ExecutiveGetUnusedName(PyMOLGlobals * G,const char * prefix,bool alwaysnumber)3586 std::string ExecutiveGetUnusedName(PyMOLGlobals * G, const char * prefix,
3587                                    bool alwaysnumber) {
3588 
3589   OrthoLineType unused_name;
3590   strcpy(unused_name, prefix);
3591 
3592   ObjectMakeValidName(G, unused_name);
3593 
3594   ExecutiveMakeUnusedName(G, unused_name, OrthoLineLength, alwaysnumber);
3595 
3596   return std::string(unused_name);
3597 }
3598 
ExecutiveProcessObjectName(PyMOLGlobals * G,const char * proposed,char * actual)3599 int ExecutiveProcessObjectName(PyMOLGlobals * G, const char *proposed, char *actual)
3600 {
3601   int result = true;
3602   UtilNCopy(actual, proposed, sizeof(ObjectNameType));
3603   if(SettingGetGlobal_b(G, cSetting_validate_object_names))
3604     ObjectMakeValidName(G, actual);
3605   if(SettingGetGlobal_b(G, cSetting_auto_rename_duplicate_objects) || !proposed[0]) {
3606     ExecutiveMakeUnusedName(G, actual, sizeof(ObjectNameType), false, 2, "_%d");
3607   }
3608   return result;
3609 }
3610 
ExecutiveSetName(PyMOLGlobals * G,const char * old_name,const char * new_name)3611 pymol::Result<> ExecutiveSetName(PyMOLGlobals * G, const char *old_name, const char *new_name)
3612 {
3613   SpecRec *rec = NULL;
3614   CExecutive *I = G->Executive;
3615   int found = false;
3616   auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
3617 
3618   ObjectNameType name;
3619   UtilNCopy(name, new_name, sizeof(ObjectNameType));
3620 
3621   if (ObjectMakeValidName(name)) {
3622     PRINTFB(G, FB_Executive, FB_Warnings)
3623       " Warning: Invalid characters in '%s' have been replaced or stripped\n",
3624       name ENDFB(G);
3625   }
3626 
3627   if(!name[0]) {
3628     return pymol::make_error("Blank names not allowed.");
3629   } else if(WordMatchExact(G, name, cKeywordSame, ignore_case) || SelectorNameIsKeyword(G, name)) {
3630     return pymol::make_error("Name ", name, " is a selection keyword.");
3631   }
3632   if(!WordMatchExact(G, name, old_name, ignore_case)) {
3633     while(ListIterate(I->Spec, rec, next)) {
3634       if(found)
3635         break;
3636       switch (rec->type) {
3637       case cExecObject:
3638         if(WordMatchExact(G, rec->obj->Name, old_name, ignore_case)) {
3639           ExecutiveDelKey(I, rec);
3640           ExecutiveDelete(G, name);
3641           ObjectSetName(rec->obj, name);
3642           UtilNCopy(rec->name, rec->obj->Name, WordLength);
3643           ExecutiveAddKey(I, rec);
3644           if(rec->obj->type == cObjectMolecule) {
3645             /*
3646                SelectorDelete(G,old_name);
3647                ExecutiveUpdateObjectSelection(G,rec->obj);
3648              */
3649             SelectorSetName(G, name, old_name);
3650 #ifndef _PYMOL_NO_UNDO
3651 #endif
3652             SceneChanged(G);
3653             SeqChanged(G);
3654           }
3655           if (rec->obj->type == cObjectMap)
3656             ExecutiveInvalidateMapDependents(G, old_name, name);
3657 
3658           found = true;
3659         }
3660         break;
3661       case cExecSelection:
3662         if(WordMatchExact(G, rec->name, old_name, ignore_case)) {
3663           if(SelectorSetName(G, name, old_name)) {
3664             ExecutiveDelete(G, name); /* just in case */
3665             ExecutiveDelKey(I, rec);
3666             UtilNCopy(rec->name, name, WordLength);
3667             ExecutiveAddKey(I, rec);
3668             found = true;
3669             OrthoDirty(G);
3670           }
3671         }
3672         break;
3673       }
3674     }
3675     if(!found)
3676       return pymol::make_error("Could not find object named ", name);
3677     else {
3678       rec = NULL;
3679       int old_name_len = strlen(old_name);
3680       int new_name_len = strlen(name);
3681       ObjectNameType childname;
3682       UtilNCopy(childname, name, sizeof(ObjectNameType));
3683       while(ListIterate(I->Spec, rec, next)) {
3684         if(WordMatchExact(G, rec->group_name, old_name, ignore_case)) {
3685           UtilNCopy(rec->group_name, name, WordLength);
3686           // rename group members for group_auto_mode
3687           if (strncmp(rec->name, old_name, old_name_len) == 0 && rec->name[old_name_len] == '.') {
3688             UtilNCopy(childname + new_name_len, rec->name + old_name_len, sizeof(ObjectNameType) - new_name_len);
3689             ExecutiveSetName(G, rec->name, childname);
3690           }
3691         }
3692       }
3693       ExecutiveInvalidateGroups(G, false);
3694     }
3695   }
3696   return {};
3697 }
3698 
3699 
3700 /*
3701  * Load any file type which is implemented in C.
3702  *
3703  * fname:       File name, can be empty if content is provided
3704  * content:     File contents, if not NULL
3705  * content_length:      Length of "content", if not NULL
3706  * content_format:      File type code
3707  * object_name:         New object name
3708  * state:       Object state to start loading new coordsets in
3709  * zoom:        Zoom on new loaded atoms
3710  * discrete:    Make discrete states
3711  * finish:      update object selection & zoom
3712  * multiplex:   Split new states into objects
3713  * quiet:       Suppress feedback
3714  * object_props:        names of object properties to load
3715  * atom_props:          names of atom properties to load
3716  */
3717 pymol::Result<>
ExecutiveLoad(PyMOLGlobals * G,const char * fname,const char * content,int content_length,cLoadType_t content_format,const char * object_name_proposed,int state,int zoom,int discrete,int finish,int multiplex,int quiet,const char * plugin_arg,const char * object_props,const char * atom_props,bool mimic)3718 ExecutiveLoad(PyMOLGlobals * G,
3719                   const char *fname,
3720                   const char *content, int content_length,
3721                   cLoadType_t content_format,
3722                   const char *object_name_proposed,
3723                   int state, int zoom,
3724                   int discrete, int finish, int multiplex, int quiet,
3725                   const char * plugin_arg,
3726                   const char * object_props,
3727                   const char * atom_props,
3728                   bool mimic)
3729 {
3730   auto res = ExecutiveLoadPrepareArgs(G, fname, content, content_length,
3731       content_format, object_name_proposed, state, zoom, discrete, finish,
3732       multiplex, quiet, plugin_arg, object_props, atom_props, mimic);
3733   if (!res) {
3734     return pymol::make_error("%s-Error: %s", __func__, res.error().what());
3735   }
3736   return ExecutiveLoad(G, res.result());
3737 }
3738 
3739 /**
3740  * Prepare arguments for ExecutiveLoad
3741  */
3742 pymol::Result<ExecutiveLoadArgs>
ExecutiveLoadPrepareArgs(PyMOLGlobals * G,pymol::null_safe_zstring_view fname,const char * content,int content_length,cLoadType_t content_format,const char * object_name_proposed,int state,int zoom,int discrete,int finish,int multiplex,int quiet,const char * plugin_arg,const char * object_props,const char * atom_props,bool mimic)3743 ExecutiveLoadPrepareArgs(PyMOLGlobals * G,
3744                   pymol::null_safe_zstring_view fname,
3745                   const char *content, int content_length,
3746                   cLoadType_t content_format,
3747                   const char *object_name_proposed,
3748                   int state, int zoom,
3749                   int discrete, int finish, int multiplex, int quiet,
3750                   const char * plugin_arg,
3751                   const char * object_props,
3752                   const char * atom_props,
3753                   bool mimic)
3754 {
3755   ExecutiveLoadArgs args;
3756   bool fname_null_ok = false;
3757 
3758   // validate proposed object name
3759   ObjectNameType object_name = "";
3760   ExecutiveProcessObjectName(G, object_name_proposed, object_name);
3761   args.object_name = object_name;
3762 
3763   // Ensure correct float parsing with scanf. It's possible to change this from
3764   // Python, so don't rely on a persistent global value.
3765   std::setlocale(LC_NUMERIC, "C");
3766 
3767   if (!object_props) object_props = SettingGetGlobal_s(G, cSetting_load_object_props_default);
3768   if (!atom_props)   atom_props   = SettingGetGlobal_s(G, cSetting_load_atom_props_default);
3769 
3770   // multiplex -2 -> "multiplex" setting
3771   // multiplex -1 -> file type dependant default
3772   // multiplex 1 -> split entries (don't try to load into existing object)
3773   if(multiplex == -2) {
3774     multiplex = SettingGetGlobal_i(G, cSetting_multiplex);
3775   }
3776 
3777   switch (content_format) {
3778   // string loading functions
3779   case cLoadTypePDBStr:
3780   case cLoadTypeVDBStr:
3781   case cLoadTypeCIFStr:
3782   case cLoadTypeMMTFStr:
3783   case cLoadTypeMAEStr:
3784   case cLoadTypeXPLORStr:
3785   case cLoadTypeCCP4Str:
3786   case cLoadTypePHIStr:
3787   case cLoadTypeMMDStr:
3788   case cLoadTypeMOLStr:
3789   case cLoadTypeMOL2Str:
3790   case cLoadTypeSDF2Str:
3791   case cLoadTypeXYZStr:
3792     if (!content) {
3793       return pymol::Error("content is NULL");
3794     }
3795     fname_null_ok = true;
3796     break;
3797   case cLoadTypePQR:
3798   case cLoadTypePDBQT:
3799   case cLoadTypePDB:
3800   case cLoadTypeCIF:
3801   case cLoadTypeMMTF:
3802   case cLoadTypeMAE:
3803   case cLoadTypeXPLORMap:
3804   case cLoadTypeCCP4Map:
3805   case cLoadTypeCCP4Unspecified:
3806   case cLoadTypeMRC:
3807   case cLoadTypePHIMap:
3808   case cLoadTypeMMD:
3809   case cLoadTypeMOL:
3810   case cLoadTypeMOL2:
3811   case cLoadTypeSDF2:
3812   case cLoadTypeXYZ:
3813     if (content) {
3814       fname_null_ok = true;
3815       break;
3816     }
3817 
3818     if (fname.empty()) {
3819       break;
3820     }
3821 
3822     try {
3823       args.content = pymol::file_get_contents(fname);
3824       PRINTFB(G, FB_Executive, FB_Blather)
3825         " %s: Loading from %s.\n", __func__, fname.c_str() ENDFB(G);
3826     } catch (...) {
3827       return pymol::Error(
3828           pymol::string_format("Unable to open file '%s'", fname.c_str()));
3829     }
3830 
3831     break;
3832 
3833   // molfile_plugin based formats
3834   case cLoadTypeCUBEMap:
3835     args.plugin = "cube";
3836     break;
3837   case cLoadTypeSpider:
3838     args.plugin = "spider";
3839     break;
3840   case cLoadTypeXTC:
3841     args.plugin = "xtc";
3842     break;
3843   case cLoadTypeTRR:
3844     args.plugin = "trr";
3845     break;
3846   case cLoadTypeGRO:
3847     args.plugin = "gro";
3848     break;
3849   case cLoadTypeG96:
3850     args.plugin = "g96";
3851     break;
3852   case cLoadTypeTRJ2:
3853     args.plugin = "trj";
3854     break;
3855   case cLoadTypeDCD:
3856     args.plugin = "dcd";
3857     break;
3858   case cLoadTypeDTR:
3859     args.plugin = "dtr";
3860     break;
3861   case cLoadTypeCMS:
3862     args.plugin = "mae";
3863     break;
3864   default:
3865     if (plugin_arg) {
3866       args.plugin = plugin_arg;
3867       // Hackish way to request a "sub-plugin". For example the "cube" plugin
3868       // can load maps or molecules:
3869       // load example.cube, molobj, format=plugin:cube:1
3870       // load example.cube, mapobj, format=plugin:cube:4
3871       auto pos = args.plugin.find(':');
3872       if (pos != std::string::npos) {
3873         args.plugin_mask = atoi(args.plugin.c_str() + pos + 1);
3874         args.plugin.resize(pos);
3875       }
3876     }
3877     break;
3878   }
3879 
3880   if (fname.empty() && !fname_null_ok) {
3881     return pymol::Error("This format requires a filename to load");
3882   }
3883 
3884   if (!args.plugin.empty()) {
3885     content_format = cLoadTypePlugin;
3886   }
3887 
3888   if (content) {
3889     assert(args.content.empty());
3890     args.content = std::string(content, content_length);
3891   }
3892 
3893   args.content_format = content_format;
3894   args.fname = fname.c_str();
3895   args.state = state;
3896   args.zoom = zoom;
3897   args.discrete = discrete;
3898   args.finish = finish;
3899   args.multiplex = multiplex;
3900   args.quiet = quiet;
3901   args.object_props = object_props;
3902   args.atom_props = atom_props;
3903   args.mimic = mimic;
3904   return args;
3905 }
3906 
ExecutiveLoad(PyMOLGlobals * G,ExecutiveLoadArgs const & args)3907 pymol::Result<> ExecutiveLoad(PyMOLGlobals* G, ExecutiveLoadArgs const& args)
3908 {
3909   CObject* origObj = nullptr;
3910   const char* fname = args.fname.c_str();
3911   const char* content = args.content.data();
3912   int size = args.content.size();
3913   const char* object_name = args.object_name.c_str();
3914   const char* object_props = args.object_props.c_str();
3915   const char* atom_props = args.atom_props.c_str();
3916   const char* plugin = args.plugin.c_str();
3917   auto content_format = args.content_format;
3918   auto state = args.state;
3919   auto zoom = args.zoom;
3920   auto discrete = args.discrete;
3921   auto finish = args.finish;
3922   auto multiplex = args.multiplex;
3923   auto quiet = args.quiet;
3924   auto mimic = args.mimic;
3925 
3926   if (multiplex != 1) {
3927     origObj = ExecutiveGetExistingCompatible(G, object_name, content_format);
3928   }
3929 
3930 #ifndef _PYMOL_NO_UNDO
3931 #endif
3932 
3933   // file type dependent multiplex and discrete default
3934   if(discrete < 0) {
3935     if(multiplex == 1) {
3936       discrete = 0;
3937     } else {
3938       switch (content_format) {
3939         case cLoadTypeMOL2:
3940         case cLoadTypeMOL2Str:
3941           discrete = -1;        /* content-dependent behavior... */
3942         case cLoadTypeSDF2:     /* SDF files currently default to discrete */
3943         case cLoadTypeSDF2Str:
3944           break;
3945         default:
3946           discrete = 0;
3947           break;
3948       }
3949     }
3950   }
3951 
3952   int ok = true;
3953   OrthoLineType buf = "";
3954   int pdb_variant = PDB_VARIANT_DEFAULT;
3955   CObject *obj = NULL;
3956 
3957   // downstream file type reading functions
3958   switch (content_format) {
3959   case cLoadTypePQR:
3960     pdb_variant = PDB_VARIANT_PQR;
3961   case cLoadTypePDBQT:
3962     if (content_format == cLoadTypePDBQT)
3963       pdb_variant = PDB_VARIANT_PDBQT;
3964   case cLoadTypeVDBStr:
3965     if (ok && content_format == cLoadTypeVDBStr)
3966       pdb_variant = PDB_VARIANT_VDB;
3967   case cLoadTypePDB:
3968   case cLoadTypePDBStr:
3969     ok = ExecutiveProcessPDBFile(G, origObj, fname, content, object_name,
3970         state, discrete, finish, buf, pdb_variant,
3971         quiet, multiplex, zoom);
3972     break;
3973   case cLoadTypeCIF:
3974   case cLoadTypeCIFStr:
3975     obj = (CObject *) ObjectMoleculeReadCifStr(G, (ObjectMolecule *) origObj,
3976         content, state, discrete, quiet, multiplex, zoom);
3977     break;
3978   case cLoadTypeMMTF:
3979   case cLoadTypeMMTFStr:
3980     obj = (CObject *) ObjectMoleculeReadMmtfStr(G, (ObjectMolecule *) origObj,
3981         content, size, state, discrete, quiet, multiplex, zoom);
3982     break;
3983   case cLoadTypeMAE:
3984   case cLoadTypeMAEStr:
3985     PRINTFB(G, FB_CCmd, FB_Errors)
3986       " Incentive-Only-Error: 'mae' format not supported by this PyMOL build\n"
3987       ENDFB(G);
3988     ok = false;
3989     break;
3990   case cLoadTypeTOP:
3991     if(origObj) {
3992       /* always reinitialize topology objects from scratch */
3993       ExecutiveDelete(G, origObj->Name);
3994       origObj = NULL;
3995     }
3996     obj = (CObject *) ObjectMoleculeLoadTOPFile(G, NULL, fname, state, discrete);
3997     break;
3998   case cLoadTypeTRJ:
3999     if(origObj) {
4000       ObjectMoleculeLoadTRJFile(G, (ObjectMolecule *) origObj, fname, state,
4001           1, 1, 1, -1, -1, NULL, 1, NULL, quiet);
4002     } else {
4003       return pymol::Error("must load object topology before loading trajectory!");
4004     }
4005     break;
4006   case cLoadTypeCRD:
4007     if(origObj) {
4008       ObjectMoleculeLoadRSTFile(G, (ObjectMolecule *) origObj, fname, state, quiet, 1);
4009     } else {
4010       return pymol::Error("must load object topology before loading coordinate file!");
4011     }
4012     break;
4013   case cLoadTypeRST:
4014     if(origObj) {
4015       ObjectMoleculeLoadRSTFile(G, (ObjectMolecule *) origObj, fname, state, quiet, 0);
4016     } else {
4017       return pymol::Error("must load object topology before loading restart file!");
4018     }
4019     break;
4020   case cLoadTypePMO:
4021     return pymol::Error("PMO format no longer supported.");
4022   case cLoadTypeDXMap:
4023     obj = (CObject *) ObjectMapLoadDXFile(G, (ObjectMap *) origObj, fname,
4024         state, quiet);
4025     break;
4026   case cLoadTypeFLDMap:
4027     obj = (CObject *) ObjectMapLoadFLDFile(G, (ObjectMap *) origObj, fname,
4028         state, quiet);
4029     break;
4030   case cLoadTypeBRIXMap:
4031     obj = (CObject *) ObjectMapLoadBRIXFile(G, (ObjectMap *) origObj, fname,
4032         state, quiet);
4033     break;
4034   case cLoadTypeGRDMap:
4035     obj = (CObject *) ObjectMapLoadGRDFile(G, (ObjectMap *) origObj, fname,
4036         state, quiet);
4037     break;
4038   case cLoadTypeACNTMap:
4039     obj = (CObject *) ObjectMapLoadACNTFile(G, (ObjectMap *) origObj, fname,
4040         state, quiet);
4041     break;
4042   case cLoadTypeXPLORMap:
4043   case cLoadTypeXPLORStr:
4044     obj = (CObject *) ObjectMapLoadXPLOR(G, (ObjectMap *) origObj, content,
4045         state, false, quiet);
4046     break;
4047   case cLoadTypePHIMap:
4048   case cLoadTypePHIStr:
4049     obj = (CObject *) ObjectMapLoadPHI(G, (ObjectMap *) origObj, content,
4050         state, true, size, quiet);
4051     break;
4052   case cLoadTypeCCP4Map:
4053   case cLoadTypeCCP4Str:
4054   case cLoadTypeCCP4Unspecified:
4055   case cLoadTypeMRC:
4056     obj = (CObject *) ObjectMapLoadCCP4(G, (ObjectMap *) origObj, content,
4057         state, true, size, quiet, content_format);
4058     break;
4059   case cLoadTypeCGO:
4060     obj = (CObject *) ObjectCGOFromFloatArray(G, (ObjectCGO *) origObj,
4061         (float *) content, size, state,
4062         quiet);
4063     break;
4064   case cLoadTypeMOL:
4065   case cLoadTypeMOLStr:
4066   case cLoadTypeMOL2:
4067   case cLoadTypeMOL2Str:
4068   case cLoadTypeSDF2:
4069   case cLoadTypeSDF2Str:
4070   case cLoadTypeXYZ:
4071   case cLoadTypeXYZStr:
4072   case cLoadTypeMMD:
4073   case cLoadTypeMMDStr:
4074     {
4075       const char * next_entry = content;
4076       char new_name[WordLength] = "";
4077 
4078       OVLexicon *loadproplex = NULL;
4079       bool loadpropertiesall = false;
4080 
4081       // (some of) these file types support multiple molecules per file,
4082       // and we support to load them into separate objects (multiplex).
4083       do {
4084         obj = (CObject *) ObjectMoleculeReadStr(G, (ObjectMolecule *) origObj,
4085             &next_entry, content_format,
4086             state, discrete,
4087             quiet, multiplex, new_name,
4088             loadpropertiesall, loadproplex);
4089 
4090         if(new_name[0]) {
4091           // multiplexing
4092           ObjectSetName(obj, new_name);
4093           ExecutiveDelete(G, obj->Name);       // just in case there is a collision
4094           ExecutiveManageObject(G, obj, zoom, true);
4095           new_name[0] = 0;
4096           obj = NULL;
4097         }
4098       } while(next_entry);
4099 
4100       OVLexicon_Del(loadproplex);
4101     }
4102     break;
4103   default:
4104     if(plugin[0]) {
4105       obj = PlugIOManagerLoad(G, origObj ? &origObj : NULL, fname, state, quiet,
4106           plugin, args.plugin_mask);
4107     } else {
4108         return pymol::make_error("Unable to read that file type from C (",
4109             content_format, ", ", plugin, ")");
4110     }
4111   }
4112 
4113   if(origObj && obj) {
4114     if(finish)
4115       ExecutiveUpdateObjectSelection(G, origObj);
4116 
4117     if(fname)
4118       sprintf(buf, " CmdLoad: \"%s\" appended into object \"%s\", state %d.\n",
4119           fname, object_name, state + 1);
4120   } else if(obj) {
4121     ObjectSetName(obj, object_name);
4122     ExecutiveManageObject(G, obj, zoom, true);
4123 
4124     if(fname)
4125       sprintf(buf, " CmdLoad: \"%s\" loaded as \"%s\".\n", fname, obj->Name);
4126     else
4127       sprintf(buf, " CmdLoad: loaded as \"%s\".\n", obj->Name);
4128   }
4129 
4130   if(!quiet && buf[0]) {
4131     PRINTFB(G, FB_Executive, FB_Actions)
4132       "%s", buf ENDFB(G);
4133   }
4134 
4135 #ifndef _PYMOL_NO_UNDO
4136 #endif
4137 
4138   return {};
4139 }
4140 
4141 /* ExecutiveGetExistingCompatible
4142  *
4143  * PARAMS
4144  *  oname -- object name
4145  *  type  -- new object type
4146  *
4147  * RETURNS
4148  *  (CObject *) Base-class object ptr
4149  *
4150  * SIDE EFFECTS
4151  *  If an object with the same name but different type already exists,
4152  *  then it is deleted.
4153  */
ExecutiveGetExistingCompatible(PyMOLGlobals * G,const char * oname,cLoadType_t type)4154 CObject* ExecutiveGetExistingCompatible(PyMOLGlobals * G, const char* oname, cLoadType_t type)
4155 {
4156   CObject *origObj = NULL;
4157   origObj = ExecutiveFindObjectByName(G, oname);
4158   /* check for existing object of right type, delete if not */
4159   if(origObj) {
4160     int new_type = -1;
4161     switch (type) {
4162     case cLoadTypePlugin:
4163       // let PlugIOManager delete incompatible objects
4164       return origObj;
4165     case cLoadTypeChemPyModel:
4166     case cLoadTypePDB:
4167     case cLoadTypePDBStr:
4168     case cLoadTypeVDBStr:
4169     case cLoadTypeCIF:
4170     case cLoadTypeCIFStr:
4171     case cLoadTypeMMTF:
4172     case cLoadTypeMMTFStr:
4173     case cLoadTypeXYZ:
4174     case cLoadTypeXYZStr:
4175     case cLoadTypeMOL:
4176     case cLoadTypeMOLStr:
4177     case cLoadTypeMMD:
4178     case cLoadTypeMMDSeparate:
4179     case cLoadTypeMMDStr:
4180     case cLoadTypeTOP:
4181     case cLoadTypeTRJ:
4182     case cLoadTypeCRD:
4183     case cLoadTypeRST:
4184     case cLoadTypeMOL2:
4185     case cLoadTypeMOL2Str:
4186     case cLoadTypeSDF2:
4187     case cLoadTypeSDF2Str:
4188     case cLoadTypePQR:
4189     case cLoadTypePDBQT:
4190     case cLoadTypeXTC:
4191     case cLoadTypeDTR:
4192     case cLoadTypeTRR:
4193     case cLoadTypeGRO:
4194     case cLoadTypeTRJ2:
4195     case cLoadTypeG96:
4196     case cLoadTypeDCD:
4197       new_type = cObjectMolecule;
4198       break;
4199     case cLoadTypeChemPyBrick:
4200     case cLoadTypeChemPyMap:
4201     case cLoadTypeXPLORMap:
4202     case cLoadTypeXPLORStr:
4203     case cLoadTypeCCP4Map:
4204     case cLoadTypeCCP4Str:
4205     case cLoadTypeCCP4Unspecified:
4206     case cLoadTypeMRC:
4207     case cLoadTypeFLDMap:
4208     case cLoadTypeBRIXMap:
4209     case cLoadTypeGRDMap:
4210     case cLoadTypeDXMap:
4211       new_type = cObjectMap;
4212       break;
4213     case cLoadTypeCallback:
4214       new_type = cObjectCallback;
4215       break;
4216     case cLoadTypeCGO:
4217       new_type = cObjectCGO;
4218       break;
4219     }
4220     if(new_type == -1 || new_type != origObj->type) {
4221       ExecutiveDelete(G, origObj->Name);
4222       origObj = NULL;
4223     }
4224   }
4225   return origObj;
4226 }
4227 
ExecutiveProcessPDBFile(PyMOLGlobals * G,CObject * origObj,const char * fname,const char * buffer,const char * oname,int frame,int discrete,int finish,OrthoLineType buf,int variant,int quiet,int multiplex,int zoom)4228 int ExecutiveProcessPDBFile(PyMOLGlobals * G, CObject * origObj,
4229                             const char *fname, const char *buffer,
4230                             const char *oname, int frame, int discrete, int finish,
4231                             OrthoLineType buf, int variant, int quiet,
4232                             int multiplex, int zoom)
4233 {
4234   int ok = true;
4235   CObject *obj;
4236   char pdb_name[WordLength] = "";
4237   char cur_name[WordLength] = "";
4238   const char *next_pdb = NULL;
4239   int repeat_flag = true;
4240   int n_processed = 0;
4241   PDBInfoRec pdb_info_rec, *pdb_info = NULL;
4242   int model_number;
4243   CObject *deferred_zoom_obj = NULL;
4244 
4245   UtilZeroMem(&pdb_info_rec, sizeof(PDBInfoRec));
4246   pdb_info = &pdb_info_rec;
4247   pdb_info->multiplex = multiplex;
4248   pdb_info->variant = variant;
4249 
4250   while(repeat_flag && ok) {
4251     const char *start_at = buffer;
4252     int is_repeat_pass = false;
4253     int eff_frame = frame;
4254     int is_new = false;
4255 
4256     if(next_pdb) {
4257       start_at = next_pdb;
4258       is_repeat_pass = true;
4259     }
4260 
4261     repeat_flag = false;
4262     next_pdb = NULL;
4263     if(!origObj) {
4264 
4265       is_new = true;
4266       pdb_name[0] = 0;
4267       model_number = 0;
4268       obj = (CObject *) ObjectMoleculeReadPDBStr(G, (ObjectMolecule *) origObj,
4269                                                  start_at, eff_frame, discrete,
4270                                                  pdb_name,
4271                                                  &next_pdb, pdb_info, quiet,
4272                                                  &model_number);
4273 
4274     } else {
4275       model_number = 0;
4276       ObjectMoleculeReadPDBStr(G, (ObjectMolecule *) origObj,
4277                                start_at, eff_frame, discrete,
4278                                pdb_name, &next_pdb, pdb_info, quiet, &model_number);
4279 
4280       if(finish) {
4281         ExecutiveUpdateObjectSelection(G, origObj);
4282         ExecutiveDoZoom(G, origObj, false, zoom, quiet);
4283       }
4284       if(eff_frame < 0)
4285         eff_frame = ((ObjectMolecule *) origObj)->NCSet - 1;
4286       if(buf) {
4287         if(fname)
4288           sprintf(buf, " CmdLoad: \"%s\" appended into object \"%s\", state %d.\n",
4289                   fname, oname, eff_frame + 1);
4290         else
4291           sprintf(buf, " CmdLoad: PDB-string appended into object \"%s\", state %d.\n",
4292                   oname, eff_frame + 1);
4293       }
4294       obj = origObj;
4295     }
4296 
4297     if(obj) {
4298       if(next_pdb) {
4299         /* NOTE: if set, assume that multiple PDBs are present in the file */
4300         repeat_flag = true;
4301       }
4302     }
4303 
4304     if(is_new) {
4305       if(obj) {
4306         if(next_pdb) {
4307           if(pdb_name[0] == 0) {
4308             if(cur_name[0]) {
4309               sprintf(pdb_name, "%s_%04d", cur_name, n_processed + 1);
4310             } else {
4311               sprintf(pdb_name, "%s_%04d", oname, n_processed + 1);
4312             }
4313           } else if(multiplex > 0) {
4314             if(pdb_info->multi_object_status == 1) {    /* this is a multi-object PDB file */
4315               strcpy(cur_name, pdb_name);
4316             } else if(cur_name[0] == 0) {
4317               strcpy(cur_name, oname);
4318             }
4319             if(model_number > 0) {
4320               sprintf(pdb_name, "%s_%04d", cur_name, model_number);
4321             } else {
4322               sprintf(pdb_name, "%s_%04d", cur_name, n_processed + 1);
4323             }
4324           }
4325           ObjectSetName(obj, pdb_name);
4326           ExecutiveDelete(G, obj->Name); /* just in case */
4327         } else {
4328           if(is_repeat_pass) {
4329             if(pdb_name[0] == 0) {
4330               if(cur_name[0]) {
4331                 sprintf(pdb_name, "%s_%04d", cur_name, n_processed + 1);
4332               } else {
4333                 sprintf(pdb_name, "%s_%04d", oname, n_processed + 1);
4334               }
4335             } else if(multiplex > 0) {
4336               if(pdb_info->multi_object_status == 1) {  /* this is a multi-object PDB file */
4337                 strcpy(cur_name, pdb_name);
4338               } else if(cur_name[0] == 0) {
4339                 strcpy(cur_name, oname);
4340               }
4341               if(model_number > 0) {
4342                 sprintf(pdb_name, "%s_%04d", cur_name, model_number);
4343               } else {
4344                 sprintf(pdb_name, "%s_%04d", cur_name, n_processed + 1);
4345               }
4346             }
4347             ObjectSetName(obj, pdb_name);       /* from PDB */
4348             ExecutiveDelete(G, obj->Name);       /* just in case */
4349           } else {
4350             ObjectSetName(obj, oname);  /* from filename/parameter */
4351           }
4352         }
4353 
4354         if(obj) {
4355           int do_zoom = repeat_flag ? 0 : zoom;
4356           if(do_zoom != zoom)
4357             deferred_zoom_obj = obj;
4358           else
4359             deferred_zoom_obj = NULL;
4360           ExecutiveManageObject(G, obj, do_zoom, true);
4361           if(eff_frame < 0)
4362             eff_frame = ((ObjectMolecule *) obj)->NCSet - 1;
4363           if(buf) {
4364             if(n_processed < 1) {
4365               if(fname)
4366                 sprintf(buf, " CmdLoad: \"%s\" loaded as \"%s\".\n", fname, oname);
4367               else
4368                 sprintf(buf,
4369                         " CmdLoad: PDB-string loaded into object \"%s\", state %d.\n",
4370                         oname, eff_frame + 1);
4371             } else {
4372               if(fname) {
4373                 sprintf(buf, " CmdLoad: loaded %d objects from \"%s\".\n",
4374                         n_processed + 1, fname);
4375               } else {
4376                 sprintf(buf, " CmdLoad: loaded %d objects from string.\n",
4377                         n_processed + 1);
4378               }
4379             }
4380           }
4381 
4382         }
4383       }
4384     }
4385 
4386     if(obj) {
4387       n_processed++;
4388     }
4389   }
4390 
4391   if(deferred_zoom_obj) {
4392     ExecutiveDoZoom(G, deferred_zoom_obj, true, zoom, true);
4393   }
4394 
4395   return ok;
4396 }
4397 
ExecutiveAssignSS(PyMOLGlobals * G,const char * target,int state,const char * context,int preserve,ObjectMolecule * single_object,int quiet)4398 pymol::Result<> ExecutiveAssignSS(PyMOLGlobals* G,
4399     const char* target, int state, const char* context, int preserve,
4400     ObjectMolecule* single_object, int quiet)
4401 {
4402   if (!target[0]) {
4403     return pymol::make_error("selection must not be empty");
4404   }
4405   pymol::Result<SelectorTmp> targetTmp;
4406   pymol::Result<SelectorTmp> contextTmp;
4407   int sele0 = -1;
4408   int sele1 = -1;
4409   sele0 = SelectorIndexByName(G, target);
4410   if (sele0 < 0) {
4411     targetTmp = SelectorTmp::make(G, target);
4412     p_return_if_error(targetTmp);
4413     sele0 = targetTmp->getIndex();
4414     assert(sele0 >= 0);
4415   }
4416   if (!context || !context[0]) {
4417     sele1 = sele0;
4418   } else {
4419     contextTmp = SelectorTmp::make(G, context);
4420     p_return_if_error(contextTmp);
4421     sele1 = contextTmp->getIndex();
4422     assert(sele1 >= 0);
4423   }
4424   SelectorAssignSS(G, sele0, sele1, state, preserve, single_object, quiet);
4425   return {};
4426 }
4427 
4428 static int * getRepArrayFromBitmask(int visRep);
4429 
ExecutiveGetVisAsPyDict(PyMOLGlobals * G)4430 PyObject *ExecutiveGetVisAsPyDict(PyMOLGlobals * G)
4431 {
4432   PyObject *result = NULL, *list;
4433   CExecutive *I = G->Executive;
4434   SpecRec *rec = NULL;
4435   result = PyDict_New();
4436   while(ListIterate(I->Spec, rec, next)) {
4437     if(rec->name[0] != '_') {
4438       list = PyList_New(4);
4439       PyList_SetItem(list, 0, PyInt_FromLong(rec->visible));
4440 
4441       PyList_SetItem(list, 1, PyList_New(0));
4442 
4443       if(rec->type != cExecObject) {
4444         PyList_SetItem(list, 2, PConvAutoNone(Py_None));
4445         PyList_SetItem(list, 3, PConvAutoNone(Py_None));
4446       } else {
4447         auto vla = getRepArrayFromBitmask(rec->obj->visRep);
4448         PyList_SetItem(list, 2, PConvIntVLAToPyList(vla));
4449         VLAFreeP(vla);
4450 
4451         PyList_SetItem(list, 3, PyInt_FromLong(rec->obj->Color));
4452       }
4453 
4454       PyDict_SetItemString(result, rec->name, list);
4455       Py_DECREF(list);
4456     }
4457   }
4458   return (result);
4459 }
4460 
getRepArrayFromBitmask(int visRep)4461 static int * getRepArrayFromBitmask(int visRep) {
4462   int n_vis = 0;
4463   int *RepVis = VLACalloc(int, cRepCnt);
4464 
4465   for(int a = 0; a < cRepCnt; a++)
4466     if(GET_BIT(visRep, a))
4467       RepVis[n_vis++] = a;
4468 
4469   VLASize(RepVis, int, n_vis);
4470   return RepVis;
4471 }
4472 
4473 #ifdef _PYMOL_LIB
4474 /*
4475  * Returns a list (VLA) of enabled atom representations (AtomInfoType.visRep)
4476  * in selection (e.g. {cRepLine, cRepNonbonded})
4477  */
ExecutiveGetRepsInSceneForObject(PyMOLGlobals * G,const char * name)4478 int *ExecutiveGetRepsInSceneForObject(PyMOLGlobals *G, const char *name){
4479   int visRep = 0;
4480 
4481   for (SeleAtomIterator iter(G, name); iter.next();) {
4482     AtomInfoType * ai = iter.getAtomInfo();
4483     visRep |= ai->visRep;
4484   }
4485 
4486   return getRepArrayFromBitmask(visRep);
4487 }
4488 
4489 /*
4490  * Returns a list (VLA) of enabled object representations (rec->obj->visRep)
4491  * (e.g. {cRepCell, cRepExtent})
4492  */
ExecutiveGetRepsForObject(PyMOLGlobals * G,const char * name)4493 int *ExecutiveGetRepsForObject(PyMOLGlobals *G, const char *name){
4494   SpecRec *rec = ExecutiveFindSpec(G, (char*)name);
4495 
4496   if(rec)
4497     return getRepArrayFromBitmask(rec->obj->visRep);
4498 
4499   return NULL;
4500 }
4501 #endif
4502 
ExecutiveSetVisFromPyDict(PyMOLGlobals * G,PyObject * dict)4503 int ExecutiveSetVisFromPyDict(PyMOLGlobals * G, PyObject * dict)
4504 {
4505 #ifdef _PYMOL_NOPY
4506   return 0;
4507 #else
4508 
4509   int ok = true;
4510   WordType name;
4511   PyObject *key, *list, *col;
4512   PyObject *vis_list = NULL;
4513   Py_ssize_t pos = 0;
4514   SpecRec *rec, *grec, **recstack = NULL;
4515   int n_vis;
4516   int rep;
4517   int a;
4518   int ll = 0;
4519   if(ok)
4520     ok = (dict != NULL);
4521   if(ok)
4522     ok = PyDict_Check(dict);
4523   if(ok) {
4524 
4525     SceneObjectDel(G, NULL, true);    /* remove all objects from scene */
4526     ExecutiveInvalidateSceneMembers(G);
4527 
4528     // stack for putative visible records
4529     recstack = pymol::calloc<SpecRec*>(PyDict_Size(dict) + 1);
4530 
4531     while(PyDict_Next(dict, &pos, &key, &list)) {
4532       if(!PConvPyStrToStr(key, name, sizeof(WordType))) {
4533         ok = false;
4534       } else {
4535         rec = ExecutiveFindSpec(G, name);
4536         if(rec) {
4537           if(ok)
4538             ok = (list != NULL);
4539           if(ok)
4540             ok = PyList_Check(list);
4541           if(ok)
4542             ll = PyList_Size(list);
4543           if(ok)
4544             ok = (ll >= 2);
4545           if(ok)
4546             ok = PConvPyObjectToInt(PyList_GetItem(list, 0), &rec->visible);
4547 
4548           /* before version 1.8 item 1 was rec reps (repOn) */
4549 
4550           if(ok && (rec->type == cExecObject)) {        /* object properties */
4551 
4552             if(ll > 2) {        /* object visibility */
4553               vis_list = PyList_GetItem(list, 2);
4554               if(ok)
4555                 ok = (vis_list != NULL);
4556               if(ok) {
4557                 if(PyList_Check(vis_list)) {
4558                   n_vis = PyList_Size(vis_list);
4559                   rec->obj->visRep = 0;
4560                   for(a = 0; a < n_vis; a++) {
4561                     if(PConvPyObjectToInt(PyList_GetItem(vis_list, a), &rep)) {
4562                       if((rep >= 0) && (rep < cRepCnt))
4563                         SET_BIT(rec->obj->visRep, rep);
4564                     }
4565                   }
4566                 } else if (PyInt_Check(vis_list)) {
4567                   PConvPyObjectToInt(vis_list, &rec->obj->visRep);
4568                 }
4569               }
4570             }
4571             if(ll > 3) {        /* object color */
4572               col = PyList_GetItem(list, 3);
4573               if(ok)
4574                 ok = (col != NULL);
4575               if(ok)
4576                 if(PyInt_Check(col)) {
4577                   ok = PConvPyObjectToInt(col, &rec->obj->Color);
4578                   rec->obj->invalidate(cRepAll, cRepInvColor, -1);
4579                 }
4580             }
4581           }
4582           if(rec->visible && (rec->type == cExecObject)) {
4583             (*(++recstack)) = rec;
4584           }
4585         }
4586       }
4587     }
4588 
4589     // add visible objects to scene
4590     for(; (rec = *recstack); recstack--) {
4591       // check visibility of all parent groups
4592       for(grec = rec; grec->visible && (grec = grec->group););
4593       if(!grec) {
4594         // ok, no invisible parent found
4595         rec->in_scene = SceneObjectAdd(G, rec->obj);
4596         ExecutiveInvalidateSceneMembers(G);
4597       }
4598     }
4599     mfree(recstack);
4600   }
4601   return ok;
4602 #endif
4603 }
4604 
4605 /*
4606  * returns a pointer to the data in a volume or map object
4607  */
ExecutiveGetVolumeField(PyMOLGlobals * G,const char * objName,int state)4608 CField * ExecutiveGetVolumeField(PyMOLGlobals * G, const char * objName, int state) {
4609   ObjectMapState *oms;
4610   CObject *obj;
4611 
4612   obj = ExecutiveFindObjectByName(G, objName);
4613   ok_assert(1, obj);
4614 
4615   switch (obj->type) {
4616   case cObjectVolume:
4617     return ObjectVolumeGetField((ObjectVolume *) obj);
4618   case cObjectMap:
4619     oms = ObjectMapGetState((ObjectMap *) obj, state);
4620     ok_assert(1, oms && oms->Field);
4621     return oms->Field->data.get();
4622   }
4623 
4624 ok_except1:
4625   return NULL;
4626 }
4627 
4628 /*
4629  * returns allocated memory
4630  */
4631 pymol::Result<std::vector<float>>
ExecutiveGetHistogram(PyMOLGlobals * G,const char * objName,int n_points,float min_val,float max_val)4632 ExecutiveGetHistogram(PyMOLGlobals * G, const char * objName, int n_points, float min_val, float max_val) {
4633   CObject *obj;
4634   ObjectMapState *oms = NULL;
4635 
4636   obj = ExecutiveFindObjectByName(G, objName);
4637 
4638   if (!obj) {
4639     return pymol::make_error("could not find object ", objName);
4640   }
4641 
4642   switch (obj->type) {
4643   case cObjectMap:
4644     oms = ObjectMapGetState((ObjectMap *) obj, 0);
4645     break;
4646   case cObjectVolume:
4647     oms = ObjectVolumeGetMapState((ObjectVolume *) obj);
4648     break;
4649   default:
4650     return pymol::make_error("object type must be map or volume");
4651   }
4652 
4653   if(oms) {
4654     auto hist = std::vector<float>(n_points + 4);
4655     float range = SettingGet_f(G, obj->Setting, NULL, cSetting_volume_data_range);
4656     ObjectMapStateGetHistogram(
4657         G, oms, n_points, range, hist.data(), min_val, max_val);
4658     return hist;
4659   }
4660 
4661   return pymol::make_error("failed to get map state");
4662 }
4663 
ExecutiveGetVolumeRamp(PyMOLGlobals * G,const char * objName)4664 PyObject* ExecutiveGetVolumeRamp(PyMOLGlobals * G, const char * objName) {
4665 #ifdef _PYMOL_NOPY
4666   return NULL;
4667 #else
4668   CObject *obj;
4669   PyObject* result = NULL;
4670 
4671   PRINTFD(G, FB_Executive) "Executive-GetVolumeRamp Entered.\n" ENDFD;
4672 
4673   obj = ExecutiveFindObjectByName(G, objName);
4674   if(obj && obj->type==cObjectVolume) {
4675     result = ObjectVolumeGetRamp((ObjectVolume *) obj);
4676   }
4677 
4678   PRINTFD(G, FB_Executive) "Executive-GetVolumeRamp Exited.\n" ENDFD;
4679 
4680   return result;
4681 
4682 #endif
4683 }
4684 
ExecutiveSetVolumeRamp(PyMOLGlobals * G,const char * objName,std::vector<float> ramp_list)4685 pymol::Result<> ExecutiveSetVolumeRamp(PyMOLGlobals * G, const char * objName, std::vector<float> ramp_list) {
4686 
4687   auto obj = ExecutiveFindObject<ObjectVolume>(G, objName);
4688   if(obj) {
4689     return ObjectVolumeSetRamp(obj, std::move(ramp_list));
4690   }
4691 
4692   return pymol::make_error("Object ", objName, " not found");
4693 }
4694 
ExecutiveIsolevel(PyMOLGlobals * G,const char * name,float level,int state,int quiet)4695 pymol::Result<> ExecutiveIsolevel(
4696     PyMOLGlobals* G, const char* name, float level, int state, int quiet)
4697 {
4698   auto obj = ExecutiveFindObjectByName(G, name);
4699   if(obj) {
4700     switch (obj->type) {
4701     case cObjectMesh:
4702       ObjectMeshSetLevel((ObjectMesh *) obj, level, state, quiet);
4703       SceneChanged(G);
4704       return {};
4705     case cObjectSurface:
4706       ObjectSurfaceSetLevel((ObjectSurface *) obj, level, state, quiet);
4707       SceneChanged(G);
4708       return {};
4709     default:
4710       return pymol::make_error("Object ", name, " is of wrong type.");
4711     }
4712   }
4713   return pymol::make_error("Object not found");
4714 }
4715 
ExecutiveGetIsolevel(PyMOLGlobals * G,const char * name,int state)4716 pymol::Result<float> ExecutiveGetIsolevel(
4717     PyMOLGlobals* G, const char* name, int state)
4718 {
4719   auto obj = ExecutiveFindObjectByName(G, name);
4720   if (obj) {
4721     switch (obj->type) {
4722     case cObjectMesh:
4723       return ObjectMeshGetLevel((ObjectMesh*) obj, state);
4724     case cObjectSurface:
4725       return ObjectSurfaceGetLevel((ObjectSurface*) obj, state);
4726     default:
4727       return pymol::make_error("Object ", name, " is of wrong type.");
4728     }
4729   }
4730   return pymol::make_error("Object not found");
4731 }
4732 
ExecutiveSpectrum(PyMOLGlobals * G,const char * s1,const char * expr,float min,float max,int first,int last,const char * prefix,int digits,int byres,int quiet)4733 pymol::Result<std::pair<float, float>> ExecutiveSpectrum(PyMOLGlobals* G,
4734     const char* s1, const char* expr, float min, float max, int first, int last,
4735     const char* prefix, int digits, int byres, int quiet)
4736 {
4737   std::pair<float, float> ret;
4738   int n_color, n_atom;
4739   ObjectMoleculeOpRec op;
4740   WordType buffer;
4741   std::vector<int> color_index;
4742   std::vector<float> value;
4743   int a, b;
4744   char pat[] = "%0Xd";
4745   int pref_len;
4746   char *at;
4747   float range;
4748 
4749   auto tmpsele1 = SelectorTmp::make(G, s1);
4750   p_return_if_error(tmpsele1);
4751   int sele1 = tmpsele1->getIndex();
4752 
4753   if(sele1 >= 0) {
4754 
4755     if(digits > 9)
4756       digits = 9;
4757     pat[2] = ('0' + digits);
4758     UtilNCopy(buffer, prefix, sizeof(WordType) - digits);
4759 
4760     pref_len = strlen(prefix);
4761     at = buffer + pref_len;
4762 
4763     n_color = abs(first - last) + 1;
4764     if(n_color) {
4765       color_index.resize(n_color);
4766       for(a = 0; a < n_color; a++) {
4767         b = first + ((last - first) * a) / (n_color - 1);
4768         sprintf(at, pat, b);
4769         color_index[a] = ColorGetIndex(G, buffer);
4770       }
4771 
4772       // set up iterator
4773       SeleAtomIterator iter(G, sele1);
4774       SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
4775 
4776       // count atoms
4777       for (n_atom = 0; iter.next();) {
4778         ++n_atom;
4779       }
4780 
4781       if(n_atom) {
4782         value.resize(n_atom);
4783 
4784         if(WordMatchExact(G, "count", expr, true)) {
4785           for(a = 0; a < n_atom; a++) {
4786             value[a] = (float) a + 1;
4787           }
4788         } else {
4789           if (WordMatchExact(G, "pc", expr, true)) {
4790             expr = "partial_charge";
4791           } else if (WordMatchExact(G, "resi", expr, true)) {
4792             expr = "resv";
4793           }
4794 
4795           // look up expression definition
4796           auto ap = PyMOL_GetAtomPropertyInfo(G->PyMOL, expr);
4797           if (!ap) {
4798             return pymol::make_error("Unknown expression: ", expr);
4799           }
4800 
4801           // for enumerated values
4802           std::map<size_t, unsigned int> enumerated_values;
4803           union {
4804             size_t value_e;
4805             char   value_s[sizeof(size_t)];
4806           };
4807 
4808           for (a = 0, iter.reset(); iter.next(); ++a) {
4809             const auto ai = iter.getAtomInfo();
4810             const auto raw_ptr = reinterpret_cast<const char*>(ai) + ap->offset;
4811 
4812             // numeric values
4813             switch (ap->Ptype) {
4814               case cPType_float:
4815                 value[a] = *reinterpret_cast<const float*>(raw_ptr);
4816                 continue;
4817               case cPType_int:
4818               case cPType_int_custom_type:
4819                 value[a] = *reinterpret_cast<const int*>(raw_ptr);
4820                 continue;
4821               case cPType_schar:
4822                 value[a] = *reinterpret_cast<const signed char*>(raw_ptr);
4823                 continue;
4824               case cPType_char_as_type:
4825                 value[a] = ai->hetatm;
4826                 continue;
4827               case cPType_index:
4828                 value[a] = iter.getAtm() + 1.f;
4829                 continue;
4830             }
4831 
4832             // enumerated values
4833             switch (ap->Ptype) {
4834               case cPType_int_as_string:
4835                 value_e = LexNumeric(*reinterpret_cast<const lexidx_t*>(raw_ptr));
4836                 break;
4837               case cPType_string:
4838                 // works for small strings
4839                 strncpy(value_s, raw_ptr, sizeof(value_e));
4840                 break;
4841               case cPType_model:
4842                 value_e = (size_t) iter.obj;
4843                 break;
4844               default:
4845                 return pymol::make_error("Unsupported Ptype for expr: ", expr);
4846             }
4847 
4848             // lookup or insert value
4849             auto& e = enumerated_values[value_e];
4850             if (e == 0)
4851               e = enumerated_values.size();
4852             value[a] = e - 1.f;
4853           }
4854 
4855           if (!quiet && !enumerated_values.empty()) {
4856             PRINTFB(G, FB_Executive, FB_Actions)
4857               " Spectrum: Expression is non-numeric, enumerating values\n" ENDFB(G);
4858           }
4859         }
4860 
4861         if(max < min) {
4862           max = value[0];
4863           min = value[0];
4864           for(a = 1; a < n_atom; a++) {
4865             if(value[a] < min)
4866               min = value[a];
4867             if(value[a] > max)
4868               max = value[a];
4869           }
4870         }
4871         range = max - min;
4872 
4873         if(!quiet) {
4874           PRINTFB(G, FB_Executive, FB_Actions)
4875             " Spectrum: range (%8.5f to %8.5f).\n", min, max ENDFB(G);
4876         }
4877         if(range == 0.0F)
4878           range = 1.0F;
4879         ret.first = min;
4880         ret.second = max;
4881 
4882         op.code = OMOP_Spectrum;
4883         op.i1 = n_color - 1;
4884         op.i2 = n_atom;
4885         op.i3 = 0;
4886         op.i4 = byres;
4887         op.ii1 = color_index.data();
4888         op.ff1 = value.data();
4889         op.f1 = min;
4890         op.f2 = range;
4891 
4892         ExecutiveObjMolSeleOp(G, sele1, &op);
4893 
4894         op.code = OMOP_INVA;
4895         op.i1 = cRepBitmask;
4896         op.i2 = cRepInvColor;
4897         ExecutiveObjMolSeleOp(G, sele1, &op);
4898 
4899       }
4900     }
4901   }
4902   return ret;
4903 }
4904 
fStrOrderFn(const char * const * array,int l,int r)4905 static int fStrOrderFn(const char * const* array, int l, int r) {
4906   return strcmp(array[l], array[r]) < 0;
4907 }
4908 
4909 /*
4910  * Returns an VLA with pointers into G->Lexicon
4911  */
ExecutiveGetChains(PyMOLGlobals * G,const char * s1,int state)4912 pymol::Result<std::vector<const char*>> ExecutiveGetChains(
4913     PyMOLGlobals* G, const char* s1, int state)
4914 {
4915   std::set<lexidx_t> chains;
4916   int c = 0;
4917   ObjectMoleculeOpRec op;
4918 
4919   SETUP_SELE_DEFAULT(1);
4920 
4921   {
4922 
4923     ObjectMoleculeOpRecInit(&op);
4924     op.code = OMOP_GetChains;
4925     op.ii1 = (int*) (void*) &chains; // pointer pack
4926     op.i1 = 0;
4927     ExecutiveObjMolSeleOp(G, sele1, &op);
4928     std::vector<const char*> result(chains.size());
4929     for (const auto& chain : chains) {
4930       result[c++] = LexStr(G, chain);
4931     }
4932     // sort the array
4933     UtilSortInPlace(G, result.data(), chains.size(), sizeof(char *),
4934         (UtilOrderFn *) fStrOrderFn);
4935     return result;
4936   }
4937 }
4938 
ExecutiveValidateObjectPtr(PyMOLGlobals * G,CObject * ptr,int object_type)4939 int ExecutiveValidateObjectPtr(PyMOLGlobals * G, CObject * ptr, int object_type)
4940 {
4941   /* this routine needs to be sped up significantly... */
4942 
4943   CExecutive *I = G->Executive;
4944   int ok = false;
4945   SpecRec *rec = NULL;
4946 
4947   while(ListIterate(I->Spec, rec, next)) {
4948     if(rec->obj == ptr) {
4949       if(rec->type == cExecObject) {
4950         if((!object_type) || (rec->obj->type == object_type)) {
4951           ok = true;
4952           break;
4953         }
4954       }
4955     }
4956   }
4957   return (ok);
4958 }
4959 
ExecutiveRampNew(PyMOLGlobals * G,const char * name,const char * src_name,pymol::vla<float> range,pymol::vla<float> color,int src_state,const char * sele,float beyond,float within,float sigma,int zero,int calc_mode,int quiet)4960 pymol::Result<> ExecutiveRampNew(PyMOLGlobals* G, const char* name,
4961     const char* src_name, pymol::vla<float> range, pymol::vla<float> color,
4962     int src_state, const char* sele, float beyond, float within, float sigma,
4963     int zero, int calc_mode, int quiet)
4964 {
4965   ObjectGadgetRamp *obj = NULL;
4966   ObjectGadgetRamp *origRamp = NULL;
4967   CObject *src_obj = NULL;
4968   CObject *origObj = ExecutiveFindObjectByName(G, name);
4969   float *vert_vla = NULL;
4970   int rampType = -1;
4971 
4972   if (origObj &&
4973       origObj->type == cObjectGadget &&
4974       ((ObjectGadget*)origObj)->GadgetType == cGadgetRamp) {
4975     origRamp = (ObjectGadgetRamp*)origObj;
4976     rampType = origRamp->RampType;
4977   } else if (!range || !(color || calc_mode)) {
4978      return pymol::make_error("Missing 'range' or 'color' to create new ramp.");
4979   }
4980 
4981   if (src_name && src_name[0]) {
4982     if (WordMatchExact(G, src_name, cKeywordNone, true)) {
4983       rampType = cRampNone;
4984     } else {
4985       src_obj = ExecutiveFindObjectByName(G, src_name);
4986       if(src_obj) {
4987         switch (src_obj->type) {
4988           case cObjectMap:
4989             rampType = cRampMap;
4990             break;
4991           case cObjectMolecule:
4992             rampType = cRampMol;
4993             break;
4994           default:
4995             pymol::make_error(src_name, " is not a map or molecule.");
4996         }
4997       } else {
4998         return pymol::make_error(src_name, " not found.");
4999       }
5000     }
5001   }
5002 
5003   switch (rampType) {
5004     case cRampMap:
5005       /* mapping this ramp from a selection */
5006       if(sele && sele[0]) {
5007         auto tmpsele = SelectorTmp::make(G, sele);
5008         p_return_if_error(tmpsele);
5009         sele = tmpsele->getName();
5010         assert(sele[0]);
5011 
5012         vert_vla = ExecutiveGetVertexVLA(G, sele, src_state);
5013       }
5014       obj = ObjectGadgetRampMapNewAsDefined(G, origRamp, (ObjectMap *) src_obj,
5015             std::move(range), std::move(color), src_state,
5016             vert_vla, beyond, within,
5017             sigma, zero, calc_mode);
5018       VLAFreeP(vert_vla);
5019       break;
5020     case cRampNone:
5021     case cRampMol:
5022       obj = ObjectGadgetRampMolNewAsDefined(G, origRamp, (ObjectMolecule *) src_obj,
5023             std::move(range), std::move(color), src_state,
5024             calc_mode);
5025       break;
5026     default:
5027       return pymol::make_error("Missing 'name' to create new ramp.");
5028   }
5029 
5030   if (!obj)
5031     return pymol::make_error("Object not found");
5032 
5033   if (obj != origRamp) {
5034     ExecutiveDelete(G, name);
5035     ObjectSetName((CObject *) obj, name);
5036     ColorRegisterExt(G, ((CObject *) obj)->Name, (void *) obj, cColorGadgetRamp);
5037     ExecutiveManageObject(G, (CObject *) obj, false, quiet);
5038   }
5039 
5040   ExecutiveInvalidateRep(G, cKeywordAll, cRepAll, cRepInvColor);      /* recolor everything */
5041   return {};
5042 }
5043 
ExecutiveSetNamedEntries(PyMOLGlobals * G,PyObject * names,int version,int part_rest,int part_sess)5044 static int ExecutiveSetNamedEntries(PyMOLGlobals * G, PyObject * names, int version,
5045                                     int part_rest, int part_sess)
5046 {
5047   CExecutive *I = G->Executive;
5048   int ok = true;
5049   int skip = false;
5050   int a = 0, l = 0, ll = 0;
5051   PyObject *cur, *el;
5052   SpecRec *rec = NULL;
5053   int extra_int;
5054   int incomplete = false;
5055   ObjectNameType new_name;
5056 
5057   if(ok)
5058     ok = (names != NULL);
5059   if(ok)
5060     ok = PyList_Check(names);
5061   if(ok)
5062     l = PyList_Size(names);
5063 
5064   while(ok && (a < l)) {
5065     cur = PyList_GetItem(names, a);
5066     if(cur != Py_None) {        /* skip over None w/o aborting */
5067       skip = false;
5068       rec = NULL;
5069       ListElemCalloc(G, rec, SpecRec);
5070       rec->next = NULL;
5071       rec->name[0] = 0;
5072       if(ok)
5073         ok = PyList_Check(cur);
5074       if(ok)
5075         ll = PyList_Size(cur);
5076       if(ok)
5077         ok = PConvPyStrToStr(PyList_GetItem(cur, 0), rec->name, sizeof(WordType));
5078       if(ok)
5079         ok = PConvPyIntToInt(PyList_GetItem(cur, 1), &rec->type);
5080       if(ok)
5081         ok = CPythonVal_PConvPyIntToInt_From_List(G, cur, 2, &rec->visible);
5082 
5083       /* before version 1.8 item 3 was rec reps (repOn) */
5084 
5085       if(ok)
5086         ok = PConvPyIntToInt(PyList_GetItem(cur, 4), &extra_int);
5087       switch (rec->type) {
5088       case cExecObject:
5089         if(!ok)
5090           break;
5091 
5092         el = PyList_GetItem(cur, 5);
5093 
5094         switch (extra_int) {
5095         case cObjectMolecule:
5096           ok = ObjectMoleculeNewFromPyList(G, el, (ObjectMolecule **) (void *) &rec->obj);
5097           break;
5098         case cObjectMeasurement:
5099           ok = ObjectDistNewFromPyList(G, el, (ObjectDist **) (void *) &rec->obj);
5100           break;
5101         case cObjectMap:
5102           ok = ObjectMapNewFromPyList(G, el, (ObjectMap **) (void *) &rec->obj);
5103           break;
5104         case cObjectMesh:
5105           ok = ObjectMeshNewFromPyList(G, el, (ObjectMesh **) (void *) &rec->obj);
5106           break;
5107         case cObjectSlice:
5108           ok = ObjectSliceNewFromPyList(G, el, (ObjectSlice **) (void *) &rec->obj);
5109           break;
5110         case cObjectSurface:
5111           ok = ObjectSurfaceNewFromPyList(G, el, (ObjectSurface **) (void *) &rec->obj);
5112           break;
5113         case cObjectCGO:
5114           ok = ObjectCGONewFromPyList(G, el, (ObjectCGO **) (void *) &rec->obj, version);
5115           break;
5116         case cObjectGadget:
5117           ok = ObjectGadgetNewFromPyList(G, el, (ObjectGadget **) (void *) &rec->obj, version);
5118           break;
5119         case cObjectAlignment:
5120           ok = ObjectAlignmentNewFromPyList(G, el, (ObjectAlignment **) (void *) &rec->obj,
5121                                               version);
5122           break;
5123         case cObjectGroup:
5124           if(part_rest) {
5125             // if group already exists, do not create new one
5126             CObject *obj = ExecutiveFindObjectByName(G, rec->name);
5127             if(obj && obj->type == cObjectGroup) {
5128               skip = 1;
5129               break;
5130             }
5131           }
5132           ok = ObjectGroupNewFromPyList(G, el, (ObjectGroup **) (void *) &rec->obj, version);
5133           break;
5134         case cObjectVolume:
5135           ok = ObjectVolumeNewFromPyList(G, el, (ObjectVolume **) (void *) &rec->obj);
5136           break;
5137 #ifndef _PYMOL_NOPY
5138         case cObjectCallback:
5139           // skip dummy entries from old sessions and failed-to-pickle sessions
5140           skip = !ObjectCallbackNewFromPyList(G, el, (ObjectCallback **) (void *) &rec->obj);
5141           break;
5142 #endif
5143         default:
5144           PRINTFB(G, FB_Executive, FB_Errors)
5145             " Executive: skipping unrecognized object \"%s\" of type %d.\n",
5146             rec->name, extra_int ENDFB(G);
5147           skip = true;
5148           break;
5149         }
5150 
5151         CPythonVal_Free(el);
5152 
5153         break;
5154       case cExecSelection:     // on the first pass, just create an entry in the rec list
5155         rec->sele_color = extra_int;
5156         if(part_rest || part_sess) {    // don't attempt to restore selections with partial sessions
5157           skip = true;
5158         }
5159         break;
5160       }
5161 
5162       if(ll > 6) {
5163         if(ok){
5164           ok = PConvPyStrToStr(PyList_GetItem(cur, 6), rec->group_name, sizeof(WordType));
5165 	}
5166       }
5167 
5168       if(PyErr_Occurred()) {
5169         PRINTFB(G, FB_Executive, FB_Errors)
5170           "ExectiveSetNamedEntries-Error: after object \"%s\".\n", rec->name ENDFB(G);
5171         PyErr_Print();
5172       }
5173 
5174       if(ok && !skip) {
5175         if(part_rest && ExecutiveProcessObjectName(G, rec->name, new_name)) {
5176           // rename duplicates
5177           strcpy(rec->obj->Name, new_name);
5178           strcpy(rec->name, new_name);
5179         }
5180 
5181         // replace existing object (unless auto_rename_duplicate_objects=1)
5182         if(ExecutiveValidName(G, rec->name)) {
5183           ExecutiveDelete(G, rec->name);
5184         }
5185 
5186         switch (rec->type) {
5187         case cExecObject:
5188           if(rec->visible) {
5189             rec->in_scene = SceneObjectAdd(G, rec->obj);
5190             ExecutiveInvalidateSceneMembers(G);
5191           }
5192           ExecutiveUpdateObjectSelection(G, rec->obj);
5193           break;
5194         }
5195 
5196         rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef *) rec);
5197         TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
5198 
5199         switch (rec->type) {
5200         case cExecObject:
5201           TrackerLink(I->Tracker, rec->cand_id, I->all_obj_list_id, 1);
5202           break;
5203         case cExecSelection:
5204           TrackerLink(I->Tracker, rec->cand_id, I->all_sel_list_id, 1);
5205           break;
5206         }
5207         ListAppend(I->Spec, rec, next, SpecRec);
5208         ExecutiveAddKey(I, rec);
5209         ExecutiveInvalidateGroups(G, false);
5210         ExecutiveInvalidatePanelList(G);
5211       } else {
5212         ListElemFree(rec);
5213       }
5214     }
5215     a++;
5216     if(!ok) {
5217       incomplete = true;
5218       ok = true;
5219     }
5220   }
5221   return (!incomplete);
5222 }
5223 
ExecutiveSetSelectionsFromPyList(PyMOLGlobals * G,PyObject * names)5224 static int ExecutiveSetSelectionsFromPyList(PyMOLGlobals * G, PyObject * names)
5225 {
5226   /* must already have objects loaded at this point... */
5227 
5228   int ok = true;
5229   int a = 0, l = 0;
5230   PyObject *cur;
5231   SpecRec *rec = NULL;
5232   int extra;
5233   int incomplete = false;
5234 
5235   if(ok)
5236     ok = (names != NULL);
5237   if(ok)
5238     ok = PyList_Check(names);
5239   if(ok)
5240     l = PyList_Size(names);
5241   while(ok && (a < l)) {
5242     cur = PyList_GetItem(names, a);
5243     if(cur != Py_None) {        /* skip over None w/o aborting */
5244       rec = NULL;
5245       ListElemCalloc(G, rec, SpecRec);
5246       rec->next = NULL;
5247 
5248       if(ok)
5249         ok = PyList_Check(cur);
5250       if(ok)
5251         ok = PConvPyStrToStr(PyList_GetItem(cur, 0), rec->name, sizeof(WordType));
5252       if(ok)
5253         ok = PConvPyIntToInt(PyList_GetItem(cur, 1), &rec->type);
5254       if(ok)
5255         ok = PConvPyIntToInt(PyList_GetItem(cur, 2), &rec->visible);
5256       if(ok)
5257         ok = PConvPyIntToInt(PyList_GetItem(cur, 4), &extra);
5258       switch (rec->type) {
5259       case cExecSelection:
5260         ok = SelectorFromPyList(G, rec->name, PyList_GetItem(cur, 5));
5261         break;
5262       }
5263       ListElemFree(rec);
5264     }
5265     a++;
5266     if(!ok) {
5267       incomplete = true;
5268       ok = true;
5269     }
5270   }
5271   return (!incomplete);
5272 }
5273 
ExecutiveGetExecObjectAsPyList(PyMOLGlobals * G,SpecRec * rec)5274 static PyObject *ExecutiveGetExecObjectAsPyList(PyMOLGlobals * G, SpecRec * rec)
5275 {
5276 
5277   PyObject *result = NULL;
5278   int recobjtype = rec->obj->type;
5279   switch (recobjtype){
5280   case cObjectMesh:
5281     { /* If a mesh no longer has its dependent map, then it gets saved as a CGO */
5282       int allMapsExist = ObjectMeshAllMapsInStatesExist((ObjectMesh *) rec->obj);
5283       if (!allMapsExist){
5284 	recobjtype = cObjectCGO;
5285       }
5286     }
5287   }
5288   result = PyList_New(7);
5289   PyList_SetItem(result, 0, PyString_FromString(rec->obj->Name));
5290   PyList_SetItem(result, 1, PyInt_FromLong(cExecObject));
5291   PyList_SetItem(result, 2, PyInt_FromLong(rec->visible));
5292   /* before version 1.8 item 3 was rec reps (repOn) */
5293   PyList_SetItem(result, 3, PConvAutoNone(NULL));
5294   PyList_SetItem(result, 4, PyInt_FromLong(recobjtype));
5295   switch (rec->obj->type) {
5296   case cObjectGadget:
5297     PyList_SetItem(result, 5, ObjectGadgetAsPyList((ObjectGadget *) rec->obj));
5298     break;
5299   case cObjectMolecule:
5300     PyList_SetItem(result, 5, ObjectMoleculeAsPyList((ObjectMolecule *) rec->obj));
5301     break;
5302   case cObjectMeasurement:
5303     PyList_SetItem(result, 5, ObjectDistAsPyList((ObjectDist *) rec->obj));
5304     break;
5305   case cObjectMap:
5306     PyList_SetItem(result, 5, ObjectMapAsPyList((ObjectMap *) rec->obj));
5307     break;
5308   case cObjectMesh:
5309     PyList_SetItem(result, 5, ObjectMeshAsPyList((ObjectMesh *) rec->obj));
5310     break;
5311   case cObjectSlice:
5312     PyList_SetItem(result, 5, ObjectSliceAsPyList((ObjectSlice *) rec->obj));
5313     break;
5314   case cObjectSurface:
5315     PyList_SetItem(result, 5, ObjectSurfaceAsPyList((ObjectSurface *) rec->obj));
5316     break;
5317   case cObjectCGO:
5318     PyList_SetItem(result, 5, ObjectCGOAsPyList((ObjectCGO *) rec->obj));
5319     break;
5320   case cObjectAlignment:
5321     PyList_SetItem(result, 5, ObjectAlignmentAsPyList((ObjectAlignment *) rec->obj));
5322     break;
5323   case cObjectGroup:
5324     PyList_SetItem(result, 5, ObjectGroupAsPyList((ObjectGroup *) rec->obj));
5325     break;
5326   case cObjectVolume:
5327     PyList_SetItem(result, 5, ObjectVolumeAsPyList((ObjectVolume *) rec->obj));
5328     break;
5329   case cObjectCallback:
5330     PyList_SetItem(result, 5, ObjectCallbackAsPyList((ObjectCallback *) rec->obj));
5331     break;
5332   default:
5333     PyList_SetItem(result, 5, PConvAutoNone(NULL));
5334     break;
5335   }
5336   PyList_SetItem(result, 6, PyString_FromString(rec->group_name));
5337 
5338   return (result);
5339 }
5340 
ExecutiveGetExecSeleAsPyList(PyMOLGlobals * G,SpecRec * rec)5341 static PyObject *ExecutiveGetExecSeleAsPyList(PyMOLGlobals * G, SpecRec * rec)
5342 {
5343   PyObject *result = NULL;
5344   int sele;
5345 
5346   sele = SelectorIndexByName(G, rec->name);
5347   if(sele >= 0) {
5348     result = PyList_New(7);
5349     PyList_SetItem(result, 0, PyString_FromString(rec->name));
5350     PyList_SetItem(result, 1, PyInt_FromLong(cExecSelection));
5351     PyList_SetItem(result, 2, PyInt_FromLong(rec->visible));
5352     /* before version 1.8 item 3 was rec reps (repOn) */
5353     PyList_SetItem(result, 3, PConvAutoNone(NULL));
5354     PyList_SetItem(result, 4, PyInt_FromLong(-1));
5355     PyList_SetItem(result, 5, SelectorAsPyList(G, sele));
5356     PyList_SetItem(result, 6, PyString_FromString(rec->group_name));
5357 
5358   }
5359   return (PConvAutoNone(result));
5360 }
5361 
ExecutiveGetNamedEntries(PyMOLGlobals * G,int list_id,int partial)5362 static PyObject *ExecutiveGetNamedEntries(PyMOLGlobals * G, int list_id, int partial)
5363 {
5364   CExecutive *I = G->Executive;
5365   CTracker *I_Tracker = I->Tracker;
5366   PyObject *result = NULL;
5367   int count = 0, total_count = 0;
5368   int iter_id = 0;
5369   SpecRec *rec = NULL, *list_rec = NULL;
5370 
5371   SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
5372 
5373   if(list_id) {
5374     total_count = TrackerGetNCandForList(I_Tracker, list_id);
5375     iter_id = TrackerNewIter(I_Tracker, 0, list_id);
5376   } else {
5377     total_count = ExecutiveCountNames(G);
5378   }
5379   result = PyList_New(total_count);
5380 
5381   /* critical reliance on short-circuit behavior */
5382 
5383   while((iter_id && TrackerIterNextCandInList(I_Tracker, iter_id,
5384                                               (TrackerRef **) (void *) &list_rec)) ||
5385         ((!iter_id) && ListIterate(I->Spec, rec, next))) {
5386 
5387     if(list_id)
5388       rec = list_rec;
5389     if(count >= total_count)
5390       break;
5391     if(rec) {
5392       switch (rec->type) {
5393       case cExecObject:
5394         PyList_SetItem(result, count, ExecutiveGetExecObjectAsPyList(G, rec));
5395         break;
5396       case cExecSelection:
5397         if(!partial) {
5398           PyList_SetItem(result, count, ExecutiveGetExecSeleAsPyList(G, rec));
5399         } else {
5400           /* cannot currently save selections in partial sessions */
5401           PyList_SetItem(result, count, PConvAutoNone(NULL));
5402         }
5403         break;
5404       default:
5405         PyList_SetItem(result, count, PConvAutoNone(NULL));
5406         break;
5407       }
5408     } else {
5409       PyList_SetItem(result, count, PConvAutoNone(NULL));
5410     }
5411     count++;
5412   }
5413 
5414   while(count < total_count) {  /* insure that all members of outgoing list are defined */
5415     PyList_SetItem(result, count, PConvAutoNone(NULL));
5416     count++;
5417   }
5418 
5419   if(iter_id) {
5420     TrackerDelIter(I_Tracker, iter_id);
5421   }
5422   return (PConvAutoNone(result));
5423 }
5424 
5425 #ifdef PYMOL_EVAL
5426 #include "ExecutiveEvalMessage.h"
5427 #endif
5428 
ExecutiveGetSession(PyMOLGlobals * G,PyObject * dict,const char * names,int partial,int quiet)5429 int ExecutiveGetSession(PyMOLGlobals * G, PyObject * dict, const char *names, int partial,
5430                         int quiet)
5431 {
5432   int ok = true;
5433   int list_id = 0;
5434   SceneViewType sv;
5435   PyObject *tmp;
5436 
5437   if(names && names[0]) {
5438     list_id = ExecutiveGetNamesListFromPattern(G, names, true, cExecExpandKeepGroups);
5439   }
5440 
5441   tmp = MovieScenesAsPyList(G);
5442   PyDict_SetItemString(dict, "moviescenes", tmp);
5443   Py_XDECREF(tmp);
5444 
5445   tmp = PyInt_FromLong(_PyMOL_VERSION_int);
5446   PyDict_SetItemString(dict, "version", tmp);
5447   Py_XDECREF(tmp);
5448 
5449   tmp = ExecutiveGetNamedEntries(G, list_id, partial);
5450   PyDict_SetItemString(dict, "names", tmp);
5451   Py_XDECREF(tmp);
5452 
5453   tmp = ColorAsPyList(G);
5454   PyDict_SetItemString(dict, "colors", tmp);
5455   Py_XDECREF(tmp);
5456 
5457   tmp = ColorExtAsPyList(G);
5458   PyDict_SetItemString(dict, "color_ext", tmp);
5459   Py_XDECREF(tmp);
5460 
5461   tmp = SettingUniqueAsPyList(G);
5462   PyDict_SetItemString(dict, "unique_settings", tmp);
5463   Py_XDECREF(tmp);
5464 
5465   if(partial) {                 /* mark this as a partial session */
5466 
5467     PyDict_SetItemString(dict, "partial", PConvAutoNone(Py_None));
5468 
5469   } else {
5470 
5471     /* none of the following information is saved in partial sessions */
5472 
5473     tmp = SelectorSecretsAsPyList(G);
5474     PyDict_SetItemString(dict, "selector_secrets", tmp);
5475     Py_XDECREF(tmp);
5476 
5477     tmp = SettingGetGlobalsAsPyList(G);
5478     PyDict_SetItemString(dict, "settings", tmp);
5479     Py_XDECREF(tmp);
5480 
5481     SceneGetView(G, sv);
5482     tmp = PConvFloatArrayToPyList(sv, cSceneViewSize);
5483     PyDict_SetItemString(dict, "view", tmp);
5484     Py_XDECREF(tmp);
5485 
5486     tmp = MovieAsPyList(G);
5487     PyDict_SetItemString(dict, "movie", tmp);
5488     Py_XDECREF(tmp);
5489 
5490     tmp = EditorAsPyList(G);
5491     PyDict_SetItemString(dict, "editor", tmp);
5492     Py_XDECREF(tmp);
5493 
5494     tmp = MainAsPyList(G);
5495     PyDict_SetItemString(dict, "main", tmp);
5496     Py_XDECREF(tmp);
5497 
5498 #ifdef PYMOL_EVAL
5499     ExecutiveEvalMessage(G, dict);
5500 #endif
5501 
5502   }
5503 
5504   if(Feedback(G, FB_Executive, FB_Errors)) {
5505     if(PyErr_Occurred()) {
5506       PRINTF
5507         " ExecutiveGetSession: a Python error occured during creation of the session object:\n"
5508         ENDF(G);
5509       PyErr_Print();
5510     }
5511   }
5512 
5513   return (ok);
5514 }
5515 
ExecutiveMigrateSession(PyMOLGlobals * G,int session_version)5516 static void ExecutiveMigrateSession(PyMOLGlobals * G, int session_version)
5517 {
5518   if (session_version < 1700) {
5519     if (SettingGetGlobal_i(G, cSetting_seq_view_label_color) == 0 /* white */) {
5520       SettingSetGlobal_i(G, cSetting_seq_view_label_color, cColorFront);
5521     }
5522   }
5523   if(session_version < 100) {
5524     /* migrate lighting model */
5525     SettingSetGlobal_f(G, cSetting_direct, 1.8 * SettingGetGlobal_f(G, cSetting_direct));
5526     SettingSetGlobal_f(G, cSetting_reflect,
5527                        0.5 * SettingGetGlobal_f(G, cSetting_reflect));
5528     SettingSetGlobal_f(G, cSetting_ambient,
5529                        1.166 * SettingGetGlobal_f(G, cSetting_ambient));
5530     SettingSetGlobal_f(G, cSetting_gamma, 0.769 * SettingGetGlobal_f(G, cSetting_gamma));
5531 
5532     /* try best to meet existing expectations with existing sessions */
5533     SettingSetGlobal_f(G, cSetting_ray_legacy_lighting, 1.0F);
5534 
5535     /* force use of movie_delay in preference to movie_fps */
5536 
5537     SettingSetGlobal_f(G, cSetting_movie_fps, 0.0F);
5538 
5539     /* and labels */
5540     SettingSetGlobal_i(G, cSetting_label_digits, 2);
5541   }
5542   if(session_version < 99) {
5543     SettingSetGlobal_f(G, cSetting_cartoon_ladder_mode, 0);
5544     SettingSetGlobal_f(G, cSetting_cartoon_tube_cap, 0);
5545     SettingSetGlobal_f(G, cSetting_cartoon_nucleic_acid_mode, 1);
5546     {
5547       float old_sulfur[3] = { 1.0, 0.5, 0.0 };
5548       ColorDef(G, "sulfur", old_sulfur, 0, true);
5549     }
5550   }
5551   if(session_version < 98) {
5552     /* produce expected rendering quality & performance with old sessions */
5553     SettingSetGlobal_b(G, cSetting_ray_orthoscopic, 1);
5554   }
5555   if(session_version < 96) {
5556     SettingSetGlobal_f(G, cSetting_ray_transparency_contrast, 1.0F);
5557   }
5558   if(session_version < 95) {
5559     {
5560       /* adjust fog to reflect current importance of seeing to the Z-slab center w/o fog */
5561 
5562       float fog_start = SettingGetGlobal_f(G, cSetting_fog_start);
5563       float ray_trace_fog_start = SettingGetGlobal_f(G, cSetting_ray_trace_fog_start);
5564       if((fog_start == 0.40F) || (fog_start == 0.35F) || (fog_start == 0.30F)) {
5565         SettingSetGlobal_f(G, cSetting_fog_start, 0.45F);
5566       }
5567       if((ray_trace_fog_start == 0.45F) || (ray_trace_fog_start == 0.40F)
5568          || (ray_trace_fog_start == 0.35F)) {
5569         SettingSetGlobal_f(G, cSetting_ray_trace_fog_start, 0.50F);
5570       }
5571     }
5572 
5573     {                           /* adjust GUI width */
5574 
5575       int gui_width = SettingGetGlobal_i(G, cSetting_internal_gui_width);
5576 
5577       if(gui_width == 160) {
5578         SettingSetGlobal_i(G, cSetting_internal_gui_width, 220);
5579       }
5580     }
5581 
5582     {                           /* enable antialiasing */
5583 
5584       int antialias = SettingGetGlobal_i(G, cSetting_antialias);
5585 
5586       if(antialias == 0) {
5587         SettingSetGlobal_i(G, cSetting_antialias, 1);
5588       }
5589 
5590     }
5591   }
5592 }
5593 
ExecutiveSetSession(PyMOLGlobals * G,PyObject * session,int partial_restore,int quiet)5594 int ExecutiveSetSession(PyMOLGlobals * G, PyObject * session,
5595                         int partial_restore, int quiet)
5596 {
5597   int ok = true;
5598   int incomplete = false;
5599   PyObject *tmp;
5600   SceneViewType sv;
5601   int version = -1, version_full;
5602   int migrate_sessions = SettingGetGlobal_b(G, cSetting_session_migration);
5603   char active[WordLength] = "";
5604   int have_active = false;
5605   int partial_session = false;
5606 
5607   G->Color->HaveOldSessionColors = false;
5608   G->Color->HaveOldSessionExtColors = false;
5609 
5610   if(!partial_restore) {        /* if user has requested partial restore */
5611     ExecutiveDelete(G, "all");
5612     ColorReset(G);
5613   }
5614 
5615   if(!session || !PyDict_Check(session)) {
5616     PRINTFB(G, FB_Executive, FB_Errors)
5617       "Error: not a dict\n" ENDFB(G);
5618     return 0;
5619   }
5620 
5621   if(ok) {                      /* if session is partial, then don't error about missing stuff */
5622     tmp = PyDict_GetItemString(session, "partial");
5623     if(tmp) {
5624       partial_session = true;
5625     }
5626   }
5627 
5628   if (partial_restore) {
5629     G->SettingUnique->old2new = OVOneToOne_New(G->Context->heap);
5630   }
5631 
5632   if(ok) {
5633     tmp = PyDict_GetItemString(session, "version");
5634     if(tmp) {
5635       ok = PConvPyIntToInt(tmp, &version);
5636       if(ok) {
5637 	version_full = version;
5638 	while (version_full < 210)  /* any version less than 2.1 (account for next major version 2) should be 4 digits, otherwise 3 */
5639 	  version_full *= 10;
5640         float version_float = version_full / (version >= 1000000 ? 1000000.f : 1000.f);
5641         if(version > _PyMOL_VERSION_int) {
5642           if(!quiet) {
5643             PRINTFB(G, FB_Executive, FB_Errors)
5644               "Warning: This session was created with a newer version of PyMOL (%1.6f).\n",
5645               version_float ENDFB(G);
5646             if(SettingGetGlobal_i(G, cSetting_session_version_check)) {
5647               PRINTFB(G, FB_Executive, FB_Errors)
5648                 "Error: Please update first -- see http://www.pymol.org\n" ENDFB(G);
5649               ok = false;
5650             } else {
5651               PRINTFB(G, FB_Executive, FB_Errors)
5652                 "Warning: Some content may not load completely.\n" ENDFB(G);
5653             }
5654           }
5655         } else {
5656           if(!quiet) {
5657             PRINTFB(G, FB_Executive, FB_Details)
5658               " Executive: Loading version %1.6f session...\n", version_float ENDFB(G);
5659           }
5660         }
5661       }
5662     }
5663   }
5664 #ifndef PYMOL_EVAL
5665   if(ok) {
5666     tmp = PyDict_GetItemString(session, "eval_nag");
5667     if(tmp) {
5668       ok = PyString_Check(tmp);
5669       if(ok) {
5670         const char *st = PyString_AsString(tmp);
5671         if(st) {
5672           if(Feedback(G, FB_Nag, FB_Warnings)) {
5673             OrthoAddOutput(G, st);
5674           }
5675         }
5676       }
5677     }
5678   }
5679 #endif
5680 
5681   if(ok) {                      /* colors must be restored before settings and objects */
5682     tmp = PyDict_GetItemString(session, "colors");
5683     if(tmp) {
5684       ok = ColorFromPyList(G, tmp, partial_restore);
5685     }
5686 
5687     if(tmp || (!partial_restore)) {     /* ignore missing if partial restore */
5688 
5689       if(PyErr_Occurred()) {
5690         PyErr_Print();
5691         ok = false;
5692       }
5693       if(!ok) {
5694         PRINTFB(G, FB_Executive, FB_Errors)
5695           "ExectiveSetSession-Error: after colors.\n" ENDFB(G);
5696       }
5697       if(!ok) {
5698         incomplete = true;
5699         ok = true;              /* keep trying...don't give up */
5700       }
5701     }
5702   }
5703 
5704   if(ok) {
5705     tmp = PyDict_GetItemString(session, "color_ext");
5706     if(tmp) {
5707       ok = ColorExtFromPyList(G, tmp, partial_restore);
5708     }
5709 
5710     if(tmp || (!partial_session)) {     /* ignore missing if partial restore */
5711       if(PyErr_Occurred()) {
5712         PyErr_Print();
5713         ok = false;
5714       }
5715       if(!ok) {
5716         PRINTFB(G, FB_Executive, FB_Errors)
5717           "ExectiveSetSession-Error: after color_ext.\n" ENDFB(G);
5718       }
5719       if(!ok) {
5720         incomplete = true;
5721         ok = true;              /* keep trying...don't give up */
5722       }
5723     }
5724   }
5725   if(ok) {
5726     tmp = PyDict_GetItemString(session, "settings");
5727     if(tmp && !partial_restore) {
5728       SettingSetGlobalsFromPyList(G, tmp);
5729     }
5730 
5731     if(tmp || (!(partial_restore | partial_session))) { /* ignore missing if partial restore */
5732       if(PyErr_Occurred()) {
5733         PyErr_Print();
5734         ok = false;
5735       }
5736       if(!ok) {
5737         PRINTFB(G, FB_Executive, FB_Errors)
5738           "ExectiveSetSession-Error: after settings.\n" ENDFB(G);
5739       }
5740       if(!ok) {
5741         incomplete = true;
5742         ok = true;              /* keep trying...don't give up */
5743       }
5744     }
5745   }
5746   if(ok) {
5747     tmp = PyDict_GetItemString(session, "unique_settings");
5748     if(tmp) {
5749       ok = SettingUniqueFromPyList(G, tmp, partial_restore);
5750     }
5751 
5752     if(tmp || (!partial_session)) {     /* ignore missing if partial restore */
5753       if(PyErr_Occurred()) {
5754         PyErr_Print();
5755         ok = false;
5756       }
5757       if(!ok) {
5758         PRINTFB(G, FB_Executive, FB_Errors)
5759           "ExectiveSetSession-Error: after settings.\n" ENDFB(G);
5760       }
5761       if(!ok) {
5762         incomplete = true;
5763         ok = true;              /* keep trying...don't give up */
5764       }
5765     }
5766   }
5767   if(ok) {
5768     tmp = PyDict_GetItemString(session, "names");
5769     if(tmp) {
5770       if(ok)
5771         ok = ExecutiveSetNamedEntries(G, tmp, version, partial_restore, partial_session);
5772       if(!(partial_restore || partial_session)) {
5773         if(ok)
5774           ok = ExecutiveSetSelectionsFromPyList(G, tmp);
5775         if(ok)
5776           have_active = ExecutiveGetActiveSeleName(G, active, false, false);
5777       }
5778     }
5779     if(PyErr_Occurred()) {
5780       PyErr_Print();
5781       ok = false;
5782     }
5783     if(!ok) {
5784       PRINTFB(G, FB_Executive, FB_Errors)
5785         "ExectiveSetSession-Error: after names.\n" ENDFB(G);
5786     }
5787     if(!ok) {
5788       incomplete = true;
5789       ok = true;                /* keep trying...don't give up */
5790     }
5791   }
5792   if(ok && !(partial_restore)) {
5793     tmp = PyDict_GetItemString(session, "selector_secrets");
5794     if(tmp) {
5795       if(ok)
5796         ok = SelectorSecretsFromPyList(G, tmp);
5797     }
5798 
5799     if(tmp || (!(partial_restore | partial_session))) { /* ignore missing if partial restore */
5800       if(PyErr_Occurred()) {
5801         PyErr_Print();
5802         ok = false;
5803       }
5804       if(!ok) {
5805         PRINTFB(G, FB_Executive, FB_Errors)
5806           "ExectiveSetSession-Error: after selector secrets.\n" ENDFB(G);
5807       }
5808       if(!ok) {
5809         incomplete = true;
5810         ok = true;              /* keep trying...don't give up */
5811       }
5812     }
5813   }
5814   if(ok && !(partial_restore)) {
5815     tmp = PyDict_GetItemString(session, "view");
5816     if(tmp) {
5817       ok = PConvPyListToFloatArrayInPlace(tmp, sv, cSceneViewSize);
5818     }
5819     if(tmp || (!(partial_restore | partial_session))) { /* ignore missing if partial restore */
5820       if(ok)
5821         SceneSetView(G, sv, true, 0, 0);
5822       if(PyErr_Occurred()) {
5823         PyErr_Print();
5824         ok = false;
5825       }
5826       if(!ok) {
5827         PRINTFB(G, FB_Executive, FB_Errors)
5828           "ExectiveSetSession-Error: after view.\n" ENDFB(G);
5829       }
5830       if(!ok) {
5831         incomplete = true;
5832         ok = true;              /* keep trying...don't give up */
5833       }
5834     }
5835   }
5836 
5837   if (ok && !partial_restore) {
5838     tmp = CPythonVal_PyDict_GetItemString(G, session, "moviescenes");
5839     if (tmp) {
5840       MovieScenesFromPyList(G, tmp);
5841       CPythonVal_Free(tmp);
5842     }
5843   }
5844 
5845   if(ok && !(partial_restore)) {
5846     int warning;
5847     tmp = PyDict_GetItemString(session, "movie");
5848     if(tmp) {
5849       ok = MovieFromPyList(G, tmp, &warning);
5850     }
5851     if(tmp || (!(partial_restore | partial_session))) { /* ignore missing if partial restore */
5852 
5853       if(PyErr_Occurred()) {
5854         PyErr_Print();
5855         ok = false;
5856       }
5857       if(!ok) {
5858         PRINTFB(G, FB_Executive, FB_Errors)
5859           "ExectiveSetSession-Error: after movie.\n" ENDFB(G);
5860       }
5861       if(!ok) {
5862         incomplete = true;
5863         ok = true;              /* keep trying...don't give up */
5864       }
5865     }
5866   }
5867 
5868   if(ok && !(partial_restore)) {
5869     tmp = PyDict_GetItemString(session, "editor");
5870     if(tmp) {
5871       ok = EditorFromPyList(G, tmp);
5872     }
5873     if(tmp || (!(partial_restore | partial_session))) { /* ignore missing if partial restore */
5874 
5875       if(PyErr_Occurred()) {
5876         PyErr_Print();
5877         ok = false;
5878       }
5879       if(!ok) {
5880         PRINTFB(G, FB_Executive, FB_Errors)
5881           "ExectiveSetSession-Error: after editor.\n" ENDFB(G);
5882       }
5883       if(!ok) {
5884         incomplete = true;
5885         ok = true;              /* keep trying...don't give up */
5886       }
5887     }
5888   }
5889   if(ok) {                      /* update mouse in GUI */
5890     PParse(G, "cmd.mouse(quiet=1)");
5891     if(!G->Option->presentation && !SettingGetGlobal_b(G, cSetting_suspend_updates))
5892       PParse(G, "viewport");    /* refresh window/internal_gui status */
5893   }
5894   // Do not load viewport size when we have a GUI
5895   if(ok) {
5896     tmp = PyDict_GetItemString(session, "main");
5897     if(tmp) {
5898       if (!G->HaveGUI &&
5899           /* PYMOL-775 added suspend_updates check, but does it make sense? */
5900           !SettingGetGlobal_b(G, cSetting_suspend_updates) &&
5901           !partial_restore) {
5902         ok = MainFromPyList(G, tmp);
5903 #ifndef _PYMOL_NOPY
5904       } else if (!quiet) {
5905         int viewport[2];
5906         PConvPyListToIntArrayInPlace(tmp, viewport, 2);
5907         PRINTFB(G, FB_Executive, FB_Actions)
5908           " Session was saved with: viewport %d, %d\n",
5909           viewport[0], viewport[1] ENDFB(G);
5910 #endif
5911       }
5912     }
5913     if(tmp || (!(partial_restore | partial_session))) { /* ignore missing if partial restore */
5914       if(PyErr_Occurred()) {
5915         PyErr_Print();
5916         ok = false;
5917       }
5918       if(!ok) {
5919         PRINTFB(G, FB_Executive, FB_Errors)
5920           "ExectiveSetSession-Error: after main.\n" ENDFB(G);
5921       }
5922       if(!ok) {
5923         incomplete = true;
5924         ok = true;              /* keep trying...don't give up */
5925       }
5926     }
5927   }
5928 
5929   if(ok && migrate_sessions) {  /* migrate sessions */
5930     tmp = PyDict_GetItemString(session, "version");
5931     if(tmp) {
5932       ok = PConvPyIntToInt(tmp, &version);
5933       if(ok) {
5934         ExecutiveMigrateSession(G, version);
5935       }
5936     }
5937   }
5938   if(ok) {
5939     if(have_active)
5940       ExecutiveSetObjVisib(G, active, true, false);
5941   }
5942 
5943   OVOneToOne_DEL_AUTO_NULL(G->SettingUnique->old2new);
5944 
5945   if(incomplete) {
5946     PRINTFB(G, FB_Executive, FB_Warnings)
5947       "ExectiveSetSession-Warning: restore may be incomplete.\n" ENDFB(G);
5948   }
5949   G->ShaderMgr->Set_Reload_Bits(RELOAD_ALL_SHADERS);
5950   OrthoBackgroundTextureNeedsUpdate(G);
5951   ExecutiveInvalidateSelectionIndicatorsCGO(G);
5952   OrthoInvalidateDoDraw(G);
5953   SceneChanged(G);
5954   return (ok);
5955 }
5956 
5957 
5958 #define ExecScrollBarMargin DIP2PIXEL(1)
5959 #define ExecScrollBarWidth DIP2PIXEL(13)
5960 
5961 /**
5962  * @param sele object name (or single-object atom selection expression)
5963  * @param state object state (only for maps, ignored for molecules)
5964  * @return true if symmetry is defined
5965  */
5966 pymol::Result<bool>
ExecutiveGetSymmetry(PyMOLGlobals * G,const char * sele,int state,float * a,float * b,float * c,float * alpha,float * beta,float * gamma,char * sgroup)5967 ExecutiveGetSymmetry(PyMOLGlobals * G, const char *sele, int state, float *a, float *b, float *c,
5968                         float *alpha, float *beta, float *gamma, char *sgroup)
5969 {
5970   // try `sele` as an object name
5971   auto obj = ExecutiveFindObjectByName(G, sele);
5972 
5973   // odd feature: accept atom selections to select single object
5974   if (!obj) {
5975     auto tmpsele1 = SelectorTmp::make(G, sele);
5976     p_return_if_error(tmpsele1);
5977     obj = SelectorGetSingleObjectMolecule(G, tmpsele1->getIndex());
5978     if (!obj) {
5979       return pymol::make_error("selection must refer to exactly one object");
5980     }
5981   }
5982 
5983   CSymmetry const* symm = obj->getSymmetry(state);
5984 
5985   if(symm) {
5986     *a = symm->Crystal.Dim[0];
5987     *b = symm->Crystal.Dim[1];
5988     *c = symm->Crystal.Dim[2];
5989     *alpha = symm->Crystal.Angle[0];
5990     *beta = symm->Crystal.Angle[1];
5991     *gamma = symm->Crystal.Angle[2];
5992     UtilNCopy(sgroup, symm->SpaceGroup, sizeof(WordType));
5993     return true;
5994   }
5995 
5996   return false;
5997 }
5998 
5999 /**
6000  * Set symmetry for one or more objects.
6001  * @param names Object name pattern
6002  * @param state Object state (supports all=-1 and current=-2)
6003  * @return True if symmetry could be set for at least one object
6004  */
ExecutiveSetSymmetry(PyMOLGlobals * G,pymol::zstring_view names,int const state,CSymmetry const & symmetry,bool const quiet=false)6005 static bool ExecutiveSetSymmetry(PyMOLGlobals* G, pymol::zstring_view names,
6006     int const state, CSymmetry const& symmetry, bool const quiet = false)
6007 {
6008   auto objects = ExecutiveGetObjectsFromPattern(G, names);
6009   bool success = false;
6010 
6011   for (auto* obj : objects) {
6012     if (!obj->setSymmetry(symmetry, state)) {
6013       PRINTFB(G, FB_Executive, FB_Warnings)
6014       " %s-Warning: Cannot set symmetry for '%s' (type %s)\n", __func__,
6015           obj->Name, typeid(obj).name() ENDFB(G);
6016       continue;
6017     }
6018 
6019     success = true;
6020 
6021     if (!quiet) {
6022       PRINTFB(G, FB_Executive, FB_Details)
6023       " %s-Details: Updated symmetry for '%s'\n", __func__, obj->Name ENDFB(G);
6024     }
6025   }
6026 
6027   return success;
6028 }
6029 
ExecutiveSetSymmetry(PyMOLGlobals * G,const char * sele,int state,float a,float b,float c,float alpha,float beta,float gamma,const char * sgroup,int quiet)6030 pymol::Result<> ExecutiveSetSymmetry(PyMOLGlobals* G, const char* sele,
6031     int state, float a, float b, float c, float alpha, float beta, float gamma,
6032     const char* sgroup, int quiet)
6033 {
6034   /* create a new symmetry object for copying */
6035   CSymmetry symmetry(G);
6036   symmetry.Crystal.Dim[0] = a;
6037   symmetry.Crystal.Dim[1] = b;
6038   symmetry.Crystal.Dim[2] = c;
6039   symmetry.Crystal.Angle[0] = alpha;
6040   symmetry.Crystal.Angle[1] = beta;
6041   symmetry.Crystal.Angle[2] = gamma;
6042   UtilNCopy(symmetry.SpaceGroup, sgroup, sizeof(WordType));
6043   SymmetryUpdate(&symmetry);
6044 
6045   if (!ExecutiveSetSymmetry(G, sele, state, symmetry, quiet)) {
6046     return pymol::Error("no object selected");
6047   }
6048 
6049   return {};
6050 }
6051 
ExecutiveSymmetryCopy(PyMOLGlobals * G,const char * source_name,const char * target_name,int source_state,int target_state,int quiet)6052 pymol::Result<> ExecutiveSymmetryCopy(PyMOLGlobals* G, const char* source_name,
6053     const char* target_name, int source_state, int target_state, int quiet)
6054 {
6055 
6056   /* Copy the symmetry info from source to target; currently maps can have
6057    * multiple states for symmetry, but ObjectMolecule cannot */
6058 
6059   auto const* source_obj = ExecutiveFindObjectByName(G, source_name);
6060   if (!source_obj) {
6061     return pymol::Error("source object not found");
6062   }
6063 
6064   auto const* source_symm = source_obj->getSymmetry(source_state);
6065   if (!source_symm) {
6066     return pymol::Error(pymol::string_format(
6067         "no symmetry in object '%s' state %d", source_name, source_state));
6068   }
6069 
6070   if (!ExecutiveSetSymmetry(
6071           G, target_name, target_state, *source_symm, quiet)) {
6072     return pymol::Error("target object not found");
6073   }
6074 
6075   return {};
6076 }
6077 
ExecutiveSmooth(PyMOLGlobals * G,const char * selection,int cycles,int window,int first,int last,int ends,int quiet)6078 pymol::Result<> ExecutiveSmooth(PyMOLGlobals* G, const char* selection,
6079     int cycles, int window, int first, int last, int ends, int quiet)
6080 {
6081   SETUP_SELE(selection, tmpsele1, sele);
6082   const char *name = tmpsele1->getName();
6083 
6084   ObjectMoleculeOpRec op;
6085   int state;
6086   int n_state;
6087   float *coord0 = NULL, *coord1 = NULL;
6088   int *flag0 = NULL, *flag1 = NULL;
6089   int a, b, c, d, st, cnt;
6090   float i_cnt;
6091   int n_atom;
6092   int backward;
6093   int forward;
6094   int range, offset;
6095   int end_skip = 0;
6096   float *v0, *v1;
6097   float sum[3];
6098   int loop = false;
6099   /*  WordType all = "_all"; */
6100 
6101   PRINTFD(G, FB_Executive)
6102     " %s: entered %s,%d,%d,%d,%d,%d\n", __func__, name, cycles, first, last, window,
6103     ends ENDFD;
6104 
6105   {
6106     /* count the number of states over which to smooth */
6107     int max_state = ExecutiveCountStates(G, name) - 1;
6108     if(last < 0)
6109       last = max_state;
6110     if(first < 0)
6111       first = 0;
6112     if(last < first) {
6113       state = last;
6114       last = first;
6115       first = state;
6116     }
6117     if(last > max_state)
6118       last = max_state;
6119 
6120     n_state = last - first + 1;
6121 
6122     backward = window / 2;
6123     forward = window / 2;
6124 
6125     if((forward - backward) == (window + 1))
6126       forward--;                /* even sizes window */
6127 
6128     switch (ends) {
6129     case 0:
6130       end_skip = 1;
6131       break;
6132     case 1:
6133       end_skip = 0;
6134       break;
6135     case 2:
6136       end_skip = backward;
6137       break;
6138     case 3:                    /* cyclic averaging */
6139       end_skip = 0;
6140       loop = true;
6141       break;
6142     default:
6143       end_skip = 0;
6144       break;
6145     }
6146 
6147     if(ends) {
6148       range = (last - first) + 1;
6149       offset = 0;
6150     } else {
6151       range = (last - end_skip) - (first + end_skip) + 1;
6152       offset = end_skip;
6153     }
6154 
6155     PRINTFD(G, FB_Executive)
6156       " %s: first %d last %d n_state %d backward %d forward %d range %d\n", __func__,
6157       first, last, n_state, backward, forward, range ENDFD;
6158 
6159     if(n_state >= window) {
6160 
6161       /* determine storage req */
6162       ObjectMoleculeOpRecInit(&op);
6163       op.code = OMOP_CountAtoms;
6164       op.i1 = 0;
6165       ExecutiveObjMolSeleOp(G, sele, &op);
6166       n_atom = op.i1;
6167       if(n_atom) {
6168         /* allocate storage */
6169         coord0 = pymol::malloc<float>(3 * n_atom * n_state);
6170         coord1 = pymol::malloc<float>(3 * n_atom * n_state);
6171         flag0 = pymol::malloc<int>(n_atom * n_state);
6172         flag1 = pymol::malloc<int>(n_atom * n_state);
6173 
6174         /* clear the arrays */
6175 
6176         UtilZeroMem(coord0, sizeof(float) * 3 * n_atom * n_state);
6177         UtilZeroMem(flag0, sizeof(int) * n_atom * n_state);
6178 
6179         /* get the data */
6180         if(!quiet) {
6181           PRINTFB(G, FB_Executive, FB_Actions)
6182             " Smooth: copying coordinates to temporary arrays..\n" ENDFB(G);
6183         }
6184         op.code = OMOP_CSetIdxGetAndFlag;
6185         op.i1 = n_atom;
6186         op.i2 = 0;
6187         op.cs1 = first;
6188         op.cs2 = last;
6189         op.vv1 = coord0;
6190         op.ii1 = flag0;
6191         op.nvv1 = 0;
6192         ExecutiveObjMolSeleOp(G, sele, &op);
6193 
6194         PRINTFD(G, FB_Executive)
6195           " %s: got %d %d\n", __func__, op.i2, op.nvv1 ENDFD;
6196 
6197         UtilZeroMem(coord1, sizeof(float) * 3 * n_atom * n_state);
6198         UtilZeroMem(flag1, sizeof(int) * n_atom * n_state);
6199 
6200         for(a = 0; a < cycles; a++) {
6201           if(!quiet) {
6202             PRINTFB(G, FB_Executive, FB_Actions)
6203               " Smooth: smoothing (pass %d)...\n", a + 1 ENDFB(G);
6204           }
6205           for(b = 0; b < range; b++) {
6206             for(c = 0; c < n_atom; c++) {
6207               zero3f(sum);
6208               cnt = 0;
6209               for(d = -backward; d <= forward; d++) {
6210                 st = b + offset + d;
6211                 if(loop) {
6212                   if(st < 0) {
6213                     st = n_state + st;
6214                   } else if(st >= n_state) {
6215                     st = st - n_state;
6216                   }
6217                 } else {
6218                   if(st < 0) {
6219                     st = 0;
6220                   } else if(st >= n_state) {
6221                     st = n_state - 1;
6222                   }
6223                 }
6224 
6225                 /*if(c==0) printf("averaging from slot %d\n",st); */
6226                 cnt += flag0[(n_atom * st) + c];
6227                 v0 = coord0 + 3 * (n_atom * st + c);
6228 		/* atom's avg position */
6229                 add3f(sum, v0, sum);
6230               }
6231               if(cnt) {
6232                 st = b + offset;
6233                 if((st >= end_skip) && (st < (n_state - end_skip))) {
6234                   /* if(c==0) printf("dumping into slot %d\n",st); */
6235                   flag1[(n_atom * st) + c] = flag0[(n_atom * st) + c];
6236                   /* don't flag states that weren't originally flagged */
6237                   i_cnt = 1.0F / cnt;
6238                   v1 = coord1 + 3 * ((n_atom * st) + c);
6239                   scale3f(sum, i_cnt, v1);
6240                 }
6241               }
6242             }
6243           }
6244           for(b = 0; b < range; b++) {
6245             for(c = 0; c < n_atom; c++) {
6246               st = b + offset;
6247               if(flag1[(n_atom * st) + c]) {
6248                 v0 = coord0 + 3 * ((n_atom * st) + c);
6249                 v1 = coord1 + 3 * ((n_atom * st) + c);
6250                 copy3f(v1, v0);
6251               }
6252             }
6253           }
6254         }
6255 
6256         if(!quiet) {
6257           PRINTFB(G, FB_Executive, FB_Actions)
6258             " Smooth: updating coordinates...\n" ENDFB(G);
6259         }
6260 
6261         /* set the new coordinates */
6262 
6263         op.code = OMOP_CSetIdxSetFlagged;
6264         op.i1 = n_atom;
6265         op.i2 = 0;
6266         if(ends) {
6267           op.cs1 = first;
6268           op.cs2 = last;
6269           op.vv1 = coord1;
6270           op.ii1 = flag1;
6271         } else {
6272           op.cs1 = first + end_skip;
6273           op.cs2 = last - end_skip;
6274           op.vv1 = coord1 + (end_skip * 3 * n_atom);
6275           op.ii1 = flag1 + (end_skip * n_atom);
6276         }
6277         op.nvv1 = 0;
6278 
6279         ExecutiveObjMolSeleOp(G, sele, &op);
6280         PRINTFD(G, FB_Executive)
6281           " %s: put %d %d\n", __func__, op.i2, op.nvv1 ENDFD;
6282 
6283         FreeP(coord0);
6284         FreeP(coord1);
6285         FreeP(flag0);
6286         FreeP(flag1);
6287       }
6288     }
6289   }
6290   return {};
6291 }
6292 
6293 
6294 /*========================================================================*/
ExecutiveDebug(PyMOLGlobals * G,const char * name)6295 int ExecutiveDebug(PyMOLGlobals * G, const char *name)
6296 {
6297   ObjectMolecule *obj;
6298   ObjectMoleculeBPRec bp;
6299   int a;
6300 
6301   obj = (ObjectMolecule *) ExecutiveFindObjectByName(G, name);
6302   if(obj) {
6303     ObjectMoleculeInitBondPath(obj, &bp);
6304     ObjectMoleculeGetBondPaths(obj, 0, 10, &bp);
6305     for(a = 0; a < bp.n_atom; a++) {
6306       printf("%d %d %d\n", a, bp.list[a], bp.dist[bp.list[a]]);
6307     }
6308 
6309     ObjectMoleculePurgeBondPath(obj, &bp);
6310   }
6311   return (1);
6312 }
6313 
6314 
6315 /*========================================================================*/
ExecutiveGetBondPrint(PyMOLGlobals * G,const char * name,int max_bond,int max_type,int * dim)6316 int ***ExecutiveGetBondPrint(PyMOLGlobals * G, const char *name, int max_bond, int max_type,
6317                              int *dim)
6318 {
6319   int ***result = NULL;
6320   CObject *obj;
6321   ObjectMolecule *objMol;
6322 
6323   obj = ExecutiveFindObjectByName(G, name);
6324   if(obj->type == cObjectMolecule) {
6325     objMol = (ObjectMolecule *) obj;
6326     result = ObjectMoleculeGetBondPrint(objMol, max_bond, max_type, dim);
6327   }
6328   return (result);
6329 }
6330 
6331 
6332 /*========================================================================*/
6333 #define cMapOperatorMinimum 0
6334 #define cMapOperatorMaximum 1
6335 #define cMapOperatorSum     2
6336 #define cMapOperatorAverage 3
6337 #define cMapOperatorDifference 4
6338 #define cMapOperatorCopy     5
6339 #define cMapOperatorUnique   6
6340 
ExecutiveMapSet(PyMOLGlobals * G,const char * name,int operator_,const char * operands,int target_state,int source_state,int zoom,int quiet)6341 pymol::Result<> ExecutiveMapSet(PyMOLGlobals* G, const char* name,
6342     int operator_, const char* operands, int target_state, int source_state,
6343     int zoom, int quiet)
6344 {
6345   CExecutive *I = G->Executive;
6346   CTracker *I_Tracker = I->Tracker;
6347   int isNew = false;
6348   ObjectMap *target = ExecutiveFindObjectMapByName(G, name);
6349   ObjectMap *first_operand = NULL;
6350   int src_state_start = 0, src_state_stop = 0;
6351   int list_id = ExecutiveGetNamesListFromPattern(G, operands, true, true);
6352 
6353   if(target_state < 0)          /* if we're targeting all states, 0 is the offset */
6354     target_state = 0;
6355 
6356   /* first, figure out what the range of input states is */
6357 
6358   if(source_state < 0) {        /* all source states */
6359     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
6360     int max_n_state = 0;
6361     SpecRec *rec;
6362     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
6363       if(rec) {
6364         switch (rec->type) {
6365         case cExecObject:
6366           if(rec->obj->type == cObjectMap) {
6367             ObjectMap *obj = (ObjectMap *) rec->obj;
6368             if(obj->State.size() > max_n_state)
6369               max_n_state = obj->State.size();        /* count states */
6370           }
6371         }
6372       }
6373     }
6374     TrackerDelIter(I_Tracker, iter_id);
6375     src_state_start = 0;
6376     src_state_stop = max_n_state;
6377   } else {
6378     src_state_start = source_state;
6379     src_state_stop = source_state + 1;
6380   }
6381 
6382   {
6383     /* next, find the first operand */
6384 
6385     OrthoLineType first_op_st;
6386     ParseWordCopy(first_op_st, operands, sizeof(OrthoLineType) - 1);    /* copy the first operand */
6387     {
6388       int sub_list_id = ExecutiveGetNamesListFromPattern(G, first_op_st, true, true);
6389       int sub_iter_id = TrackerNewIter(I_Tracker, 0, sub_list_id);
6390       SpecRec *rec;
6391 
6392       while(TrackerIterNextCandInList(I_Tracker, sub_iter_id,
6393                                       (TrackerRef **) (void *) &rec)) {
6394         if(rec) {
6395           switch (rec->type) {
6396           case cExecObject:
6397             if(rec->obj->type == cObjectMap) {
6398               ObjectMap *obj = (ObjectMap *) rec->obj;
6399               first_operand = obj;
6400             }
6401             break;
6402           }
6403         }
6404         if(first_operand)
6405           break;
6406       }
6407       TrackerDelList(I_Tracker, sub_list_id);
6408       TrackerDelIter(I_Tracker, sub_iter_id);
6409     }
6410   }
6411 
6412   {
6413 
6414     /* okay, next thing we need to worry about is where we're putting the data.
6415 
6416        Case 1. If the map already exists, then we'll use the existing map points for storing
6417        the result.
6418 
6419        Case 2. If the operation implies a copy of existing map geometry, then we'll create that
6420        copy first before performing the calulation.
6421 
6422        Case 3. If the operation implies a new map geometry, then we need to compute that geometry and
6423        create the map.
6424 
6425      */
6426 
6427     if(!target) {               /* target map doesn't exist... */
6428       int need_union_geometry = false;
6429       int need_first_geometry = false;
6430       switch (operator_) {
6431       case cMapOperatorSum:
6432       case cMapOperatorAverage:
6433       case cMapOperatorMinimum:
6434       case cMapOperatorMaximum:
6435       case cMapOperatorDifference:
6436         need_union_geometry = true;
6437         break;
6438       case cMapOperatorUnique:
6439       case cMapOperatorCopy:
6440         need_first_geometry = true;
6441         break;
6442       }
6443 
6444       if(need_union_geometry) {
6445         int src_state, trg_state;
6446         ObjectMapDesc desc;
6447         target = new ObjectMap(G);
6448 
6449         ObjectSetName((CObject *) target, name);
6450         isNew = true;
6451 
6452         for(src_state = src_state_start; src_state < src_state_stop; src_state++) {
6453           trg_state = src_state + target_state;
6454           desc.mode = cObjectMap_OrthoMinMaxGrid;       /* Orthorhombic: min, max,
6455                                                            spacing,
6456                                                            centered over range  */
6457           desc.init_mode = 0;   /* zeros */
6458           desc.Grid[0] = 1.0F;
6459           desc.Grid[1] = 1.0F;
6460           desc.Grid[2] = 1.0F;
6461 
6462           {
6463             int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
6464             float grid_sum[3] = { 0.0F, 0.0F, 0.0F };
6465             int grid_count = 0;
6466             int first_extent = true;
6467 
6468             SpecRec *rec;
6469             while(TrackerIterNextCandInList(I_Tracker, iter_id,
6470                                             (TrackerRef **) (void *) &rec)) {
6471               if(rec) {
6472                 /* compute an average grid and get the effective "union" extent */
6473                 switch (rec->type) {
6474                 case cExecObject:
6475                   if(rec->obj->type == cObjectMap) {
6476 
6477                     ObjectMap *obj = (ObjectMap *) rec->obj;
6478                     ObjectMapState *ms = &obj->State[src_state];
6479                     if(src_state < obj->State.size()) {
6480                       if(ms->Active) {
6481                         if(first_extent) {
6482                           copy3f(ms->ExtentMin, desc.MinCorner);
6483                           copy3f(ms->ExtentMax, desc.MaxCorner);
6484                           first_extent = false;
6485                         } else {
6486                           int b;
6487                           for(b = 0; b < 3; b++) {
6488                             if(ms->ExtentMin[b] < desc.MinCorner[b])
6489                               desc.MinCorner[b] = ms->ExtentMin[b];
6490                             if(ms->ExtentMax[b] > desc.MaxCorner[b])
6491                               desc.MaxCorner[b] = ms->ExtentMax[b];
6492                           }
6493                         }
6494                         if(!ObjectMapStateValidXtal(ms)) {
6495                           /* other general-purpose maps currently handled */
6496                           int b;
6497                           for(b = 0; b < 3; b++) {
6498                             grid_sum[b] += ms->Grid[b];
6499                           }
6500                           grid_count++;
6501                         }
6502                       }
6503                     }
6504                   }
6505                 }
6506               }
6507             }
6508             TrackerDelIter(I_Tracker, iter_id);
6509             if(grid_count) {
6510               int b;
6511               for(b = 0; b < 3; b++) {
6512                 desc.Grid[b] = grid_sum[b] / grid_count;
6513               }
6514             }
6515             if(!first_extent) {
6516               add3f(desc.Grid, desc.MaxCorner, desc.MaxCorner);
6517               subtract3f(desc.MinCorner, desc.Grid, desc.MinCorner);
6518               ObjectMapNewStateFromDesc(G, target, &desc, trg_state, quiet);
6519               target->State[trg_state].Active = true;
6520             }
6521           }
6522         }
6523         /* need union geometry */
6524       } else if(need_first_geometry) {
6525         if(first_operand) {
6526           if(ObjectMapNewCopy(G, first_operand, &target, source_state, target_state)) {
6527             if(target) {
6528               ObjectSetName((CObject *) target, name);
6529               isNew = true;
6530             }
6531           }
6532         }
6533       }
6534     }
6535   }
6536 
6537   if(!target) {
6538     return pymol::make_error("Cannot find or construct target map.");
6539   }
6540 
6541   /* now do the actual operation */
6542 
6543   int src_state;
6544   for(src_state = src_state_start; src_state < src_state_stop; src_state++) {
6545     int trg_state = src_state + target_state;
6546     ObjectMapState *ms;
6547     VecCheckEmplace(target->State, trg_state, G);
6548 
6549     ms = &target->State[target_state];
6550     if(ms->Active) {
6551       int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
6552       int n_pnt = (ms->Field->points->size() / ms->Field->points->base_size) / 3;
6553       float *pnt = (float *) ms->Field->points->data.data();
6554       float *r_value = pymol::malloc<float>(n_pnt);
6555       float *l_value = pymol::calloc<float>(n_pnt);
6556       int *present = pymol::calloc<int>(n_pnt);
6557       int *inside = pymol::malloc<int>(n_pnt);
6558       SpecRec *rec;
6559 
6560       while(TrackerIterNextCandInList(I_Tracker, iter_id,
6561                                       (TrackerRef **) (void *) &rec)) {
6562         if(rec) {
6563           if(rec->type == cExecObject) {
6564             if(rec->obj->type == cObjectMap) {
6565               ObjectMap *obj = (ObjectMap *) rec->obj;
6566               if (ObjectMapInterpolate(obj, src_state, pnt, r_value, inside, n_pnt))
6567               {
6568                 int a;
6569                 float *rv = r_value;
6570                 float *lv = l_value;
6571                 int *flg = inside;
6572                 int *pre = present;
6573 
6574                 switch (operator_) {
6575                 case cMapOperatorCopy:
6576                   for(a = 0; a < n_pnt; a++) {
6577                     if(flg) {
6578                       *lv = *rv;
6579                     }
6580                     rv++;
6581                     lv++;
6582                     flg++;
6583                   }
6584                   break;
6585                 case cMapOperatorMinimum:
6586                   for(a = 0; a < n_pnt; a++) {
6587                     if(flg) {
6588                       if(*pre) {
6589                         if(*lv > *rv)
6590                           *lv = *rv;
6591                       } else {        /* first map */
6592                         *pre = 1;
6593                         *lv = *rv;
6594                       }
6595 
6596                     }
6597                     rv++;
6598                     lv++;
6599                     flg++;
6600                     pre++;
6601                   }
6602                   break;
6603                 case cMapOperatorMaximum:
6604                   for(a = 0; a < n_pnt; a++) {
6605                     if(flg) {
6606                       if(*pre) {
6607                         if(*lv < *rv)
6608                           *lv = *rv;
6609                       } else {        /* first map */
6610                         *pre = 1;
6611                         *lv = *rv;
6612                       }
6613                     }
6614                     rv++;
6615                     lv++;
6616                     flg++;
6617                     pre++;
6618                   }
6619                   break;
6620                 case cMapOperatorSum:
6621                   for(a = 0; a < n_pnt; a++) {
6622                     if(flg) {
6623                       *lv += *rv;
6624                     }
6625                     rv++;
6626                     lv++;
6627                     flg++;
6628                   }
6629                   break;
6630                 case cMapOperatorAverage:
6631                   for(a = 0; a < n_pnt; a++) {
6632                     if(flg) {
6633                       *lv += *rv;
6634                     }
6635                     (*pre)++;
6636                     rv++;
6637                     lv++;
6638                     flg++;
6639                     pre++;
6640                   }
6641                   break;
6642                 case cMapOperatorDifference:
6643                   if(obj != first_operand) {
6644                     for(a = 0; a < n_pnt; a++) {
6645                       if(flg) {
6646                         *lv -= *rv;
6647                       }
6648                       rv++;
6649                       lv++;
6650                       flg++;
6651                     }
6652                   } else {
6653                     for(a = 0; a < n_pnt; a++) {
6654                       if(flg) {
6655                         *lv += *rv;
6656                       }
6657                       rv++;
6658                       lv++;
6659                       flg++;
6660                     }
6661                   }
6662                   break;
6663                 case cMapOperatorUnique:
6664                   if(obj != first_operand) {
6665                     for(a = 0; a < n_pnt; a++) {
6666                       if(flg) {
6667                         *lv -= *rv;
6668                       }
6669                       rv++;
6670                       lv++;
6671                       flg++;
6672                     }
6673                   } else {
6674                     for(a = 0; a < n_pnt; a++) {
6675                       if(flg) {
6676                         *lv += *rv;
6677                       }
6678                       rv++;
6679                       lv++;
6680                       flg++;
6681                     }
6682                   }
6683 
6684                   break;
6685                 }
6686               }
6687             }
6688           }
6689         }
6690       }
6691 
6692       {
6693         int a;
6694         float *lv = l_value;
6695         int *pre = present;
6696 
6697         switch (operator_) {
6698         case cMapOperatorUnique:
6699           lv = l_value;
6700           for(a = 0; a < n_pnt; a++) {
6701             if(*lv < 0.0F)
6702               *lv = 0.0F;
6703             lv++;
6704           }
6705           break;
6706         case cMapOperatorAverage:
6707           lv = l_value;
6708           pre = present;
6709           for(a = 0; a < n_pnt; a++) {
6710             if(*pre)
6711               *lv /= *pre;
6712             lv++;
6713             pre++;
6714           }
6715         }
6716       }
6717 
6718       /* copy after calculation so that operand can include target */
6719 
6720       memcpy(ms->Field->data->data.data(), l_value, n_pnt * sizeof(float));
6721 
6722       FreeP(present);
6723       FreeP(l_value);
6724       FreeP(r_value);
6725       FreeP(inside);
6726       TrackerDelIter(I_Tracker, iter_id);
6727     }
6728   }
6729 
6730 
6731   /* and finally, update */
6732 
6733   if(target) {
6734     ObjectMapUpdateExtents(target);
6735     if(isNew) {
6736       ExecutiveManageObject(G, target, -1, quiet);
6737     } else {
6738       ExecutiveDoZoom(G, target, false, zoom, true);
6739     }
6740     SceneChanged(G);
6741   }
6742   TrackerDelList(I_Tracker, list_id);
6743 
6744   return {};
6745 }
6746 
6747 
ExecutiveMapGenerate(PyMOLGlobals * G,const char * name,const char * reflection_file,const char * tempFile,char * amplitudes,const char * phases,const char * weights,double reso_low,double reso_high,const char * space_group,double cell[6],int quiet,int zoom)6748 const char * ExecutiveMapGenerate(PyMOLGlobals * G, const char * name, const char * reflection_file, const char * tempFile,
6749 				  char * amplitudes, const char * phases, const char * weights, double reso_low,
6750 				  double reso_high, const char * space_group, double cell[6], int quiet, int zoom)
6751 {
6752   /* FIXME: this should be returned in memory!! */
6753   /* In the meantime, use mkstemp and load in Python */
6754   int ok;
6755 
6756   ok = 0;
6757 
6758   if (weights && (!strncmp(weights,"None",4)))
6759     weights=NULL;
6760 
6761   /* printf("Passing to primex driver: space_group=%s, cell=[%f %f %f %f %f %f], reso_high=%f, rseo_low=%f, refl_file=%s, ampl=%s, phases=%s, weights=%s, map_file=%s", space_group, cell[0], cell[1], cell[2], cell[3], cell[4], cell[5], reso_high, reso_low, reflection_file, amplitudes, phases, weights, tempFile); */
6762 
6763 #ifndef NO_MMLIBS
6764   ok = !(primex_pymol_driver2(space_group, cell, reso_high, reso_low, reflection_file, amplitudes,
6765 			    phases, weights, tempFile));
6766 #else
6767   PRINTFB(G, FB_Executive, FB_Errors)
6768     " Error: MTZ map loading not supported in this PyMOL build.\n" ENDFB(G);
6769 #endif
6770 
6771   if (!ok)
6772     return NULL;
6773   else
6774     return (const char*) tempFile;
6775 
6776 }
6777 
6778 pymol::Result<>
ExecutiveMapNew(PyMOLGlobals * G,const char * name,int type,float grid_spacing,const char * s0,float buffer,const float * minCorner,const float * maxCorner,int state,int have_corners,int quiet,int zoom,int normalize,float clamp_floor,float clamp_ceiling,float resolution)6779 ExecutiveMapNew(PyMOLGlobals * G, const char *name, int type, float grid_spacing,
6780                 const char *s0, float buffer,
6781                 const float *minCorner,
6782                 const float *maxCorner, int state, int have_corners,
6783                 int quiet, int zoom, int normalize, float clamp_floor,
6784                 float clamp_ceiling, float resolution)
6785 {
6786   CObject *origObj = NULL;
6787   ObjectMap *objMap;
6788   ObjectMapState *ms = NULL;
6789   int a;
6790   float v[3];
6791   ObjectMapDesc _md, *md;
6792 
6793   SETUP_SELE_DEFAULT(0);
6794   const char* sele = tmpsele0->getName();
6795 
6796   int isNew = true;
6797   int n_state;
6798   int valid_extent = false;
6799   int st;
6800   int st_once_flag = true;
6801   int n_st;
6802   int extent_state;
6803   int clamp_flag = (clamp_floor <= clamp_ceiling);
6804 
6805   md = &_md;
6806 
6807   float grid[3] = {grid_spacing, grid_spacing, grid_spacing};
6808 
6809   if((state == -2) || (state == -3))    /* TO DO: support per-object states */
6810     state = SceneGetState(G);
6811 
6812   /* remove object if it already exists */
6813 
6814   origObj = ExecutiveFindObjectByName(G, name);
6815 
6816   if(origObj) {
6817     if(origObj->type != cObjectMap) {
6818       ExecutiveDelete(G, origObj->Name);
6819     } else {
6820       isNew = false;
6821     }
6822   }
6823 
6824   n_st = ExecutiveCountStates(G, NULL);
6825 
6826   for(st = 0; st < n_st; st++) {
6827     if(state == -1)
6828       st_once_flag = false;     /* each state, separate map, separate extent */
6829     if(!st_once_flag)
6830       state = st;
6831     extent_state = state;
6832     if(state <= -3)
6833       extent_state = -1;
6834     if(strlen(sele) && (!have_corners)) {
6835       valid_extent = ExecutiveGetExtent(G, sele, md->MinCorner,
6836                                         md->MaxCorner, true, extent_state, false);
6837       /* TODO restrict to state */
6838     } else {
6839       valid_extent = 1;
6840       copy3f(minCorner, md->MinCorner);
6841       copy3f(maxCorner, md->MaxCorner);
6842     }
6843     copy3f(grid, md->Grid);
6844 
6845     subtract3f(md->MaxCorner, md->MinCorner, v);
6846     for(a = 0; a < 3; a++) {
6847       if(v[a] < 0.0)
6848         std::swap(md->MaxCorner[a], md->MinCorner[a]);
6849     };
6850     subtract3f(md->MaxCorner, md->MinCorner, v);
6851 
6852     if(buffer < -0.5F) { // buffer == -1
6853       buffer = SettingGetGlobal_f(G, cSetting_gaussian_resolution);
6854     }
6855 
6856     if(buffer > 0.0F) {
6857       for(a = 0; a < 3; a++) {
6858         md->MinCorner[a] -= buffer;
6859         md->MaxCorner[a] += buffer;
6860       }
6861     }
6862     md->mode = cObjectMap_OrthoMinMaxGrid;
6863     md->init_mode = -1;         /* no initialization */
6864 
6865     /* validate grid */
6866     for(a = 0; a < 3; a++)
6867       if(md->Grid[a] <= R_SMALL8)
6868         md->Grid[a] = R_SMALL8;
6869 
6870     if(isNew)
6871       objMap = new ObjectMap(G);
6872     else
6873       objMap = (ObjectMap *) origObj;
6874     if(objMap) {
6875       int once_flag = true;
6876       n_state = SelectorCountStates(G, sele0);
6877       if(valid_extent)
6878         for(a = 0; a < n_state; a++) {
6879           if(state == -5)
6880             once_flag = false;        /* api: state=-4 = each state, separate map, shared extent */
6881           if(state == -4)
6882             state = -1;       /* api: state=-3 all states, but one map */
6883           if(!once_flag)
6884             state = a;
6885           ms = ObjectMapNewStateFromDesc(G, objMap, md, state, quiet);
6886           if(!ms) {
6887             return pymol::make_error("Invalid state ", state);
6888           }
6889 
6890           switch (type) {
6891           case 0:          /* vdw */
6892             SelectorMapMaskVDW(G, sele0, ms, 0.0F, state);
6893             break;
6894           case 1:          /* coulomb */
6895             SelectorMapCoulomb(G, sele0, ms, 0.0F, state, false, false, 1.0F);
6896             break;
6897           case 2:          /* gaussian */
6898             SelectorMapGaussian(G, sele0, ms, 0.0F, state, normalize, false, quiet, resolution);
6899             break;
6900           case 3:          /* coulomb_neutral */
6901             SelectorMapCoulomb(G, sele0, ms, 0.0F, state, true, false, 1.0F);
6902             break;
6903           case 4:          /* coulomb_local */
6904             SelectorMapCoulomb(G, sele0, ms,
6905                                SettingGetGlobal_f(G, cSetting_coulomb_cutoff), state,
6906                                false, true, 2.0F);
6907             break;
6908           case 5:          /* gaussian_max */
6909             SelectorMapGaussian(G, sele0, ms, 0.0F, state, normalize, true, quiet, resolution);
6910             break;
6911           }
6912           if(!ms->Active)
6913             ObjectMapStatePurge(G, ms);
6914           else if(clamp_flag) {
6915             ObjectMapStateClamp(ms, clamp_floor, clamp_ceiling);
6916           }
6917 
6918           if(once_flag)
6919             break;
6920         }
6921 
6922       ObjectSetName((CObject *) objMap, name);
6923       ObjectMapUpdateExtents(objMap);
6924       if(isNew) {
6925         ExecutiveManageObject(G, (CObject *) objMap, -1, quiet);
6926       } else {
6927         ExecutiveDoZoom(G, (CObject *) objMap, false, zoom, true);
6928       }
6929       isNew = false;
6930       origObj = (CObject *) objMap;
6931     }
6932     SceneChanged(G);
6933 
6934     if(st_once_flag)
6935       break;
6936   }
6937   return {};
6938 }
6939 
6940 
6941 /*========================================================================*/
ExecutiveSculptIterateAll(PyMOLGlobals * G)6942 int ExecutiveSculptIterateAll(PyMOLGlobals * G)
6943 {
6944   int active = false;
6945   float center_array[8] = { 0.0F, 0.0F, 0.0F, 0.0F };
6946   float *center = center_array;
6947   CExecutive *I = G->Executive;
6948   SpecRec *rec = NULL;
6949   ObjectMolecule *objMol;
6950   CGOReset(G->DebugCGO);
6951 
6952   if(SettingGetGlobal_b(G, cSetting_sculpting)) {
6953     if(!SettingGetGlobal_b(G, cSetting_sculpt_auto_center))
6954       center = NULL;
6955 
6956     while(ListIterate(I->Spec, rec, next)) {
6957       if(rec->type == cExecObject) {
6958         if(rec->obj->type == cObjectMolecule) {
6959           objMol = (ObjectMolecule *) rec->obj;
6960           if(SettingGet_b(G, NULL, objMol->Setting, cSetting_sculpting)) {
6961             constexpr int state = -2; // current state
6962             ObjectMoleculeSculptIterate(objMol, state,
6963                                         SettingGet_i(G, NULL, objMol->Setting,
6964                                                      cSetting_sculpting_cycles), center);
6965             active = true;
6966           }
6967         }
6968       }
6969     }
6970     if(center && (center[3] > 1.0F)) {
6971       float pos[3];
6972       SceneGetCenter(G, pos);
6973       center[3] = 1.0F / center[3];
6974       scale3f(center, center[3], center);
6975       center[7] = 1.0F / center[7];
6976       scale3f(center + 4, center[7], center + 4);
6977       subtract3f(center, center + 4, center);
6978       add3f(pos, center, center);
6979       ExecutiveCenter(G, NULL, -1, true, false, center, true);
6980     }
6981   }
6982   if (active){
6983     EditorInvalidateShaderCGO(G);
6984   }
6985   return (active);
6986 }
6987 
6988 
6989 /*========================================================================*/
ExecutiveSculptIterate(PyMOLGlobals * G,const char * name,int state,int n_cycle)6990 float ExecutiveSculptIterate(PyMOLGlobals * G, const char *name, int state, int n_cycle)
6991 {
6992   CObject *obj = ExecutiveFindObjectByName(G, name);
6993   CExecutive *I = G->Executive;
6994   SpecRec *rec = NULL;
6995   ObjectMolecule *objMol;
6996   float total_strain = 0.0F;
6997 
6998   if(state < 0)
6999     state = SceneGetState(G);
7000 
7001   if(WordMatchExact(G, name, cKeywordAll, true)) {
7002     while(ListIterate(I->Spec, rec, next)) {
7003       if(rec->type == cExecObject) {
7004         if(rec->obj->type == cObjectMolecule) {
7005           objMol = (ObjectMolecule *) rec->obj;
7006           total_strain += ObjectMoleculeSculptIterate(objMol, state, n_cycle, NULL);
7007         }
7008       }
7009     }
7010   } else if(!obj) {
7011     PRINTFB(G, FB_Executive, FB_Errors)
7012       "Executive-Error: object %s not found.\n", name ENDFB(G);
7013   } else if(obj->type != cObjectMolecule) {
7014     PRINTFB(G, FB_Executive, FB_Errors)
7015       "Executive-Error: object %s is not a molecular object.\n", name ENDFB(G);
7016   } else {
7017     total_strain =
7018       ObjectMoleculeSculptIterate((ObjectMolecule *) obj, state, n_cycle, NULL);
7019   }
7020   return (total_strain);
7021 }
7022 
7023 
7024 /*========================================================================*/
ExecutiveSculptActivate(PyMOLGlobals * G,const char * name,int state,int match_state,int match_by_segment)7025 int ExecutiveSculptActivate(PyMOLGlobals * G, const char *name, int state, int match_state,
7026                             int match_by_segment)
7027 {
7028   CObject *obj = ExecutiveFindObjectByName(G, name);
7029   SpecRec *rec = NULL;
7030   ObjectMolecule *objMol;
7031   CExecutive *I = G->Executive;
7032   int ok = true;
7033   if(state < 0)
7034     state = SceneGetState(G);
7035 
7036   if(WordMatchExact(G, name, cKeywordAll, true)) {
7037     while(ListIterate(I->Spec, rec, next)) {
7038       if(rec->type == cExecObject) {
7039         if(rec->obj->type == cObjectMolecule) {
7040           objMol = (ObjectMolecule *) rec->obj;
7041           ObjectMoleculeSculptImprint(objMol, state, match_state, match_by_segment);
7042         }
7043       }
7044     }
7045   } else if(!obj) {
7046     PRINTFB(G, FB_Executive, FB_Errors)
7047       "Executive-Error: object %s not found.\n", name ENDFB(G);
7048     ok = false;
7049   } else if(obj->type != cObjectMolecule) {
7050     PRINTFB(G, FB_Executive, FB_Errors)
7051       "Executive-Error: object %s is not a molecular object.\n", name ENDFB(G);
7052     ok = false;
7053   } else {
7054     ObjectMoleculeSculptImprint((ObjectMolecule *) obj, state, match_state,
7055                                 match_by_segment);
7056   }
7057   return (ok);
7058 }
7059 
7060 
7061 /*========================================================================*/
ExecutiveSculptDeactivate(PyMOLGlobals * G,const char * name)7062 int ExecutiveSculptDeactivate(PyMOLGlobals * G, const char *name)
7063 {
7064   CObject *obj = ExecutiveFindObjectByName(G, name);
7065   SpecRec *rec = NULL;
7066   ObjectMolecule *objMol;
7067   CExecutive *I = G->Executive;
7068 
7069   int ok = true;
7070 
7071   if(WordMatchExact(G, name, cKeywordAll, true)) {
7072     while(ListIterate(I->Spec, rec, next)) {
7073       if(rec->type == cExecObject) {
7074         if(rec->obj->type == cObjectMolecule) {
7075           objMol = (ObjectMolecule *) rec->obj;
7076           ObjectMoleculeSculptClear(objMol);
7077         }
7078       }
7079     }
7080   } else if(!obj) {
7081     PRINTFB(G, FB_Executive, FB_Errors)
7082       "Executive-Error: object %s not found.\n", name ENDFB(G);
7083     ok = false;
7084   } else if(obj->type != cObjectMolecule) {
7085     PRINTFB(G, FB_Executive, FB_Errors)
7086       "Executive-Error: object %s is not a molecular object.\n", name ENDFB(G);
7087     ok = false;
7088   } else {
7089     ObjectMoleculeSculptClear((ObjectMolecule *) obj);
7090   }
7091   return (ok);
7092 }
7093 
7094 
7095 /*========================================================================*/
ExecutiveSetGeometry(PyMOLGlobals * G,const char * s1,int geom,int valence)7096 pymol::Result<> ExecutiveSetGeometry(
7097     PyMOLGlobals* G, const char* s1, int geom, int valence)
7098 {
7099   SETUP_SELE_DEFAULT(1);
7100 
7101   int count = 0;
7102   auto recs = pymol::make_list_adapter(G->Executive->Spec);
7103   for (auto& rec : recs) {
7104     auto obj = ExecutiveIsObjectType(rec, cObjectMolecule) ? rec.obj : nullptr;
7105     if(obj) {
7106       count += ObjectMoleculeSetGeometry(
7107         G, static_cast<ObjectMolecule*>(obj), sele1, geom, valence);
7108     }
7109   }
7110   if (count == 0) {
7111     return pymol::make_error("Empty selection.");
7112   }
7113   return {};
7114 }
7115 
7116 
7117 /*========================================================================*/
ExecutiveMapSetBorder(PyMOLGlobals * G,const char * name,float level,int state)7118 int ExecutiveMapSetBorder(PyMOLGlobals * G, const char *name, float level, int state)
7119 {
7120   CExecutive *I = G->Executive;
7121   int result = true;
7122   CTracker *I_Tracker = I->Tracker;
7123   int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
7124   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
7125   SpecRec *rec;
7126 
7127   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
7128     if(rec) {
7129       switch (rec->type) {
7130       case cExecObject:
7131         if(rec->obj->type == cObjectMap) {
7132           ObjectMap *obj = (ObjectMap *) rec->obj;
7133           result = ObjectMapSetBorder(obj, level, state);
7134 
7135           if(result) {
7136             ExecutiveInvalidateMapDependents(G, obj->Name);
7137           }
7138         }
7139         break;
7140       }
7141     }
7142   }
7143 
7144   TrackerDelList(I_Tracker, list_id);
7145   TrackerDelIter(I_Tracker, iter_id);
7146   return result;
7147 }
7148 
ExecutiveMapDouble(PyMOLGlobals * G,const char * name,int state)7149 pymol::Result<> ExecutiveMapDouble(PyMOLGlobals* G, const char* name, int state)
7150 {
7151   CExecutive *I = G->Executive;
7152   CTracker *I_Tracker = I->Tracker;
7153   int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
7154   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
7155   SpecRec *rec;
7156 
7157   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
7158     if(rec) {
7159       switch (rec->type) {
7160       case cExecObject:
7161         if(rec->obj->type == cObjectMap) {
7162           ObjectMap *obj = (ObjectMap *) rec->obj;
7163           auto result = ObjectMapDouble(obj, state);
7164           if(result) {
7165             ExecutiveInvalidateMapDependents(G, obj->Name);
7166           } else {
7167             return result;
7168           }
7169           if(result && rec->visible)
7170             SceneChanged(G);
7171         }
7172         break;
7173       }
7174     }
7175   }
7176 
7177   TrackerDelList(I_Tracker, list_id);
7178   TrackerDelIter(I_Tracker, iter_id);
7179   return {};
7180 }
7181 
ExecutiveMapHalve(PyMOLGlobals * G,const char * name,int state,int smooth)7182 pymol::Result<> ExecutiveMapHalve(
7183     PyMOLGlobals* G, const char* name, int state, int smooth)
7184 {
7185   CExecutive *I = G->Executive;
7186   CTracker *I_Tracker = I->Tracker;
7187   int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
7188   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
7189   SpecRec *rec;
7190 
7191   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
7192     if(rec) {
7193       switch (rec->type) {
7194       case cExecObject:
7195         if(rec->obj->type == cObjectMap) {
7196           ObjectMap *obj = (ObjectMap *) rec->obj;
7197           auto result = ObjectMapHalve(obj, state, smooth);
7198           if(result) {
7199             ExecutiveInvalidateMapDependents(G, obj->Name);
7200           } else {
7201             return result;
7202           }
7203           if(result && rec->visible)
7204             SceneChanged(G);
7205         }
7206         break;
7207       }
7208     }
7209   }
7210 
7211   TrackerDelList(I_Tracker, list_id);
7212   TrackerDelIter(I_Tracker, iter_id);
7213   return {};
7214 }
7215 
ExecutiveMapTrim(PyMOLGlobals * G,const char * name,const char * sele,float buffer,int map_state,int sele_state,int quiet)7216 pymol::Result<> ExecutiveMapTrim(PyMOLGlobals* G, const char* name,
7217     const char* sele, float buffer, int map_state, int sele_state, int quiet)
7218 {
7219   auto s1 = SelectorTmp2::make(G, sele);
7220   CExecutive *I = G->Executive;
7221   float mn[3], mx[3];
7222   if(ExecutiveGetExtent(G, s1->getName(), mn, mx, true, sele_state, false)) {
7223     CTracker *I_Tracker = I->Tracker;
7224     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
7225     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
7226     SpecRec *rec;
7227 
7228     {
7229       int a;
7230       float t;
7231       for(a = 0; a < 3; a++) {
7232         mn[a] -= buffer;
7233         mx[a] += buffer;
7234         if(mn[a] > mx[a]) {
7235           t = mn[a];
7236           mn[a] = mx[a];
7237           mx[a] = t;
7238         }
7239       }
7240     }
7241     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
7242       if(rec) {
7243         switch (rec->type) {
7244         case cExecObject:
7245           if(rec->obj->type == cObjectMap) {
7246             ObjectMap *obj = (ObjectMap *) rec->obj;
7247             auto result = ObjectMapTrim(obj, map_state, mn, mx, quiet);
7248             if(result) {
7249               ExecutiveInvalidateMapDependents(G, obj->Name);
7250             } else {
7251               return result;
7252             }
7253             if(result && rec->visible)
7254               SceneChanged(G);
7255           }
7256           break;
7257         }
7258       }
7259     }
7260     TrackerDelList(I_Tracker, list_id);
7261     TrackerDelIter(I_Tracker, iter_id);
7262   }
7263   return {};
7264 }
7265 
ExecutiveSelectRect(PyMOLGlobals * G,BlockRect * rect,int mode)7266 void ExecutiveSelectRect(PyMOLGlobals * G, BlockRect * rect, int mode)
7267 {
7268   Multipick smp;
7269   OrthoLineType buffer, buf2;
7270   char selName[WordLength] = cLeftButSele;
7271   char prefix[3] = "";
7272   int log_box = 0;
7273   int logging;
7274   const char *sel_mode_kw = "";
7275 
7276   logging = SettingGetGlobal_i(G, cSetting_logging);
7277   if(logging)
7278     log_box = SettingGetGlobal_b(G, cSetting_log_box_selections);
7279   /*  if(logging==cPLog_pml)
7280      strcpy(prefix,"_ "); */
7281   smp.x = rect->left;
7282   smp.y = rect->bottom;
7283   smp.w = rect->right - rect->left;
7284   smp.h = rect->top - rect->bottom;
7285   SceneMultipick(G, &smp);
7286   if (!smp.picked.empty()) {
7287     SelectorCreate(G, cTempRectSele, NULL, NULL, 1, &smp);
7288     if(log_box)
7289       SelectorLogSele(G, cTempRectSele);
7290     switch (mode) {
7291     case cButModeRect:
7292       if(mode == cButModeRect) {
7293         SelectorCreate(G, cLeftButSele, cTempRectSele, NULL, 1, NULL);
7294         if(log_box) {
7295           sprintf(buf2, "%scmd.select(\"%s\",\"%s\",enable=1)\n", prefix, cLeftButSele,
7296                   cTempRectSele);
7297           PLog(G, buf2, cPLog_no_flush);
7298         }
7299       }
7300       break;
7301     case cButModeSeleSetBox:
7302     case cButModeSeleAddBox:
7303     case cButModeSeleSubBox:
7304       ExecutiveGetActiveSeleName(G, selName, true, SettingGetGlobal_i(G, cSetting_logging));
7305       sel_mode_kw = SceneGetSeleModeKeyword(G);
7306       /* intentional omission of break! */
7307     case cButModeRectAdd:
7308     case cButModeRectSub:
7309       if(SelectorIndexByName(G, selName) >= 0) {
7310         if((mode == cButModeRectAdd) || (mode == cButModeSeleAddBox)) {
7311           sprintf(buffer, "(?%s or %s(%s))", selName, sel_mode_kw, cTempRectSele);
7312           SelectorCreate(G, selName, buffer, NULL, 0, NULL);
7313           if(log_box) {
7314             sprintf(buf2, "%scmd.select(\"%s\",\"(%s)\",enable=1)\n", prefix, selName,
7315                     buffer);
7316             PLog(G, buf2, cPLog_no_flush);
7317           }
7318         } else if((mode == cButModeRectSub) || (mode == cButModeSeleSubBox)) {
7319           sprintf(buffer, "(%s(?%s) and not %s(%s))", sel_mode_kw, selName, sel_mode_kw,
7320                   cTempRectSele);
7321           SelectorCreate(G, selName, buffer, NULL, 0, NULL);
7322           if(log_box) {
7323             sprintf(buf2, "%scmd.select(\"%s\",\"%s\",enable=1)\n", prefix, selName,
7324                     buffer);
7325             PLog(G, buf2, cPLog_no_flush);
7326           }
7327         } else {
7328           sprintf(buffer, "(%s(?%s))", sel_mode_kw, cTempRectSele);
7329           SelectorCreate(G, selName, buffer, NULL, 0, NULL);
7330           if(log_box) {
7331             sprintf(buf2, "%scmd.select(\"%s\",\"%s\",enable=1)\n", prefix, selName,
7332                     buffer);
7333             PLog(G, buf2, cPLog_no_flush);
7334           }
7335         }
7336       } else {
7337         if((mode == cButModeRectAdd) || (mode == cButModeSeleAddBox)) {
7338           sprintf(buffer, "%s(?%s)", sel_mode_kw, cTempRectSele);
7339           SelectorCreate(G, selName, buffer, NULL, 0, NULL);
7340           if(log_box) {
7341             sprintf(buf2, "%scmd.select(\"%s\",\"%s\",enable=1)\n", prefix, selName,
7342                     buffer);
7343             PLog(G, buf2, cPLog_no_flush);
7344           }
7345         } else if((mode == cButModeRectSub) || (mode == cButModeSeleSubBox)) {
7346           SelectorCreate(G, selName, "(none)", NULL, 0, NULL);
7347           if(log_box) {
7348             sprintf(buf2, "%scmd.select(\"%s\",\"(none)\",enable=1)\n", prefix, selName);
7349             PLog(G, buf2, cPLog_no_flush);
7350           }
7351         } else {
7352           sprintf(buffer, "%s(?%s)", sel_mode_kw, cTempRectSele);
7353           SelectorCreate(G, selName, buffer, NULL, 0, NULL);
7354           if(log_box) {
7355             sprintf(buf2, "%scmd.select(\"%s\",\"%s\",enable=1)\n", prefix, selName,
7356                     buffer);
7357             PLog(G, buf2, cPLog_no_flush);
7358           }
7359         }
7360       }
7361       if(SettingGetGlobal_b(G, cSetting_auto_show_selections)) {
7362         ExecutiveSetObjVisib(G, selName, true, false);
7363       }
7364       break;
7365     }
7366     if(log_box) {
7367       sprintf(buf2, "%scmd.delete(\"%s\")\n", prefix, cTempRectSele);
7368       PLog(G, buf2, cPLog_no_flush);
7369       PLogFlush(G);
7370     }
7371     ExecutiveDelete(G, cTempRectSele);
7372     WizardDoSelect(G, selName);
7373   } else {
7374     switch (mode) {
7375     case cButModeSeleSetBox:
7376       {
7377         OrthoLineType buf2;
7378         ObjectNameType name;
7379 
7380         if(ExecutiveGetActiveSeleName(G, name, false, SettingGetGlobal_i(G, cSetting_logging))) {
7381           ExecutiveSetObjVisib(G, name, 0, false);
7382           if(SettingGetGlobal_i(G, cSetting_logging)) {
7383             sprintf(buf2, "cmd.disable('%s')\n", name);
7384             PLog(G, buf2, cPLog_no_flush);
7385           }
7386         }
7387       }
7388       break;
7389     }
7390   }
7391 }
7392 
ExecutiveTranslateAtom(PyMOLGlobals * G,const char * sele,const float * v,int state,int mode,int log)7393 pymol::Result<> ExecutiveTranslateAtom(
7394     PyMOLGlobals* G, const char* sele, const float* v, int state, int mode, int log)
7395 {
7396   SETUP_SELE(sele, tmpsele0, sele0);
7397 
7398   {
7399     auto obj0 = SelectorGetSingleObjectMolecule(G, sele0);
7400     if(!obj0) {
7401       return pymol::make_error("Selection isn't a single atom.");
7402     } else {
7403       auto i0 = ObjectMoleculeGetAtomIndex(obj0, sele0);
7404       if(i0 < 0) {
7405         return pymol::make_error("Selection isn't a single atom.");
7406       } else {
7407         ObjectMoleculeMoveAtom(obj0, state, i0, v, mode, log);
7408       }
7409     }
7410   }
7411   return {};
7412 }
7413 
ExecutiveCombineObjectTTT(PyMOLGlobals * G,const char * name,const float * ttt,int reverse_order,int store)7414 pymol::Result<> ExecutiveCombineObjectTTT(PyMOLGlobals* G,
7415     const char* name, const float* ttt, int reverse_order, int store)
7416 {
7417   CExecutive *I = G->Executive;
7418   if((!name)||(!name[0])||(!strcmp(name,cKeywordAll))||(!strcmp(name,cKeywordSame))) {
7419     SpecRec *rec = NULL;
7420     while(ListIterate(I->Spec, rec, next)) {
7421       switch(rec->type) {
7422       case cExecObject:
7423         {
7424           CObject *obj = rec->obj;
7425           if((ObjectGetSpecLevel(rec->obj,0)>=0)||(!strcmp(name,cKeywordAll))) {
7426             ObjectCombineTTT(obj, ttt, reverse_order, store);
7427             obj->invalidate(cRepNone, cRepInvExtents, -1);
7428           }
7429         }
7430         break;
7431       }
7432     }
7433     if(store && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
7434       ExecutiveMotionReinterpolate(G);
7435     }
7436   } else { /* pattern */
7437     CTracker *I_Tracker = I->Tracker;
7438     SpecRec *rec = NULL;
7439     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
7440     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
7441     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
7442       if(rec) {
7443 
7444         switch (rec->type) {
7445         case cExecObject:
7446           {
7447             CObject *obj = rec->obj;
7448             ObjectCombineTTT(obj, ttt, reverse_order, store);
7449             obj->invalidate(cRepNone, cRepInvExtents, -1);
7450           }
7451           break;
7452         }
7453       }
7454     }
7455     TrackerDelList(I_Tracker, list_id);
7456     TrackerDelIter(I_Tracker, iter_id);
7457     if(store && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
7458       ExecutiveMotionReinterpolate(G);
7459     }
7460 
7461   }
7462   SceneInvalidate(G);
7463   return {};
7464 }
7465 
ExecutiveTranslateObjectTTT(PyMOLGlobals * G,const char * name,const float * trans,int store,int quiet)7466 pymol::Result<> ExecutiveTranslateObjectTTT(PyMOLGlobals * G, const char *name, const float *trans, int store, int quiet)
7467 {
7468   CExecutive *I = G->Executive;
7469   if((!name)||(!name[0])||(!strcmp(name,cKeywordAll))||(!strcmp(name,cKeywordSame))) {
7470     SpecRec *rec = NULL;
7471     while(ListIterate(I->Spec, rec, next)) {
7472       switch(rec->type) {
7473       case cExecObject:
7474         {
7475           CObject *obj = rec->obj;
7476           if((ObjectGetSpecLevel(rec->obj,0)>=0)||(!strcmp(name,cKeywordAll))) {
7477             ObjectTranslateTTT(obj, trans, store);
7478             obj->invalidate(cRepNone, cRepInvExtents, -1);
7479           }
7480         }
7481         break;
7482       }
7483     }
7484     if(store && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
7485       ExecutiveMotionReinterpolate(G);
7486     }
7487   } else { /* pattern */
7488     CTracker *I_Tracker = I->Tracker;
7489     SpecRec *rec = NULL;
7490     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
7491     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
7492     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
7493       if(rec) {
7494         switch (rec->type) {
7495         case cExecObject:
7496           {
7497             CObject *obj = rec->obj;
7498             ObjectTranslateTTT(obj, trans, store);
7499             obj->invalidate(cRepNone, cRepInvExtents, -1);
7500           }
7501           break;
7502         }
7503       }
7504     }
7505     TrackerDelList(I_Tracker, list_id);
7506     TrackerDelIter(I_Tracker, iter_id);
7507     if(store && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
7508       ExecutiveMotionReinterpolate(G);
7509     }
7510   }
7511   SceneInvalidate(G);
7512   return {};
7513 }
7514 
ExecutiveSetObjectTTT(PyMOLGlobals * G,const char * name,const float * ttt,int state,int quiet,int store)7515 pymol::Result<> ExecutiveSetObjectTTT(PyMOLGlobals * G, const char *name, const float *ttt, int state, int quiet, int store)
7516 {
7517   CExecutive *I = G->Executive;
7518   if((!name)||(!name[0])||(!strcmp(name,cKeywordAll))||(!strcmp(name,cKeywordSame))) {
7519     SpecRec *rec = NULL;
7520     while(ListIterate(I->Spec, rec, next)) {
7521       switch(rec->type) {
7522       case cExecObject:
7523         {
7524           CObject *obj = rec->obj;
7525           if((ObjectGetSpecLevel(rec->obj,0)>=0)||(!strcmp(name,cKeywordAll))) {
7526             ObjectSetTTT(obj, ttt, state, store);
7527             obj->invalidate(cRepNone, cRepInvExtents, -1);
7528           }
7529         }
7530         break;
7531       }
7532     }
7533     if(store && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
7534       ExecutiveMotionReinterpolate(G);
7535     }
7536   } else { /* pattern */
7537     CTracker *I_Tracker = I->Tracker;
7538     SpecRec *rec = NULL;
7539     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
7540     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
7541     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
7542       if(rec) {
7543 
7544         switch (rec->type) {
7545         case cExecObject:
7546           {
7547             CObject *obj = rec->obj;
7548              ObjectSetTTT(obj, ttt, state, store);
7549              obj->invalidate(cRepNone, cRepInvExtents, -1);
7550           }
7551           break;
7552         }
7553       }
7554     }
7555     TrackerDelList(I_Tracker, list_id);
7556     TrackerDelIter(I_Tracker, iter_id);
7557     if(store && SettingGetGlobal_i(G,cSetting_movie_auto_interpolate)) {
7558       ExecutiveMotionReinterpolate(G);
7559     }
7560   }
7561   SceneInvalidate(G);
7562   return {};
7563 }
7564 
ExecutiveGetObjectTTT(PyMOLGlobals * G,const char * name,const float ** ttt,int state,int quiet)7565 int ExecutiveGetObjectTTT(PyMOLGlobals * G, const char *name, const float **ttt, int state, int quiet)
7566 {
7567   CObject *obj = ExecutiveFindObjectByName(G, name);
7568   int ok = true;
7569 
7570   if(!obj) {
7571     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
7572       "Error: object %s not found.\n", name ENDFB(G);
7573     ok = false;
7574   } else {
7575     ObjectGetTTT(obj, ttt, state);
7576   }
7577   return (ok);
7578 }
7579 
ExecutiveTransformSelection(PyMOLGlobals * G,int state,const char * s1,int log,const float * ttt,int homogenous)7580 pymol::Result<> ExecutiveTransformSelection(PyMOLGlobals* G,
7581     int state, const char* s1, int log, const float* ttt, int homogenous)
7582 {
7583   SETUP_SELE_DEFAULT(1);
7584 
7585   auto vla = pymol::vla_take_ownership(SelectorGetObjectMoleculeVLA(G, sele1));
7586   if(!vla)
7587     return pymol::make_error("Could not find selection");
7588   for(auto obj : vla) {
7589     ObjectMoleculeTransformSelection(obj, state, sele1, ttt, log,
7590         tmpsele1->getName(), homogenous, true);
7591   }
7592   SceneInvalidate(G);
7593   return {};
7594 }
7595 
ExecutiveTransformObjectSelection2(PyMOLGlobals * G,CObject * obj,int state,const char * s1,int log,const float * matrix,int homogenous,int global)7596 pymol::Result<> ExecutiveTransformObjectSelection2(PyMOLGlobals* G,
7597     CObject* obj, int state, const char* s1, int log, const float* matrix,
7598     int homogenous, int global)
7599 {
7600   int ok = true;
7601 
7602   switch (obj->type) {
7603   case cObjectMolecule:
7604     {
7605       int sele = -1;
7606       ObjectMolecule *objMol = (ObjectMolecule *) obj;
7607 
7608       if(s1 && s1[0]) {
7609         sele = SelectorIndexByName(G, s1);
7610         if(sele < 0)
7611           ok = false;
7612       }
7613       if(!ok) {
7614         return pymol::make_error("Selection object ", s1, " not found.");
7615       } else {
7616         ObjectMoleculeTransformSelection(objMol, state, sele, matrix, log, s1, homogenous,
7617                                          global);
7618       }
7619       EditorDihedralInvalid(G, objMol);
7620       SceneInvalidate(G);
7621     }
7622     break;
7623   default:
7624     if (auto* objstate = obj->getObjectState(state)) {
7625       double matrixd[116];
7626       if(homogenous) {
7627         convert44f44d(matrix, matrixd);
7628       } else {
7629         convertTTTfR44d(matrix, matrixd);
7630       }
7631       ObjectStateTransformMatrix(objstate, matrixd);
7632       obj->invalidate(cRepNone, cRepInvExtents, state);
7633     }
7634     break;
7635   }
7636   return {};
7637 }
7638 
ExecutiveTransformObjectSelection(PyMOLGlobals * G,const char * name,int state,const char * s1,int log,const float * matrix,int homogenous,int global)7639 pymol::Result<> ExecutiveTransformObjectSelection(PyMOLGlobals* G,
7640     const char* name, int state, const char* s1, int log, const float* matrix,
7641     int homogenous, int global)
7642 {
7643   CObject *obj = ExecutiveFindObjectByName(G, name);
7644   if(obj) {
7645     return ExecutiveTransformObjectSelection2(G, obj, state, s1, log, matrix, homogenous,
7646                                               global);
7647   }
7648   return {};
7649 }
7650 
ExecutiveValidName(PyMOLGlobals * G,const char * name)7651 int ExecutiveValidName(PyMOLGlobals * G, const char *name)
7652 {
7653   if(!ExecutiveFindSpec(G, name)) {
7654     int ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
7655 
7656     if(!WordMatchExact(G, name, cKeywordAll, ignore_case))
7657       if(!WordMatchExact(G, name, cKeywordSame, ignore_case))
7658         if(!WordMatchExact(G, name, cKeywordCenter, ignore_case))
7659           if(!WordMatchExact(G, name, cKeywordOrigin, ignore_case))
7660             return false;
7661   }
7662   return true;
7663 }
7664 
ExecutivePhiPsi(PyMOLGlobals * G,const char * s1,ObjectMolecule *** objVLA,int ** iVLA,float ** phiVLA,float ** psiVLA,int state)7665 int ExecutivePhiPsi(PyMOLGlobals * G, const char *s1, ObjectMolecule *** objVLA, int **iVLA,
7666                     float **phiVLA, float **psiVLA, int state)
7667 {
7668   SelectorTmp tmpsele1(G, s1);
7669   int sele1 = tmpsele1.getIndex();
7670 
7671   int result = false;
7672   ObjectMoleculeOpRec op1;
7673   if(sele1 >= 0) {
7674     ObjectMoleculeOpRecInit(&op1);
7675     op1.i1 = 0;
7676     op1.i2 = state;
7677     op1.obj1VLA = VLAlloc(ObjectMolecule *, 1000);
7678     op1.i1VLA = VLAlloc(int, 1000);
7679     op1.f1VLA = VLAlloc(float, 1000);
7680     op1.f2VLA = VLAlloc(float, 1000);
7681     op1.code = OMOP_PhiPsi;
7682     ExecutiveObjMolSeleOp(G, sele1, &op1);
7683     result = op1.i1;
7684     VLASize(op1.i1VLA, int, op1.i1);
7685     VLASize(op1.obj1VLA, ObjectMolecule *, op1.i1);
7686     VLASize(op1.f1VLA, float, op1.i1);
7687     VLASize(op1.f2VLA, float, op1.i1);
7688     *iVLA = op1.i1VLA;
7689     *objVLA = op1.obj1VLA;
7690     *phiVLA = op1.f1VLA;
7691     *psiVLA = op1.f2VLA;
7692   } else {
7693     *objVLA = NULL;
7694     *iVLA = NULL;
7695     *phiVLA = NULL;
7696     *psiVLA = NULL;
7697   }
7698   return (result);
7699 }
7700 
ExecutiveAlign(PyMOLGlobals * G,const char * s1,const char * s2,const char * mat_file,float gap,float extend,int max_gap,int max_skip,float cutoff,int cycles,int quiet,const char * oname,int state1,int state2,ExecutiveRMSInfo * rms_info,int transform,int reset,float seq_wt,float radius,float scale,float base,float coord_wt,float expect,int window,float ante)7701 int ExecutiveAlign(PyMOLGlobals * G, const char *s1, const char *s2, const char *mat_file, float gap,
7702                    float extend, int max_gap, int max_skip, float cutoff, int cycles,
7703                    int quiet, const char *oname, int state1, int state2,
7704                    ExecutiveRMSInfo * rms_info, int transform, int reset, float seq_wt,
7705                    float radius, float scale, float base, float coord_wt, float expect,
7706                    int window, float ante)
7707 {
7708   int sele1 = SelectorIndexByName(G, s1);
7709   int sele2 = SelectorIndexByName(G, s2);
7710   int *vla1 = NULL;
7711   int *vla2 = NULL;
7712   int na, nb;
7713   int c;
7714   int ok = true;
7715   int use_sequence = (mat_file && mat_file[0] && (seq_wt != 0.0F));
7716   int use_structure = (seq_wt >= 0.0F); /* negative seq_wt means sequence only! */
7717   ObjectMolecule *mobile_obj = NULL;
7718   CMatch *match = NULL;
7719 
7720   if(!use_structure)
7721     window = 0;
7722 
7723   if((scale == 0.0F) && (seq_wt == 0.0F) && (ante < 0.0F) && window)
7724     ante = window;
7725 
7726   if(ante < 0.0F)
7727     ante = 0.0F;
7728 
7729   if((sele1 >= 0)) {
7730     mobile_obj = SelectorGetSingleObjectMolecule(G, sele1);
7731     if(!mobile_obj) {
7732       ok = false;
7733       PRINTFB(G, FB_Executive, FB_Errors)
7734         " ExecutiveAlign: mobile selection must derive from one object only.\n" ENDFB(G);
7735     }
7736   }
7737   if(ok && (sele1 >= 0) && (sele2 >= 0) && rms_info) {
7738     vla1 = SelectorGetResidueVLA(G, sele1, use_structure, NULL);
7739     vla2 = SelectorGetResidueVLA(G, sele2, use_structure, mobile_obj);
7740     if(vla1 && vla2) {
7741       na = VLAGetSize(vla1) / 3;
7742       nb = VLAGetSize(vla2) / 3;
7743       if(na && nb) {
7744         match = MatchNew(G, na, nb, window);
7745         if(match) {
7746           if(use_sequence) {
7747             if(ok)
7748               ok = MatchResidueToCode(match, vla1, na);
7749             if(ok)
7750               ok = MatchResidueToCode(match, vla2, nb);
7751             if(ok)
7752               ok = MatchMatrixFromFile(match, mat_file, quiet);
7753             if(ok)
7754               ok = MatchPreScore(match, vla1, na, vla2, nb, quiet);
7755           }
7756           if(use_structure) {
7757 	    /* avoid degenerate alignments */
7758 	    ok = ((na>1) && (nb>1) && ok);
7759             if(ok) {
7760               ok = SelectorResidueVLAsTo3DMatchScores(G, match,
7761                                                       vla1, na, state1,
7762                                                       vla2, nb, state2, seq_wt,
7763                                                       radius, scale, base,
7764                                                       coord_wt, expect);
7765 	    } else {
7766 	      PRINTFB(G, FB_Executive, FB_Errors)
7767 		" ExecutiveAlign: No alignment found.\n" ENDFB(G);
7768 	    }
7769 	  }
7770           if(ok)
7771             ok = MatchAlign(match, gap, extend, max_gap, max_skip, quiet, window, ante);
7772           if(ok) {
7773             rms_info->raw_alignment_score = match->score;
7774             rms_info->n_residues_aligned = match->n_pair;
7775             if(match->pair) {
7776 
7777               c = SelectorCreateAlignments(G, match->pair,
7778                                            sele1, vla1, sele2, vla2,
7779                                            "_align1", "_align2", false, false);
7780 
7781               if(c) {
7782                 int mode = 2;
7783                 if(!quiet) {
7784                   PRINTFB(G, FB_Executive, FB_Actions)
7785                     " %s: %d atoms aligned.\n", __func__, c ENDFB(G);
7786                 }
7787                 if(oname && oname[0] && reset)
7788                   ExecutiveDelete(G, oname);
7789                 if(!transform)
7790                   mode = 1;
7791                 ok = ExecutiveRMS(G, "_align1", "_align2", mode, cutoff, cycles,
7792                                   quiet, oname, state1, state2, false, 0, rms_info);
7793               } else {
7794                 if(!quiet) {
7795                   PRINTFB(G, FB_Executive, FB_Actions)
7796                     " ExecutiveAlign-Error: atomic alignment failed (mismatched identifiers?).\n"
7797                     ENDFB(G);
7798                 }
7799                 ok = false;
7800               }
7801             }
7802           }
7803           MatchFree(match);
7804         }
7805       } else {
7806         ok = false;
7807         PRINTFB(G, FB_Executive, FB_Errors)
7808           " ExecutiveAlign: invalid selections for alignment.\n" ENDFB(G);
7809       }
7810     }
7811     ExecutiveUpdateCoordDepends(G, mobile_obj); //Updates dynamic_measures - see PYMOL-3090
7812   }
7813 
7814   VLAFreeP(vla1);
7815   VLAFreeP(vla2);
7816   return ok;
7817 }
7818 
7819 /**
7820  * Implementation of `cmd.find_pairs()`
7821  *
7822  * @param s1 atom selection expression
7823  * @param s2 atom selection expression
7824  * @param state1 object state
7825  * @param state2 object state
7826  * @param mode 0: find all pairs, 1: find hydrogen bonds
7827  * @param cutoff maximum distance in Angstrom
7828  * @param h_angle hydrogen bond angle cutoff for mode=1
7829  * @param[out] indexVLA pairs of atom indices (0-based)
7830  * @param[out] objVLA pairs of objects
7831  * @return number of pairs
7832  */
7833 pymol::Result<int>
ExecutivePairIndices(PyMOLGlobals * G,const char * s1,const char * s2,int state1,int state2,int mode,float cutoff,float h_angle,int ** indexVLA,ObjectMolecule *** objVLA)7834 ExecutivePairIndices(PyMOLGlobals * G, const char *s1, const char *s2, int state1, int state2,
7835                          int mode, float cutoff, float h_angle,
7836                          int **indexVLA, ObjectMolecule *** objVLA)
7837 {
7838   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
7839   SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
7840 
7841   return SelectorGetPairIndices(G, sele1, state1, sele2, state2,
7842                                     mode, cutoff, h_angle, indexVLA, objVLA);
7843 }
7844 
ExecutiveCartoon(PyMOLGlobals * G,int type,const char * s1)7845 pymol::Result<int> ExecutiveCartoon(PyMOLGlobals* G, int type, const char* s1)
7846 {
7847   SETUP_SELE_DEFAULT(1);
7848 
7849   ObjectMoleculeOpRec op1;
7850 
7851   ObjectMoleculeOpRecInit(&op1);
7852   op1.i2 = 0;
7853   {
7854     op1.code = OMOP_Cartoon;
7855     op1.i1 = type;
7856     op1.i2 = 0;
7857     op1.i3 = 0;
7858     ExecutiveObjMolSeleOp(G, sele1, &op1);
7859     if (op1.i3>0){
7860         op1.code = OMOP_INVA;
7861         op1.i1 = cRepCartoonBit;
7862         op1.i2 = cRepInvRep;
7863         ExecutiveObjMolSeleOp(G, sele1, &op1);
7864     }
7865   }
7866   return (op1.i2);
7867 }
7868 
7869 
7870 /*========================================================================*/
ExecutiveGetVertexVLA(PyMOLGlobals * G,const char * s1,int state)7871 float *ExecutiveGetVertexVLA(PyMOLGlobals * G, const char *s1, int state)
7872 {
7873   /* returns NULL if none found */
7874 
7875   float *result = NULL;
7876   ObjectMoleculeOpRec op1;
7877   int sele1;
7878   sele1 = SelectorIndexByName(G, s1);
7879   if(sele1 >= 0) {
7880     ObjectMoleculeOpRecInit(&op1);
7881     op1.nvv1 = 0;
7882     op1.vv1 = VLAlloc(float, 1000);
7883     if(state >= 0) {
7884       op1.cs1 = state;
7885       op1.code = OMOP_SingleStateVertices;
7886     } else {
7887       op1.code = OMOP_VERT;
7888     }
7889     ExecutiveObjMolSeleOp(G, sele1, &op1);
7890     VLASize(op1.vv1, float, op1.nvv1 * 3);
7891     result = op1.vv1;
7892   }
7893   return (result);
7894 }
7895 
7896 
7897 /*========================================================================*/
7898 #ifndef _PYMOL_NOPY
ExecutiveGetSettingOfType(PyMOLGlobals * G,int index,const char * object,int state,int type)7899 PyObject *ExecutiveGetSettingOfType(PyMOLGlobals * G, int index,
7900                                     const char *object, int state, int type)
7901 {
7902   /* Assumes blocked Python interpreter */
7903   PyObject *result = NULL;
7904   CObject *obj = NULL;
7905   CSetting **handle = NULL, *set_ptr1 = NULL, *set_ptr2 = NULL;
7906 
7907   if(object && object[0]) {
7908       obj = ExecutiveFindObjectByName(G, object);
7909       if(!obj)
7910         return PyErr_Format(P_CmdException, "object \"%s\" not found", object);
7911     handle = obj->getSettingHandle(-1);
7912     if(handle)
7913       set_ptr1 = *handle;
7914     if(state >= 0) {
7915       handle = obj->getSettingHandle(state);
7916       if(handle)
7917         set_ptr2 = *handle;
7918       else {
7919         return PyErr_Format(
7920             P_CmdException, "object \"%s\" lacks state %d", object, state + 1);
7921       }
7922     }
7923   }
7924   {
7925     switch (type) {
7926     case cSetting_boolean:
7927       {
7928         int value = SettingGet_b(G, set_ptr2, set_ptr1, index);
7929         result = PyBool_FromLong(value);
7930       }
7931       break;
7932     case cSetting_int:
7933       {
7934         int value = SettingGet_i(G, set_ptr2, set_ptr1, index);
7935         result = Py_BuildValue("i", value);
7936       }
7937       break;
7938     case cSetting_float:
7939       {
7940         float value = SettingGet_f(G, set_ptr2, set_ptr1, index);
7941         result = PyFloat_FromDouble(pymol::pretty_f2d(value));
7942       }
7943       break;
7944     case cSetting_float3:
7945       {
7946         const float * value = SettingGet_3fv(G, set_ptr2, set_ptr1, index);
7947         if (value) {
7948           result = Py_BuildValue("fff", pymol::pretty_f2d(value[0]),
7949               pymol::pretty_f2d(value[1]), pymol::pretty_f2d(value[2]));
7950         } else {
7951           PyErr_SetNone(PyExc_ValueError);
7952         }
7953       }
7954       break;
7955     case cSetting_color:
7956       {
7957         int value = SettingGet_color(G, set_ptr2, set_ptr1, index);
7958         result = Py_BuildValue("i", value);
7959       }
7960       break;
7961     case cSetting_string:
7962       {
7963         OrthoLineType buffer = "";
7964         result = Py_BuildValue("s",
7965             SettingGetTextPtr(G, set_ptr2, set_ptr1, index, buffer));
7966       }
7967       break;
7968     case cSetting_tuple:
7969       result = SettingGetTuple(G, set_ptr2, set_ptr1, index);
7970       break;
7971     default:
7972       PyErr_Format(PyExc_ValueError, "invalid setting type %d", type);
7973       break;
7974     }
7975   }
7976   return (result);
7977 }
7978 #endif
7979 
7980 
7981 /*========================================================================*/
ExecutiveSetLastObjectEdited(PyMOLGlobals * G,CObject * o)7982 void ExecutiveSetLastObjectEdited(PyMOLGlobals * G, CObject * o)
7983 {
7984   CExecutive *I = G->Executive;
7985   I->LastEdited = o;
7986 }
7987 
7988 
7989 /*========================================================================*/
ExecutiveGetLastObjectEdited(PyMOLGlobals * G)7990 CObject *ExecutiveGetLastObjectEdited(PyMOLGlobals * G)
7991 {
7992   CExecutive *I = G->Executive;
7993   return (I->LastEdited);
7994 }
7995 
7996 
7997 /*========================================================================*/
ExecutiveSaveUndo(PyMOLGlobals * G,const char * s1,int state)7998 int ExecutiveSaveUndo(PyMOLGlobals * G, const char *s1, int state)
7999   {
8000   int sele1;
8001   ObjectMoleculeOpRec op1;
8002 
8003   if(state < 0)
8004     state = SceneGetState(G);
8005   sele1 = SelectorIndexByName(G, s1);
8006   ObjectMoleculeOpRecInit(&op1);
8007   op1.i2 = 0;
8008   if(sele1 >= 0) {
8009     op1.code = OMOP_SaveUndo;
8010     op1.i1 = state;
8011     ExecutiveObjMolSeleOp(G, sele1, &op1);
8012   }
8013   return (op1.i2);
8014 }
8015 
8016 
8017 /*========================================================================*/
ExecutiveSetTitle(PyMOLGlobals * G,const char * name,int state,const char * text)8018 pymol::Result<> ExecutiveSetTitle(PyMOLGlobals * G, const char *name, int state, const char *text)
8019 {
8020   ObjectMolecule *obj;
8021   obj = ExecutiveFindObjectMoleculeByName(G, name);
8022   if(!obj) {
8023     return pymol::make_error("Object ", name, " not found.");
8024   } else {
8025     auto res = ObjectMoleculeSetStateTitle(obj, state, text);
8026     if(!res){
8027       return res;
8028     }
8029   }
8030   SceneDirty(G);
8031   return {};
8032 }
8033 
8034 
8035 /*========================================================================*/
ExecutiveGetTitle(PyMOLGlobals * G,const char * name,int state)8036 const char *ExecutiveGetTitle(PyMOLGlobals * G, const char *name, int state)
8037 {
8038   ObjectMolecule *obj;
8039   obj = ExecutiveFindObjectMoleculeByName(G, name);
8040   if(!obj) {
8041     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
8042       "Error: object %s not found.\n", name ENDFB(G);
8043     return NULL;
8044   }
8045   return ObjectMoleculeGetStateTitle(obj, state);
8046 }
8047 
8048 
8049 /*========================================================================*/
ExecutiveHideSelections(PyMOLGlobals * G)8050 void ExecutiveHideSelections(PyMOLGlobals * G)
8051 {
8052   CExecutive *I = G->Executive;
8053   SpecRec *rec = NULL;
8054 
8055   while(ListIterate(I->Spec, rec, next)) {
8056     if(rec->type == cExecSelection) {
8057       if(rec->visible) {
8058         rec->visible = false;
8059         SceneInvalidate(G);
8060         SeqDirty(G);
8061 	ReportEnabledChange(G, rec);
8062       }
8063     }
8064   }
8065 }
8066 
ExecutiveInvalidateSelectionIndicators(PyMOLGlobals * G)8067 void ExecutiveInvalidateSelectionIndicators(PyMOLGlobals *G){
8068   CExecutive *I = G->Executive;
8069   ExecutiveInvalidateSelectionIndicatorsCGO(G);
8070   I->selectorTextureSize = 0;
8071 }
8072 
ExecutiveInvalidateSelectionIndicatorsCGO(PyMOLGlobals * G)8073 void ExecutiveInvalidateSelectionIndicatorsCGO(PyMOLGlobals *G){
8074   CExecutive *I = G->Executive;
8075   SpecRec *rec = NULL;
8076   if (I){
8077     if (I->selIndicatorsCGO){
8078       CGOFree(I->selIndicatorsCGO);
8079       I->selIndicatorsCGO = 0;
8080     }
8081     while(ListIterate(I->Spec, rec, next)) {
8082       if(rec->type == cExecObject) {
8083 	  CGOFree(rec->gridSlotSelIndicatorsCGO);
8084       }
8085     }
8086   }
8087 }
8088 
ExecutiveRegenerateTextureForSelector(PyMOLGlobals * G,int round_points,int * widths_arg)8089 static void ExecutiveRegenerateTextureForSelector(PyMOLGlobals *G, int round_points, int *widths_arg){
8090   CExecutive *I = G->Executive;
8091   unsigned char *temp_buffer = pymol::malloc<unsigned char>(widths_arg[0] * widths_arg[0] * 4);
8092   int a, b;
8093   float mid_point, disty, distx, dist, wminusd;
8094   float widths[] = { widths_arg[0]/2.f, widths_arg[1]/2.f, widths_arg[2]/2.f };
8095   unsigned char *q;
8096   mid_point = ((widths_arg[0]-1.f)/2.f);
8097   TextureInitTextTexture(G);
8098   if (I->selectorTextureAllocatedSize < widths_arg[0]){
8099     // need to re-allocate a new part of the texture for the selection indicator
8100     TextureGetPlacementForNewSubtexture(G, widths_arg[0], widths_arg[0], &I->selectorTexturePosX, &I->selectorTexturePosY);
8101     I->selectorTextureAllocatedSize = widths_arg[0];
8102   }
8103   q = temp_buffer;
8104   if (round_points){
8105     for(b = 0; b < widths_arg[0]; b++) {
8106       disty = fabs(mid_point - b);
8107       for(a = 0; a < widths_arg[0]; a++) {
8108 	distx = fabs(mid_point - a);
8109 	dist = sqrt(distx*distx + disty*disty);
8110 	wminusd = widths[0] - dist;
8111 	if (dist < widths[2]){
8112 	  // white
8113 	  q[0] = q[1] = q[2] = q[3] = 255;
8114 	} else if (dist < widths[1]){
8115 	  // black
8116 	  q[0] = q[1] = q[2] = 0; q[3] = 255;
8117 	} else if (fabs(wminusd) < .5f) {
8118 	  // color plus transparency
8119 	  q[0] = 255;
8120 	  q[1] = 51;
8121 	  q[2] = 153;
8122 	  q[3] = (unsigned char)((wminusd + .5)*255);
8123 	} else if (dist < widths[0]) {
8124 	  // color (pink by default 1.f, .2f, .6f)
8125 	  q[0] = 255;
8126 	  q[1] = 51;
8127 	  q[2] = 153;
8128           q[3] = 255;
8129 	} else {
8130 	  // black
8131 	  q[0] = q[1] = q[2] = q[3] = 0;
8132 	}
8133 	//	printf("%3d ", q[1]);
8134 	q += 4;
8135       }
8136       //      printf("\n");
8137     }
8138   } else {
8139     for(b = 0; b < widths_arg[0]; b++) {
8140       dist = disty = fabs(mid_point - b);
8141       for(a = 0; a < widths_arg[0]; a++) {
8142 	distx = fabs(mid_point - a);
8143 	dist = disty;
8144 	if (distx > dist){
8145 	  dist = distx;
8146 	}
8147 	if (dist < widths[2]){
8148 	  // white
8149 	  q[0] = q[1] = q[2] = q[3] = 255;
8150 	} else if (dist < widths[1]){
8151 	  // black
8152 	  q[0] = q[1] = q[2] = 0; q[3] = 255;
8153 	} else {
8154 	  // color (pink by default 1.f, .2f, .6f)
8155 	  q[0] = 255;
8156 	  q[1] = 51;
8157 	  q[2] = 153;
8158           q[3] = 255;
8159 	}
8160 	//	printf("%3d ", q[1]);
8161 	q += 4;
8162       }
8163       //      printf("\n");
8164     }
8165   }
8166   glTexSubImage2D(GL_TEXTURE_2D, 0, I->selectorTexturePosX, I->selectorTexturePosY,
8167 		  widths_arg[0], widths_arg[0], GL_RGBA, GL_UNSIGNED_BYTE, temp_buffer);
8168   FreeP(temp_buffer);
8169 }
8170 
ExecutiveRenderIndicatorCGO(PyMOLGlobals * G,CGO * selIndicatorsCGO)8171 static void ExecutiveRenderIndicatorCGO(PyMOLGlobals * G, CGO *selIndicatorsCGO){
8172   CExecutive *I = G->Executive;
8173   CShaderPrg *shaderPrg;
8174   float text_texture_dim = TextureGetTextTextureSize(G);
8175   float textureScale;
8176   int no_depth = (int) SettingGetGlobal_f(G, cSetting_selection_overlay);
8177   shaderPrg = G->ShaderMgr->Enable_IndicatorShader();
8178   if (!shaderPrg)return;
8179   glEnable(GL_POINT_SPRITE);
8180   glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
8181   shaderPrg->SetLightingEnabled(0);
8182   shaderPrg->SetAttrib4fLocation("a_Color", 1.f, 1.f, 1.f, 1.f);
8183   shaderPrg->Set1f("g_pointSize", DIP2PIXEL(I->selectorTextureSize));
8184   shaderPrg->Set2f("textureLookup", I->selectorTexturePosX/text_texture_dim, I->selectorTexturePosY/text_texture_dim);
8185   textureScale = I->selectorTextureSize/text_texture_dim ;
8186   shaderPrg->Set2f("textureScale", textureScale, textureScale);
8187   int v[4];
8188   glGetIntegerv(GL_VIEWPORT, v);
8189   shaderPrg->Set4f("viewport", v[0], v[1], v[2], v[3]);
8190   if(no_depth)
8191     glDisable(GL_DEPTH_TEST);
8192   CGORenderGL(selIndicatorsCGO, NULL, NULL, NULL, NULL, NULL);
8193   if(no_depth)
8194     glEnable(GL_DEPTH_TEST);
8195   glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
8196   glDisable(GL_POINT_SPRITE);
8197   shaderPrg->Disable();
8198 }
8199 
ExecutiveSetupIndicatorPassGLImmediate(PyMOLGlobals * G,SpecRec * rec,int pass,float gl_width,int width)8200 static void ExecutiveSetupIndicatorPassGLImmediate(PyMOLGlobals * G, SpecRec *rec, int pass, float gl_width, int width){
8201 #ifndef PURE_OPENGL_ES_2
8202   switch (pass){
8203   case 0:
8204     if(rec->sele_color < 0)
8205       glColor3f(1.0F, 0.2F, 0.6F);
8206     else
8207       glColor3fv(ColorGet(G, rec->sele_color));
8208     glPointSize(gl_width);
8209     break;
8210   case 1:
8211     if(width > 2) {
8212       switch (width) {
8213       case 1: case 2: case 3:
8214 	glPointSize(1.0F);
8215 	break;
8216       case 4:
8217 	glPointSize(2.0F);
8218 	break;
8219       case 5:
8220 	glPointSize(3.0F);
8221 	break;
8222       case 6: case 7: case 8: case 9:
8223 	glPointSize(4.0F);
8224 	break;
8225       default:
8226 	glPointSize(6.0F);
8227 	break;
8228       }
8229       glColor3f(0.0F, 0.0F, 0.0F);
8230       break;
8231     }
8232   case 2:
8233     if(width > 4) {
8234       if(width > 5) {
8235 	glPointSize(2.0F);
8236       } else {
8237 	glPointSize(1.0F);
8238       }
8239       glColor3f(1.0F, 1.0F, 1.0F);
8240     }
8241     break;
8242   }
8243 #endif
8244 }
8245 
8246 /*========================================================================*/
ExecutiveRenderSelections(PyMOLGlobals * G,int curState,int slot,GridInfo * grid)8247 void ExecutiveRenderSelections(PyMOLGlobals * G, int curState, int slot, GridInfo *grid)
8248 {
8249   CExecutive *I = G->Executive;
8250   SpecRec *rec = NULL;
8251   int any_active = false;
8252   int no_depth = (int) SettingGetGlobal_f(G, cSetting_selection_overlay);
8253   short use_shader = (short) SettingGetGlobal_b(G, cSetting_use_shaders);
8254   float min_width;
8255   float gl_width;
8256   int width;
8257   int max_width = (int) SettingGetGlobal_f(G, cSetting_selection_width_max);
8258   float width_scale = SettingGetGlobal_f(G, cSetting_selection_width_scale);
8259   int round_points = SettingGetGlobal_b(G, cSetting_selection_round_points);
8260   short inv_indicators = false;
8261   min_width = SettingGetGlobal_f(G, cSetting_selection_width);
8262   if(width_scale >= 0.0F) {
8263     width = (int) ((width_scale *
8264 		    fabs(SettingGetGlobal_f(G, cSetting_stick_radius)) /
8265 		    SceneGetScreenVertexScale(G, NULL)));
8266     if(width < min_width)
8267       width = (int) min_width;
8268     else if(width > max_width)
8269       width = (int) max_width;
8270   } else
8271     width = (int) min_width;
8272   if(round_points) {
8273     width = (int) (width * 1.44F);
8274   }
8275   gl_width = (float) width;
8276   if(width > 6) {   /* keep it even above 6 */
8277     if(width & 0x1) {
8278       width--;
8279       gl_width = (float) width;
8280     }
8281   }
8282 
8283   if (use_shader){
8284     if (I->selectorTextureSize!=(int)gl_width || round_points != I->selectorIsRound ){
8285       inv_indicators = true;
8286     } else {
8287       if (slot){
8288 	while(ListIterate(I->Spec, rec, next)) {
8289 	  if(rec->type == cExecObject) {
8290 	    if (SceneGetDrawFlagGrid(G, grid, rec->obj->grid_slot)){
8291 	      if (rec->gridSlotSelIndicatorsCGO){
8292 		ExecutiveRenderIndicatorCGO(G, rec->gridSlotSelIndicatorsCGO);
8293 	      }
8294 	    }
8295 	  }
8296 	}
8297 	rec = NULL;
8298       } else if (I->selIndicatorsCGO){
8299 	ExecutiveRenderIndicatorCGO(G, I->selIndicatorsCGO);
8300 	return;
8301       }
8302     }
8303   } else {
8304     inv_indicators = true;
8305   }
8306   if (inv_indicators){
8307       CGOFree(I->selIndicatorsCGO);
8308     if (slot){
8309       while(ListIterate(I->Spec, rec, next)) {
8310 	if(rec->type == cExecObject) {
8311 	  if (SceneGetDrawFlagGrid(G, grid, rec->obj->grid_slot)){
8312 	      CGOFree(rec->gridSlotSelIndicatorsCGO);
8313 	  }
8314 	}
8315       }
8316     }
8317   }
8318   while(ListIterate(I->Spec, rec, next)) {
8319     if(rec->type == cExecSelection) {
8320       if (rec->visible) {
8321         any_active = true;
8322         break;
8323       }
8324     }
8325   }
8326 
8327   if(any_active) {
8328     SpecRec *rec1;
8329     int sele;
8330     int vis_only = SettingGetGlobal_b(G, cSetting_selection_visible_only);
8331     int fog = SettingGetGlobal_b(G, cSetting_depth_cue) && SettingGetGlobal_f(G, cSetting_fog);
8332 
8333     (void)fog;
8334 
8335     rec = NULL;
8336 
8337     if(round_points) {
8338       glEnable(GL_POINT_SMOOTH);
8339       glAlphaFunc(GL_GREATER, 0.5F);
8340       glEnable(GL_ALPHA_TEST);
8341       glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
8342     } else {
8343       glDisable(GL_POINT_SMOOTH);
8344       glDisable(GL_ALPHA_TEST);
8345       glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
8346     }
8347 
8348     if (use_shader && (I->selectorTextureSize!=(int)gl_width || round_points != I->selectorIsRound )){
8349       int level_widths[] = { 0, 0, 0 };
8350       level_widths[0] = (int)gl_width;
8351       if(width > 2) {
8352 	switch (width) {
8353 	case 1: case 2: case 3:
8354 	  level_widths[1] = 1;
8355 	  break;
8356 	case 4:
8357 	  level_widths[1] = 2;
8358 	  break;
8359 	case 5:
8360 	  level_widths[1] = 3;
8361 	  break;
8362 	case 6: case 7: case 8: case 9:
8363 	  level_widths[1] = 4;
8364 	  break;
8365 	default:
8366 	  level_widths[1] = 6;
8367 	  break;
8368 	}
8369       }
8370       if(width > 4) {
8371 	if(width > 5) {
8372 	  level_widths[2] = 2;
8373 	} else {
8374 	  level_widths[2] = 1;
8375 	}
8376       }
8377       ExecutiveInvalidateSelectionIndicatorsCGO(G);
8378       ExecutiveRegenerateTextureForSelector(G, round_points, level_widths);
8379       I->selectorTextureSize = gl_width;
8380       I->selectorIsRound = round_points;
8381     }
8382 
8383     while(ListIterate(I->Spec, rec, next)) {
8384       if(rec->type == cExecSelection) {
8385 
8386         if(rec->visible) {
8387           int enabled = true;
8388           SpecRec *group_rec = rec->group;
8389           while(enabled && group_rec) {
8390             if(!group_rec->visible)
8391               enabled = false;
8392             else
8393               group_rec = group_rec->group;
8394           }
8395 
8396           if(enabled) {
8397 
8398             sele = SelectorIndexByName(G, rec->name);   /* TODO: speed this up */
8399             if(sele >= 0) {
8400 	      int pass, totpass = use_shader ? 1 : 3;
8401 
8402 	      if (!use_shader){
8403 		if(no_depth)
8404 		  glDisable(GL_DEPTH_TEST);
8405 		glDisable(GL_FOG);
8406 	      }
8407 	      for (pass=0; pass<totpass; pass++){
8408 		if (use_shader){ // there is only one pass anyway
8409 		  if (!slot){
8410 		    I->selIndicatorsCGO = CGONew(G);
8411 		    CGODotwidth(I->selIndicatorsCGO, gl_width);
8412 		    CGOBegin(I->selIndicatorsCGO, GL_POINTS);
8413 		  }
8414 		} else {
8415 		  ExecutiveSetupIndicatorPassGLImmediate(G, rec, pass, gl_width, width);
8416 		  glBegin(GL_POINTS);
8417 		}
8418 		rec1 = NULL;
8419 		while(ListIterate(I->Spec, rec1, next)) {
8420 		  if(rec1->type == cExecObject) {
8421 		    if(rec1->obj->type == cObjectMolecule) {
8422 		      if (SceneGetDrawFlagGrid(G, grid, rec1->obj->grid_slot)){
8423 			if (!use_shader || !slot){
8424 			  ObjectMoleculeRenderSele((ObjectMolecule *) rec1->obj, curState, sele,
8425 						   vis_only SELINDICATORARGVAR);
8426 			} else if (!rec1->gridSlotSelIndicatorsCGO){
8427 			  if (!use_shader){
8428 			    ObjectMoleculeRenderSele((ObjectMolecule *) rec1->obj, curState, sele,
8429 						     vis_only, NULL);
8430 			  } else {
8431 			    CGO *drawArrayCGO = NULL;
8432 			    rec1->gridSlotSelIndicatorsCGO = CGONew(G);
8433 			    CGODotwidth(rec1->gridSlotSelIndicatorsCGO, gl_width);
8434 			    CGOBegin(rec1->gridSlotSelIndicatorsCGO, GL_POINTS);
8435 			    ObjectMoleculeRenderSele((ObjectMolecule *) rec1->obj, curState, sele,
8436 						     vis_only, rec1->gridSlotSelIndicatorsCGO);
8437 			    CGOEnd(rec1->gridSlotSelIndicatorsCGO);
8438 			    CGOStop(rec1->gridSlotSelIndicatorsCGO);
8439 			    drawArrayCGO = CGOCombineBeginEnd(rec1->gridSlotSelIndicatorsCGO, 0);
8440 			    CGOFree(rec1->gridSlotSelIndicatorsCGO);
8441 			    rec1->gridSlotSelIndicatorsCGO = CGOOptimizeToVBONotIndexedNoShader(drawArrayCGO, 0);
8442 			    CGOFree(drawArrayCGO);
8443 			    rec1->gridSlotSelIndicatorsCGO->use_shader = true;
8444 			    ExecutiveRenderIndicatorCGO(G, rec1->gridSlotSelIndicatorsCGO);
8445 			  }
8446 			}
8447 		      } else {
8448 			CGOFree(rec1->gridSlotSelIndicatorsCGO);
8449 		      }
8450 		    }
8451 		  }
8452 		}
8453 		if (use_shader){
8454 		  if (!slot){
8455 		    CGO *drawArrayCGO = NULL;
8456 		    CGOEnd(I->selIndicatorsCGO);
8457 		    CGOStop(I->selIndicatorsCGO);
8458 		    drawArrayCGO = CGOCombineBeginEnd(I->selIndicatorsCGO, 0);
8459 		    CGOFree(I->selIndicatorsCGO);
8460 		    I->selIndicatorsCGO = CGOOptimizeToVBONotIndexedNoShader(drawArrayCGO, 0);
8461 		    CGOFree(drawArrayCGO);
8462 		    if (I->selIndicatorsCGO){
8463 		      I->selIndicatorsCGO->use_shader = true;
8464 		      return ExecutiveRenderSelections(G, curState, slot, grid);
8465 		    } else
8466 		      return;
8467 		  }
8468 		} else {
8469 		  glEnd();
8470 		}
8471 	      }
8472 	      if(fog)
8473 		glEnable(GL_FOG);
8474 	      if(no_depth)
8475 		glEnable(GL_DEPTH_TEST);
8476             }
8477           }
8478         }
8479       }
8480     }
8481     if(!use_shader && round_points) {
8482       glAlphaFunc(GL_GREATER, 0.05F);
8483     }
8484   }
8485 }
8486 
8487 /*========================================================================*/
8488 
8489 /**
8490  * Macro with error handling to get a single atom vertex and associated temp
8491  * selection. Assigns `vertexN`, `tmpseleN`, and `resultN`
8492  */
8493 #define SETUP_SINGLE_ATOM_VERTEX(N)                                            \
8494   auto tmpsele##N = SelectorTmp::make(G, s##N);                                \
8495   p_return_if_error_prefixed(tmpsele##N, "Selection " #N ": ");                \
8496   auto result##N =                                                             \
8497       SelectorGetSingleAtomVertex(G, (tmpsele##N)->getIndex(), state);         \
8498   p_return_if_error_prefixed(result##N, "Selection " #N ": ");                 \
8499   const float* vertex##N = (result##N).result().data()
8500 
8501 /**
8502  * Get the distance between two atoms.
8503  * Implementation of `cmd.get_distance()`
8504  *
8505  * @param s0 single-atom selection expression
8506  * @param s1 single-atom selection expression
8507  * @param state object state
8508  * @return distance in Angstrom
8509  */
ExecutiveGetDistance(PyMOLGlobals * G,const char * s1,const char * s2,int state)8510 pymol::Result<float> ExecutiveGetDistance(
8511     PyMOLGlobals* G, const char* s1, const char* s2, int state)
8512 {
8513   SETUP_SINGLE_ATOM_VERTEX(1);
8514   SETUP_SINGLE_ATOM_VERTEX(2);
8515   return static_cast<float>(diff3f(vertex1, vertex2));
8516 }
8517 
8518 
8519 /*========================================================================*/
8520 /**
8521  * Get the angle between tree atoms s0-s1-s2.
8522  * Implementation of `cmd.get_angle()`
8523  *
8524  * @param s0 single-atom selection expression
8525  * @param s1 single-atom selection expression
8526  * @param s2 single-atom selection expression
8527  * @param state object state
8528  * @return angle in degrees
8529  */
ExecutiveGetAngle(PyMOLGlobals * G,const char * s1,const char * s2,const char * s3,int state)8530 pymol::Result<float> ExecutiveGetAngle(
8531     PyMOLGlobals* G, const char* s1, const char* s2, const char* s3, int state)
8532 {
8533   SETUP_SINGLE_ATOM_VERTEX(1);
8534   SETUP_SINGLE_ATOM_VERTEX(2);
8535   SETUP_SINGLE_ATOM_VERTEX(3);
8536 
8537   float d1[3], d2[3];
8538   subtract3f(vertex1, vertex2, d1);
8539   subtract3f(vertex3, vertex2, d2);
8540   return rad_to_deg(get_angle3f(d1, d2));
8541 }
8542 
8543 
8544 /*========================================================================*/
8545 /**
8546  * Get the dihedral angle between four atoms.
8547  * Implementation of `cmd.get_dihedral()`
8548  *
8549  * @param s0 single-atom selection expression
8550  * @param s1 single-atom selection expression
8551  * @param s2 single-atom selection expression
8552  * @param s3 single-atom selection expression
8553  * @param state object state
8554  * @return dihedral angle in degrees
8555  */
ExecutiveGetDihe(PyMOLGlobals * G,const char * s1,const char * s2,const char * s3,const char * s4,int state)8556 pymol::Result<float> ExecutiveGetDihe(PyMOLGlobals* G, const char* s1,
8557     const char* s2, const char* s3, const char* s4, int state)
8558 {
8559   SETUP_SINGLE_ATOM_VERTEX(1);
8560   SETUP_SINGLE_ATOM_VERTEX(2);
8561   SETUP_SINGLE_ATOM_VERTEX(3);
8562   SETUP_SINGLE_ATOM_VERTEX(4);
8563   return rad_to_deg(get_dihedral3f(vertex1, vertex2, vertex3, vertex4));
8564 }
8565 
8566 
8567 /*========================================================================*/
ExecutiveSetDihe(PyMOLGlobals * G,const char * s1,const char * s2,const char * s3,const char * s4,float value,int state,int quiet)8568 pymol::Result<> ExecutiveSetDihe(PyMOLGlobals* G, const char* s1,
8569     const char* s2, const char* s3, const char* s4, float value, int state,
8570     int quiet)
8571 {
8572   SETUP_SINGLE_ATOM_VERTEX(1);
8573   SETUP_SINGLE_ATOM_VERTEX(2);
8574   SETUP_SINGLE_ATOM_VERTEX(3);
8575   SETUP_SINGLE_ATOM_VERTEX(4);
8576   float current = rad_to_deg(get_dihedral3f(vertex1, vertex2, vertex3, vertex4));
8577   float change = value - current;
8578   auto save_state = SceneGetState(G);
8579   SceneSetFrame(G, -1, state);        /* KLUDGE ALERT!
8580                                        * necessary because the editor
8581                                        * can only work on the current state...this
8582                                        * needs to be changed.*/
8583   EditorSelect(G,
8584       tmpsele3->getName(),
8585       tmpsele2->getName(),
8586       "", "", false, true, true);
8587   EditorTorsion(G, change);
8588   SceneSetFrame(G, -1, save_state);
8589   if(!quiet) {
8590     PRINTFB(G, FB_Editor, FB_Actions)
8591       " SetDihedral: adjusted to %5.3f\n", value ENDFB(G);
8592   }
8593   return {};
8594 }
8595 
8596 
8597 /*========================================================================*/
ExecutiveGetArea(PyMOLGlobals * G,const char * s0,int sta0,int load_b)8598 float ExecutiveGetArea(PyMOLGlobals * G, const char *s0, int sta0, int load_b)
8599 {
8600   ObjectMolecule *obj0;
8601   RepDot *rep;
8602   float result = -1.0F;
8603   int a, sele0;
8604   int known_member = -1;
8605   int is_member;
8606   int *ati;
8607   float *area;
8608   AtomInfoType *ai = NULL;
8609   ObjectMoleculeOpRec op;
8610 
8611   SelectorTmp tmpsele0(G, s0);
8612   sele0 = tmpsele0.getIndex();
8613 
8614   if(sele0 < 0) {
8615     ErrMessage(G, "Area", "Invalid selection.");
8616   } else {
8617     obj0 = SelectorGetSingleObjectMolecule(G, sele0);
8618     if(!(obj0)) {
8619       if(SelectorCountAtoms(G, sele0, sta0) > 0)
8620         ErrMessage(G, "Area", "Selection must be within a single object.");
8621       else
8622         result = 0.0F;
8623     } else {
8624       auto cs = obj0->getCoordSet(sta0);
8625       if(!cs)
8626         ErrMessage(G, "Area", "Invalid state.");
8627       else {
8628         rep = (RepDot *) RepDotDoNew(cs, cRepDotAreaType, sta0);
8629         if(!rep)
8630           ErrMessage(G, "Area", "Can't get dot representation.");
8631         else {
8632 
8633           if(load_b) {
8634             /* zero out B-values within selection */
8635             ObjectMoleculeOpRecInit(&op);
8636             op.code = OMOP_SetB;
8637             op.f1 = 0.0;
8638             op.i1 = 0;
8639             ExecutiveObjMolSeleOp(G, sele0, &op);
8640           }
8641 
8642           result = 0.0;
8643 
8644           area = rep->A;
8645           ati = rep->Atom;
8646 
8647           is_member = false;
8648 
8649           for(a = 0; a < rep->N; a++) {
8650 
8651             if(known_member != (*ati)) {
8652               known_member = (*ati);
8653               ai = obj0->AtomInfo + known_member;
8654               is_member = SelectorIsMember(G, ai->selEntry, sele0);
8655             }
8656 
8657             if(is_member) {
8658               result += (*area);
8659               if(load_b)
8660                 ai->b += (*area);
8661             }
8662             area++;
8663             ati++;
8664           }
8665 
8666           rep->R.fFree((Rep *) rep);    /* free the representation */
8667         }
8668       }
8669     }
8670   }
8671   return (result);
8672 }
8673 
8674 
8675 /*========================================================================*/
8676 /**
8677  * Implementation of `cmd.get_names()`
8678  *
8679  * @param mode see modules/pymol/querying.py
8680  * @param enabled_only only include enabled names
8681  * @param s0 atom selection expression, limits results to molecular objects and
8682  * named selections which have at least one atom in the selection.
8683  */
ExecutiveGetNames(PyMOLGlobals * G,int mode,int enabled_only,const char * s0)8684 pymol::Result<std::vector<const char*>> ExecutiveGetNames(
8685     PyMOLGlobals* G, int mode, int enabled_only, const char* s0)
8686 {
8687   enum {
8688     cGetNames_all = 0,
8689     cGetNames_objects = 1,
8690     cGetNames_selections = 2,
8691     cGetNames_public = 3,
8692     cGetNames_public_objects = 4,
8693     cGetNames_public_selections = 5,
8694     cGetNames_public_nongroup_objects = 6,
8695     cGetNames_public_group_objects = 7,
8696     cGetNames_nongroup_objects = 8,
8697     cGetNames_group_objects = 9,
8698   };
8699 
8700   bool include_objects = //
8701       mode == cGetNames_all || mode == cGetNames_objects ||
8702       mode == cGetNames_public || mode == cGetNames_public_objects;
8703   bool include_selections =
8704       mode == cGetNames_all || mode == cGetNames_selections ||
8705       mode == cGetNames_public || mode == cGetNames_public_selections;
8706   bool include_group_objects =
8707       mode == cGetNames_group_objects || mode == cGetNames_public_group_objects;
8708   bool include_nongroup_objects = //
8709       mode == cGetNames_nongroup_objects ||
8710       mode == cGetNames_public_nongroup_objects;
8711   bool public_only =
8712       mode >= cGetNames_public && mode <= cGetNames_public_group_objects;
8713 
8714   CExecutive *I = G->Executive;
8715   SpecRec *rec = NULL;
8716   std::vector<const char*> result;
8717   pymol::Result<SelectorTmp> tmpsele0;
8718   SelectorID_t sele0 = cSelectionInvalid;
8719   int incl_flag;
8720   if(s0[0]) {
8721     tmpsele0 = SelectorTmp::make(G, s0);
8722     p_return_if_error(tmpsele0);
8723     sele0 = tmpsele0->getIndex();
8724     assert(sele0 != cSelectionInvalid);
8725   }
8726 
8727   while(ListIterate(I->Spec, rec, next)) {
8728     incl_flag = 0;
8729     if((rec->type == cExecObject
8730         && (include_objects
8731             || (rec->obj->type != cObjectGroup && include_nongroup_objects)
8732             || (rec->obj->type == cObjectGroup && include_group_objects)))
8733        || (rec->type == cExecSelection && include_selections)
8734       ) {
8735       if(!public_only || rec->name[0] != '_') {
8736         if((!enabled_only) || (rec->visible)) {
8737           incl_flag = 0;
8738           if(sele0 < 0)
8739             incl_flag = 1;
8740           else
8741             switch (rec->type) {
8742             case cExecObject:
8743               if(rec->obj->type == cObjectMolecule) {
8744                 int a;
8745                 ObjectMolecule *obj_mol = (ObjectMolecule *) rec->obj;
8746                 const AtomInfoType *ai = obj_mol->AtomInfo.data();
8747                 for(a = 0; a < obj_mol->NAtom; a++) {
8748                   if(SelectorIsMember(G, ai->selEntry, sele0)) {
8749                     incl_flag = 1;
8750                     break;
8751                   }
8752                   ai++;
8753                 }
8754               }
8755               break;
8756             case cExecSelection:
8757               if(SelectorCheckIntersection(G, sele0, SelectorIndexByName(G, rec->name))) {
8758                 incl_flag = 1;
8759                 break;
8760               }
8761               break;
8762             }
8763           if(incl_flag) {
8764             result.push_back(rec->name);
8765           }
8766         }
8767       }
8768     }
8769   }
8770   return (result);
8771 }
8772 
8773 
8774 /*========================================================================*/
8775 /*
8776  * Return true if `name` is the name of an object molecule or a named selection
8777  */
ExecutiveIsMoleculeOrSelection(PyMOLGlobals * G,const char * name)8778 bool ExecutiveIsMoleculeOrSelection(PyMOLGlobals * G, const char *name)
8779 {
8780   if (!strcmp(name, cKeywordAll))
8781     return true;
8782 
8783   SpecRec *rec = ExecutiveFindSpec(G, name);
8784   if (rec && (
8785         (rec->type == cExecObject && rec->obj->type == cObjectMolecule) ||
8786         (rec->type == cExecSelection)))
8787     return true;
8788 
8789   return false;
8790 }
8791 
8792 
8793 /*========================================================================*/
ExecutiveGetType(PyMOLGlobals * G,const char * name,WordType type)8794 int ExecutiveGetType(PyMOLGlobals * G, const char *name, WordType type)
8795 {
8796   SpecRec *rec = NULL;
8797   int ok = true;
8798   rec = ExecutiveFindSpec(G, name);
8799   if(!rec) {
8800     ok = false;
8801   } else {
8802     if(rec->type == cExecObject) {
8803       strcpy(type, "object:");
8804       if(rec->obj->type == cObjectMolecule)
8805         strcat(type, "molecule");
8806       else if(rec->obj->type == cObjectMap)
8807         strcat(type, "map");
8808       else if(rec->obj->type == cObjectMesh)
8809         strcat(type, "mesh");
8810       else if(rec->obj->type == cObjectSlice)
8811         strcat(type, "slice");
8812       else if(rec->obj->type == cObjectSurface)
8813         strcat(type, "surface");
8814       else if(rec->obj->type == cObjectMeasurement)
8815         strcat(type, "measurement");
8816       else if(rec->obj->type == cObjectCGO)
8817         strcat(type, "cgo");
8818       else if(rec->obj->type == cObjectGroup)
8819         strcat(type, "group");
8820       else if(rec->obj->type == cObjectVolume)
8821 	strcat(type, "volume");
8822       else if(rec->obj->type == cObjectAlignment)
8823        strcat(type, "alignment");
8824       else if(rec->obj->type == cObjectGadget)
8825        strcat(type, "ramp");
8826     } else if(rec->type == cExecSelection) {
8827       strcpy(type, "selection");
8828     }
8829   }
8830   return (ok);
8831 }
8832 
8833 
8834 /*========================================================================*/
ExecutiveUpdateCmd(PyMOLGlobals * G,const char * s0,const char * s1,int sta0,int sta1,int method,int quiet)8835 pymol::Result<> ExecutiveUpdateCmd(PyMOLGlobals* G, const char* s0,
8836     const char* s1, int sta0, int sta1, int method, int quiet)
8837 {
8838   SETUP_SELE_DEFAULT_PREFIXED(0, cSelectionInvalid);
8839   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
8840 
8841   return SelectorUpdateCmd(G, sele0, sele1, sta0, sta1, method, quiet);
8842 }
8843 
8844 
8845 /*========================================================================*/
ExecutiveRenameObjectAtoms(PyMOLGlobals * G,const char * selection,int force,int quiet)8846 pymol::Result<> ExecutiveRenameObjectAtoms(
8847     PyMOLGlobals* G, const char* selection, int force, int quiet)
8848 {
8849   SETUP_SELE(selection, tmpsele1, sele);
8850 
8851   {
8852     ObjectMoleculeOpRec op;
8853     ObjectMoleculeOpRecInit(&op);
8854     op.code = OMOP_RenameAtoms;
8855     op.i1 = 0;
8856     op.i2 = force;
8857     ExecutiveObjMolSeleOp(G, sele, &op);
8858 
8859     if(!quiet) {
8860       PRINTFB(G, FB_Executive, FB_Actions)
8861         " Rename: renamed %d atoms.\n", op.i1 ENDFB(G);
8862     }
8863   }
8864   return {};
8865 }
8866 
8867 
8868 /*========================================================================*/
ExecutiveFuse(PyMOLGlobals * G,const char * s0,const char * s1,int mode,int recolor,int move_flag)8869 pymol::Result<> ExecutiveFuse(PyMOLGlobals* G, const char* s0, const char* s1,
8870     int mode, int recolor, int move_flag)
8871 {
8872   int i0 = -1;
8873   int i1 = -1;
8874   SelectorID_t sele2 = cSelectionInvalid;
8875   ObjectMolecule *obj0, *obj1;
8876   ObjectMoleculeOpRec op;
8877   int ok = true;
8878 #define tmp_fuse_sele "tmp_fuse_sele"
8879 
8880   SETUP_SELE_DEFAULT_PREFIXED(0, cSelectionInvalid);
8881   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
8882 
8883   {
8884 #ifndef _PYMOL_NO_UNDO
8885 #endif
8886     {
8887       EditorInactivate(G);
8888       obj0 = SelectorGetSingleObjectMolecule(G, sele0);
8889       obj1 = SelectorGetSingleObjectMolecule(G, sele1);
8890       if(obj0)
8891         i0 = ObjectMoleculeGetAtomIndex(obj0, sele0);
8892       if(obj1)
8893         i1 = ObjectMoleculeGetAtomIndex(obj1, sele1);
8894       if(obj0 && obj1 && (i0 >= 0) && (i1 >= 0) && (obj0 != obj1)) {
8895         ObjectMoleculeVerifyChemistry(obj0, -1);
8896         ObjectMoleculeVerifyChemistry(obj1, -1);
8897 
8898         SelectorCreate(G, tmp_fuse_sele, NULL, obj0, 1, NULL);
8899         sele2 = SelectorIndexByName(G, tmp_fuse_sele);
8900         if(mode) {
8901           ObjectMoleculeOpRecInit(&op);
8902           op.code = OMOP_PrepareFromTemplate;
8903           op.ai = obj1->AtomInfo + i1;
8904           op.i1 = mode;
8905           op.i2 = 0;
8906           op.i3 = recolor;
8907           if(recolor)
8908             op.i4 = obj1->Color;
8909           ExecutiveObjMolSeleOp(G, sele2, &op);
8910         }
8911         SelectorDelete(G, tmp_fuse_sele);
8912 
8913         switch (mode) {
8914         case 0:
8915         case 1:
8916         case 2:
8917           if((obj0->AtomInfo[i0].protons == 1) && (obj1->AtomInfo[i1].protons == 1))
8918             ok &= ObjectMoleculeFuse(obj1, i1, obj0, i0, 0, move_flag);
8919           else if((obj0->AtomInfo[i0].protons != 1) && (obj1->AtomInfo[i1].protons != 1))
8920             ok &= ObjectMoleculeFuse(obj1, i1, obj0, i0, 1, move_flag);
8921           else
8922             return pymol::make_error("Can't fuse between a hydrogen and a non-hydrogen");
8923           break;
8924         case 3:
8925           ok &= ObjectMoleculeFuse(obj1, i1, obj0, i0, 3, false);
8926           break;
8927         }
8928       }
8929     }
8930   }
8931   if(!ok) {
8932     return pymol::make_error("Could not fuse.");
8933   }
8934   return {};
8935 }
8936 
8937 
8938 /*========================================================================*/
ExecutiveSpheroid(PyMOLGlobals * G,const char * name,int average)8939 pymol::Result<> ExecutiveSpheroid(PyMOLGlobals * G, const char *name, int average)
8940 {                               /* EXPERIMENTAL */
8941   CExecutive *I = G->Executive;
8942   CObject *os = NULL;
8943 
8944   if(strlen(name)) {
8945     os = ExecutiveFindObjectByName(G, name);
8946     if(!os)
8947       return pymol::make_error("Object not found.");
8948     else if(os->type != cObjectMolecule) {
8949       return pymol::make_error("Bad object type.");
8950     }
8951   }
8952 
8953   if(os || (!strlen(name))) {   /* sort one or all */
8954     SpecRec* rec = nullptr;
8955     while(ListIterate(I->Spec, rec, next)) {
8956       if(rec->type == cExecObject)
8957         if(rec->obj->type == cObjectMolecule)
8958           if((!os) || (rec->obj == os)) {
8959             auto obj = (ObjectMolecule *) rec->obj;
8960             ObjectMoleculeCreateSpheroid(obj, average);
8961             obj->invalidate(cRepAll, cRepInvRep, -1);
8962           }
8963     }
8964     SceneChanged(G);
8965   }
8966   return {};
8967 }
8968 
8969 
8970 /*========================================================================*/
ExecutiveRebuildAll(PyMOLGlobals * G)8971 void ExecutiveRebuildAll(PyMOLGlobals * G)
8972 {
8973   CExecutive *I = G->Executive;
8974   SpecRec *rec = NULL;
8975   PRINTFD(G, FB_Executive)
8976     " ExecutiveRebuildAll: entered.\n" ENDFD;
8977   auto defer_builds_mode = SettingGet<bool>(G, cSetting_defer_builds_mode);
8978   while(ListIterate(I->Spec, rec, next)) {
8979     if(rec->type == cExecObject) {
8980       auto level = cRepInvAll;
8981       switch (rec->obj->type) {
8982       case cObjectMeasurement:
8983         ObjectDistInvalidateRep((ObjectDist *) rec->obj, cRepAll);
8984         break;
8985       case cObjectMolecule:
8986         level = defer_builds_mode ? cRepInvPurge : cRepInvRep;
8987       case cObjectSurface:
8988       case cObjectMesh:
8989       case cObjectSlice:
8990       case cObjectAlignment:
8991       case cObjectCGO:
8992         rec->obj->invalidate(cRepAll, level, -1);
8993         break;
8994       }
8995     }
8996   }
8997   SeqChanged(G);
8998   SceneChanged(G);
8999 }
9000 
9001 
9002 /*========================================================================*/
ExecutiveRebuildAllObjectDist(PyMOLGlobals * G)9003 void ExecutiveRebuildAllObjectDist(PyMOLGlobals * G)
9004 {
9005   CExecutive *I = G->Executive;
9006   SpecRec *rec = NULL;
9007   while(ListIterate(I->Spec, rec, next)) {
9008     if(rec->type == cExecObject) {
9009       if(rec->obj->type == cObjectMeasurement) {
9010         ObjectDistInvalidateRep((ObjectDist *) rec->obj, cRepAll);
9011       }
9012     }
9013   }
9014   SceneInvalidate(G);
9015 }
9016 
9017 
9018 /*========================================================================*/
ExecutiveUndo(PyMOLGlobals * G,int dir)9019 void ExecutiveUndo(PyMOLGlobals * G, int dir)
9020 {
9021   CExecutive *I = G->Executive;
9022   CObject *o;
9023   ObjectMolecule *obj = NULL, *compObj;
9024   SpecRec *rec = NULL;
9025 
9026   o = ExecutiveGetLastObjectEdited(G);
9027   PRINTFB(G, FB_Executive, FB_Debugging)
9028   " ExecutiveUndo: last object %p\n", (void *) o ENDFB(G);
9029   if(o)
9030     if(o->type == cObjectMolecule)
9031       obj = (ObjectMolecule *) o;
9032   /* make sure this is still a real object */
9033   if(obj) {
9034     while(ListIterate(I->Spec, rec, next)) {
9035       if(rec->type == cExecObject)
9036         if(rec->obj->type == cObjectMolecule) {
9037           compObj = (ObjectMolecule *) rec->obj;
9038           if(obj == compObj) {
9039             ObjectMoleculeUndo(obj, dir);
9040             break;
9041           }
9042         }
9043     }
9044   }
9045 
9046 }
9047 
9048 
9049 /*========================================================================*/
ExecutiveSort(PyMOLGlobals * G,const char * name)9050 pymol::Result<> ExecutiveSort(PyMOLGlobals* G, const char* name)
9051 {
9052   CExecutive *I = G->Executive;
9053   ObjectMolecule *obj;
9054   SpecRec *rec = NULL;
9055   ObjectMoleculeOpRec op;
9056   int sele;
9057   int ok = true;
9058   if((!name) || (!name[0]))
9059     name = cKeywordAll;
9060 
9061   {
9062     CTracker *I_Tracker = I->Tracker;
9063     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
9064     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
9065     int changed = false;
9066     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
9067       if(rec) {
9068         switch (rec->type) {
9069         case cExecAll:
9070           rec = NULL;
9071           while(ListIterate(I->Spec, rec, next)) {
9072             if((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
9073               obj = (ObjectMolecule *) rec->obj;
9074               if (ok)
9075 		ok &= ObjectMoleculeSort(obj);
9076 	      if (ok){
9077 		changed = true;
9078 		sele = SelectorIndexByName(G, rec->name);
9079 		if(sele >= 0) {
9080 		  ObjectMoleculeOpRecInit(&op);
9081 		  op.code = OMOP_INVA;
9082 		  op.i1 = cRepCartoonBit | cRepRibbonBit;
9083 		  op.i2 = cRepInvRep;
9084 		  ExecutiveObjMolSeleOp(G, sele, &op);
9085 		}
9086 	      }
9087             }
9088           }
9089           break;
9090         case cExecSelection:
9091           sele = SelectorIndexByName(G, rec->name);
9092           if(sele >= 0) {
9093             op.code = OMOP_Sort;
9094             ExecutiveObjMolSeleOp(G, sele, &op);
9095             ObjectMoleculeOpRecInit(&op);
9096             op.code = OMOP_INVA;
9097             op.i1 = cRepCartoonBit | cRepRibbonBit;
9098             op.i2 = cRepInvRep;
9099             ExecutiveObjMolSeleOp(G, sele, &op);
9100             ObjectMoleculeOpRecInit(&op);
9101           }
9102           break;
9103         case cExecObject:
9104           if(rec->obj->type == cObjectMolecule) {
9105             obj = (ObjectMolecule *) rec->obj;
9106             if (ok)
9107 	      ok &= ObjectMoleculeSort(obj);
9108             changed = true;
9109             sele = SelectorIndexByName(G, rec->name);
9110             if(sele >= 0) {
9111               ObjectMoleculeOpRecInit(&op);
9112               op.code = OMOP_INVA;
9113               op.i1 = cRepCartoonBit | cRepRibbonBit;
9114               op.i2 = cRepInvRep;
9115               ExecutiveObjMolSeleOp(G, sele, &op);
9116             }
9117           }
9118           break;
9119         }
9120       }
9121     }
9122     TrackerDelList(I_Tracker, list_id);
9123     TrackerDelIter(I_Tracker, iter_id);
9124     if(changed)
9125       SceneChanged(G);
9126   }
9127   return {};
9128 }
9129 
9130 
9131 /*========================================================================*/
ExecutiveRemoveAtoms(PyMOLGlobals * G,const char * s1,int quiet)9132 pymol::Result<> ExecutiveRemoveAtoms(PyMOLGlobals * G, const char *s1, int quiet)
9133 {
9134   SETUP_SELE_DEFAULT(1);
9135 
9136   CExecutive *I = G->Executive;
9137   SpecRec *rec = NULL;
9138   ObjectMolecule *obj = NULL;
9139   ObjectMoleculeOpRec op;
9140 
9141   {
9142 #ifndef _PYMOL_NO_UNDO
9143 #endif
9144     while(ListIterate(I->Spec, rec, next)) {
9145       if(rec->type == cExecObject) {
9146         if(rec->obj->type == cObjectMolecule) {
9147           ObjectMoleculeOpRecInit(&op);
9148           op.code = OMOP_Remove;
9149           op.i1 = 0;
9150           obj = (ObjectMolecule *) rec->obj;
9151           ObjectMoleculeVerifyChemistry(obj, -1);       /* remember chemistry for later */
9152           ObjectMoleculeSeleOp(obj, sele1, &op);
9153           if(op.i1) {
9154             if(!quiet) {
9155               PRINTFD(G, FB_Editor)
9156                 " ExecutiveRemove-Debug: purging %i of %i atoms in %s\n",
9157                 op.i1, obj->NAtom, obj->Name ENDFD;
9158             }
9159             ObjectMoleculePurge(obj);
9160             if(!quiet) {
9161               PRINTFB(G, FB_Editor, FB_Actions)
9162                 " Remove: eliminated %d atoms in model \"%s\".\n",
9163                 op.i1, obj->Name ENDFB(G);
9164             }
9165           }
9166         }
9167       }
9168     }
9169   }
9170   return {};
9171 }
9172 
9173 
9174 /*========================================================================*/
ExecutiveAddHydrogens(PyMOLGlobals * G,const char * s1,int quiet,int state,bool legacy)9175 pymol::Result<> ExecutiveAddHydrogens(
9176     PyMOLGlobals* G, const char* s1, int quiet, int state, bool legacy)
9177 {
9178   ObjectMoleculeOpRec op;
9179 
9180   if (legacy) {
9181     PRINTFB(G, FB_Executive, FB_Warnings)
9182       " %s-Warning: legacy mode was removed\n", __FUNCTION__ ENDFB(G);
9183   }
9184 
9185   SETUP_SELE_DEFAULT(1);
9186 
9187   {
9188     ObjectMoleculeOpRecInit(&op);
9189     op.code = OMOP_AddHydrogens;
9190     op.i1 = state;
9191     ExecutiveObjMolSeleOp(G, sele1, &op);
9192   }
9193   return {};
9194 }
9195 
9196 
9197 /*========================================================================*/
ExecutiveFixHydrogens(PyMOLGlobals * G,const char * s1,int quiet)9198 void ExecutiveFixHydrogens(PyMOLGlobals * G, const char *s1, int quiet)
9199 {
9200   int sele1;
9201   ObjectMoleculeOpRec op;
9202 
9203   sele1 = SelectorIndexByName(G, s1);
9204   if(sele1 >= 0) {
9205     ObjectMoleculeOpRecInit(&op);
9206     op.code = OMOP_FixHydrogens;
9207     ExecutiveObjMolSeleOp(G, sele1, &op);
9208   }
9209 }
9210 
9211 
9212 /*========================================================================*/
ExecutiveFlag(PyMOLGlobals * G,int flag,const char * sele,int action,int quiet)9213 pymol::Result<> ExecutiveFlag(PyMOLGlobals * G, int flag, const char* sele, int action, int quiet)
9214 {
9215   auto s1 = SelectorTmp::make(G, sele);
9216   p_return_if_error(s1);
9217   ObjectMoleculeOpRec op;
9218 
9219   int sele1 = s1->getIndex();
9220   ObjectMoleculeOpRecInit(&op);
9221   switch (action) {
9222   case 0:
9223     op.code = OMOP_Flag;
9224     break;
9225   case 1:
9226     op.code = OMOP_FlagSet;
9227     break;
9228   case 2:
9229     op.code = OMOP_FlagClear;
9230     break;
9231   default:
9232     op.code = OMOP_Flag;
9233     break;
9234   }
9235   op.i1 = (((unsigned int) 1) << flag);
9236   op.i2 = ((unsigned int) 0xFFFFFFFF - (((unsigned int) 1) << flag));
9237   op.i3 = 0;
9238   op.i4 = 0;
9239   ExecutiveObjMolSeleOp(G, sele1, &op);
9240   if(Feedback(G, FB_Executive, FB_Actions)) {
9241     if(!quiet) {
9242       switch (action) {
9243       case 0:
9244         if(op.i3) {
9245           PRINTF " Flag: flag %d is set in %d of %d atoms.\n", flag, op.i3,
9246             op.i4 ENDF(G);
9247         } else {
9248           PRINTF " Flag: flag %d cleared on all atoms.\n", flag ENDF(G);
9249         }
9250         break;
9251       case 1:
9252         PRINTF " Flag: flag %d set on %d atoms.\n", flag, op.i3 ENDF(G);
9253         break;
9254       case 2:
9255         PRINTF " Flag: flag %d cleared on %d atoms.\n", flag, op.i3 ENDF(G);
9256         break;
9257       }
9258     }
9259   }
9260   if(SettingGetGlobal_b(G, cSetting_auto_indicate_flags)) {
9261     auto buffer = pymol::string_format("(flag %d)", flag);
9262     SelectorCreate(G, cIndicateSele, buffer.c_str(), NULL, true, NULL);
9263     ExecutiveSetObjVisib(G, cIndicateSele, true, false);
9264     SceneInvalidate(G);
9265   }
9266   return {};
9267 }
9268 
9269 
9270 /*========================================================================*/
ExecutiveOverlap(PyMOLGlobals * G,const char * s1,int state1,const char * s2,int state2,float adjust)9271 float ExecutiveOverlap(PyMOLGlobals * G, const char *s1, int state1, const char *s2, int state2,
9272                        float adjust)
9273 {
9274   SelectorTmp tmpsele1(G, s1);
9275   SelectorTmp tmpsele2(G, s2);
9276   int sele1 = tmpsele1.getIndex();
9277   int sele2 = tmpsele2.getIndex();
9278 
9279   float result = 0.0;
9280 
9281   if(state1 < 0)
9282     state1 = 0;
9283   if(state2 < 0)
9284     state2 = 0;
9285 
9286   if((sele1 >= 0) && (sele2 >= 0))
9287     result = SelectorSumVDWOverlap(G, sele1, state1, sele2, state2, adjust);
9288 
9289   return (result);
9290 }
9291 
9292 
9293 /*========================================================================*/
ExecutiveProtect(PyMOLGlobals * G,const char * s1,int mode,int quiet)9294 pymol::Result<> ExecutiveProtect(PyMOLGlobals * G, const char *s1, int mode, int quiet)
9295 {
9296   ObjectMoleculeOpRec op;
9297 
9298   SETUP_SELE_DEFAULT(1);
9299 
9300   {
9301     ObjectMoleculeOpRecInit(&op);
9302     op.code = OMOP_Protect;
9303     op.i1 = mode;
9304     op.i2 = 0;
9305     ExecutiveObjMolSeleOp(G, sele1, &op);
9306     if(!quiet) {
9307       if(Feedback(G, FB_Executive, FB_Actions)) {
9308         if(op.i2) {
9309           if(mode) {
9310             PRINTF " Protect: %d atoms protected from movement.\n", op.i2 ENDF(G);
9311           } else {
9312             PRINTF " Protect: %d atoms deprotected.\n", op.i2 ENDF(G);
9313           }
9314         }
9315       }
9316     }
9317   }
9318   return {};
9319 }
9320 
9321 
9322 /*========================================================================*/
ExecutiveMask(PyMOLGlobals * G,const char * s1,int mode,int quiet)9323 pymol::Result<> ExecutiveMask(
9324     PyMOLGlobals* G, const char* s1, int mode, int quiet)
9325 {
9326   ObjectMoleculeOpRec op;
9327 
9328   SETUP_SELE_DEFAULT(1);
9329 
9330   {
9331     ObjectMoleculeOpRecInit(&op);
9332     op.code = OMOP_Mask;
9333     op.i1 = mode;
9334     op.i2 = 0;
9335     ExecutiveObjMolSeleOp(G, sele1, &op);
9336     if(!quiet) {
9337       if(Feedback(G, FB_Executive, FB_Actions)) {
9338         if(op.i2) {
9339           if(mode) {
9340             PRINTF " Mask: %d atoms masked (cannot be picked or selected).\n",
9341               op.i2 ENDF(G);
9342           } else {
9343             PRINTF " Mask: %d atoms unmasked.\n", op.i2 ENDF(G);
9344           }
9345         }
9346       }
9347     }
9348     op.code = OMOP_INVA;        /* need to invalidate all pickable representations */
9349     op.i1 = cRepsAtomMask;
9350     op.i2 = cRepInvPick;
9351     ExecutiveObjMolSeleOp(G, sele1, &op);
9352   }
9353   return {};
9354 }
9355 
9356 
9357 /*========================================================================*/
9358 /**
9359  * flag > 0:  Set stereo_mode and turn stereo on
9360  * flag = 0:  Turn off stereo
9361  * flag = -1: Swap eyes (stereo_shift *= -1)
9362  * flag = -2: Turn on stereo with current stereo_mode
9363  * flag = -3: Turn on chromadepth and turn off stereo
9364  */
ExecutiveStereo(PyMOLGlobals * G,int flag)9365 pymol::Result<> ExecutiveStereo(PyMOLGlobals * G, int flag)
9366 {
9367   switch (flag) {
9368   case -3:
9369     SettingSet(G, cSetting_chromadepth, 1);
9370     SceneSetStereo(G, 0);
9371     break;
9372   case -1:
9373     SettingSetGlobal_f(G, cSetting_stereo_shift, -SettingGetGlobal_f(G, cSetting_stereo_shift));
9374     break;
9375   default:
9376     SettingSet(G, cSetting_chromadepth, 0);
9377 
9378     if (flag == cStereo_quadbuffer && !G->StereoCapable) {
9379       return pymol::make_error("no 'quadbuffer' support detected (force with 'pymol -S')");
9380     }
9381 
9382     if (flag == cStereo_openvr) {
9383 #ifdef _PYMOL_OPENVR
9384       OpenVRInit(G);
9385       OpenVRFeedback(G);
9386 #else
9387       return pymol::make_error("'openvr' stereo mode not available in this build");
9388 #endif
9389     }
9390 
9391     if (flag > 0) {
9392       SettingSet(G, cSetting_stereo_mode, flag);
9393     }
9394 
9395     SceneSetStereo(G, flag != 0);
9396   }
9397 
9398   // for chromadepth
9399   G->ShaderMgr->Set_Reload_Bits(RELOAD_VARIABLES);
9400 
9401   SceneDirty(G);
9402   return {};
9403 }
9404 
9405 
9406 /*========================================================================*/
ExecutiveRevalence(PyMOLGlobals * G,const char * s1,const char * s2,const char * src,int target_state,int source_state,int reset,int quiet)9407 pymol::Result<> ExecutiveRevalence(PyMOLGlobals* G, const char* s1,
9408     const char* s2, const char* src, int target_state, int source_state,
9409     int reset, int quiet)
9410 {
9411   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
9412   SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
9413 
9414   {
9415     if(src && src[0]) {
9416       SETUP_SELE(src, tmpsele3, sele3);
9417 
9418       {
9419         ObjectMolecule *obj3 = SelectorGetSingleObjectMolecule(G, sele3);
9420         if(!obj3) {
9421           return pymol::make_error("Revalence can only source a single object at a time.");
9422         } else {
9423           ObjectMoleculeOpRec op;
9424 
9425           ObjectMoleculeOpRecInit(&op);
9426           op.code = OMOP_RevalenceFromSource;
9427           op.i1 = sele1;
9428           op.i2 = sele2;
9429           op.i3 = target_state;
9430           op.obj3 = obj3;
9431           op.i4 = sele3;
9432           op.i5 = source_state;
9433           op.i6 = quiet;
9434 
9435           ExecutiveObjMolSeleOp(G, sele1, &op);
9436 
9437           /*
9438              if(ObjectMoleculeXferValences(obj1,sele1,sele2,target_state,obj3,sele3,source_state,quiet)) {
9439              ObjectMoleculeVerifyChemistry(obj1,target_state);
9440              ObjectMoleculeInvalidate(obj1,cRepAll,cRepInvBonds,target_state);
9441              }
9442            */
9443 
9444         }
9445       }
9446     } else {                    /* guess valences */
9447       ObjectMoleculeOpRec op;
9448 
9449       ObjectMoleculeOpRecInit(&op);
9450       op.code = OMOP_RevalenceByGuessing;
9451       op.i1 = sele1;
9452       op.i2 = sele2;
9453       op.i3 = target_state;
9454       op.i4 = reset;
9455       op.i6 = quiet;
9456 
9457       ExecutiveObjMolSeleOp(G, sele1, &op);
9458 
9459     }
9460   }
9461   return {};
9462 }
9463 
9464 
9465 /*========================================================================*/
ExecutiveBond(PyMOLGlobals * G,const char * s1,const char * s2,int order,int mode,int quiet)9466 pymol::Result<> ExecutiveBond(PyMOLGlobals * G, const char *s1, const char *s2, int order, int mode, int quiet)
9467 {
9468   int cnt;
9469   CExecutive *I = G->Executive;
9470   SpecRec *rec = NULL;
9471   int flag = false;
9472 
9473   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
9474   SETUP_SELE_DEFAULT_PREFIXED(2, cSelectionInvalid);
9475 
9476   {
9477     ObjectMolecule *obj1 = SelectorGetSingleObjectMolecule(G, sele1);
9478     ObjectMolecule *obj2 = SelectorGetSingleObjectMolecule(G, sele2);
9479     if((!obj1) || (!obj2) || (obj1 != obj2)) {
9480       if((!quiet) && (mode == 1)) {
9481         PRINTFB(G, FB_Editor, FB_Warnings)
9482           "Editor-Warning: bonds cannot be created between objects, only within.\n"
9483           ENDFB(G);
9484       }
9485     }
9486     while(ListIterate(I->Spec, rec, next)) {
9487       if(rec->type == cExecObject) {
9488         if(rec->obj->type == cObjectMolecule) {
9489           switch (mode) {
9490           case 1:              /* add */
9491             cnt = ObjectMoleculeAddBond((ObjectMolecule *) rec->obj, sele1, sele2, order);
9492             if(cnt) {
9493               if(!quiet) {
9494                 PRINTFB(G, FB_Editor, FB_Actions)
9495                   " Bond: %d bonds added to model \"%s\".\n", cnt, rec->obj->Name
9496                   ENDFB(G);
9497                 flag = true;
9498               }
9499             }
9500             break;
9501           case 2:              /* adjust */
9502             cnt =
9503               ObjectMoleculeAdjustBonds((ObjectMolecule *) rec->obj, sele1, sele2, 1,
9504                                         order);
9505             if(cnt) {
9506               if(!quiet) {
9507                 PRINTFB(G, FB_Editor, FB_Actions)
9508                   " Valence: %d bond valences adjusted in model \"%s\".\n", cnt,
9509                   rec->obj->Name ENDFB(G);
9510                 flag = true;
9511               }
9512             }
9513             break;
9514           case 0:              /* remove */
9515           default:
9516             cnt = ObjectMoleculeRemoveBonds((ObjectMolecule *) rec->obj, sele1, sele2);
9517             if(cnt) {
9518               if(!quiet) {
9519                 PRINTFB(G, FB_Editor, FB_Actions)
9520                   " Unbond: %d bonds removed from model \"%s\".\n",
9521                   cnt, rec->obj->Name ENDFB(G);
9522               }
9523               flag = true;
9524             }
9525           }
9526         }
9527       }
9528     }
9529     if(!flag) {
9530       if(!quiet) {
9531         switch (mode) {
9532         case 1:
9533           PRINTFB(G, FB_Editor, FB_Warnings)
9534             "Bond-Warning: no bonds added." ENDFB(G);
9535           break;
9536         case 2:
9537           PRINTFB(G, FB_Editor, FB_Warnings)
9538             "Valence-Warning: no bond valences changed." ENDFB(G);
9539           break;
9540         case 0:
9541         default:
9542           PRINTFB(G, FB_Editor, FB_Warnings)
9543             "Unbond-Warning: no bonds removed." ENDFB(G);
9544           break;
9545         }
9546       }
9547     }
9548   }
9549   return {};
9550 }
9551 
9552 
9553 /*========================================================================*/
ExecutiveAngle(PyMOLGlobals * G,const char * nam,const char * s1,const char * s2,const char * s3,int mode,int labels,int reset,int zoom,int quiet,int state,int state1,int state2,int state3)9554 pymol::Result<float> ExecutiveAngle(PyMOLGlobals* G,
9555     const char* nam, const char* s1, const char* s2, const char* s3, int mode,
9556     int labels, int reset, int zoom, int quiet, int state, int state1,
9557     int state2, int state3)
9558 {
9559   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
9560   SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
9561   SETUP_SELE_DEFAULT_PREFIXED(3, sele2);
9562 
9563   auto obj = ExecutiveFindOrDeleteObject<ObjectDist>(G, nam);
9564   auto need_manage = !obj;
9565   float result = -1.0F;
9566 
9567   obj = ObjectDistNewFromAngleSele(G, obj,
9568                                      sele1, sele2, sele3,
9569                                      mode, labels, &result, reset, state,
9570                                      state1, state2, state3);
9571 
9572   // ObjectDistNewFrom... always succeeds
9573   assert(obj);
9574 
9575   if (need_manage) {
9576         ObjectSetName((CObject *) obj, nam);
9577         ExecutiveManageObject(G, (CObject *) obj, zoom, quiet);
9578         if(!labels)
9579           ExecutiveSetRepVisib(G, nam, cRepLabel, 0);
9580   }
9581 
9582   return rad_to_deg(result);
9583 }
9584 
9585 
9586 /*========================================================================*/
ExecutiveDihedral(PyMOLGlobals * G,const char * nam,const char * s1,const char * s2,const char * s3,const char * s4,int mode,int labels,int reset,int zoom,int quiet,int state)9587 pymol::Result<float> ExecutiveDihedral(PyMOLGlobals* G, const char* nam,
9588     const char* s1, const char* s2, const char* s3, const char* s4, int mode,
9589     int labels, int reset, int zoom, int quiet, int state)
9590 {
9591   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
9592   SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
9593   SETUP_SELE_DEFAULT_PREFIXED(3, sele2);
9594   SETUP_SELE_DEFAULT_PREFIXED(4, sele3);
9595 
9596   auto obj = ExecutiveFindOrDeleteObject<ObjectDist>(G, nam);
9597   auto need_manage = !obj;
9598   float result = -1.0F;
9599 
9600   obj = ObjectDistNewFromDihedralSele(G, obj,
9601                                         sele1, sele2, sele3, sele4,
9602                                         mode, labels, &result, reset, state);
9603 
9604   // ObjectDistNewFrom... always succeeds
9605   assert(obj);
9606 
9607   if (need_manage) {
9608         ObjectSetName((CObject *) obj, nam);
9609         ExecutiveManageObject(G, (CObject *) obj, zoom, quiet);
9610         if(!labels)
9611           ExecutiveSetRepVisib(G, nam, cRepLabel, 0);
9612   }
9613 
9614   return rad_to_deg(result);
9615 }
9616 
9617 /**
9618  * Create a distance measurement object
9619  *
9620  * @param nam: name of measurement object to create or add to
9621  * @param s1: selection expression
9622  * @param s2: selection expression or "same" keyword (shortcut for s1 = s2)
9623  * @param mode: 0 (any), 1 (bonds), 2 (hbonds), 3 (distance_exclusion),
9624  * 4 (centroids), 5 (pi-*), 6 (pi-pi), 7 (pi-cat), 8 (vdw-dist-ratio)
9625  * @param cutoff: Distance cutoff in Angstrom, or vdw-distance ratio cutoff (mode 8)
9626  * @param labels: Boolean flag to show distance labels
9627  * @param reset: Boolean flag to clear a distance object if it already exists
9628  * @return average of measured distances in Angstrom
9629  */
ExecutiveDistance(PyMOLGlobals * G,const char * nam,const char * s1,const char * s2,int mode,float cutoff,int labels,int quiet,int reset,int state,int zoom,int state1,int state2)9630 pymol::Result<float> ExecutiveDistance(PyMOLGlobals* G, const char* nam,
9631     const char* s1, const char* s2, int mode, float cutoff, int labels,
9632     int quiet, int reset, int state, int zoom, int state1, int state2)
9633 {
9634   if (strcmp(s1, s2) == 0) {
9635     s2 = cKeywordSame;
9636   }
9637 
9638   SETUP_SELE_DEFAULT_PREFIXED(1, cSelectionInvalid);
9639   SETUP_SELE_DEFAULT_PREFIXED(2, sele1);
9640 
9641   auto obj = ExecutiveFindOrDeleteObject<ObjectDist>(G, nam);
9642   auto need_manage = !obj;
9643   float result = -1.0F;
9644 
9645   /* create a new distance from the two selections */
9646   obj = ObjectDistNewFromSele(G, obj, sele1, sele2, mode, cutoff, labels, reset,
9647       &result, state, state1, state2);
9648 
9649   // ObjectDistNewFrom... always succeeds
9650   assert(obj);
9651 
9652   if (need_manage) {
9653     switch (mode) {
9654     case 6: // pi-pi
9655       SettingSet(cSetting_dash_color, "0x06c5ff" /* light blue */, obj);
9656       break;
9657     case 7: // pi-cat
9658       SettingSet(cSetting_dash_color, "0x468c00" /* dark green */, obj);
9659       break;
9660     case 8: // "clashes"
9661       SettingSet(cSetting_dash_color, "0xff8800" /* light red */, obj);
9662       break;
9663     }
9664 
9665       ObjectSetName(obj, nam);
9666       ExecutiveManageObject(G, (CObject *) obj, zoom, quiet);
9667       if(!labels)
9668         ExecutiveSetRepVisib(G, nam, cRepLabel, 0);
9669   }
9670 
9671   return result;
9672 }
9673 
9674 /*========================================================================*/
ExecutiveNameToSeqAlignStrVLA(PyMOLGlobals * G,const char * name,int state,int format,int quiet)9675 char *ExecutiveNameToSeqAlignStrVLA(PyMOLGlobals * G, const char *name, int state, int format,
9676                                     int quiet)
9677 {
9678   char *result = NULL;
9679   if((!name) || (!name[0]) || (strcmp(name, "(all)") == 0)) {
9680     /* use current alignment as the default */
9681     name = SettingGetGlobal_s(G, cSetting_seq_view_alignment);
9682     if(name[0] == 0) {
9683       SpecRec *rec = NULL;
9684       CExecutive *I = G->Executive;
9685       while(ListIterate(I->Spec, rec, next)) {
9686         if(rec->visible) {
9687           if(rec->type == cExecObject)
9688             if(rec->obj->type == cObjectAlignment) {
9689               name = rec->obj->Name;
9690               break;
9691             }
9692         }
9693       }
9694     }
9695   }
9696   if(!name) {
9697     ErrMessage(G, " Executive", "invalid alignment object name.");
9698   } else {
9699     CObject *obj = ExecutiveFindObjectByName(G, name);
9700 
9701     if(!obj) {
9702       ErrMessage(G, " Executive", "alignment object not found.");
9703     } else if(obj->type != cObjectAlignment) {
9704       ErrMessage(G, " Executive", "invalid object type.");
9705     } else {
9706       ObjectAlignmentAsStrVLA(G, (ObjectAlignment *) obj, state, format, &result);
9707     }
9708   }
9709   return (result);
9710 }
9711 
9712 
9713 /*========================================================================*/
ExecutiveSeleToObject(PyMOLGlobals * G,const char * name,const char * s1,int source,int target,int discrete,int zoom,int quiet,int singletons,int copy_properties)9714 pymol::Result<> ExecutiveSeleToObject(PyMOLGlobals* G, const char* name,
9715     const char* s1, int source, int target, int discrete, int zoom, int quiet,
9716     int singletons, int copy_properties)
9717 {
9718   SelectorTmp tmpsele1(G, s1);
9719   int sele1 = tmpsele1.getIndex();
9720 
9721   int ok = false;
9722   ObjectNameType valid_name;
9723 
9724   UtilNCopy(valid_name, name, sizeof(valid_name));
9725   if(SettingGetGlobal_b(G, cSetting_validate_object_names)) {
9726     ObjectMakeValidName(G, valid_name);
9727     name = valid_name;
9728   }
9729   {
9730     int exists = (ExecutiveFindObjectMoleculeByName(G, name) != NULL);
9731 
9732     if(sele1 >= 0) {
9733       ok = SelectorCreateObjectMolecule(G, sele1, name, target,
9734                                         source, discrete, false, quiet, singletons, copy_properties);
9735       if(ok) {
9736         int sele2 = SelectorIndexByName(G, name);
9737         ObjectMolecule *old_obj, *new_obj;
9738         old_obj = SelectorGetFirstObjectMolecule(G, sele1);     /* get at least one object */
9739         new_obj = SelectorGetSingleObjectMolecule(G, sele2);
9740 
9741         /* first we need to make sure that the object being moved
9742            matches the target with respect to both the TTT and the
9743            object's state matrix (if any) */
9744 
9745         if(old_obj && new_obj) {
9746           ExecutiveMatrixCopy(G, old_obj->Name, new_obj->Name, 1, 1,    /* TTT mode */
9747                               source, target, false, 0, quiet);
9748 
9749           ExecutiveMatrixCopy(G, old_obj->Name, new_obj->Name, 2, 2,    /* Object state mode */
9750                               source, target, false, 0, quiet);
9751 
9752           ExecutiveDoZoom(G, (CObject *) new_obj, !exists, zoom, true);
9753         }
9754       }
9755     }
9756   }
9757   if(ok){
9758     return {};
9759   } else {
9760     return pymol::make_error("Failed to Create Object");
9761   }
9762 }
9763 
9764 
9765 /*========================================================================*/
ExecutiveCopy(PyMOLGlobals * G,const char * src,const char * dst,int zoom)9766 pymol::Result<> ExecutiveCopy(
9767     PyMOLGlobals* G, const char* src, const char* dst, int zoom)
9768 {
9769   const CObject *os = ExecutiveFindObjectByName(G, src);
9770   if(!os) {
9771     return pymol::make_error("Object not found.");
9772   }
9773 
9774   CObject *oDst = nullptr;
9775   ObjectMap *oDstMap = nullptr;
9776 
9777   switch (os->type) {
9778   case cObjectMolecule:
9779     oDst = ObjectMoleculeCopy(static_cast<const ObjectMolecule *>(os));
9780     break;
9781   case cObjectMap:
9782     ObjectMapNewCopy(G, static_cast<const ObjectMap *>(os), &oDstMap,
9783                      /* source_state=all */ -1,
9784                      /* target_state=(unused)*/ 0);
9785     oDst = oDstMap;
9786     break;
9787   default:
9788     return pymol::make_error("Bad object type.");
9789   }
9790 
9791   if(!oDst) {
9792     return pymol::make_error("Failed to create copy");
9793   }
9794 
9795   strcpy(oDst->Name, dst);
9796   ExecutiveManageObject(G, (CObject *) oDst, zoom, false);
9797 
9798   PRINTFB(G, FB_Executive, FB_Actions)
9799     " Executive: object %s created.\n", oDst->Name ENDFB(G);
9800   SceneChanged(G);
9801   return {};
9802 }
9803 
9804 
9805 /*========================================================================*/
ExecutiveOrient(PyMOLGlobals * G,const char * sele,int state,float animate,int complete,float buffer,int quiet)9806 pymol::Result<> ExecutiveOrient(PyMOLGlobals * G, const char *sele,
9807                      int state, float animate, int complete, float buffer, int quiet)
9808 {
9809   double egval[3], egvali[3];
9810   double evect[3][3];
9811   float m[4][4], mt[4][4];
9812   float t[3];
9813   const float _0 = 0.0F;
9814   int a, b;
9815 
9816   double mi[16];
9817 
9818   SelectorTmp tmpsele(G, sele);
9819   sele = tmpsele.getName();
9820 
9821   if(!ExecutiveGetMoment(G, sele, mi, state)) {
9822     return {};
9823   }
9824 
9825   if(!MatrixEigensolveC33d(G, mi, egval, egvali, (double *) (void *) evect)) {
9826 
9827     normalize3d(evect[0]);
9828     normalize3d(evect[1]);
9829     normalize3d(evect[2]);
9830 
9831     for(a = 0; a < 3; a++) {
9832       for(b = 0; b < 3; b++) {
9833         m[a][b] = (float) evect[b][a];  /* fill columns */
9834       }
9835     }
9836 
9837     for(a = 0; a < 3; a++) {    /* expand to 4x4 */
9838       m[3][a] = 0;
9839       m[a][3] = 0;
9840     }
9841     m[3][3] = 1.0;
9842 
9843     normalize3f(m[0]);          /* cross normalization (probably unnec.)  */
9844     normalize3f(m[1]);
9845     normalize3f(m[2]);
9846 
9847     for(a = 0; a < 3; a++)      /* convert to row-major */
9848       for(b = 0; b < 3; b++)
9849         mt[a][b] = m[b][a];
9850 
9851     cross_product3f(mt[0], mt[1], t);   /* insure right-handed matrix */
9852     if(dot_product3f(t, mt[2]) < 0.0) {
9853       mt[2][0] = -mt[2][0];
9854       mt[2][1] = -mt[2][1];
9855       mt[2][2] = -mt[2][2];
9856     }
9857 
9858     for(a = 0; a < 3; a++)      /* convert back to column major */
9859       for(b = 0; b < 3; b++)
9860         m[a][b] = mt[b][a];
9861 
9862     if(animate < 0.0F) {
9863       if(SettingGetGlobal_b(G, cSetting_animation))
9864         animate = SettingGetGlobal_f(G, cSetting_animation_duration);
9865       else
9866         animate = 0.0F;
9867     }
9868     if(animate != 0.0F)
9869       ScenePrimeAnimation(G);
9870 
9871     {
9872       float old_mat[16];
9873       float new_mat[16];
9874       float x, y, z;
9875       copy44f(SceneGetMatrix(G), old_mat);
9876 
9877       SceneSetMatrix(G, m[0]);  /* load matrix */
9878 
9879       /* there must  be a more elegant to get the PC on X and the SC
9880        * on Y then what is shown below, but I couldn't get it to work.
9881        * I tried swapping the eigen-columns around but either that is
9882        * a bogus approach (?) or my code was buggy.  Hence the following...*/
9883 
9884       if((egval[0] < egval[2]) && (egval[2] < egval[1])) {      /* X < Z < Y */
9885         SceneRotate(G, 90, 1, 0, 0);    /*1<-->2 */
9886       } else if((egval[1] < egval[0]) && (egval[0] < egval[2])) {       /* Y < X < Z */
9887         SceneRotate(G, 90, 0, 0, 1);    /*0<-->1 */
9888       } else if((egval[1] < egval[2]) && (egval[2] < egval[0])) {       /* Y < Z < X */
9889         SceneRotate(G, 90, 0, 1, 0);    /*1<-->2 */
9890         SceneRotate(G, 90, 0, 0, 1);    /*0<-->1 */
9891       } else if((egval[2] < egval[1]) && (egval[1] < egval[0])) {       /* Z < Y < X */
9892         SceneRotate(G, 90, 0, 1, 0);    /*0<-->2 */
9893       } else if((egval[2] < egval[0]) && (egval[0] < egval[1])) {       /* Z < X < Y */
9894         SceneRotate(G, 90, 0, 1, 0);    /*0<-->2 */
9895         SceneRotate(G, 90, 1, 0, 0);    /*0<-->1 */
9896       }
9897 
9898       /* now choose orientation that has the least perturbation from the starting matrix */
9899 
9900       copy44f(SceneGetMatrix(G), new_mat);
9901 
9902       x = old_mat[0] * new_mat[0] + old_mat[4] * new_mat[4] + old_mat[8] * new_mat[8];
9903       y = old_mat[1] * new_mat[1] + old_mat[5] * new_mat[5] + old_mat[9] * new_mat[9];
9904       z = old_mat[2] * new_mat[2] + old_mat[6] * new_mat[6] + old_mat[10] * new_mat[10];
9905 
9906       if((x > _0) && (y < _0) && (z < _0)) {
9907         SceneRotate(G, 180, 1, 0, 0);
9908       } else if((x < _0) && (y > _0) && (z < _0)) {
9909         SceneRotate(G, 180, 0, 1, 0);
9910       } else if((x < _0) && (y < _0) && (z > _0)) {
9911         SceneRotate(G, 180, 0, 0, 1);
9912       }
9913     }
9914 
9915     /* X < Y < Z  - do nothing - that's what we want */
9916 
9917     ExecutiveWindowZoom(G, sele, buffer, state, complete, false, quiet);
9918     if(animate != 0.0F)
9919       SceneLoadAnimation(G, animate, 0);
9920 
9921   }
9922   return {};
9923 }
9924 
ExecutiveMove(PyMOLGlobals * G,pymol::zstring_view axis,float dist)9925 pymol::Result<> ExecutiveMove(
9926     PyMOLGlobals* G, pymol::zstring_view axis, float dist)
9927 {
9928   switch (axis[0]) {
9929   case 'x':
9930     SceneTranslate(G, dist, 0.0, 0.0);
9931     break;
9932   case 'y':
9933     SceneTranslate(G, 0.0, dist, 0.0);
9934     break;
9935   case 'z':
9936     SceneTranslate(G, 0.0, 0.0, dist);
9937     break;
9938   default:
9939     return pymol::make_error("Axis must be x, y, or z");
9940   }
9941   return {};
9942 }
9943 
9944 /*========================================================================*/
ExecutiveLabel(PyMOLGlobals * G,const char * str1,const char * expr,int quiet,int eval_mode)9945 pymol::Result<> ExecutiveLabel(PyMOLGlobals * G, const char *str1, const char *expr, int quiet, int eval_mode)
9946 {
9947   int sele1;
9948   ObjectMoleculeOpRec op1;
9949   int cnt;
9950 
9951   SelectorTmp s1(G, str1);
9952   sele1 = s1.getIndex();
9953   if(sele1 >= 0) {
9954     ObjectMoleculeOpRecInit(&op1);
9955     op1.code = OMOP_LABL;
9956     op1.s1 = expr;
9957     op1.i1 = 0;
9958     op1.i2 = eval_mode;
9959     ExecutiveObjMolSeleOp(G, sele1, &op1);
9960     cnt = op1.i1;
9961     op1.code = OMOP_VISI;
9962     op1.i1 = cRepLabelBit;
9963     op1.i2 = cVis_SHOW;
9964     ExecutiveObjMolSeleOp(G, sele1, &op1);
9965     op1.code = OMOP_INVA;
9966     op1.i2 = cRepInvVisib;
9967     ExecutiveObjMolSeleOp(G, sele1, &op1);
9968 
9969     if(!quiet) {
9970       {
9971 	const char *unlabelledstr = "";
9972 	if (cnt<0){ /* if negative, say unlabelled */
9973 	  cnt = -cnt;
9974 	  unlabelledstr = "un";
9975 	}
9976       PRINTFB(G, FB_Executive, FB_Actions)
9977         " Label: %slabelled %i atoms.\n", unlabelledstr, cnt ENDFB(G);
9978       }
9979     }
9980   } else {
9981     return pymol::make_error("No atoms selected");
9982   }
9983   return {};
9984 }
9985 
9986 
9987 /*========================================================================*/
9988 #ifdef _WEBGL
9989 #else
ExecutiveIterate(PyMOLGlobals * G,const char * str1,const char * expr,int read_only,int quiet,PyObject * space)9990 pymol::Result<int> ExecutiveIterate(PyMOLGlobals * G, const char *str1, const char *expr, int read_only, int quiet,
9991                      PyObject * space)
9992 #endif
9993 {
9994   ObjectMoleculeOpRec op1;
9995   ObjectMoleculeOpRecInit(&op1);
9996 #ifdef _WEBGL
9997 #endif
9998   SelectorTmp tmpsele1(G, str1);
9999   int sele1 = tmpsele1.getIndex();
10000   op1.i1 = 0;
10001   if(sele1 >= 0) {
10002     op1.code = OMOP_ALTR;
10003     op1.i1 = 0;
10004     op1.i2 = read_only;
10005 #ifdef _WEBGL
10006 #else
10007     op1.s1 = expr;
10008     op1.py_ob1 = space;
10009 #endif
10010     ExecutiveObjMolSeleOp(G, sele1, &op1);
10011     if(!quiet) {
10012       if(!read_only) {
10013         PRINTFB(G, FB_Executive, FB_Actions)
10014           " Alter: modified %i atoms.\n", op1.i1 ENDFB(G);
10015       } else {
10016         PRINTFB(G, FB_Executive, FB_Actions)
10017           " Iterate: iterated over %i atoms.\n", op1.i1 ENDFB(G);
10018       }
10019     }
10020     if (!read_only) {
10021       SeqChanged(G);
10022     }
10023 #ifndef _PYMOL_NO_UNDO
10024 #endif
10025   } else {
10026     if(!quiet) {
10027       PRINTFB(G, FB_Executive, FB_Warnings)
10028         " %s: No atoms selected.\n", __func__ ENDFB(G);
10029     }
10030   }
10031   return (op1.i1);
10032 }
10033 
10034 /**
10035  * cmd.select() implementation
10036  *
10037  * @param name Name of selection to create, or selection expression if `sele` is
10038  * empty. If `sele` is empty, `name` will become "sele" or "sel01", depending on
10039  * `auto_number_selections` and `sel_counter`. If `name` is empty, it will
10040  * become "sel01", depending on `sel_counter` but independent of
10041  * `auto_number_selections`.
10042  * @param sele Selection expression
10043  * @param enable Enable the named selection's SpecRec if 1, disable if 0, keep
10044  * unchanged (if exists, otherwise enable) if -1.
10045  * @param merge Discard existing named selection if 0. Merge with existing named
10046  * selection if 1, merge only if existing one is enabled if 2.
10047  * @param state Object state for state (coordinate) dependent expressions
10048  * @param domain Existing selection name, same as selecting `(sele) and (domain)`
10049  * @return Number of selected atoms
10050  */
ExecutiveSelect(PyMOLGlobals * G,const char * name,const char * sele,int enable,int quiet,int merge,int state,const char * domain)10051 pymol::Result<int> ExecutiveSelect(PyMOLGlobals* G, const char* name,
10052     const char* sele, int enable, int quiet, int merge, int state,
10053     const char* domain)
10054 {
10055   // selection in first argument (cmd.select("expr"))
10056   if (!sele[0]) {
10057     sele = name;
10058     name = SettingGet<bool>(G, cSetting_auto_number_selections) ? "" : "sele";
10059   }
10060 
10061   // auto name (cmd.select("", "expr"))
10062   char namebuf[16];
10063   if (!name[0]) {
10064     auto sel_num = SettingGet<int>(G, cSetting_sel_counter) + 1;
10065     SettingSet<int>(G, cSetting_sel_counter, sel_num);
10066     snprintf(
10067         namebuf, sizeof(namebuf), "sel%02u", static_cast<unsigned>(sel_num));
10068     name = namebuf;
10069   }
10070 
10071   // bail if name not available
10072   if (ExecutiveFindObjectByName(G, name)) {
10073     return pymol::make_error("name conflicts with an object");
10074   }
10075 
10076   // merge with existing selection
10077   std::string selebuf;
10078   if (merge) {
10079     if (merge == 2) {
10080       // merge if exists and active
10081       selebuf = pymol::join_to_string("(", sele, ") or ??", name);
10082     } else {
10083       // merge if exists
10084       selebuf = pymol::join_to_string("(", sele, ") or ?", name);
10085     }
10086     sele = selebuf.c_str();
10087   }
10088 
10089   auto res = SelectorCreateWithStateDomain(
10090       G, name, sele, nullptr, quiet, nullptr, state, domain);
10091 
10092   p_return_if_error(res);
10093 
10094   if (enable == 1) {
10095     ExecutiveSetObjVisib(G, name, 1, 0);
10096   } else if (enable == 0) {
10097     ExecutiveSetObjVisib(G, name, 0, 0);
10098   }
10099 
10100   SceneInvalidate(G);
10101   SeqDirty(G);
10102 
10103   return res.result();
10104 }
10105 
10106 /*========================================================================*/
ExecutiveSelectList(PyMOLGlobals * G,const char * sele_name,const char * s1,int * list,int list_len,int state,int mode,int quiet)10107 int ExecutiveSelectList(PyMOLGlobals * G, const char *sele_name, const char *s1,
10108                         int *list, int list_len, int state, int mode, int quiet)
10109 {                               /* assumes a blocked Python interpreter */
10110   int ok = true;
10111   int n_eval = 0;
10112   int sele0 = SelectorIndexByName(G, s1);
10113   int n_sele = 0;
10114   ObjectMolecule *obj = NULL;
10115   if(sele0 >= 0)
10116     obj = SelectorGetSingleObjectMolecule(G, sele0);
10117   if(obj) {
10118     int a;
10119     int index = 0;
10120     CoordSet* cs = obj->getCoordSet(state);
10121 
10122     if(ok && list) {
10123       if(list_len) {
10124         switch (mode) {
10125         case 0:                /* object indices */
10126           for(a = 0; a < list_len; a++) {
10127             list[a]--;          /* convert 1-based indices to 0-based array offsets */
10128           }
10129           if(ok)
10130             n_sele =
10131               SelectorCreateOrderedFromObjectIndices(G, sele_name, obj, list, list_len).result();
10132           break;
10133         case 1:                /* atom identifier */
10134         case 2:                /* rank */
10135 
10136           {
10137             OVOneToAny *o2a = OVOneToAny_New(G->Context->heap);
10138             AtomInfoType *ai;
10139             OVstatus res;
10140             OVreturn_word ret;
10141             int n_idx = 0;
10142             int *idx_list = VLAlloc(int, list_len);
10143             ai = obj->AtomInfo.data();
10144 
10145             for(a = 0; a < obj->NAtom; a++) {
10146               ai->temp1 = -1;
10147               ai++;
10148             }
10149 
10150             /* create linked list using temp1 as "next" field */
10151 
10152             ai = obj->AtomInfo.data();
10153             for(a = 0; a < obj->NAtom; a++) {
10154               if(mode == 1) {   /* id */
10155                 index = ai[a].id;
10156               } else            /* rank */
10157                 index = ai[a].rank;
10158               if((OVreturn_IS_ERROR((res = OVOneToAny_SetKey(o2a, index, a))))) {
10159                 if((OVreturn_IS_ERROR((ret = OVOneToAny_GetKey(o2a, index))))) {
10160                   ok = false;
10161                 } else {
10162                   int cur = ret.word;
10163                   while(1) {
10164                     if(ai[cur].temp1 < 0) {
10165                       ai[cur].temp1 = a;
10166                       break;
10167                     } else {
10168                       cur = ai[cur].temp1;
10169                     }
10170                   }
10171                 }
10172               }
10173             }
10174 
10175             {
10176               int cur;
10177               for(a = 0; a < list_len; a++) {
10178                 index = list[a];
10179                 if((OVreturn_IS_OK((ret = OVOneToAny_GetKey(o2a, index))))) {
10180                   cur = ret.word;
10181                   while(cur >= 0) {
10182                     if (!cs || cs->atmToIdx(cur) >= 0) {
10183                       VLACheck(idx_list, int, n_idx);
10184                       idx_list[n_idx] = cur;
10185                       n_idx++;
10186                     }
10187                     cur = ai[cur].temp1;
10188                   }
10189                 }
10190               }
10191             }
10192             if(ok)
10193               n_sele =
10194                 SelectorCreateOrderedFromObjectIndices(G, sele_name, obj, idx_list,
10195                                                        n_idx).result();
10196             OVOneToAny_DEL_AUTO_NULL(o2a);
10197             VLAFreeP(idx_list);
10198           }
10199           break;
10200 
10201         }
10202       } else
10203         SelectorCreateEmpty(G, sele_name, true);
10204     }
10205   } else {
10206     PRINTFB(G, FB_Executive, FB_Errors)
10207       " SelectList-Error: selection cannot span more than one object.\n" ENDFB(G);
10208   }
10209   if(ok) {
10210     if(!quiet) {
10211       PRINTFB(G, FB_Executive, FB_Actions)
10212         " SelectList: modified %i atoms.\n", n_eval ENDFB(G);
10213     }
10214   } else {
10215     if(!quiet) {
10216       PRINTFB(G, FB_Executive, FB_Warnings)
10217         "ExecutiveIterateList: An error occurred.\n" ENDFB(G);
10218     }
10219   }
10220   if(!ok)
10221     return -1;
10222   else
10223     return n_sele;
10224 }
10225 
10226 
10227 /*========================================================================*/
ExecutiveIterateList(PyMOLGlobals * G,const char * str1,PyObject * list,int read_only,int quiet,PyObject * space)10228 pymol::Result<int> ExecutiveIterateList(PyMOLGlobals* G, const char* str1,
10229                          PyObject * list, int read_only, int quiet, PyObject * space)
10230 {
10231 #ifdef _PYMOL_NOPY
10232   return pymol::make_error("Iterate List not available.");
10233 #else
10234   int ok = true;
10235   int n_eval = 0;
10236   SelectorTmp s1(G, str1);
10237   int sele0 = s1.getIndex();
10238   PyObject *entry = NULL;
10239   ObjectMolecule *obj = NULL;
10240   if(sele0 >= 0)
10241     obj = SelectorGetSingleObjectMolecule(G, sele0);
10242   if(obj) {
10243     int n_atom = obj->NAtom;
10244     int list_len = 0;
10245     int a;
10246     int index = 0;
10247     const char *expr = NULL;
10248     if(ok)
10249       ok = PyList_Check(list);
10250     if(ok) {
10251       list_len = PyList_Size(list);
10252       for(a = 0; a < list_len; a++) {
10253         if(ok)
10254           entry = PyList_GetItem(list, a);
10255         if(ok)
10256           ok = PyList_Check(entry);
10257         if(ok)
10258           ok = (PyList_Size(entry) == 2);
10259         if(ok)
10260           ok = PConvPyIntToInt(PyList_GetItem(entry, 0), &index);
10261         if(ok)
10262           ok = PConvPyStrToStrPtr(PyList_GetItem(entry, 1), &expr);
10263         if(ok)
10264           ok = ((index <= n_atom) && (index > 0));
10265         if(ok)
10266         {
10267           PyCodeObject *expr_co = (PyCodeObject *)Py_CompileString(expr, "", Py_single_input);
10268 	  CoordSet *cs = NULL;
10269 	  if(obj->DiscreteFlag && obj->DiscreteCSet) {
10270 	    cs = obj->DiscreteCSet[index - 1];
10271 	  } else if (obj->NCSet == 1){
10272 	    cs = obj->CSet[0];
10273 	  }
10274           ok =
10275             (expr_co != NULL) &&
10276             PAlterAtom(G, obj, cs, expr_co, read_only, index - 1,
10277                        space);
10278           Py_XDECREF(expr_co);
10279         }
10280         if(ok)
10281           n_eval++;
10282       }
10283     }
10284   } else {
10285     return pymol::make_error("Selection cannot span more than one object.");
10286   }
10287   if(ok) {
10288     if(!quiet) {
10289       if(!read_only) {
10290         PRINTFB(G, FB_Executive, FB_Actions)
10291           " AlterList: modified %i atoms.\n", n_eval ENDFB(G);
10292       } else {
10293         PRINTFB(G, FB_Executive, FB_Actions)
10294           " IterateList: iterated over %i atoms.\n", n_eval ENDFB(G);
10295       }
10296     }
10297     if (!read_only) {
10298       SeqChanged(G);
10299     }
10300   }
10301   if(!ok)
10302     return pymol::make_error("An error occurred.");
10303   else
10304     return n_eval;
10305 #endif
10306 }
10307 
10308 
10309 /*========================================================================*/
10310 #ifdef _WEBGL
10311 #else
ExecutiveIterateState(PyMOLGlobals * G,int state,const char * str1,const char * expr,int read_only,int atomic_props,int quiet,PyObject * space)10312 pymol::Result<int> ExecutiveIterateState(PyMOLGlobals* G, int state,
10313     const char* str1, const char* expr, int read_only, int atomic_props,
10314     int quiet, PyObject* space)
10315 #endif
10316 {
10317 #ifdef _WEBGL
10318 #endif
10319   SelectorTmp tmpsele1(G, str1);
10320   int sele1 = tmpsele1.getIndex();
10321   if(sele1 >= 0) {
10322     int start_state = 0, stop_state = 0;
10323     ObjectMoleculeOpRec op1;
10324 
10325     if(state >= 0) {
10326       start_state = state;
10327       stop_state = state + 1;
10328     } else {
10329       if((state == -2) || (state == -3)) {      /* current state, TO DO: effective object state */
10330         state = SceneGetState(G);
10331         start_state = state;
10332         stop_state = state + 1;
10333       } else if(state == -1) {  /* all states (for the selection) */
10334         start_state = 0;
10335         stop_state = SelectorCountStates(G, sele1);
10336       }
10337     }
10338     ObjectMoleculeOpRecInit(&op1);
10339     op1.i1 = 0;
10340 
10341     for(state = start_state; state < stop_state; state++) {
10342       op1.code = OMOP_AlterState;
10343 #ifdef _WEBGL
10344 #else
10345       op1.s1 = expr;
10346       op1.py_ob1 = space;
10347 #endif
10348       op1.i2 = state;
10349       op1.i3 = read_only;
10350       op1.i4 = atomic_props;
10351       ExecutiveObjMolSeleOp(G, sele1, &op1);
10352     }
10353     if(!read_only) {
10354 #ifndef _PYMOL_NO_UNDO
10355 #endif
10356       // for dynamic_measures
10357       ExecutiveUpdateCoordDepends(G, NULL);
10358       SeqChanged(G);
10359     }
10360     if(!quiet) {
10361       if(!read_only) {
10362         PRINTFB(G, FB_Executive, FB_Actions)
10363           " AlterState: modified %i atom coordinate states.\n", op1.i1 ENDFB(G);
10364       } else {
10365         PRINTFB(G, FB_Executive, FB_Actions)
10366           " IterateState: iterated over %i atom coordinate states.\n", op1.i1 ENDFB(G);
10367       }
10368     }
10369     return op1.i1;
10370   } else {
10371     if(!quiet) {
10372       PRINTFB(G, FB_Executive, FB_Warnings)
10373         "ExecutiveIterateState: No atoms selected.\n" ENDFB(G);
10374     }
10375     return 0;
10376   }
10377 }
10378 
10379 typedef struct {
10380   int priority;
10381   float vertex[3];
10382   AtomInfoType *ai;
10383 } FitVertexRec;
10384 
fVertexOrdered(const FitVertexRec * array,int l,int r)10385 static int fVertexOrdered(const FitVertexRec * array, int l, int r)
10386 {
10387   return (array[l].priority <= array[r].priority);
10388 }
10389 
fAtomOrdered(PyMOLGlobals * G,const AtomInfoType * const * array,int l,int r)10390 static int fAtomOrdered(PyMOLGlobals * G, const AtomInfoType* const* array, int l, int r)
10391 {
10392   return (AtomInfoCompare(G, array[l], array[r]));
10393 }
10394 
fAtomIDOrdered(const AtomInfoType * const * array,int l,int r)10395 static int fAtomIDOrdered(const AtomInfoType* const* array, int l, int r)
10396 {
10397   return (array[l]->id <= array[r]->id);
10398 }
10399 
fAtomRankOrdered(const AtomInfoType * const * array,int l,int r)10400 static int fAtomRankOrdered(const AtomInfoType* const* array, int l, int r)
10401 {
10402   return (array[l]->rank <= array[r]->rank);
10403 }
10404 
fAtomTemp1Ordered(const AtomInfoType * const * array,int l,int r)10405 static int fAtomTemp1Ordered(const AtomInfoType* const* array, int l, int r)
10406 {
10407   return (array[l]->temp1 <= array[r]->temp1);
10408 }
10409 
PackSortedIndices(int n,int * x,int rec_size,void * data)10410 static void PackSortedIndices(int n, int *x, int rec_size, void *data)
10411 {
10412   int a;
10413   for(a = 0; a < n; a++) {
10414     if(a != x[a]) {
10415       memcpy(((char *) data) + (a * rec_size),
10416              ((char *) data) + (x[a] * rec_size), rec_size);
10417     }
10418   }
10419 }
10420 
10421 
10422 /*========================================================================*/
ExecutiveRMS(PyMOLGlobals * G,const char * s1,const char * s2,int mode,float refine,int max_cyc,int quiet,const char * oname,int state1,int state2,int ordered_selections,int matchmaker,ExecutiveRMSInfo * rms_info)10423 int ExecutiveRMS(PyMOLGlobals * G, const char *s1, const char *s2, int mode, float refine,
10424                  int max_cyc, int quiet, const char *oname, int state1, int state2,
10425                  int ordered_selections, int matchmaker, ExecutiveRMSInfo * rms_info)
10426 {
10427   /* mode 0: measure rms without superposition
10428      mode 1: measure rms with trial superposition
10429      mode 2: measure rms with actual superposition */
10430 
10431   int sele1, sele2;
10432   float rms = -1.0;
10433   int a, b;
10434   float inv, *f, *f1, *f2;
10435   ObjectMoleculeOpRec op1;
10436   ObjectMoleculeOpRec op2;
10437   OrthoLineType buffer;
10438   int *flag;
10439   int ok = true;
10440   int repeat;
10441   float v1[3], *v2;
10442   ObjectAlignment *align_to_update = NULL;
10443 
10444   bool ignore_case = SettingGetGlobal_b(G, cSetting_ignore_case);
10445   bool ignore_case_chain = SettingGetGlobal_b(G, cSetting_ignore_case_chain);
10446 
10447   int matrix_mode = SettingGetGlobal_i(G, cSetting_matrix_mode);
10448   if(matrix_mode < 0)
10449     matrix_mode = 0; /* for now */
10450 
10451   if(matchmaker == -1) {
10452     /* matchmaker -1 is the same as matchmaker 0 except that the
10453        selections are not pre-matched prior to calling of this routine */
10454     matchmaker = 0;
10455   }
10456 
10457   sele1 = SelectorIndexByName(G, s1);
10458   sele2 = SelectorIndexByName(G, s2);
10459 
10460   /* this function operates on stored coordinates -- thus transformation
10461      matrices will need to be applied to the resulting atoms */
10462 
10463   // get coordinates
10464   {
10465     auto sele = sele1;
10466     auto state = state1;
10467     auto op = &op1;
10468 
10469     // for both selections
10470     do {
10471       ObjectMoleculeOpRecInit(op);
10472 
10473       if(sele >= 0) {
10474         if(state < 0) {
10475           op->code = OMOP_AVRT;
10476         } else {
10477           op->code = OMOP_StateVRT;
10478           op->i1 = state;
10479         }
10480 
10481         op->nvv1 = 0;                       // length of vc1 (number of atoms with coordinates)
10482         op->vc1 = VLACalloc(int, 1000);     // number of states per atom
10483         op->vv1 = VLACalloc(float, 1000);   // coordinates (sum over states)
10484 
10485         if(mode == 0)
10486           op->i2 = true;            /* if measuring current coordinates, then get global txfd values */
10487 
10488         if(matchmaker || (oname && oname[0]))
10489           op->ai1VLA = VLACalloc(AtomInfoType*, 1000);
10490 
10491         if(ordered_selections)
10492           op->vp1 = VLAlloc(int, 1000);     // selection member "priority"? (MemberType::tag)
10493 
10494         ExecutiveObjMolSeleOp(G, sele, op);
10495 
10496         for(a = 0; a < op->nvv1; a++) {
10497           inv = (float) op->vc1[a]; /* average over coordinate sets */
10498           if(inv) {
10499             f = op->vv1 + (a * 3);
10500             scale3f(f, 1.F / inv, f);
10501           }
10502         }
10503       }
10504 
10505       // second iteration done
10506       if (sele == sele2)
10507         break;
10508 
10509       sele = sele2;
10510       state = state2;
10511       op = &op2;
10512 
10513     } while (true);
10514   }
10515 
10516   if(op1.vv1 && op2.vv1) {
10517     if(op1.nvv1 && op2.nvv1) {
10518       ObjectMolecule *mobile_obj = NULL;
10519 
10520       int n_pair = 0;
10521 
10522       if(!(mobile_obj = SelectorGetSingleObjectMolecule(G, sele1))) {
10523         if(mode != 2) {
10524           PRINTFB(G, FB_Executive, FB_Warnings)
10525             "Executive-Warning: Mobile selection spans more than one object.\n" ENDFB(G);
10526         } else {
10527           PRINTFB(G, FB_Executive, FB_Errors)
10528             "Executive-Error: Mobile selection spans more than one object. Aborting.\n"
10529             ENDFB(G);
10530           ok = false;
10531         }
10532       }
10533 
10534       if(ok && op1.nvv1 && op2.nvv1 && (matchmaker > 0)) {
10535         /* matchmaker 0 is the default... by internal atom ordering only */
10536         int *idx1 = pymol::malloc<int>(op1.nvv1);
10537         int *idx2 = pymol::malloc<int>(op2.nvv1);
10538         int sort_flag = false;
10539         if(!(idx1 && idx2))
10540           ok = false;
10541         else {
10542           switch (matchmaker) {
10543           case 1:              /* by atom info-based ordering */
10544             UtilSortIndexGlobals(G, op1.nvv1, op1.ai1VLA, idx1,
10545                                  (UtilOrderFnGlobals *) fAtomOrdered);
10546             UtilSortIndexGlobals(G, op2.nvv1, op2.ai1VLA, idx2,
10547                                  (UtilOrderFnGlobals *) fAtomOrdered);
10548             sort_flag = true;
10549             break;
10550           case 2:              /* by matching atom identifiers */
10551             UtilSortIndex(op1.nvv1, op1.ai1VLA, idx1, (UtilOrderFn *) fAtomIDOrdered);
10552             UtilSortIndex(op2.nvv1, op2.ai1VLA, idx2, (UtilOrderFn *) fAtomIDOrdered);
10553             sort_flag = true;
10554             break;
10555           case 3:              /* by matching atom ranks */
10556             UtilSortIndex(op1.nvv1, op1.ai1VLA, idx1, (UtilOrderFn *) fAtomRankOrdered);
10557             UtilSortIndex(op2.nvv1, op2.ai1VLA, idx2, (UtilOrderFn *) fAtomRankOrdered);
10558             sort_flag = true;
10559             break;
10560           case 4:              /* by internal atom indexes (stored in temp1 kludge field) */
10561             UtilSortIndex(op1.nvv1, op1.ai1VLA, idx1, (UtilOrderFn *) fAtomTemp1Ordered);
10562             UtilSortIndex(op2.nvv1, op2.ai1VLA, idx2, (UtilOrderFn *) fAtomTemp1Ordered);
10563             sort_flag = true;
10564             break;
10565           }
10566           if(sort_flag) {
10567             /* GOD this is SO ugly! */
10568 
10569             if(op1.vv1) {
10570               float *tmp = VLAlloc(float, op1.nvv1 * 3);
10571               if(!tmp)
10572                 ok = false;
10573               else {
10574                 UtilApplySortedIndices(op1.nvv1, idx1, 3 * sizeof(float), op1.vv1, tmp);
10575                 VLAFreeP(op1.vv1);
10576                 op1.vv1 = tmp;
10577               }
10578             }
10579             if(op1.vc1) {
10580               int *tmp = VLAlloc(int, op1.nvv1);
10581               if(!tmp)
10582                 ok = false;
10583               else {
10584                 UtilApplySortedIndices(op1.nvv1, idx1, sizeof(int), op1.vc1, tmp);
10585                 VLAFreeP(op1.vc1);
10586                 op1.vc1 = tmp;
10587               }
10588             }
10589             if(op1.vp1) {
10590               int *tmp = VLAlloc(int, op1.nvv1);
10591               if(!tmp)
10592                 ok = false;
10593               else {
10594                 UtilApplySortedIndices(op1.nvv1, idx1, sizeof(int), op1.vp1, tmp);
10595                 VLAFreeP(op1.vp1);
10596                 op1.vp1 = tmp;
10597               }
10598             }
10599             if(op1.ai1VLA) {
10600               AtomInfoType **tmp = VLACalloc(AtomInfoType *, op1.nvv1);
10601               if(!tmp)
10602                 ok = false;
10603               else {
10604                 UtilApplySortedIndices(op1.nvv1, idx1, sizeof(AtomInfoType *), op1.ai1VLA,
10605                                        tmp);
10606                 VLAFreeP(op1.ai1VLA);
10607                 op1.ai1VLA = tmp;
10608               }
10609             }
10610 
10611             if(op2.vv1) {
10612               float *tmp = VLAlloc(float, op2.nvv1 * 3);
10613               if(!tmp)
10614                 ok = false;
10615               else {
10616                 UtilApplySortedIndices(op2.nvv1, idx2, 3 * sizeof(float), op2.vv1, tmp);
10617                 VLAFreeP(op2.vv1);
10618                 op2.vv1 = tmp;
10619               }
10620             }
10621             if(op2.vc1) {
10622               int *tmp = VLAlloc(int, op2.nvv1);
10623               if(!tmp)
10624                 ok = false;
10625               else {
10626                 UtilApplySortedIndices(op2.nvv1, idx2, sizeof(int), op2.vc1, tmp);
10627                 VLAFreeP(op2.vc1);
10628                 op2.vc1 = tmp;
10629               }
10630             }
10631             if(op2.vp1) {
10632               int *tmp = VLAlloc(int, op2.nvv1);
10633               if(!tmp)
10634                 ok = false;
10635               else {
10636                 UtilApplySortedIndices(op2.nvv1, idx2, sizeof(int), op2.vp1, tmp);
10637                 VLAFreeP(op2.vp1);
10638                 op2.vp1 = tmp;
10639               }
10640             }
10641             if(op2.ai1VLA) {
10642               AtomInfoType **tmp = VLACalloc(AtomInfoType *, op2.nvv1);
10643               if(!tmp)
10644                 ok = false;
10645               else {
10646                 UtilApplySortedIndices(op2.nvv1, idx2, sizeof(AtomInfoType *), op2.ai1VLA,
10647                                        tmp);
10648                 VLAFreeP(op2.ai1VLA);
10649                 op2.ai1VLA = tmp;
10650               }
10651             }
10652 
10653           }
10654         }
10655 
10656         if(matchmaker != 0) {
10657           int n1 = 0, n2 = 0, c1 = 0, c2 = 0;
10658           int cmp;
10659 
10660           while((n1 < op1.nvv1) && (n2 < op2.nvv1)) {
10661             cmp = 0;
10662             switch (matchmaker) {
10663             case 1:            /* insure that AtomInfoType matches */
10664               if(AtomInfoMatch(G, op1.ai1VLA[n1], op2.ai1VLA[n2], ignore_case, ignore_case_chain))
10665                 cmp = 0;
10666               else
10667                 cmp = AtomInfoCompare(G, op1.ai1VLA[n1], op2.ai1VLA[n2]);
10668               printf("%d-%d %d-%d: %d\n", c1, n1, c2, n2, cmp);
10669               break;
10670             case 2:            /* ID */
10671             case 3:            /* rank */
10672               {
10673                 int val1;
10674                 int val2;
10675 
10676                 switch (matchmaker) {
10677                 case 2:        /* ID */
10678                   val1 = op1.ai1VLA[n1]->id;
10679                   val2 = op2.ai1VLA[n2]->id;
10680                   break;
10681                 case 3:        /* rank */
10682                   val1 = op1.ai1VLA[n1]->rank;
10683                   val2 = op2.ai1VLA[n2]->rank;
10684                   break;
10685                 case 4:        /* index (via temp1) */
10686                   val1 = op1.ai1VLA[n1]->temp1;
10687                   val2 = op2.ai1VLA[n2]->temp1;
10688                   break;
10689                 default:
10690                   val1 = 0;
10691                   val2 = 0;
10692                   break;
10693                 }
10694                 if(val1 == val2)
10695                   cmp = 0;
10696                 else if(val1 < val2)
10697                   cmp = -1;
10698                 else
10699                   cmp = 1;
10700               }
10701               break;
10702             }
10703             if(!cmp) {          /* match found */
10704               idx1[c1++] = n1++;
10705               idx2[c2++] = n2++;
10706               n_pair++;
10707             } else if(cmp < 0) {        /* op1 below op2 */
10708               n1++;
10709             } else {            /* op2 below op1 */
10710               n2++;
10711             }
10712           }
10713 
10714           if(n_pair) {
10715             if(op1.vv1)
10716               PackSortedIndices(n_pair, idx1, 3 * sizeof(float), op1.vv1);
10717             if(op1.vc1)
10718               PackSortedIndices(n_pair, idx1, sizeof(int), op1.vc1);
10719             if(op1.vp1)
10720               PackSortedIndices(n_pair, idx1, sizeof(int), op1.vp1);
10721             if(op1.ai1VLA)
10722               PackSortedIndices(n_pair, idx1, sizeof(AtomInfoType *), op1.ai1VLA);
10723 
10724             if(op2.vv1)
10725               PackSortedIndices(n_pair, idx2, 3 * sizeof(float), op2.vv1);
10726             if(op2.vc1)
10727               PackSortedIndices(n_pair, idx2, sizeof(int), op2.vc1);
10728             if(op2.vp1)
10729               PackSortedIndices(n_pair, idx2, sizeof(int), op2.vp1);
10730             if(op2.ai1VLA)
10731               PackSortedIndices(n_pair, idx2, sizeof(AtomInfoType *), op2.ai1VLA);
10732           }
10733         }
10734         FreeP(idx1);
10735         FreeP(idx2);
10736       } else if(op1.nvv1 != op2.nvv1) {
10737         sprintf(buffer, "Atom counts between selections don't match (%d vs %d)",
10738                 op1.nvv1, op2.nvv1);
10739         ErrMessage(G, "ExecutiveRMS", buffer);
10740         n_pair = 0;
10741         ok = false;
10742       } else {
10743         n_pair = 0;
10744         for(a = 0; a < op1.nvv1; ++a) { // for atoms in selection
10745           if (op1.vc1[a] && op2.vc1[a]) { // check state counts
10746             if (n_pair < a) { // copy over if necessary
10747               copy3(op1.vv1 + 3 * a, op1.vv1 + 3 * n_pair);
10748               copy3(op2.vv1 + 3 * a, op2.vv1 + 3 * n_pair);
10749               if(op1.ai1VLA) op1.ai1VLA[n_pair] = op1.ai1VLA[a];
10750               if(op2.ai1VLA) op2.ai1VLA[n_pair] = op2.ai1VLA[a];
10751               if(op1.vp1) op1.vp1[n_pair] = op1.vp1[a];
10752               if(op2.vp1) op2.vp1[n_pair] = op2.vp1[a];
10753               op1.vc1[n_pair] = op1.vc1[a];
10754               op2.vc1[n_pair] = op2.vc1[a];
10755             }
10756             ++n_pair;
10757           }
10758         }
10759       }
10760 
10761       if(n_pair) {
10762         /* okay -- we're on track to do an alignment */
10763 
10764         if(ordered_selections && op1.vp1 && op2.vp1) {
10765           /* if we expected ordered selections and have priorities,
10766              then we may need to sort vertices */
10767 
10768           int sort_flag1 = false, sort_flag2 = false;
10769           int well_defined1 = true, well_defined2 = true;
10770 
10771           for(a = 0; a < (n_pair - 1); a++) {
10772             /*          printf("op1 vertex %d priority %d\n",a,op1.vp1[a]);
10773                printf("op2 vertex %d priority %d\n",a,op2.vp1[a]); */
10774 
10775             if(op1.vp1[a] > op1.vp1[a + 1])
10776               sort_flag1 = true;
10777             else if(op1.vp1[a] == op1.vp1[a + 1])
10778               well_defined1 = false;
10779             if(op2.vp1[a] > op2.vp1[a + 1])
10780               sort_flag2 = true;
10781             else if(op2.vp1[a] == op2.vp1[a + 1])
10782               well_defined2 = false;
10783           }
10784 
10785           if(sort_flag1 || sort_flag2) {
10786             if(!(well_defined1 || well_defined2)) {
10787               PRINTFB(G, FB_Executive, FB_Warnings)
10788                 "Executive-Warning: Ordering requested but not well defined.\n" ENDFB(G);
10789             } else {
10790               FitVertexRec *vert = pymol::malloc<FitVertexRec>(n_pair);
10791 
10792               if(sort_flag1) {
10793                 float *src, *dst;
10794                 src = op1.vv1;
10795                 for(a = 0; a < n_pair; a++) {
10796                   vert[a].priority = op1.vp1[a];
10797                   dst = vert[a].vertex;
10798                   copy3f(src, dst);
10799                   src += 3;
10800                 }
10801                 UtilSortInPlace(G, vert, n_pair, sizeof(FitVertexRec),
10802                                 (UtilOrderFn *) fVertexOrdered);
10803                 dst = op1.vv1;
10804                 for(a = 0; a < n_pair; a++) {
10805                   src = vert[a].vertex;
10806                   copy3f(src, dst);
10807                   dst += 3;
10808                 }
10809               }
10810 
10811               if(sort_flag2) {
10812                 float *src, *dst;
10813                 src = op2.vv1;
10814                 for(a = 0; a < n_pair; a++) {
10815                   vert[a].priority = op2.vp1[a];
10816                   dst = vert[a].vertex;
10817                   copy3f(src, dst);
10818                   src += 3;
10819                 }
10820                 UtilSortInPlace(G, vert, n_pair, sizeof(FitVertexRec),
10821                                 (UtilOrderFn *) fVertexOrdered);
10822                 dst = op2.vv1;
10823                 for(a = 0; a < n_pair; a++) {
10824                   src = vert[a].vertex;
10825                   copy3f(src, dst);
10826                   dst += 3;
10827                 }
10828               }
10829 
10830               FreeP(vert);
10831             }
10832           }
10833         }
10834 
10835         if(rms_info) {
10836           rms_info->initial_n_atom = n_pair;
10837           rms_info->n_cycles_run = 0;
10838           rms_info->final_n_atom = n_pair;      /* in case there is no refinement */
10839         }
10840 
10841         if(mode != 0) {
10842           rms = MatrixFitRMSTTTf(G, n_pair, op1.vv1, op2.vv1, NULL, op2.ttt);
10843           if(rms_info) {
10844             rms_info->initial_rms = rms;
10845             rms_info->final_rms = rms;
10846           }
10847           repeat = true;
10848           b = 0;
10849           while(repeat) {
10850             repeat = false;
10851             b++;
10852             if(b > max_cyc)
10853               break;
10854             if((refine > R_SMALL4) && (rms > R_SMALL4)) {
10855               int n_next = n_pair;
10856               AtomInfoType **ai1, **ai2;
10857 
10858               flag = pymol::malloc<int>(n_pair);
10859 
10860               if(flag) {
10861                 for(a = 0; a < n_pair; a++) {
10862                   MatrixTransformTTTfN3f(1, v1, op2.ttt, op1.vv1 + (a * 3));
10863                   v2 = op2.vv1 + (a * 3);
10864                   if((diff3f(v1, v2) / rms) > refine) {
10865                     flag[a] = false;
10866                     repeat = true;
10867                   } else
10868                     flag[a] = true;
10869                 }
10870                 f1 = op1.vv1;
10871                 f2 = op2.vv1;
10872                 ai1 = op1.ai1VLA;
10873                 ai2 = op2.ai1VLA;
10874                 for(a = 0; a < n_pair; a++) {
10875                   if(!flag[a]) {
10876                     n_next--;
10877                   } else {
10878                     copy3f(op1.vv1 + (3 * a), f1);
10879                     copy3f(op2.vv1 + (3 * a), f2);
10880                     f1 += 3;
10881                     f2 += 3;
10882                     if(ai1 && ai2) {    /* make sure we keep track of which atoms are aligned */
10883                       *(ai1++) = op1.ai1VLA[a];
10884                       *(ai2++) = op2.ai1VLA[a];
10885                     }
10886                   }
10887                 }
10888                 if(!quiet && (n_next != n_pair)) {
10889                   PRINTFB(G, FB_Executive, FB_Actions)
10890                     " %s: %d atoms rejected during cycle %d (RMSD=%0.2f).\n", __func__,
10891                     n_pair - n_next, b, rms ENDFB(G);
10892                 }
10893                 n_pair = n_next;
10894                 FreeP(flag);
10895                 if(n_pair) {
10896                   rms = MatrixFitRMSTTTf(G, n_pair, op1.vv1, op2.vv1, NULL, op2.ttt);
10897                   if(rms_info) {
10898                     rms_info->n_cycles_run = b;
10899                     rms_info->final_n_atom = n_pair;
10900                     rms_info->final_rms = rms;
10901                   }
10902                 } else
10903                   break;
10904               }
10905             }
10906           }
10907         } else {                /* mode == 0 -- simple RMS, with no coordinate movement */
10908           rms = MatrixGetRMS(G, n_pair, op1.vv1, op2.vv1, NULL);
10909           if(rms_info) {
10910             rms_info->initial_rms = rms;
10911             rms_info->final_rms = rms;
10912           }
10913         }
10914       }
10915       if(!n_pair) {
10916         PRINTFB(G, FB_Executive, FB_Results)
10917           " Executive: Error -- no atoms left after refinement!\n" ENDFB(G);
10918         ok = false;
10919       }
10920 
10921       if(ok) {
10922         if(!quiet) {
10923           PRINTFB(G, FB_Executive, FB_Results)
10924             " Executive: RMSD = %8.3f (%d to %d atoms)\n", rms, n_pair, n_pair ENDFB(G);
10925         }
10926         if(oname && oname[0]) {
10927             int align_state = state2;
10928             ObjectMolecule *trg_obj = SelectorGetSingleObjectMolecule(G, sele2);
10929 
10930             if(align_state < 0) {
10931               align_state = SceneGetState(G);
10932             }
10933 
10934             /* we're going to create/update an alignment object */
10935 
10936             {
10937               /* Get unique ids and construct the alignment vla */
10938               pymol::vla<int> align_vla(n_pair * 3);
10939 
10940               {
10941                 int *id_p = align_vla.data();
10942                 int i;
10943                 for(i = 0; i < n_pair; i++) {
10944                   id_p[0] = AtomInfoCheckUniqueID(G, op2.ai1VLA[i]);    /* target */
10945                   id_p[1] = AtomInfoCheckUniqueID(G, op1.ai1VLA[i]);
10946                   id_p[2] = 0;
10947                   id_p += 3;
10948                 }
10949                 VLASize(align_vla, int, n_pair * 3);
10950               }
10951               {
10952                 ObjectAlignment *obj = NULL;
10953 
10954                 /* does object already exist? */
10955                 {
10956                   CObject *execObj = ExecutiveFindObjectByName(G, oname);
10957                   if(execObj && (execObj->type != cObjectAlignment))
10958                     ExecutiveDelete(G, oname);
10959                   else
10960                     obj = (ObjectAlignment *) execObj;
10961                 }
10962                 obj =
10963                   ObjectAlignmentDefine(G, obj, align_vla, align_state, true, trg_obj,
10964                                         mobile_obj);
10965                 obj->Color = ColorGetIndex(G, "yellow");
10966                 ObjectSetName((CObject *) obj, oname);
10967                 ExecutiveManageObject(G, (CObject *) obj, 0, quiet);
10968                 align_to_update = obj;
10969                 SceneInvalidate(G);
10970               }
10971             }
10972         }
10973         if(ok && mode == 2) {
10974           if(matrix_mode>0) {
10975 
10976             ObjectMolecule *src_obj, *trg_obj;
10977             src_obj = SelectorGetFirstObjectMolecule(G, sele1); /* get at least one object */
10978             trg_obj = SelectorGetSingleObjectMolecule(G, sele2);
10979 
10980             /* first we need to make sure that the object being moved
10981                matches the target with respect to both the TTT and the
10982                object's state matrix (if any) */
10983 
10984             if(src_obj && trg_obj) {
10985               ExecutiveMatrixCopy(G, trg_obj->Name, src_obj->Name, 1, 1,        /* TTT mode */
10986                                   state2, state1, false, 0, quiet);
10987 
10988               ExecutiveMatrixCopy(G, trg_obj->Name, src_obj->Name, 2, 2,        /* Object state mode */
10989                                   state2, state1, false, 0, quiet);
10990 
10991               switch (matrix_mode) {
10992               case 1:          /* TTTs */
10993                 ExecutiveCombineObjectTTT(G, src_obj->Name, op2.ttt, true, -1);
10994                 break;
10995               case 2:
10996                 {
10997                   double homo[16], *src_homo;
10998                   convertTTTfR44d(op2.ttt, homo);
10999                   if(ExecutiveGetObjectMatrix
11000                      (G, src_obj->Name, state1, &src_homo, false)) {
11001                     left_multiply44d44d(src_homo, homo);
11002                     ExecutiveSetObjectMatrix(G, src_obj->Name, state1, homo);
11003                   }
11004                 }
11005                 break;
11006               }
11007               /* next we need to update the object's TTT matrix to reflect
11008                  the transformation */
11009             }
11010           } else {              /* matrix_mode is zero -- legacy behavior */
11011             /* this will transform the actual coordinates */
11012             op2.code = OMOP_TTTF;
11013             ExecutiveObjMolSeleOp(G, sele1, &op2);
11014           }
11015         }
11016       }
11017     } else {
11018       ErrMessage(G, __func__, "No atoms selected.");
11019       ok = false;
11020     }
11021   }
11022 
11023   if(align_to_update) {
11024     align_to_update->update();
11025   }
11026 
11027   VLAFreeP(op1.vv1);
11028   VLAFreeP(op2.vv1);
11029   VLAFreeP(op1.vc1);
11030   VLAFreeP(op2.vc1);
11031   VLAFreeP(op1.vp1);
11032   VLAFreeP(op2.vp1);
11033   VLAFreeP(op1.ai1VLA);
11034   VLAFreeP(op2.ai1VLA);
11035   return (ok);
11036 }
11037 
11038 
11039 /*========================================================================*/
11040 /**
11041  * Implementation of `cmd.identify()`
11042  *
11043  * @param s1 atom selection expression
11044  * @param mode 0 for index only, 1 for (obj, index)
11045  * @param[out] indexVLA
11046  * @param[out] objVLA
11047  * @return number of atoms or -1 on selection error
11048  */
ExecutiveIdentifyObjects(PyMOLGlobals * G,const char * s1,int mode,int ** indexVLA,ObjectMolecule *** objVLA)11049 int ExecutiveIdentifyObjects(PyMOLGlobals * G, const char *s1, int mode, int **indexVLA,
11050                              ObjectMolecule *** objVLA)
11051 {
11052   SelectorTmp tmpsele1(G, s1);
11053   int sele1 = tmpsele1.getIndex();
11054   ObjectMoleculeOpRec op2;
11055   if(sele1 >= 0) {
11056     ObjectMoleculeOpRecInit(&op2);
11057     op2.code = OMOP_IdentifyObjects;
11058     if (mode != 0) {
11059       op2.obj1VLA = VLAlloc(ObjectMolecule*, 1000);
11060     }
11061     op2.i1VLA = VLAlloc(int, 1000);
11062     op2.i1 = 0;
11063     ExecutiveObjMolSeleOp(G, sele1, &op2);
11064     VLASize(op2.i1VLA, int, op2.i1);
11065     if (mode != 0) {
11066       VLASize(op2.obj1VLA, ObjectMolecule*, op2.i1);
11067     }
11068     (*indexVLA) = op2.i1VLA;
11069     (*objVLA) = op2.obj1VLA;
11070   } else {
11071     return -1;
11072   }
11073   return (op2.i1);
11074 }
11075 
11076 
11077 /*========================================================================*/
ExecutiveIndex(PyMOLGlobals * G,const char * s1,int mode,int ** indexVLA,ObjectMolecule *** objVLA)11078 int ExecutiveIndex(PyMOLGlobals * G, const char *s1, int mode, int **indexVLA,
11079                    ObjectMolecule *** objVLA)
11080 {
11081   ObjectMoleculeOpRec op2;
11082 
11083   SelectorTmp tmpsele1(G, s1);
11084   int sele1 = tmpsele1.getIndex();
11085 
11086   if(sele1 >= 0) {
11087     ObjectMoleculeOpRecInit(&op2);
11088     op2.code = OMOP_Index;
11089     op2.obj1VLA = VLAlloc(ObjectMolecule *, 1000);
11090     op2.i1VLA = VLAlloc(int, 1000);
11091     op2.i1 = 0;
11092     ExecutiveObjMolSeleOp(G, sele1, &op2);
11093     VLASize(op2.i1VLA, int, op2.i1);
11094     VLASize(op2.obj1VLA, ObjectMolecule *, op2.i1);
11095     (*indexVLA) = op2.i1VLA;
11096     (*objVLA) = op2.obj1VLA;
11097   } else {
11098     return -1; // invalid selection
11099   }
11100   return (op2.i1);
11101 }
11102 
11103 
11104 /*========================================================================*/
11105 /**
11106  * Fit states or calculate ensemble RMSD
11107  *
11108  * @param s1 atom selection expression
11109  * @param target reference state
11110  * @param mode 2=intra_fit, 1=intra_rms, 0=intra_rms_cur
11111  * @param mix intra_fit only, average the prior target coordinates
11112  */
ExecutiveRMSStates(PyMOLGlobals * G,const char * s1,int target,int mode,int quiet,int mix)11113 pymol::Result<pymol::vla<float>> ExecutiveRMSStates(
11114     PyMOLGlobals * G, const char *s1, int target, int mode, int quiet, int mix)
11115 {
11116   SelectorTmp tmpsele1(G, s1);
11117   int sele1 = tmpsele1.getIndex();
11118 
11119   ObjectMoleculeOpRec op1;
11120   ObjectMoleculeOpRec op2;
11121   float *result = NULL;
11122   int ok = true;
11123 
11124   ObjectMoleculeOpRecInit(&op1);
11125   ObjectMoleculeOpRecInit(&op2);
11126   op1.vv1 = NULL;
11127   op2.vv1 = NULL;
11128 
11129   ObjectMolecule* obj = SelectorGetSingleObjectMolecule(G, sele1);
11130 
11131   if (!obj) {
11132     if(mode != 2) {
11133       PRINTFB(G, FB_Executive, FB_Warnings)
11134         "Executive-Warning: Mobile selection spans more than one object.\n" ENDFB(G);
11135     } else {
11136       return pymol::make_error("Mobile selection spans more than one object.");
11137     }
11138   }
11139 
11140   if(ok && sele1 >= 0) {
11141     op1.code = OMOP_SVRT;
11142     op1.nvv1 = 0;
11143     op1.i1 = target;
11144     op1.vv1 = (float *) VLAMalloc(1000, sizeof(float), 5, 0);
11145     op1.i1VLA = VLAlloc(int, 1000);
11146     ExecutiveObjMolSeleOp(G, sele1, &op1);
11147 
11148     op2.vv2 = op1.vv1;
11149     op2.nvv2 = op1.nvv1;
11150     op2.i1VLA = op1.i1VLA;
11151     op2.i2 = target;
11152     op2.i1 = mode;
11153     op2.i3 = mix;
11154     op2.f1VLA = VLAlloc(float, 10);
11155     VLASize(op2.f1VLA, float, 0);       /* failsafe */
11156     op2.vv1 = (float *) VLAMalloc(1000, sizeof(float), 5, 0);
11157     op2.code = OMOP_SFIT;
11158     op2.nvv1 = 0;
11159     ExecutiveObjMolSeleOp(G, sele1, &op2);
11160     result = op2.f1VLA;
11161     VLAFreeP(op1.vv1);
11162     VLAFreeP(op1.i1VLA);
11163     VLAFreeP(op2.vv1);
11164 
11165     if (mode == 2) {
11166       ExecutiveUpdateCoordDepends(G, obj);
11167     }
11168   }
11169   return pymol::vla_take_ownership(result);
11170 }
11171 
11172 
11173 /*========================================================================*/
ExecutiveRMSPairs(PyMOLGlobals * G,const std::vector<SelectorTmp> & sele,int mode,bool quiet)11174 float ExecutiveRMSPairs(PyMOLGlobals* G, const std::vector<SelectorTmp>& sele,
11175     int mode, bool quiet)
11176 {
11177   int sele1, sele2;
11178   int a, c;
11179   float rms = -1.0, inv, *f;
11180   OrthoLineType buffer;
11181 
11182   ObjectMoleculeOpRec op1;
11183   ObjectMoleculeOpRec op2;
11184   OrthoLineType combi, s1;
11185 
11186   ObjectMoleculeOpRecInit(&op1);
11187   ObjectMoleculeOpRecInit(&op2);
11188   op1.nvv1 = 0;
11189   op1.vc1 = (int *) VLAMalloc(1000, sizeof(int), 5, 1);
11190   op1.vv1 = (float *) VLAMalloc(1000, sizeof(float), 5, 1);     /* auto-zero */
11191   op1.code = OMOP_AVRT;
11192 
11193   op2.nvv1 = 0;
11194   op2.vc1 = (int *) VLAMalloc(1000, sizeof(int), 5, 1);
11195   op2.vv1 = (float *) VLAMalloc(1000, sizeof(float), 5, 1);     /* auto-zero */
11196   op2.code = OMOP_AVRT;
11197 
11198   strcpy(combi, "(");
11199   c = 0;
11200   auto pairs = sele.size() / 2;
11201   for(a = 0; a < pairs; a++) {
11202     sele1 = sele[c].getIndex();
11203     if(sele1 >= 0)
11204       ExecutiveObjMolSeleOp(G, sele1, &op1);
11205     strcat(combi, sele[c].getName());
11206     if(a < (pairs - 1))
11207       strcat(combi, " or ");
11208     c++;
11209     sele2 = sele[c].getIndex();
11210     if(sele2 >= 0)
11211       ExecutiveObjMolSeleOp(G, sele2, &op2);
11212     c++;
11213   }
11214   strcat(combi, ")");
11215   for(a = 0; a < op1.nvv1; a++) {
11216     inv = (float) op1.vc1[a];
11217     if(inv) {
11218       f = op1.vv1 + (a * 3);
11219       inv = 1.0F / inv;
11220       *(f++) *= inv;
11221       *(f++) *= inv;
11222       *(f++) *= inv;
11223     }
11224   }
11225   for(a = 0; a < op2.nvv1; a++) {
11226     inv = (float) op2.vc1[a];
11227     if(inv) {
11228       f = op2.vv1 + (a * 3);
11229       inv = 1.0F / inv;
11230       *(f++) *= inv;
11231       *(f++) *= inv;
11232       *(f++) *= inv;
11233     }
11234   }
11235   if(op1.vv1 && op2.vv1) {
11236     if(op1.nvv1 != op2.nvv1) {
11237       sprintf(buffer, "Atom counts between selection sets don't match (%d != %d).",
11238               op1.nvv1, op2.nvv1);
11239       ErrMessage(G, __func__, buffer);
11240     } else if(op1.nvv1) {
11241       if(mode != 0)
11242         rms = MatrixFitRMSTTTf(G, op1.nvv1, op1.vv1, op2.vv1, NULL, op2.ttt);
11243       else
11244         rms = MatrixGetRMS(G, op1.nvv1, op1.vv1, op2.vv1, NULL);
11245 
11246       if (!quiet)
11247       PRINTFB(G, FB_Executive, FB_Results)
11248         " %s: RMSD = %8.3f (%d to %d atoms)\n", __func__, rms, op1.nvv1, op2.nvv1 ENDFB(G);
11249 
11250       op2.code = OMOP_TTTF;
11251       SelectorGetTmp(G, combi, s1);
11252       sele1 = SelectorIndexByName(G, s1);
11253       ExecutiveObjMolSeleOp(G, sele1, &op2);
11254       SelectorFreeTmp(G, s1);
11255     } else {
11256       ErrMessage(G, __func__, "No atoms selected.");
11257     }
11258   }
11259   VLAFreeP(op1.vv1);
11260   VLAFreeP(op2.vv1);
11261   VLAFreeP(op1.vc1);
11262   VLAFreeP(op2.vc1);
11263   return (rms);
11264 }
11265 
11266 
11267 /*========================================================================*/
ExecutiveUpdateObjectSelection(PyMOLGlobals * G,CObject * obj)11268 void ExecutiveUpdateObjectSelection(PyMOLGlobals * G, CObject * obj)
11269 {
11270   if(obj->type == cObjectMolecule) {
11271     SelectorUpdateObjectSele(G, (ObjectMolecule *) obj);
11272   }
11273 }
11274 
11275 
11276 /*========================================================================*/
11277 /**
11278  * Reset camera view or object TTT matrix. Stores key frames for modified
11279  * objects if `movie_auto_store=on`.
11280  *
11281  * @param name Empty, "all", "same", or object name pattern
11282  *
11283  * - empty name: Reset camera view
11284  * - "all": Reset TTT matrices and store key frames for all objects
11285  * - "same": Reset TTT matrices and store key frames for objects which currently have any key frames
11286  * - pattern: Reset TTT matrices and store key frames for objects which match the pattern
11287  */
ExecutiveReset(PyMOLGlobals * G,pymol::zstring_view name)11288 pymol::Result<> ExecutiveReset(PyMOLGlobals* G, pymol::zstring_view name)
11289 {
11290   if (name.empty()) {
11291     SceneResetMatrix(G);
11292     ExecutiveWindowZoom(G, cKeywordAll, 0.0, -1, 0, 0, true);   /* reset does all states */
11293     return {};
11294   }
11295 
11296   bool do_reset_all = name == cKeywordAll;
11297   auto store = SettingGet<bool>(G, cSetting_movie_auto_store);
11298 
11299   /**
11300    * @param any_spec_level If false, then filter for objects with spec level >= 0
11301    */
11302   auto reset_rec = [&](SpecRec& rec, bool any_spec_level = true) {
11303     CObject* obj = rec.obj;
11304     if (rec.type == cExecObject &&
11305         (any_spec_level || ObjectGetSpecLevel(obj, 0) >= 0)) {
11306       ObjectResetTTT(obj, store);
11307       obj->invalidate(cRepNone, cRepInvExtents, -1);
11308     }
11309   };
11310 
11311   if (do_reset_all || name == cKeywordSame) {
11312     for (auto& rec : pymol::make_list_adapter(G->Executive->Spec)) {
11313       reset_rec(rec, do_reset_all);
11314     }
11315   } else {
11316     for (auto& rec : ExecutiveGetSpecRecsFromPattern(G, name)) {
11317       reset_rec(rec);
11318     }
11319   }
11320 
11321   if (store && SettingGet<bool>(G, cSetting_movie_auto_interpolate)) {
11322     ExecutiveMotionReinterpolate(G);
11323   }
11324 
11325   SceneInvalidate(G);
11326 
11327   return {};
11328 }
11329 
11330 
11331 /*========================================================================*/
ExecutiveDrawNow(PyMOLGlobals * G)11332 void ExecutiveDrawNow(PyMOLGlobals * G)
11333 {
11334   CExecutive *I = G->Executive;
11335 
11336   if(PyMOL_GetIdleAndReady(G->PyMOL) && !SettingGetGlobal_b(G, cSetting_suspend_deferred))
11337     OrthoExecDeferred(G);
11338   if(!SettingGetGlobal_b(G, cSetting_suspend_updates)){
11339     int stereo_mode = SettingGetGlobal_i(G, cSetting_stereo_mode);
11340     int stereo = SettingGetGlobal_i(G, cSetting_stereo);
11341     if(G->HaveGUI && G->ValidContext) {
11342       glMatrixMode(GL_MODELVIEW);       /* why is this necessary?  is it? */
11343     }
11344 
11345     ExecutiveUpdateSceneMembers(G);
11346     SceneUpdate(G, false);
11347     if(WizardUpdate(G))
11348       SceneUpdate(G, false);
11349     if (stereo){
11350       switch (stereo_mode) {
11351       case cStereo_geowall:
11352 	{
11353 	  int width = G->Option->winX;
11354 	  int height = G->Option->winY;
11355 	  glViewport(0, 0, width / 2, height);
11356 	  OrthoDoDraw(G, 1);
11357 	  OrthoDoDraw(G, 2);
11358 	  glViewport(0, 0, width, height);
11359 	}
11360 	break;
11361 #ifdef _PYMOL_OPENVR
11362       case cStereo_openvr:
11363         {
11364           Block* scene_block = SceneGetBlock(G);
11365           int scene_width = scene_block->rect.right - scene_block->rect.left;
11366           int scene_height = scene_block->rect.top - scene_block->rect.bottom;
11367           OpenVRFrameStart(G);
11368           float matrix[16];
11369           SceneGetModel2WorldMatrix(G, matrix);
11370           OpenVRHandleInput(G, scene_block->rect.left, scene_block->rect.bottom, scene_width, scene_height, matrix);
11371           OrthoDoDraw(G, -1);
11372           if (SettingGetGlobal_b(G, cSetting_openvr_cut_laser) && OpenVRIsScenePickerActive(G)) {
11373             int x = scene_block->rect.left + scene_width / 2;
11374             int y = scene_block->rect.bottom + scene_height / 2;
11375             float atomWorldPos[3];
11376             ScenePickAtomInWorld(G, x, y, atomWorldPos);
11377             OpenVRUpdateScenePickerLength(G, atomWorldPos);
11378           }
11379           OpenVRFrameFinish(G);
11380           PyMOL_NeedRedisplay(G->PyMOL);
11381         }
11382         break;
11383 #endif
11384       default:
11385 	OrthoDoDraw(G, 0);
11386 	break;
11387       }
11388     } else {
11389       OrthoDoDraw(G, 0);
11390     }
11391 
11392     if(G->HaveGUI && G->ValidContext) {
11393       if(I->CaptureFlag) {
11394         I->CaptureFlag = false;
11395         SceneCaptureWindow(G);
11396       }
11397     }
11398     PyMOL_NeedSwap(G->PyMOL);
11399   }
11400 
11401   //  PRINTFD(G, FB_Executive)
11402   //    " ExecutiveDrawNow: leaving.\n" ENDFD;
11403 }
11404 
11405 
11406 /*========================================================================*/
ExecutiveCountStates(PyMOLGlobals * G,const char * s1)11407 int ExecutiveCountStates(PyMOLGlobals * G, const char *s1)
11408 {
11409   CExecutive *I = G->Executive;
11410   int sele1;
11411   int result = 0;
11412   int n_state;
11413   SpecRec* list_rec = nullptr;
11414   if((!s1) || (!s1[0]))
11415     s1 = cKeywordAll;
11416   for (const auto& rec : ExecutiveGetSpecRecsFromPattern(G, s1)) {
11417     switch (rec.type) {
11418     case cExecAll:
11419       while(ListIterate(I->Spec, list_rec, next)) {
11420         if(list_rec->type == cExecObject) {
11421             n_state = list_rec->obj->getNFrame();
11422             if(result < n_state)
11423               result = n_state;
11424         }
11425       }
11426       break;
11427     case cExecSelection:
11428       sele1 = SelectorIndexByName(G, rec.name);
11429       if(sele1 >= 0) {
11430         SelectorUpdateTable(G, cSelectorUpdateTableAllStates, -1);
11431         n_state = SelectorGetSeleNCSet(G, sele1);
11432         if(result < n_state)
11433           result = n_state;
11434       }
11435       break;
11436     case cExecObject:
11437         n_state = rec.obj->getNFrame();
11438         if(result < n_state)
11439           result = n_state;
11440       break;
11441     }
11442   }
11443   return (result);
11444 }
11445 
11446 
11447 /*========================================================================*/
ExecutiveRay(PyMOLGlobals * G,int width,int height,int mode,float angle,float shift,int quiet,int defer,int antialias)11448 int ExecutiveRay(PyMOLGlobals * G, int width, int height, int mode,
11449                  float angle, float shift, int quiet, int defer, int antialias)
11450 {
11451   if((mode == 0) && G->HaveGUI && SettingGetGlobal_b(G, cSetting_auto_copy_images)) {
11452     /* force deferred behavior if copying image to clipboard */
11453     defer = 1;
11454   }
11455 
11456   ExecutiveUpdateSceneMembers(G);
11457 
11458   if(defer && (mode == 0)) {
11459     SceneDeferRay(G, width, height, mode, angle, shift, quiet, true, antialias);
11460   } else {
11461     SceneRay(G, width, height, mode, NULL, NULL, angle, shift, quiet, NULL, true,
11462                antialias);
11463   }
11464   return 1;
11465 }
11466 
11467 
11468 /*========================================================================*/
ExecutiveGetG3d(PyMOLGlobals * G)11469 int *ExecutiveGetG3d(PyMOLGlobals * G)
11470 {
11471   int *result = NULL;
11472   SceneRay(G, 0, 0, 3, NULL, NULL, 0.0F, 0.0F, true, (G3dPrimitive **) (void *) &result,
11473            false, -1);
11474   return result;
11475 }
11476 
ExecutiveSetBondSettingFromString(PyMOLGlobals * G,int index,const char * value,const char * s1,const char * s2,int state,int quiet,int updates)11477 int ExecutiveSetBondSettingFromString(PyMOLGlobals * G,
11478                                       int index, const char *value,
11479                                       const char *s1, const char *s2, int state,
11480                                       int quiet, int updates)
11481 {
11482   CExecutive *I = G->Executive;
11483   SpecRec *rec = NULL;
11484   ObjectMolecule *obj = NULL;
11485   int sele1, sele2;
11486   SettingName name;
11487   int ok = true;
11488   int side_effects = false;
11489   int value_storage[3], *value_ptr;
11490   float float_storage[3];
11491   int value_type = 0;
11492   PRINTFD(G, FB_Executive)
11493     " %s: entered. '%s' '%s'\n", __func__, s1, s2 ENDFD;
11494   sele1 = SelectorIndexByName(G, s1);
11495   sele2 = SelectorIndexByName(G, s2);
11496   value_ptr = &value_storage[0];
11497   if((sele1 >= 0) && (sele2 >= 0)) {
11498     int have_value = false;
11499     int type = SettingGetType(G, index);
11500     switch (type) {
11501     case cSetting_boolean:
11502       {
11503         if((!*value) || (*value == '0') || (*value == 'F') || WordMatchExact(G, value, "on", true)
11504            || WordMatchExact(G, value, "false", true))
11505           *(value_ptr) = 0;
11506         else
11507           *(value_ptr) = 1;
11508         value_type = cSetting_boolean;
11509         have_value = true;
11510       }
11511       break;
11512     case cSetting_int:
11513       {
11514         if(sscanf(value, "%d", value_ptr) == 1) {
11515           value_type = cSetting_int;
11516           have_value = true;
11517         } else {
11518           ok = false;
11519         }
11520       }
11521       break;
11522     case cSetting_float:
11523       {
11524         if(sscanf(value, "%f", &float_storage[0]) == 1) {
11525           value_ptr = (int*) (void*) &float_storage[0];
11526           value_type = cSetting_float;
11527           have_value = true;
11528         } else {
11529           ok = false;
11530         }
11531       }
11532       break;
11533     case cSetting_float3:
11534       if(sscanf(value, "%f%f%f", &float_storage[0],
11535                 &float_storage[1], &float_storage[2]) == 3) {
11536         value_ptr = (int*) (void*) &float_storage[0];
11537         value_type = cSetting_float3;
11538         have_value = true;
11539       } else {
11540         ok = false;
11541       }
11542       break;
11543     case cSetting_color:
11544       {
11545         int color_index = ColorGetIndex(G, value);
11546         if((color_index < 0) && (color_index > cColorExtCutoff))
11547           color_index = 0;
11548         *(value_ptr) = color_index;
11549         value_type = cSetting_color;
11550         have_value = true;
11551       }
11552       break;
11553       /* cSetting_string? */
11554     default:
11555       ok = false;
11556       break;
11557     }
11558 
11559     if(ok && have_value) {
11560       rec = NULL;
11561       while((ListIterate(I->Spec, rec, next))) {
11562         if((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
11563           obj = (ObjectMolecule *) rec->obj;
11564           {
11565             int a, nBond = obj->NBond;
11566             int nSet = 0;
11567             BondType *bi = obj->Bond.data();
11568             const AtomInfoType *ai1, *ai2, *ai = obj->AtomInfo.data();
11569             for(a = 0; a < nBond; a++) {
11570               ai1 = ai + bi->index[0];
11571               ai2 = ai + bi->index[1];
11572               if((SelectorIsMember(G, ai1->selEntry, sele1) &&
11573                   SelectorIsMember(G, ai2->selEntry, sele2)) ||
11574                  (SelectorIsMember(G, ai2->selEntry, sele1) &&
11575                   SelectorIsMember(G, ai1->selEntry, sele2))) {
11576 
11577                 int uid = AtomInfoCheckUniqueBondID(G, bi);
11578 		int isset;
11579                 bi->has_setting = true;
11580                 isset = SettingUniqueSetTypedValue(G, uid, index, value_type, value_ptr);
11581                 if(updates && isset)
11582                   side_effects = true;
11583                 nSet++;
11584               }
11585               bi++;
11586             }
11587             if(nSet && !quiet) {
11588               SettingGetName(G, index, name);
11589               PRINTF
11590                 " Setting: %s set for %d bonds in object \"%s\".\n",
11591                 name, nSet, obj->Name ENDF(G);
11592             }
11593           }
11594         }
11595       }
11596     }
11597   }
11598   if(side_effects) {
11599     SettingGenerateSideEffects(G, index, s1, state, quiet); /* not strickly correct */
11600     /*    SettingGenerateSideEffects(G,index,s2,state); */
11601   }
11602   return (ok);
11603 }
11604 /*========================================================================*/
ExecutiveGetBondSetting(PyMOLGlobals * G,int index,char * s1,const char * s2,int state,int quiet,int updates)11605 PyObject *ExecutiveGetBondSetting(PyMOLGlobals * G, int index,
11606 				  char *s1, const char *s2, int state, int quiet, int updates)
11607 {
11608 #ifdef _PYMOL_NOPY
11609   return 0;
11610 #else
11611   CExecutive *I = G->Executive;
11612   SpecRec *rec = NULL;
11613   ObjectMolecule *obj = NULL;
11614   int sele1, sele2;
11615   SettingName name;
11616   int unblock;
11617   //  int *value_ptr;
11618   //  int value_type = 0;
11619   PyObject *result = PyList_New(0);
11620 
11621   PRINTFD(G, FB_Executive)
11622     " %s: entered. '%s' '%s'\n", __func__, s1, s2 ENDFD;
11623   unblock = PAutoBlock(G);
11624   sele1 = SelectorIndexByName(G, s1);
11625   sele2 = SelectorIndexByName(G, s2);
11626 
11627   if((sele1 >= 0) && (sele2 >= 0)) {
11628     while((ListIterate(I->Spec, rec, next))) {
11629       if((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
11630 	obj = (ObjectMolecule *) rec->obj;
11631 	{
11632 	  int a, nBond = obj->NBond ;
11633 	  int nSet = 0;
11634 	  const BondType *bi = obj->Bond.data();
11635 	  const AtomInfoType *ai1, *ai2, *ai = obj->AtomInfo.data();
11636 
11637 	  PyObject *pyObjList = NULL;
11638 	  PyObject *pyBondList = NULL;
11639 
11640 	  for(a = 0; a < nBond; a++) {
11641 	    ai1 = ai + bi->index[0];
11642 	    ai2 = ai + bi->index[1];
11643 	    if((SelectorIsMember(G, ai1->selEntry, sele1) &&
11644 		SelectorIsMember(G, ai2->selEntry, sele2)) ||
11645 	       (SelectorIsMember(G, ai2->selEntry, sele1) &&
11646 		SelectorIsMember(G, ai1->selEntry, sele2))) {
11647 	      PyObject *pyBondInfo = PyList_New(3);
11648 	      PyObject *bond_setting_value = NULL;
11649 	      if (!pyObjList){
11650 		pyObjList = PyList_New(2);
11651 		pyBondList = PyList_New(0);
11652 		PyList_SetItem(pyObjList, 0, PyString_FromString(obj->Name));
11653 		PyList_SetItem(pyObjList, 1, pyBondList);
11654 		PyList_Append(result, pyObjList);
11655 		Py_DECREF(pyObjList);
11656 	      }
11657 	      PyList_SetItem(pyBondInfo, 0, PyInt_FromLong((long)bi->index[0]+1));
11658 	      PyList_SetItem(pyBondInfo, 1, PyInt_FromLong((long)bi->index[1]+1));
11659 	      if (bi->has_setting){
11660 		bond_setting_value = SettingUniqueGetPyObject(G, bi->unique_id, index);
11661 	      }
11662 	      PyList_SetItem(pyBondInfo, 2, PConvAutoNone(bond_setting_value));
11663 	      PyList_Append(pyBondList, pyBondInfo);
11664 	      Py_DECREF(pyBondInfo);
11665 	      nSet++;
11666 	    }
11667 	    bi++;
11668 	  }
11669 	  if(nSet && !quiet) {
11670 	    SettingGetName(G, index, name);
11671 	    PRINTF
11672 	      " Getting: %s for %d bonds in object \"%s\".\n",
11673 	      name, nSet, obj->Name ENDF(G);
11674 	  }
11675 	}
11676       }
11677     }
11678   }
11679   PRINTFD(G, FB_Executive)
11680     " %s: end. '%s' '%s'\n", __func__, s1, s2 ENDFD;
11681   PAutoUnblock(G, unblock);
11682   return result;
11683 #endif
11684 }
11685 /*========================================================================*/
ExecutiveSetBondSetting(PyMOLGlobals * G,int index,PyObject * tuple,const char * s1,const char * s2,int state,int quiet,int updates)11686 int ExecutiveSetBondSetting(PyMOLGlobals * G, int index, PyObject * tuple,
11687                             const char *s1, const char *s2, int state, int quiet, int updates)
11688 {
11689 #ifdef _PYMOL_NOPY
11690   return 0;
11691 #else
11692 
11693   CExecutive *I = G->Executive;
11694   SpecRec *rec = NULL;
11695   ObjectMolecule *obj = NULL;
11696   int sele1, sele2;
11697   SettingName name = "";
11698   int unblock;
11699   int ok = true;
11700   int side_effects = false;
11701   union {
11702     int value_ptr[1];
11703     float float_storage[1];
11704   };
11705   int value_type = 0;
11706 
11707   PRINTFD(G, FB_Executive)
11708     " %s: entered. '%s' '%s'\n", __func__, s1, s2 ENDFD;
11709   unblock = PAutoBlock(G);
11710   sele1 = SelectorIndexByName(G, s1);
11711   sele2 = SelectorIndexByName(G, s2);
11712   if((sele1 >= 0) && (sele2 >= 0)) {
11713     int have_value = false;
11714     int type = PyInt_AsLong(PyTuple_GetItem(tuple, 0));
11715     PyObject *value = PyTuple_GetItem(tuple, 1);
11716     if(value) {
11717       switch (type) {
11718       case cSetting_boolean:
11719         *(value_ptr) = PyInt_AsLong(value);
11720         value_type = cSetting_boolean;
11721         have_value = true;
11722         break;
11723       case cSetting_int:
11724         *(value_ptr) = PyInt_AsLong(value);
11725         value_type = cSetting_int;
11726         have_value = true;
11727         break;
11728       case cSetting_float:
11729         float_storage[0] = PyFloat_AsDouble(value);
11730         value_type = cSetting_float;
11731         have_value = true;
11732         break;
11733       case cSetting_color:
11734         {
11735           int color_index =
11736             ColorGetIndex(G, PyString_AsString(value));
11737           if((color_index < 0) && (color_index > cColorExtCutoff))
11738             color_index = 0;
11739           *(value_ptr) = color_index;
11740           value_type = cSetting_color;
11741           have_value = true;
11742         }
11743         break;
11744       }
11745       if(have_value) {
11746         rec = NULL;
11747         while((ListIterate(I->Spec, rec, next))) {
11748           if((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
11749             obj = (ObjectMolecule *) rec->obj;
11750             {
11751               int a, nBond = obj->NBond;
11752               int nSet = 0;
11753               BondType *bi = obj->Bond.data();
11754               const AtomInfoType *ai1, *ai2, *ai = obj->AtomInfo.data();
11755               for(a = 0; a < nBond; a++) {
11756                 ai1 = ai + bi->index[0];
11757                 ai2 = ai + bi->index[1];
11758                 if((SelectorIsMember(G, ai1->selEntry, sele1) &&
11759                     SelectorIsMember(G, ai2->selEntry, sele2)) ||
11760                    (SelectorIsMember(G, ai2->selEntry, sele1) &&
11761                     SelectorIsMember(G, ai1->selEntry, sele2))) {
11762 
11763                   int uid = AtomInfoCheckUniqueBondID(G, bi);
11764                   bi->has_setting = true;
11765                   SettingUniqueSetTypedValue(G, uid, index, value_type, value_ptr);
11766                   if(updates)
11767                     side_effects = true;
11768                   nSet++;
11769                 }
11770                 bi++;
11771               }
11772               if(nSet && !quiet) {
11773                 SettingGetName(G, index, name);
11774                 PRINTF
11775                   " Setting: %s set for %d bonds in object \"%s\".\n",
11776                   name, nSet, obj->Name ENDF(G);
11777               }
11778             }
11779           }
11780         }
11781       }
11782     }
11783   }
11784   if(side_effects) {
11785     SettingGenerateSideEffects(G, index, s1, state, quiet);  /* not strictly correct */
11786     /*    SettingGenerateSideEffects(G,index,s2,state); */
11787   }
11788 
11789   if(!SettingLevelCheck(G, index, cSettingLevel_bond)) {
11790     if (!name[0])
11791       SettingGetName(G, index, name);
11792 
11793     PRINTFB(G, FB_Setting, FB_Warnings)
11794       " Setting-Warning: '%s' is not a bond-level setting\n", name
11795       ENDFB(G);
11796   }
11797 
11798   PAutoUnblock(G, unblock);
11799   return (ok);
11800 #endif
11801 }
11802 
11803 
11804 /*========================================================================*/
ExecutiveUnsetBondSetting(PyMOLGlobals * G,int index,const char * s1,const char * s2,int state,int quiet,int updates)11805 int ExecutiveUnsetBondSetting(PyMOLGlobals * G, int index, const char *s1, const char *s2,
11806                               int state, int quiet, int updates)
11807 {
11808   CExecutive *I = G->Executive;
11809   SpecRec *rec = NULL;
11810   ObjectMolecule *obj = NULL;
11811   SettingName name;
11812   /*  int unblock; */
11813   int ok = true;
11814   int side_effects = false;
11815   int sele1, sele2;
11816   PRINTFD(G, FB_Executive)
11817     " %s: entered. sele '%s' '%s'\n", __func__, s1, s2 ENDFD;
11818   /* unblock = PAutoBlock(G); */
11819   sele1 = SelectorIndexByName(G, s1);
11820   sele2 = SelectorIndexByName(G, s2);
11821   if((sele1 >= 0) && (sele2 >= 0)) {
11822     rec = NULL;
11823     while((ListIterate(I->Spec, rec, next))) {
11824       if((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
11825         obj = (ObjectMolecule *) rec->obj;
11826         {
11827           int nSet = 0;
11828           BondType *bi = obj->Bond.data();
11829           BondType *bi_end = bi + obj->NBond;
11830           AtomInfoType *ai1, *ai2, *ai = obj->AtomInfo.data();
11831           for(; bi != bi_end; ++bi) {
11832             if(!bi->has_setting)
11833               continue;
11834             ai1 = ai + bi->index[0];
11835             ai2 = ai + bi->index[1];
11836             if((SelectorIsMember(G, ai1->selEntry, sele1) &&
11837                 SelectorIsMember(G, ai2->selEntry, sele2)) ||
11838                (SelectorIsMember(G, ai2->selEntry, sele1) &&
11839                 SelectorIsMember(G, ai1->selEntry, sele2))) {
11840               int uid = AtomInfoCheckUniqueBondID(G, bi);
11841               if(!SettingUniqueUnset(G, uid, index))
11842                 continue;
11843               if(updates)
11844                 side_effects = true;
11845               nSet++;
11846             }
11847           }
11848           if(nSet && !quiet) {
11849             SettingGetName(G, index, name);
11850             PRINTF
11851               " Setting: %s unset for %d bonds in object \"%s\".\n",
11852               name, nSet, rec->obj->Name ENDF(G);
11853           }
11854         }
11855       }
11856     }
11857   }
11858   if(side_effects) {
11859     SettingGenerateSideEffects(G, index, s1, state, quiet);
11860     /*    SettingGenerateSideEffects(G,index,s2,state); */
11861   }
11862   /* PAutoUnblock(G, unblock); */
11863   return (ok);
11864 }
11865 
11866 
11867 /*========================================================================*/
ExecutiveSetSetting(PyMOLGlobals * G,int index,PyObject * tuple,const char * sele,int state,int quiet,int updates)11868 int ExecutiveSetSetting(PyMOLGlobals * G, int index, PyObject * tuple, const char *sele,
11869                         int state, int quiet, int updates)
11870 {
11871 #ifdef _PYMOL_NOPY
11872   return 0;
11873 #else
11874 
11875   CExecutive *I = G->Executive;
11876   SpecRec *rec = NULL;
11877   ObjectMolecule *obj = NULL;
11878   int sele1;
11879   ObjectMoleculeOpRec op;
11880   OrthoLineType value;
11881   CSetting **handle = NULL;
11882   SettingName name = "";
11883   int nObj = 0;
11884   int unblock;
11885   int ok = true;
11886 
11887   PRINTFD(G, FB_Executive)
11888     " %s: entered. sele \"%s\" updates=%d index=%d\n", __func__, sele, updates, index ENDFD;
11889 
11890   if(!quiet) {
11891     SettingGetName(G, index, name);
11892   }
11893 
11894   unblock = PAutoBlock(G);
11895   if((!sele) || (sele[0] == 0)) {       /* global setting */
11896     ok = SettingSetFromTuple(G, NULL, index, tuple);
11897     if(ok) {
11898       if(!quiet) {
11899         if(Feedback(G, FB_Setting, FB_Actions)) {
11900           SettingGetTextValue(G, NULL, NULL, index, value);
11901           PRINTF " Setting: %s set to %s.\n", name, value ENDF(G);
11902         }
11903       }
11904       if(updates) {
11905         SettingGenerateSideEffects(G, index, NULL, state, quiet);
11906       }
11907     }
11908   }
11909   else {
11910     unsigned char levelmask = 0;
11911     int side_effects = false;
11912 
11913     CTracker *I_Tracker = I->Tracker;
11914     int list_id = ExecutiveGetNamesListFromPattern(G, sele, true, true);
11915     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
11916     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
11917       if(rec) {
11918         switch (rec->type) {
11919         case cExecAll:
11920           levelmask |= SettingLevelInfo[state < 0 ? cSettingLevel_object : cSettingLevel_ostate].mask;
11921           rec = NULL;
11922           while(ListIterate(I->Spec, rec, next)) {
11923             if(rec->type == cExecObject) {
11924               {
11925                 handle = rec->obj->getSettingHandle(state);
11926                 if(handle) {
11927                   SettingCheckHandle(G, handle);
11928                   ok = SettingSetFromTuple(G, *handle, index, tuple);
11929                   if(updates)
11930                     side_effects = true;
11931                   nObj++;
11932                 }
11933               }
11934             }
11935           }
11936           if(Feedback(G, FB_Setting, FB_Actions)) {
11937             if(nObj && handle) {
11938               SettingGetTextValue(G, *handle, NULL, index, value);
11939               if(!quiet) {
11940                 if(state < 0) {
11941                   PRINTF
11942                     " Setting: %s set to %s in %d objects.\n", name, value, nObj ENDF(G);
11943                 } else {
11944                   PRINTF
11945                     " Setting: %s set to %s in %d objects, state %d.\n",
11946                     name, value, nObj, state + 1 ENDF(G);
11947                 }
11948               }
11949             }
11950           }
11951           break;
11952         case cExecSelection:
11953           if (SettingLevelCheckMask(G, index, SettingLevelInfo[cSettingLevel_bond].mask)) {
11954             // handle bond-level settings (PYMOL-2726)
11955             ok = ExecutiveSetBondSetting(G, index, tuple, sele, sele, state, quiet, false);
11956             if (updates)
11957               side_effects = true;
11958             sele1 = -1;
11959           } else {
11960             levelmask |= SettingLevelInfo[cSettingLevel_atom].mask;
11961             sele1 = SelectorIndexByName(G, rec->name);
11962           }
11963 
11964           if(sele1 >= 0) {
11965             int have_atomic_value = false;
11966             int type = PyInt_AsLong(PyTuple_GetItem(tuple, 0));
11967             PyObject *value = PyTuple_GetItem(tuple, 1);
11968             if(value) {
11969               ObjectMoleculeOpRecInit(&op);
11970               op.code = OMOP_SetAtomicSetting;
11971               op.i1 = index;
11972               op.ii1 = &op.i3;
11973               switch (type) {
11974               case cSetting_boolean:
11975                 *(op.ii1) = PyInt_AsLong(value);
11976                 op.i2 = cSetting_boolean;
11977                 have_atomic_value = true;
11978                 break;
11979               case cSetting_int:
11980                 *(op.ii1) = PyInt_AsLong(value);
11981                 op.i2 = cSetting_int;
11982                 have_atomic_value = true;
11983                 break;
11984               case cSetting_float:
11985                 *(float *) op.ii1 = (float) PyFloat_AsDouble(value);
11986                 op.i2 = cSetting_float;
11987                 have_atomic_value = true;
11988                 break;
11989               case cSetting_float3:
11990 		{
11991 		  PConvPyListOrTupleToFloatArrayInPlace(value, op.ttt, 3);
11992 		  op.mat1 = op.ttt; // for passing (float**)
11993 		  op.ii1 = (int*) &op.mat1;
11994 		  op.i2 = cSetting_float3;
11995 		  have_atomic_value = true;
11996 		}
11997                 break;
11998               case cSetting_color:
11999                 {
12000                   int color_index =
12001                     ColorGetIndex(G, PyString_AsString(value));
12002                   if((color_index < 0) && (color_index > cColorExtCutoff)) {
12003                     switch (color_index) {
12004                     case cColorAtomic:
12005                       color_index = -1;
12006                       break;
12007                     case cColorFront:
12008                     case cColorBack:
12009                     case cColorDefault:
12010                       break;
12011                     default:
12012                       color_index = 0;
12013                       break;
12014                     }
12015                   }
12016                   *(op.ii1) = color_index;
12017                   op.i2 = cSetting_color;
12018                   have_atomic_value = true;
12019                 }
12020                 break;
12021               }
12022               if(have_atomic_value) {
12023                 rec = NULL;
12024                 while((ListIterate(I->Spec, rec, next))) {
12025                   if((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
12026                     obj = (ObjectMolecule *) rec->obj;
12027                     op.i4 = 0;
12028                     ObjectMoleculeSeleOp(obj, sele1, &op);
12029                     if(op.i4) {
12030                       if(updates)
12031                         side_effects = true;
12032                       if(!quiet) {
12033                         PRINTF
12034                           " Setting: %s set for %d atoms in object \"%s\".\n",
12035                           name, op.i4, rec->obj->Name ENDF(G);
12036                       }
12037                     }
12038                   }
12039                 }
12040               }
12041             }
12042           }
12043           break;
12044         case cExecObject:
12045           levelmask |= SettingLevelInfo[state < 0 ? cSettingLevel_object : cSettingLevel_ostate].mask;
12046           {
12047             handle = rec->obj->getSettingHandle(state);
12048             if(handle) {
12049               SettingCheckHandle(G, handle);
12050               ok = SettingSetFromTuple(G, *handle, index, tuple);
12051               if(ok) {
12052                 if(updates)
12053                   side_effects = true;
12054                 if(!quiet) {
12055                   if(state < 0) {       /* object-specific */
12056                     if(Feedback(G, FB_Setting, FB_Actions)) {
12057                       SettingGetTextValue(G, *handle, NULL, index, value);
12058                       PRINTF
12059                         " Setting: %s set to %s in object \"%s\".\n",
12060                         name, value, rec->obj->Name ENDF(G);
12061                     }
12062                   } else {      /* state-specific */
12063                     if(Feedback(G, FB_Setting, FB_Actions)) {
12064                       SettingGetTextValue(G, *handle, NULL, index, value);
12065                       PRINTF
12066                         " Setting: %s set to %s in object \"%s\", state %d.\n",
12067                         name, value, rec->obj->Name, state + 1 ENDF(G);
12068                     }
12069                   }
12070                 }
12071               }
12072             }
12073           }
12074           break;
12075         }
12076       }
12077     }
12078     TrackerDelList(I_Tracker, list_id);
12079     TrackerDelIter(I_Tracker, iter_id);
12080 
12081     if(side_effects)
12082       SettingGenerateSideEffects(G, index, sele, state, quiet);
12083 
12084     if(!SettingLevelCheckMask(G, index, levelmask)) {
12085       if(!name[0])
12086         SettingGetName(G, index, name);
12087 
12088       PRINTFB(G, FB_Setting, FB_Warnings)
12089         " Setting-Warning: '%s' is a %s-level setting\n", name, SettingLevelGetName(G, index)
12090         ENDFB(G);
12091     }
12092   }
12093 
12094   PAutoUnblock(G, unblock);
12095   return (ok);
12096 #endif
12097 }
12098 
ExecutiveGetSettingFromString(PyMOLGlobals * G,PyMOLreturn_value * result,int index,const char * sele,int state,int quiet)12099 int ExecutiveGetSettingFromString(PyMOLGlobals * G, PyMOLreturn_value *result,
12100                                   int index, const char *sele,
12101                                   int state, int quiet)
12102 {
12103   CObject *obj = NULL;
12104   CSetting **handle = NULL, *set_ptr1 = NULL, *set_ptr2 = NULL;
12105   int ok = true;
12106   int type;
12107   type = SettingGetType(G, index);
12108   if(sele)
12109     if(sele[0]) {
12110       obj = ExecutiveFindObjectByName(G, sele);
12111       if(!obj)
12112         ok = false;
12113     }
12114   if(!ok) {
12115     PRINTFB(G, FB_Executive, FB_Errors)
12116       " %s-Error: sele \"%s\" not found.\n", __func__, sele ENDFB(G);
12117     ok = false;
12118   } else if(obj) {
12119     handle = obj->getSettingHandle(-1);
12120     if(handle)
12121       set_ptr1 = *handle;
12122     if(state >= 0) {
12123       handle = obj->getSettingHandle(state);
12124       if(handle)
12125         set_ptr2 = *handle;
12126       else {
12127         PRINTFB(G, FB_Executive, FB_Errors)
12128           " %s-Error: sele \"%s\" lacks state %d.\n", __func__, sele, state + 1
12129           ENDFB(G);
12130         ok = false;
12131       }
12132     }
12133   }
12134   if(ok) {
12135     switch (type) {
12136     case cSetting_boolean:
12137       {
12138         int value = SettingGet_b(G, set_ptr2, set_ptr1, index);
12139 	result->type = PYMOL_RETURN_VALUE_IS_INT;
12140 	result->int_value = value;
12141       }
12142       break;
12143     case cSetting_int:
12144       {
12145         int value = SettingGet_i(G, set_ptr2, set_ptr1, index);
12146 	result->type = PYMOL_RETURN_VALUE_IS_INT;
12147 	result->int_value = value;
12148       }
12149       break;
12150     case cSetting_float:
12151       {
12152         float value = SettingGet_f(G, set_ptr2, set_ptr1, index);
12153 	result->type = PYMOL_RETURN_VALUE_IS_FLOAT;
12154 	result->float_value = value;
12155       }
12156       break;
12157     case cSetting_float3:
12158       {
12159 	result->type = PYMOL_RETURN_VALUE_IS_FLOAT_ARRAY;
12160 	result->float_array = VLAlloc(float, 3);
12161 	result->array_length = 3;
12162 	copy3f(SettingGet<const float *>(G, set_ptr2, set_ptr1, index),
12163 	    result->float_array);
12164       }
12165       break;
12166     case cSetting_color:
12167       {
12168         int value = SettingGet_color(G, set_ptr2, set_ptr1, index);
12169 	result->type = PYMOL_RETURN_VALUE_IS_INT;
12170 	result->int_value = value;
12171       }
12172       break;
12173     case cSetting_string:
12174       {
12175         OrthoLineType buffer = "";
12176 	result->type = PYMOL_RETURN_VALUE_IS_STRING;
12177 	result->string = mstrdup(SettingGetTextPtr(G, set_ptr2, set_ptr1, index, buffer));
12178       }
12179       break;
12180     default:
12181       break;
12182     }
12183   }
12184   return (ok);
12185 }
12186 
ExecutiveSetSettingFromString(PyMOLGlobals * G,int index,const char * value,const char * sele,int state,int quiet,int updates)12187 int ExecutiveSetSettingFromString(PyMOLGlobals * G,
12188                                   int index, const char *value, const char *sele,
12189                                   int state, int quiet, int updates)
12190 {
12191   CExecutive *I = G->Executive;
12192   SpecRec *rec = NULL;
12193   ObjectMolecule *obj = NULL;
12194   int sele1;
12195   ObjectMoleculeOpRec op;
12196   OrthoLineType value2;
12197   CSetting **handle = NULL;
12198   SettingName name;
12199   int nObj = 0;
12200   int ok = true;
12201 
12202   PRINTFD(G, FB_Executive)
12203     " %s: entered. sele \"%s\"\n", __func__, sele ENDFD;
12204   if(sele[0] == 0) {            /* global setting */
12205     ok = SettingSetFromString(G, NULL, index, value);
12206     if(ok) {
12207       if(!quiet) {
12208         if(Feedback(G, FB_Setting, FB_Actions)) {
12209           SettingGetTextValue(G, NULL, NULL, index, value2);
12210           SettingGetName(G, index, name);
12211           PRINTF " Setting: %s set to %s.\n", name, value2 ENDF(G);
12212         }
12213       }
12214       if(updates)
12215         SettingGenerateSideEffects(G, index, sele, state, quiet);
12216     }
12217   }
12218   else {
12219     CTracker *I_Tracker = I->Tracker;
12220     int list_id = ExecutiveGetNamesListFromPattern(G, sele, true, true);
12221     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
12222     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
12223       if(rec) {
12224         switch (rec->type) {
12225         case cExecAll:
12226           rec = NULL;
12227           while(ListIterate(I->Spec, rec, next)) {
12228             if(rec->type == cExecObject) {
12229               {
12230                 handle = rec->obj->getSettingHandle(state);
12231                 if(handle) {
12232                   SettingCheckHandle(G, handle);
12233                   ok = SettingSetFromString(G, *handle, index, value);
12234                   if(updates)
12235                     SettingGenerateSideEffects(G, index, rec->name, state, quiet);
12236                   nObj++;
12237                 }
12238               }
12239             }
12240           }
12241           if(Feedback(G, FB_Setting, FB_Actions)) {
12242             if(nObj && handle) {
12243               SettingGetTextValue(G, *handle, NULL, index, value2);
12244               SettingGetName(G, index, name);
12245               if(!quiet) {
12246                 if(state < 0) {
12247                   PRINTF
12248                     " Setting: %s set to %s in %d objects.\n", name, value2, nObj ENDF(G);
12249                 } else {
12250                   PRINTF
12251                     " Setting: %s set to %s in %d objects, state %d.\n",
12252                     name, value2, nObj, state + 1 ENDF(G);
12253                 }
12254               }
12255             }
12256           }
12257           break;
12258         case cExecSelection:
12259           /* this code has not yet been tested... */
12260 
12261           sele1 = SelectorIndexByName(G, rec->name);
12262           if(sele1 >= 0) {
12263             int type;
12264             int value_store;
12265             if(SettingStringToTypedValue(G, index, value, &type, &value_store)) {
12266               ObjectMoleculeOpRecInit(&op);
12267               op.code = OMOP_SetAtomicSetting;
12268               op.i1 = index;
12269               op.i2 = type;
12270               op.ii1 = &value_store;
12271               rec = NULL;
12272               while((ListIterate(I->Spec, rec, next))) {
12273                 if((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
12274                   obj = (ObjectMolecule *) rec->obj;
12275                   op.i4 = 0;
12276                   ObjectMoleculeSeleOp(obj, sele1, &op);
12277                   if(op.i4) {
12278                     if(updates)
12279                       SettingGenerateSideEffects(G, index, rec->name, state, quiet);
12280                     if(!quiet) {
12281                       SettingGetName(G, index, name);
12282                       PRINTF
12283                         " Setting: %s set for %d atoms in object \"%s\".\n",
12284                         name, op.i4, rec->obj->Name ENDF(G);
12285                     }
12286                   }
12287                 }
12288               }
12289             }
12290           }
12291           break;
12292         case cExecObject:
12293           {
12294             handle = rec->obj->getSettingHandle(state);
12295             if(handle) {
12296               SettingCheckHandle(G, handle);
12297               ok = SettingSetFromString(G, *handle, index, value);
12298               if(ok) {
12299                 if(updates)
12300                   SettingGenerateSideEffects(G, index, sele, state, quiet);
12301                 if(!quiet) {
12302                   if(state < 0) {       /* object-specific */
12303                     if(Feedback(G, FB_Setting, FB_Actions)) {
12304                       SettingGetTextValue(G, *handle, NULL, index, value2);
12305                       SettingGetName(G, index, name);
12306                       PRINTF
12307                         " Setting: %s set to %s in object \"%s\".\n",
12308                         name, value2, rec->obj->Name ENDF(G);
12309                     }
12310                   } else {      /* state-specific */
12311                     if(Feedback(G, FB_Setting, FB_Actions)) {
12312                       SettingGetTextValue(G, *handle, NULL, index, value2);
12313                       SettingGetName(G, index, name);
12314                       PRINTF
12315                         " Setting: %s set to %s in object \"%s\", state %d.\n",
12316                         name, value2, rec->obj->Name, state + 1 ENDF(G);
12317                     }
12318                   }
12319                 }
12320               }
12321             }
12322           }
12323           break;
12324         }
12325       }
12326     }
12327     TrackerDelList(I_Tracker, list_id);
12328     TrackerDelIter(I_Tracker, iter_id);
12329   }
12330   return (ok);
12331 }
12332 
ExecutiveSetObjSettingFromString(PyMOLGlobals * G,int index,const char * value,CObject * obj,int state,int quiet,int updates)12333 int ExecutiveSetObjSettingFromString(PyMOLGlobals * G,
12334                                      int index, const char *value, CObject * obj,
12335                                      int state, int quiet, int updates)
12336 {
12337   OrthoLineType value2;
12338   CSetting **handle = NULL;
12339   SettingName name;
12340   int ok = true;
12341 
12342   PRINTFD(G, FB_Executive)
12343     " ExecutiveSetObjSettingFromString: entered \n" ENDFD;
12344   if(!obj) {                    /* global */
12345     ok = SettingSetFromString(G, NULL, index, value);
12346     if(ok) {
12347       if(!quiet) {
12348         if(Feedback(G, FB_Setting, FB_Actions)) {
12349           SettingGetTextValue(G, NULL, NULL, index, value2);
12350           SettingGetName(G, index, name);
12351           PRINTF " Setting: %s set to %s.\n", name, value2 ENDF(G);
12352         }
12353       }
12354       if(updates)
12355         SettingGenerateSideEffects(G, index, obj->Name, state, quiet);
12356     }
12357   } else {                      /* based on a single object */
12358     {
12359       handle = obj->getSettingHandle(state);
12360       if(handle) {
12361         SettingCheckHandle(G, handle);
12362         ok = SettingSetFromString(G, *handle, index, value);
12363         if(ok) {
12364           if(updates)
12365             SettingGenerateSideEffects(G, index, obj->Name, state, quiet);
12366           if(!quiet) {
12367             if(state < 0) {     /* object-specific */
12368               if(Feedback(G, FB_Setting, FB_Actions)) {
12369                 SettingGetTextValue(G, *handle, NULL, index, value2);
12370                 SettingGetName(G, index, name);
12371                 PRINTF
12372                   " Setting: %s set to %s in object \"%s\".\n",
12373                   name, value2, obj->Name ENDF(G);
12374               }
12375             } else {            /* state-specific */
12376               if(Feedback(G, FB_Setting, FB_Actions)) {
12377                 SettingGetTextValue(G, *handle, NULL, index, value2);
12378                 SettingGetName(G, index, name);
12379                 PRINTF
12380                   " Setting: %s set to %s in object \"%s\", state %d.\n",
12381                   name, value2, obj->Name, state + 1 ENDF(G);
12382               }
12383             }
12384           }
12385         }
12386       }
12387     }
12388   }
12389   return (ok);
12390 }
12391 
12392 
12393 /*========================================================================*/
ExecutiveUnsetSetting(PyMOLGlobals * G,int index,const char * sele,int state,int quiet,int updates)12394 int ExecutiveUnsetSetting(PyMOLGlobals * G, int index, const char *sele,
12395                           int state, int quiet, int updates)
12396 {
12397   CExecutive *I = G->Executive;
12398   SpecRec *rec = NULL;
12399   ObjectMolecule *obj = NULL;
12400   int sele1;
12401   ObjectMoleculeOpRec op;
12402   CSetting **handle = NULL;
12403   const char * name = SettingGetName(index);
12404   int nObj = 0;
12405   int unblock;
12406   int ok = true;
12407 
12408   PRINTFD(G, FB_Executive)
12409     " %s: entered. sele \"%s\"\n", __func__, sele ENDFD;
12410   unblock = PAutoBlock(G);
12411   if(sele[0] == 0) {
12412     // Set global setting to an "off" value.
12413     // NOTE: It would be nice if this would restore the default setting, but
12414     //       that's not how Warren implemented it and we don't want to break
12415     //       legacy PyMOL behavior.
12416     if (!SettingIsDefaultZero(index)) {
12417       PRINTFB(G, FB_Executive, FB_Warnings)
12418         " Warning: The behavior of \"unset\" for global numeric settings will change.\n"
12419         " Use \"set %s, 0\" to ensure consistent behavior in future PyMOL versions.",
12420         name ENDFB(G);
12421       SettingSetGlobal_i(G, index, 0);
12422     } else {
12423       SettingRestoreDefault(G->Setting, index, G->Default);
12424       if (!quiet)
12425       PRINTFB(G, FB_Executive, FB_Actions)
12426         " Setting: %s restored to default\n", name ENDFB(G);
12427     }
12428   }
12429   else {
12430     // Undefine per-object, per-state, or per-atom settings.
12431     CTracker *I_Tracker = I->Tracker;
12432     int list_id = ExecutiveGetNamesListFromPattern(G, sele, true, true);
12433     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
12434     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
12435       if(rec) {
12436         switch (rec->type) {
12437         case cExecAll:
12438           rec = NULL;
12439           while(ListIterate(I->Spec, rec, next)) {
12440             if(rec->type == cExecObject) {
12441               {
12442                 handle = rec->obj->getSettingHandle(state);
12443                 if(handle) {
12444                   SettingCheckHandle(G, handle);
12445                   ok = SettingUnset(*handle, index);
12446                   nObj++;
12447                 }
12448               }
12449             }
12450           }
12451           if(Feedback(G, FB_Setting, FB_Actions)) {
12452             if(nObj && handle) {
12453               if(!quiet) {
12454                 if(state < 0) {
12455                   PRINTF " Setting: %s unset in %d objects.\n", name, nObj ENDF(G);
12456                 } else {
12457                   PRINTF
12458                     " Setting: %s unset in %d objects, state %d.\n",
12459                     name, nObj, state + 1 ENDF(G);
12460                 }
12461               }
12462             }
12463           }
12464           break;
12465         case cExecSelection:
12466           if (SettingLevelCheckMask(G, index, SettingLevelInfo[cSettingLevel_bond].mask)) {
12467             // handle bond-level settings (PYMOL-2726)
12468             ok = ExecutiveUnsetBondSetting(G, index, sele, sele, state, quiet, false);
12469             sele1 = -1;
12470           } else {
12471             sele1 = SelectorIndexByName(G, rec->name);
12472           }
12473 
12474           if(sele1 >= 0) {
12475             ObjectMoleculeOpRecInit(&op);
12476             op.code = OMOP_SetAtomicSetting;
12477             op.i1 = index;
12478             op.i2 = cSetting_blank;
12479             op.ii1 = NULL;
12480 
12481             rec = NULL;
12482             while((ListIterate(I->Spec, rec, next))) {
12483               if((rec->type == cExecObject) && (rec->obj->type == cObjectMolecule)) {
12484                 obj = (ObjectMolecule *) rec->obj;
12485                 op.i4 = 0;
12486                 ObjectMoleculeSeleOp(obj, sele1, &op);
12487                 if(op.i4) {
12488                   if(!quiet) {
12489                     PRINTF
12490                       " Setting: %s unset for %d atoms in object \"%s\".\n",
12491                       name, op.i4, rec->obj->Name ENDF(G);
12492                   }
12493                 }
12494               }
12495             }
12496           }
12497           break;
12498         case cExecObject:
12499           {
12500             handle = rec->obj->getSettingHandle(state);
12501             if(handle) {
12502               SettingCheckHandle(G, handle);
12503               ok = SettingUnset(*handle, index);
12504               if(ok) {
12505                 if(!quiet) {
12506                   if(state < 0) {       /* object-specific */
12507                     if(Feedback(G, FB_Setting, FB_Actions)) {
12508                       PRINTF
12509                         " Setting: %s unset in object \"%s\".\n",
12510                         name, rec->obj->Name ENDF(G);
12511                     }
12512                   } else {      /* state-specific */
12513                     if(Feedback(G, FB_Setting, FB_Actions)) {
12514                       PRINTF
12515                         " Setting: %s unset in object \"%s\", state %d.\n",
12516                         name, rec->obj->Name, state + 1 ENDF(G);
12517                     }
12518                   }
12519                 }
12520               }
12521             }
12522           }
12523           break;
12524         }
12525       }
12526     }
12527     TrackerDelList(I_Tracker, list_id);
12528     TrackerDelIter(I_Tracker, iter_id);
12529   }
12530   if(updates)
12531     SettingGenerateSideEffects(G, index, sele, state, quiet);
12532   PAutoUnblock(G, unblock);
12533   return (ok);
12534 }
12535 
12536 
12537 /*========================================================================*/
ExecutiveColorFromSele(PyMOLGlobals * G,const char * sele,const char * color,int flags,int quiet)12538 pymol::Result<> ExecutiveColorFromSele(
12539     PyMOLGlobals* G, const char* sele, const char* color, int flags, int quiet)
12540 {
12541   auto s1 = SelectorTmp2::make(G, sele);
12542   p_return_if_error(s1);
12543   return ExecutiveColor(G, s1->getName(), color, flags, quiet);
12544 }
12545 
ExecutiveColor(PyMOLGlobals * G,const char * name,const char * color,int flags,int quiet)12546 pymol::Result<> ExecutiveColor(
12547     PyMOLGlobals* G, const char* name, const char* color, int flags, int quiet)
12548 {
12549   /* flags:
12550      0x1 -- ignore or suppress selection name matches
12551    */
12552 
12553   CExecutive *I = G->Executive;
12554   int col_ind;
12555   int ok = false;
12556   col_ind = ColorGetIndex(G, color);
12557   if((!name) || (!name[0]))
12558     name = cKeywordAll;
12559   if(col_ind == -1) {
12560     return pymol::Error("Unknown color.");
12561   } else {
12562     CTracker *I_Tracker = I->Tracker;
12563     SpecRec *rec = NULL;
12564     int n_atm = 0;
12565     int n_obj = 0;
12566 
12567     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
12568     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
12569     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
12570       if(rec) {
12571         switch (rec->type) {
12572         case cExecSelection:
12573         case cExecObject:
12574         case cExecAll:
12575           if((rec->type == cExecSelection) ||   /* coloring a selection */
12576              (rec->type == cExecAll) || /* coloring all */
12577              ((rec->type == cExecObject) &&     /* coloring object and its backing selection */
12578               (rec->obj->type == cObjectMolecule))) {
12579             if(!(flags & 0x1)) {
12580               int sele = SelectorIndexByName(G, rec->name);
12581               ObjectMoleculeOpRec op;
12582               if(sele >= 0) {
12583                 ok = true;
12584                 ObjectMoleculeOpRecInit(&op);
12585                 op.code = OMOP_COLR;
12586                 op.i1 = col_ind;
12587                 op.i2 = n_atm;
12588                 ExecutiveObjMolSeleOp(G, sele, &op);
12589                 n_atm = op.i2;
12590                 op.code = OMOP_INVA;
12591                 op.i1 = cRepBitmask;
12592                 op.i2 = cRepInvColor;
12593                 ExecutiveObjMolSeleOp(G, sele, &op);
12594               }
12595             }
12596           }
12597           break;
12598         }
12599 
12600         switch (rec->type) {    /* sets object color */
12601         case cExecObject:
12602           rec->obj->Color = col_ind;
12603           rec->obj->invalidate(cRepAll, cRepInvColor, -1);
12604           n_obj++;
12605           ok = true;
12606           SceneInvalidate(G);
12607           break;
12608         case cExecAll:
12609           rec = NULL;
12610           while(ListIterate(I->Spec, rec, next)) {
12611             if(rec->type == cExecObject) {
12612               rec->obj->Color = col_ind;
12613               rec->obj->invalidate(cRepAll, cRepInvColor, -1);
12614               n_obj++;
12615               ok = true;
12616               SceneInvalidate(G);
12617             }
12618           }
12619           break;
12620         }
12621       }
12622     }
12623     TrackerDelList(I_Tracker, list_id);
12624     TrackerDelIter(I_Tracker, iter_id);
12625 
12626     if(n_obj || n_atm) {
12627       char atms[] = "s";
12628       char objs[] = "s";
12629       if(n_obj < 2)
12630         objs[0] = 0;
12631       if(n_atm < 2)
12632         atms[0] = 0;
12633       if(!quiet) {
12634 
12635         if(n_obj && n_atm) {
12636           PRINTFB(G, FB_Executive, FB_Actions)
12637             " Executive: Colored %d atom%s and %d object%s.\n", n_atm, atms, n_obj, objs
12638             ENDFB(G);
12639         } else if(n_obj) {
12640           PRINTFB(G, FB_Executive, FB_Actions)
12641             " Executive: Colored %d object%s.\n", n_obj, objs ENDFB(G);
12642         } else {
12643           PRINTFB(G, FB_Executive, FB_Actions)
12644             " Executive: Colored %d atom%s.\n", n_atm, atms ENDFB(G);
12645         }
12646       }
12647     }
12648   }
12649   return {};
12650 }
12651 
12652 
12653 /*========================================================================*/
ExecutiveFindBestNameMatch(PyMOLGlobals * G,const char * name)12654 const char *ExecutiveFindBestNameMatch(PyMOLGlobals * G, const char *name)
12655 {
12656   const char *result;
12657   CExecutive *I = G->Executive;
12658   SpecRec *rec = NULL, *best_rec = NULL;
12659   int best;
12660   int wm;
12661   auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
12662 
12663   best = 0;
12664   result = name;
12665 
12666   while(ListIterate(I->Spec, rec, next)) {
12667     wm = WordMatch(G, name, rec->name, ignore_case);
12668     if(wm < 0) {
12669       best_rec = rec;
12670       best = wm;
12671       break;
12672     } else if((best > 0) && (best < wm)) {
12673       best_rec = rec;
12674       best = wm;
12675     }
12676   }
12677   if(best_rec)
12678     result = best_rec->name;
12679   return (result);
12680 }
12681 
12682 
12683 /*========================================================================*/
count_objects(PyMOLGlobals * G,int public_only)12684 static int count_objects(PyMOLGlobals * G, int public_only)
12685 {
12686   int count = 0;
12687   CExecutive *I = G->Executive;
12688   SpecRec *rec = NULL;
12689   while(ListIterate(I->Spec, rec, next)) {
12690     if(rec->type == cExecObject) {
12691       if(!public_only)
12692         count++;
12693       else if(rec->obj->Name[0] != '_')
12694         count++;
12695     }
12696   }
12697   return count;
12698 }
12699 
ExecutiveFindSpec(PyMOLGlobals * G,const char * name)12700 static SpecRec *ExecutiveFindSpec(PyMOLGlobals * G, const char *name)
12701 {
12702   CExecutive *I = G->Executive;
12703   SpecRec *rec = NULL;
12704   // ignore % prefix
12705   if(name[0] && name[0] == '%')
12706     name++;
12707   {                             /* first, try for perfect, case-specific match */
12708     OVreturn_word result;
12709     if(OVreturn_IS_OK((result = OVLexicon_BorrowFromCString(I->Lex, name)))) {
12710       if(OVreturn_IS_OK((result = OVOneToOne_GetForward(I->Key, result.word)))) {
12711         if(!TrackerGetCandRef(I->Tracker, result.word, (TrackerRef **) (void *) &rec)) {
12712           rec = NULL;
12713         }
12714       }
12715     }
12716     if(!rec) {                  /* otherwise try partial/case-nonspecific match */
12717       rec = ExecutiveAnyCaseNameMatch(G, name);
12718     }
12719   }
12720   return (rec);
12721 }
12722 
12723 
12724 /*========================================================================*/
ExecutiveObjMolSeleOp(PyMOLGlobals * G,int sele,ObjectMoleculeOpRec * op)12725 void ExecutiveObjMolSeleOp(PyMOLGlobals * G, int sele, ObjectMoleculeOpRec * op)
12726 {
12727   CExecutive *I = G->Executive;
12728   SpecRec *rec = NULL;
12729   ObjectMolecule *obj = NULL;
12730   int update_table = true;
12731 
12732 	/* if we're given a valid selection */
12733   if(sele >= 0) {
12734 		/* iterate over all the objects in the global list */
12735     while(ListIterate(I->Spec, rec, next)) {
12736       if(rec->type == cExecObject) {
12737         if(rec->obj->type == cObjectMolecule) {
12738 					/* if the objects are valid molecules, then perform the operation in op_code */
12739           obj = (ObjectMolecule *) rec->obj;
12740 
12741           switch (op->code) {
12742           case OMOP_RenameAtoms:
12743             {
12744               int result = SelectorRenameObjectAtoms(G, obj, sele, op->i2, update_table);
12745               if(result > 0)
12746                 op->i1 += result;
12747               update_table = false;
12748             }
12749             break;
12750           default:
12751 						/* all other cases, perform the operation on obj */
12752             ObjectMoleculeSeleOp(obj, sele, op);
12753             break;
12754           }
12755         }
12756       }
12757     }
12758   }
12759 }
12760 
12761 
12762 /*========================================================================*/
ExecutiveGetCameraExtent(PyMOLGlobals * G,const char * name,float * mn,float * mx,int transformed,int state)12763 int ExecutiveGetCameraExtent(PyMOLGlobals * G, const char *name, float *mn, float *mx,
12764                              int transformed, int state)
12765 {
12766   int sele;
12767   ObjectMoleculeOpRec op;
12768   int flag = false;
12769 
12770   if((state == -2) || (state == -3))    /* TO DO: support per-object states */
12771     state = SceneGetState(G);
12772 
12773   PRINTFD(G, FB_Executive)
12774     " %s: name %s state %d\n", __func__, name, state ENDFD;
12775 
12776   sele = SelectorIndexByName(G, name);
12777 
12778   if(sele >= 0) {
12779     ObjectMoleculeOpRecInit(&op);
12780     if(state < 0) {
12781       op.code = OMOP_CameraMinMax;
12782     } else {
12783       op.code = OMOP_CSetCameraMinMax;
12784       op.cs1 = state;
12785     }
12786     op.v1[0] = FLT_MAX;
12787     op.v1[1] = FLT_MAX;
12788     op.v1[2] = FLT_MAX;
12789     op.v2[0] = -FLT_MAX;
12790     op.v2[1] = -FLT_MAX;
12791     op.v2[2] = -FLT_MAX;
12792     op.i1 = 0;
12793     op.i2 = transformed;
12794     op.mat1 = SceneGetMatrix(G);
12795 
12796     ExecutiveObjMolSeleOp(G, sele, &op);
12797 
12798     PRINTFD(G, FB_Executive)
12799       " %s: minmax over %d vertices\n", __func__, op.i1 ENDFD;
12800     if(op.i1)
12801       flag = true;
12802   }
12803   copy3f(op.v1, mn);
12804   copy3f(op.v2, mx);
12805 
12806   PRINTFD(G, FB_Executive)
12807     " %s: returning %d\n", __func__, flag ENDFD;
12808 
12809   return (flag);
12810 }
12811 
12812 
12813 /*========================================================================*/
ExecutiveGetExtent(PyMOLGlobals * G,const char * name,float * mn,float * mx,int transformed,int state,int weighted)12814 int ExecutiveGetExtent(PyMOLGlobals * G, const char *name, float *mn, float *mx,
12815                        int transformed, int state, int weighted)
12816 {
12817   int sele;
12818   ObjectMoleculeOpRec op, op2;
12819   CExecutive *I = G->Executive;
12820   CObject *obj;
12821   int result = false;
12822   float f1, f2, fmx;
12823   int a;
12824 
12825   if(WordMatchExact(G, cKeywordCenter, name, true)) {
12826     SceneGetCenter(G, mn);
12827     copy3f(mn, mx);
12828     return 1;
12829   }
12830   if(WordMatchExact(G, cKeywordOrigin, name, true)) {
12831     SceneOriginGet(G, mn);
12832     copy3f(mn, mx);
12833     return 1;
12834   }
12835 
12836   PRINTFD(G, FB_Executive)
12837     " %s: name %s state %d\n", __func__, name, state ENDFD;
12838 
12839   ObjectMoleculeOpRecInit(&op);
12840   ObjectMoleculeOpRecInit(&op2);
12841 
12842   if((state == -2) || (state == -3)) {  /* we want the currently displayed state */
12843     state = SceneGetState(G);
12844     op.include_static_singletons = true;        /* make sure we get the static singletons too */
12845     op2.include_static_singletons = true;
12846   }
12847 
12848   op2.i1 = 0;
12849   op2.v1[0] = -1.0;
12850   op2.v1[1] = -1.0;
12851   op2.v1[2] = -1.0;
12852   op2.v2[0] = 1.0;
12853   op2.v2[1] = 1.0;
12854   op2.v2[2] = 1.0;
12855 
12856   {
12857     auto matched_recs = ExecutiveGetSpecRecsFromPattern(G, name);
12858     int have_atoms_flag = false;
12859     int have_extent_flag = false;
12860 
12861     /* first, compute atomic extents */
12862 
12863     if(weighted) {
12864       op2.i1 = 0;
12865 
12866       op2.v1[0] = 0.0F;
12867       op2.v1[1] = 0.0F;
12868       op2.v1[2] = 0.0F;
12869 
12870       op.i1 = 0;
12871 
12872       op.v1[0] = FLT_MAX;
12873       op.v1[1] = FLT_MAX;
12874       op.v1[2] = FLT_MAX;
12875 
12876       op.v2[0] = -FLT_MAX;
12877       op.v2[1] = -FLT_MAX;
12878       op.v2[2] = -FLT_MAX;
12879     }
12880 
12881     /* first, handle molecular objects */
12882 
12883     for (auto& recref : matched_recs) {
12884       auto* rec = &recref;
12885           switch (rec->type) {
12886           case cExecObject:
12887             if (rec->obj->type != cObjectMolecule &&
12888                 rec->obj->type != cObjectAlignment)
12889               break;
12890           case cExecSelection:
12891           case cExecAll:
12892             if(rec->type == cExecAll)
12893               sele = SelectorIndexByName(G, cKeywordAll);
12894             else
12895               sele = SelectorIndexByName(G, rec->name);
12896             if(sele >= 0) {
12897               if(state < 0) {
12898                 op.code = OMOP_MNMX;
12899               } else {
12900                 op.code = OMOP_CSetMinMax;
12901                 op.cs1 = state;
12902               }
12903               op.i2 = transformed;
12904               ExecutiveObjMolSeleOp(G, sele, &op);
12905               if(op.i1) {
12906                 have_atoms_flag = true;
12907               }
12908               PRINTFD(G, FB_Executive)
12909                 " %s: minmax over %d vertices\n", __func__, op.i1 ENDFD;
12910             }
12911 
12912             if(weighted) {
12913               if(state < 0)
12914                 op2.code = OMOP_SUMC;
12915               else {
12916                 op2.code = OMOP_CSetSumVertices;
12917                 op2.cs1 = state;
12918               }
12919               op2.i2 = transformed;
12920               ExecutiveObjMolSeleOp(G, sele, &op2);
12921             }
12922             break;
12923           }
12924     }
12925     if(have_atoms_flag)
12926       have_extent_flag = true;
12927 
12928     /* now handle nonmolecular objects */
12929 
12930     for (auto& recref : matched_recs) {
12931       auto* rec = &recref;
12932           switch (rec->type) {
12933           case cExecAll:
12934             rec = NULL;
12935             while(ListIterate(I->Spec, rec, next)) {
12936               if(rec->type == cExecObject) {
12937                 obj = rec->obj;
12938                 if(!obj->ExtentFlag) {
12939                   switch (obj->type) {
12940                   case cObjectMap:
12941                   case cObjectMesh:
12942                   case cObjectSurface:
12943                     if(!rec->obj->ExtentFlag) {
12944                       /* allow object to update extents, if necessary */
12945                       rec->obj->update();
12946                     }
12947                   }
12948                 }
12949                 if(obj->ExtentFlag)
12950                   switch (obj->type) {
12951                   case cObjectMolecule:
12952                     break;
12953                     /* intentional fall-through */
12954                   default:
12955                     if(!have_extent_flag) {
12956                       copy3f(obj->ExtentMin, op.v1);
12957                       copy3f(obj->ExtentMax, op.v2);
12958                       have_extent_flag = true;
12959                     } else {
12960                       min3f(obj->ExtentMin, op.v1, op.v1);
12961                       max3f(obj->ExtentMax, op.v2, op.v2);
12962                     }
12963                     break;
12964                   }
12965               }
12966             }
12967             break;
12968           case cExecObject:
12969             obj = rec->obj;
12970             if(!obj->ExtentFlag) {
12971               switch (obj->type) {
12972               case cObjectMap:
12973               case cObjectMesh:
12974               case cObjectSurface:
12975                 if(!rec->obj->ExtentFlag) {
12976                   /* allow object to update extents, if necessary */
12977                   rec->obj->update();
12978                 }
12979               }
12980             }
12981             if(obj->ExtentFlag)
12982               switch (obj->type) {
12983               case cObjectMolecule:    /* will have been handled above... */
12984                 break;
12985               default:
12986                 if(!have_extent_flag) {
12987                   copy3f(obj->ExtentMin, op.v1);
12988                   copy3f(obj->ExtentMax, op.v2);
12989                   have_extent_flag = true;
12990                 } else {
12991                   min3f(obj->ExtentMin, op.v1, op.v1);
12992                   max3f(obj->ExtentMax, op.v2, op.v2);
12993                 }
12994                 break;
12995               }
12996             break;
12997           }
12998     }
12999 
13000     if(have_atoms_flag && weighted) {
13001       if(op2.i1) {
13002         op2.v1[0] /= op2.i1;    /* compute average */
13003         op2.v1[1] /= op2.i1;
13004         op2.v1[2] /= op2.i1;
13005 
13006         for(a = 0; a < 3; a++) {        /* this puts origin at the weighted center */
13007           f1 = op2.v1[a] - op.v1[a];
13008           f2 = op.v2[a] - op2.v1[a];
13009           if(f1 > f2)
13010             fmx = f1;
13011           else
13012             fmx = f2;
13013           op.v1[a] = op2.v1[a] - fmx;
13014           op.v2[a] = op2.v1[a] + fmx;
13015         }
13016       }
13017     }
13018 
13019     if(have_extent_flag) {
13020       copy3f(op.v1, mn);
13021       copy3f(op.v2, mx);
13022     } else {
13023       zero3f(mn);
13024       zero3f(mx);
13025     }
13026 
13027     result = have_extent_flag;
13028 
13029   }
13030 
13031   PRINTFD(G, FB_Executive)
13032     " %s: returning %d\n", __func__, result ENDFD;
13033 
13034   return result;
13035 }
13036 
13037 
13038 /*========================================================================*/
ExecutiveGetMaxDistance(PyMOLGlobals * G,const char * name,float * pos,float * dev,int transformed,int state)13039 static int ExecutiveGetMaxDistance(PyMOLGlobals * G, const char *name, float *pos, float *dev,
13040                                    int transformed, int state)
13041 {
13042   int sele;
13043   ObjectMoleculeOpRec op, op2;
13044   CExecutive *I = G->Executive;
13045   CObject *obj;
13046   int flag = false;
13047   float f1, fmx = 0.0F;
13048 
13049   if((state == -2) || (state == -3))    /* TO DO: support per-object states */
13050     state = SceneGetState(G);
13051 
13052   PRINTFD(G, FB_Executive)
13053     " %s: name %s state %d\n", __func__, name, state ENDFD;
13054 
13055   ObjectMoleculeOpRecInit(&op);
13056   ObjectMoleculeOpRecInit(&op2);
13057 
13058   {
13059     auto matched_recs = ExecutiveGetSpecRecsFromPattern(G, name);
13060 
13061     op2.i1 = 0;
13062     op2.v1[0] = -1.0;
13063     op2.v1[1] = -1.0;
13064     op2.v1[2] = -1.0;
13065     op2.v2[0] = 1.0;
13066     op2.v2[1] = 1.0;
13067     op2.v2[2] = 1.0;
13068 
13069     {
13070       /* first handle molecular objects */
13071 
13072       for (auto& recref : matched_recs) {
13073         auto* rec = &recref;
13074           switch (rec->type) {
13075           case cExecObject:
13076           case cExecSelection:
13077           case cExecAll:
13078             if(rec->type == cExecAll)
13079               sele = SelectorIndexByName(G, cKeywordAll);
13080             else
13081               sele = SelectorIndexByName(G, rec->name);
13082             if(sele >= 0) {
13083               if(state < 0) {
13084                 op.code = OMOP_MaxDistToPt;
13085               } else {
13086                 op.code = OMOP_CSetMaxDistToPt;
13087                 op.cs1 = state;
13088               }
13089               op.v1[0] = pos[0];
13090               op.v1[1] = pos[1];
13091               op.v1[2] = pos[2];
13092               op.i1 = 0;
13093               op.f1 = 0.0F;
13094               op.i2 = transformed;
13095               ExecutiveObjMolSeleOp(G, sele, &op);
13096               fmx = op.f1;
13097 
13098               if(op.i1)
13099                 flag = true;
13100             }
13101             break;
13102           }
13103       }
13104     }
13105 
13106     {
13107       /* now handle nonmolecular objects */
13108 
13109       for (auto& recref : matched_recs) {
13110         auto* rec = &recref;
13111           switch (rec->type) {
13112           case cExecAll:
13113             rec = NULL;
13114             while(ListIterate(I->Spec, rec, next)) {
13115               if(rec->type == cExecObject) {
13116                 obj = rec->obj;
13117                 if(obj->ExtentFlag) {
13118                   switch (obj->type) {
13119                   case cObjectMolecule:
13120                     break;
13121                   default:
13122                     if(obj->ExtentFlag) {
13123                       f1 = (float) diff3f(obj->ExtentMin, pos);
13124                       if(fmx < f1)
13125                         fmx = f1;
13126                       f1 = (float) diff3f(obj->ExtentMax, pos);
13127                       if(fmx < f1)
13128                         fmx = f1;
13129                       flag = true;
13130                       break;
13131                     }
13132                   }
13133                 }
13134               }
13135             }
13136             break;
13137           case cExecObject:
13138             obj = rec->obj;
13139             switch (rec->obj->type) {
13140             case cObjectMolecule:
13141               break;
13142             default:
13143               if(obj->ExtentFlag) {
13144                 f1 = (float) diff3f(obj->ExtentMin, pos);
13145                 if(fmx < f1)
13146                   fmx = f1;
13147                 f1 = (float) diff3f(obj->ExtentMax, pos);
13148                 if(fmx < f1)
13149                   fmx = f1;
13150                 flag = true;
13151               }
13152               break;
13153             }
13154           }
13155         }
13156     }
13157   }
13158   *dev = fmx;
13159   return (flag);
13160 }
13161 
13162 
13163 /*========================================================================*/
ExecutiveWindowZoom(PyMOLGlobals * G,const char * name,float buffer,int state,int inclusive,float animate,int quiet)13164 pymol::Result<> ExecutiveWindowZoom(PyMOLGlobals* G,
13165     const char* name, float buffer, int state, int inclusive, float animate,
13166     int quiet)
13167 {
13168   float center[3], radius;
13169   float mn[3], mx[3], df[3];
13170   int sele0;
13171 
13172   PRINTFD(G, FB_Executive)
13173     " ExecutiveWindowZoom-DEBUG: entered\n" ENDFD;
13174   if(ExecutiveGetExtent(G, name, mn, mx, true, state, true)) {
13175     if(buffer != 0.0F) {
13176       mx[0] += buffer;
13177       mx[1] += buffer;
13178       mx[2] += buffer;
13179       mn[0] -= buffer;
13180       mn[1] -= buffer;
13181       mn[2] -= buffer;
13182     }
13183     subtract3f(mx, mn, df);
13184     average3f(mn, mx, center);
13185     if(inclusive) {
13186       if(!ExecutiveGetMaxDistance(G, name, center, &radius, true, state))
13187         radius = 0.0;
13188       radius += buffer;
13189     } else {
13190       radius = df[0];
13191       if(radius < df[1])
13192         radius = df[1];
13193       if(radius < df[2])
13194         radius = df[2];
13195       radius = radius / 2.0F;
13196     }
13197     if(radius < MAX_VDW)
13198       radius = MAX_VDW;
13199     PRINTFD(G, FB_Executive)
13200       " %s: zooming with radius %8.3f...state %d\n", __func__, radius, state ENDFD;
13201     PRINTFD(G, FB_Executive)
13202       " %s: on center %8.3f %8.3f %8.3f...\n", __func__, center[0],
13203       center[1], center[2]
13204       ENDFD;
13205     if(animate < 0.0F) {
13206       if(SettingGetGlobal_b(G, cSetting_animation))
13207         animate = SettingGetGlobal_f(G, cSetting_animation_duration);
13208       else
13209         animate = 0.0F;
13210     }
13211     if(animate != 0.0F)
13212       ScenePrimeAnimation(G);
13213     SceneOriginSet(G, center, false);
13214     SceneWindowSphere(G, center, radius);
13215     if(animate != 0.0F)
13216       SceneLoadAnimation(G, animate, 0);
13217     else
13218       SceneAbortAnimation(G);
13219     SceneInvalidate(G);
13220   } else {
13221 
13222     sele0 = SelectorIndexByName(G, name);
13223     if(sele0 > 0) {             /* any valid selection except "all" */
13224       /* no longer an error to zoom on an empty selection -- just has no effect */
13225       if(!quiet) {
13226         PRINTFB(G, FB_Executive, FB_Warnings)
13227           "ExecutiveWindowZoom-Warning: selection doesn't specify any coordinates.\n"
13228           ENDFB(G);
13229       }
13230     } else if(ExecutiveValidName(G, name)) {
13231       PRINTFD(G, FB_Executive)
13232         " ExecutiveWindowZoom-DEBUG: name valid, but no extents -- using default view\n"
13233         ENDFD;
13234       SceneSetDefaultView(G);
13235       SceneInvalidate(G);
13236     } else {
13237       return pymol::make_error( __func__, "selection or object unknown.");
13238     }
13239   }
13240   return {};
13241 }
13242 
13243 
13244 /*========================================================================*/
ExecutiveCenter(PyMOLGlobals * G,const char * name,int state,int origin,float animate,float * pos,int quiet)13245 pymol::Result<> ExecutiveCenter(PyMOLGlobals* G, const char* name,
13246     int state, int origin, float animate, float* pos, int quiet)
13247 {
13248   float center[3];
13249   float mn[3], mx[3];
13250   int sele0;
13251   int have_center = false;
13252 
13253   if(name && ExecutiveGetExtent(G, name, mn, mx, true, state, true)) {
13254     average3f(mn, mx, center);
13255     have_center = true;
13256     PRINTFD(G, FB_Executive)
13257       " %s: centering state %d\n", __func__, state ENDFD;
13258     PRINTFD(G, FB_Executive)
13259       " %s: on center %8.3f %8.3f %8.3f...\n", __func__, center[0],
13260       center[1], center[2]
13261       ENDFD;
13262   } else if(pos) {
13263     have_center = true;
13264     copy3f(pos, center);
13265   }
13266   if(have_center) {
13267     if(animate < 0.0F) {
13268       if(SettingGetGlobal_b(G, cSetting_animation))
13269         animate = SettingGetGlobal_f(G, cSetting_animation_duration);
13270       else
13271         animate = 0.0F;
13272     }
13273 
13274     if(animate != 0.0F)
13275       ScenePrimeAnimation(G);
13276     if(origin)
13277       SceneOriginSet(G, center, false);
13278     SceneRelocate(G, center);
13279     SceneInvalidate(G);
13280     if(animate != 0.0F)
13281       SceneLoadAnimation(G, animate, 0);
13282   } else {
13283     sele0 = SelectorIndexByName(G, name);
13284     if(sele0 >= 0) {            /* any valid selection except "all" */
13285       if(!quiet) {
13286         /* no longer an error to center on an empty selection -- just have no effect */
13287         PRINTFB(G, FB_Executive, FB_Warnings)
13288           "ExecutiveCenter-Warning: selection doesn't specify any coordinates.\n"
13289           ENDFB(G);
13290       }
13291     } else if(ExecutiveValidName(G, name)) {
13292       SceneSetDefaultView(G);
13293       SceneInvalidate(G);
13294     } else {
13295       return pymol::make_error("Selection or object unknown.");
13296     }
13297   }
13298   return {};
13299 }
13300 
13301 
13302 /*========================================================================*/
ExecutiveOrigin(PyMOLGlobals * G,const char * sele,int preserve,const char * oname,float * pos,int state)13303 pymol::Result<> ExecutiveOrigin(PyMOLGlobals* G, const char* sele, int preserve,
13304     const char* oname, float* pos, int state)
13305 {
13306 
13307   float center[3];
13308   float mn[3], mx[3];
13309   CObject *obj = NULL;
13310   int have_center = false;
13311   if(oname && oname[0]) {
13312     obj = ExecutiveFindObjectByName(G, oname);
13313     if(!obj)
13314       return pymol::make_error("Object ", oname, " not found.");
13315   }
13316   if(sele && sele[0]) {
13317     auto s1 = SelectorTmp2::make(G, sele);
13318     auto has_extent = ExecutiveGetExtent(G, s1->getName(), mn, mx, true, state, true);
13319     if(!has_extent) {
13320       return pymol::make_error("Could not determine extent of selection.");
13321     }
13322     average3f(mn, mx, center);
13323     have_center = true;
13324   } else if(pos) {
13325     copy3f(pos, center);
13326     have_center = true;
13327   }
13328 
13329   if(!have_center) {
13330     return pymol::make_error("Center could not be determined.");
13331   }
13332 
13333   if(obj) {
13334     ObjectSetTTTOrigin(obj, center);
13335     PRINTFB(G, FB_Executive, FB_Blather)
13336       " %s: origin for %s set to %8.3f %8.3f %8.3f\n", __func__,
13337       oname, center[0], center[1], center[2]
13338       ENDFB(G);
13339   } else {
13340     PRINTFB(G, FB_Executive, FB_Blather)
13341       " %s: scene origin set to %8.3f %8.3f %8.3f\n", __func__,
13342       center[0], center[1], center[2]
13343       ENDFB(G);
13344     SceneOriginSet(G, center, preserve);
13345   }
13346   SceneInvalidate(G);
13347   return {};
13348 }
13349 
13350 
13351 /*========================================================================*/
ExecutiveGetMoment(PyMOLGlobals * G,const char * name,double * mi,int state)13352 int ExecutiveGetMoment(PyMOLGlobals * G, const char *name, double *mi, int state)
13353 {
13354   int sele;
13355   ObjectMoleculeOpRec op;
13356   int a, b;
13357   int c = 0;
13358 
13359   if((state == -2) || (state == -3))    /* TO DO: support per-object states */
13360     state = SceneGetState(G);
13361 
13362   sele = SelectorIndexByName(G, name);
13363   if(sele >= 0) {
13364     ObjectMoleculeOpRecInit(&op);
13365     if(state < 0) {
13366       op.code = OMOP_SUMC;
13367     } else {
13368       op.code = OMOP_CSetSumVertices;
13369       op.cs1 = state;
13370     }
13371 
13372     op.v1[0] = 0.0;
13373     op.v1[1] = 0.0;
13374     op.v1[2] = 0.0;
13375     op.i1 = 0;
13376     op.i2 = 0;                  /* untransformed...is this right? */
13377 
13378     ExecutiveObjMolSeleOp(G, sele, &op);
13379 
13380     if(op.i1) {                 /* any vertices? */
13381       c += op.i1;
13382       scale3f(op.v1, 1.0F / op.i1, op.v1);      /* compute raw average */
13383       if(state < 0) {
13384         op.code = OMOP_MOME;
13385       } else {
13386         op.code = OMOP_CSetMoment;
13387         op.cs1 = state;
13388       }
13389       for(a = 0; a < 3; a++)
13390         for(b = 0; b < 3; b++)
13391           op.d[a][b] = 0.0;
13392       ExecutiveObjMolSeleOp(G, sele, &op);
13393       {
13394         double *p = mi;
13395         for(a = 0; a < 3; a++)
13396           for(b = 0; b < 3; b++)
13397             *(p++) = op.d[a][b];
13398       }
13399     }
13400   } else {
13401     identity33d(mi);
13402   }
13403 
13404   return (c);
13405 }
13406 
13407 
13408 /*========================================================================*/
13409 pymol::Result<>
ExecutiveSetObjVisib(PyMOLGlobals * G,pymol::zstring_view name,int onoff,int parents)13410 ExecutiveSetObjVisib(PyMOLGlobals * G, pymol::zstring_view name, int onoff, int parents)
13411 {
13412   CExecutive *I = G->Executive;
13413   PRINTFD(G, FB_Executive)
13414     " ExecutiveSetObjVisib: entered.\n" ENDFD;
13415   {
13416     CTracker *I_Tracker = I->Tracker;
13417     SpecRec *rec;
13418     int list_id = ExecutiveGetNamesListFromPattern(G, name.data(), true, false);
13419     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
13420     int suppress_hidden = SettingGetGlobal_b(G, cSetting_suppress_hidden);
13421     int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
13422     if(suppress_hidden && hide_underscore)
13423       ExecutiveUpdateGroups(G, false);
13424     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
13425 
13426       if(rec) {
13427         switch (rec->type) {
13428         case cExecAll:
13429           {
13430             SpecRec *tRec = NULL;
13431             while(ListIterate(I->Spec, tRec, next)) {
13432               if(onoff != tRec->visible) {
13433                 if(tRec->type == cExecObject) {
13434                   if(tRec->visible) {
13435                     tRec->in_scene = SceneObjectDel(G, tRec->obj, true);
13436                     ExecutiveInvalidateSceneMembers(G);
13437                     tRec->visible = !tRec->visible;
13438 		    ReportEnabledChange(G, rec);
13439                   } else {
13440                     if((!suppress_hidden) || (!hide_underscore) || (!tRec->is_hidden)) {
13441                       tRec->in_scene = SceneObjectAdd(G, tRec->obj);
13442                       ExecutiveInvalidateSceneMembers(G);
13443                       tRec->visible = !tRec->visible;
13444 		      ReportEnabledChange(G, rec);
13445                     }
13446                   }
13447                 } else if((tRec->type != cExecSelection) || (!onoff)) {  /* hide all selections, but show all */
13448                   tRec->visible = !tRec->visible;
13449                   ReportEnabledChange(G, rec);
13450                 }
13451               }
13452             }
13453           }
13454           break;
13455         case cExecObject:
13456           /*
13457              if(rec->visible!=onoff) {
13458              if(rec->visible) {
13459              rec->in_scene = SceneObjectDel(G,rec->obj);
13460              ExecutiveInvalidateSceneMembers(G);
13461              } else {
13462              rec->in_scene = SceneObjectAdd(G,rec->obj);
13463              ExecutiveInvalidateSceneMembers(G);
13464              }
13465              rec->visible=!rec->visible;
13466              }
13467            */
13468           if(onoff) {           /* enable */
13469             ExecutiveSpecEnable(G, rec, parents, false);
13470           } else {              /* disable */
13471             if(rec->visible) {
13472               if(rec->in_scene)
13473                 rec->in_scene = SceneObjectDel(G, rec->obj, true);
13474               rec->visible = false;
13475               ExecutiveInvalidateSceneMembers(G);
13476 	      ReportEnabledChange(G, rec);
13477             }
13478           }
13479           break;
13480         case cExecSelection:
13481           if(rec->visible != onoff) {
13482 	    int previousVisible = rec->visible;
13483             rec->visible = !rec->visible;
13484             if(rec->visible)
13485               if(SettingGetGlobal_b(G, cSetting_active_selections)) {
13486                 ExecutiveHideSelections(G);
13487                 rec->visible = true;
13488               }
13489             SceneInvalidate(G);
13490             SeqDirty(G);
13491 	    if (previousVisible!=rec->visible){
13492 	      ReportEnabledChange(G, rec);
13493 	    }
13494           }
13495           break;
13496         }
13497       }
13498     }
13499     TrackerDelList(I_Tracker, list_id);
13500     TrackerDelIter(I_Tracker, iter_id);
13501   }
13502   PRINTFD(G, FB_Executive)
13503     " ExecutiveSetObjVisib: leaving...\n" ENDFD;
13504   return {};
13505 }
13506 
13507 
13508 /*========================================================================*/
13509 /*
13510  * Full screen state fallback in case we can't get get the state from
13511  * the window manager.
13512  */
13513 static bool _is_full_screen = false;
13514 
13515 /*========================================================================*/
13516 /*
13517  * Get the fullscreen state from the window manager or return -1 if
13518  * not available.
13519  */
ExecutiveIsFullScreen(PyMOLGlobals * G)13520 bool ExecutiveIsFullScreen(PyMOLGlobals * G) {
13521   if(!G->HaveGUI || !G->ValidContext)
13522     return false;
13523 
13524   int flag = -1;
13525 
13526 #if defined(GLUT_FULL_SCREEN)
13527   flag = glutGet(GLUT_FULL_SCREEN);
13528 #endif
13529 
13530   PRINTFD(G, FB_Executive)
13531     " %s: flag=%d fallback=%d.\n", __func__,
13532     flag, _is_full_screen ENDFD;
13533 
13534   if (flag > -1)
13535     return flag;
13536   return _is_full_screen;
13537 }
13538 
13539 /*========================================================================*/
ExecutiveFullScreen(PyMOLGlobals * G,int flag)13540 void ExecutiveFullScreen(PyMOLGlobals * G, int flag)
13541 {
13542   if(!G->HaveGUI)
13543     return;
13544 
13545   int wm_flag = ExecutiveIsFullScreen(G);
13546 
13547   if(flag < 0) {
13548     flag = !wm_flag;
13549   }
13550 
13551   _is_full_screen = (flag != 0);
13552 
13553 #ifndef _PYMOL_NO_GLUT
13554   if(G->HaveGUI && G->ValidContext) {
13555     if (flag) {
13556 #ifndef GLUT_FULL_SCREEN
13557       if(wm_flag < 1) {
13558         CExecutive *I = G->Executive;
13559         I->oldPX = p_glutGet(P_GLUT_WINDOW_X);
13560         I->oldPY = p_glutGet(P_GLUT_WINDOW_Y);
13561         I->oldWidth = p_glutGet(P_GLUT_WINDOW_WIDTH);
13562         I->oldHeight = p_glutGet(P_GLUT_WINDOW_HEIGHT);
13563       }
13564 #endif
13565 
13566       p_glutFullScreen();
13567     } else {
13568 #ifndef GLUT_FULL_SCREEN
13569       // freeglut < 2.6
13570       CExecutive *I = G->Executive;
13571       p_glutReshapeWindow(I->oldWidth, I->oldHeight);
13572       p_glutPositionWindow(I->oldPX, I->oldPY);
13573 #elif !defined(GLUT_HAS_MULTI)
13574       // freeglut < 2.8
13575       if(wm_flag)
13576         glutFullScreenToggle();
13577 #else
13578       glutLeaveFullScreen();
13579 #endif
13580     }
13581   }
13582 #endif
13583 
13584   PyMOL_NeedReshape(G->PyMOL, flag, 0, 0, 0, 0);
13585   SceneChanged(G);
13586 }
13587 
13588 
13589 /*========================================================================*/
13590 static
fInvalidateRepMask(CObject * obj,int repmask,int state=-1)13591 void fInvalidateRepMask(CObject * obj, int repmask, int state=-1) {
13592     for(int a = 0; a < cRepCnt; a++) {
13593       if ((1 << a) & repmask)
13594         obj->invalidate(a, cRepInvVisib, state);
13595     }
13596 }
13597 
13598 
13599 /*========================================================================*/
13600 pymol::Result<>
ExecutiveToggleRepVisib(PyMOLGlobals * G,const char * name,int rep)13601 ExecutiveToggleRepVisib(PyMOLGlobals * G, const char *name, int rep)
13602 {
13603   int sele = -1;
13604   SpecRec *tRec;
13605   ObjectMoleculeOpRec op;
13606   OrthoLineType tmpname;
13607 
13608   PRINTFD(G, FB_Executive)
13609     " ExecutiveToggleRepVisib: entered.\n" ENDFD;
13610 
13611   tRec = ExecutiveFindSpec(G, name);
13612 
13613   if(rep == -2) {
13614     // special case: toggle object visibility (should that be in this function?)
13615     if(tRec) {
13616       ExecutiveSetObjVisib(G, name, !tRec->visible, 0);
13617     } else {
13618       return pymol::make_error(name, " not found.");
13619     }
13620   } else if(tRec && tRec->type == cExecObject &&
13621       tRec->obj->type != cObjectMolecule) {
13622     // non-atom object
13623     tRec->obj->visRep ^= rep;
13624     fInvalidateRepMask(tRec->obj, rep, 0);
13625     SceneChanged(G);
13626   } else if(SelectorGetTmp(G, name, tmpname) >= 0) {
13627     // atom selection
13628     sele = SelectorIndexByName(G, tmpname);
13629     if(sele >= 0) {
13630           ObjectMoleculeOpRecInit(&op);
13631 
13632           op.code = OMOP_CheckVis;
13633           op.i1 = rep;
13634           op.i2 = false;
13635           ExecutiveObjMolSeleOp(G, sele, &op);
13636           op.i2 = !op.i2;
13637 
13638           op.code = OMOP_VISI;
13639           op.i1 = rep;
13640           ExecutiveObjMolSeleOp(G, sele, &op);
13641           op.code = OMOP_INVA;
13642           op.i2 = cRepInvVisib;
13643           ExecutiveObjMolSeleOp(G, sele, &op);
13644 
13645     }
13646     SelectorFreeTmp(G, tmpname);
13647   }
13648   PRINTFD(G, FB_Executive)
13649     " ExecutiveToggleRepVisib: leaving...\n" ENDFD;
13650   return {};
13651 }
13652 
13653 
13654 /*========================================================================*/
13655 pymol::Result<>
ExecutiveSetRepVisib(PyMOLGlobals * G,pymol::zstring_view name,int rep,int state)13656 ExecutiveSetRepVisib(PyMOLGlobals * G, pymol::zstring_view name, int rep, int state)
13657 {
13658   int repmask = (rep == cRepAll) ? cRepBitmask : (1 << rep);
13659   return ExecutiveSetRepVisMask(G, name, repmask, state);
13660 }
13661 
13662 pymol::Result<>
ExecutiveSetRepVisMask(PyMOLGlobals * G,pymol::zstring_view name,int repmask,int state)13663 ExecutiveSetRepVisMask(PyMOLGlobals * G, pymol::zstring_view name, int repmask, int state)
13664 {
13665   PRINTFD(G, FB_Executive)
13666     " ExecutiveSetRepVisib: entered.\n" ENDFD;
13667 
13668   {
13669     CExecutive *I = G->Executive;
13670     CTracker *I_Tracker = I->Tracker;
13671     SpecRec *rec = NULL;
13672     int list_id = ExecutiveGetNamesListFromPattern(G, name.data(), true, true);
13673     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
13674     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
13675       if(rec) {
13676         /* per-atom */
13677 
13678         switch (rec->type) {
13679         case cExecObject:
13680           if (rec->obj->type != cObjectMolecule &&
13681               rec->obj->type != cObjectAlignment)
13682             break;
13683         case cExecSelection:
13684           {
13685             int sele = SelectorIndexByName(G, rec->name);
13686             if(sele >= 0) {
13687               ObjectMoleculeOpRec op;
13688               ObjectMoleculeOpRecInit(&op);
13689               op.code = OMOP_VISI;
13690               op.i1 = repmask;
13691               op.i2 = state;
13692               ExecutiveObjMolSeleOp(G, sele, &op);
13693               op.code = OMOP_INVA;
13694               if (state == cVis_AS)
13695                 op.i1 = cRepBitmask;
13696               op.i2 = cRepInvVisib;
13697               ExecutiveObjMolSeleOp(G, sele, &op);
13698             }
13699           }
13700           break;
13701         }
13702 
13703         /* per-object/name */
13704 
13705         switch (rec->type) {
13706         case cExecObject:
13707           ObjectSetRepVisMask(rec->obj, repmask, state);
13708           fInvalidateRepMask(rec->obj, repmask, 0);
13709           SceneChanged(G);
13710           break;
13711         case cExecAll:
13712           ExecutiveSetAllRepVisMask(G, repmask, state);
13713           break;
13714         }
13715       }
13716     }
13717     TrackerDelList(I_Tracker, list_id);
13718     TrackerDelIter(I_Tracker, iter_id);
13719   }
13720   PRINTFD(G, FB_Executive)
13721     " ExecutiveSetRepVisib: leaving...\n" ENDFD;
13722   return {};
13723 }
13724 
13725 
13726 /*========================================================================*/
13727 pymol::Result<>
ExecutiveSetOnOffBySele(PyMOLGlobals * G,pymol::zstring_view sname,int onoff)13728 ExecutiveSetOnOffBySele(PyMOLGlobals * G, pymol::zstring_view sname, int onoff)
13729 {
13730   SelectorTmp2 tmp{G, sname.data()};
13731   const char* name = tmp.getName();
13732   int sele;
13733   SpecRec *tRec;
13734   ObjectMoleculeOpRec op;
13735 
13736   tRec = ExecutiveFindSpec(G, name);
13737   if((!tRec) && (!strcmp(sname.data(), cKeywordAll))) {
13738     ExecutiveSetObjVisib(G, name, onoff, false);
13739   }
13740   if(tRec) {
13741     sele = tmp.getIndex();
13742     if(sele >= 0) {
13743       ObjectMoleculeOpRecInit(&op);
13744 
13745       op.code = OMOP_OnOff;
13746       op.i1 = onoff;
13747       ExecutiveObjMolSeleOp(G, sele, &op);
13748     }
13749   }
13750 
13751   return {};
13752 }
13753 
13754 
13755 /*========================================================================*/
13756 /*
13757  * repmask: rep bit mask
13758  * state: 0 (hide), 1 (show), 2 (as)
13759  */
ExecutiveSetAllRepVisMask(PyMOLGlobals * G,int repmask,int state)13760 static void ExecutiveSetAllRepVisMask(PyMOLGlobals * G, int repmask, int state)
13761 {
13762   ObjectMoleculeOpRec op;
13763   ObjectMolecule *obj;
13764   int sele;
13765 
13766   CExecutive *I = G->Executive;
13767   SpecRec *rec = NULL;
13768   PRINTFD(G, FB_Executive)
13769     " ExecutiveSetAllRepVisib: entered.\n" ENDFD;
13770   while(ListIterate(I->Spec, rec, next)) {
13771     if(rec->type == cExecObject) {
13772       if(rec->type == cExecObject) {
13773         switch (rec->obj->type) {
13774         case cObjectMolecule:
13775           obj = (ObjectMolecule *) rec->obj;
13776           sele = SelectorIndexByName(G, obj->Name);
13777           ObjectMoleculeOpRecInit(&op);
13778 
13779           op.code = OMOP_VISI;
13780           op.i1 = repmask;
13781           op.i2 = state;
13782           ObjectMoleculeSeleOp(obj, sele, &op);
13783           op.code = OMOP_INVA;
13784           if (state == cVis_AS)
13785             op.i1 = cRepBitmask;
13786           op.i2 = cRepInvVisib;
13787           ObjectMoleculeSeleOp(obj, sele, &op);
13788           break;
13789         default:
13790           ObjectSetRepVisMask(rec->obj, repmask, state);
13791           fInvalidateRepMask(rec->obj, repmask, -1);
13792           SceneInvalidate(G);
13793           break;
13794         }
13795       }
13796     }
13797   }
13798   PRINTFD(G, FB_Executive)
13799     " ExecutiveSetAllRepVisib: leaving...\n" ENDFD;
13800 
13801 }
13802 
13803 
13804 /*========================================================================*/
ExecutiveInvalidateRep(PyMOLGlobals * G,const char * str1,int rep,int level)13805 pymol::Result<> ExecutiveInvalidateRep(
13806     PyMOLGlobals* G, const char* str1, int rep, int level)
13807 {
13808   CExecutive *I = G->Executive;
13809   ObjectMoleculeOpRec op;
13810   SpecRec *rec = NULL;
13811   const char* name = "";
13812 
13813   SelectorTmp2 s1;
13814   if(str1 && !WordMatchExact(G, str1, "all", true)) {
13815     s1 = SelectorTmp2(G, str1);
13816     name = s1.getName();
13817   } else {
13818     name = str1;
13819   }
13820 
13821   if((!name) || (!name[0]))
13822     name = cKeywordAll;
13823   {
13824     CTracker *I_Tracker = I->Tracker;
13825     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
13826     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
13827 
13828     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
13829       if(rec) {
13830         switch (rec->type) {
13831         case cExecSelection:
13832         case cExecObject:
13833           {
13834             int sele = SelectorIndexByName(G, rec->name);
13835             if(sele >= 0) {
13836               ObjectMoleculeOpRecInit(&op);
13837               op.code = OMOP_INVA;
13838               op.i1 = (rep == cRepAll) ? cRepBitmask : (1 << rep);
13839               op.i2 = level;
13840               ExecutiveObjMolSeleOp(G, sele, &op);
13841             } else {
13842               rec->obj->invalidate(rep, level, -1);
13843             }
13844           }
13845           break;
13846         case cExecAll:
13847           rec = NULL;
13848           while(ListIterate(I->Spec, rec, next)) {
13849             if(rec->type == cExecObject) {
13850               rec->obj->invalidate(rep, level, -1);
13851             }
13852           }
13853           SceneInvalidate(G);
13854           break;
13855         }
13856       }
13857     }
13858     TrackerDelList(I_Tracker, list_id);
13859     TrackerDelIter(I_Tracker, iter_id);
13860   }
13861   return {};
13862 }
13863 
ExecutiveCheckGroupMembership(PyMOLGlobals * G,int list_id,CObject * obj)13864 int ExecutiveCheckGroupMembership(PyMOLGlobals * G, int list_id, CObject * obj)
13865 {
13866   CExecutive *I = G->Executive;
13867   int result = false;
13868   CTracker *I_Tracker = I->Tracker;
13869   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
13870   if(iter_id) {
13871     SpecRec *rec = NULL;
13872     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
13873       if(rec && (rec->type == cExecObject) && (rec->obj == obj)) {
13874         result = true;
13875         break;
13876       }
13877     }
13878     TrackerDelIter(I_Tracker, iter_id);
13879   }
13880   return result;
13881 }
13882 
ExecutiveGetExpandedGroupListFromPattern(PyMOLGlobals * G,const char * name)13883 int ExecutiveGetExpandedGroupListFromPattern(PyMOLGlobals * G, const char *name)
13884 {
13885   CExecutive *I = G->Executive;
13886   int result = 0;
13887   CWordMatcher *matcher;
13888   CWordMatchOptions options;
13889   CTracker *I_Tracker = I->Tracker;
13890   const char *wildcard = SettingGetGlobal_s(G, cSetting_wildcard);
13891   int iter_id = TrackerNewIter(I_Tracker, 0, I->all_names_list_id);
13892   int cand_id;
13893   SpecRec *rec;
13894 
13895   WordMatchOptionsConfigNameList(&options,
13896                                  *wildcard, SettingGetGlobal_b(G, cSetting_ignore_case));
13897   matcher = WordMatcherNew(G, name, &options, false);
13898   if(matcher) {
13899     if(iter_id) {
13900       while((cand_id = TrackerIterNextCandInList(I_Tracker, iter_id,
13901                                                  (TrackerRef **) (void *) &rec))) {
13902         if(rec && !(rec->type == cExecAll)) {
13903           if(WordMatcherMatchAlpha(matcher, rec->name)) {
13904             if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
13905               if(!result)
13906                 result = TrackerNewList(I_Tracker, NULL);
13907               if(result) {
13908                 TrackerLink(I_Tracker, cand_id, result, 1);
13909               }
13910             }
13911           }
13912         }
13913       }
13914     }
13915   } else if((rec = ExecutiveFindSpec(G, name))) {       /* only one name in list */
13916     if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
13917       result = TrackerNewList(I_Tracker, NULL);
13918       TrackerLink(I_Tracker, rec->cand_id, result, 1);
13919     }
13920   } else if((rec = ExecutiveUnambiguousNameMatch(G, name))) {
13921     if((rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
13922       result = TrackerNewList(I_Tracker, NULL);
13923       TrackerLink(I_Tracker, rec->cand_id, result, 1);
13924     }
13925   }
13926   if(matcher)
13927     WordMatcherFree(matcher);
13928   if(iter_id)
13929     TrackerDelIter(I->Tracker, iter_id);
13930   if(result)
13931     ExecutiveExpandGroupsInList(G, result, cExecExpandGroups);
13932   return result;
13933 }
13934 
ExecutiveGetExpandedGroupList(PyMOLGlobals * G,const char * name)13935 int ExecutiveGetExpandedGroupList(PyMOLGlobals * G, const char *name)
13936 {
13937   CExecutive *I = G->Executive;
13938   int result = 0;
13939   int list_id = 0;
13940 
13941   SpecRec *rec = ExecutiveFindSpec(G, name);
13942   ExecutiveUpdateGroups(G, false);
13943   if(rec && (rec->type == cExecObject) && (rec->obj->type == cObjectGroup)) {
13944     list_id = rec->group_member_list_id;
13945   }
13946   if(list_id) {
13947     result = TrackerNewListCopy(I->Tracker, list_id, NULL);
13948     ExecutiveExpandGroupsInList(G, result, cExecExpandGroups);
13949   }
13950   return result;
13951 }
13952 
ExecutiveFreeGroupList(PyMOLGlobals * G,int list_id)13953 void ExecutiveFreeGroupList(PyMOLGlobals * G, int list_id)
13954 {
13955   CExecutive *I = G->Executive;
13956   TrackerDelList(I->Tracker, list_id);
13957 }
13958 
13959 
13960 /*========================================================================*/
ExecutiveFindObjectByName(PyMOLGlobals * G,const char * name)13961 CObject *ExecutiveFindObjectByName(PyMOLGlobals * G, const char *name)
13962 {
13963   CObject *obj = NULL;
13964   SpecRec *rec = ExecutiveFindSpec(G, name);
13965   if(rec && (rec->type == cExecObject)) {
13966     obj = rec->obj;
13967   }
13968   return (obj);
13969 }
13970 
13971 /*========================================================================*/
13972 /* returns NULL if none found */
ExecutiveFindObjectsByType(PyMOLGlobals * G,int objType)13973 CObject ** ExecutiveFindObjectsByType(PyMOLGlobals * G, int objType) {
13974   CExecutive *I = G->Executive;
13975   SpecRec *rec = NULL;
13976   int n = 0;
13977   CObject** rVal = VLAlloc(CObject*, 1);
13978 
13979   /* loop over all known objects */
13980   while(ListIterate(I->Spec,rec,next)) {
13981     /* make sure it exists and is the right type */
13982     if (rec->obj && rec->type==cExecObject) {
13983       if(rec->obj->type==objType) {
13984 	/* this could be optimized */
13985 	VLACheck(rVal, CObject*, n);
13986 	*(rVal+n) = rec->obj;
13987 	n++;
13988       }
13989     }
13990   }
13991   VLASize(rVal, CObject*, n);
13992   if (n==0) {
13993 	VLAFree(rVal);
13994     return NULL;
13995   }
13996   else
13997     return rVal;
13998 }
13999 
14000 /*========================================================================*/
ExecutiveGetBlock(PyMOLGlobals * G)14001 Block *ExecutiveGetBlock(PyMOLGlobals * G)
14002 {
14003   CExecutive *I = G->Executive;
14004   return (I);
14005 }
14006 
14007 
14008 /*========================================================================*/
ExecutiveSymExp(PyMOLGlobals * G,const char * name,const char * oname,const char * s1,float cutoff,int segi,int quiet)14009 void ExecutiveSymExp(PyMOLGlobals * G, const char *name,
14010                      const char *oname, const char *s1, float cutoff, int segi, int quiet)
14011 {                               /* TODO state */
14012   CObject *ob;
14013   ObjectMolecule *obj = NULL;
14014   ObjectMolecule *new_obj = NULL;
14015   ObjectMoleculeOpRec op;
14016   MapType *map;
14017   int x, y, z, b, c, i, j, h, k, l, n;
14018   ov_size a;
14019   CoordSet *cs, *os;
14020   int keepFlag, sele, tt[3];
14021   const float *v1, *v2;
14022   float m[16], tc[3], ts[3];
14023   OrthoLineType new_name;
14024   float auto_save;
14025 
14026   PRINTFD(G, FB_Executive)
14027     " ExecutiveSymExp: entered.\n" ENDFD;
14028 
14029   /* controls whether we zoom in on newly created objects or not */
14030 	auto_save = SettingGetGlobal_i(G, cSetting_auto_zoom);
14031   SettingSetGlobal_i(G, cSetting_auto_zoom, 0);
14032 
14033   SelectorTmp tmpsele1(G, s1);
14034   sele = tmpsele1.getIndex();
14035 
14036 	/* object to expand */
14037   ob = ExecutiveFindObjectByName(G, oname);
14038 	/* make sure it's a "molecule" type and that it had valid symmetry info */
14039   if(ob->type == cObjectMolecule)
14040     obj = (ObjectMolecule *) ob;
14041   if(!(obj && sele)) {
14042     ErrMessage(G, __func__, "Invalid object");
14043   } else if(!obj->Symmetry) {
14044     ErrMessage(G, __func__, "No symmetry loaded!");
14045   } else if(!SymmetryAttemptGeneration(obj->Symmetry)) {
14046     // unknown space group
14047   } else if(obj->Symmetry->getNSymMat() < 1) {
14048     ErrMessage(G, __func__, "No symmetry matrices!");
14049   } else {
14050     if(!quiet) {
14051       PRINTFB(G, FB_Executive, FB_Actions)
14052         " ExecutiveSymExp: Generating symmetry mates...\n" ENDFB(G);
14053     }
14054 
14055 		/* 1.  Get the center of mass for this object/selection */
14056 		/* the object and selection are valid, so initialize the NEW object */
14057     ObjectMoleculeOpRecInit(&op);
14058     op.code = OMOP_SUMC;
14059     op.i1 = 0;
14060     op.i2 = 0;
14061     op.v1[0] = 0.0;
14062     op.v1[1] = 0.0;
14063     op.v1[2] = 0.0;
14064     ExecutiveObjMolSeleOp(G, sele, &op);
14065 		/* op.v1 is now the complete sum over all coordinates in this selection */
14066     tc[0] = op.v1[0];
14067     tc[1] = op.v1[1];
14068     tc[2] = op.v1[2];
14069 		/* calculate center of mass.  op.i1 is the number of atoms we counter over in the
14070 		 * previous ExecutiveObjMolSeleOp, so this is the average coordinate or center of mass */
14071     if(op.i1) {
14072       tc[0] /= op.i1;
14073       tc[1] /= op.i1;
14074       tc[2] /= op.i1;
14075     }
14076 		/* Transformation: RealToFrac (3x3) * tc (3x1) = (3x1) */
14077     transform33f3f(obj->Symmetry->Crystal.RealToFrac, tc, tc);
14078 
14079 		/* 2.  Copy the coordinates for the atoms in this selection into op */
14080     op.code = OMOP_VERT;
14081     op.nvv1 = 0;
14082     op.vv1 = VLAlloc(float, 10000);
14083     ExecutiveObjMolSeleOp(G, sele, &op);
14084 
14085 		/* op.nvv1 is the number of atom coordinates we copied in the previous step */
14086     if(!op.nvv1) {
14087       ErrMessage(G, __func__, "No atoms indicated!");
14088     } else {
14089       int nsymmat = obj->Symmetry->getNSymMat();
14090       map = MapNew(G, -cutoff, op.vv1, op.nvv1, NULL);
14091       if(map) {
14092         MapSetupExpress(map);
14093         /* go out no more than one lattice step in each direction: -1, 0, +1 */
14094         for(x = -1; x < 2; x++)
14095           for(y = -1; y < 2; y++)
14096             for(z = -1; z < 2; z++)
14097               for(a = 0; a < nsymmat; a++) {
14098 								/* make a copy of the original */
14099                 new_obj = ObjectMoleculeCopy(obj);
14100 
14101                 keepFlag = false;
14102                 for(b = 0; b < new_obj->NCSet; b++)
14103                   if(new_obj->CSet[b]) {
14104 										/* get the new and original coordinate sets for this state */
14105                     cs = new_obj->CSet[b];
14106                     os = obj->CSet[b];
14107 										/* convert coordinates into fractional, based on unit cell */
14108                     CoordSetRealToFrac(cs, &obj->Symmetry->Crystal);
14109                     CoordSetTransform44f(cs, obj->Symmetry->getSymMat(a));
14110                     CoordSetGetAverage(cs, ts);
14111                     identity44f(m);
14112                     /* compute the effective translation resulting
14113                        from application of the symmetry operator so
14114                        that we can shift it into the cell of the
14115                        target selection */
14116                     for(c = 0; c < 3; c++) {    /* manual rounding - rint broken */
14117 											/* here, we subtract the center of mass of the selection from tc */
14118                       ts[c] = tc[c] - ts[c];
14119                       if(ts[c] < 0)
14120                         ts[c] -= 0.5;
14121                       else
14122                         ts[c] += 0.5;
14123                       tt[c] = (int) ts[c];
14124                     }
14125 										/* translate the coordinate set by adding Tx, Ty, Tz from the symmetry matrix */
14126 										/* Now we're at the center of mass and can make an non-affine rotation */
14127                     m[3] = (float) tt[0] + x;
14128                     m[7] = (float) tt[1] + y;
14129                     m[11] = (float) tt[2] + z;
14130 										/* cs = m x cs, for rotation matrix, m and coordinate set cs */
14131                     CoordSetTransform44f(cs, m);
14132 
14133 										/* this steps through the coordinate list until it finds a vertex close enough */
14134                     CoordSetFracToReal(cs, &obj->Symmetry->Crystal);
14135                     if(!keepFlag) {
14136 											/* for each coordinate in this coordinate set */
14137                       v2 = cs->Coord;
14138                       n = cs->NIndex;
14139                       while(n--) {
14140                         MapLocus(map, v2, &h, &k, &l);
14141                         i = *(MapEStart(map, h, k, l));
14142                         if(i) {
14143                           j = map->EList[i++];
14144                           while(j >= 0) {
14145 														/* if op.vv1+3*j (a vertex) is within cutoff angstroms of v2, keep it and break */
14146                             if(within3f(op.vv1 + 3 * j, v2, cutoff)) {
14147                               keepFlag = true;
14148                               break;
14149                             }
14150                             j = map->EList[i++];
14151                           }
14152                         }
14153                         v2 += 3;
14154                         if(keepFlag)
14155                           break;
14156                       }
14157                     }
14158                     if(keepFlag) {
14159                       /* make sure that we aren't simply duplicating the template coordinates */
14160                       keepFlag = false;
14161                       v1 = os->Coord;
14162                       v2 = cs->Coord;
14163                       n = cs->NIndex;
14164                       while(n--) {
14165                         if(diffsq3f(v1, v2) > R_SMALL8) {
14166                           keepFlag = true;
14167                           break;
14168                         }
14169                         v1++;
14170                         v2++;
14171                       }
14172                     }
14173                   }
14174                 if(keepFlag) {  /* we need to create new object */
14175 
14176                   /* TODO: should also transform the U tensor at this point... */
14177 
14178 									/* make and manage the new object; update the scene for the new object */
14179 
14180                   PRINTFB(G, FB_Executive, FB_Blather)
14181                     "new_name before: %s\n", new_name ENDFB(G);
14182                   sprintf(new_name, "%s%02d%02d%02d%02d", name, (int)a, x, y, z);
14183                   PRINTFB(G, FB_Executive, FB_Blather)
14184                     "Making new object: %s from name=%s, a=%d, x=%d, y=%d, z=%d\n",
14185                     new_name, name, (int)a, x, y, z ENDFB(G);
14186 
14187                   ObjectSetName((CObject *) new_obj, new_name);
14188                   ExecutiveDelete(G, new_obj->Name);
14189                   ExecutiveManageObject(G, (CObject *) new_obj, -1, quiet);
14190                   SceneChanged(G);
14191 
14192                   if(segi == 1) {
14193                     SegIdent seg;
14194 										/* a == index of this symmetryMatrix */
14195                     if(a > 61) {
14196                       // beyond what can be encoded with a single alphanumeric
14197                       // character (PYMOL-2475)
14198                       seg[0] = '_';
14199                     } else
14200                     if(a > 35) {
14201                       seg[0] = 'a' + (a - 36);
14202                     } else if(a > 25) {
14203                       seg[0] = '0' + (a - 26);
14204                     } else {
14205                       seg[0] = 'A' + a;
14206                     }
14207                     if(x > 0) {
14208                       seg[1] = 'A' + x - 1;
14209                     } else if(x < 0) {
14210                       seg[1] = 'Z' + x + 1;
14211                     } else {
14212                       seg[1] = '0';
14213                     }
14214                     if(y > 0) {
14215                       seg[2] = 'A' + y - 1;
14216                     } else if(y < 0) {
14217                       seg[2] = 'Z' + y + 1;
14218                     } else {
14219                       seg[2] = '0';
14220                     }
14221                     if(z > 0) {
14222                       seg[3] = 'A' + z - 1;
14223                     } else if(z < 0) {
14224                       seg[3] = 'Z' + z + 1;
14225                     } else {
14226                       seg[3] = '0';
14227                     }
14228                     seg[4] = 0;
14229                     {
14230                       lexidx_t segi = LexIdx(G, seg);
14231                       int a;
14232                       AtomInfoType *ai = new_obj->AtomInfo.data();
14233                       for(a = 0; a < new_obj->NAtom; a++) {
14234                         LexAssign(G, ai->segi, segi);
14235                         ai++;
14236                       }
14237 
14238                       LexDec(G, segi);
14239                     }
14240                   }
14241                 } else {
14242                   DeleteP(new_obj);
14243                 }
14244               }
14245         MapFree(map);
14246       }
14247     }
14248     VLAFreeP(op.vv1);
14249   }
14250   PRINTFD(G, FB_Executive)
14251     " ExecutiveSymExp: leaving...\n" ENDFD;
14252   SettingSetGlobal_i(G, cSetting_auto_zoom, auto_save);
14253 }
14254 
ExecutivePurgeSpec(PyMOLGlobals * G,SpecRec * rec)14255 static void ExecutivePurgeSpec(PyMOLGlobals * G, SpecRec * rec)
14256 {
14257   CExecutive *I = G->Executive;
14258 
14259     CGOFree(rec->gridSlotSelIndicatorsCGO);
14260 
14261   ExecutiveInvalidateGroups(G, false);
14262   ExecutiveInvalidatePanelList(G);
14263   switch (rec->type) {
14264   case cExecObject:
14265     if(I->LastEdited == rec->obj)
14266       I->LastEdited = NULL;
14267     if(rec->obj->type == cObjectMolecule)
14268       if(EditorIsAnActiveObject(G, (ObjectMolecule *) rec->obj))
14269         EditorInactivate(G);
14270     SeqChanged(G);
14271     if(rec->visible) {
14272       SceneObjectDel(G, rec->obj, false);
14273       ExecutiveInvalidateSceneMembers(G);
14274     }
14275     ExecutiveDelKey(I, rec);
14276     SelectorDelete(G, rec->name);
14277     DeleteP(rec->obj);
14278     TrackerDelCand(I->Tracker, rec->cand_id);
14279     break;
14280   case cExecSelection:
14281     if(rec->visible) {
14282       SceneInvalidate(G);
14283       SeqDirty(G);
14284     }
14285     ExecutiveDelKey(I, rec);
14286     SelectorDelete(G, rec->name);
14287     TrackerDelCand(I->Tracker, rec->cand_id);
14288     break;
14289   }
14290 }
14291 
14292 
14293 /*========================================================================*/
ExecutiveDelete(PyMOLGlobals * G,const char * name)14294 void ExecutiveDelete(PyMOLGlobals * G, const char *name)
14295 {
14296   CExecutive *I = G->Executive;
14297   SpecRec *rec = NULL;
14298   CTracker *I_Tracker = I->Tracker;
14299   int list_id = ExecutiveGetNamesListFromPattern(G, name, false, false);
14300   int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
14301   while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
14302     if(rec) {
14303       switch (rec->type) {
14304       case cExecSelection:
14305         ExecutivePurgeSpec(G, rec);
14306         ListDelete(I->Spec, rec, next, SpecRec);        /* NOTE: order N in list length! TO FIX */
14307         break;
14308       case cExecObject:
14309         if(rec->obj->type == cObjectGroup) {
14310           /* remove members of the group */
14311           ExecutiveGroup(G, rec->name, "", cExecutiveGroupPurge, true);
14312         }
14313         ExecutivePurgeSpec(G, rec);
14314         ListDelete(I->Spec, rec, next, SpecRec);        /* NOTE: order N in list length! TO FIX */
14315         break;
14316       case cExecAll:
14317         rec = NULL;
14318         while(ListIterate(I->Spec, rec, next)) {
14319           switch (rec->type) {
14320           case cExecAll:
14321             break;
14322           default:
14323             ExecutivePurgeSpec(G, rec);
14324             ListDelete(I->Spec, rec, next, SpecRec);
14325             rec = NULL;
14326             break;
14327           }
14328         }
14329         SelectorDefragment(G);
14330         break;
14331       }
14332     }
14333   }
14334   TrackerDelList(I_Tracker, list_id);
14335   TrackerDelIter(I_Tracker, iter_id);
14336 
14337   // fix for PYMOL-757 - Note: This should probably go somewhere else, but we
14338   // couldn't figure out the correct place so far
14339   ExecutiveUpdateGroups(G, false);
14340 }
14341 
14342 
14343 /*========================================================================*/
ExecutiveDump(PyMOLGlobals * G,const char * fname,const char * obj,int state,int quiet)14344 void ExecutiveDump(PyMOLGlobals * G, const char *fname, const char *obj, int state, int quiet)
14345 {
14346   SpecRec *rec = NULL;
14347   CExecutive *I = G->Executive;
14348 
14349   SceneUpdate(G, false);
14350 
14351   while(ListIterate(I->Spec, rec, next)) {
14352     if(rec->type == cExecObject) {
14353       if(strcmp(rec->obj->Name, obj) == 0)
14354         break;
14355     }
14356   }
14357   if(rec) {
14358     if(rec->obj->type == cObjectMesh) {
14359       ObjectMeshDump((ObjectMesh *) rec->obj, fname, state, quiet);
14360     } else if(rec->obj->type == cObjectSurface) {
14361       ObjectSurfaceDump((ObjectSurface *) rec->obj, fname, state, quiet);
14362     } else if (rec->obj->type == cObjectMap) {
14363       ObjectMapDump((ObjectMap *) rec->obj, fname, state, quiet);
14364     } else {
14365       ErrMessage(G, __func__, "Invalid object type for this operation.");
14366     }
14367   } else {
14368     ErrMessage(G, __func__, "Object not found.");
14369   }
14370 }
14371 
14372 
14373 /*========================================================================*/
ExecutiveMemoryDump(PyMOLGlobals * G)14374 void ExecutiveMemoryDump(PyMOLGlobals * G)
14375 {
14376   CExecutive *I = G->Executive;
14377   fprintf(stderr, " Executive: %d candidate(s) %d list(s) %d link(s).\n",
14378           TrackerGetNCand(I->Tracker),
14379           TrackerGetNList(I->Tracker), TrackerGetNLink(I->Tracker));
14380 }
14381 
14382 /**
14383  * The `zoom` argument takes the following values:
14384  *
14385  *   -1 = use `auto_zoom` setting
14386  *    0 = do nothing
14387  *    1 = zoom `obj` if `is_new` is ture, otherwise do nothing
14388  *    2 = zoom `obj`
14389  *    3 = zoom current state of `obj`
14390  *    4 = zoom all
14391  *    5 = zoom `obj` if it is the only object
14392  *
14393  */
ExecutiveDoZoom(PyMOLGlobals * G,CObject * obj,int is_new,int zoom,int quiet)14394 void ExecutiveDoZoom(PyMOLGlobals * G, CObject * obj, int is_new, int zoom, int quiet)
14395 {
14396   if (zoom) {
14397     if(zoom < 0) {
14398       zoom = SettingGetGlobal_i(G, cSetting_auto_zoom);
14399       if(zoom < 0) {
14400         zoom = 1;
14401       }
14402     }
14403     switch (zoom) {
14404     case 1:                    /* zoom when new */
14405       if(is_new)
14406         ExecutiveWindowZoom(G, obj->Name, 0.0, -1, 0, 0, quiet);
14407       /* (all states) */
14408       break;
14409     case 2:                    /* zoom always */
14410       ExecutiveWindowZoom(G, obj->Name, 0.0, -1, 0, 0, quiet);
14411       /* (all states) */
14412       break;
14413     case 3:                    /* always zoom current state */
14414       ExecutiveWindowZoom(G, obj->Name, 0.0, ObjectGetCurrentState(obj, false), 0, 0, quiet);
14415       break;
14416     case 4:                    /* zoom all objects */
14417       ExecutiveWindowZoom(G, cKeywordAll, 0.0, -1, 0, 0, quiet);
14418       break;
14419     case 5:                    /* zoom first object only */
14420       if(count_objects(G, true) == 1)
14421         ExecutiveWindowZoom(G, obj->Name, 0.0, -1, 0, 0, quiet);        /* (all states) */
14422       break;
14423     }
14424   }
14425 }
14426 
ExecutiveDoAutoGroup(PyMOLGlobals * G,SpecRec * rec)14427 static void ExecutiveDoAutoGroup(PyMOLGlobals * G, SpecRec * rec)
14428 {
14429   CExecutive *I = G->Executive;
14430   int auto_mode = SettingGetGlobal_i(G, cSetting_group_auto_mode);
14431   if(auto_mode && (rec->name[0] != '_')) {
14432     auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
14433     char *period = rec->name + strlen(rec->name);
14434     SpecRec *found_group = NULL;
14435     WordType seek_group_name;
14436     UtilNCopy(seek_group_name, rec->name, sizeof(WordType));
14437 
14438     while((period > rec->name) && (!found_group)) {
14439       period--;
14440       if(*period == '.') {
14441         seek_group_name[period - rec->name] = 0;
14442         {
14443           SpecRec *group_rec = NULL;
14444           while(ListIterate(I->Spec, group_rec, next)) {
14445             if((group_rec->type == cExecObject) && (group_rec->obj->type == cObjectGroup)) {
14446               if(WordMatchExact(G, group_rec->name, seek_group_name, ignore_case)) {
14447                 found_group = group_rec;
14448                 strcpy(rec->group_name, seek_group_name);
14449                 break;
14450               }
14451             }
14452           }
14453         }
14454 
14455         if((!found_group) && (auto_mode == 2)) {
14456           auto obj = new ObjectGroup(G);
14457           if(obj) {
14458             ObjectSetName(obj, seek_group_name);
14459             strcpy(rec->group_name, obj->Name);
14460             ExecutiveManageObject(G, obj, false, true);
14461             ExecutiveInvalidateGroups(G, false);
14462             break;
14463           }
14464         }
14465       }
14466     }
14467     if(found_group)
14468       ExecutiveInvalidateGroups(G, false);
14469   }
14470 }
14471 
14472 
14473 /*========================================================================*/
14474 /**
14475  * Manages an object. Adds it to the list of spec records and related trackers,
14476  * and to the scene for rendering.
14477  *
14478  * Also handles:
14479  * - auto_dss
14480  * - group_auto_mode
14481  * - auto_defer_builds
14482  *
14483  * If the object is already managed, then only do `auto_dss` and
14484  * `auto_defer_builds`.
14485  *
14486  * If an object with the same name exists, then delete it and re-use the
14487  * existing spec rec to manage the new object.
14488  *
14489  * @param obj Object to manage. Executive takes ownership.
14490  * @param zoom Zoom the camera, see ExecutiveDoZoom for valid values.
14491  */
ExecutiveManageObject(PyMOLGlobals * G,CObject * obj,int zoom,int quiet)14492 void ExecutiveManageObject(PyMOLGlobals * G, CObject * obj, int zoom, int quiet)
14493 {
14494   SpecRec *rec = NULL;
14495   CExecutive *I = G->Executive;
14496   int exists = false;
14497   int previousVisible;
14498   int previousObjType = 0;
14499 
14500   if(SettingGetGlobal_b(G, cSetting_auto_hide_selections))
14501     ExecutiveHideSelections(G);
14502   while(ListIterate(I->Spec, rec, next)) {
14503     if(rec->obj == obj) {
14504       exists = true;
14505     }
14506   }
14507   if(!exists) {
14508     if (WordMatchExact(G, cKeywordAll, obj->Name, true)) {
14509       PRINTFB(G, FB_Executive, FB_Warnings)
14510         " Executive: object name \"%s\" is illegal -- renamed to 'all_'.\n", obj->Name
14511         ENDFB(G);
14512       strcat(obj->Name, "_");   /* don't allow object named "all" */
14513     } else if (SelectorNameIsKeyword(G, obj->Name)) {
14514       PRINTFB(G, FB_Executive, FB_Warnings)
14515         " Executive-Warning: name \"%s\" collides with a selection language keyword.\n",
14516         obj->Name ENDFB(G);
14517     }
14518 
14519     while(ListIterate(I->Spec, rec, next)) {
14520       if(rec->type == cExecObject) {
14521         if(strcmp(rec->obj->Name, obj->Name) == 0)
14522           break;
14523       }
14524     }
14525     if(rec) {                   /* another object of this type already exists */
14526       /* purge it */
14527       SceneObjectDel(G, rec->obj, false);
14528       ExecutiveInvalidateSceneMembers(G);
14529       previousObjType = rec->obj->type;
14530       DeleteP(rec->obj);
14531     } else {
14532       if(!quiet)
14533         if(obj->Name[0] != '_') {       /* suppress internal objects */
14534           PRINTFB(G, FB_Executive, FB_Actions)
14535             " Executive: object \"%s\" created.\n", obj->Name ENDFB(G);
14536         }
14537     }
14538     if(!rec)
14539       ListElemCalloc(G, rec, SpecRec);
14540 
14541     strcpy(rec->name, obj->Name);
14542     rec->type = cExecObject;
14543     rec->obj = obj;
14544     previousVisible = rec->visible;
14545     if (previousObjType == rec->obj->type) {
14546       // skip
14547     } else if (rec->obj->type == cObjectMap) {
14548       rec->visible = 0;
14549     } else {
14550       rec->visible = 1;
14551       /*      SceneObjectAdd(G,obj); */
14552     }
14553     if (previousVisible!=rec->visible){
14554       ReportEnabledChange(G, rec);
14555     }
14556 
14557     // Is this a new rec?
14558     if (!rec->cand_id) {
14559       rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef *) (void *) rec);
14560 
14561       TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
14562       TrackerLink(I->Tracker, rec->cand_id, I->all_obj_list_id, 1);
14563       ListAppend(I->Spec, rec, next, SpecRec);
14564       ExecutiveAddKey(I, rec);
14565       ExecutiveInvalidatePanelList(G);
14566 
14567       ExecutiveDoAutoGroup(G, rec);
14568     }
14569 
14570     if(rec->visible) {
14571       rec->in_scene = SceneObjectAdd(G, obj);
14572       ExecutiveInvalidateSceneMembers(G);
14573     }
14574   }
14575 
14576   ExecutiveUpdateObjectSelection(G, obj);
14577 
14578   if(SettingGetGlobal_b(G, cSetting_auto_dss)) {
14579     if(obj->type == cObjectMolecule) {
14580       ObjectMolecule *objMol = (ObjectMolecule *) obj;
14581       if(objMol->NCSet == 1) {
14582         ExecutiveAssignSS(G, obj->Name, 0, NULL, true, objMol, true);
14583       }
14584     }
14585   }
14586 
14587   {
14588     int n_state = obj->getNFrame();
14589     int defer_limit = SettingGetGlobal_i(G, cSetting_auto_defer_builds);
14590     if((defer_limit >= 0) && (n_state >= defer_limit)) {
14591       int defer_builds = SettingGetGlobal_b(G, cSetting_defer_builds_mode);
14592       if(!defer_builds)
14593         SettingSetGlobal_b(G, cSetting_defer_builds_mode, 1);
14594     }
14595   }
14596 
14597   ExecutiveDoZoom(G, obj, !exists, zoom, true);
14598 
14599   SeqChanged(G);
14600   OrthoInvalidateDoDraw(G);
14601 }
14602 
14603 
14604 /*========================================================================*/
ExecutiveManageSelection(PyMOLGlobals * G,const char * name)14605 void ExecutiveManageSelection(PyMOLGlobals * G, const char *name)
14606 {
14607 
14608   SpecRec *rec = NULL;
14609   CExecutive *I = G->Executive;
14610   int hide_all = SettingGetGlobal_b(G, cSetting_active_selections);
14611   if(name[0] == '_')
14612     hide_all = false;           /* hidden selections don't change active selection */
14613   while(ListIterate(I->Spec, rec, next)) {
14614     if(rec->type == cExecSelection) {
14615       if(strcmp(rec->name, name) == 0)
14616         break;
14617       if(hide_all && rec->visible){
14618         rec->visible = false;
14619 	ReportEnabledChange(G, rec);
14620       }
14621     }
14622   }
14623   if(rec && hide_all)
14624     while(ListIterate(I->Spec, rec, next))
14625       if(rec->type == cExecSelection && rec->visible){
14626         rec->visible = false;
14627 	ReportEnabledChange(G, rec);
14628       }
14629 
14630   if(!rec) {
14631     ListElemCalloc(G, rec, SpecRec);
14632     strcpy(rec->name, name);
14633     rec->type = cExecSelection;
14634     rec->next = NULL;
14635     rec->sele_color = -1;
14636     if (rec->visible){
14637       rec->visible = false;
14638       ReportEnabledChange(G, rec);
14639     }
14640     rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef *) (void *) rec);
14641     TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
14642     TrackerLink(I->Tracker, rec->cand_id, I->all_sel_list_id, 1);
14643     ListAppend(I->Spec, rec, next, SpecRec);
14644     ExecutiveAddKey(I, rec);
14645     ExecutiveInvalidatePanelList(G);
14646   }
14647   if(rec) {
14648     if(name[0] != '_') {
14649       if(SettingGetGlobal_b(G, cSetting_auto_hide_selections))
14650         ExecutiveHideSelections(G);
14651       if(SettingGetGlobal_b(G, cSetting_auto_show_selections) && !rec->visible) {
14652         rec->visible = true;
14653 	ReportEnabledChange(G, rec);
14654       }
14655     }
14656     if(rec->visible)
14657       SceneInvalidate(G);
14658     ExecutiveDoAutoGroup(G, rec);
14659   }
14660   SeqDirty(G);
14661 }
14662 
14663 
14664 /*========================================================================*/
click(int button,int x,int y,int mod)14665 int CExecutive::click(int button, int x, int y, int mod)
14666 {
14667   PyMOLGlobals *G = m_G;
14668   CExecutive *I = G->Executive;
14669   int n, a;
14670   SpecRec *rec = NULL;
14671   PanelRec *panel = NULL;
14672   int t, xx;
14673   int pass = false;
14674   int skip;
14675   int ExecLineHeight = DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_control_size));
14676   int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
14677   int op_cnt = get_op_cnt(G);
14678 
14679   if(y < I->HowFarDown) {
14680     if(SettingGetGlobal_b(G, cSetting_internal_gui_mode) == 1)
14681       return SceneGetBlock(G)->click(button, x, y, mod);
14682   }
14683 
14684   switch(button) {
14685     case P_GLUT_BUTTON_SCROLL_FORWARD:
14686       I->m_ScrollBar.moveBy(-1);
14687       return 1;
14688     case P_GLUT_BUTTON_SCROLL_BACKWARD:
14689       I->m_ScrollBar.moveBy(1);
14690       return 1;
14691   }
14692 
14693   n = ((I->rect.top - y) - (ExecTopMargin + ExecClickMargin)) / ExecLineHeight;
14694   a = n;
14695   xx = (x - rect.left);
14696   if(I->ScrollBarActive) {
14697     if((x - rect.left) <
14698        (ExecScrollBarWidth + ExecScrollBarMargin + ExecToggleMargin)) {
14699       pass = 1;
14700       I->m_ScrollBar.click(button, x, y, mod);
14701     }
14702     xx -= (ExecScrollBarWidth + ExecScrollBarMargin);
14703   }
14704   skip = I->NSkip;
14705   if(!pass) {
14706     I->RecoverPressed = NULL;
14707     /* while(ListIterate(I->Spec,rec,next)) { */
14708     while(ListIterate(I->Panel, panel, next)) {
14709       rec = panel->spec;
14710 
14711       if((rec->name[0] != '_') || (!hide_underscore)) {
14712         if(skip) {
14713           skip--;
14714         } else {
14715           if(!a) {
14716             t = ((rect.right - ExecRightMargin) - x - 1) / ExecToggleWidth;
14717             if(t < op_cnt) {
14718               int my = rect.top - (ExecTopMargin + n * ExecLineHeight) - 3;
14719               int mx = rect.right - (ExecRightMargin + t * ExecToggleWidth);
14720 
14721 #if 0
14722               // prefix name with % as guarantee to not match a selection keyword
14723               WordType namesele = "%";
14724               UtilNCopy(namesele + 1, (rec->type == cExecObject) ?
14725                   rec->obj->Name : rec->name, WordLength - 1);
14726 #else
14727               char *namesele = (rec->type == cExecObject) ? rec->obj->Name : rec->name;
14728 #endif
14729 
14730               t = (op_cnt - t) - 1;
14731               switch (t) {
14732               case 0:          /* action */
14733                 switch (rec->type) {
14734                 case cExecAll:
14735                   MenuActivate(G, mx, my, x, y, false, "all_action", namesele);
14736                   break;
14737                 case cExecSelection:
14738                   MenuActivate(G, mx, my, x, y, false, "sele_action", namesele);
14739                   break;
14740                 case cExecObject:
14741                   switch (rec->obj->type) {
14742                   case cObjectGroup:
14743                     MenuActivate(G, mx, my, x, y, false, "group_action", namesele);
14744                     break;
14745                   case cObjectMolecule:
14746                     MenuActivate(G, mx, my, x, y, false, "mol_action", namesele);
14747                     break;
14748                   case cObjectMap:
14749                     MenuActivate(G, mx, my, x, y, false, "map_action", namesele);
14750                     break;
14751                   case cObjectSurface:
14752                     MenuActivate(G, mx, my, x, y, false, "surface_action",
14753                                  namesele);
14754                     break;
14755                   case cObjectMesh:
14756                     MenuActivate(G, mx, my, x, y, false, "mesh_action", namesele);
14757                     break;
14758                   case cObjectMeasurement:
14759                   case cObjectCGO:
14760                   case cObjectCallback:
14761                   case cObjectAlignment:
14762 		  case cObjectVolume:
14763                     MenuActivate(G, mx, my, x, y, false, "simple_action", namesele);
14764                     break;
14765                   case cObjectSlice:
14766                     MenuActivate(G, mx, my, x, y, false, "slice_action", namesele);
14767                     break;
14768                   case cObjectGadget:
14769                     MenuActivate(G, mx, my, x, y, false, "ramp_action", namesele);
14770                     break;
14771                   }
14772                   break;
14773                 }
14774                 break;
14775               case 1:
14776                 switch (rec->type) {
14777                 case cExecAll:
14778                   MenuActivate(G, mx, my, x, y, false, "mol_show", cKeywordAll);
14779                   break;
14780                 case cExecSelection:
14781                   MenuActivate(G, mx, my, x, y, false, "mol_show", namesele);
14782                   break;
14783                 case cExecObject:
14784                   switch (rec->obj->type) {
14785                   case cObjectGroup:
14786                   case cObjectMolecule:
14787                     MenuActivate(G, mx, my, x, y, false, "mol_show", namesele);
14788                     break;
14789                   case cObjectCGO:
14790                   case cObjectAlignment:
14791                     MenuActivate(G, mx, my, x, y, false, "cgo_show", namesele);
14792                     break;
14793                   case cObjectMeasurement:
14794                     MenuActivate(G, mx, my, x, y, false, "measurement_show",
14795                                  namesele);
14796                     break;
14797                   case cObjectMap:
14798                     MenuActivate(G, mx, my, x, y, false, "map_show", namesele);
14799                     break;
14800                   case cObjectMesh:
14801                     MenuActivate(G, mx, my, x, y, false, "mesh_show", namesele);
14802                     break;
14803                   case cObjectSurface:
14804                     MenuActivate(G, mx, my, x, y, false, "surface_show", namesele);
14805                     break;
14806                   case cObjectSlice:
14807                     MenuActivate(G, mx, my, x, y, false, "slice_show", namesele);
14808                     break;
14809 		  case cObjectVolume:
14810                     MenuActivate(G, mx, my, x, y, false, "volume_show", namesele);
14811                     break;
14812 
14813                   }
14814                   break;
14815                 }
14816                 break;
14817               case 2:
14818                 switch (rec->type) {
14819                 case cExecAll:
14820                   MenuActivate(G, mx, my, x, y, false, "mol_hide", cKeywordAll);
14821                   break;
14822                 case cExecSelection:
14823                   MenuActivate(G, mx, my, x, y, false, "mol_hide", namesele);
14824                   break;
14825                 case cExecObject:
14826                   switch (rec->obj->type) {
14827                   case cObjectGroup:
14828                   case cObjectMolecule:
14829                     MenuActivate(G, mx, my, x, y, false, "mol_hide", namesele);
14830                     break;
14831                   case cObjectCGO:
14832                   case cObjectAlignment:
14833                     MenuActivate(G, mx, my, x, y, false, "cgo_hide", namesele);
14834                     break;
14835                   case cObjectMeasurement:
14836                     MenuActivate(G, mx, my, x, y, false, "measurement_hide",
14837                                  namesele);
14838                     break;
14839                   case cObjectMap:
14840                     MenuActivate(G, mx, my, x, y, false, "map_hide", namesele);
14841                     break;
14842                   case cObjectMesh:
14843                     MenuActivate(G, mx, my, x, y, false, "mesh_hide", namesele);
14844                     break;
14845                   case cObjectSurface:
14846                     MenuActivate(G, mx, my, x, y, false, "surface_hide", namesele);
14847                     break;
14848                   case cObjectSlice:
14849                     MenuActivate(G, mx, my, x, y, false, "slice_hide", namesele);
14850                     break;
14851 		  case cObjectVolume:
14852                     MenuActivate(G, mx, my, x, y, false, "volume_hide", namesele);
14853                     break;
14854 
14855 
14856                   }
14857                   break;
14858                 }
14859                 break;
14860               case 3:
14861                 switch (rec->type) {
14862                 case cExecAll:
14863                   MenuActivate(G, mx, my, x, y, false, "mol_labels", "(all)");
14864                   break;
14865                 case cExecSelection:
14866                   MenuActivate(G, mx, my, x, y, false, "mol_labels", namesele);
14867                   break;
14868                 case cExecObject:
14869                   switch (rec->obj->type) {
14870                   case cObjectGroup:
14871                   case cObjectMolecule:
14872                     MenuActivate(G, mx, my, x, y, false, "mol_labels", namesele);
14873                     break;
14874                   case cObjectMeasurement:
14875                     break;
14876                   case cObjectMap:
14877                   case cObjectSurface:
14878                   case cObjectMesh:
14879                   case cObjectSlice:
14880                     break;
14881                   }
14882                   break;
14883                 }
14884                 break;
14885               case 4:
14886                 switch (rec->type) {
14887                 case cExecAll:
14888                 case cExecSelection:
14889                   MenuActivate(G, mx, my, x, y, false, "mol_color", namesele);
14890                   break;
14891                 case cExecObject:
14892                   switch (rec->obj->type) {
14893                   case cObjectGroup:
14894                   case cObjectMolecule:
14895                     MenuActivate(G, mx, my, x, y, false, "mol_color", namesele);
14896                     break;
14897                   case cObjectMap:
14898                   case cObjectCGO:
14899                   case cObjectAlignment:
14900                     MenuActivate(G, mx, my, x, y, false, "general_color", namesele);
14901                     break;
14902                   case cObjectMesh:
14903                     MenuActivate(G, mx, my, x, y, false, "mesh_color", namesele);
14904                     break;
14905                   case cObjectSurface:
14906                     MenuActivate2Arg(G, mx, my, x, y, false, "mesh_color", namesele, "surface");
14907                     break;
14908                   case cObjectMeasurement:
14909                     MenuActivate(G, mx, my, x, y, false, "measurement_color", namesele);
14910                     break;
14911                   case cObjectSlice:
14912                     MenuActivate(G, mx, my, x, y, false, "slice_color", namesele);
14913                     break;
14914                   case cObjectVolume:
14915                     MenuActivate(G, mx, my, x, y, false, "vol_color", namesele);
14916                     break;
14917                   case cObjectGadget:
14918                     MenuActivate(G, mx, my, x, y, false, "ramp_color", namesele);
14919                     break;
14920                   }
14921                   break;
14922                 }
14923                 break;
14924               case 5:
14925                 switch (rec->type) {
14926                 case cExecAll:
14927                   MenuActivate0Arg(G, mx, my, x, y, false, "camera_motion");
14928                   break;
14929                 case cExecSelection: /* not relevant at present */
14930 		  break;
14931                 case cExecObject: /* this kinds of objects can be animated */
14932                   switch (rec->obj->type) {
14933                   case cObjectGroup: /* however, this just groups motions for a set of objects */
14934                   case cObjectMolecule:
14935 		  case cObjectMeasurement:
14936 		  case cObjectMap:
14937 		  case cObjectSurface:
14938 		  case cObjectCGO:
14939 		  case cObjectMesh:
14940                     MenuActivate(G, mx, my, x, y, false, "obj_motion", namesele);
14941                     break;
14942                   }
14943                   break;
14944                 }
14945                 break;
14946               }
14947             } else {            /* clicked in variable area */
14948 
14949               if(((panel->is_group) && (((xx) - 1) / DIP2PIXEL(8)) > (panel->nest_level + 1)) ||
14950                  ((!panel->is_group) && (((xx) - 1) / DIP2PIXEL(8)) > panel->nest_level)) {
14951                 /* clicked on name */
14952 
14953                 rec->hilight = 1;
14954                 switch (button) {
14955                 case P_GLUT_LEFT_BUTTON:
14956                   I->Pressed = n;
14957                   I->OldVisibility = rec->visible;
14958                   I->Over = n;
14959                   I->DragMode = 1;
14960                   I->ToggleMode = 0;
14961                   I->LastChanged = NULL;
14962                   I->LastZoomed = NULL;
14963                   if(mod == (cOrthoSHIFT | cOrthoCTRL)) {
14964                     I->ToggleMode = 2;
14965                     if(!rec->visible) {
14966                       ExecutiveSpecSetVisibility(G, rec, !I->OldVisibility, mod, false);
14967                     }
14968                     if(rec != I->LastZoomed)
14969                       ExecutiveWindowZoom(G, rec->name, 0.0F, -1, false, -1.0F, true);
14970                     I->LastZoomed = rec;
14971                     I->LastChanged = rec;
14972                   } else if(mod & cOrthoSHIFT) {
14973                     ExecutiveSpecSetVisibility(G, rec, !I->OldVisibility, mod, false);
14974                     I->ToggleMode = 1;
14975                   } else if(mod & cOrthoCTRL) {
14976                     I->ToggleMode = 2;
14977                     if(!rec->visible) {
14978                       ExecutiveSpecSetVisibility(G, rec, !I->OldVisibility, mod, false);
14979                     }
14980                     I->LastChanged = rec;
14981                   }
14982                   I->PressedWhat = 1;
14983                   I->OverWhat = 1;
14984                   break;
14985                 case P_GLUT_MIDDLE_BUTTON:
14986                   I->Pressed = n;
14987                   I->OldVisibility = rec->visible;
14988                   I->Over = n;
14989                   I->LastOver = I->Over;
14990                   I->DragMode = 3;
14991                   I->ToggleMode = 0;
14992                   I->LastChanged = rec;
14993                   I->LastZoomed = NULL;
14994                   if(mod & cOrthoCTRL) {
14995                     I->ToggleMode = 5;
14996                     ExecutiveWindowZoom(G, rec->name, 0.0F, -1, false, -1.0F, true);
14997                     I->LastZoomed = rec;
14998                     if(mod & cOrthoSHIFT) {     /* exclusive */
14999                       I->ToggleMode = 6;
15000                       ExecutiveSetObjVisib(G, cKeywordAll, false, false);
15001                       /* need to log this */
15002                       if(!rec->visible)
15003                         ExecutiveSpecSetVisibility(G, rec, true, 0, true);
15004                     }
15005                   } else {
15006                     I->ToggleMode = 4;
15007                     ExecutiveCenter(G, rec->name, -1, true, -1.0F, NULL, true);
15008                   }
15009                   if(!rec->visible) {
15010                     ExecutiveSpecSetVisibility(G, rec, !rec->visible, mod, false);
15011                     I->LastChanged = rec;
15012                   }
15013                   I->PressedWhat = 1;
15014                   I->OverWhat = 1;
15015                   break;
15016                 case P_GLUT_RIGHT_BUTTON:
15017                   I->DragMode = 2;      /* reorder */
15018                   I->Pressed = n;
15019                   I->Over = n;
15020                   I->LastOver = I->Over;
15021                   I->PressedWhat = 1;
15022                   I->OverWhat = 1;
15023                   break;
15024                 }
15025                 OrthoGrab(G, this);
15026                 OrthoDirty(G);
15027               } else if(panel->is_group) {
15028                 /* clicked on group control */
15029                 rec->hilight = 2;
15030                 I->DragMode = 1;
15031                 I->Pressed = n;
15032                 I->Over = n;
15033                 I->LastOver = I->Over;
15034                 I->PressedWhat = 2;
15035                 I->OverWhat = 2;
15036 
15037                 OrthoGrab(G, this);
15038                 OrthoDirty(G);
15039               }
15040             }
15041           }
15042           a--;
15043         }
15044       }
15045     }
15046   }
15047   PyMOL_NeedRedisplay(G->PyMOL);
15048   return (1);
15049 }
15050 
15051 
15052 /*========================================================================*/
ExecutiveSpecEnable(PyMOLGlobals * G,SpecRec * rec,int parents,int log)15053 static void ExecutiveSpecEnable(PyMOLGlobals * G, SpecRec * rec, int parents, int log)
15054 {
15055   if(log && SettingGetGlobal_b(G, cSetting_logging)) {
15056     OrthoLineType buffer = "";
15057     sprintf(buffer, "cmd.enable('%s',%d)", rec->obj->Name, parents);
15058     PLog(G, buffer, cPLog_pym);
15059   }
15060 
15061   if(!rec->visible) {
15062     rec->visible = true;
15063     ReportEnabledChange(G, rec);
15064   }
15065   if(!rec->in_scene) {
15066     rec->in_scene = SceneObjectAdd(G, rec->obj);
15067   }
15068 
15069   if(parents) {
15070     CExecutive *I = G->Executive;
15071     CTracker *I_Tracker = I->Tracker;
15072     int list_id = ExecutiveGetObjectParentList(G, rec);
15073     if(list_id) {
15074       int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
15075       SpecRec *parent_rec = NULL;
15076 
15077       while(TrackerIterNextCandInList(I_Tracker, iter_id,
15078                                       (TrackerRef **) (void *) &parent_rec)) {
15079         if(rec) {
15080           switch (parent_rec->type) {
15081           case cExecObject:
15082             if(!parent_rec->in_scene) {
15083               parent_rec->in_scene = SceneObjectAdd(G, parent_rec->obj);
15084             }
15085             if(!parent_rec->visible) {
15086               parent_rec->visible = true;
15087 	      ReportEnabledChange(G, parent_rec);
15088             }
15089           }
15090         }
15091       }
15092       TrackerDelIter(I_Tracker, iter_id);
15093     }
15094     TrackerDelList(I_Tracker, list_id);
15095   }
15096   ExecutiveInvalidateSceneMembers(G);
15097 }
15098 
ExecutiveSpecSetVisibility(PyMOLGlobals * G,SpecRec * rec,int new_vis,int mod,int parents)15099 static void ExecutiveSpecSetVisibility(PyMOLGlobals * G, SpecRec * rec,
15100                                        int new_vis, int mod, int parents)
15101 {
15102   OrthoLineType buffer = "";
15103   int logging = SettingGetGlobal_i(G, cSetting_logging);
15104   if(rec->type == cExecObject) {
15105     if(rec->visible && !new_vis) {
15106       if(logging)
15107         sprintf(buffer, "cmd.disable('%s')", rec->obj->Name);
15108       SceneObjectDel(G, rec->obj, true);
15109       ExecutiveInvalidateSceneMembers(G);
15110       if (rec->visible != new_vis){
15111 	rec->visible = new_vis;
15112 	ReportEnabledChange(G, rec);
15113       }
15114     } else if((!rec->visible) && new_vis) {
15115       ExecutiveSpecEnable(G, rec, parents, logging);
15116       /*
15117          if(logging)
15118          sprintf(buffer,"cmd.enable('%s')",rec->obj->Name);
15119          rec->in_scene = SceneObjectAdd(G,rec->obj);
15120          rec->visible=new_vis;
15121          ExecutiveInvalidateSceneMembers(G);
15122        */
15123     }
15124     SceneChanged(G);
15125     if(logging && buffer[0]) {
15126       PLog(G, buffer, cPLog_pym);
15127     }
15128   } else if(rec->type == cExecAll) {
15129     if(SettingGetGlobal_i(G, cSetting_logging)) {
15130       if(rec->visible)
15131         sprintf(buffer, "cmd.disable('all')");
15132       else
15133         sprintf(buffer, "cmd.enable('all')");
15134       PLog(G, buffer, cPLog_pym);
15135     }
15136     ExecutiveSetObjVisib(G, cKeywordAll, !rec->visible, false);
15137   } else if(rec->type == cExecSelection) {
15138     if(mod & cOrthoCTRL) {
15139       sprintf(buffer, "cmd.enable('%s')", rec->name);
15140       PLog(G, buffer, cPLog_pym);
15141       if (!rec->visible){
15142 	rec->visible = true;
15143 	ReportEnabledChange(G, rec);
15144       }
15145     } else {
15146 
15147       if(rec->visible && !new_vis) {
15148         if(SettingGetGlobal_i(G, cSetting_logging))
15149           sprintf(buffer, "cmd.disable('%s')", rec->name);
15150       } else if((!rec->visible) && new_vis) {
15151         sprintf(buffer, "cmd.enable('%s')", rec->name);
15152       }
15153       if(new_vis && SettingGetGlobal_b(G, cSetting_active_selections)) {
15154         ExecutiveHideSelections(G);
15155       }
15156       if(SettingGetGlobal_i(G, cSetting_logging)) {
15157         PLog(G, buffer, cPLog_pym);
15158       }
15159       if (rec->visible != new_vis){
15160 	rec->visible = new_vis;
15161 	ReportEnabledChange(G, rec);
15162       }
15163     }
15164     SceneChanged(G);
15165   }
15166 }
15167 
ExecutiveAssignAtomTypes(PyMOLGlobals * G,const char * s1,int format,int state,int quiet)15168 int ExecutiveAssignAtomTypes(PyMOLGlobals * G, const char *s1, int format, int state, int quiet)
15169 {
15170   int result = 0;
15171   int sele1;
15172 
15173   sele1 = SelectorIndexByName(G, s1);
15174   if(state < 0)
15175     state = 0;
15176   {
15177     int unblock = PAutoBlock(G);        /*   PBlock(G);    PBlockAndUnlockAPI(); */
15178     if(sele1 >= 0) {
15179       result = SelectorAssignAtomTypes(G, sele1, state, quiet, format);
15180     }
15181     PAutoUnblock(G, unblock);   /*    PUnblock(G);  PLockAPIAndUnblock(); */
15182   }
15183   return (result);
15184 }
15185 
release(int button,int x,int y,int mod)15186 int CExecutive::release(int button, int x, int y, int mod)
15187 {
15188   PyMOLGlobals *G = m_G;
15189   CExecutive *I = G->Executive;
15190   SpecRec *rec = NULL;
15191   PanelRec *panel = NULL;
15192   int pass = false;
15193   int skip;
15194   int xx;
15195   int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
15196   if(y < I->HowFarDown) {
15197     if(SettingGetGlobal_b(G, cSetting_internal_gui_mode) == 1)
15198       return SceneGetBlock(G)->release(button, x, y, mod);
15199   }
15200 
15201   xx = (x - rect.left);
15202   if(I->ScrollBarActive) {
15203     if((x - rect.left) <
15204        (ExecScrollBarWidth + ExecScrollBarMargin + ExecToggleMargin)) {
15205       pass = 1;
15206       I->m_ScrollBar.release(button, x, y, mod);
15207       OrthoUngrab(G);
15208     }
15209     xx -= (ExecScrollBarWidth + ExecScrollBarMargin);
15210   }
15211 
15212   skip = I->NSkip;
15213 
15214   if(!pass) {
15215     I->drag(x, y, mod);    /* incorporate final changes in cursor position */
15216     switch (I->DragMode) {
15217     case 1:
15218 
15219       /*while(ListIterate(I->Spec,rec,next)) { */
15220       while(ListIterate(I->Panel, panel, next)) {
15221         rec = panel->spec;
15222 
15223         if((rec->name[0] != '_') || (!hide_underscore)) {
15224           if(skip) {
15225             skip--;
15226           } else {
15227             if((I->PressedWhat == 1) &&
15228                (((panel->is_group) && ((xx - 1) / DIP2PIXEL(8)) > (panel->nest_level + 1)) ||
15229                 ((!panel->is_group) && ((xx - 1) / DIP2PIXEL(8)) > panel->nest_level))) {
15230               /* over name */
15231               if(rec->hilight == 1) {
15232                 if(rec->type == cExecSelection) {
15233                   ExecutiveSpecSetVisibility(G, rec, !I->OldVisibility, 0, false);
15234                 } else {
15235                   ExecutiveSpecSetVisibility(G, rec, !I->OldVisibility, mod, true);
15236                 }
15237               }
15238             } else if((I->PressedWhat == 2) && (panel->is_group)) {
15239               if(rec->hilight == 2) {
15240                 ObjectGroup *obj = (ObjectGroup *) rec->obj;
15241                 OrthoLineType buf2;
15242                 sprintf(buf2, "cmd.group(\"%s\",action='%s')\n", rec->obj->Name,
15243                         obj->OpenOrClosed ? "close" : "open");
15244                 PLog(G, buf2, cPLog_no_flush);
15245                 ExecutiveGroup(G, rec->obj->Name, "", 5, 1);
15246 
15247               }
15248               /* over group control */
15249             }
15250           }
15251         }
15252       }
15253       break;
15254     case 2:
15255       if(I->ReorderFlag) {
15256         I->ReorderFlag = false;
15257         PLog(G, I->ReorderLog, cPLog_no_flush);
15258       }
15259       break;
15260     }
15261   }
15262 
15263   {
15264     SpecRec *rec = NULL;
15265     while(ListIterate(I->Spec, rec, next)) {
15266       rec->hilight = 0;
15267     }
15268   }
15269 
15270   I->Over = -1;
15271   I->Pressed = -1;
15272   I->DragMode = 0;
15273   I->PressedWhat = 0;
15274   OrthoUngrab(G);
15275   PyMOL_NeedRedisplay(G->PyMOL);
15276   return (1);
15277 }
15278 
15279 
15280 /*========================================================================*/
drag(int x,int y,int mod)15281 int CExecutive::drag(int x, int y, int mod)
15282 {
15283   PyMOLGlobals *G = m_G;
15284   CExecutive *I = G->Executive;
15285   int xx, t;
15286   int ExecLineHeight = DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_control_size));
15287   int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
15288   int op_cnt = get_op_cnt(G);
15289 
15290   ExecutiveUpdateGroups(G, false);
15291   ExecutiveUpdatePanelList(G);
15292   if(y < I->HowFarDown) {
15293     if(SettingGetGlobal_b(G, cSetting_internal_gui_mode) == 1)
15294       return SceneGetBlock(G)->drag(x, y, mod);
15295   }
15296 
15297   if(I->DragMode) {
15298     xx = (x - rect.left);
15299     t = ((rect.right - ExecRightMargin) - x) / ExecToggleWidth;
15300     if(I->ScrollBarActive) {
15301       xx -= (ExecScrollBarWidth + ExecScrollBarMargin);
15302     }
15303 
15304     {
15305       int row_offset;
15306       if((xx >= 0) && (t >= op_cnt)) {
15307         row_offset = ((rect.top - y) -
15308                       (ExecTopMargin + ExecClickMargin)) / ExecLineHeight;
15309         I->Over = row_offset;
15310       } else {
15311         I->Over = -1;
15312         row_offset = -1;
15313         {
15314           SpecRec *rec = NULL;
15315           while(ListIterate(I->Spec, rec, next))
15316             rec->hilight = 0;
15317         }
15318       }
15319 
15320       if(I->RecoverPressed) {
15321         SpecRec *rec = NULL;
15322         PanelRec *panel = NULL;
15323         int skip = I->NSkip;
15324         int row = 0;
15325         while(ListIterate(I->Panel, panel, next)) {
15326           rec = panel->spec;
15327 
15328           if((rec->name[0] != '_') || (!hide_underscore)) {
15329             if(skip) {
15330               skip--;
15331             } else {
15332               if(rec == I->RecoverPressed) {
15333                 I->Pressed = row;
15334                 I->RecoverPressed = NULL;
15335               }
15336               row++;
15337             }
15338           }
15339         }
15340       }
15341 
15342       if(I->PressedWhat == 2) {
15343         I->OverWhat = 0;
15344       }
15345       if(I->Over >= 0) {
15346         SpecRec *rec = NULL;
15347         PanelRec *panel = NULL;
15348         int skip = I->NSkip;
15349         int row = 0;
15350         switch (I->DragMode) {
15351         case 1:
15352 
15353           /*          while(ListIterate(I->Spec,rec,next)) { */
15354           while(ListIterate(I->Panel, panel, next)) {
15355             rec = panel->spec;
15356 
15357             if((rec->name[0] != '_') || (!hide_underscore)) {
15358               if(skip) {
15359                 skip--;
15360               } else {
15361 
15362                 rec->hilight = 0;
15363                 if((I->PressedWhat == 1) &&     /* name button */
15364                    (((row >= I->Over) && (row <= I->Pressed)) ||
15365                     ((row >= I->Pressed) && (row <= I->Over)))) {
15366 
15367                   if(((panel->is_group) && ((xx - 1) / DIP2PIXEL(8)) > (panel->nest_level + 1)) ||
15368                      ((!panel->is_group) && ((xx - 1) / DIP2PIXEL(8)) > panel->nest_level)) {
15369                     /* dragged over name */
15370 
15371                     I->OverWhat = 1;
15372 
15373                     switch (I->ToggleMode) {
15374                     case 0:
15375                       if(row || (row == I->Pressed))
15376                         rec->hilight = 1;
15377                       break;
15378                     case 1:
15379                       if(row)
15380                         ExecutiveSpecSetVisibility(G, rec, !I->OldVisibility, mod, false);
15381                       break;
15382                     case 2:
15383                       if((row == I->Over) && row) {
15384                         if(I->LastChanged != rec) {
15385                           ExecutiveSpecSetVisibility(G, I->LastChanged, false, mod,
15386                                                      false);
15387                         }
15388                         if(!rec->visible) {
15389                           ExecutiveSpecSetVisibility(G, rec, true, mod, false);
15390                           I->LastChanged = rec;
15391                         }
15392                         if(mod == (cOrthoSHIFT | cOrthoCTRL)) {
15393                           if(rec != I->LastZoomed)
15394                             ExecutiveWindowZoom(G, rec->name, 0.0F, -1, false, -1.0F,
15395                                                 true);
15396                           I->LastZoomed = rec;
15397                         }
15398                       }
15399                       break;
15400                     }
15401                   }
15402                 } else if((row == I->Pressed) && (I->PressedWhat == 2)) {
15403                   if(!((panel->is_group) && ((xx - 1) / DIP2PIXEL(8)) > (panel->nest_level + 1))) {
15404 
15405                     /* on group control */
15406 
15407                     I->OverWhat = 2;
15408                     if(I->PressedWhat == I->OverWhat) {
15409                       rec->hilight = 2;
15410                     }
15411                   } else {
15412                     I->OverWhat = 0;
15413                   }
15414                 }
15415                 row++;
15416               }
15417             }
15418           }
15419           break;
15420         case 2:                /* right buttonBROKEN */
15421           {
15422             if((I->Over != I->Pressed) && (I->LastOver != I->Over)) {
15423               SpecRec *new_rec = NULL;
15424               SpecRec *mov_rec = NULL;
15425               PanelRec *panel = NULL;
15426 
15427               while(ListIterate(I->Panel, panel, next)) {
15428                 rec = panel->spec;
15429                 if((rec->name[0] != '_') || (!hide_underscore)) {
15430                   {
15431                     if(skip) {
15432                       skip--;
15433                     } else {
15434                       if(row == I->Pressed) {
15435                         mov_rec = rec;
15436                       }
15437                       if(row == I->Over) {
15438                         new_rec = rec;
15439                       }
15440                       row++;
15441                     }
15442                   }
15443                 }
15444               }
15445               {
15446                 int group_flag = false;
15447                 int order_flag = false;
15448                 int location = 0;
15449                 char *first = NULL, *second = NULL;
15450                 int is_child = false;
15451 
15452                 if(mov_rec && (!new_rec) && (I->Over > I->Pressed) && mov_rec->group) {
15453                   first = mov_rec->group->name;
15454                   second = mov_rec->name;
15455                   order_flag = true;
15456                   strcpy(mov_rec->group_name, mov_rec->group->group_name);
15457                   group_flag = true;
15458                 } else if(mov_rec && new_rec) {
15459                   if(mov_rec == new_rec->group) {
15460                     /* do nothing when a group is dragged over one of its members */
15461                   } else {
15462 
15463                     if(I->Pressed < I->Over) {  /* downward */
15464                       first = new_rec->name;
15465                       second = mov_rec->name;
15466                       order_flag = true;
15467                     } else {    /* upward */
15468                       first = mov_rec->name;
15469                       second = new_rec->name;
15470                       order_flag = true;
15471                       location = -2;    /* upper */
15472                     }
15473 
15474                     if(mov_rec->group == new_rec->group) {      /* reordering within a group level */
15475                       if((new_rec->type == cExecObject)
15476                          && (new_rec->obj->type == cObjectGroup)) {
15477                         ObjectGroup *group = (ObjectGroup *) new_rec->obj;
15478                         if(group->OpenOrClosed && !is_child) {
15479                           /* put inside an open group */
15480                           strcpy(mov_rec->group_name, new_rec->name);
15481                           order_flag = false;
15482                           group_flag = true;
15483                         }
15484                       }
15485                     } else if((mov_rec->group != new_rec) && (new_rec->group != mov_rec)) {
15486 
15487                       if((new_rec->type == cExecObject)
15488                          && (new_rec->obj->type == cObjectGroup)) {
15489                         ObjectGroup *group = (ObjectGroup *) new_rec->obj;
15490                         if(group->OpenOrClosed && !is_child) {
15491                           /* put inside group */
15492                           strcpy(mov_rec->group_name, new_rec->name);
15493                         } else {
15494                           strcpy(mov_rec->group_name, new_rec->group_name);
15495                         }
15496 
15497                         /* WLD TEST */
15498 
15499                         if(I->Pressed < I->Over) {      /* downward */
15500                           first = new_rec->name;
15501                           second = mov_rec->name;
15502                           order_flag = true;
15503                         } else {        /* upward */
15504                           first = mov_rec->name;
15505                           second = new_rec->name;
15506                           order_flag = true;
15507                         }
15508 
15509                         /* WLD END */
15510 
15511                       } else
15512                         strcpy(mov_rec->group_name, new_rec->group_name);
15513                       group_flag = true;
15514                     }
15515                   }
15516                 }
15517 
15518                 if(group_flag) {
15519                   OrthoLineType buf;
15520                   if(mov_rec->group_name[0]) {
15521                     sprintf(buf, "group %s, %s\n", mov_rec->group_name, mov_rec->name);
15522                   } else {
15523                     sprintf(buf, "ungroup %s\n", mov_rec->name);
15524                   }
15525                   PLog(G, buf, cPLog_no_flush);
15526                   ExecutiveInvalidateGroups(G, false);
15527                   I->RecoverPressed = mov_rec;
15528                   I->Pressed = 0;
15529                 }
15530                 if(order_flag && first && second) {
15531                   OrthoLineType order_input;
15532                   sprintf(order_input, "%s %s", first, second);
15533                   ExecutiveOrder(G, order_input, false, location);
15534                   sprintf(I->ReorderLog, "cmd.order(\"%s\",location=\"%s\")\n",
15535                           order_input, (location == -2) ? "upper" : "current");
15536                   PLog(G, I->ReorderLog, cPLog_no_flush);
15537                   I->RecoverPressed = mov_rec;
15538                   I->Pressed = 0;
15539                 }
15540               }
15541             }
15542           }
15543           break;
15544         case 3:                /* middle button */
15545           /* while(ListIterate(I->Spec,rec,next)) { */
15546           while(ListIterate(I->Panel, panel, next)) {
15547             rec = panel->spec;
15548             if((rec->name[0] != '_') || (!hide_underscore)) {
15549               if(skip) {
15550                 skip--;
15551               } else {
15552                 rec->hilight = 0;
15553                 if(((row >= I->Over) && (row <= I->Pressed)) ||
15554                    ((row >= I->Pressed) && (row <= I->Over))) {
15555                   switch (I->ToggleMode) {
15556                   case 4:      /* center and activate, while deactivating previous */
15557                     if((row == I->Over) && row) {
15558                       if(I->LastChanged != rec) {
15559                         ExecutiveSpecSetVisibility(G, I->LastChanged, false, mod, false);
15560                         ExecutiveCenter(G, rec->name, -1, true, -1.0F, NULL, true);
15561                         if(!rec->visible)
15562                           ExecutiveSpecSetVisibility(G, rec, true, mod, false);
15563                         I->LastChanged = rec;
15564                       }
15565                       rec->hilight = 0;
15566                     }
15567                     break;
15568                   case 5:      /* zoom and activate, while deactivating previous */
15569                     if((row == I->Over) && row) {
15570                       if(I->LastChanged != rec) {
15571                         ExecutiveSpecSetVisibility(G, I->LastChanged, false, mod, false);
15572                         ExecutiveWindowZoom(G, rec->name, 0.0F, -1, false, -1.0F, true);
15573                         if(!rec->visible)
15574                           ExecutiveSpecSetVisibility(G, rec, true, mod, false);
15575                         I->LastChanged = rec;
15576                       }
15577                       rec->hilight = 1;
15578                     }
15579                     break;
15580                   case 6:      /* zoom and make only object enabled */
15581                     if((row == I->Over) && row) {
15582                       if(rec != I->LastZoomed) {
15583                         ExecutiveSpecSetVisibility(G, I->LastZoomed, false, mod, false);
15584                         ExecutiveWindowZoom(G, rec->name, 0.0F, -1, false, -1.0F, true);
15585                         I->LastZoomed = rec;
15586                         ExecutiveSpecSetVisibility(G, rec, true, 0, false);
15587                       }
15588                       rec->hilight = 1;
15589                     }
15590                   }
15591                 }
15592                 row++;
15593               }
15594             }
15595           }
15596           break;
15597         }
15598         I->LastOver = I->Over;
15599 
15600       } else if(I->LastChanged)
15601         ExecutiveSpecSetVisibility(G, I->LastChanged, false, mod, false);
15602       OrthoDirty(G);
15603     }
15604   }
15605   return (1);
15606 }
15607 
draw_button(int x2,int y2,int w,int h,const float * light,const float * dark,const float * inside ORTHOCGOARG)15608 static void draw_button(int x2, int y2, int w, int h, const float *light, const float *dark,
15609                         const float *inside ORTHOCGOARG)
15610 {
15611   if (orthoCGO){
15612     CGOColorv(orthoCGO, light);
15613     CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
15614     CGOVertex(orthoCGO, x2, y2, 0.f);
15615     CGOVertex(orthoCGO, x2, y2 + h, 0.f);
15616     CGOVertex(orthoCGO, x2 + w, y2, 0.f);
15617     CGOVertex(orthoCGO, x2 + w, y2 + h, 0.f);
15618     CGOEnd(orthoCGO);
15619   } else {
15620     glColor3fv(light);
15621     glBegin(GL_POLYGON);
15622     glVertex2i(x2, y2);
15623     glVertex2i(x2, y2 + h);
15624     glVertex2i(x2 + w, y2 + h);
15625     glVertex2i(x2 + w, y2);
15626     glEnd();
15627   }
15628 
15629   if (orthoCGO){
15630     CGOColorv(orthoCGO, dark);
15631     CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
15632     CGOVertex(orthoCGO, x2 + 1, y2, 0.f);
15633     CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, 0.f);
15634     CGOVertex(orthoCGO, x2 + w, y2, 0.f);
15635     CGOVertex(orthoCGO, x2 + w, y2 + h - 1, 0.f);
15636     CGOEnd(orthoCGO);
15637   } else {
15638     glColor3fv(dark);
15639     glBegin(GL_POLYGON);
15640     glVertex2i(x2 + 1, y2);
15641     glVertex2i(x2 + 1, y2 + h - 1);
15642     glVertex2i(x2 + w, y2 + h - 1);
15643     glVertex2i(x2 + w, y2);
15644     glEnd();
15645   }
15646 
15647   if(inside) {
15648     if (orthoCGO){
15649       CGOColorv(orthoCGO, inside);
15650       CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
15651       CGOVertex(orthoCGO, x2 + 1, y2 + 1, 0.f);
15652       CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, 0.f);
15653       CGOVertex(orthoCGO, x2 + w - 1, y2 + 1, 0.f);
15654       CGOVertex(orthoCGO, x2 + w - 1, y2 + h - 1, 0.f);
15655       CGOEnd(orthoCGO);
15656     } else {
15657       glColor3fv(inside);
15658       glBegin(GL_POLYGON);
15659       glVertex2i(x2 + 1, y2 + 1);
15660       glVertex2i(x2 + 1, y2 + h - 1);
15661       glVertex2i(x2 + w - 1, y2 + h - 1);
15662       glVertex2i(x2 + w - 1, y2 + 1);
15663       glEnd();
15664     }
15665   } else {                      /* rainbow */
15666     if (orthoCGO){
15667       CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
15668       CGOColor(orthoCGO, 0.1F, 1.0F, 0.1F); // green
15669       CGOVertex(orthoCGO, x2 + 1, y2 + h - 1, 0.f);
15670       CGOColor(orthoCGO, 1.0F, 1.0F, 0.1F);  // yellow
15671       CGOVertex(orthoCGO, x2 + w - 1, y2 + h - 1, 0.f);
15672       CGOColor(orthoCGO, 1.f, 0.1f, 0.1f); // red
15673       CGOVertex(orthoCGO, x2 + 1, y2 + 1, 0.f);
15674       CGOColor(orthoCGO, 0.1F, 0.1F, 1.0F);  // blue
15675       CGOVertex(orthoCGO, x2 + w - 1, y2 + 1, 0.f);
15676       CGOEnd(orthoCGO);
15677     } else {
15678       glBegin(GL_POLYGON);
15679       glColor3f(1.0F, 0.1F, 0.1F);
15680       glVertex2i(x2 + 1, y2 + 1);
15681       glColor3f(0.1F, 1.0F, 0.1F);
15682       glVertex2i(x2 + 1, y2 + h - 1);
15683       glColor3f(1.0F, 1.0F, 0.1F);
15684       glVertex2i(x2 + w - 1, y2 + h - 1);
15685       glColor3f(0.1F, 0.1F, 1.0F);
15686       glVertex2i(x2 + w - 1, y2 + 1);
15687       glEnd();
15688     }
15689   }
15690 }
15691 
15692 #ifndef _PYMOL_NOPY
draw_button_char(PyMOLGlobals * G,int x2,int y2,char ch ORTHOCGOARG)15693 static void draw_button_char(PyMOLGlobals * G, int x2, int y2, char ch ORTHOCGOARG)
15694 {
15695   TextSetColor3f(G, 0.0F, 0.0F, 0.0F);
15696   TextSetPos2i(G, x2 + ExecToggleTextShift, y2);
15697   TextDrawChar(G, ch ORTHOCGOARGVAR);
15698 }
15699 #endif
15700 
15701 /**
15702  * Get the text color for the object name.
15703  * @param obj Object pointer, may be NULL
15704  * @param mode 0: default, 1: first carbon atom color, 2: object color
15705  * @param bg_rgb Background color
15706  * @param default_rgb Default text color
15707  * @return pointer to borrowed RGB array
15708  */
getNameColor(const CObject * obj,int mode,const float * bg_rgb,const float * default_rgb)15709 static const float* getNameColor(
15710     const CObject* obj, int mode, const float* bg_rgb, const float* default_rgb)
15711 {
15712   enum {
15713     cNameColorMode_default = 0,
15714     cNameColorMode_carbon = 1,
15715     cNameColorMode_object = 2,
15716   };
15717 
15718   if (mode == cNameColorMode_default || !obj) {
15719     return default_rgb;
15720   }
15721 
15722   int color = cColorDefault;
15723 
15724   switch (mode) {
15725   case cNameColorMode_carbon:
15726     // First carbon atom color
15727     // Assuming that there is typically a carbon atom within the first few
15728     // atoms, this procedure should be cheap (O(1)).
15729     if (obj->type == cObjectMolecule) {
15730       auto objmol = static_cast<const ObjectMolecule*>(obj);
15731       for (auto ai = objmol->AtomInfo.data(), ai_end = ai + objmol->NAtom;
15732            ai != ai_end; ++ai) {
15733         if (ai->protons == cAN_C) {
15734           color = ai->color;
15735           break;
15736         }
15737       }
15738     }
15739     break;
15740   case cNameColorMode_object:
15741     // Object color
15742     color = obj->Color;
15743     break;
15744   }
15745 
15746   if (color != cColorDefault) {
15747     const float* rgb = ColorGet(obj->G, color);
15748 
15749     // only use if it's different from the background color
15750     if (!within3f(bg_rgb, rgb, 0.1f)) {
15751       return rgb;
15752     }
15753   }
15754 
15755   return default_rgb;
15756 }
15757 
15758 /*========================================================================*/
draw(CGO * orthoCGO)15759 void CExecutive::draw(CGO* orthoCGO)
15760 {
15761   PyMOLGlobals *G = m_G;
15762   int x, y, xx, x2, y2;
15763   WordType ch;
15764   char *c = NULL;
15765   float enabledColor[3] = { 0.5F, 0.5F, 0.5F };
15766   float cloakedColor[3] = { 0.35F, 0.35F, 0.35F };
15767   float pressedColor[3] = { 0.7F, 0.7F, 0.7F };
15768   float disabledColor[3] = { 0.25F, 0.25F, 0.25F };
15769   float lightEdge[3] = { 0.6F, 0.6F, 0.6F };
15770   float darkEdge[3] = { 0.35F, 0.35F, 0.35F };
15771   float captionColor[3] = { 0.3F, 0.9F, 0.3F };
15772 
15773 #ifndef _PYMOL_NOPY
15774   float activeColor[3] = { 0.9F, 0.9F, 1.0F };
15775   float toggleColor3[3] = { 0.6F, 0.6F, 0.8F };
15776 #endif
15777 
15778   SpecRec *rec = NULL;
15779   PanelRec *panel = NULL;
15780   CExecutive *I = G->Executive;
15781   int n_ent;
15782   int n_disp;
15783   int skip = 0;
15784   int row = -1;
15785   int ExecLineHeight = DIP2PIXEL(SettingGetGlobal_i(G, cSetting_internal_gui_control_size));
15786   int text_lift = (ExecLineHeight / 2) - DIP2PIXEL(5);
15787   int hide_underscore = SettingGetGlobal_b(G, cSetting_hide_underscore_names);
15788   int op_cnt = get_op_cnt(G);
15789   int full_names = SettingGetGlobal_b(G, cSetting_group_full_member_names);
15790   int arrows = SettingGetGlobal_b(G, cSetting_group_arrow_prefix);
15791   auto name_color_mode = SettingGet<int>(G, cSetting_internal_gui_name_color_mode);
15792 
15793   ExecutiveUpdatePanelList(G);
15794 
15795   /* if we're running with a GUI and have a valid panel */
15796   if(G->HaveGUI && G->ValidContext && ((rect.right - rect.left) > 6)
15797      && I->ValidPanel) {
15798     int max_char;
15799     int nChar;
15800 
15801     /* count entries
15802      * do we have enough structures to warrant a scroll bar? */
15803     n_ent = 0;
15804     while(ListIterate(I->Panel, panel, next)) {
15805       rec = panel->spec;
15806       if(rec && ((rec->name[0] != '_') || (!hide_underscore)))
15807         n_ent++;
15808     }
15809 
15810     n_disp =
15811       ((rect.top - rect.bottom) - (ExecTopMargin)) / ExecLineHeight;
15812     if(n_disp < 1)
15813       n_disp = 1;
15814 
15815     /* we need a scrollbar */
15816     if(n_ent > n_disp) {
15817       int bar_maxed = I->m_ScrollBar.isMaxed();
15818       if(!I->ScrollBarActive) {
15819         I->m_ScrollBar.setLimits(n_ent, n_disp);
15820         if(bar_maxed) {
15821           I->m_ScrollBar.maxOut();
15822           I->NSkip = static_cast<int>(I->m_ScrollBar.getValue());
15823         } else {
15824           I->m_ScrollBar.setValue(0);
15825           I->NSkip = 0;
15826         }
15827       } else {
15828         I->m_ScrollBar.setLimits(n_ent, n_disp);
15829         if(bar_maxed)
15830           I->m_ScrollBar.maxOut();
15831         I->NSkip = static_cast<int>(I->m_ScrollBar.getValue());
15832       }
15833       I->ScrollBarActive = 1;
15834     } else {
15835       I->ScrollBarActive = 0;
15836       I->NSkip = 0;
15837     }
15838 
15839     /* determination of longest string based on internal_gui_size, etc... */
15840     max_char =
15841       (((rect.right - rect.left) -
15842         (ExecLeftMargin + ExecRightMargin + 4)) - (op_cnt * ExecToggleWidth));
15843     if(I->ScrollBarActive) {
15844       max_char -= (ExecScrollBarMargin + ExecScrollBarWidth);
15845     }
15846     max_char /= DIP2PIXEL(8);
15847 
15848     /* fill and outline the entire block */
15849     if(SettingGetGlobal_b(G, cSetting_internal_gui_mode) == 0) {
15850       if (orthoCGO)
15851 	CGOColorv(orthoCGO, BackColor);
15852 #ifndef PURE_OPENGL_ES_2
15853       else
15854 	glColor3fv(BackColor);
15855 #endif
15856       fill(orthoCGO);
15857       drawLeftEdge(orthoCGO);
15858     }
15859 
15860     /* draw the scroll bar */
15861     if(I->ScrollBarActive) {
15862       I->m_ScrollBar.setBox(rect.top - ExecScrollBarMargin,
15863                       rect.left + ExecScrollBarMargin,
15864                       rect.bottom + 2,
15865                       rect.left + ExecScrollBarMargin + ExecScrollBarWidth);
15866       I->m_ScrollBar.draw(orthoCGO);
15867     }
15868 
15869     x = rect.left + ExecLeftMargin;
15870     y = (rect.top - ExecLineHeight) - ExecTopMargin;
15871     /*    xx = rect.right-ExecRightMargin-ExecToggleWidth*(cRepCnt+op_cnt); */
15872 #ifndef _PYMOL_NOPY
15873     xx = rect.right - ExecRightMargin - ExecToggleWidth * (op_cnt);
15874 #else
15875     xx = rect.right - ExecRightMargin;
15876 #endif
15877 
15878     if(I->ScrollBarActive) {
15879       x += ExecScrollBarWidth + ExecScrollBarMargin;
15880     }
15881     skip = I->NSkip;
15882 
15883     /* for each object in the Panel... */
15884     while(ListIterate(I->Panel, panel, next)) {
15885       rec = panel->spec;
15886       {
15887         if(skip) {
15888           skip--;
15889         } else {
15890 	  /* setup the X,Y offsets for this entry */
15891           row++;
15892           x2 = xx;
15893           y2 = y;
15894           nChar = max_char;
15895 
15896           if((x - ExecToggleMargin) - (xx - ExecToggleMargin) > -10) {
15897             x2 = x + 10;
15898           }
15899 #ifndef _PYMOL_NOPY
15900 	  /*
15901 	   * The ASHLC menus; these access Python so,
15902 	   * protect this block from non-python instances
15903 	   */
15904           {
15905             int a;
15906             float toggleColor[3] = { 0.5F, 0.5F, 1.0F };
15907             float toggleColor2[3] = { 0.4F, 0.4F, 0.6F };
15908             float toggleDarkEdge[3] = { 0.3F, 0.3F, 0.5F };
15909             float toggleLightEdge[3] = { 0.7F, 0.7F, 0.9F };
15910 
15911             glColor3fv(toggleColor);
15912             for(a = 0; a < op_cnt; a++) {
15913               switch (a) {
15914               case 0:
15915                 /*
15916                    glColor3fv(toggleColor);
15917                    glBegin(GL_POLYGON);
15918                    glVertex2i(x2,y2+(ExecToggleSize)/2);
15919                    glVertex2i(x2+(ExecToggleSize)/2,y2);
15920                    glVertex2i(x2+ExecToggleSize,y2+(ExecToggleSize)/2);
15921                    glVertex2i(x2+(ExecToggleSize)/2,y2+ExecToggleSize);
15922                    glEnd();
15923                  */
15924 
15925 		/* the infamous ASHLC! */
15926 
15927                 draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
15928                             toggleLightEdge, toggleDarkEdge, toggleColor ORTHOCGOARGVAR);
15929 
15930                 draw_button_char(G, x2, y2 + text_lift, 'A' ORTHOCGOARGVAR);
15931                 break;
15932               case 1:
15933                 draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
15934                             toggleLightEdge, toggleDarkEdge, toggleColor3 ORTHOCGOARGVAR);
15935 
15936                 draw_button_char(G, x2, y2 + text_lift, 'S' ORTHOCGOARGVAR);
15937                 break;
15938               case 2:
15939                 draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
15940                             toggleLightEdge, toggleDarkEdge, toggleColor2 ORTHOCGOARGVAR);
15941                 draw_button_char(G, x2, y2 + text_lift, 'H' ORTHOCGOARGVAR);
15942                 break;
15943               case 3:
15944                 draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
15945                             toggleLightEdge, toggleDarkEdge, toggleColor ORTHOCGOARGVAR);
15946                 draw_button_char(G, x2, y2 + text_lift, 'L' ORTHOCGOARGVAR);
15947                 break;
15948               case 4:
15949                 draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
15950                             toggleLightEdge, toggleDarkEdge, NULL ORTHOCGOARGVAR);
15951                 draw_button_char(G, x2, y2 + text_lift, 'C' ORTHOCGOARGVAR);
15952                 break;
15953               case 5:
15954                 {
15955                   float *button_color = toggleColor2;
15956                   int spec_level = 0;
15957                   /* choose color / brightness based on specification level */
15958 
15959                   if(rec->type == cExecAll) {
15960                     spec_level = MovieGetSpecLevel(G,SceneGetFrame(G));
15961                   } else if(rec->type == cExecObject) {
15962                     spec_level = ObjectGetSpecLevel(rec->obj,SceneGetFrame(G));
15963                   }
15964                   switch(spec_level) {
15965                     case 1:
15966                       button_color = toggleColor3;
15967                       break;
15968                     case 2:
15969                       button_color = activeColor;
15970                       break;
15971                   }
15972                   draw_button(x2, y2, ExecToggleSize, (ExecLineHeight - 1),
15973                               toggleLightEdge, toggleDarkEdge, button_color ORTHOCGOARGVAR);
15974                 }
15975 
15976                 draw_button_char(G, x2, y2 + text_lift, 'M' ORTHOCGOARGVAR);
15977                 break;
15978               }
15979               x2 += ExecToggleWidth;
15980             }
15981           }
15982 	  /* end ASHLC */
15983 #endif
15984 
15985           {
15986             int x3 = x;
15987             int hidden_prefix = false;
15988 
15989             TextSetColor(G, TextColor);
15990             TextSetPos2i(G, x3 + DIP2PIXEL(2), y2 + text_lift);
15991 
15992             if((rec->type == cExecObject) ||
15993                (rec->type == cExecAll) || (rec->type == cExecSelection)) {
15994 
15995               y2 = y;
15996               x2 = xx;
15997               if((x - ExecToggleMargin) - (xx - ExecToggleMargin) > DIP2PIXEL(-10)) {
15998                 x2 = x + DIP2PIXEL(10);
15999               }
16000               x3 += panel->nest_level * DIP2PIXEL(8);
16001               TextSetPos2i(G, x3 + DIP2PIXEL(2), y2 + text_lift);
16002               nChar -= panel->nest_level;
16003 
16004               const float* but_color = disabledColor;
16005               {
16006                 int but_width = (x2 - x3) - 1;
16007 
16008 		/* drawing a group +/- NAME */
16009                 if(panel->is_group) {
16010                   const int button_width = DIP2PIXEL(15);
16011                   if((rec->hilight == 2) && (I->Over == I->Pressed)) {
16012                     draw_button(x3, y2, button_width, (ExecLineHeight - 1), lightEdge, darkEdge,
16013                                 pressedColor ORTHOCGOARGVAR);
16014                   } else if(panel->is_open) {
16015                     draw_button(x3, y2, button_width, (ExecLineHeight - 1), lightEdge, darkEdge,
16016                                 disabledColor ORTHOCGOARGVAR);
16017                   } else {
16018                     draw_button(x3, y2, button_width, (ExecLineHeight - 1), lightEdge, darkEdge,
16019                                 disabledColor ORTHOCGOARGVAR);
16020                   }
16021                   TextSetPos2i(G, x3 + DIP2PIXEL(4), y2 + text_lift);
16022                   if(panel->is_open) {
16023                     TextDrawChar(G, '-' ORTHOCGOARGVAR);
16024                   } else {
16025                     TextDrawChar(G, '+' ORTHOCGOARGVAR);
16026                   }
16027 
16028                   but_width -= DIP2PIXEL(16);
16029                   x3 += DIP2PIXEL(16);
16030                   nChar -= 2;
16031 
16032                   TextSetPos2i(G, x3 + DIP2PIXEL(2), y2 + text_lift);
16033                 }
16034 
16035                 if((rec->hilight == 1) || ((row == I->Over) && (I->OverWhat == 1))) {
16036 		  /* button hull */
16037                   but_color = pressedColor;
16038                 } else if(rec->visible) {
16039                   int enabled = true;
16040                   SpecRec *group_rec = rec->group;
16041                   while(enabled && group_rec) {
16042                     if(!group_rec->visible)
16043                       enabled = false;
16044                     else
16045                       group_rec = group_rec->group;
16046                   }
16047 
16048                   if(enabled) {
16049                     but_color = enabledColor;
16050                   } else {
16051                     but_color = cloakedColor;
16052                   }
16053                 }
16054 
16055                 draw_button(x3, y2, but_width, (ExecLineHeight - 1), lightEdge,
16056                     darkEdge, but_color ORTHOCGOARGVAR);
16057               }
16058 
16059               TextSetColor(G, getNameColor(rec->obj, name_color_mode, but_color,
16060                                   TextColor));
16061 
16062 	      /* object name */
16063               c = rec->name;
16064 
16065 	      /* parse out the prefix if group.foo */
16066               if(!full_names) {
16067                 if(rec->group) {        /* if prefix matches group name, then truncate */
16068                   char *p = c, *q = rec->group->name;
16069                   while((*p == *q) && (*q)) {
16070                     p++;
16071                     q++;
16072                   }
16073                   if((*p) && (!*q) && (*p == '.')) {
16074                     hidden_prefix = true;
16075                     c = p;
16076                   }
16077                 }
16078               }
16079 
16080 	      /* wrap selection names with "(" and ")" */
16081               if(rec->type == cExecSelection)
16082                 if((nChar--) > 0) {
16083                   TextDrawChar(G, '(' ORTHOCGOARGVAR);
16084                 }
16085             }
16086 
16087             if(c) {
16088               if(hidden_prefix) {
16089 		/* ^.name */
16090                 if(arrows && ((nChar--) > 0)) {
16091                   TextDrawChar(G, '^' ORTHOCGOARGVAR);
16092                   TextSetPos2i(G, x3 + DIP2PIXEL(2), y2 + text_lift);
16093                   TextDrawChar(G, '|' ORTHOCGOARGVAR);
16094                 }
16095               }
16096 
16097 	      /* draw the object name, char by char */
16098               while(*c) {
16099                 if((nChar--) > 0) {
16100                   TextDrawChar(G, *(c++) ORTHOCGOARGVAR);
16101 		}
16102                 else
16103                   break;
16104               }
16105             }
16106 
16107 	    /* SELECTIONS: wrap selection names with "(" and ")" */
16108             if(rec->type == cExecSelection) {
16109               if((nChar--) > 0) {
16110                 TextDrawChar(G, ')' ORTHOCGOARGVAR);
16111               }
16112             }
16113 
16114 	    /* OBJECTS: output any label captions, like state number, state title */
16115             if(rec->type == cExecObject) {
16116 		/* get this object's "caption" that goes on its title line,
16117 		 * currently, this is "state-title [curState/nState]" */
16118               c = rec->obj->getCaption(ch, WordLength);
16119 	      /* now print the caption */
16120               if(c && c[0] && nChar > 1) {
16121                 TextSetColor(G, captionColor);
16122                 TextSetPos2i(G, x + DIP2PIXEL(2) + DIP2PIXEL(8) * (max_char - nChar), y2 + text_lift);
16123                 if((nChar--) > 0)
16124                   TextDrawChar(G, ' ' ORTHOCGOARGVAR);
16125                 while(*c && nChar > 0) {
16126 		    /* allow color encoding for names */
16127                   if(TextSetColorFromCode(G, c, captionColor)) {
16128 		      c += 4;
16129                   } else {
16130                     TextDrawChar(G, *(c++) ORTHOCGOARGVAR);
16131                     --nChar;
16132 		  }
16133                 }
16134               }
16135             }
16136           }
16137 
16138           y -= ExecLineHeight;
16139           if(y < (rect.bottom))
16140             break;
16141         }
16142       }
16143     }
16144     I->HowFarDown = y;
16145   }
16146 }
16147 
16148 
16149 /*========================================================================*/
ExecutiveIterateObject(PyMOLGlobals * G,CObject ** obj,void ** hidden)16150 int ExecutiveIterateObject(PyMOLGlobals * G, CObject ** obj, void **hidden)
16151 {
16152   SpecRec **rec = (SpecRec **) hidden, *I_Spec = G->Executive->Spec;
16153   while(ListIterate(I_Spec, (*rec), next)) {
16154     if((*rec)->type == cExecObject)
16155       break;
16156   }
16157   if(*rec)
16158     (*obj) = (*rec)->obj;
16159   else
16160     (*obj) = NULL;
16161   return ((*rec) != NULL);
16162 }
16163 
16164 
16165 /*========================================================================*/
ExecutiveIterateObjectMolecule(PyMOLGlobals * G,ObjectMolecule ** obj,void ** hidden)16166 int ExecutiveIterateObjectMolecule(PyMOLGlobals * G, ObjectMolecule ** obj, void **hidden)
16167 {
16168   SpecRec **rec = (SpecRec **) hidden, *I_Spec = G->Executive->Spec;
16169   while(ListIterate(I_Spec, (*rec), next)) {
16170     if(((*rec)->type == cExecObject) && ((*rec)->obj->type == cObjectMolecule))
16171       break;
16172   }
16173   if(*rec)
16174     (*obj) = (ObjectMolecule *) (*rec)->obj;
16175   else
16176     (*obj) = NULL;
16177   return ((*rec) != NULL);
16178 }
16179 
16180 
16181 /*========================================================================*/
reshape(int width,int height)16182 void CExecutive::reshape(int width, int height)
16183 {
16184   PyMOLGlobals *G = m_G;
16185   CExecutive *I = G->Executive;
16186 
16187   Block::reshape(width, height);
16188 
16189   I->Width = rect.right - rect.left + 1;
16190   I->Height = rect.top - rect.bottom + 1;
16191 
16192 }
16193 
16194 
16195 /*========================================================================*/
ExecutiveReinitialize(PyMOLGlobals * G,int what,pymol::zstring_view inPattern)16196 pymol::Result<> ExecutiveReinitialize(PyMOLGlobals * G, int what, pymol::zstring_view inPattern)
16197 {
16198   CExecutive *I = G->Executive;
16199 #ifndef _PYMOL_NOPY
16200   int blocked = false;
16201 #endif
16202   /* reinitialize PyMOL */
16203   const char* pattern = inPattern.data();
16204   if(what == 2)
16205     pattern = NULL;
16206 
16207   if(pattern && (!pattern[0]))
16208     pattern = NULL;
16209   if(!pattern) {
16210 
16211     switch (what) {
16212     case 0:                    /* everything */
16213       ExecutiveDelete(G, cKeywordAll);
16214       ColorReset(G);
16215       SettingInitGlobal(G, false, false, true);
16216       ColorUpdateFrontFromSettings(G);
16217       MovieReset(G);
16218       EditorInactivate(G);
16219       ControlRock(G, 0);
16220       OrthoReshape(G, -1, -1, false);
16221       MovieScenesInit(G);
16222 
16223 #ifndef _PYMOL_NOPY
16224       blocked = PAutoBlock(G);
16225       PRunStringInstance(G, "cmd.view('*','clear')");
16226       PRunStringInstance(G, "cmd.config_mouse(\"three_button\")");
16227       WizardSet(G, NULL, false);
16228       PAutoUnblock(G, blocked);
16229 #endif
16230 
16231       SculptCachePurge(G);
16232       SceneReinitialize(G);
16233       SelectorReinit(G);
16234       SeqChanged(G);
16235       break;
16236     case 1:                    /* settings */
16237       SettingInitGlobal(G, false, false, true);
16238       ExecutiveRebuildAll(G);
16239       break;
16240     case 2:                    /* store_defaults */
16241       SettingStoreDefault(G);
16242       break;
16243     case 3:                    /* original_settings */
16244       SettingInitGlobal(G, false, false, false);
16245       ExecutiveRebuildAll(G);
16246       break;
16247     case 4:                    /* purge_defaults */
16248       SettingPurgeDefault(G);
16249       break;
16250       /* reinitialize is called with what + 5 to reset internal_gui if necessary (PYMOL-1425) */
16251     case 5:
16252     case 6:
16253       if (G->Default){
16254 	SettingSetGlobal_i(G, cSetting_internal_gui, SettingGet_i(G, G->Default, NULL, cSetting_internal_gui));
16255 	SettingGenerateSideEffects(G, cSetting_internal_gui, NULL, -1, 0);
16256       }
16257       break;
16258     }
16259     SceneUpdateStereo(G);
16260   } else {
16261     {
16262       CTracker *I_Tracker = I->Tracker;
16263       int list_id = ExecutiveGetNamesListFromPattern(G, pattern, true, true);
16264       int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
16265       SpecRec *rec;
16266 
16267       while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
16268         if(rec) {
16269           switch (rec->type) {
16270           case cExecObject:
16271             switch (what) {
16272             case 0:
16273             case 1:
16274               if(rec->obj->Setting) {
16275                 ObjectPurgeSettings(rec->obj);
16276                 rec->obj->invalidate(cRepAll, cRepInvAll, -1);
16277                 SceneInvalidate(G);
16278                 SeqChanged(G);
16279               }
16280               break;
16281             }
16282           }
16283         }
16284       }
16285       TrackerDelList(I_Tracker, list_id);
16286       TrackerDelIter(I_Tracker, iter_id);
16287     }
16288 
16289     /* to do */
16290   }
16291 
16292   return {};
16293 }
16294 
16295 
16296 /*========================================================================*/
ExecutiveInit(PyMOLGlobals * G)16297 int ExecutiveInit(PyMOLGlobals * G)
16298 {
16299   CExecutive *I = NULL;
16300   if((I = (G->Executive = new CExecutive(G)))) {
16301 
16302     SpecRec *rec = NULL;
16303 
16304     ListInit(I->Spec);
16305     I->Tracker = TrackerNew(G);
16306     I->all_names_list_id = TrackerNewList(I->Tracker, NULL);
16307     I->all_obj_list_id = TrackerNewList(I->Tracker, NULL);
16308     I->all_sel_list_id = TrackerNewList(I->Tracker, NULL);
16309     I->active = true;
16310     OrthoAttach(G, I, cOrthoTool);
16311 #ifndef GLUT_FULL_SCREEN
16312     I->oldWidth = 640;
16313     I->oldHeight = 480;
16314 #endif
16315 
16316     ListInit(I->Panel);
16317     I->ValidPanel = false;
16318 
16319     I->Lex = OVLexicon_New(G->Context->heap);
16320     I->Key = OVOneToOne_New(G->Context->heap);
16321 
16322     /* create "all" entry */
16323 
16324     ListElemCalloc(G, rec, SpecRec);
16325 
16326     strcpy(rec->name, cKeywordAll);
16327     rec->type = cExecAll;
16328     rec->visible = true;
16329     rec->next = NULL;
16330     rec->cand_id = TrackerNewCand(I->Tracker, (TrackerRef *) (void *) rec);
16331     TrackerLink(I->Tracker, rec->cand_id, I->all_names_list_id, 1);
16332     ListAppend(I->Spec, rec, next, SpecRec);
16333     ExecutiveAddKey(I, rec);
16334 
16335     return 1;
16336   } else
16337     return 0;
16338 
16339 }
16340 
16341 
16342 /*========================================================================*/
ExecutiveFree(PyMOLGlobals * G)16343 void ExecutiveFree(PyMOLGlobals * G)
16344 {
16345   CExecutive *I = G->Executive;
16346   SpecRec *rec = NULL;
16347     CGOFree(I->selIndicatorsCGO);
16348   while(ListIterate(I->Spec, rec, next)) {
16349     if(rec->type == cExecObject)
16350       DeleteP(rec->obj);
16351   }
16352   ListFree(I->Spec, next, SpecRec);
16353   ListFree(I->Panel, next, PanelRec);
16354   if(I->Tracker)
16355     TrackerFree(I->Tracker);
16356   OVLexicon_DEL_AUTO_NULL(I->Lex);
16357   OVOneToOne_DEL_AUTO_NULL(I->Key);
16358 
16359   ExecutiveUniqueIDAtomDictInvalidate(G);
16360 
16361   DeleteP(G->Executive);
16362 }
16363 
16364 #ifdef _undefined
16365 
16366 matrix checking code ...
16367 
16368 double mt[3][3], mt2[3][3], pr[3][3], im[3][3], em[3][3];
16369 printf("normalized matrix \n");
16370 for(a = 0; a < 3; a++) {
16371   for(b = 0; b < 3; b++) {
16372     printf("%12.3f ", evect[a][b]);
16373   }
16374   printf("\n");
16375 }
16376 
16377 printf("\n");
16378 
16379 printf("tensor \n");
16380 for(a = 0; a < 3; a++) {
16381   for(b = 0; b < 3; b++) {
16382     printf("%12.3f ", mi[a][b]);
16383   }
16384   printf("\n");
16385 }
16386 
16387 printf("\n");
16388 
16389 for(a = 0; a < 3; a++) {
16390   for(b = 0; b < 3; b++) {
16391     mt[a][b] = evect[a][b];
16392   }
16393 }
16394 
16395 for(a = 0; a < 3; a++) {
16396   for(b = 0; b < 3; b++) {
16397     mt2[a][b] = evect[b][a];
16398   }
16399 }
16400 
16401 matrix_multiply33d33d(mt, mt2, pr);
16402 printf("self product 1 \n");
16403 for(a = 0; a < 3; a++) {
16404   for(b = 0; b < 3; b++) {
16405     printf("%8.3f ", pr[a][b]);
16406   }
16407   printf("\n");
16408 }
16409 
16410 printf("\n");
16411 
16412 matrix_multiply33d33d(mt, mi, im);
16413 matrix_multiply33d33d(im, mt2, pr);
16414 printf("diagonal product 1 \n");
16415 for(a = 0; a < 3; a++) {
16416   for(b = 0; b < 3; b++) {
16417     printf("%8.3f ", pr[a][b]);
16418   }
16419   printf("\n");
16420 }
16421 
16422 printf("\n");
16423 
16424 for(a = 0; a < 3; a++) {
16425   for(b = 0; b < 3; b++) {
16426     em[a][b] = 0.0;
16427   }
16428   em[a][a] = egval[a];
16429 }
16430 
16431 matrix_multiply33d33d(mt2, em, im);
16432 matrix_multiply33d33d(im, mt, pr);
16433 printf("diagonal product 4 \n");
16434 for(a = 0; a < 3; a++) {
16435   for(b = 0; b < 3; b++) {
16436     printf("%8.3f ", pr[a][b]);
16437   }
16438   printf("\n");
16439 }
16440 
16441 printf("\n");
16442 #endif
16443 
ExecutiveCEAlign(PyMOLGlobals * G,PyObject * listA,PyObject * listB,int lenA,int lenB,float d0,float d1,int windowSize,int gapMax)16444 PyObject * ExecutiveCEAlign(PyMOLGlobals * G, PyObject * listA, PyObject * listB, int lenA, int lenB, float d0, float d1, int windowSize, int gapMax) {
16445 #ifdef _PYMOL_NOPY
16446   return NULL;
16447 #else
16448   int i=0;
16449   int smaller;
16450   double **dmA ,**dmB, **S;
16451   int bufferSize;
16452   pcePoint coordsA, coordsB;
16453   pathCache paths = NULL;
16454   PyObject * result;
16455 
16456   smaller = lenA < lenB ? lenA : lenB;
16457 
16458   /* get the coodinates from the Python objects */
16459   coordsA = (pcePoint) getCoords(listA, lenA);
16460   coordsB = (pcePoint) getCoords(listB, lenB);
16461 
16462   /* calculate the distance matrix for each protein */
16463   dmA = (double**) calcDM(coordsA, lenA);
16464   dmB = (double**) calcDM(coordsB, lenB);
16465 
16466   /* calculate the CE Similarity matrix */
16467   S = (double**) calcS(dmA, dmB, lenA, lenB, windowSize);
16468 
16469   /* find the best path through the CE Sim. matrix */
16470   bufferSize = 0;
16471 
16472   /* the following line HANGS PyMOL */
16473   paths = (pathCache) findPath(S, dmA, dmB, lenA, lenB, d0, d1, windowSize, gapMax, &bufferSize);
16474 
16475   /* Get the optimal superposition here... */
16476   result = (PyObject*) findBest(coordsA, coordsB, paths, bufferSize, smaller, windowSize);
16477 
16478   /* release memory */
16479   free(coordsA);
16480   free(coordsB);
16481   for ( i = 0; i < bufferSize; ++i )
16482     free(paths[i]);
16483   free(paths);
16484 
16485   /* distance matrices	 */
16486   for ( i = 0; i < lenA; i++ )
16487     free( dmA[i] );
16488   free(dmA);
16489 
16490   for  ( i = 0; i < lenB; i++ )
16491     free( dmB[i] );
16492   free(dmB);
16493 
16494   /* similarity matrix */
16495   for ( i = 0; i < lenA; i++ )
16496     free( S[i] );
16497   free(S);
16498 
16499   return (PyObject*) result;
16500 #endif
16501 }
16502 
ExecutiveGetObjectNames(PyMOLGlobals * G,int mode,const char * name,int enabled_only,int * numstrs)16503 char *ExecutiveGetObjectNames(PyMOLGlobals * G, int mode, const char *name, int enabled_only, int *numstrs){
16504   char *res;
16505   int size=0, stlen;
16506   CExecutive *I = G->Executive;
16507   CTracker *I_Tracker = I->Tracker;
16508   *numstrs = 0;
16509   {
16510     int list_id = ExecutiveGetNamesListFromPattern(G, name, true, true);
16511     int iter_id = TrackerNewIter(I_Tracker, 0, list_id);
16512     SpecRec *rec;
16513     res = VLAlloc(char, 1000);
16514     while(TrackerIterNextCandInList(I_Tracker, iter_id, (TrackerRef **) (void *) &rec)) {
16515       if((rec->type == cExecObject
16516 	  && (((!mode) || (mode == cObjectTypeObjects) || (mode == cObjectTypePublic) || (mode == cObjectTypePublicObjects))
16517 	      || ((rec->obj->type != cObjectGroup) && ((mode == cObjectTypePublicNonGroupObjects) || (mode == cObjectTypeNonGroupObjects)))
16518 	      || ((rec->obj->type == cObjectGroup) && ((mode == cObjectTypePublicGroupObjects) || (mode == cObjectTypeGroupObjects)))))
16519 	 || (rec->type == cExecSelection
16520 	     && ((!mode) || (mode == cObjectTypeSelections) || (mode == cObjectTypePublic) || (mode == cObjectTypePublicSelections)))
16521 	 ) {
16522 	if((mode < 3) || (mode > 7) || (mode == cObjectTypeGroupObjects) || (rec->name[0] != '_')) {
16523 	  if((!enabled_only) || (rec->visible)) {
16524 	    stlen = strlen(rec->name);
16525             VLACheck(res, char, size + stlen + 1);
16526             strcpy(res + size, rec->name);
16527             size += stlen + 1;
16528 	    *numstrs += 1;
16529 	  }
16530 	}
16531       }
16532     }
16533   }
16534   if (!size){
16535     VLAFreeP(res);
16536     res = NULL;
16537   } else {
16538     VLASize(res, char, size);
16539   }
16540   return (res);
16541 }
16542 
16543 /*
16544  * Get the coord set for the given object name and state index. If "omp" is
16545  * not NULL, then also store a pointer to the object molecule.
16546  */
ExecutiveGetCoordSet(PyMOLGlobals * G,const char * name,int state,ObjectMolecule ** omp)16547 CoordSet * ExecutiveGetCoordSet(PyMOLGlobals * G, const char * name, int state, ObjectMolecule ** omp) {
16548   ObjectMolecule * om = NULL;
16549   CoordSet * cs = NULL;
16550 
16551   ok_assert(1, om = ExecutiveFindObject<ObjectMolecule>(G, name));
16552   ok_assert(1, cs = om->getCoordSet(state));
16553 
16554 ok_except1:
16555   if (omp != NULL)
16556     *omp = om;
16557   return cs;
16558 }
16559 
16560 #ifdef _PYMOL_IP_PROPERTIES
16561 #endif // _PYMOL_IP_PROPERTIES
16562 
16563 
ExecutiveUniqueIDAtomDictInvalidate(PyMOLGlobals * G)16564 void ExecutiveUniqueIDAtomDictInvalidate(PyMOLGlobals * G) {
16565   CExecutive *I = G->Executive;
16566   if (I->m_eoo) {
16567     OVOneToOne_DEL_AUTO_NULL(I->m_id2eoo);
16568     VLAFreeP(I->m_eoo);
16569   }
16570 }
16571 
ExecutiveUniqueIDAtomDictGet(PyMOLGlobals * G,int i)16572 const ExecutiveObjectOffset * ExecutiveUniqueIDAtomDictGet(PyMOLGlobals * G, int i) {
16573   CExecutive *I = G->Executive;
16574   OVreturn_word offset;
16575 
16576   if (!I->m_eoo)
16577     ExecutiveGetUniqueIDAtomVLADict(G, &I->m_eoo, &I->m_id2eoo);
16578 
16579   if(!OVreturn_IS_OK(offset = OVOneToOne_GetForward(I->m_id2eoo, i)))
16580     return NULL;
16581 
16582   return I->m_eoo + offset.word;
16583 }
16584 
ExecutiveSetFeedbackMask(PyMOLGlobals * G,int action,unsigned int sysmod,unsigned char mask)16585 pymol::Result<> ExecutiveSetFeedbackMask(
16586     PyMOLGlobals* G, int action, unsigned int sysmod, unsigned char mask)
16587 {
16588   switch (action) {
16589   case 0:
16590     G->Feedback->setMask(sysmod, mask);
16591     break;
16592   case 1:
16593     G->Feedback->enable(sysmod, mask);
16594     break;
16595   case 2:
16596     G->Feedback->disable(sysmod, mask);
16597     break;
16598   case 3:
16599     G->Feedback->push();
16600     break;
16601   case 4:
16602     G->Feedback->pop();
16603     break;
16604   }
16605   return {};
16606 }
16607 
ExecutiveSliceNew(PyMOLGlobals * G,const char * slice_name,const char * map_name,int state,int map_state)16608 pymol::Result<> ExecutiveSliceNew(PyMOLGlobals* G, const char* slice_name,
16609     const char* map_name, int state, int map_state)
16610 {
16611   int multi = false;
16612   CObject *obj = NULL, *mObj, *origObj;
16613   ObjectMap *mapObj;
16614   ObjectMapState *ms;
16615 
16616   origObj = ExecutiveFindObjectByName(G, slice_name);
16617   if(origObj) {
16618     if(origObj->type != cObjectSlice) {
16619       return pymol::make_error("Object ", slice_name, " is not an ObjectSlice.");
16620     }
16621   }
16622 
16623   mObj = ExecutiveFindObjectByName(G, map_name);
16624   if(mObj) {
16625     if(mObj->type != cObjectMap)
16626       mObj = NULL;
16627   }
16628   if(mObj) {
16629     mapObj = (ObjectMap *) mObj;
16630     if(state == -1) {
16631       multi = true;
16632       state = 0;
16633       map_state = 0;
16634     } else if(state == -2) {
16635       state = SceneGetState(G);
16636       if(map_state < 0)
16637         map_state = state;
16638     } else if(state == -3) {    /* append mode */
16639       state = 0;
16640       if(origObj)
16641         state = origObj->getNFrame();
16642     } else {
16643       if(map_state == -1) {
16644         map_state = 0;
16645         multi = true;
16646       } else {
16647         multi = false;
16648       }
16649     }
16650     while(1) {
16651       if(map_state == -2)
16652         map_state = SceneGetState(G);
16653       if(map_state == -3)
16654         map_state = mapObj->getNFrame() - 1;
16655       ms = ObjectMapStateGetActive(mapObj, map_state);
16656       if(ms) {
16657         obj = (CObject *) ObjectSliceFromMap(G, (ObjectSlice *) origObj, mapObj,
16658                                              state, map_state);
16659 
16660         if(!origObj) {
16661           ObjectSetName(obj, slice_name);
16662           ExecutiveManageObject(G, (CObject *) obj, -1, false);
16663         }
16664         PRINTFB(G, FB_ObjectMesh, FB_Actions)
16665           " SliceMap: created \"%s\".\n", slice_name ENDFB(G);
16666 
16667       } else if(!multi) {
16668         return pymol::make_error("State ", map_state + 1, " not present in map ", map_name);
16669       }
16670       if(multi) {
16671         origObj = obj;
16672         map_state++;
16673         state++;
16674         if(map_state >= mapObj->State.size())
16675           break;
16676       } else {
16677         break;
16678       }
16679     }
16680   } else {
16681     return pymol::make_error("Map or brick object ", map_name, " not found.");
16682   }
16683   return {};
16684 }
16685 
16686 /**
16687  * Discard all bonds and do distance based bonding.
16688  * Implementation of `cmd.rebond()`
16689  *
16690  * @param oname object name
16691  * @param state object state, negative values fall back to current state
16692  */
ExecutiveRebond(PyMOLGlobals * G,const char * oname,int state)16693 pymol::Result<> ExecutiveRebond(PyMOLGlobals* G, const char* oname, int state)
16694 {
16695   auto obj = ExecutiveFindObjectMoleculeByName(G, oname);
16696   if (!obj) {
16697     return pymol::make_error("cannot find object");
16698   }
16699 
16700   auto cs = obj->getCoordSet(state);
16701   if (!cs) {
16702     return pymol::make_error("no such state");
16703   }
16704 
16705   ObjectMoleculeRemoveBonds(obj, 0, 0);
16706   ObjectMoleculeConnect(obj, cs, true, 3);
16707   obj->invalidate(cRepAll, cRepInvAll, -1);
16708 
16709   return {};
16710 }
16711 
ExecutiveAddBondByIndices(PyMOLGlobals * G,pymol::zstring_view oname,unsigned int atm1,unsigned int atm2,int order)16712 pymol::Result<> ExecutiveAddBondByIndices(PyMOLGlobals* G,
16713     pymol::zstring_view oname, unsigned int atm1, unsigned int atm2, int order)
16714 {
16715   auto obj = ExecutiveFindObject<ObjectMolecule>(G, oname.c_str());
16716   if (!obj) {
16717     return pymol::make_error("Cannot find object ", oname);
16718   }
16719   return ObjectMoleculeAddBondByIndices(obj, atm1, atm2, order);
16720 }
16721 
ExecutiveFit(PyMOLGlobals * G,pymol::zstring_view str1,pymol::zstring_view str2,int mode,int cutoff,int cycles,int quiet,pymol::zstring_view object,int state1,int state2,int matchmaker)16722 pymol::Result<ExecutiveRMSInfo> ExecutiveFit(PyMOLGlobals* G,
16723     pymol::zstring_view str1, pymol::zstring_view str2, int mode, int cutoff,
16724     int cycles, int quiet, pymol::zstring_view object, int state1, int state2,
16725     int matchmaker)
16726 {
16727   SelectorTmp s1(G, str1.c_str());
16728   SelectorTmp s2(G, str2.c_str());
16729   ExecutiveRMSInfo rmsinfo;
16730   ExecutiveRMS(G, s1.getName(), s2.getName(), mode, cutoff, cycles, quiet,
16731       object.c_str(), state1, state2, false, matchmaker, &rmsinfo);
16732   return rmsinfo;
16733 }
16734 
16735