flattr.cpp 10.5 KB
Newer Older
thomas's avatar
thomas committed
1
2
3
4
/* 

flext - C++ layer for Max/MSP and pd (pure data) externals

thomas's avatar
thomas committed
5
Copyright (c) 2001-2005 Thomas Grill (gr@grrrr.org)
thomas's avatar
thomas committed
6
7
8
For information on usage and redistribution, and for a DISCLAIMER OF ALL
WARRANTIES, see the file, "license.txt," in this distribution.  

thomas's avatar
thomas committed
9
10
11
$LastChangedRevision$
$LastChangedDate$
$LastChangedBy$
thomas's avatar
thomas committed
12
13
14
15
16
17
18
19
*/

/*! \file flattr.cpp
    \brief Attribute handling for the flext base class
*/
 
#include "flext.h"
#include <string.h>
thomas's avatar
thomas committed
20
#include <ctype.h>
21
22
23

#include <set>

thomas's avatar
thomas committed
24

thomas's avatar
thomas committed
25
#ifdef __MWERKS__
thomas's avatar
thomas committed
26
27
28
29
30
#define STD std
#else
#define STD
#endif

thomas's avatar
thomas committed
31
flext_base::AttrItem::AttrItem(const t_symbol *t,metharg tp,methfun f,int fl):
thomas's avatar
thomas committed
32
	Item(NULL),index(0),
thomas's avatar
thomas committed
33
	flags(fl|afl_shown),
thomas's avatar
thomas committed
34
35
	argtp(tp),fun(f),
	counter(NULL),tag(t)
thomas's avatar
thomas committed
36
{}
thomas's avatar
thomas committed
37

38

39
/*
40
41
42
flext_base::AttrDataCont::AttrDataCont() {}

flext_base::AttrDataCont::~AttrDataCont()
thomas's avatar
thomas committed
43
{
44
45
	for(iterator it = begin(); it != end(); ++it)
		if(it.data()) delete it.data();
thomas's avatar
thomas committed
46
}
47
*/
thomas's avatar
thomas committed
48

49
50
51
52
53
54
55
flext_base::AttrDataCont::~AttrDataCont() { clear(); }

void flext_base::AttrDataCont::clear()
{
    for(iterator it(*this); it; ++it) delete it.data();
    TablePtrMap<const t_symbol *,AttrData *,8>::clear();
}
thomas's avatar
thomas committed
56

thomas's avatar
thomas committed
57
//! Add get and set attributes
thomas's avatar
thomas committed
58
void flext_base::AddAttrib(ItemCont *aa,ItemCont *ma,const t_symbol *asym,metharg tp,methfun gfun,methfun sfun)
thomas's avatar
thomas committed
59
{
thomas's avatar
thomas committed
60
	AttrItem *a,*b;
thomas's avatar
thomas committed
61

62
63
    FLEXT_ASSERT(asym != sym__ && asym != sym_list && asym != sym_float && asym != sym_symbol && asym != sym_anything);

64
	if(sfun) // if commented out, there will be a warning at run-time (more user-friendly)
thomas's avatar
thomas committed
65
	{
thomas's avatar
thomas committed
66
		a = new AttrItem(asym,tp,sfun,AttrItem::afl_set);
thomas's avatar
thomas committed
67
68
        a->index = aa->Members();
		aa->Add(a,asym); 
thomas's avatar
thomas committed
69
70

		// bind attribute to a method
thomas's avatar
thomas committed
71
		MethItem *mi = new MethItem(a);
thomas's avatar
thomas committed
72
		mi->SetArgs(sfun,1,new metharg(tp));
thomas's avatar
thomas committed
73
		ma->Add(mi,asym);
thomas's avatar
thomas committed
74
	}
thomas's avatar
thomas committed
75
76
	else
		a = NULL;
thomas's avatar
thomas committed
77

78
	if(gfun) // if commented out, there will be a warning at run-time (more user-friendly)
thomas's avatar
thomas committed
79
	{
thomas's avatar
thomas committed
80
		b = new AttrItem(asym,tp,gfun,AttrItem::afl_get);
thomas's avatar
thomas committed
81
82
        b->index = aa->Members();
		aa->Add(b,asym); 
thomas's avatar
thomas committed
83

thomas's avatar
thomas committed
84
		static char tmp[256] = "get";
thomas's avatar
thomas committed
85
		strcpy(tmp+3,GetString(asym));
thomas's avatar
thomas committed
86
87

		// bind attribute to a method
thomas's avatar
thomas committed
88
		MethItem *mi = new MethItem(b);
thomas's avatar
thomas committed
89
		mi->SetArgs(gfun,0,NULL);
thomas's avatar
thomas committed
90
		ma->Add(mi,MakeSymbol(tmp));
thomas's avatar
thomas committed
91
	}
thomas's avatar
thomas committed
92
93
94
95
96
97
98
	else
		b = NULL;

	if(a && b) {
		a->counter = b;
		b->counter = a;
	}
thomas's avatar
thomas committed
99
100
}

