Index: cpp/frams/param/param.cpp
===================================================================
--- cpp/frams/param/param.cpp	(revision 325)
+++ cpp/frams/param/param.cpp	(revision 326)
@@ -499,15 +499,8 @@
 }
 
-static bool stringIsNumeric(const char* str)
-{//   /-?.?[0-9]+/
-	if (!str) return false;
-	if (*str == '-') str++;
-	if (*str == '.') str++;
-	return isdigit(*str) != 0;
-}
-
 int ParamInterface::setInt(int i, const char* str)
 {
-	if (!stringIsNumeric(str))
+	paInt value;
+	if (!ExtValue::parseInt(str,value,false,true))
 	{
 		paInt mn, mx, def;
@@ -518,10 +511,11 @@
 	}
 	else
-		return setInt(i, ExtValue::getInt(str));
+		return setInt(i, value);
 }
 
 int ParamInterface::setDouble(int i, const char* str)
 {
-	if (!stringIsNumeric(str))
+	double value;
+	if (!ExtValue::parseDouble(str,value,true))
 	{
 		double mn, mx, def;
@@ -532,5 +526,5 @@
 	}
 	else
-		return setDouble(i, ExtValue::getDouble(str));
+		return setDouble(i, value);
 }
 
Index: cpp/frams/param/paramobj.cpp
===================================================================
--- cpp/frams/param/paramobj.cpp	(revision 325)
+++ cpp/frams/param/paramobj.cpp	(revision 326)
@@ -7,159 +7,215 @@
 #include <common/nonstd_stl.h>
 
-static const char* maybedup(bool dup,const char* src)
-{ return dup ? (src?strdup(src):0) : src; }
+static const char* maybedup(bool dup, const char* src)
+{
+	return dup ? (src ? strdup(src) : 0) : src;
+}
 static void maybefree(void* mem)
