1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 #include "../base/defines.h"
11 
12 
13 #include "lex.h"
14 #include "Parameter.h"
15 #include "DXApplication.h"
16 #include "SequencerNode.h"
17 #include "SequencerWindow.h"
18 #include "DXStrings.h"
19 #include "ErrorDialogManager.h"
20 #include "Network.h"
21 #include "Command.h"
22 #include "WarningDialogManager.h"
23 #include "IBMVersion.h"
24 
25 #include "../widgets/XmDX.h"
26 
27 #define ID_PARAM_NUM	1
28 #define DATA_PARAM_NUM 	2
29 #define FRAME_PARAM_NUM 3
30 #define MIN_PARAM_NUM 	4
31 #define MAX_PARAM_NUM 	5
32 #ifndef HAS_START_STOP
33 #define DELTA_PARAM_NUM 6
34 #define ATTR_PARAM_NUM  7
35 #define EXPECTED_INPUTS ATTR_PARAM_NUM
36 #else
37 #define START_PARAM_NUM 6
38 #define STOP_PARAM_NUM 	7
39 #define DELTA_PARAM_NUM 8
40 #define EXPECTED_INPUTS DELTA_PARAM_NUM
41 #endif
42 
43 
SequencerNode(NodeDefinition * nd,Network * net,int instnc)44 SequencerNode::SequencerNode(NodeDefinition *nd, Network *net, int instnc) :
45     ShadowedOutputNode(nd, net, instnc)
46 {
47     this->seq_window = NULL;
48     this->wasExecuted = FALSE;
49     this->xpos = this->ypos = UIComponent::UnspecifiedPosition;
50     this->width = this->height = UIComponent::UnspecifiedDimension;
51 
52     //
53     // Initialize the startup value based on the net version number.  This preserves
54     // an old behvior: the sequencer always appears when you startup in image mode.
55     // It also provides a new behavior: you can mark the vcr as non-startup window
56     // so that it won't automatically appear when loading the .net in image mode.
57     // The behavior for new nets will be to mark the vcr as a startup window if
58     // it's startup flag was set.  The startup flag will be set thru the vcr's
59     // manage/unmange methods rather than a toggle button.
60     //
61     int major = net->getNetMajorVersion();
62     int minor = net->getNetMinorVersion();
63     int micro = net->getNetMicroVersion();
64     if (VERSION_NUMBER (major, minor, micro) < VERSION_NUMBER (3,1,4))
65 	this->startup = TRUE;
66     else
67 	this->startup = FALSE;
68 }
69 
~SequencerNode()70 SequencerNode::~SequencerNode()
71 {
72     theDXApplication->openSequencerCmd->deactivate();
73 #if 00
74     theDXApplication->network->sequencer = NUL(SequencerNode*);
75 #endif
76 
77     if (this->seq_window) delete this->seq_window;
78 }
79 
initialize()80 boolean SequencerNode::initialize()
81 {
82     this->seq_window = NUL(SequencerWindow*);
83 
84     if (this->getInputCount() != EXPECTED_INPUTS) {
85         fprintf(stderr,
86            "Expected %d inputs for %s node, please check the mdf file.\n",
87                         EXPECTED_INPUTS,
88                         this->getNameString());
89 	return FALSE;
90     }
91 
92     //
93     // This keeps updateAttributes() from sending messages until
94     // we're done.
95     //
96     this->deferVisualNotification();
97 
98     if (!this->setMessageIdParameter(ID_PARAM_NUM) 	||
99     	!this->initMinimumValue(1)			||
100 	!this->initMaximumValue(100)			||
101 	!this->initDeltaValue(    1)			||
102 	!this->initStartValue(    1)			||
103 	!this->initStopValue(    100)) {
104 	ErrorMessage("Error setting default attributes for %s",
105                         this->getNameString());
106 	return FALSE;
107     }
108 
109     this->undeferVisualNotification();
110 
111     this->current         = 0;
112     this->next            = 1;
113     this->previous        = 1;
114     this->step            = FALSE;
115     this->loop            = FALSE;
116     this->palindrome      = FALSE;
117     this->current_defined = FALSE;
118     this->ignoreFirstFrameMsg = FALSE;
119 
120     this->transmitted     = False;
121 
122 #if 00
123     theDXApplication->network->sequencer = this;
124 #endif
125 
126     theDXApplication->openSequencerCmd->activate();
127 
128     return TRUE;
129 }
130 
131 //
132 // If the min or max input has changed, update the attribute parameter
133 // (integer list of min and max) and then call the super class method
134 // if the input is not the attribute parameter.
135 //
ioParameterStatusChanged(boolean input,int index,NodeParameterStatusChange status)136 void SequencerNode::ioParameterStatusChanged(boolean input, int index,
137                                 NodeParameterStatusChange status)
138 {
139     if (input && (status & Node::ParameterValueChanged) &&
140 	((index == MIN_PARAM_NUM)   ||
141 	 (index == DELTA_PARAM_NUM) ||
142 	 (index == MAX_PARAM_NUM))) {
143 	//
144 	// Each time the min or max attribute values (which are stored
145 	// in AttributeParamters and so we come here each time they are
146 	// changed) update the attribute parameter which is an integer
147 	// list of the min and max values.
148 	// Note that the attribute parameter is expected to have
149  	// InputDoesNotDeriveOutputCacheTag cacheability, so that setting
150 	// this over and over again will not cause executions.
151 	//
152 	this->updateAttributes();
153     }
154     if (!input || (index != ATTR_PARAM_NUM))
155 	ShadowedOutputNode::ioParameterStatusChanged(input,index,status);
156 }
openDefaultWindow(Widget parent)157 void SequencerNode::openDefaultWindow(Widget parent)
158 {
159     if (this->seq_window)
160 	this->seq_window->manage();
161     else
162     {
163 	this->seq_window = new SequencerWindow(this);
164         if ((this->xpos != UIComponent::UnspecifiedPosition)  &&
165             theDXApplication->applyWindowPlacements())
166 	    this->seq_window->setGeometry(this->xpos,this->ypos,
167 					this->width,this->height);
168   	this->seq_window->initialize();
169 	this->seq_window->manage();
170 
171 	if (theDXApplication->getExecCtl()->isExecuting())
172 	    this->disableFrameControl();
173 	else
174 	    this->enableFrameControl();
175     }
176 }
177 
netNodeString(const char * prefix)178 char* SequencerNode::netNodeString(const char *prefix)
179 {
180     char *s;
181 
182     if (!this->isDataDriven()) {
183         int inst = this->getInstanceNumber();
184         s = new char[96];
185     	sprintf(s, "    %sSequencer_%d_out_1 = @frame;\n", prefix, inst);
186     } else {
187 	//
188 	// Sequencer_%d_in_(FRAME_PARAM_NUM) = @frame;
189 	// Sequencer_%d_out_1 = Sequencer(xxx, xxx,
190  	//		 	    Sequencer_%d_in_(FRAME_PARAM_NUM)....);
191 	//
192 	char buf[128];
193 	char *call = this->ShadowedOutputNode::netNodeString(prefix);
194 	this->getNetworkInputNameString(FRAME_PARAM_NUM,buf);
195 	int len = STRLEN(call) + STRLEN(buf) + 64;
196 	s = new char[len];
197 	sprintf(s,"    %s = @frame;\n%s", buf, call);
198 	delete call;
199     }
200     return s;
201 }
202 
netPrintAuxComment(FILE * f)203 boolean SequencerNode::netPrintAuxComment(FILE *f)
204 {
205     this->wasExecuted = TRUE;
206 
207     if (!this->DrivenNode::netPrintAuxComment(f))
208 	return FALSE;
209 
210     if (!this->printCommonComments(f,"    "))
211         return FALSE;
212 
213     return TRUE;
214 
215 }
netParseAuxComment(const char * comment,const char * file,int lineno)216 boolean SequencerNode::netParseAuxComment(const char* comment,
217                             		const char *file,
218                             		int lineno)
219 {
220     this->wasExecuted = TRUE;
221 
222     if (this->DrivenNode::netParseAuxComment(comment,file,lineno))
223 	return TRUE;
224 
225     if (this->parseCommonComments(comment,file,lineno))
226         return TRUE;
227 
228     return FALSE;
229 
230 }
231 
cfgPrintNode(FILE * f,PrintType dest)232 boolean SequencerNode::cfgPrintNode(FILE *f, PrintType dest)
233 {
234     if (!this->cfgPrintNodeLeader(f))
235         return FALSE;
236 
237     if (!this->printCommonComments(f))
238 	return FALSE;
239 
240     //
241     // Print startup mode into the cfg file.
242     // Do this only when saving a file because Network will need to manage
243     // the sequencer window if the startup flag is turned on.  That's unwanted
244     // behavior during drag-n-drop and not relevant when printing to the exec.
245     //
246     if (fprintf (f, "// startup = %d\n", (dest == PrintFile ? this->startup: 0)) < 0)
247 	return FALSE;
248 
249     return TRUE;
250 }
cfgParseComment(const char * comment,const char * file,int lineno)251 boolean SequencerNode::cfgParseComment(const char* comment,
252                             		const char *file,
253                             		int lineno)
254 {
255     if (this->cfgParseNodeLeader(comment,file,lineno))
256         return TRUE;
257 
258     if (this->parseCommonComments(comment,file,lineno))
259         return TRUE;
260 
261     //
262     // Parse the startup mode from the cfg file.
263     //
264     const char* substr = " startup";
265     if (EqualSubstring (comment, substr, strlen(substr))) {
266 	int startup_flag;
267 	int items_parsed = sscanf (comment, " startup = %d", &startup_flag);
268 	if (items_parsed == 1) {
269 	    this->setStartup(startup_flag);
270 	    return TRUE;
271 	}
272     }
273 
274     return FALSE;
275 }
276 
printCommonComments(FILE * f,const char * indent)277 boolean SequencerNode::printCommonComments(FILE *f, const char *indent)
278 {
279     if (!indent)
280 	indent = "";
281 
282     //
283     // Include the instance number for forward compatibility reasons
284     // since we don't enforce sequencer instance numbers to be 1.
285     //
286     if (fprintf(f, "%s//"
287            " vcr[%d]: min = %d, max = %d, beg = %d, end = %d, cur = %d, "
288 		"inc = %d, loop = %s, step = %s, pal = %s\n",
289 	   indent,
290            this->getInstanceNumber(),
291            this->getMinimumValue(),
292 	   this->getMaximumValue(),
293 	   this->getStartValue(),
294 	   this->getStopValue(),
295 	   this->next,
296 	   this->getDeltaValue(),
297            this->loop ? "on" : "off",
298 	   this->isStepMode() ? "on" : "off",
299 	   this->palindrome ? "on" : "off") < 0)
300 	return FALSE;
301 
302     int x,y,w,h;
303     if (this->seq_window) {
304 	this->seq_window->getGeometry(&x,&y,&w,&h);
305     } else {
306 	x = this->xpos; y = this->ypos;
307 	w = this->width; h = this->height;
308     }
309 
310     if (x != UIComponent::UnspecifiedPosition) {
311 	if (!UIComponent::PrintGeometryComment(f,x,y,w,h,NULL,indent))
312 	    return FALSE;
313     }
314     return TRUE;
315 }
316 
317 
parseCommonComments(const char * comment,const char * file,int lineno)318 boolean SequencerNode::parseCommonComments(const char* comment,
319                             		const char *file,
320                             		int lineno)
321 {
322     int  expected_items, items, instance;
323     int  min, max, beg, end, cur, inc;
324     char loop[64];
325     char step[64];
326     char pal[64];
327 
328     if (EqualSubstring(comment," vcr",4)) {
329 
330 	//
331 	// See comment in :cfgPrint.  This is to preserve behavior of existing
332 	// nets.
333 	//
334 	this->startup = TRUE;
335 
336 	if (EqualSubstring(comment," vcr[",5)) {
337 	    expected_items = 10;
338 	    items = sscanf(comment,
339 	       " vcr[%d]: min = %d, max = %d, beg = %d, end = %d, cur = %d,"
340 	       " inc = %d, loop = %[^,], step = %[^,], pal = %[^\n]",
341 	       &instance, // Drop this on the floor until we support >1 Sequencers
342 	       &min, &max, &beg, &end, &cur, &inc,
343 	       loop, step, pal);
344 	} else {
345 	    //
346 	    // Pre 2.0.0 nets
347 	    //
348 	    expected_items = 9;
349 	    items = sscanf(comment,
350 	       " vcr: min = %d, max = %d, beg = %d, end = %d, cur = %d, inc = %d,"
351 	       " loop = %[^,], step = %[^,], pal = %[^\n]",
352 	       &min, &max, &beg, &end, &cur, &inc,
353 	       loop, step, pal);
354 	}
355 
356 	if(items < expected_items)
357 	{
358 	    ErrorMessage("Malformed comment at line %d in file %s (ignoring)",
359 					lineno, file);
360 	    return TRUE;
361 	}
362 
363 	//
364 	// This keeps updateAttributes() from sending messages until
365 	// we're done.
366 	//
367 	this->deferVisualNotification();
368 
369 	this->setMinimumValue(min);
370 	this->setMaximumValue(max);
371 	this->setDeltaValue(inc);
372 	this->setStartValue(beg);
373 	this->setStopValue(end);
374 
375 	this->undeferVisualNotification();
376 
377 	this->next       = cur;
378 	this->loop       = EqualString(loop, "on");
379 	this->step       = EqualString(step, "on");
380 	this->palindrome = EqualString(pal,  "on");
381 	return TRUE;
382 
383     }
384 
385 
386     if (UIComponent::ParseGeometryComment(comment,file,lineno,
387 				&this->xpos,&this->ypos,
388 				&this->width, &this->height)) {
389         if (this->seq_window &&
390             (this->xpos != UIComponent::UnspecifiedPosition)  &&
391             theDXApplication->applyWindowPlacements())
392 	    this->seq_window->setGeometry(this->xpos,this->ypos,
393 					this->width,this->height);
394 
395 	return TRUE;
396     }
397 
398     return FALSE;
399 }
400 
401 //
402 // Send the values as per the super class method and also send the
403 // @startframe, @nextframe, @endframe, @deltaframe values.  If the
404 // Sequencer is data-driven then we send the @*frame values as NULL,
405 // since the Sequencer module will set these internally.  Also, we
406 // want to be sure we don't pass in any values that might be replicated
407 // later in the sequence which would confuse the caching mechanism.
408 // For example, if we set @nextframe=2, and the sequence goes 0 1 2 3 ...,
409 // then the output will be 0 1 2 0, because the executive thinks the
410 // Sequencer has already seen the 4th set of values (remember that the
411 // Sequencer is called something like the following...
412 //     Sequencer_1_in_3 = @frame ;
413 //     Sequencer_1_out_1 = Sequencer(...,Sequencer_1_in_3,...);
414 // The idea here is to set @nextframe to something that will never be
415 // seen during the sequence.
416 //
valuesString(const char * prefix)417 char* SequencerNode::valuesString(const char *prefix)
418 {
419     char* inputs = this->ShadowedOutputNode::valuesString(prefix);
420     char* s = new char[STRLEN(inputs) + 128];
421 
422 #if 0
423     if (this->isDataDriven()) {
424 	sprintf(s,
425 	    "%s\n"
426 	    // FIXME: these -1e6's should be replaced by NULLs and 'play'
427   	    // should not init @nextframe=0 if it sees @nextframe==NULL;
428 	    "@startframe = -1e6;\n"
429 	    "@nextframe  = -1e6;\n"
430 	    "@endframe   = -1e6;\n"
431 	    "@deltaframe = -1e6;\n",
432 	    inputs);
433     } else
434 #endif
435 	{
436 	sprintf(s,
437 	    "%s\n"
438 	    "@startframe = %d;\n"
439 	    "@nextframe  = @startframe;\n"
440 	    "@endframe   = %d;\n"
441 	    "@deltaframe = %d;\n",
442 	    inputs,
443 	    this->getStartValue(),
444 	    this->getStopValue(),
445 	    this->getDeltaValue());
446     }
447 
448     delete inputs;
449     return s;
450 }
451 
getVCRWidget()452 Widget  SequencerNode::getVCRWidget()
453 {
454     if(this->seq_window)
455        return seq_window->vcr;
456     else
457        return NUL(Widget);
458 }
459 
460 // The messages we parse can contain one or more of the following...
461 //
462 // 'min=%g' 'max=%g' 'start=%d' 'end=%d' 'delta=%g'
463 //
464 // If any input or output values are to be changed, don't send them
465 // because the module backing up the sequencer will have just executed
466 // and if the UI is in 'execute on change' mode then there will be an
467 // extra execution.
468 //
469 // Returns the number of attributes parsed.
470 //
handleNodeMsgInfo(const char * line)471 int SequencerNode::handleNodeMsgInfo(const char *line)
472 {
473     int index, values = 0;
474     char *p, *buf = NULL;
475 
476     this->wasExecuted = TRUE;
477 
478     //
479     // Handle the 'min=%g' part of the message.
480     //
481     if ( (p = strstr((char*)line,"min=")) ) {
482 	values++;
483 	while (*p != '=') p++;
484 	p++;
485 	buf = DuplicateString(p);
486 	index = 0;
487 	if (IsScalar(buf, index)) {
488 	    buf[index] = '\0';
489 	    this->setInputAttributeFromServer(MIN_PARAM_NUM,
490 					buf, DXType::UndefinedType);
491 #ifndef HAS_START_STOP
492 	    this->setStartValue(atoi(buf));
493 #endif
494 	}
495 	delete buf;
496     }
497     //
498     // Handle the 'max=%g' part of the message.
499     //
500     if ( (p = strstr((char*)line,"max=")) ) {
501 	values++;
502 	while (*p != '=') p++;
503 	p++;
504 	buf = DuplicateString(p);
505 	index = 0;
506 	if (IsScalar(buf, index)) {
507 	    buf[index] = '\0';
508 	    this->setInputAttributeFromServer(MAX_PARAM_NUM,
509 					buf, DXType::UndefinedType);
510 #ifndef HAS_START_STOP
511 	     this->setStopValue(atoi(buf));
512 #endif
513 	}
514 	delete buf;
515     }
516     //
517     // Handle the 'delta=%g' part of the message.
518     // Since the attribute and parameter value have the same type, but
519     // different meanings we only set the attribute value as received from
520     // the exec.
521     // NOTE: this is done before 'frame=' so that we can compute an accurate
522     //	next frame.
523     //
524     if ( (p = strstr((char*)line,"delta=")) ) {
525 	values++;
526 	while (*p != '=') p++;
527 	p++;
528 	buf = DuplicateString(p);
529 	index = 0;
530 	if (IsScalar(buf, index)) {
531 	    buf[index] = '\0';
532 	    this->setInputAttributeFromServer(DELTA_PARAM_NUM,
533 					buf, DXType::UndefinedType);
534 	}
535 	delete buf;
536     }
537 
538     //
539     // Handle the 'frame=%g' part of the message.
540     //
541     if ( (p = strstr((char*)line,"frame=")) ) {
542 	values++;
543 	while (*p != '=') p++;
544 	p++;
545 	//
546 	// Change the currently display frame and ignore the next
547 	// 'frame %d %d' message from the executive (not Sequencer).
548 	//
549 	this->current_defined = TRUE;
550 	this->current = atoi(p);
551 	this->next = this->current + this->getDeltaValue();
552 	this->ignoreFirstFrameMsg = TRUE;
553     }
554 
555     //
556     // If min/max is defaulting (i.e. set from the frame control) and
557     // max/min is set then we need to be sure that the defaulting value
558     // is consistent with the set value.
559     //
560     int min_val = this->getMinimumValue();
561     int max_val = this->getMaximumValue();
562     boolean min_dflting = this->isInputDefaulting(MIN_PARAM_NUM);
563     boolean max_dflting = this->isInputDefaulting(MAX_PARAM_NUM);
564     if (min_dflting || max_dflting) {
565 	char *setattr = NULL, *set = NULL;
566 	if (max_dflting && (min_val > max_val)) {
567 	    this->setMaximumValue(min_val);
568 	    max_val = min_val;
569 	    set     = "minimum";
570 	    setattr = "maximum";
571 	} else if (min_dflting && (min_val > max_val)) {
572 	    this->setMinimumValue(max_val);
573 	    min_val = max_val;
574 	    set     = "maximum";
575 	    setattr = "minimum";
576  	}
577 	 if (setattr) {
578 	    WarningMessage("%s value provided to %s conflicts "
579 			    "with %s value set with 'Frame Control' dialog"
580 			    "...adjusting %s.",
581 			    set, this->getNameString(),
582 			    setattr,setattr);
583 	}
584     }
585 
586     //
587     // Make sure that the start/stop values are both within range of the
588     // possibly new min and/or max values.
589     //
590     int stop_val = this->getStopValue();
591     int start_val = this->getStartValue();
592     if ((start_val < min_val) || (start_val > max_val))
593 	this->setStartValue(min_val);
594     if ((stop_val < min_val) || (stop_val > max_val))
595 	this->setStopValue(max_val);
596 
597 #ifdef HAS_START_STOP
598     //
599     // Handle the 'start=%d' part of the message.
600     //
601     if (p = strstr((char*)line,"start=")) {
602 	values++;
603 	while (*p != '=') p++;
604 	p++;
605 	buf = DuplicateString(p);
606 	index = 0;
607 	if (IsScalar(buf, index)) {
608 	    buf[index] = '\0';
609 	    this->setInputAttributeFromServer(START_PARAM_NUM,
610 					buf, DXType::UndefinedType);
611 	    //
612 	    // Change the currently display frame and ignore the next
613 	    // 'frame %d %d' message.
614 	    //
615 	    this->current_defined = TRUE;
616 	    this->current = this->getStartValue();
617 	    this->next = this->current + this->getDeltaValue();
618 	    this->ignoreFirstFrameMsg = TRUE;
619 	}
620 	delete buf;
621     }
622     //
623     // Handle the 'end=%d' part of the message.
624     //
625     if (p = strstr((char*)line,"end=")) {
626 	values++;
627 	while (*p != '=') p++;
628 	p++;
629 	buf = DuplicateString(p);
630 	index = 0;
631 	if (IsScalar(buf, index)) {
632 	    buf[index] = '\0';
633 	    this->setInputAttributeFromServer(STOP_PARAM_NUM,
634 					buf, DXType::UndefinedType);
635 	}
636 	delete buf;
637     }
638 #endif
639 
640     return values;
641 }
642 
reflectStateChange(boolean unmanage)643 void SequencerNode::reflectStateChange(boolean unmanage)
644 {
645     if (this->seq_window)
646 	this->seq_window->handleStateChange(unmanage);
647 
648     //
649     // We do this here because we use isVisualNotificationDeferred() in
650     // in this->updateAttributes() which means that we must call it again
651     // once notification is undeferred (i.e. in reflectStateChange()).
652     //
653     this->updateAttributes();
654 }
655 
656 #if 0 // 8/9/93
657 boolean SequencerNode::isAttributeVisuallyWriteable(int input_index)
658 {
659      return
660 	 this->isInputDefaulting(DATA_PARAM_NUM) &&
661      	 this->ShadowedOutputNode::isAttributeVisuallyWriteable(input_index);
662 }
663 #endif
isMinimumVisuallyWriteable()664 boolean SequencerNode::isMinimumVisuallyWriteable()
665 {
666     return this->isInputDefaulting(DATA_PARAM_NUM) &&
667    	   this->isAttributeVisuallyWriteable(MIN_PARAM_NUM);
668 }
isMaximumVisuallyWriteable()669 boolean SequencerNode::isMaximumVisuallyWriteable()
670 {
671     return this->isInputDefaulting(DATA_PARAM_NUM) &&
672    	   this->isAttributeVisuallyWriteable(MAX_PARAM_NUM);
673 }
674 #ifdef HAS_START_STOP
isStartVisuallyWriteable()675 boolean SequencerNode::isStartVisuallyWriteable()
676 	{ return this->isAttributeVisuallyWriteable(START_PARAM_NUM); }
isStopVisuallyWriteable()677 boolean SequencerNode::isStopVisuallyWriteable()
678 	{ return this->isAttributeVisuallyWriteable(STOP_PARAM_NUM); }
679 #endif
isDeltaVisuallyWriteable()680 boolean SequencerNode::isDeltaVisuallyWriteable()
681 	{ return this->isAttributeVisuallyWriteable(DELTA_PARAM_NUM); }
682 
initMinimumValue(int val)683 boolean SequencerNode::initMinimumValue(int val)
684 {
685     char buf[64];
686     sprintf(buf,"%d",val);
687     return this->initInputAttributeParameter(MIN_PARAM_NUM,buf);
688 }
setMinimumValue(int val)689 boolean SequencerNode::setMinimumValue(int val)
690 {
691     char buf[64];
692     sprintf(buf,"%d",val);
693     return this->setInputAttributeParameter(MIN_PARAM_NUM,buf);
694 }
695 #if 0
696 boolean SequencerNode::setMinimumValue(const char *val)
697 {
698     return this->setInputAttributeParameter(MIN_PARAM_NUM,val);
699 }
700 #endif
getMinimumValue()701 int SequencerNode::getMinimumValue()
702 {
703     const char *val = this->getInputAttributeParameterValue(MIN_PARAM_NUM);
704     return atoi(val);
705 }
initMaximumValue(int val)706 boolean SequencerNode::initMaximumValue(int val)
707 {
708     char buf[64];
709     sprintf(buf,"%d",val);
710     return this->initInputAttributeParameter(MAX_PARAM_NUM,buf);
711 }
setMaximumValue(int val)712 boolean SequencerNode::setMaximumValue(int val)
713 {
714     char buf[64];
715     sprintf(buf,"%d",val);
716     return this->setInputAttributeParameter(MAX_PARAM_NUM,buf);
717 }
718 #if 0
719 boolean SequencerNode::setMaximumValue(const char *val)
720 {
721     return this->setInputAttributeParameter(MAX_PARAM_NUM,val);
722 }
723 #endif
getMaximumValue()724 int SequencerNode::getMaximumValue()
725 {
726     const char *val = this->getInputAttributeParameterValue(MAX_PARAM_NUM);
727     return atoi(val);
728 }
initDeltaValue(int val)729 boolean SequencerNode::initDeltaValue(int val)
730 {
731     char buf[64];
732     sprintf(buf,"%d",val);
733     return this->initInputAttributeParameter(DELTA_PARAM_NUM,buf);
734 }
setDeltaValue(int val)735 boolean SequencerNode::setDeltaValue(int val)
736 {
737     char buf[64];
738     sprintf(buf,"%d",val);
739     return this->setInputAttributeParameter(DELTA_PARAM_NUM,buf);
740 }
741 #if 0
742 boolean SequencerNode::setDeltaValue(const char *val)
743 {
744     return this->setInputAttributeParameter(DELTA_PARAM_NUM,val);
745 }
746 #endif
getDeltaValue()747 int SequencerNode::getDeltaValue()
748 {
749     const char *val = this->getInputAttributeParameterValue(DELTA_PARAM_NUM);
750     return atoi(val);
751 }
initStartValue(int val)752 boolean SequencerNode::initStartValue(int val)
753 {
754 #ifndef HAS_START_STOP
755     this->startValue = val;
756     this->updateAttributes();
757     return TRUE;
758 #else
759     char buf[64];
760     sprintf(buf,"%d",val);
761     return this->initInputAttributeParameter(START_PARAM_NUM,buf);
762 #endif
763 }
setStartValue(int val)764 boolean SequencerNode::setStartValue(int val)
765 {
766 #ifndef HAS_START_STOP
767     this->startValue = val;
768     this->updateAttributes();
769     return TRUE;
770 #else
771     char buf[64];
772     sprintf(buf,"%d",val);
773     return this->setInputAttributeParameter(START_PARAM_NUM,buf);
774 #endif
775 }
776 #if 0
777 boolean SequencerNode::setStartValue(const char *val)
778 {
779     return this->setInputAttributeParameter(START_PARAM_NUM,val);
780 }
781 #endif
getStartValue()782 int SequencerNode::getStartValue()
783 {
784 #ifndef HAS_START_STOP
785     return this->startValue;
786 #else
787     const char *val = this->getInputAttributeParameterValue(START_PARAM_NUM);
788     return atoi(val);
789 #endif
790 }
initStopValue(int val)791 boolean SequencerNode::initStopValue(int val)
792 {
793 #ifndef HAS_START_STOP
794     this->stopValue = val;
795     this->updateAttributes();
796     return TRUE;
797 #else
798     char buf[64];
799     sprintf(buf,"%d",val);
800     return this->initInputAttributeParameter(STOP_PARAM_NUM,buf);
801 #endif
802 }
setStopValue(int val)803 boolean SequencerNode::setStopValue(int val)
804 {
805 #ifndef HAS_START_STOP
806     this->stopValue = val;
807     this->updateAttributes();
808     return TRUE;
809 #else
810     char buf[64];
811     sprintf(buf,"%d",val);
812     return this->setInputAttributeParameter(STOP_PARAM_NUM,buf);
813 #endif
814 }
815 #if 0
816 boolean SequencerNode::setStopValue(const char *val)
817 {
818     return this->setInputAttributeParameter(STOP_PARAM_NUM,val);
819 }
820 #endif
getStopValue()821 int SequencerNode::getStopValue()
822 {
823 #ifndef HAS_START_STOP
824     return this->stopValue;
825 #else
826     const char *val = this->getInputAttributeParameterValue(STOP_PARAM_NUM);
827     return atoi(val);
828 #endif
829 }
830 
831 //
832 // Disable the Frame control.
833 //
disableFrameControl()834 void SequencerNode::disableFrameControl()
835 {
836     if (this->seq_window)
837 	this->seq_window->disableFrameControl();
838 }
839 //
840 // Enable the Frame control if the node is not data driven.
841 //
enableFrameControl()842 void SequencerNode::enableFrameControl()
843 {
844     if (this->seq_window)
845 	this->seq_window->enableFrameControl();
846 }
setPlayDirection(SequencerDirection dir)847 void SequencerNode::setPlayDirection(SequencerDirection dir)
848 {
849     if (this->seq_window)
850 	this->seq_window->setPlayDirection(dir);
851 }
setForwardPlay()852 void SequencerNode::setForwardPlay()
853 {
854     if (this->seq_window)
855 	this->seq_window->setPlayDirection(SequencerNode::ForwardDirection);
856 }
setBackwardPlay()857 void SequencerNode::setBackwardPlay()
858 {
859     if (this->seq_window)
860 	this->seq_window->setPlayDirection(SequencerNode::BackwardDirection);
861 }
setStepMode(boolean val)862 void SequencerNode::setStepMode(boolean val)
863 {
864     if (this->step == val)
865 	return;
866 
867     this->step = val;
868     if (this->seq_window)
869 	this->seq_window->handleStateChange(FALSE);
870 
871 }
setLoopMode(boolean val)872 void SequencerNode::setLoopMode(boolean val)
873 {
874     if (this->loop == val)
875 	return;
876 
877     this->loop = val;
878     if (this->seq_window)
879 	this->seq_window->handleStateChange(FALSE);
880 
881 }
setPalindromeMode(boolean val)882 void SequencerNode::setPalindromeMode(boolean val)
883 {
884     if (this->palindrome == val)
885 	return;
886 
887     this->palindrome = val;
888     if (this->seq_window)
889 	this->seq_window->handleStateChange(FALSE);
890 
891 }
892 //
893 // Get a string representing the assignment to the global vcr variables,
894 // @startframe, @frame, @nextframe, @endframe and @deltaframe.
895 // The returned string must be deleted by the caller.
896 // If the Sequencer is data-driven then we send the @*frame values as NULL,
897 // since the Sequencer module will set these internally.  Also, we
898 // want to be sure we don't pass in any values that might be replicated
899 // later in the sequence which would confuse the caching mechanism.
900 // For example, if we set @nextframe=2, and the sequence goes 0 1 2 3 ...,
901 // then the output will be 0 1 2 0, because the executive thinks the
902 // Sequencer has already seen the 4th set of values (remember that the
903 // Sequencer is called something like the following...
904 //     Sequencer_1_in_3 = @frame ;
905 //     Sequencer_1_out_1 = Sequencer(...,Sequencer_1_in_3,...);
906 // The idea here is to set @nextframe to something that will never be
907 // seen during the sequence.
908 //
909 //
getFrameControlString()910 char *SequencerNode::getFrameControlString()
911 {
912     char *command = new char[128];
913 
914 #if 0
915     if (this->isDataDriven()) {
916 	    // FIXME: these -1e6's should be replaced by NULLs and 'play'
917   	    // should not init @nextframe=0 if it sees @nextframe==NULL;
918 	strcpy(command, "@startframe,@frame,@nextframe,@endframe,@deltaframe ="
919 			     " -1e6, -1e6, -1e6, -1e6, -1e6;\n");
920     }
921     else
922 #endif
923     {
924 	sprintf(command,
925 	     "@startframe,@frame,@nextframe,@endframe,@deltaframe "
926 	     "= %d,%d,%d,%d,%d;\n",
927 	     this->getStartValue(),
928 	     this->current_defined ? this->current : this->next,
929 	     this->next,
930 	     this->getStopValue(),
931 	     this->getDeltaValue());
932     }
933 
934     return command;
935 }
936 //
937 // The Sequencer always expects the 'frame %d %d'  message and when
938 // data-driven expects 'Sequencer_%d: ...' messages.  We install the
939 // handler for the 'frame' message here and then call the super class
940 // to install the 'Sequencer_%d:' handler.
941 //
updateModuleMessageProtocol(DXPacketIF * pif)942 void SequencerNode::updateModuleMessageProtocol(DXPacketIF *pif)
943 {
944 
945     pif->setHandler(DXPacketIF::INTERRUPT,
946                   SequencerNode::ProcessFrameInterrupt,
947                   (void*)this,
948                   "frame ");
949 
950     this->ShadowedOutputNode::updateModuleMessageProtocol(pif);
951 }
952 
953 //
954 // Handler for 'frame %d %d' messages.
955 // If the Sequencer is data-driven and has received a 'frame=%d:%d' message
956 // then we ignore the next 'frame %d %d' message.
957 //
ProcessFrameInterrupt(void * clientData,int id,void * p)958 void SequencerNode::ProcessFrameInterrupt(void *clientData, int id, void *p)
959 {
960     char *line = (char *)p;
961     char buffer[10];
962     int  frame, next_frame, parsed;
963     Widget vcr = NUL(Widget);
964     SequencerNode* sequencer = (SequencerNode*)clientData;
965 
966     if (sequencer->ignoreFirstFrameMsg == TRUE) {
967     	sequencer->ignoreFirstFrameMsg = FALSE;
968 	return;
969     }
970 
971     vcr = sequencer->getVCRWidget();
972 
973     parsed = sscanf(line,"%s %d %d", buffer, &frame, &next_frame);
974     ASSERT(parsed == 3);
975 
976     sequencer->current         = frame;
977     sequencer->next            = next_frame;
978     sequencer->current_defined = TRUE;
979 
980     if (vcr)
981           XtVaSetValues(vcr,
982                         XmNcurrent,        sequencer->current,
983                         XmNcurrentVisible, sequencer->current_defined,
984                         XmNnext,           sequencer->next,
985                         NULL);
986 }
987 
988 //
989 // Determine if this node is of the given class.
990 //
isA(Symbol classname)991 boolean SequencerNode::isA(Symbol classname)
992 {
993     Symbol s = theSymbolManager->registerSymbol(ClassSequencerNode);
994     if (s == classname)
995 	return TRUE;
996     else
997 	return this->ShadowedOutputNode::isA(classname);
998 }
999 
1000 //
1001 // Make sure the value of the parameter that holds a list of ui attributes
1002 // (i.e. min/max/incr) is up to date in the executive.
1003 // Always send it since the input attribute is cache:0.
1004 //
updateAttributes()1005 void SequencerNode::updateAttributes()
1006 {
1007      char val[128];
1008 
1009      //
1010      // Using isVisualNotificationDeferred() is somewhat inconsistent,
1011      // but we're using it here to avoid sending multiple messages back to
1012      // the executive when inside this->handleModuleMessage() during which
1013      // time we know that visual notification is deferred.
1014      // This of course means that we must call this again when notification
1015      // is undeferred (i.e. in reflectStateChange);
1016      //
1017      if (!this->isVisualNotificationDeferred()) {
1018 	 sprintf(val,"{ %d %d %d %d %d %d }",
1019 				  this->getMinimumValue(),
1020 				  this->getMaximumValue(),
1021 				  this->getDeltaValue(),
1022 				  this->getStartValue(),
1023 				  this->getStopValue(),
1024 				  this->wasExecuted ? 1 : 0);
1025 	 this->setInputValueQuietly(ATTR_PARAM_NUM,val,
1026 				DXType::IntegerListType);
1027     }
1028 }
canSwitchNetwork(Network * from,Network * to)1029 boolean SequencerNode::canSwitchNetwork(Network *from, Network *to)
1030 {
1031     if (to->sequencer != NULL) {
1032 	WarningMessage("Attempt to add a second Sequencer to network ignored.");
1033 	return FALSE;
1034     }
1035 
1036     return TRUE;
1037 }
1038 //
1039 // Return TRUE if this node has state that will be saved in a .cfg file.
1040 //
hasCfgState()1041 boolean SequencerNode::hasCfgState()
1042 {
1043     return TRUE;
1044 }
1045 
1046 
getMessageIdParamNumber()1047 int SequencerNode::getMessageIdParamNumber() { return ID_PARAM_NUM; }
1048 
isStartup()1049 boolean SequencerNode::isStartup()
1050 {
1051     if (!this->seq_window) return this->startup;
1052     return this->seq_window->isStartup();
1053 }
1054 
printInputAsJava(int input)1055 boolean SequencerNode::printInputAsJava(int input)
1056 {
1057     boolean retval = FALSE;
1058     switch (input) {
1059 	case MIN_PARAM_NUM:
1060 	case MAX_PARAM_NUM:
1061 	case DELTA_PARAM_NUM:
1062 	    retval = TRUE;
1063 	    break;
1064 	default:
1065 	    break;
1066     }
1067     return retval;
1068 }
1069 
getJavaInputValueString(int input)1070 const char* SequencerNode::getJavaInputValueString(int input)
1071 {
1072     const char* retval = NUL(char*);
1073     static char tbuf[32];
1074     switch (input) {
1075 	case MIN_PARAM_NUM:
1076 	    sprintf (tbuf, "%d", this->getMinimumValue());
1077 	    retval = tbuf;
1078 	    break;
1079 	case MAX_PARAM_NUM:
1080 	    sprintf (tbuf, "%d", this->getMaximumValue());
1081 	    retval = tbuf;
1082 	    break;
1083 	case DELTA_PARAM_NUM:
1084 	    sprintf (tbuf, "%d", this->getDeltaValue());
1085 	    retval = tbuf;
1086 	    break;
1087 	default:
1088 	    retval = this->ShadowedOutputNode::getInputValueString(input);
1089 	    break;
1090     }
1091     return retval;
1092 }
1093