thomas's avatar
thomas committed
101
void flext_base::AddAttrib(const t_symbol *attr,metharg tp,methfun gfun,methfun sfun)
thomas's avatar
thomas committed
102
{
thomas's avatar
thomas committed
103
	if(HasAttributes())
thomas's avatar
thomas committed
104
		AddAttrib(ThAttrs(),ThMeths(),attr,tp,gfun,sfun);
thomas's avatar
thomas committed
105
106
107
108
	else
		error("%s - attribute procession is not enabled!",thisName());
}

thomas's avatar
thomas committed
109
void flext_base::AddAttrib(t_classid c,const t_symbol *attr,metharg tp,methfun gfun,methfun sfun)
thomas's avatar
thomas committed
110
111
112
113
{
	AddAttrib(ClAttrs(c),ClMeths(c),attr,tp,gfun,sfun);
}

114
115
void flext_base::ListAttrib(AtomList &la) const
{
thomas's avatar
thomas committed
116
	typedef TablePtrMap<int,const t_symbol *,32> AttrList;
117
	AttrList list[2];
thomas's avatar
thomas committed
118
    ItemCont *clattrhead = ClAttrs(thisClassId());
thomas's avatar
thomas committed
119

thomas's avatar
thomas committed
120
121
	int i;
	for(i = 0; i <= 1; ++i) {
thomas's avatar
thomas committed
122
123
124
        ItemCont *a = i?attrhead:clattrhead;
		if(a && a->Contained(0)) {
            ItemSet &ai = a->GetInlet();
125
            for(ItemSet::iterator as(ai); as; ++as) {
126
127
                for(Item *al = as.data(); al; al = al->nxt) {
					AttrItem *aa = (AttrItem *)al;
thomas's avatar
thomas committed
128
					list[i].insert(aa->index,as.key());
thomas's avatar
thomas committed
129
130
                    break;
                }
thomas's avatar
thomas committed
131
132
133
			}
		}
	}
thomas's avatar
thomas committed
134

thomas's avatar
   
thomas committed
135
	la((int)(list[0].size()+list[1].size()));
thomas's avatar
thomas committed
136
	int ix = 0;
137
	for(i = 0; i <= 1; ++i)
138
		for(AttrList::iterator it(list[i]); it; ++it) 
139
			SetSymbol(la[ix++],it.data());
thomas's avatar
thomas committed
140
141
}

thomas's avatar
thomas committed
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
int flext_base::CheckAttrib(int argc,const t_atom *argv)
{
	int offs = 0;
	for(; offs < argc; ++offs)
		if(IsString(argv[offs]) && *GetString(argv[offs]) == '@') break;
	return offs;
}

bool flext_base::InitAttrib(int argc,const t_atom *argv)
{
	int cur,nxt;
	for(cur = 0; cur < argc; cur = nxt) {
		// find next @symbol
		for(nxt = cur+1; nxt < argc; ++nxt)
			if(IsString(argv[nxt]) && *GetString(argv[nxt]) == '@') break;

		const t_symbol *tag = MakeSymbol(GetString(argv[cur])+1);
159
160

		// find puttable attribute
thomas's avatar
thomas committed
161
		AttrItem *attr = FindAttrib(tag,false,true);
162
		if(attr) {
163
			// make an entry (there are none beforehand...)
164
/*
165
166
167
168
169
170
171
172
173
			AttrDataCont::iterator it = attrdata->find(tag);
			if(it == attrdata->end()) {
				AttrDataCont::pair pair; 
				pair.key() = tag;
				pair.data() = new AttrData;
				it = attrdata->insert(attrdata->begin(),pair);
			}

			AttrData &a = *it.data();
174
175
176
177
			a.SetInit(true);
			a.SetInitValue(nxt-cur-1,argv+cur+1);

			// pass value to object
thomas's avatar
thomas committed
178
			SetAttrib(tag,attr,a.GetInitValue());
179
180
*/
			AttrData *a = attrdata->find(tag);
181
182
183
184
            if(!a) {
                AttrData *old = attrdata->insert(tag,a = new AttrData);
                FLEXT_ASSERT(!old);
            }
185
186
187
188
189
190

			a->SetInit(true);
			a->SetInitValue(nxt-cur-1,argv+cur+1);

			// pass value to object
			SetAttrib(tag,attr,a->GetInitValue());
191
		}
thomas's avatar
thomas committed
192
193
194
195
	}
	return true;
}

