aboutsummaryrefslogtreecommitdiff
path: root/vendor/x11iraf/obm/param.c
blob: 27226330214ba9a9d9c2e7ea563ea8756cbb5df2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/* Copyright(c) 1993 Association of Universities for Research in Astronomy Inc.
 */

#include <ObmP.h>


/*
 * UI PARAMETER class.
 * --------------------------
 * The UI parameter class is used for client-UI communications.  The client
 * does not control the user interface directly, rather the UI defines a set
 * of abstract UI parameters, and during execution the client application
 * assigns values to these parameters.  These UI parameters should be thought
 * of as describing the runtime state of the client as viewed by the GUI.
 * The GUI is free to interpret this state information in any way, including
 * ignoring it.  Many GUIs can be written which use the same client state
 * as described by the UI parameters.
 *
 * Assigning a value to a UI parameter causes the new value to be stored, and
 * any parameter action procedures registered by the UI to be called.
 * The action or actions (if any) taken when a parameter value changes are
 * arbitrary, e.g. the action might be something as simple as changing a
 * displayed value of a UI widget, or something more complex like displaying
 * a popup.
 *
 * UI Parameter class commands:
 *
 *             getValue
 *             setValue <new-value>
 *          addCallback <procedure-name>
 *       deleteCallback <procedure-name>
 *               notify
 *
 * The most common usage is for the GUI to post one or more callbacks for
 * each UI parameter.  When the UI parameter value is changed (with setValue,
 * e.g. by the client) the GUI callback procedures are called with the old
 * and new UI parameter values on the command line.  addCallback is used to
 * add a callback procedure, and deleteCallback to delete one.  Multiple
 * callbacks may be registered for a single UI parameter.  notify is used
 * to simulate a parameter value change, causing any callback procedures to
 * be invoked.
 *
 * The callback procedure is called as follows:
 *
 *	user-procedure param-name {old-value} {new-value}
 *
 * The important thing to note here is that the old and new value strings
 * are quoted with braces.  This prevents any interpretation of the string
 * by Tcl when the callback is executed, which is necessary because the
 * strings can contain arbitrary data.  When Tcl calls the callback the
 * first level of braces will be stripped off, leaving old-value and new-value
 * each as a single string argument.
 */

struct parameterPrivate {
	ObmContext obm;
	char *value;
	int len_value;
	ObmCallback callback;
};

typedef	struct parameterPrivate *ParameterPrivate;

struct parameterObject {
	struct obmObjectCore core;
	struct parameterPrivate parameter;
};

typedef	struct parameterObject *ParameterObject;

/* Object message context. */
struct msgContext {
	Tcl_Interp *tcl;		/* class interpreter */
	ObmObject object[MAX_LEVELS];	/* object which received last message */
	int level;
};
typedef struct msgContext *MsgContext;

static	void ParameterDestroy();
static	int ParameterEvaluate();
static	ObmObject ParameterCreate();
static	void ParameterClassDestroy();
static	int parameterSetValue(), parameterGetValue(), parameterNotify();
static	int parameterAddCallback(), parameterDeleteCallback();


/* ParameterClassInit -- Initialize the class record for the parameter class.
 */
void
ParameterClassInit (obm, classrec)
ObmContext obm;
register ObjClassRec classrec;
{
	register Tcl_Interp *tcl;
	register MsgContext msg;

	/* Install the class methods. */
	classrec->ClassDestroy = ParameterClassDestroy;
	classrec->Create = (ObmFunc) ParameterCreate;
	classrec->Destroy = ParameterDestroy;
	classrec->Evaluate = ParameterEvaluate;

	/* Since there can be many instances of the parameter object and
	 * they all respond to the same class messages, a single interpreter
	 * is used for all objects.
	 */
	msg = (MsgContext) XtMalloc (sizeof (struct msgContext));
	classrec->class_data = (XtPointer) msg;
	msg->tcl = tcl = Tcl_CreateInterp();
	msg->level = 0;

	/* Register parameter-object actions.  */
	Tcl_CreateCommand (tcl, "setValue",
	    parameterSetValue, (ClientData)msg, NULL);
	Tcl_CreateCommand (tcl, "getValue",
	    parameterGetValue, (ClientData)msg, NULL);
	Tcl_CreateCommand (tcl, "addCallback",
	    parameterAddCallback, (ClientData)msg, NULL);
	Tcl_CreateCommand (tcl, "deleteCallback",
	    parameterDeleteCallback, (ClientData)msg, NULL);
	Tcl_CreateCommand (tcl, "notify",
	    parameterNotify, (ClientData)msg, NULL);
}


