1 
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* -------------------------------------------------------------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17 #include"os_python.h"
18 
19 #include"os_predef.h"
20 #include"os_std.h"
21 #include"os_gl.h"
22 
23 #include"Base.h"
24 #include"OOMac.h"
25 #include"MemoryDebug.h"
26 #include"Executive.h"
27 #include"Ortho.h"
28 #include"Movie.h"
29 #include"Scene.h"
30 #include"MyPNG.h"
31 #include"P.h"
32 #include"Setting.h"
33 #include"main.h"
34 #include"PConv.h"
35 #include"Util.h"
36 #include"Util2.h"
37 #include"Parse.h"
38 #include"PyMOL.h"
39 #include"ScrollBar.h"
40 #include"Menu.h"
41 #include"View.h"
42 #include"Seq.h"
43 #include"CGO.h"
44 #include"MovieScene.h"
45 
46 #define cMovieDragModeMoveKey   1
47 #define cMovieDragModeInsDel    2
48 #define cMovieDragModeCopyKey   3
49 #define cMovieDragModeOblate    4
50 
CMovie(PyMOLGlobals * G)51 CMovie::CMovie(PyMOLGlobals* G)
52     : Block(G)
53     , m_ScrollBar(G, true)
54 {
55   active = true;
56   OrthoAttach(G, this, cOrthoTool);
57 }
58 
MovieViewReinterpolate(PyMOLGlobals * G)59 void MovieViewReinterpolate(PyMOLGlobals *G)
60 {
61   float power  = SettingGetGlobal_f(G, cSetting_motion_power);
62   float bias   = SettingGetGlobal_f(G, cSetting_motion_bias);
63   float linear = SettingGetGlobal_f(G, cSetting_motion_linear);
64   int hand     = SettingGetGlobal_i(G, cSetting_motion_hand);
65 
66   MovieView(G, 3, -1, -1, power, bias, 1, /* note simple always = 1 for camera motion...*/
67             linear,
68             SettingGetGlobal_b(G,cSetting_movie_loop) ? 1 : 0 ,
69             hand, 5, 1, NULL, 0.5, -1, 1);
70 }
71 
MovieXtoFrame(PyMOLGlobals * G,BlockRect * rect,int frames,int x,int nearest)72 int MovieXtoFrame(PyMOLGlobals *G, BlockRect *rect, int frames, int x, int nearest)
73 {
74   return ViewElemXtoFrame(rect,frames,x,nearest);
75 }
76 
MovieViewTrim(PyMOLGlobals * G,int n_frame)77 void MovieViewTrim(PyMOLGlobals *G,int n_frame)
78 {
79   CMovie *I = G->Movie;
80   if(n_frame>=0) {
81     I->Sequence.resize(n_frame);
82     I->Cmd.resize(n_frame);
83     I->ViewElem.resize(n_frame);
84     I->NFrame = n_frame;
85   }
86 }
87 
88 
MovieViewModify(PyMOLGlobals * G,int action,int index,int count,int target,int freeze,int localize)89 int MovieViewModify(PyMOLGlobals *G,int action, int index, int count,int target, int freeze, int localize)
90 {
91   CMovie *I = G->Movie;
92   int ok = true;
93   MovieClearImages(G);
94   if( (ok = ViewElemModify(G,&I->ViewElem, action, index, count, target)) ) {
95     switch(action) {
96     case cViewElemModifyInsert:
97       I->Sequence.insert(index, count);
98       I->Cmd.insert(I->Cmd.begin() + index, count, "");
99       I->NFrame = VLAGetSize(I->Sequence);
100       {
101         int frame = SceneGetFrame(G);
102         if(frame >= index) {
103           SceneSetFrame(G,0,frame + count);
104         }
105       }
106       break;
107     case cViewElemModifyDelete:
108       I->Sequence.erase(index, count);
109       I->Cmd.erase(I->Cmd.begin() + index, I->Cmd.begin() + index + count);
110       I->NFrame = VLAGetSize(I->Sequence);
111       break;
112     case cViewElemModifyMove:
113       if((index>=0) && (target>=0) && (index<I->NFrame) && (target<I->NFrame)) {
114         int i;
115         for(i=0;i<count;i++) {
116           if( ((i+index)<I->NFrame) && ((i+target)<I->NFrame)) {
117             int src,dst;
118             if(index>target) {
119               src = index+i;
120               dst = target+i;
121             } else {
122               src = index+(count-1)-i;
123               dst = target+(count-1)-i;
124             }
125             I->Sequence[dst] = I->Sequence[src];
126             I->Cmd[dst] = std::move(I->Cmd[src]);
127             I->Cmd[src].clear();
128           }
129         }
130       }
131       break;
132     case cViewElemModifyCopy:
133       if((index>=0) && (target>=0) && (index<I->NFrame) && (target<I->NFrame)) {
134         int i;
135         for(i=0;i<count;i++) {
136           if( ((i+index)<I->NFrame) && ((i+target)<I->NFrame)) {
137             int src,dst;
138             if(index>target) {
139               src = index+i;
140               dst = target+i;
141             } else {
142               src = index+(count-1)-i;
143               dst = target+(count-1)-i;
144             }
145             I->Cmd[dst] = I->Cmd[src];
146           }
147         }
148       }
149       break;
150     }
151   }
152   if(ok && ((!freeze)&&(!localize))) {
153     ExecutiveMotionExtend(G,freeze);
154   }
155   return ok;
156 }
157 
MovieGetSpecLevel(PyMOLGlobals * G,int frame)158 int MovieGetSpecLevel(PyMOLGlobals *G,int frame)
159 {
160   CMovie *I = G->Movie;
161   if(I->ViewElem) {
162     int size = VLAGetSize(I->ViewElem);
163     if(frame<0) {
164       int max_level = 0;
165       int i;
166       for(i=0;i<size;i++) {
167         if(max_level < I->ViewElem[i].specification_level)
168           max_level = I->ViewElem[i].specification_level;
169       }
170       return max_level;
171     }
172     if((frame>=0) && (frame<size))
173       return I->ViewElem[frame].specification_level;
174     return 0;
175   }
176   return -1;
177 }
178 
MovieSetRealtime(PyMOLGlobals * G,int realtime)179 void MovieSetRealtime(PyMOLGlobals * G, int realtime)
180 {
181   CMovie *I = G->Movie;
182   I->RealtimeFlag = realtime;
183 }
184 
MovieGetRealtime(PyMOLGlobals * G)185 int MovieGetRealtime(PyMOLGlobals * G)
186 {
187   CMovie *I = G->Movie;
188   return I->RealtimeFlag;
189 }
190 
MovieCopyPrepare(PyMOLGlobals * G,int * width,int * height,int * length)191 void MovieCopyPrepare(PyMOLGlobals * G, int *width, int *height, int *length)
192 {
193   /* assumed locked api, blocked threads, and master thread on entry */
194 
195   /* writes the current movie sequence to a set of PNG files
196    * this routine can take a LOT of time --
197    * TODO: develop an interrupt mechanism */
198 
199   int start, stop;
200   CMovie *I = G->Movie;
201   int nFrame;
202 
203   I->CacheSave = SettingGetGlobal_b(G, cSetting_cache_frames);
204   I->OverlaySave = SettingGetGlobal_i(G, cSetting_overlay);
205   if(!I->CacheSave)
206     MovieClearImages(G);
207   SettingSetGlobal_b(G, cSetting_cache_frames, 1);
208   SettingSetGlobal_i(G, cSetting_overlay, 5);
209   nFrame = I->NFrame;
210   if(!nFrame) {
211     nFrame = SceneGetNFrame(G, NULL);
212   }
213   start = 0;
214   stop = nFrame;
215   if((start != 0) || (stop != (nFrame + 1)))
216     SceneSetFrame(G, 0, 0);
217   MoviePlay(G, cMoviePlay);
218   VecCheck(I->Image, nFrame);
219   SceneGetWidthHeight(G, width, height);
220   {
221     int uniform_height = -1;
222     int uniform_width = -1;
223     int uniform_flag = false;
224     int scene_match = true;
225     int a;
226     /* make sure all the movie frames match the screen size or are pre-rendered and are already the same size */
227     for(a = 0; a < nFrame; a++) {
228       const pymol::Image* image = I->Image[a].get();
229       if(image) {
230         if((image->getHeight() != *height) || (image->getWidth() != *width)) {
231           scene_match = false;
232           if(uniform_height < 0) {
233             uniform_height = image->getHeight();
234             uniform_width = image->getWidth();
235           } else {
236             if((image->getHeight() != uniform_height) || (image->getWidth() != uniform_width))
237               uniform_flag = false;
238           }
239         }
240       } else
241         uniform_flag = false;   /* missing at least one image, so not uniform */
242     }
243     if(!scene_match) {
244       if(uniform_flag) {
245         *height = uniform_height;
246         *width = uniform_width;
247       } else {
248         MovieClearImages(G);
249       }
250     }
251 
252   }
253   *length = nFrame;
254 }
255 
MovieFlushCommands(PyMOLGlobals * G)256 void MovieFlushCommands(PyMOLGlobals * G)
257 {
258   CMovie *I = G->Movie;
259   I->RecursionFlag = true;
260   PFlush(G);
261   I->RecursionFlag = false;
262 }
263 
MovieCopyFrame(PyMOLGlobals * G,int frame,int width,int height,int rowbytes,void * ptr)264 int MovieCopyFrame(PyMOLGlobals * G, int frame, int width, int height, int rowbytes,
265                    void *ptr)
266 {
267   CMovie *I = G->Movie;
268   int result = false;
269   int nFrame;
270   nFrame = I->NFrame;
271   if(!nFrame) {
272     nFrame = SceneGetNFrame(G, NULL);
273   }
274 
275   if((frame < nFrame) && (ptr)) {
276     int a = frame;
277     int i;
278     SceneSetFrame(G, 0, a);
279     MovieDoFrameCommand(G, a);
280     MovieFlushCommands(G);
281     i = MovieFrameToImage(G, a);
282     VecCheck(I->Image, i);
283     if(!I->Image[i]) {
284       SceneUpdate(G, false);
285       SceneMakeMovieImage(G, false, false, cSceneImage_Default);
286     }
287     if(!I->Image[i]) {
288       PRINTFB(G, FB_Movie, FB_Errors)
289         "MoviePNG-Error: Missing rendered image.\n" ENDFB(G);
290     } else {
291       if((I->Image[i]->getHeight() == height) && (I->Image[i]->getWidth() == width)) {
292         unsigned char *srcImage = I->Image[i]->bits();
293         int i, j;
294         for(i = 0; i < height; i++) {
295           unsigned char *dst = ((unsigned char *) ptr) + i * rowbytes;
296           unsigned char *src = srcImage + ((height - 1) - i) * width * 4;
297           for(j = 0; j < width; j++) {
298             *dst++ = src[3];
299             *dst++ = src[0];
300             *dst++ = src[1];
301             *dst++ = src[2];
302             src += 4;
303           }
304         }
305         result = true;
306       } else {
307         /* mismatched dimensions, so furnish a white image */
308         memset(ptr, 0xFF, 4 * height * width);
309       }
310       ExecutiveDrawNow(G);
311       if(G->HaveGUI)
312         PyMOL_SwapBuffers(G->PyMOL);
313     }
314     if(!I->CacheSave) {
315       if(I->Image[i]) {
316         I->Image[i] = nullptr;
317       }
318     }
319   }
320   return result;
321 }
322 
MoviePurgeFrame(PyMOLGlobals * G,int frame)323 int MoviePurgeFrame(PyMOLGlobals * G, int frame)
324 {
325   CMovie *I = G->Movie;
326   int result = false;
327   int nFrame;
328   int i;
329   nFrame = I->NFrame;
330   if(!nFrame) {
331     nFrame = SceneGetNFrame(G, NULL);
332   }
333   if(!I->CacheSave) {
334     if(frame < nFrame) {
335       int a = frame;
336       i = MovieFrameToImage(G, a);
337       VecCheck(I->Image, i);
338       if(I->Image[i]) {
339         I->Image[i] = nullptr;
340         result = true;
341       }
342     }
343   }
344   return result;
345 }
346 
MovieCopyFinish(PyMOLGlobals * G)347 void MovieCopyFinish(PyMOLGlobals * G)
348 {
349   CMovie *I = G->Movie;
350   SceneInvalidate(G);           /* important */
351   SettingSetGlobal_b(G, cSetting_cache_frames, I->CacheSave);
352   SettingSetGlobal_i(G, cSetting_overlay, I->OverlaySave);
353   MoviePlay(G, cMovieStop);
354   if(!I->CacheSave) {
355     MovieClearImages(G);
356   }
357 }
358 
MovieLocked(PyMOLGlobals * G)359 int MovieLocked(PyMOLGlobals * G)
360 {
361   CMovie *I = G->Movie;
362   return (I->Locked);
363 }
364 
MovieSetLock(PyMOLGlobals * G,int lock)365 void MovieSetLock(PyMOLGlobals * G, int lock)
366 {
367   CMovie *I = G->Movie;
368   I->Locked = lock;
369 }
370 
371 
372 /*========================================================================*/
MovieDump(PyMOLGlobals * G)373 void MovieDump(PyMOLGlobals * G)
374 {
375   int a;
376   CMovie *I = G->Movie;
377   int flag = false;
378 
379   for(a = 0; a < I->NFrame; a++) {
380     if(!I->Cmd[a].empty()) {
381       flag = true;
382       break;
383     }
384   }
385   if(flag && I->NFrame) {
386     PRINTFB(G, FB_Movie, FB_Results)
387       " Movie: General Purpose Commands:\n" ENDFB(G);
388     for(a = 0; a < I->NFrame; a++) {
389       if(!I->Cmd[a].empty()) {
390         auto buffer = pymol::string_format("%5d: %s\n", a + 1, I->Cmd[a]);
391         OrthoAddOutput(G, buffer.c_str());
392       }
393     }
394   } else {
395     PRINTFB(G, FB_Movie, FB_Results)
396       " Movie: No movie commands are defined.\n" ENDFB(G);
397   }
398 }
399 
MovieCmdFromPyList(PyMOLGlobals * G,PyObject * list,int * warning)400 static int MovieCmdFromPyList(PyMOLGlobals * G, PyObject * list, int *warning)
401 {
402 
403   CMovie *I = G->Movie;
404   int ok = true;
405   int a;
406   int warn = false;
407 
408   if(ok)
409     ok = (list != NULL);
410   if(ok)
411     ok = PyList_Check(list);
412 
413   for(a = 0; a < I->NFrame; a++) {
414     if(ok)
415       ok = PConvFromPyListItem(G, list, a, I->Cmd[a]);
416     if(ok)
417       warn = (warn || !I->Cmd[a].empty());
418   }
419   *warning = warn;
420   return (ok);
421 
422 }
423 
424 /*========================================================================*/
MovieFromPyList(PyMOLGlobals * G,PyObject * list,int * warning)425 int MovieFromPyList(PyMOLGlobals * G, PyObject * list, int *warning)
426 {
427   int ok = true;
428   CMovie *I = G->Movie;
429   int ll = 0;
430 
431   MovieReset(G);
432   if(ok)
433     ok = PyList_Check(list);
434   if(ok)
435     ll = PyList_Size(list);
436   /* TO SUPPORT BACKWARDS COMPATIBILITY...
437      Always check ll when adding new PyList_GetItem's */
438   if(ok)
439     ok = PConvPyIntToInt(PyList_GetItem(list, 0), &I->NFrame);
440   if(ok)
441     ok = PConvPyIntToInt(PyList_GetItem(list, 1), &I->MatrixFlag);
442   if(ok && I->MatrixFlag)
443     ok =
444       PConvPyListToFloatArrayInPlace(PyList_GetItem(list, 2), I->Matrix, cSceneViewSize);
445   if(ok)
446     ok = PConvPyIntToInt(PyList_GetItem(list, 3), &I->Playing);
447   if(ok && I->NFrame) {
448     I->Sequence = pymol::vla<int>(I->NFrame);
449     I->Cmd = std::vector<std::string>(I->NFrame);
450     if(ok)
451       ok = PConvPyListToIntArrayInPlace(PyList_GetItem(list, 4), I->Sequence.data(), I->NFrame);
452     if(ok)
453       ok = MovieCmdFromPyList(G, PyList_GetItem(list, 5), warning);
454     if((*warning) && G->Security) {
455       MovieSetLock(G, true);
456     }
457   }
458   if(ok && (ll > 6)) {
459     PyObject *tmp;
460     VLAFreeP(I->ViewElem);
461     I->ViewElem = NULL;
462     tmp = PyList_GetItem(list, 6);
463     if(tmp && !(tmp == Py_None))
464       ok = ViewElemVLAFromPyList(G, tmp, &I->ViewElem, I->NFrame);
465   }
466   if(!ok) {
467     MovieReset(G);
468   } else if(MovieDefined(G)) {
469     OrthoReshape(G,-1,-1,true);
470     SceneCountFrames(G);
471   }
472   return (ok);
473 }
474 
475 
476 /*========================================================================*/
MovieCmdAsPyList(PyMOLGlobals * G)477 static PyObject *MovieCmdAsPyList(PyMOLGlobals * G)
478 {
479 
480   CMovie *I = G->Movie;
481   PyObject *result = NULL;
482   int a;
483 
484   result = PyList_New(I->NFrame);
485   if(result)
486     for(a = 0; a < I->NFrame; a++) {
487       PyList_SetItem(result, a, PyString_FromString(I->Cmd[a].data()));
488     }
489   return (PConvAutoNone(result));
490 
491 }
492 
493 /*========================================================================*/
MovieAsPyList(PyMOLGlobals * G)494 PyObject *MovieAsPyList(PyMOLGlobals * G)
495 {
496   CMovie *I = G->Movie;
497   PyObject *result = NULL;
498 
499   result = PyList_New(7);
500   PyList_SetItem(result, 0, PyInt_FromLong(I->NFrame));
501   PyList_SetItem(result, 1, PyInt_FromLong(I->MatrixFlag));
502   PyList_SetItem(result, 2, PConvFloatArrayToPyList(I->Matrix, cSceneViewSize));
503   PyList_SetItem(result, 3, PyInt_FromLong(I->Playing));
504   if(I->Sequence) {
505     PyList_SetItem(result, 4, PConvIntArrayToPyList(I->Sequence, I->NFrame));
506   } else {
507     PyList_SetItem(result, 4, PConvAutoNone(NULL));
508   }
509   if(!I->Cmd.empty()) {
510     PyList_SetItem(result, 5, MovieCmdAsPyList(G));
511   } else {
512     PyList_SetItem(result, 5, PConvAutoNone(NULL));
513   }
514   if(I->ViewElem) {
515     PyList_SetItem(result, 6, ViewElemVLAAsPyList(G, I->ViewElem, I->NFrame));
516   } else {
517     PyList_SetItem(result, 6, PConvAutoNone(NULL));
518   }
519 
520 
521   /*   pymol::Image *Image;
522        int *Sequence;
523        MovieCmdType *Cmd;
524        int NImage,NFrame;
525        unsigned Width,Height;
526        int MatrixFlag;
527        float Matrix[16];
528        int Playing;
529   */
530   return (PConvAutoNone(result));
531 }
532 
533 
534 /*========================================================================*/
MoviePlaying(PyMOLGlobals * G)535 int MoviePlaying(PyMOLGlobals * G)
536 {
537   CMovie *I = G->Movie;
538   if(I->Locked)
539     return false;
540   if(I->Playing && G->Interrupt) {
541     I->Playing = false;
542   }
543   return (I->Playing || I->RecursionFlag);
544   /* returns true if movie is playing OR if we're in the process of evaluating
545      movie commands */
546 }
547 
548 
549 /*========================================================================*/
MoviePlay(PyMOLGlobals * G,int cmd)550 void MoviePlay(PyMOLGlobals * G, int cmd)
551 {
552   CMovie *I = G->Movie;
553   switch (cmd) {
554   case cMovieToggle:
555     I->Playing = !I->Playing;
556     if(I->Playing && !SettingGetGlobal_b(G, cSetting_movie_loop)) {
557       /* if not looping, and at end of movie, then automatically rewind
558          and force execution of the first movie command */
559       if((SettingGetGlobal_i(G, cSetting_frame)) == (SceneGetNFrame(G, NULL))) {
560         SceneSetFrame(G, 7, 0);
561       }
562     }
563     break;
564   case cMovieStop:
565     I->Playing = false;
566     break;
567   case cMoviePlay:
568     if(!SettingGetGlobal_b(G, cSetting_movie_loop)) {
569       /* if not looping, and at end of movie, then automatically rewind
570          and force execution of the first movie command */
571       if((SettingGetGlobal_i(G, cSetting_frame)) == (SceneGetNFrame(G, NULL))) {
572         SceneSetFrame(G, 7, 0);
573       }
574     }
575     I->Playing = true;
576     break;
577   }
578   OrthoDirty(G);
579   SceneRestartFrameTimer(G);
580 }
581 
582 
583 /*========================================================================*/
MovieMatrix(PyMOLGlobals * G,int action)584 int MovieMatrix(PyMOLGlobals * G, int action)
585 {
586   CMovie *I = G->Movie;
587   int result = false;
588   switch (action) {
589   case cMovieMatrixClear:
590     I->MatrixFlag = false;
591     result = 1;
592     break;
593   case cMovieMatrixStore:
594     SceneGetView(G, I->Matrix);
595     I->MatrixFlag = true;
596     result = 1;
597     break;
598   case cMovieMatrixRecall:
599     if(I->MatrixFlag) {
600       SceneSetView(G, I->Matrix, true, 0, 0);
601       result = 1;
602     } else
603       result = 0;
604     break;
605   case cMovieMatrixCheck:
606     result = I->MatrixFlag;
607     break;
608   }
609   return result;
610 }
611 
612 
613 /*========================================================================*/
MovieSetSize(PyMOLGlobals * G,unsigned int width,unsigned int height)614 void MovieSetSize(PyMOLGlobals * G, unsigned int width, unsigned int height)
615 {
616   return;
617 }
618 
619 
620 /*========================================================================*/
MovieModalPNG(PyMOLGlobals * G,CMovie * I,CMovieModal * M)621 static void MovieModalPNG(PyMOLGlobals * G, CMovie * I, CMovieModal * M)
622 {
623   switch (M->stage) {
624   case 0:                      /* setup */
625     MovieSetRealtime(G, false);
626     M->save = SettingGetGlobal_b(G, cSetting_cache_frames);
627     if(!M->save)
628       MovieClearImages(G);
629     SettingSetGlobal_b(G, cSetting_cache_frames, 1);
630     OrthoBusyPrime(G);
631     M->nFrame = I->NFrame;
632     if(!M->nFrame) {
633       M->nFrame = SceneGetNFrame(G, NULL);
634       if(M->nFrame < 1) {
635         M->nFrame = 1;
636       }
637     }
638     if(M->start < 0)
639       M->start = 0;
640     if(M->start > M->nFrame)
641       M->start = M->nFrame;
642     if(M->stop < 0)
643       M->stop = M->nFrame;
644     if(M->stop > M->nFrame)
645       M->stop = M->nFrame;
646     {
647       auto buffer = pymol::string_format("Creating movie (%d frames)...", M->nFrame);
648       OrthoBusyMessage(G, buffer.c_str());
649     }
650     if((M->start != 0) || (M->stop != (M->nFrame + 1)))
651       SceneSetFrame(G, 0, 0);
652     MoviePlay(G, cMoviePlay);
653     VecCheck(I->Image, M->nFrame);
654     M->frame = 0;
655     M->stage = 1;
656     if(G->Interrupt) {
657       M->stage = 5;             /* abort */
658     }
659     break;
660   case 1:                      /* RENDER LOOP: advance a frame */
661     if(M->frame < M->nFrame) {
662 
663       M->file_missing = true;
664       M->timing = UtilGetSeconds(G);    /* start timing the process */
665 
666       PRINTFB(G, FB_Movie, FB_Debugging)
667         " MoviePNG-DEBUG: Cycle %d...\n", M->frame ENDFB(G);
668       switch (M->format) {
669       case cMyPNG_FormatPPM:
670         M->fname = pymol::string_format("%s%04d.ppm", M->prefix.c_str(), M->frame + 1);
671         break;
672       case cMyPNG_FormatPNG:
673       default:
674         M->fname = pymol::string_format("%s%04d.png", M->prefix.c_str(), M->frame + 1);
675         break;
676       }
677 
678       if(M->missing_only) {
679         FILE *tmp = fopen(M->fname.c_str(), "rb");
680         if(tmp) {
681           fclose(tmp);
682           M->file_missing = false;
683         } else {
684           M->file_missing = true;
685         }
686       }
687       SceneSetFrame(G, 0, M->frame);
688       MovieDoFrameCommand(G, M->frame);
689       MovieFlushCommands(G);
690 
691       M->image = MovieFrameToImage(G, M->frame);
692       M->stage = 2;
693       if(G->Interrupt) {
694         M->stage = 5;           /* abort */
695       }
696     }
697     break;
698   }
699 
700   switch (M->stage) {
701   case 2:                      /* IN RENDER LOOP: create the image */
702     VecCheck(I->Image, M->image);
703     if((M->frame >= M->start) &&        /* only render frames in the specified interval... */
704        (M->frame <= M->stop) && (M->file_missing)) {    /* ...that don't already exist */
705       if(!I->Image[M->image]) {
706         SceneUpdate(G, false);
707         if(SceneMakeMovieImage(G, false, M->modal, M->mode, M->width, M->height)
708             || !M->modal) {
709           M->stage = 3;
710         } else {
711           /* didn't get an image... */
712           PRINTFB(G, FB_Movie, FB_Errors)
713             " MoviePNG-Error: unable to obtain a valid OpenGL image.  Trying again...\n"
714             ENDFB(G);
715         }
716       } else {                  /* frame already rendered */
717         M->stage = 3;
718       }
719     } else {                    /* we don't need to render this frame */
720       M->stage = 4;
721     }
722     if(G->Interrupt) {
723       M->stage = 5;             /* abort */
724     }
725     break;
726   }
727 
728   switch (M->stage) {
729   case 3:                      /* IN RENDER LOOP: have image, so write to file */
730     if(!I->Image[M->image]) {
731       PRINTFB(G, FB_Movie, FB_Errors)
732         "MoviePNG-Error: Missing rendered image.\n" ENDFB(G);
733     } else {
734       if (!MyPNGWrite(M->fname.c_str(), *I->Image[M->image],
735               SettingGetGlobal_f(G, cSetting_image_dots_per_inch), M->format,
736               M->quiet, SettingGetGlobal_f(G, cSetting_png_screen_gamma),
737               SettingGetGlobal_f(G, cSetting_png_file_gamma))) {
738         PRINTFB(G, FB_Movie, FB_Errors)
739           " MoviePNG-Error: unable to write '%s'\n", M->fname.c_str() ENDFB(G);
740       }
741       ExecutiveDrawNow(G);
742       OrthoBusySlow(G, M->frame, M->nFrame);
743       if(G->HaveGUI)
744         PyMOL_SwapBuffers(G->PyMOL);
745       PRINTFB(G, FB_Movie, FB_Debugging)
746         " MoviePNG-DEBUG: i = %d, I->Image[image] = %p\n", M->image,
747         I->Image[M->image]->bits() ENDFB(G);
748     }
749     if(I->Image[M->image]) {
750       I->Image[M->image] = nullptr;
751     }
752     M->timing = UtilGetSeconds(G) - M->timing;
753     M->accumTiming += M->timing;
754     {
755       double est1 = (M->nFrame - M->frame) * M->timing;
756       double est2 = ((M->nFrame - M->frame) / (float) (M->frame + 1)) * M->accumTiming;
757 
758       PRINTFB(G, FB_Movie, FB_Details)
759         " Movie: frame %4d of %4d, %4.2f sec. (%d:%02d:%02d - %d:%02d:%02d to go).\n",
760         M->frame + 1, M->nFrame,
761         M->timing,
762         (int) (est1 / 3600),
763         ((int) (est1 / 60)) % 60,
764         ((int) est1) % 60,
765         (int) (est2 / 3600), ((int) (est2 / 60)) % 60, ((int) est2) % 60 ENDFB(G);
766     }
767     M->stage = 4;
768     if(G->Interrupt) {
769       M->stage = 5;             /* abort */
770     }
771     break;
772   }
773 
774   switch (M->stage) {
775   case 4:                      /* IN RENDER LOOP: advance to next frame or kick out when done */
776     M->frame++;
777     if(M->frame >= M->nFrame) {
778       M->stage = 5;
779     } else {
780       M->stage = 1;             /* RESTART LOOP */
781     }
782     if(G->Interrupt) {
783       M->stage = 5;             /* abort */
784     }
785     break;
786   }
787 
788   switch (M->stage) {
789   case 5:                      /* finish up */
790 
791     SceneInvalidate(G);         /* important */
792     PRINTFB(G, FB_Movie, FB_Debugging)
793       " MoviePNG-DEBUG: done.\n" ENDFB(G);
794     SettingSetGlobal_b(G, cSetting_cache_frames, M->save);
795     MoviePlay(G, cMovieStop);
796     MovieClearImages(G);
797     MovieSetRealtime(G, true);
798     M->complete = true;
799     M->stage = 6;
800     break;
801   }
802 }
803 
804 static void MovieModalDraw(PyMOLGlobals * G);
805 
MovieModalDraw(PyMOLGlobals * G)806 static void MovieModalDraw(PyMOLGlobals * G)
807 {
808   CMovie *I = G->Movie;
809   MovieModalPNG(G, I, &I->Modal);
810   if(!I->Modal.complete)        /* force modalic return until job is done */
811     PyMOL_SetModalDraw(G->PyMOL, (PyMOLModalDrawFn *) MovieModalDraw);
812 }
813 
MoviePNG(PyMOLGlobals * G,const char * prefix,int save,int start,int stop,int missing_only,int modal,int format,int mode,int quiet,int width,int height)814 int MoviePNG(PyMOLGlobals * G, const char* prefix, int save, int start,
815              int stop, int missing_only, int modal, int format, int mode, int quiet,
816              int width, int height)
817 {
818   /* assumes locked api, blocked threads, and master thread on entry */
819   CMovie *I = G->Movie;
820 
821   /* new routine allows PyMOL to workaround XP and Vista Windowing
822      issues */
823 
824   CMovieModal *M = &I->Modal;
825 
826   *M = CMovieModal();
827 
828   mode = SceneValidateImageMode(G, mode, width || height);
829 
830   /* default behavior is to go modal unless we're ray tracing */
831   if(modal < 0 && mode == cSceneImage_Ray) {
832     modal = 0;
833   }
834 
835   M->prefix = prefix;
836   M->save = save;
837   M->start = start;
838   M->stop = stop;
839   M->missing_only = missing_only;
840   M->stage = 0;
841   M->format = format;
842   M->mode = mode;
843   M->quiet = quiet;
844   M->width = width;
845   M->height = height;
846 
847   if(SettingGetGlobal_b(G, cSetting_seq_view)) {
848     PRINTFB(G, FB_Movie, FB_Warnings)
849       " MoviePNG-Warning: disabling seq_view, may conflict with movie export\n" ENDFB(G);
850     SettingSetGlobal_b(G, cSetting_seq_view, 0);
851     // force viewport update
852     SeqChanged(G);
853     OrthoDoDraw(G, 0);
854   }
855 
856   M->modal = modal;
857 
858   if(modal) {
859     PyMOL_SetModalDraw(G->PyMOL, (PyMOLModalDrawFn *) MovieModalDraw);
860   } else {
861     while(!M->complete) {
862       MovieModalPNG(G, I, &I->Modal);
863     }
864   }
865   return true;
866 }
867 
868 
869 /*========================================================================*/
MovieAppendSequence(PyMOLGlobals * G,const char * str,int start_from,int freeze)870 void MovieAppendSequence(PyMOLGlobals * G, const char *str, int start_from,int freeze)
871 {
872   CMovie *I = G->Movie;
873   int c = 0;
874   int i;
875   /*  int old_NFrame = I->NFrame; */
876   const char *s;
877   char number[20];
878 
879   if(start_from < 0)
880     start_from = I->NFrame;
881 
882   c = start_from;
883 
884   PRINTFB(G, FB_Movie, FB_Debugging)
885     " MovieSequence: entered. str:%s\n", str ENDFB(G);
886 
887   s = str;
888   while(*s) {
889     s = ParseWord(number, s, 20);
890     if(sscanf(number, "%i", &i)) {      /* slow */
891       c++;
892     }
893   }
894 
895   if(!c) {
896     VLAFreeP(I->Sequence);
897     I->Cmd.clear();
898     VLAFreeP(I->ViewElem);
899     I->NFrame = 0;
900   } else {
901     // to clear
902     I->Sequence.resize(start_from);
903     I->Cmd.resize(start_from);
904     I->ViewElem.resize(start_from);
905 
906     // append (c - start_from) zero-initialized elements
907     I->Sequence.resize(c);
908     I->Cmd.resize(c);
909     I->ViewElem.resize(c);
910   }
911 
912   if(c && str[0]) {             /* not just a reset */
913     for(i = start_from; i < c; i++)
914       I->Cmd[i].clear();
915     c = start_from;
916     s = str;
917     while(*s) {
918       s = ParseWord(number, s, 20);
919       if(sscanf(number, "%i", &I->Sequence[c])) {
920         c++;
921       }
922     }
923     I->NFrame = c;
924   } else if(!str[0]) {
925     I->NFrame = start_from;
926   }
927 
928   // fixes PYMOL-2710
929   MovieClearImages(G);
930 
931   I->Image.resize(I->NFrame);
932   PRINTFB(G, FB_Movie, FB_Debugging)
933     " MovieSequence: leaving... I->NFrame%d\n", I->NFrame ENDFB(G);
934 
935   if(!freeze) {
936     if(SettingGetGlobal_b(G,cSetting_movie_auto_interpolate))
937       ExecutiveMotionReinterpolate(G);
938   }
939   ExecutiveCountMotions(G);
940 }
941 
942 
943 /*========================================================================*/
MovieFrameToImage(PyMOLGlobals * G,int frame)944 int MovieFrameToImage(PyMOLGlobals * G, int frame)
945 {
946   int result = 0;
947   int single_image = SettingGetGlobal_b(G, cSetting_single_image);
948   if(single_image)
949     result = MovieFrameToIndex(G, frame);
950   else
951     result = frame;
952   PRINTFB(G, FB_Movie, FB_Debugging)
953     " MovieFrameToImage-DEBUG: result %d\n", result ENDFB(G);
954   return (result);
955 }
956 
957 
958 /*========================================================================*/
MovieFrameToIndex(PyMOLGlobals * G,int frame)959 int MovieFrameToIndex(PyMOLGlobals * G, int frame)
960 {
961   CMovie *I = G->Movie;
962   if(I->Sequence && I->NFrame) {
963     if(frame >= I->NFrame) {
964       frame = I->NFrame - 1;
965     }
966     if(I->ViewElem && I->ViewElem[frame].state_flag) {
967       return I->ViewElem[frame].state;
968     }
969     return (I->Sequence[frame]);
970   } else {
971     return (frame);
972   }
973 }
974 /*========================================================================*/
MovieSetImage(PyMOLGlobals * G,int index,std::shared_ptr<pymol::Image> image)975 void MovieSetImage(PyMOLGlobals * G, int index, std::shared_ptr<pymol::Image> image)
976 {
977   CMovie *I = G->Movie;
978 
979   PRINTFB(G, FB_Movie, FB_Blather)
980     " MovieSetImage: setting movie image %d\n", index + 1 ENDFB(G);
981 
982   VecCheck(I->Image, index);
983   I->Image[index] = image;
984   if(I->NImage < (index + 1))
985     I->NImage = index + 1;
986 }
987 
MovieSeekScene(PyMOLGlobals * G,int loop)988 int MovieSeekScene(PyMOLGlobals * G, int loop)
989 {
990   CMovie *I = G->Movie;
991   int result = -1;
992   OVreturn_word ret;
993   const char *scene_name = SettingGetGlobal_s(G,cSetting_scene_current_name);
994   if(OVreturn_IS_OK
995      ((ret = OVLexicon_BorrowFromCString
996        (G->Lexicon, scene_name)))) {
997     if(I->ViewElem) {
998       int i,len = MovieGetLength(G);
999       for(i = SceneGetFrame(G); i < len; i++) {
1000 	if(I->ViewElem[i].scene_flag) {
1001 	  if(I->ViewElem[i].scene_name == ret.word) {
1002 	    result = i;
1003 	    break;
1004 	  }
1005 	}
1006       }
1007       if(loop) {
1008 	len = SceneGetFrame(G);
1009 	for(i = 0; i < len; i++ ) {
1010 	  if(I->ViewElem[i].scene_flag) {
1011 	    if(I->ViewElem[i].scene_name == ret.word) {
1012 	      result = i;
1013 	      break;
1014 	    }
1015 	  }
1016 	}
1017       }
1018     }
1019   }
1020 
1021   return result;
1022 }
1023 
1024 /*========================================================================*/
MovieDoFrameCommand(PyMOLGlobals * G,int frame)1025 void MovieDoFrameCommand(PyMOLGlobals * G, int frame)
1026 {
1027   CMovie *I = G->Movie;
1028   if(frame == 0)
1029     MovieMatrix(G, cMovieMatrixRecall);
1030   if(!I->Locked) {
1031     if((frame >= 0) && (frame < I->NFrame)) {
1032       if(!I->Cmd[frame].empty()) {
1033         if(!I->RecursionFlag) {
1034           PParse(G, I->Cmd[frame].data());
1035         }
1036       }
1037       if(I->ViewElem) {
1038         if(I->ViewElem[frame].scene_flag) {
1039           char *st = OVLexicon_FetchCString(G->Lexicon, I->ViewElem[frame].scene_name);
1040           if(strcmp(st, SettingGetGlobal_s(G, cSetting_scene_current_name))) {
1041             MovieSceneRecall(G, st, 0.0,
1042                 /* view */ false, true, true, true,
1043                 /* frame */ false);
1044           }
1045         }
1046         SceneFromViewElem(G, I->ViewElem + frame, true);
1047       }
1048     }
1049   }
1050 }
1051 
1052 
1053 /*========================================================================*/
MovieSetCommand(PyMOLGlobals * G,int frame,const char * command)1054 void MovieSetCommand(PyMOLGlobals* G, int frame, const char* command)
1055 {
1056   CMovie *I = G->Movie;
1057   if((frame >= 0) && (frame < I->NFrame)) {
1058     I->Cmd[frame] = command;
1059   } else {
1060     PRINTFB(G, FB_Movie, FB_Errors)
1061       " Movie-Error: frame %d does not exist.  Use 'mset' to define movie first.\n",
1062       frame + 1 ENDFB(G);
1063   }
1064 }
1065 
1066 
1067 /*========================================================================*/
MovieView(PyMOLGlobals * G,int action,int first,int last,float power,float bias,int simple,float linear,int wrap,int hand,int window,int cycles,const char * scene_name,float scene_cut,int state,int quiet)1068 int MovieView(PyMOLGlobals * G, int action, int first,
1069               int last, float power, float bias,
1070               int simple, float linear, int wrap,
1071               int hand, int window, int cycles,
1072               const char *scene_name, float scene_cut, int state, int quiet)
1073 {
1074   CMovie *I = G->Movie;
1075   int frame;
1076 
1077   if(wrap<0) {
1078     wrap = SettingGetGlobal_b(G,cSetting_movie_loop);
1079   }
1080   if((action == 7) || (action == 8)) { /* toggle */
1081     frame = first;
1082     if(first < 0)
1083       frame = SceneGetFrame(G);
1084     VLACheck(I->ViewElem, CViewElem, frame);
1085     if(action == 7) {
1086       if(I->ViewElem[frame].specification_level>1) {
1087         action = 1;
1088       } else {
1089         action = 0;
1090       }
1091     } else if(action == 8) {
1092       if(I->ViewElem[frame].specification_level>1) {
1093         int frame;
1094         action = 3;
1095         for(frame=0;frame<I->NFrame;frame++) {
1096           if(I->ViewElem[frame].specification_level==1) {
1097             action = 6;
1098             break;
1099           }
1100         }
1101       }
1102       else if(I->ViewElem[frame].specification_level>0) {
1103         action = 6;
1104       } else {
1105         action = 3;
1106       }
1107     }
1108   }
1109   if(action == 4) {
1110     if(I->ViewElem) {
1111       int save_last = last;
1112       if(first < 0)
1113         first = 0;
1114 
1115       if(last < 0) {
1116         last = SceneGetNFrame(G, NULL) - 1;
1117       }
1118       if(last >= I->NFrame) {
1119         last = I->NFrame - 1;
1120       }
1121       if(first <= last) {
1122         int a;
1123         VLACheck(I->ViewElem, CViewElem, last);
1124         for(a = 0; a < cycles; a++) {
1125           ViewElemSmooth(I->ViewElem + first, I->ViewElem + last, window, wrap);
1126         }
1127       }
1128       if(SettingGetGlobal_b(G, cSetting_movie_auto_interpolate)) {
1129         action = 3; /* reinterpolate */
1130         last = save_last;
1131       }
1132     }
1133   }
1134   switch (action) {
1135   case 0:                      /* store */
1136     if(I->ViewElem) {
1137       if(first < 0)
1138         first = SceneGetFrame(G);
1139       if(last < 0)
1140         last = first;
1141 
1142       VLACheck(I->ViewElem, CViewElem, last);
1143 
1144       for(frame = first; frame <= last; frame++) {
1145         if((frame >= 0) && (frame < I->NFrame)) {
1146           VLACheck(I->ViewElem, CViewElem, frame);
1147           if(!quiet) {
1148             PRINTFB(G, FB_Movie, FB_Details)
1149               " MovieView: Setting frame %d.\n", frame + 1 ENDFB(G);
1150           }
1151           if(scene_name && (!scene_name[0]))
1152             scene_name = NULL;
1153           SceneToViewElem(G, I->ViewElem + frame, scene_name);
1154           if(state>=0) {
1155             I->ViewElem[frame].state = state;
1156             I->ViewElem[frame].state_flag = true;
1157           } else if(state==-2) {
1158             I->ViewElem[frame].state = SceneGetState(G);
1159             I->ViewElem[frame].state_flag = true;
1160           }
1161 
1162           if(power!=0.0F) {
1163             I->ViewElem[frame].power_flag = true;
1164             I->ViewElem[frame].power = power;
1165           }
1166 
1167           if(bias > 0.0F) {
1168             I->ViewElem[frame].bias_flag = true;
1169             I->ViewElem[frame].bias = bias;
1170           }
1171 
1172           I->ViewElem[frame].specification_level = 2;
1173         }
1174       }
1175     }
1176     break;
1177   case 1:                      /* clear */
1178     if(I->ViewElem) {
1179       if(first < 0)
1180         first = SceneGetFrame(G);
1181       if(last < 0)
1182         last = first;
1183       for(frame = first; frame <= last; frame++) {
1184         if((frame >= 0) && (frame < I->NFrame)) {
1185           VLACheck(I->ViewElem, CViewElem, frame);
1186           ViewElemArrayPurge(G, I->ViewElem + frame, 1);
1187           UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
1188         }
1189       }
1190     }
1191     break;
1192   case 2:                      /* interpolate & reinterpolate */
1193   case 3:
1194     if(I->ViewElem) {
1195       int view_found = false;
1196       CViewElem *first_view = NULL, *last_view = NULL;
1197       if(first < 0)
1198         first = 0;
1199 
1200       if(first > I->NFrame) {
1201         first = I->NFrame - 1;
1202       }
1203       /* note that we're leaving a blank frame at the end... */
1204       if(last < 0) {
1205         last = MovieGetLength(G);
1206 
1207         if(last) {
1208           if(!wrap)
1209             last--;
1210           else {
1211             int frame = 0;
1212             VLACheck(I->ViewElem, CViewElem, last);
1213             for(frame = 0; frame < last; frame++) {
1214               if(I->ViewElem[frame].specification_level > 1) {
1215                 last += frame;
1216                 break;
1217               }
1218             }
1219           }
1220         }
1221       } else if(last >= I->NFrame) {
1222         last = I->NFrame;
1223         if(last && (!wrap))
1224           last--;
1225       }
1226 
1227       VLACheck(I->ViewElem, CViewElem, last);
1228 
1229       if(wrap && (last >= I->NFrame)) {
1230         /* if we're interpolating beyond the last frame, then wrap by
1231            copying early frames to last frames */
1232         int a;
1233         for(a = I->NFrame; a <= last; a++) {
1234           ViewElemCopy(G, I->ViewElem + a - I->NFrame, I->ViewElem + a);
1235         }
1236       } else if(!wrap) {
1237         /* if we're not wrapping, then make sure we nuke any stray / old
1238            interpolated frames */
1239         frame = I->NFrame - 1;
1240         while(frame>=0) {
1241           if(I->ViewElem[frame].specification_level > 1)
1242             break;
1243           else
1244             UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
1245           frame--;
1246         }
1247       }
1248 
1249       if(!quiet) {
1250         if(action == 2) {
1251           if(last == I->NFrame) {
1252             PRINTFB(G, FB_Movie, FB_Details)
1253               " MovieView: interpolating unspecified frames %d to %d (wrapping)\n",
1254               first + 1, last ENDFB(G);
1255           } else {
1256             PRINTFB(G, FB_Movie, FB_Details)
1257               " MovieView: interpolating unspecified frames %d to %d.\n", first + 1,
1258               last + 1 ENDFB(G);
1259           }
1260 
1261         } else {
1262           if(last == I->NFrame) {
1263             PRINTFB(G, FB_Movie, FB_Details)
1264               " MovieView: reinterpolating all frames %d to %d (wrapping).\n", first + 1,
1265               last ENDFB(G);
1266           } else {
1267             PRINTFB(G, FB_Movie, FB_Details)
1268               " MovieView: reinterpolating all frames %d to %d.\n", first + 1, last + 1
1269               ENDFB(G);
1270           }
1271         }
1272       }
1273 
1274       for(frame = first; frame <= last; frame++) {
1275         if(!first_view) {
1276           if(I->ViewElem[frame].specification_level == 2) {     /* specified */
1277             first_view = I->ViewElem + frame;
1278             view_found = true;
1279           }
1280         } else {
1281           CViewElem *view;
1282           int interpolate_flag = false;
1283           if(I->ViewElem[frame].specification_level == 2) {     /* specified */
1284             last_view = I->ViewElem + frame;
1285             if(action == 2) {   /* interpolate */
1286               for(view = first_view + 1; view < last_view; view++) {
1287                 if(!view->specification_level)
1288                   interpolate_flag = true;
1289               }
1290             } else {
1291               interpolate_flag = true;
1292             }
1293             if(interpolate_flag) {
1294               ViewElemInterpolate(G, first_view, last_view,
1295                                   power, bias, simple, linear, hand, scene_cut);
1296             }
1297             first_view = last_view;
1298             last_view = NULL;
1299           }
1300         }
1301       }
1302 
1303       if(first_view) {
1304         if(wrap && (last >= I->NFrame)) {
1305           /* if we're interpolating beyond the last frame, then wrap by
1306              copying the last frames back over the early frames */
1307           int a;
1308           for(a = I->NFrame; a <= last; a++) {
1309             ViewElemCopy(G, I->ViewElem + a, I->ViewElem + a - I->NFrame);
1310           }
1311         }
1312       }
1313 
1314       if((!view_found) && (last>=first) && (first>=0) && (last<=I->NFrame)) {
1315         UtilZeroMem(I->ViewElem + first, sizeof(CViewElem) * (1 + (last-first)));
1316       }
1317 
1318       if(last >= I->NFrame) {   /* now erase temporary views */
1319         ViewElemArrayPurge(G, I->ViewElem + I->NFrame, (1 + last - I->NFrame));
1320         UtilZeroMem((void *) (I->ViewElem + I->NFrame),
1321                     sizeof(CViewElem) * (1 + last - I->NFrame));
1322       }
1323     }
1324     break;
1325   case 5:                      /* reset */
1326     if(I->ViewElem) {
1327       int size = VLAGetSize(I->ViewElem);
1328       I->ViewElem = pymol::vla<CViewElem>(size);
1329     }
1330     break;
1331   case 6:                      /* uninterpolate */
1332     if(I->ViewElem) {
1333       if(first < 0)
1334         first = 0;
1335       if(last < 0) {
1336         last = SceneGetNFrame(G, NULL) - 1;
1337       }
1338       for(frame = first; frame <= last; frame++) {
1339         if((frame >= 0) && (frame < I->NFrame)) {
1340           VLACheck(I->ViewElem, CViewElem, frame);
1341           if(I->ViewElem[frame].specification_level < 2) {
1342             ViewElemArrayPurge(G, I->ViewElem + frame, 1);
1343             UtilZeroMem((void *) (I->ViewElem + frame), sizeof(CViewElem));
1344           }
1345         }
1346       }
1347     }
1348     break;
1349   }
1350   /* adjust VLA to current movie length */
1351   if(I->ViewElem) {
1352     VLASize(I->ViewElem,CViewElem,I->NFrame);
1353   }
1354   SceneSetFrame(G,1,0); /* force frame update */
1355   return 1;
1356 }
1357 
1358 
1359 /*========================================================================*/
MovieAppendCommand(PyMOLGlobals * G,int frame,const char * command)1360 void MovieAppendCommand(PyMOLGlobals * G, int frame, const char* command)
1361 {
1362   CMovie *I = G->Movie;
1363   if((frame >= 0) && (frame < I->NFrame)) {
1364     I->Cmd[frame] += command;
1365   } else {
1366     PRINTFB(G, FB_Movie, FB_Errors)
1367       " Movie-Error: frame %d does not exist.  Use 'mset' to define movie first.\n",
1368       frame + 1 ENDFB(G);
1369   }
1370 }
1371 
1372 
1373 /*========================================================================*/
MovieGetImage(PyMOLGlobals * G,int index)1374 std::shared_ptr<pymol::Image> MovieGetImage(PyMOLGlobals * G, int index)
1375 {
1376   CMovie *I = G->Movie;
1377   if((index >= 0) && (index < I->NImage))
1378     return I->Image[index];
1379   else
1380     return nullptr;
1381 }
1382 
1383 
1384 /*========================================================================*/
MovieDefined(PyMOLGlobals * G)1385 int MovieDefined(PyMOLGlobals * G)
1386 {
1387   CMovie *I = G->Movie;
1388   return (I->NFrame > 0);
1389 }
1390 
1391 
1392 /*========================================================================*/
MovieGetLength(PyMOLGlobals * G)1393 int MovieGetLength(PyMOLGlobals * G)
1394 {
1395   CMovie *I = G->Movie;
1396   int len;
1397   if(!I->NFrame)
1398     len = -I->NImage;
1399   else
1400     len = I->NFrame;
1401   return (len);
1402 }
1403 
1404 
1405 /*========================================================================*/
MovieClearImages(PyMOLGlobals * G,CMovie * I)1406 void MovieClearImages(PyMOLGlobals * G, CMovie* I)
1407 {
1408   I->Image.clear();
1409   I->NImage = 0;
1410   SceneInvalidate(G);
1411   SceneSuppressMovieFrame(G);
1412 }
1413 
MovieClearImages(PyMOLGlobals * G)1414 void MovieClearImages(PyMOLGlobals * G)
1415 {
1416   PRINTFB(G, FB_Movie, FB_Blather)
1417     " MovieClearImages: clearing...\n" ENDFB(G);
1418   MovieClearImages(G, G->Movie);
1419 }
1420 
1421 /*========================================================================*/
MovieReset(PyMOLGlobals * G)1422 void MovieReset(PyMOLGlobals * G)
1423 {
1424   CMovie *I = G->Movie;
1425   MovieClearImages(G);
1426 
1427   I->Cmd.clear();
1428   VLAFreeP(I->Sequence);
1429   VLAFreeP(I->ViewElem);
1430 
1431   I->NFrame = 0;
1432   I->MatrixFlag = false;
1433   I->Locked = false;
1434   I->Playing = false;
1435 }
1436 
1437 
1438 /*========================================================================*/
~CMovie()1439 CMovie::~CMovie()
1440 {
1441   MovieClearImages(m_G, this);
1442 }
1443 
MovieGetBlock(PyMOLGlobals * G)1444 Block *MovieGetBlock(PyMOLGlobals * G)
1445 {
1446   return G->Movie;
1447 }
1448 
1449 
MoviePrepareDrag(PyMOLGlobals * G,BlockRect * rect,CObject * obj,int mode,int x,int y,int nearest)1450 void MoviePrepareDrag(PyMOLGlobals *G, BlockRect * rect,
1451                       CObject * obj, int mode, int x, int y, int nearest)
1452 {
1453   CMovie *I = G->Movie;
1454   I->DragMode = mode;
1455   I->DragObj = obj;
1456   I->DragX = x;
1457   I->DragY = y;
1458   I->DragRect = *rect;
1459   if(I->DragColumn) {
1460     I->DragRect.top = I->rect.top - 1;
1461     I->DragRect.bottom = I->rect.bottom + 1;
1462   }
1463   I->DragStartFrame = ViewElemXtoFrame(rect,MovieGetLength(G),x,nearest);
1464   if(I->DragStartFrame > MovieGetLength(G))
1465     I->DragStartFrame = MovieGetLength(G);
1466   I->DragCurFrame = ViewElemXtoFrame(rect,MovieGetLength(G),x,nearest);
1467   I->DragNearest = nearest;
1468 }
1469 
click(int button,int x,int y,int mod)1470 int CMovie::click(int button, int x, int y, int mod)
1471 {
1472   PyMOLGlobals *G = m_G;
1473   CMovie *I = G->Movie;
1474   int count = ExecutiveCountMotions(G);
1475   short scrolldir = 1;
1476   BlockRect tmpRect = rect;
1477   tmpRect.right -= I->LabelIndent;
1478 
1479   switch(button) {
1480   case P_GLUT_RIGHT_BUTTON:
1481     {
1482       int n_frame = MovieGetLength(G);
1483       if(mod == (cOrthoCTRL | cOrthoSHIFT))
1484         I->DragColumn = true;
1485       if(mod == (cOrthoSHIFT))
1486         ExecutiveMotionClick(G,&tmpRect,cMovieDragModeCopyKey,count,x,y,false);
1487       else
1488         ExecutiveMotionClick(G,&tmpRect,cMovieDragModeMoveKey,count,x,y,false);
1489       if(I->DragStartFrame<n_frame) {
1490         I->DragDraw = true;
1491         I->DragMenu = true;
1492         OrthoDirty(G);
1493       } else {
1494         ExecutiveMotionMenuActivate(G,&tmpRect,count,false,x,y,I->DragColumn);
1495       }
1496     }
1497     break;
1498   case P_GLUT_LEFT_BUTTON:
1499     {
1500       switch(mod) {
1501       case cOrthoSHIFT: /* TEMPORAL SELECTIONS -- TO COME in PYMOL 1.3+ */
1502         break;
1503       case (cOrthoSHIFT | cOrthoCTRL):
1504         I->DragColumn = true;
1505         ExecutiveMotionClick(G,&tmpRect,cMovieDragModeInsDel,count,x,y, true);
1506         I->DragDraw = true;
1507         OrthoDirty(G);
1508         break;
1509       case cOrthoCTRL:
1510         ExecutiveMotionClick(G,&tmpRect,cMovieDragModeInsDel,count,x,y, true);
1511         I->DragDraw = true;
1512         OrthoDirty(G);
1513         break;
1514       default:
1515         I->m_ScrollBar.click(button, x, y, mod);
1516         {
1517 	  SceneSetFrame(G, 7, I->m_ScrollBar.getValue());
1518 	}
1519         break;
1520       }
1521     }
1522     break;
1523   case P_GLUT_MIDDLE_BUTTON:
1524     {
1525       switch(mod) {
1526       case (cOrthoCTRL | cOrthoSHIFT):
1527         I->DragColumn = true;
1528         /* intentional fall-through */
1529       case cOrthoCTRL:
1530         I->DragDraw = true;
1531         ExecutiveMotionClick(G,&tmpRect,cMovieDragModeOblate,count,x,y,false);
1532         break;
1533       default:
1534         I->m_ScrollBar.click(button, x, y, mod);
1535         break;
1536       }
1537     }
1538     break;
1539   case P_GLUT_BUTTON_SCROLL_FORWARD:
1540     scrolldir = -1;
1541   case P_GLUT_BUTTON_SCROLL_BACKWARD:
1542     switch(mod) {
1543       case (cOrthoCTRL | cOrthoSHIFT):
1544         SettingSetGlobal_i(G, cSetting_movie_panel_row_height,
1545             SettingGetGlobal_i(G, cSetting_movie_panel_row_height) - scrolldir);
1546         OrthoReshape(G,-1,-1,true);
1547         break;
1548       default:
1549         SceneSetFrame(G, 5, scrolldir);
1550     }
1551     break;
1552   }
1553   return 1;
1554 }
1555 
drag(int x,int y,int mod)1556 int CMovie::drag(int x, int y, int mod)
1557 {
1558   PyMOLGlobals *G = m_G;
1559 
1560   CMovie *I = this; // TODO: Remove all I's in Movie refactor
1561   if(I->DragMode) {
1562     I->DragDraw = ((y < (rect.top + 50)) && (y > (rect.bottom - 50)));
1563     switch(I->DragMode) {
1564     case cMovieDragModeMoveKey:
1565     case cMovieDragModeCopyKey:
1566       {
1567         int n_frame = MovieGetLength(G);
1568         I->DragCurFrame = ViewElemXtoFrame(&I->DragRect,n_frame,x,false);
1569         if(I->DragStartFrame<n_frame) {
1570           if((abs(x-I->DragX)>3) ||
1571              (abs(y-I->DragY)>5)) {
1572             I->DragMenu = false;
1573           }
1574           OrthoDirty(G);
1575         }
1576       }
1577       break;
1578     case cMovieDragModeOblate:
1579       I->DragCurFrame = ViewElemXtoFrame(&I->DragRect,MovieGetLength(G),x,false);
1580       OrthoDirty(G);
1581       break;
1582     case cMovieDragModeInsDel:
1583       I->DragCurFrame = ViewElemXtoFrame(&I->DragRect,MovieGetLength(G),x,true);
1584       OrthoDirty(G);
1585       break;
1586     }
1587   }
1588   return 1;
1589 }
1590 
release(int button,int x,int y,int mod)1591 int CMovie::release(int button, int x, int y, int mod)
1592 {
1593   PyMOLGlobals *G = m_G;
1594   CMovie *I = G->Movie;
1595   I->m_ScrollBar.release(button, x, y, mod);
1596   if(I->DragMode) {
1597     std::string buffer;
1598     std::string extra;
1599 
1600     int n_frame = MovieGetLength(G);
1601 
1602     if(I->DragColumn) {
1603       extra = ",object=''";
1604     } else if(I->DragObj && ExecutiveValidateObjectPtr(G,I->DragObj,0)) {
1605       extra = pymol::string_format(",object='%s'",I->DragObj->Name);
1606     } else {
1607       extra = ",object='none'";
1608     }
1609     switch(I->DragMode) {
1610     case cMovieDragModeMoveKey:
1611       if((I->DragCurFrame == I->DragStartFrame) && (I->DragMenu)) {
1612         int count = ExecutiveCountMotions(G);
1613         BlockRect tmpRect = rect;
1614         tmpRect.right -= I->LabelIndent;
1615         ExecutiveMotionMenuActivate(G,&tmpRect,count,true,x,y,I->DragColumn);
1616         I->DragMenu = false;
1617       } else if(I->DragDraw &&
1618                 (I->DragCurFrame!=I->DragStartFrame) &&
1619                 (I->DragCurFrame >= 0) &&
1620                 (I->DragCurFrame < n_frame)) {
1621         buffer = pymol::string_format("cmd.mmove(%d,%d,%d%s)", 1+I->DragCurFrame, 1+I->DragStartFrame, 1, extra);
1622       }
1623       break;
1624     case cMovieDragModeCopyKey:
1625       if((I->DragCurFrame == I->DragStartFrame) && (I->DragMenu)) {
1626         int count = ExecutiveCountMotions(G);
1627         BlockRect tmpRect = rect;
1628         tmpRect.right -= I->LabelIndent;
1629         ExecutiveMotionMenuActivate(G,&tmpRect,count,true,x,y,I->DragColumn);
1630         I->DragMenu = false;
1631       } else if(I->DragDraw &&
1632                 (I->DragCurFrame!=I->DragStartFrame) &&
1633                 (I->DragCurFrame >= 0) &&
1634                 (I->DragCurFrame < n_frame)) {
1635         buffer = pymol::string_format("cmd.mcopy(%d,%d,%d%s)", 1+I->DragCurFrame, 1+I->DragStartFrame, 1, extra);
1636       }
1637       break;
1638     case cMovieDragModeOblate:
1639       if(I->DragDraw) {
1640         int min_frame = (I->DragStartFrame < I->DragCurFrame) ? I->DragStartFrame : I->DragCurFrame;
1641         int max_frame = (I->DragStartFrame > I->DragCurFrame) ? I->DragStartFrame : I->DragCurFrame;
1642         if(min_frame<0) min_frame = 0;
1643         if(max_frame<0) max_frame = 0;
1644         if(min_frame>=n_frame) min_frame = n_frame - 1;
1645         if(max_frame>=n_frame) max_frame = n_frame - 1;
1646         if(I->DragColumn) {
1647           extra = ",object='same'";
1648         }
1649         buffer = pymol::string_format("cmd.mview('clear',first=%d,last=%d%s)",
1650                 1 + min_frame, 1 + max_frame, extra);
1651       }
1652       break;
1653     case cMovieDragModeInsDel:
1654       if(I->DragDraw) {
1655         if(I->DragCurFrame<0)
1656           I->DragCurFrame = 0;
1657         if(I->DragCurFrame>I->DragStartFrame) {
1658           int first = I->DragStartFrame + 1;
1659           if(first<0) first = 0;
1660           buffer = pymol::string_format("cmd.minsert(%d,%d%s)", I->DragCurFrame - I->DragStartFrame, first, extra);
1661         } else {
1662           int first = I->DragCurFrame;
1663           if(first<0) first = 0;
1664           buffer = pymol::string_format("cmd.mdelete(%d,%d%s)", I->DragStartFrame - I->DragCurFrame, first+1, extra);
1665         }
1666       }
1667       break;
1668     }
1669     if(!buffer.empty()) {
1670       PParse(G, buffer.c_str());
1671       PFlush(G);
1672       PLog(G, buffer.c_str(), cPLog_pym);
1673     }
1674   }
1675   I->DragMode = 0;
1676   I->DragDraw = false;
1677   I->DragMenu = false;
1678   I->DragColumn = false;
1679 
1680   return 1;
1681 }
1682 
MovieGetPanelHeight(PyMOLGlobals * G)1683 int MovieGetPanelHeight(PyMOLGlobals * G)
1684 {
1685   int movie_panel = SettingGetGlobal_i(G, cSetting_movie_panel);
1686   CMovie *I = G->Movie;
1687   if(movie_panel != 0) {
1688     if(MovieGetLength(G)) {
1689       movie_panel = 1;
1690     } else {
1691       movie_panel = SceneGetNFrame(G) > 1;
1692     }
1693   }
1694 
1695   if(movie_panel) {
1696     int row_height = DIP2PIXEL(SettingGetGlobal_i(G,cSetting_movie_panel_row_height));
1697     I->PanelActive = true;
1698     if(SettingGetGlobal_b(G, cSetting_presentation)) {
1699       /* show camera line only when in presentation mode */
1700       return row_height;
1701     } else {
1702       return row_height * ExecutiveCountMotions(G);
1703     }
1704   } else {
1705     I->PanelActive = false;
1706     return 0;
1707   }
1708 }
1709 
MovieDrawViewElem(PyMOLGlobals * G,BlockRect * rect,int frames ORTHOCGOARG)1710 void MovieDrawViewElem(PyMOLGlobals *G, BlockRect *rect,int frames ORTHOCGOARG)
1711 {
1712   CMovie *I = G->Movie;
1713   if(I->ViewElem) {
1714     ViewElemDraw(G,I->ViewElem,rect,frames,"camera" ORTHOCGOARGVAR);
1715   }
1716 }
1717 
fastDraw(CGO * orthoCGO)1718 bool CMovie::fastDraw(CGO* orthoCGO)
1719 {
1720   return true;
1721 }
1722 
draw(CGO * orthoCGO)1723 void CMovie::draw(CGO* orthoCGO)
1724 {
1725   PyMOLGlobals *G = m_G;
1726   CMovie *I = G->Movie;
1727   if(I->PanelActive) {
1728     int n_frame = SceneGetNFrame(G);
1729     int frame = SceneGetFrame(G);
1730     int count = ExecutiveCountMotions(G);
1731     BlockRect tmpRect = rect;
1732     if(count) {
1733       tmpRect.right -= I->LabelIndent;
1734 
1735       if(G->HaveGUI && G->ValidContext) {
1736         float black[3] = {0.0F,0.0F,0.0F};
1737 	if (orthoCGO){
1738 	  CGOColorv(orthoCGO, black);
1739 	  CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
1740 	  CGOVertex(orthoCGO, tmpRect.right, tmpRect.bottom, 0.f);
1741 	  CGOVertex(orthoCGO, tmpRect.right, tmpRect.top, 0.f);
1742 	  CGOVertex(orthoCGO, rect.right, tmpRect.bottom, 0.f);
1743 	  CGOVertex(orthoCGO, rect.right, tmpRect.top, 0.f);
1744 	  CGOEnd(orthoCGO);
1745 	} else {
1746 	  glColor3fv(black);
1747 	  glBegin(GL_POLYGON);
1748 	  glVertex2f(tmpRect.right, tmpRect.bottom);
1749 	  glVertex2f(tmpRect.right, tmpRect.top);
1750 	  glVertex2f(rect.right, tmpRect.top);
1751 	  glVertex2f(rect.right, tmpRect.bottom);
1752 	  glEnd();
1753 	}
1754       }
1755 
1756       if(!n_frame) {
1757         I->m_ScrollBar.setLimits(1, 1);
1758         I->m_ScrollBar.setValue(0);
1759       } else {
1760         float scroll_value = I->m_ScrollBar.getValue();
1761         int new_frame = (int) (scroll_value + 0.5F);
1762         if(I->m_ScrollBar.grabbed()) {
1763 	  if(new_frame != frame) {
1764 	    frame = new_frame;
1765 	    SceneSetFrame(G, 7, frame);
1766 	  }
1767         }
1768         I->m_ScrollBar.setLimits(n_frame, 1);
1769       }
1770       I->m_ScrollBar.setBox(tmpRect.top, tmpRect.left,
1771                              tmpRect.bottom, tmpRect.right);
1772       {
1773 	I->m_ScrollBar.draw(orthoCGO);
1774 	ExecutiveMotionDraw(G,&tmpRect,count ORTHOCGOARGVAR);
1775 	I->m_ScrollBar.drawHandle(0.35F, orthoCGO);
1776       }
1777 
1778       /* drag selection box */
1779       if(I->DragDraw) {
1780 
1781         float white[4] = {1.0F, 1.0F, 1.0F,0.5F};
1782 
1783         switch(I->DragMode) {
1784         case cMovieDragModeMoveKey:
1785         case cMovieDragModeCopyKey:
1786           {
1787             float grey[4] = {0.75F,0.75F,0.75f,0.5};
1788             if(I->DragStartFrame<n_frame)
1789               ViewElemDrawBox(G,&I->DragRect, I->DragStartFrame, I->DragStartFrame+1, n_frame, white, false ORTHOCGOARGVAR);
1790             if((I->DragCurFrame>=0) && (I->DragCurFrame<n_frame)) {
1791               ViewElemDrawBox(G,&I->DragRect, I->DragCurFrame, I->DragCurFrame+1, n_frame, grey, true ORTHOCGOARGVAR);
1792             }
1793           }
1794           break;
1795         case cMovieDragModeOblate:
1796           {
1797             float grey[4] = {0.75F,0.75F,0.75f,0.5};
1798 
1799             int min_frame = (I->DragStartFrame < I->DragCurFrame) ? I->DragStartFrame : I->DragCurFrame;
1800             int max_frame = (I->DragStartFrame > I->DragCurFrame) ? I->DragStartFrame : I->DragCurFrame;
1801             if(min_frame<0) min_frame = 0;
1802             if(max_frame<0) max_frame = 0;
1803             if(min_frame>=n_frame) min_frame = n_frame - 1;
1804             if(max_frame>=n_frame) max_frame = n_frame - 1;
1805             ViewElemDrawBox(G,&I->DragRect, min_frame, max_frame+1, n_frame, white, false ORTHOCGOARGVAR);
1806             ViewElemDrawBox(G,&I->DragRect, min_frame, max_frame+1, n_frame, grey, true ORTHOCGOARGVAR);
1807           }
1808           break;
1809         case cMovieDragModeInsDel:
1810           if(I->DragCurFrame==I->DragStartFrame) {
1811             ViewElemDrawBox(G,&I->DragRect, I->DragStartFrame, I->DragStartFrame, n_frame, white, true ORTHOCGOARGVAR);
1812           } else if(I->DragCurFrame>=I->DragStartFrame) {
1813             float green[4] = {0.5F, 1.0F, 0.5F,0.5F};
1814             ViewElemDrawBox(G,&I->DragRect, I->DragStartFrame, I->DragCurFrame, n_frame, green, true ORTHOCGOARGVAR);
1815           } else {
1816             float red[4] = {1.0F, 0.5F, 0.5F,0.5F};
1817             ViewElemDrawBox(G,&I->DragRect, I->DragCurFrame, I->DragStartFrame, n_frame, red, true ORTHOCGOARGVAR);
1818           }
1819           break;
1820         }
1821 
1822       }
1823 
1824       if (!ViewElem) {
1825         ViewElemDrawLabel(G, "states", &tmpRect, orthoCGO);
1826       }
1827     }
1828   }
1829 }
1830 
MovieSetScrollBarFrame(PyMOLGlobals * G,int frame)1831 void MovieSetScrollBarFrame(PyMOLGlobals * G, int frame)
1832 {
1833   CMovie *I = G->Movie;
1834   if(!I->m_ScrollBar.grabbed()) {
1835     I->m_ScrollBar.setValue(frame);
1836   }
1837 }
1838 
reshape(int width,int height)1839 void CMovie::reshape(int width, int height)
1840 {
1841   PyMOLGlobals *G = m_G;
1842   CMovie *I = G->Movie;
1843   Block::reshape(width, height);
1844   I->Width = rect.right - rect.left + 1;
1845   I->Height = rect.top - rect.bottom + 1;
1846   if(SettingGetGlobal_b(G, cSetting_presentation)) {
1847     I->LabelIndent = 0;
1848   } else {
1849     I->LabelIndent = DIP2PIXEL(8 * 8);
1850   }
1851 }
1852 
1853