thomas's avatar
thomas committed
196
bool flext_base::ListAttrib() const
thomas's avatar
thomas committed
197
{
thomas's avatar
thomas committed
198
    if(HasAttributes()) {
199
        // defined in flsupport.cpp
200
		AtomListStatic<32> la;
201
		ListAttrib(la);
thomas's avatar
thomas committed
202
		ToOutAnything(GetOutAttr(),sym_attributes,la.Count(),la.Atoms());
thomas's avatar
thomas committed
203
204
205
206
207
208
		return true;
	}
	else
		return false;
}

thomas's avatar
thomas committed
209
flext_base::AttrItem *flext_base::FindAttrib(const t_symbol *tag,bool get,bool msg) const
thomas's avatar
thomas committed
210
{
thomas's avatar
thomas committed
211
212
    ItemCont *clattrhead = ClAttrs(thisClassId());

thomas's avatar
thomas committed
213
    // first search within object scope
thomas's avatar
thomas committed
214
215
	AttrItem *a = NULL;
    {
216
217
218
        for(Item *lst = attrhead->FindList(tag); lst; lst = lst->nxt) {
            AttrItem *b = (AttrItem *)lst;
            if(get?b->IsGet():b->IsSet()) { a = b; break; }
thomas's avatar
thomas committed
219
220
        }
    }
thomas's avatar
thomas committed
221
222

    // then (if nothing found) search within class scope
thomas's avatar
thomas committed
223
	if(!a) {
224
225
226
        for(Item *lst = clattrhead->FindList(tag); lst; lst = lst->nxt) {
            AttrItem *b = (AttrItem *)lst;
            if(get?b->IsGet():b->IsSet()) { a = b; break; }
thomas's avatar
thomas committed
227
        }
thomas's avatar
thomas committed
228
	}
229

thomas's avatar
thomas committed
230
    if(!a && msg) {
231
232
233
		// print a message
		error("%s - %s: attribute not found",thisName(),GetString(tag));
	}
thomas's avatar
thomas committed
234
235
	return a;
}
thomas's avatar
thomas committed
236

thomas's avatar
thomas committed
237
238
239
bool flext_base::SetAttrib(const t_symbol *tag,int argc,const t_atom *argv)
{
	// search for matching attribute
thomas's avatar
thomas committed
240
	AttrItem *a = FindAttrib(tag,false,true);
thomas's avatar
thomas committed
241
	return a && SetAttrib(tag,a,argc,argv);
thomas's avatar
thomas committed
242
}
thomas's avatar
thomas committed
243