-{ if (mem) free(mem); }
-
-ParamEntry* ParamObject::makeParamTab(ParamInterface *pi,bool stripgroups,bool stripproc,
-				      int firstprop, int maxprops, bool dupentries, int flagsexclude)
-{
-ParamEntry *tab,*t;
-int i,n,offs;
-static ExtValue ex;
-int count=0,gcount=1;
-if (!stripgroups) gcount=pi->getGroupCount();
-if (stripproc||flagsexclude)
-for (int i=firstprop;i < pi->getPropCount();i++)
-	{
-	const char*t=pi->type(i);
-	if ((!stripproc)||(strchr("dfsox",*t)))
-		if ((!flagsexclude)||(!(pi->flags(i)&flagsexclude)))
-			if (++count >= maxprops) break;
-	}
-else count=pi->getPropCount()-firstprop;
-t=tab=(ParamEntry*)malloc(sizeof(ParamEntry)*(count+gcount+1));
-t->group=(short)gcount;
-t->flags=(short)count;
-t->name=maybedup(dupentries,pi->getName());
-t->type=maybedup(dupentries,pi->getDescription());
-for(i=0;i<gcount;i++)
-	{
-	t->id=maybedup(dupentries,pi->grname(i));
-	t->offset=0;
-	t++;
-	}
-n=1; offs=1;
-for (i=firstprop;i < pi->getPropCount();i++)
-	{
-	if ((!stripproc)||(strchr("dfsox",*pi->type(i))))
+{
+	if (mem) free(mem);
+}
+
+int ParamObject::firstFieldOffset()
+{
+	static ParamObject dummy(0, NULL);
+	return ((char*)&dummy.fields[0]) - (char*)&dummy;
+}
+
+ParamEntry* ParamObject::makeParamTab(ParamInterface *pi, bool stripgroups, bool stripproc,
+	int firstprop, int maxprops, bool dupentries, int flagsexclude, bool addnew, const char* rename)
+{
+	ParamEntry *tab, *t;
+	int i, n, offset;
+	static ExtValue ex;
+	int count = 0, gcount = 1;
+	if (!stripgroups) gcount = pi->getGroupCount();
+	if (stripproc || flagsexclude)
+		for (int i = firstprop; i < pi->getPropCount(); i++)
 		{
-		if ((!flagsexclude)||(!(pi->flags(i)&flagsexclude)))
+		const char*t = pi->type(i);
+		if ((!stripproc) || (strchr("dfsox", *t)))
+			if ((!flagsexclude) || (!(pi->flags(i)&flagsexclude)))
+				if (++count >= maxprops) break;
+		}
+	else count = pi->getPropCount() - firstprop;
+	if (addnew) count++;
+	t = tab = (ParamEntry*)malloc(sizeof(ParamEntry)*(count + gcount + 1));
+	t->group = (short)gcount;
+	t->flags = (short)count;
+	t->name = maybedup(dupentries, rename ? rename : pi->getName());
+	t->type = maybedup(dupentries, pi->getDescription());
+	for (i = 0; i < gcount; i++)
+	{
+		t->id = maybedup(dupentries, pi->grname(i));
+		t->offset = 0;
+		t++;
+	}
+	n = 1;
+	offset = firstFieldOffset();
+	if (addnew)
+	{
+		t->id = maybedup(dupentries, "new");
+		t->name = maybedup(dupentries, "create new object");
+		SString tmp = SString::sprintf("p o%s()", pi->getName());
+		t->type = maybedup(dupentries, (const char*)tmp);
+		t->help = maybedup(dupentries, pi->help(i));
+		t->flags = 0;
+		t->group = 0;
+		t->offset = PARAM_ILLEGAL_OFFSET;
+		t->fun1 = (void*)p_new;
+		t->fun2 = 0;
+		t++;
+	}
+	for (i = firstprop; i < pi->getPropCount(); i++)
+	{
+		if ((!stripproc) || (strchr("dfsox", *pi->type(i))))
+		{
+			if ((!flagsexclude) || (!(pi->flags(i)&flagsexclude)))
 			{
-			t->offset=offs*sizeof(ExtValue);
-			if (*pi->type(i)!='x') t->offset+=(((char*)&ex.data[0])-((char*)&ex));
-			t->group=(short)(stripgroups?0:pi->group(i));
-			t->flags=(short)pi->flags(i);
-			t->fun1=0;
-			t->fun2=0;
-			t->id=maybedup(dupentries,pi->id(i));
-			t->name=maybedup(dupentries,pi->name(i));
-			t->type=maybedup(dupentries,pi->type(i));
-			t->help=maybedup(dupentries,pi->help(i));
-			t++; n++; offs++;
-			if (n>count) break;
+				t->offset = offset;
+				if (*pi->type(i) != 'x') t->offset += (((char*)&ex.data[0]) - ((char*)&ex));
+				t->group = (short)(stripgroups ? 0 : pi->group(i));
+				t->flags = (short)pi->flags(i);
+				t->fun1 = 0;
+				t->fun2 = 0;
+				t->id = maybedup(dupentries, pi->id(i));
+				t->name = maybedup(dupentries, pi->name(i));
+				t->type = maybedup(dupentries, pi->type(i));
+				t->help = maybedup(dupentries, pi->help(i));
+				t++; n++; offset += sizeof(ExtValue);
+				if (n > count) break;
 			}
 		}
 	}
-t->id=0; t->group=0; t->flags=dupentries?MUTPARAM_ALLOCENTRY:0;
-return tab;
+	t->id = 0; t->group = 0; t->flags = dupentries ? MUTPARAM_ALLOCENTRY : 0;
+	return tab;
+}
+
+void ParamObject::setParamTabText(ParamEntry *pe, const char* &ptr, const char* txt)
+{
+	if (!paramTabAllocatedString(pe))
+		return;
+	maybefree((char*)ptr);
+	ptr = maybedup(true, txt);
+}
+
+bool ParamObject::paramTabAllocatedString(ParamEntry *pe)
+{
+	return (pe[pe->flags + pe->group].flags & MUTPARAM_ALLOCENTRY) ? true : false;
 }
 
 void ParamObject::freeParamTab(ParamEntry *pe)
 {
-if (pe[pe->flags+pe->group].flags & MUTPARAM_ALLOCENTRY)
-	{
-	int i;
-	ParamEntry *e;
-	maybefree((void*)pe->name);
-	maybefree((void*)pe->type);
-	for (i=0,e=pe;i<pe->group;i++,e++) 
-		maybefree((void*)e->id);
-	for (i=pe->group,e=pe+i;i<pe->group+pe->flags;i++,e++) 
+	if (paramTabAllocatedString(pe))
+	{
+		int i;
+		ParamEntry *e;
+		maybefree((void*)pe->name);
+		maybefree((void*)pe->type);
+		for (i = 0, e = pe; i < pe->group; i++, e++)
+			maybefree((void*)e->id);
+		for (i = pe->group, e = pe + i; i < pe->group + pe->flags; i++, e++)
 		{
-		maybefree((void*)e->id);
-		maybefree((void*)e->name);
-		maybefree((void*)e->type);
-		maybefree((void*)e->help);
+			maybefree((void*)e->id);
+			maybefree((void*)e->name);
+			maybefree((void*)e->type);
+			maybefree((void*)e->help);
 		}
 	}
-free(pe);
-}
-
-bool ParamObject::paramTabEqual(ParamEntry *pe1,ParamEntry *pe2)
-{
-if (pe1->flags != pe2->flags) return false;
-ParamEntry *e1=pe1+pe1->group, *e2=pe2+pe2->group;
-for(int i=0;i<pe1->flags;i++,e1++,e2++)
-	{
-	if (strcmp(e1->id,e2->id)) return false;
-	if (strcmp(e1->name,e2->name)) return false;
-	if (strcmp(e1->type,e2->type)) return false;
-	if (e1->offset!=e2->offset) return false;
-	}
-return true;
-}
-
-void* ParamObject::makeObject(ParamEntry *tab)
-{
-if (!tab) return 0;
-int n=tab->flags;
-if (!n) return 0;
-ExtValue *ret=new ExtValue[n+1];
-ret->setInt(n);
-ExtValue *v=ret+1;
-tab+=tab->group;
-for (;n>0;n--,v++,tab++)
-	switch(*tab->type)
-	{
-	case 'd': v->setInt(0); break;
-	case 'f': v->setDouble(0); break;
-	case 's': v->setString(SString::empty()); break;
-	case 'o': v->setObject(ExtObject()); break;
-	case 'x': break;
-	}
-return ret;
-}
-
-void ParamObject::copyObject(void* dst,void* src)
-{
-if ((!dst)||(!src)) return;
-ExtValue *s=(ExtValue *)src;
-ExtValue *d=(ExtValue *)dst;
-int n=min(s->getInt(),d->getInt());
-s++; d++;
-for (int i=0;i<n;i++,d++,s++)
-	*d=*s;
+	free(pe);
+}
+
+bool ParamObject::paramTabEqual(ParamEntry *pe1, ParamEntry *pe2)
+{
+	if (pe1->flags != pe2->flags) return false;
+	ParamEntry *e1 = pe1 + pe1->group, *e2 = pe2 + pe2->group;
+	for (int i = 0; i < pe1->flags; i++, e1++, e2++)
+	{
+		if (strcmp(e1->id, e2->id)) return false;
+		if (strcmp(e1->name, e2->name)) return false;
+		if (strcmp(e1->type, e2->type)) return false;
+		if (e1->offset != e2->offset) return false;
+	}
+	return true;
+}
+
+void ParamObject::p_new(void* obj, ExtValue *args, ExtValue *ret)
+{
+	ParamObject *this_obj = (ParamObject*)obj;
+	ParamObject *po = makeObject(this_obj->par.getParamTab());
+	ret->setObject(ExtObject(&this_obj->par, po));
+	po->par.setDefault();
+}
+
+ParamObject::ParamObject(int _numfields, ParamEntry *_tab)
+{
+	numfields = _numfields;
+	par.setParamTab(_tab);
+	par.select(this);
+	for (int i = 0; i < numfields; i++)
+		new(&fields[i])ExtValue();
+}
+
+ParamObject::~ParamObject()
+{
+	for (int i = 0; i < numfields; i++)
+		fields[i].~ExtValue();
+}
+
+ParamObject* ParamObject::makeObject(ParamEntry *tab)
+{
+	if (!tab) return 0;
+	int n = tab->flags;
+	if (!n) return 0;
+	ParamObject *obj = new(n)ParamObject(n, tab); // new(n): allocate n fields ; ParamObject(n,...): tell the object it has n fields
+	ExtValue *v = &obj->fields[0];
+	tab += tab->group;
+	for (; n > 0; n--, tab++)
+		switch (*tab->type)
+	{
+		case 'd': v->setInt(0); v++; break;
+		case 'f': v->setDouble(0); v++; break;
+		case 's': v->setString(SString::empty()); v++; break;
+		case 'o': v->setObject(ExtObject()); v++; break;
+		case 'x': v++; break;
+	}
+	return obj;
+}
+
+void ParamObject::operator=(const ParamObject& src)
+{
+	const ExtValue *s = &src.fields[0];
+	ExtValue *d = &fields[0];
+	int n = min(numfields, src.numfields);
+	for (int i = 0; i < n; i++, d++, s++)
+		*d = *s;
+}
+
+ParamObject* ParamObject::clone()
+{
+	ParamObject *c = new(numfields)ParamObject(numfields, par.getParamTab());
+	*c = *this;
+	return c;
+}
+
+void ParamObject::copyObject(void* dst, void* src)
+{
+	if ((!dst) || (!src)) return;
+	ParamObject *s = (ParamObject*)src;
+	ParamObject *d = (ParamObject*)dst;
+	*d = *s;
 }
 
 void* ParamObject::dupObject(void* src)
 {
-if (!src) return NULL;
-ExtValue *s=(ExtValue *)src;
-int n=s->getInt();
-
-ExtValue *ret=new ExtValue[n+1];
-ExtValue *d=ret;
-d->setInt(n);
-d++; s++;
-
-for (int i=0;i<n;i++,d++,s++)
-	*d=*s;
-
-return ret;
+	if (!src) return NULL;
+	ParamObject *s = (ParamObject*)src;
+	return s->clone();
 }
 
 void ParamObject::freeObject(void* obj)
 {
-if (!obj) return;
-delete[] (ExtValue *)obj;
-}
-
-
-
-
-
-
-
-
-
+	if (!obj) return;
+	ParamObject *o = (ParamObject*)obj;
+	delete o;
+}
Index: cpp/frams/param/paramobj.h
===================================================================
--- cpp/frams/param/paramobj.h	(revision 325)
+++ cpp/frams/param/paramobj.h	(revision 326)
@@ -7,43 +7,70 @@
 
 #include "param.h"
+#include <frams/util/extvalue.h>
 
-class ParamObject
+class ParamObject : public DestrBase
 {
-  public:
-/** make a ParamEntry* array for use with Param object.
-    offsets in the array are calculated for the ExtObject array as the target.
-    valid array can be created with makeObject().
-    sample code:
-    @code
-    ParamInterface *pi=...; // any param interface
-    ParamEntry *tab=ParamObject::makeParamTab(pi);
-    void* obj=ParamObject::makeObject(tab);
-    void* obj2=ParamObject::makeObject(tab);
-    Param par(tab,obj);
-    par.set(...), par.get(...), par.load(...), par.save(...);
-    par.select(obj);
-    par.select(obj2);
-    ParamObject::freeObject(obj);
-    ParamObject::freeObject(obj2);
- */
-static ParamEntry* makeParamTab(ParamInterface *pi,bool stripgroups=0,bool stripproc=0,int firstprop=0,int maxprops=9999,bool dupentries=false, int flagsexclude=0);
+	ParamObject(int _numfields, ParamEntry *_tab);
+public:
+	int numfields;
+	Param par;
+	ExtValue fields[0];
+	ParamObject() { numfields = 0; }
+	~ParamObject();
 
-/** deallocate paramtab obtained from makeParamTab() */
-static void freeParamTab(ParamEntry *pe);
+	void* operator new(size_t s, int numfields){ return ::operator new(s + sizeof(ExtValue)*numfields); }
+	void* operator new(size_t s){ return ::operator new(s); }
+	ParamObject *clone();
+	static void p_new(void* obj, ExtValue *args, ExtValue *ret);
 
-static bool paramTabEqual(ParamEntry *pe1,ParamEntry *pe2);
+	void operator=(const ParamObject& src);
 
-/** @return the object, suitable for Param.select(...).
-    @return NULL if 'pi' has no usable properties */
-static void* makeObject(ParamEntry *tab);
+	static int firstFieldOffset();
 
-/** copy data from src to dst  */
-static void copyObject(void* dst,void* src);
+	/** make a ParamEntry* array for use with Param object.
+		offsets in the array are calculated for the ExtObject array as the target.
+		valid array can be created with makeObject().
+		sample code:
+		@code
+		ParamInterface *pi=...; // any param interface
+		ParamEntry *tab=ParamObject::makeParamTab(pi);
+		void* obj=ParamObject::makeObject(tab);
+		void* obj2=ParamObject::makeObject(tab);
+		Param par(tab,obj);
+		par.set(...), par.get(...), par.load(...), par.save(...);
+		par.select(obj);
+		par.select(obj2);
+		ParamObject::freeObject(obj);
+		ParamObject::freeObject(obj2);
+		*/
+	static ParamEntry* makeParamTab(ParamInterface *pi, bool stripgroups = 0, bool stripproc = 0, int firstprop = 0, int maxprops = 9999, bool dupentries = false, int flagsexclude = 0, bool addnew = false, const char* rename = NULL);
 
-/** duplicate object */
-static void* dupObject(void* obj);
+	/** deallocate paramtab obtained from makeParamTab() */
+	static void freeParamTab(ParamEntry *pe);
 
-/** delete all data in the array and deallocate it  */
-static void freeObject(void* obj);
+	static void setParamTabText(ParamEntry *pe, const char* &ptr, const char* txt);
+	static bool paramTabAllocatedString(ParamEntry *pe);
+	static bool paramTabEqual(ParamEntry *pe1, ParamEntry *pe2);
+
+	/** @return the object, suitable for Param.select(...).
+		@return NULL if 'pi' has no usable properties */
+	static ParamObject* makeObject(ParamEntry *tab);
+
+	/** copy data from src to dst (compatibility with older implementation), same as operator=  */
+	static void copyObject(void* dst, void* src);
+
+	/** duplicate object (compatibility with older implementation), same as clone()  */
+	static void* dupObject(void* obj);
+
+	/** delete all data in the array and deallocate it (compatibility with older implementation), same as delete */
+	static void freeObject(void* obj);
+};
+
+class ParamTabOwner
+{
+public:
+	ParamEntry *pe;
+	ParamTabOwner(ParamEntry *_pe) :pe(_pe) {}
+	~ParamTabOwner() { ParamObject::freeParamTab(pe); }
 };
 
Index: cpp/frams/util/extvalue.cpp
===================================================================
--- cpp/frams/util/extvalue.cpp	(revision 325)
+++ cpp/frams/util/extvalue.cpp	(revision 326)
@@ -610,20 +610,28 @@
 }
 
-bool ExtValue::parseInt(const char* s, paInt &result, bool strict)
+bool ExtValue::parseInt(const char* s, paInt &result, bool strict, bool error)
 {
 	ExtValue tmp;
 	const char* after = tmp.parseNumber(s, strict ? TInt : TUnknown);
-	if (after == NULL) return false;
-	if (after[0] != 0) return false;
+	if ((after == NULL) || (after[0] != 0))
+		{
+		if (error) 
+			FMprintf("ExtValue", "parseInt", FMLV_ERROR, "Could not parse '%s'%s", s, strict?" (strict)":"");
+		return false;
+		}
 	result = tmp.getInt();
 	return true;
 }
 
-bool ExtValue::parseDouble(const char* s, double &result)
+bool ExtValue::parseDouble(const char* s, double &result, bool error)
 {
 	ExtValue tmp;
 	const char* after = tmp.parseNumber(s, TDouble);
-	if (after == NULL) return false;
-	if (after[0] != 0) return false;
+	if ((after == NULL) || (after[0] != 0))
+		{
+		if (error) 
+			FMprintf("ExtValue", "parseDouble", FMLV_ERROR, "Could not parse '%s'", s);
+		return false;
+		}
 	result = tmp.getDouble();
 	return true;
@@ -633,7 +641,6 @@
 {
 	paInt result;
-	if (parseInt(s, result, strict))
+	if (parseInt(s, result, strict, true))
 		return result;
-	FMprintf("ExtValue", "getInt", FMLV_ERROR, "Could not parse '%s'%s", s, strict?" (strict)":"");
 	return 0;
 }
@@ -642,7 +649,6 @@
 {
 	double result;
-	if (parseDouble(s, result))
+	if (parseDouble(s, result, true))
 		return result;
-	FMprintf("ExtValue", "getDouble", FMLV_ERROR, "Could not parse '%s'", s);
 	return 0;
 }
@@ -729,9 +735,10 @@
 	if (in[0] == 0) return NULL;
 	while (isspace(*in)) in++;
-	bool minus = false;
-	if (((in[0] == '0') && (in[1] == 'x'))
-		|| (((minus = (in[0] == '-')) && (in[1] == '0') && (in[2] == 'x'))))
-	{
-		in += minus ? 3 : 2;
+	bool minus = (in[0] == '-');
+	bool plus = (in[0] == '+');
+	if (((in[0] == '0') && ((in[1] == 'x') || (in[1]=='X')))
+	     || (((minus || plus) && (in[1] == '0') && ((in[2] == 'x') || (in[2] == 'X')))))
+	{
+		in += (minus || plus) ? 3 : 2;
 		if (isspace(*in)) return NULL;
 		errno = 0;
Index: cpp/frams/util/extvalue.h
===================================================================
--- cpp/frams/util/extvalue.h	(revision 325)
+++ cpp/frams/util/extvalue.h	(revision 326)
@@ -160,6 +160,6 @@
 	void setString(const SString &v) { if (type != TString) setrs(v); else sdata() = v; }
 	void setObject(const ExtObject &src) { if (type != TObj) setro(src); else odata() = src; }
-	static bool parseInt(const char* s, paInt &result, bool strict = false);
-	static bool parseDouble(const char* s, double &result);
+	static bool parseInt(const char* s, paInt &result, bool strict, bool error);
+	static bool parseDouble(const char* s, double &result, bool error);
 	static paInt getInt(const char* s, bool strict = false);//< @param strict=true will fail on floating point
 	static double getDouble(const char* s);