/* ParameterClassDestroy -- Custom destroy procedure for the parameter
 * class.
 */
static void
ParameterClassDestroy (obm, classrec)
ObmContext obm;
register ObjClassRec classrec;
{
	register MsgContext msg = (MsgContext) classrec->class_data;

	if (msg) {
	    if (msg->tcl)
		Tcl_DeleteInterp (msg->tcl);
	    XtFree ((char *)msg);
	    classrec->class_data = NULL;
	}
}


/* ParameterCreate -- Create an instance of a parameter object.
 */
static ObmObject
ParameterCreate (obm, name, classrec, parent, args, nargs)
ObmContext obm;
char *name;
ObjClassRec classrec;
char *parent;
ArgList args;
int nargs;
{
	register ParameterObject obj;

	obj = (ParameterObject) XtCalloc (1, sizeof (struct parameterObject));
	obj->parameter.obm = obm;

	return ((ObmObject) obj);
}


/* ParameterDestroy -- Destroy an instance of a parameter object.
 */
static void
ParameterDestroy (object)
ObmObject object;
{
	register ParameterObject obj = (ParameterObject) object;
	register ObmCallback cb, next;

	/* Destroy the object in the second final call to Destroy. */
	if (!obj->core.being_destroyed++)
	    return;

	XtFree ((char *)obj->parameter.value);
	for (cb = obj->parameter.callback;  cb;  cb = next) {
	    next = cb->next;
	    XtFree ((char *)cb);
	}
}


/* ParameterEvaluate -- Evaluate a parameter command or message.
 */
static int
ParameterEvaluate (object, command)
ObmObject object;
char *command;
{
	register ParameterObject obj = (ParameterObject) object;
	register MsgContext msg = (MsgContext) obj->core.classrec->class_data;
	register ObmContext obm = obj->parameter.obm;
	int status;

	/* Since the class wide interpreter is used to evaluate the message
	 * we can't pass the object descriptor directly to the class procedure
	 * referenced in the message.  Instead we pass the object reference
	 * in the message descriptor.
	 */
	msg->object[++msg->level] = object;
	Tcl_SetResult (obm->tcl, "", TCL_VOLATILE);

	if (!obmClientCommand (msg->tcl, command)) {
	    Tcl_SetResult (obm->tcl, "invalid command", TCL_VOLATILE);
	    status = TCL_ERROR;
	} else {
	    status = Tcl_Eval (msg->tcl, command);
	    if (status == TCL_ERROR) {
		if (*msg->tcl->result)
		    Tcl_SetResult (obm->tcl, msg->tcl->result, TCL_VOLATILE);
		else {
		    /* Supply a default error message if none was returned. */
		    Tcl_SetResult (obm->tcl, "evaluation error", TCL_VOLATILE);
		}
		obm->tcl->errorLine = msg->tcl->errorLine;

	    } else if (*msg->tcl->result)
		Tcl_SetResult (obm->tcl, msg->tcl->result, TCL_VOLATILE);
	}

	msg->level--;
	return (status);
}


/* parameterSetValue -- Set the value of a parameter, and notify all clients
 * via the posted callback procedures that the parameter value has changed.
 *
 *  Usage:	setValue <new-value>
 */