thomas's avatar
thomas committed
244
bool flext_base::SetAttrib(const t_symbol *tag,AttrItem *a,int argc,const t_atom *argv)
thomas's avatar
thomas committed
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
{
	if(a->fun) {
		bool ok = true;

		t_any any;
		switch(a->argtp) {
		case a_float:
			if(argc == 1 && CanbeFloat(argv[0])) {
				any.ft = GetAFloat(argv[0]);
				((methfun_1)a->fun)(this,any);				
			}
			else ok = false;
			break;
		case a_int:
			if(argc == 1 && CanbeInt(argv[0])) {
				any.it = GetAInt(argv[0]);
				((methfun_1)a->fun)(this,any);				
			}
			else ok = false;
			break;
		case a_symbol:
			if(argc == 1 && IsSymbol(argv[0])) {
267
268
269
                t_atom at;
                GetParamSym(at,GetSymbol(argv[0]),thisCanvas());
				any.st = const_cast<t_symbol *>(GetSymbol(at));
thomas's avatar
thomas committed
270
271
272
273
				((methfun_1)a->fun)(this,any);				
			}
			else ok = false;
			break;
thomas's avatar
thomas committed
274
275
276
277
278
279
280
		case a_bool:
			if(argc == 1 && CanbeBool(argv[0])) {
				any.bt = GetABool(argv[0]);
				((methfun_1)a->fun)(this,any);				
			}
			else ok = false;
			break;
thomas's avatar
thomas committed
281
		case a_LIST: {
282
			AtomListStatic<16> la(argc);
thomas's avatar
thomas committed
283
284
			for(int i = 0; i < argc; ++i)
				if(IsSymbol(argv[i])) 
thomas's avatar
thomas committed
285
					GetParamSym(la[i],GetSymbol(argv[i]),thisCanvas());
thomas's avatar
thomas committed
286
287
288
289
				else
					la[i] = argv[i];

			any.vt = &la;
thomas's avatar
thomas committed
290
291
			((methfun_1)a->fun)(this,any);				
			break;
thomas's avatar
thomas committed
292
		}
thomas's avatar
thomas committed
293
294
		default:
			ERRINTERNAL();
thomas's avatar
thomas committed
295
		}
thomas's avatar
thomas committed
296
297

		if(!ok)
thomas's avatar
thomas committed
298
			post("%s - wrong arguments for attribute %s",thisName(),GetString(tag));
thomas's avatar
thomas committed
299
300
	}
	else
thomas's avatar
thomas committed
301
		post("%s - attribute %s has no get method",thisName(),GetString(tag));
thomas's avatar
thomas committed
302
303
304
	return true;
}

thomas's avatar
thomas committed
305

thomas's avatar
thomas committed
306
bool flext_base::GetAttrib(const t_symbol *tag,AttrItem *a,AtomList &la) const
thomas's avatar
thomas committed
307
{
thomas's avatar
thomas committed
308
	bool ok = true;
thomas's avatar
thomas committed
309
	// main attribute tag
thomas's avatar
thomas committed
310
	if(a) {
thomas's avatar
thomas committed
311
		if(a->fun) {
thomas's avatar
thomas committed
312
313
314
			t_any any;
			switch(a->argtp) {
			case a_float: {
thomas's avatar
thomas committed
315
				((methfun_1)a->fun)(const_cast<flext_base *>(this),any);				
thomas's avatar
thomas committed
316
317
318
319
320
				la(1);
				SetFloat(la[0],any.ft);
				break;
			}
			case a_int: {
thomas's avatar
thomas committed
321
				((methfun_1)a->fun)(const_cast<flext_base *>(this),any);				
thomas's avatar
thomas committed
322
323
324
325
				la(1);
				SetInt(la[0],any.it);
				break;
			}
thomas's avatar
thomas committed
326
327
328
329
330
331
			case a_bool: {
				((methfun_1)a->fun)(const_cast<flext_base *>(this),any);				
				la(1);
				SetBool(la[0],any.bt);
				break;
			}
thomas's avatar
thomas committed
332
			case a_symbol: {
thomas's avatar
thomas committed
333
				((methfun_1)a->fun)(const_cast<flext_base *>(this),any);				
thomas's avatar
thomas committed
334
335
336
337
338
339
				la(1);
				SetSymbol(la[0],any.st);
				break;
			}
			case a_LIST: {
				any.vt = &la;
thomas's avatar
thomas committed
340
				((methfun_1)a->fun)(const_cast<flext_base *>(this),any);				
thomas's avatar
thomas committed
341
342
343
344
				break;
			}
			default:
				ERRINTERNAL();
thomas's avatar
thomas committed
345
				ok = false;
thomas's avatar
thomas committed
346
347
			}
		}
thomas's avatar
thomas committed
348
		else {
thomas's avatar
thomas committed
349
			post("%s - attribute %s has no get method",thisName(),GetString(tag));
thomas's avatar
thomas committed
350
351
			ok = false;
		}
thomas's avatar
thomas committed
352
	}
thomas's avatar
thomas committed
353
	else {
thomas's avatar
thomas committed
354
		error("%s - %s: attribute not found",thisName(),GetString(tag));
thomas's avatar
thomas committed
355
356
357
358
359
		ok = false;
	}
	return ok;
}

