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