static int 
parameterSetValue (msg, tcl, argc, argv)
MsgContext msg;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	ParameterObject obj = (ParameterObject) msg->object[msg->level];
	register ObmContext obm = obj->parameter.obm;
	register ParameterPrivate pp = &obj->parameter;
	char *new_value, *old_value;
	ObmCallback cb, cbl[128];
	int ncb, status, i;

	/* Assign new value. */
	old_value = pp->value;
	pp->len_value = strlen (argv[1]);
	pp->value = new_value = XtMalloc (pp->len_value + 1);
	memmove (pp->value, argv[1], pp->len_value + 1);

	/* Safeguard callback list against changes by callback procs. */
	for (cb = pp->callback, ncb=0;  cb;  cb = cb->next)
	    cbl[ncb++] = cb;

	/* Notify clients that value has changed. */
	for (i=0;  i < ncb && (cb = cbl[i]) != NULL;  i++) {
/*printf ("setValue: i=%d  obj='%s' cb='%s' new='%s'\n", 
i, obj->core.name, cb->name, new_value);*/
	    status = Tcl_VarEval (obm->tcl,
		cb->name, " ",
		obj->core.name, " ",
		"{", old_value ? old_value : "", "} ",
		"{", new_value, "} ",
		NULL);
	    if (status != TCL_OK) {
		char *errstr = Tcl_GetVar (obm->tcl, "errorInfo", 0);
		fprintf (stderr, "Error on line %d in %s: %s\n",
		    obm->tcl->errorLine, cb->name,
		    errstr ? errstr : obm->tcl->result);
	    }
	}

	XtFree ((char *)old_value);
	Tcl_SetResult (obm->tcl, "", TCL_STATIC);
	return (TCL_OK);
}


/* parameterGetValue -- Get the value of a parameter.
 *
 *  Usage:	getValue
 */
static int 
parameterGetValue (msg, tcl, argc, argv)
MsgContext msg;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	ParameterObject obj = (ParameterObject) msg->object[msg->level];
	register ObmContext obm = obj->parameter.obm;
	register ParameterPrivate pp = &obj->parameter;

	Tcl_SetResult (obm->tcl, pp->value, TCL_STATIC);
	return (TCL_OK);
}


/* parameterNotify -- Notify the registered clients of a parameter as if the
 * value had changed.
 *
 *  Usage:	notify
 */
static int 
parameterNotify (msg, tcl, argc, argv)
MsgContext msg;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	ParameterObject obj = (ParameterObject) msg->object[msg->level];
	register ObmContext obm = obj->parameter.obm;
	register ParameterPrivate pp = &obj->parameter;
	ObmCallback cb;
	int status;

	/* Notify clients. */
	for (cb = pp->callback;  cb;  cb = cb->next) {
	    status = Tcl_VarEval (obm->tcl,
		cb->name, " ",
		obj->core.name, " ",
		"{", pp->value, "} ",
		"{", pp->value, "} ",
		NULL);
	    if (status != TCL_OK) {
		char *errstr = Tcl_GetVar (obm->tcl, "errorInfo", 0);
		fprintf (stderr, "Error on line %d in %s: %s\n",
		    obm->tcl->errorLine, cb->name,
		    errstr ? errstr : obm->tcl->result);
	    }
	}

	Tcl_SetResult (obm->tcl, "", TCL_STATIC);
	return (TCL_OK);
}


/* parameterAddCallback -- Add a callback procedure to the callback list for
 * a parameter.
 *
 *  Usage:	addCallback <procedure-name>
 */
static int 
parameterAddCallback (msg, tcl, argc, argv)
MsgContext msg;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	ParameterObject obj = (ParameterObject) msg->object[msg->level];
	register ParameterPrivate pp = &obj->parameter;
	ObmCallback cb, new_cb;

	/* Create callback record. */
	new_cb = (ObmCallback) XtCalloc (1, sizeof (obmCallback));
	strcpy (new_cb->name, argv[1]);

	/* Add callback to tail of callback list. */
	if (pp->callback) {
	    for (cb = pp->callback;  cb->next;  cb = cb->next)
		;
	    cb->next = new_cb;
	} else
	    pp->callback = new_cb;

	return (TCL_OK);
}


/* parameterDeleteCallback -- Delete a callback procedure previously registered
 * for a parameter.
 *
 *  Usage:	deleteCallback <procedure-name>
 */
static int 
parameterDeleteCallback (msg, tcl, argc, argv)
MsgContext msg;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	ParameterObject obj = (ParameterObject) msg->object[msg->level];
	register ParameterPrivate pp = &obj->parameter;
	ObmCallback cb, prev;

	/* Locate and delete procedure entry in callback list. */
	for (prev=NULL, cb=pp->callback;  cb;  prev=cb, cb=cb->next)
	    if (strcmp (cb->name, argv[1]) == 0) {
		if (prev)
		    prev->next = cb->next;
		else
		    pp->callback = cb->next;
		XtFree ((char *)cb);
		break;
	    }

	return (TCL_OK);
}