thomas's avatar
thomas committed
360
361
362
bool flext_base::GetAttrib(const t_symbol *s,AtomList &a) const
{
	AttrItem *attr = FindAttrib(s,true);
thomas's avatar
thomas committed
363
	return attr && GetAttrib(s,attr,a);
thomas's avatar
thomas committed
364
365
}

thomas's avatar
thomas committed
366
//! \param tag symbol "get[attribute]"
thomas's avatar
thomas committed
367
bool flext_base::DumpAttrib(const t_symbol *tag,AttrItem *a) const
thomas's avatar
thomas committed
368
{
369
	AtomListStatic<16> la;
thomas's avatar
thomas committed
370
	bool ret = GetAttrib(tag,a,la);
thomas's avatar
thomas committed
371
372
373
	if(ret) {
		ToOutAnything(GetOutAttr(),a->tag,la.Count(),la.Atoms());
	}
thomas's avatar
thomas committed
374
	return ret;
thomas's avatar
thomas committed
375
376
}

thomas's avatar
thomas committed
377
bool flext_base::DumpAttrib(const t_symbol *attr) const
thomas's avatar
thomas committed
378
{
thomas's avatar
thomas committed
379
	AttrItem *item = FindAttrib(attr,true);
thomas's avatar
thomas committed
380
	return item && DumpAttrib(attr,item);
thomas's avatar
thomas committed
381
382
}

thomas's avatar
thomas committed
383
bool flext_base::BangAttrib(const t_symbol *attr,AttrItem *item)
thomas's avatar
thomas committed
384
{
385
	AtomListStatic<16> val;
thomas's avatar
thomas committed
386
387
388
389
390
	AttrItem *item2;
	if(!item->IsGet()) 
		item = item->Counterpart();
	if(item) {
		item2 = item->Counterpart();
thomas's avatar
thomas committed
391
		return item2 && GetAttrib(attr,item,val) && SetAttrib(attr,item2,val);
thomas's avatar
thomas committed
392
393
394
395
	}
	else
		return false;
}
thomas's avatar
thomas committed
396

thomas's avatar
thomas committed
397
398
399
bool flext_base::BangAttrib(const t_symbol *attr)
{
	AttrItem *item = FindAttrib(attr,true);
thomas's avatar
thomas committed
400
	return item && BangAttrib(attr,item);
thomas's avatar
thomas committed
401
402
403
404
}

bool flext_base::BangAttribAll()
{
thomas's avatar
thomas committed
405
406
    ItemCont *clattrhead = ClAttrs(thisClassId());

thomas's avatar
thomas committed
407
408
409
410
	for(int i = 0; i <= 1; ++i) {
        ItemCont *a = i?attrhead:clattrhead;
		if(a) {
            ItemSet &ai = a->GetInlet(); // \todo need to check for presence of inlet 0?
411
/*
thomas's avatar
thomas committed
412
            for(ItemSet::iterator as = ai.begin(); as != ai.end(); ++as) {
413
414
415
                for(Item *al = as.data(); al; al = al->nxt) {
					AttrItem *a = (AttrItem *)al;
	        		if(a->IsGet() && a->BothExist()) BangAttrib(as.key(),a);
thomas's avatar
thomas committed
416
417
                }
			}
418
419
420
421
422
423
424
*/
            for(ItemSet::iterator as(ai); as; ++as) {
                for(Item *al = as.data(); al; al = al->nxt) {
					AttrItem *a = (AttrItem *)al;
	        		if(a->IsGet() && a->BothExist()) BangAttrib(as.key(),a);
                }
			}
thomas's avatar
thomas committed
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
		}
	}
	return true;
}

bool flext_base::ShowAttrib(AttrItem *a,bool show) const
{
	if(show) a->flags |= AttrItem::afl_shown;
	else a->flags &= ~AttrItem::afl_shown;

	// also change counterpart, if present
	AttrItem *ca = a->Counterpart();
	if(ca) {
		if(show) ca->flags |= AttrItem::afl_shown;
		else ca->flags &= ~AttrItem::afl_shown;
	}
	return true;
}

bool flext_base::ShowAttrib(const t_symbol *attr,bool show) const
thomas's avatar
thomas committed
445
{
thomas's avatar
thomas committed
446
	AttrItem *item = FindAttrib(attr,true);
thomas's avatar
thomas committed
447
	return item && ShowAttrib(item,show);
thomas's avatar
thomas committed
448
}