{
	domLibrary_physics_materialsRef ref = new domLibrary_physics_materials(dae);
	return ref;
}

/*
*
* http://example.com
*
*/

#include <dae.h>
#include <dae/daeDom.h>
#include <1.4/dom/domLibrary_physics_materials.h>
#include <dae/daeMetaCMPolicy.h>
#include <dae/daeMetaSequence.h>
#include <dae/daeMetaChoice.h>
#include <dae/daeMetaGroup.h>
#include <dae/daeMetaAny.h>
#include <dae/daeMetaElementAttribute.h>

namespace ColladaDOM141 {
daeElementRef
domLibrary_physics_materials::create(DAE& dae)

daeMetaElement *
domLibrary_physics_materials::registerElement(DAE& dae)
{
	daeMetaElement* meta = dae.getMeta(ID());
	if ( meta != NULL ) return meta;

	meta = new daeMetaElement(dae);
	dae.setMeta(ID(), *meta);
	meta->setName( "library_physics_materials" );
	meta->registerClass(domLibrary_physics_materials::create);

	daeMetaCMPolicy *cm = NULL;
	daeMetaElementAttribute *mea = NULL;
	cm = new daeMetaSequence( meta, cm, 0, 1, 1 );

	mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 );
	mea->setName( "asset" );
	mea->setOffset( daeOffsetOf(domLibrary_physics_materials,elemAsset) );
	mea->setElementType( domAsset::registerElement(dae) );
	cm->appendChild( mea );

	mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 );
	mea->setName( "physics_material" );
	mea->setOffset( daeOffsetOf(domLibrary_physics_materials,elemPhysics_material_array) );
	mea->setElementType( domPhysics_material::registerElement(dae) );
	cm->appendChild( mea );

	mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 );
	mea->setName( "extra" );
	mea->setOffset( daeOffsetOf(domLibrary_physics_materials,elemExtra_array) );
	mea->setElementType( domExtra::registerElement(dae) );
	cm->appendChild( mea );

	cm->setMaxOrdinal( 2 );
	meta->setCMRoot( cm );

	//	Add attribute: id
	{
		daeMetaAttribute *ma = new daeMetaAttribute;
		ma->setName( "id" );
		ma->setType( dae.getAtomicTypes().get("xsID"));
		ma->setOffset( daeOffsetOf( domLibrary_physics_materials , attrId ));
		ma->setContainer( meta );

		meta->appendAttribute(ma);
	}

	//	Add attribute: name
	{
		daeMetaAttribute *ma = new daeMetaAttribute;
		ma->setName( "name" );
		ma->setType( dae.getAtomicTypes().get("xsNCName"));
		ma->setOffset( daeOffsetOf( domLibrary_physics_materials , attrName ));
		ma->setContainer( meta );

		meta->appendAttribute(ma);
	}

	meta->setElementSize(sizeof(domLibrary_physics_materials));
	meta->validate();

	return meta;
}

} // ColladaDOM141


{
	push = focus = disabled = false;
	lspc = rspc = 0;
	limg_never_hide = false;
	rimg_never_hide = false;
	ink = disabledink = Null;
	align = valign = ALIGN_CENTER;
	accesskey = 0;
	accesspos = -1;
	font = StdFont();
	nowrap = false;
}

#include "CtrlLib.h"

namespace Upp {

void CtrlsImageLook(Value *look, int i, int n)
{
	while(n--)
		*look++ = CtrlsImg::Get(i++);
}

void CtrlsImageLook(Value *look, int i, const Image& image, const Color *color, int n)
{
	for(int q = 0; q < n; q++)
		*look++ = ChLookWith(CtrlsImg::Get(i++), image, *color++);
}

void CtrlsImageLook(Value *look, int i, const Image& image, int n)
{
	for(int q = 0; q < n; q++)
		*look++ = ChLookWith(CtrlsImg::Get(i++), image);
}

String DeAmp(const char *s)
{
	String out;
	for(; *s; out.Cat(*s++))
		if(*s == '&')
			out.Cat('&');
	return out;
}

Size GetSmartTextSize(const char *text, Font font, int cx) {
	if(*text == '\1') {
		Size sz;
		RichText txt = ParseQTF(text + 1);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		sz.cx = min(cx, txt.GetWidth());
		sz.cy = txt.GetHeight(Zoom(1, 1), sz.cx);
		return sz;
	}
	return GetTLTextSize(ToUnicode(text, CHARSET_DEFAULT), font);
}

int GetSmartTextHeight(const char *s, int cx, Font font) {
	if(*s == '\1') {
		Size sz;
		RichText txt = ParseQTF(s + 1);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		return txt.GetHeight(Zoom(1, 1), cx);
	}
	int cy = font.Info().GetHeight();
	int h = cy;
	while(*s) {
		if(*s == '\n')
			h += cy;
		s++;
	}
	return h;
}

void DrawSmartText(Draw& draw, int x, int y, int cx, const char *text, Font font, Color ink, int accesskey, Color qtf_ink) {
	if(*text == '\1') {
		RichText txt = ParseQTF(text + 1, accesskey);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		PaintInfo pi;
		pi.darktheme = Grayscale(SColorPaper()) < 100;
		pi.textcolor = qtf_ink;
		txt.Paint(draw, x, y, cx, pi);
		return;
	}
	DrawTLText(draw, x, y, cx, ToUnicode(text, CHARSET_DEFAULT), font, ink, accesskey);
}

bool CompareAccessKey(int accesskey, dword key)
{
	return accesskey && dword(ToUpper(accesskey & 255) - 'A' + K_ALT_A) == key;
}

int  ExtractAccessKey(const char *s, String& label)
{
	byte akey = 0;
	int  pos = 0;
	String text;
	bool qtf = *s == '\1';
	while(*s)
		if((*s == '&' && !qtf || *s == '\b') && s[1] && s[1] != '&') {
			akey = ToAscii(ToUpper(s[1]));
			pos = text.GetLength() + 1;
			s++;
		}
		else
			text.Cat(*s++);
	text.Shrink();
	label = text;
	return MAKELONG(akey, pos);
}

int  ChooseAccessKey(const char *text, dword used)
{
	for(const char *s = text; *s; s++) {
		byte ac = *s;
		if(ac < 128 && ac >= 'A' && ac <= 'Z' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return MAKELONG(ac, s - text + 1);
	}
	for(const char *s = text; *s; s++) {
		dword ac = ToUpper(*s);
		if(ac < 128 && ac >= 'A' && ac <= 'Z' && ac != 'I' && ac != 'L' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return ac;
	}
	for(const char *s = text; *s; s++) {
		dword ac = ToUpper(*s);
		if(ac < 128 && ac >= 'A' && ac <= 'Z' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return ac;
	}
	return 0;
}

DrawLabel::DrawLabel()

Size DrawLabel::GetSize(int txtcx) const
{
	return GetSize(txtcx, limg.GetSize(), lspc, rimg.GetSize(), rspc);
}

Size DrawLabel::GetSize(int txtcx, Size sz1, int lspc, Size sz2, int rspc) const
{
	Size isz(0, 0);
	Size txtsz = *text ? GetSmartTextSize(text, font, nowrap ? INT_MAX/2 : txtcx)
	                   : paintrect.GetStdSize();

	if(!IsNull(lspc)) {
		isz.cx = lspc;
		isz.cy = sz1.cy;
		isz.cx += sz1.cx;
	}

	if(!IsNull(rspc)) {
		isz.cx += rspc;
		if(sz2.cy > isz.cy) isz.cy = sz2.cy;
		isz.cx += sz2.cx;
	}

	isz.cy = max(txtsz.cy, max(sz1.cy, sz2.cy));
	isz.cx += txtsz.cx;

	return isz;
}

Image DisImage(const Image& m)
{
	Image mm = Grayscale(m, 200);
	ImageBuffer ib(mm);
	RGBA *s = ~ib;
	RGBA *e = s + ib.GetLength();
	while(s < e)
		(s++)->a /= 3;
	Premultiply(ib);
	return ib;
}

Image DisabledImage(const Image& img, bool dis)
{
	return dis ? MakeImage(img, GUI_GlobalStyle() == GUISTYLE_CLASSIC ? Etched : DisImage)
	           : img;
}

Color GetLabelTextColor(const Ctrl *ctrl)
{
	if(!IsLabelTextColorMismatch())
		return SColorLabel();
	while(ctrl) {
		if(!ctrl->IsTransparent()) {
			if(dynamic_cast<const TopWindow *>(ctrl) || dynamic_cast<const TabCtrl *>(ctrl) ||
			   dynamic_cast<const ToolBar *>(ctrl) || dynamic_cast<const MenuBar *>(ctrl) ||
			   dynamic_cast<const StaticRect *>(ctrl) || dynamic_cast<const StaticBarArea *>(ctrl))
				break;
			return SColorText();
		}
		ctrl = ctrl->GetParent();
	}
	return SColorLabel();
}

Size DrawLabel::Paint(Ctrl *ctrl, Draw& w, const Rect& r, bool visibleaccesskey) const
{
	int lspc = this->lspc;
	int rspc = this->rspc;
	Size sz1 = limg.GetSize();
	Size sz2 = rimg.GetSize();
	int txtcx = r.GetWidth() - sz1.cx - Nvl(lspc, 0) - sz2.cx - Nvl(rspc, 0);
	bool donowrap = nowrap && *text != '\1';
	Size txtsz = *text ? GetSmartTextSize(text, font, donowrap ? INT_MAX/2 : txtcx) : paintrect.GetStdSize();
	if(txtsz.cx) {
		if(!rimg_never_hide && txtsz.cx + sz1.cx + sz2.cx + Nvl(lspc, 0) + Nvl(rspc, 0) > r.GetWidth()) {
			sz2.cx = 0;
			rspc = 0;
		}
		if(!limg_never_hide && txtsz.cx + sz1.cx + sz2.cx + Nvl(lspc, 0) + Nvl(rspc, 0) > r.GetWidth()) {
			sz1.cx = 0;
			lspc = 0;
		}
	}
	Size isz = GetSize(txtcx, sz1, lspc, sz2, rspc);
	Point p = r.TopLeft(), ip;
	if(align == ALIGN_LEFT)
		p.x = r.left;
	else
	if(align == ALIGN_RIGHT)
		p.x = r.right - isz.cx;
	else
	if(align == ALIGN_CENTER)
		p.x = (r.right + r.left - isz.cx) / 2;
	if(valign == ALIGN_TOP)
		p.y = r.top;
	else
	if(valign == ALIGN_BOTTOM)
		p.y = r.bottom - isz.cy;
	else
	if(valign == ALIGN_CENTER)
		p.y = (r.bottom + r.top - txtsz.cy) / 2;
	Color color = disabled && !IsNull(disabledink) ? disabledink : ink;
	if(IsNull(color))
		color = disabled ? SColorDisabled : GetLabelTextColor(ctrl);
	int ix;
	if(IsNull(lspc))
		ix = r.left + push;
	else {
		ix = p.x + push;
		p.x += sz1.cx;
		p.x += lspc;
	}
	int iy = push + (r.top + r.bottom - sz1.cy) / 2;

	if(sz1.cx) {
		if(IsNull(lcolor))
			w.DrawImage(ix, iy, DisabledImage(limg, disabled));
		else
			w.DrawImage(ix, iy, limg, lcolor);
	}
	iy = push + (r.top + r.bottom - sz2.cy) / 2;
	ix = (IsNull(rspc) ? r.right - sz2.cx : p.x + txtsz.cx + rspc) + push;
	if(sz2.cx) {
		if(IsNull(rcolor))
			w.DrawImage(ix, iy, DisabledImage(rimg, disabled));
		else
			w.DrawImage(ix, iy, rimg, rcolor);
	}
	paintrect.Paint(w, p.x + push, p.y + push, txtsz.cx, isz.cy, color, Null);

	if(*text) {
		if(disabled && *text != '\1')
			DrawSmartText(w, p.x + push + 1, p.y + push + 1,
			              donowrap ? INT_MAX/2 : txtcx, text, font, SColorPaper);
		DrawSmartText(w, p.x + push, p.y + push, donowrap ? INT_MAX/2 : txtcx,
		              text, font, color, visibleaccesskey ? accesskey : 0);
		if(focus)
			DrawFocus(w, p.x - 2, p.y, txtsz.cx + 5, isz.cy);
	}

	return isz;
}

Size DrawLabel::Paint(Ctrl *ctrl, Draw& w, int x, int y, int cx, int cy, bool visibleaccesskey) const
{
	return Paint(ctrl, w, RectC(x, y, cx, cy), visibleaccesskey);
}

Size DrawLabel::Paint(Draw& w, const Rect& r, bool visibleaccesskey) const
{
	return Paint(NULL, w, r, visibleaccesskey);
}

Size DrawLabel::Paint(Draw& w, int x, int y, int cx, int cy, bool vak) const
{
	return Paint(w, RectC(x, y, cx, cy), vak);
}

void LabelBase::LabelUpdate() {}

LabelBase& LabelBase::SetLeftImage(const Image& img, int spc, bool never_hide) {
	lbl.limg = img;
	lbl.lspc = spc;
	lbl.limg_never_hide = never_hide;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetRightImage(const Image& img, int spc, bool never_hide) {
	lbl.rimg = img;
	lbl.rspc = spc;
	lbl.rimg_never_hide = never_hide;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetPaintRect(const PaintRect& paintrect) {
	lbl.paintrect = paintrect;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetText(const char *text) {
	lbl.text = text;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetFont(Font font) {
	if(lbl.font != font) {
		lbl.font = font;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::NoWrap(bool b)
{
	if(lbl.nowrap != b) {
		lbl.nowrap = b;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetInk(Color ink, Color disabledink) {
	if(lbl.ink != ink || lbl.disabledink != disabledink) {
		lbl.ink = ink;
		lbl.disabledink = disabledink;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetAlign(int align) {
	if(lbl.align != align) {
		lbl.align = align;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetVAlign(int valign) {
	if(lbl.valign != valign) {
		lbl.valign = valign;
		LabelUpdate();
	}
	return *this;
}

Size LabelBase::PaintLabel(Ctrl *ctrl, Draw& w, const Rect& r, bool disabled, bool push, bool focus, bool vak)
{
	DrawLabel lbl1 = lbl;
	lbl1.disabled = disabled;
	lbl1.push = push;
	lbl1.focus = focus;
	return lbl1.Paint(ctrl, w, r, vak);
}

Size LabelBase::PaintLabel(Ctrl *ctrl, Draw& w, int x, int y, int cx, int cy, bool disabled, bool push, bool focus, bool vak)
{
	return PaintLabel(ctrl, w, RectC(x, y, cx, cy), disabled, push, focus, vak);
}

Size LabelBase::PaintLabel(Draw& w, const Rect& r, bool disabled, bool push, bool focus, bool vak)
{
	return PaintLabel(NULL, w, r, disabled, push, focus, vak);
}

Size LabelBase::PaintLabel(Draw& w, int x, int y, int cx, int cy, bool disabled, bool push, bool focus, bool vak)
{
	return PaintLabel(w, RectC(x, y, cx, cy), disabled, push, focus, vak);
}

Size LabelBase::GetLabelSize() const
{
	return lbl.GetSize();
}

void LinkToolTipIn__();

LabelBase::~LabelBase() {
	LinkToolTipIn__();
}

void DrawFocus(Draw& w, int x, int y, int cx, int cy, Color c) {
	w.Clipoff(x, y, cx, cy);
	for(int a = 0; a < cx; a += CtrlImg::focus_h().GetWidth()) {
		w.DrawImage(a, 0, CtrlImg::focus_h(), c);
		w.DrawImage(a, cy - DPI(1), CtrlImg::focus_h(), c);
	}
	for(int a = 0; a < cy; a += CtrlImg::focus_v().GetHeight()) {
		w.DrawImage(0, a, CtrlImg::focus_v(), c);
		w.DrawImage(cx - DPI(1), a, CtrlImg::focus_v(), c);
	}
	w.End();
}

void DrawFocus(Draw& w, const Rect& r, Color c) {
	DrawFocus(w, r.left, r.top, r.Width(), r.Height(), c);
}

void DrawHorzDrop(Draw& w, int x, int y, int cx)
{
	w.DrawRect(x, y, cx, 2, SColorHighlight);
	w.DrawRect(x, y - 2, 1, 6, SColorHighlight);
	w.DrawRect(x + cx - 1, y - 2, 1, 6, SColorHighlight);
	w.DrawRect(x + 1, y - 1, 1, 4, SColorHighlight);
	w.DrawRect(x + cx - 2, y - 1, 1, 4, SColorHighlight);
}

void DrawVertDrop(Draw& w, int x, int y, int cy)
{
	w.DrawRect(x, y, 2, cy, SColorHighlight);
	w.DrawRect(x - 2, y, 6, 1, SColorHighlight);
	w.DrawRect(x - 2, y + cy - 1, 6, 1, SColorHighlight);
	w.DrawRect(x - 1, y + 1, 4, 1, SColorHighlight);
	w.DrawRect(x - 1, y + cy - 2, 4, 1, SColorHighlight);
}

Point GetDragScroll(Ctrl *ctrl, Point p, Size max)
{
	if(ctrl->IsReadOnly())
		return Point(0, 0);
	Size sz = ctrl->GetSize();
	Size sd = min(sz / 6, Size(16, 16));
	Point d(0, 0);
	if(p.x < sd.cx)
		d.x = p.x - sd.cx;
	if(p.x > sz.cx - sd.cx)
		d.x = p.x - sz.cx + sd.cx;
	if(p.y < sd.cy)
		d.y = p.y - sd.cy;
	if(p.y > sz.cy - sd.cy)
		d.y = p.y - sz.cy + sd.cy;
	d.x = minmax(d.x, -max.cx, max.cx);
	d.y = minmax(d.y, -max.cy, max.cy);
	return d;
}

Point GetDragScroll(Ctrl *ctrl, Point p, int max)
{
	return GetDragScroll(ctrl, p, Size(max, max));
}

Rect LookMargins(const Rect& r, const Value& ch)
{
	Rect m = ChMargins(ch);
	int fcy = GetStdFontCy();
	if(m.top + m.bottom + fcy > r.GetHeight())
		m.top = m.bottom = max((r.GetHeight() - fcy) / 2, 0);
	return m;
}

void ActiveEdgeFrame::FrameLayout(Rect& r)
{
	Rect m = LookMargins(r, edge[0]);
	r.left += m.left;
	r.right -= m.right;
	r.top += m.top;
	r.bottom -= m.bottom;
}

void ActiveEdgeFrame::FramePaint(Draw& w, const Rect& r)
{
	int i = 0;
	if(ctrl) {
		i = !ctrl->IsEnabled() || ctrl->IsReadOnly() ? CTRL_DISABLED
		    : button ? push : ctrl->HasFocus() ? CTRL_PRESSED
		    : mousein ? CTRL_HOT
		    : CTRL_NORMAL;
	}
	ChPaintEdge(w, r, edge[i]);
	if(!IsNull(coloredge))
		ChPaintEdge(w, r, coloredge, color);
}

void ActiveEdgeFrame::FrameAddSize(Size& sz)
{
	Rect m = ChMargins(edge[0]);
	sz.cx += m.left + m.right;
	sz.cy += m.top + m.bottom;
}

void ActiveEdgeFrame::Set(const Ctrl *ctrl_, const Value *edge_, bool active)
{
	ctrl = active ? ctrl_ : NULL;
	edge = edge_;
}

}


while(n--)
		*look++ = CtrlsImg::Get(i++);

#include "CtrlLib.h"

namespace Upp {

void CtrlsImageLook(Value *look, int i, int n)
{

}

void CtrlsImageLook(Value *look, int i, const Image& image, const Color *color, int n)
{
	for(int q = 0; q < n; q++)
		*look++ = ChLookWith(CtrlsImg::Get(i++), image, *color++);
}

void CtrlsImageLook(Value *look, int i, const Image& image, int n)
{
	for(int q = 0; q < n; q++)
		*look++ = ChLookWith(CtrlsImg::Get(i++), image);
}

String DeAmp(const char *s)
{
	String out;
	for(; *s; out.Cat(*s++))
		if(*s == '&')
			out.Cat('&');
	return out;
}

Size GetSmartTextSize(const char *text, Font font, int cx) {
	if(*text == '\1') {
		Size sz;
		RichText txt = ParseQTF(text + 1);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		sz.cx = min(cx, txt.GetWidth());
		sz.cy = txt.GetHeight(Zoom(1, 1), sz.cx);
		return sz;
	}
	return GetTLTextSize(ToUnicode(text, CHARSET_DEFAULT), font);
}

int GetSmartTextHeight(const char *s, int cx, Font font) {
	if(*s == '\1') {
		Size sz;
		RichText txt = ParseQTF(s + 1);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		return txt.GetHeight(Zoom(1, 1), cx);
	}
	int cy = font.Info().GetHeight();
	int h = cy;
	while(*s) {
		if(*s == '\n')
			h += cy;
		s++;
	}
	return h;
}

void DrawSmartText(Draw& draw, int x, int y, int cx, const char *text, Font font, Color ink, int accesskey, Color qtf_ink) {
	if(*text == '\1') {
		RichText txt = ParseQTF(text + 1, accesskey);
		txt.ApplyZoom(GetRichTextStdScreenZoom());
		PaintInfo pi;
		pi.darktheme = Grayscale(SColorPaper()) < 100;
		pi.textcolor = qtf_ink;
		txt.Paint(draw, x, y, cx, pi);
		return;
	}
	DrawTLText(draw, x, y, cx, ToUnicode(text, CHARSET_DEFAULT), font, ink, accesskey);
}

bool CompareAccessKey(int accesskey, dword key)
{
	return accesskey && dword(ToUpper(accesskey & 255) - 'A' + K_ALT_A) == key;
}

int  ExtractAccessKey(const char *s, String& label)
{
	byte akey = 0;
	int  pos = 0;
	String text;
	bool qtf = *s == '\1';
	while(*s)
		if((*s == '&' && !qtf || *s == '\b') && s[1] && s[1] != '&') {
			akey = ToAscii(ToUpper(s[1]));
			pos = text.GetLength() + 1;
			s++;
		}
		else
			text.Cat(*s++);
	text.Shrink();
	label = text;
	return MAKELONG(akey, pos);
}

int  ChooseAccessKey(const char *text, dword used)
{
	for(const char *s = text; *s; s++) {
		byte ac = *s;
		if(ac < 128 && ac >= 'A' && ac <= 'Z' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return MAKELONG(ac, s - text + 1);
	}
	for(const char *s = text; *s; s++) {
		dword ac = ToUpper(*s);
		if(ac < 128 && ac >= 'A' && ac <= 'Z' && ac != 'I' && ac != 'L' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return ac;
	}
	for(const char *s = text; *s; s++) {
		dword ac = ToUpper(*s);
		if(ac < 128 && ac >= 'A' && ac <= 'Z' && (Ctrl::AccessKeyBit(ac) & used) == 0)
			return ac;
	}
	return 0;
}

DrawLabel::DrawLabel()
{
	push = focus = disabled = false;
	lspc = rspc = 0;
	limg_never_hide = false;
	rimg_never_hide = false;
	ink = disabledink = Null;
	align = valign = ALIGN_CENTER;
	accesskey = 0;
	accesspos = -1;
	font = StdFont();
	nowrap = false;
}

Size DrawLabel::GetSize(int txtcx) const
{
	return GetSize(txtcx, limg.GetSize(), lspc, rimg.GetSize(), rspc);
}

Size DrawLabel::GetSize(int txtcx, Size sz1, int lspc, Size sz2, int rspc) const
{
	Size isz(0, 0);
	Size txtsz = *text ? GetSmartTextSize(text, font, nowrap ? INT_MAX/2 : txtcx)
	                   : paintrect.GetStdSize();

	if(!IsNull(lspc)) {
		isz.cx = lspc;
		isz.cy = sz1.cy;
		isz.cx += sz1.cx;
	}

	if(!IsNull(rspc)) {
		isz.cx += rspc;
		if(sz2.cy > isz.cy) isz.cy = sz2.cy;
		isz.cx += sz2.cx;
	}

	isz.cy = max(txtsz.cy, max(sz1.cy, sz2.cy));
	isz.cx += txtsz.cx;

	return isz;
}

Image DisImage(const Image& m)
{
	Image mm = Grayscale(m, 200);
	ImageBuffer ib(mm);
	RGBA *s = ~ib;
	RGBA *e = s + ib.GetLength();
	while(s < e)
		(s++)->a /= 3;
	Premultiply(ib);
	return ib;
}

Image DisabledImage(const Image& img, bool dis)
{
	return dis ? MakeImage(img, GUI_GlobalStyle() == GUISTYLE_CLASSIC ? Etched : DisImage)
	           : img;
}

Color GetLabelTextColor(const Ctrl *ctrl)
{
	if(!IsLabelTextColorMismatch())
		return SColorLabel();
	while(ctrl) {
		if(!ctrl->IsTransparent()) {
			if(dynamic_cast<const TopWindow *>(ctrl) || dynamic_cast<const TabCtrl *>(ctrl) ||
			   dynamic_cast<const ToolBar *>(ctrl) || dynamic_cast<const MenuBar *>(ctrl) ||
			   dynamic_cast<const StaticRect *>(ctrl) || dynamic_cast<const StaticBarArea *>(ctrl))
				break;
			return SColorText();
		}
		ctrl = ctrl->GetParent();
	}
	return SColorLabel();
}

Size DrawLabel::Paint(Ctrl *ctrl, Draw& w, const Rect& r, bool visibleaccesskey) const
{
	int lspc = this->lspc;
	int rspc = this->rspc;
	Size sz1 = limg.GetSize();
	Size sz2 = rimg.GetSize();
	int txtcx = r.GetWidth() - sz1.cx - Nvl(lspc, 0) - sz2.cx - Nvl(rspc, 0);
	bool donowrap = nowrap && *text != '\1';
	Size txtsz = *text ? GetSmartTextSize(text, font, donowrap ? INT_MAX/2 : txtcx) : paintrect.GetStdSize();
	if(txtsz.cx) {
		if(!rimg_never_hide && txtsz.cx + sz1.cx + sz2.cx + Nvl(lspc, 0) + Nvl(rspc, 0) > r.GetWidth()) {
			sz2.cx = 0;
			rspc = 0;
		}
		if(!limg_never_hide && txtsz.cx + sz1.cx + sz2.cx + Nvl(lspc, 0) + Nvl(rspc, 0) > r.GetWidth()) {
			sz1.cx = 0;
			lspc = 0;
		}
	}
	Size isz = GetSize(txtcx, sz1, lspc, sz2, rspc);
	Point p = r.TopLeft(), ip;
	if(align == ALIGN_LEFT)
		p.x = r.left;
	else
	if(align == ALIGN_RIGHT)
		p.x = r.right - isz.cx;
	else
	if(align == ALIGN_CENTER)
		p.x = (r.right + r.left - isz.cx) / 2;
	if(valign == ALIGN_TOP)
		p.y = r.top;
	else
	if(valign == ALIGN_BOTTOM)
		p.y = r.bottom - isz.cy;
	else
	if(valign == ALIGN_CENTER)
		p.y = (r.bottom + r.top - txtsz.cy) / 2;
	Color color = disabled && !IsNull(disabledink) ? disabledink : ink;
	if(IsNull(color))
		color = disabled ? SColorDisabled : GetLabelTextColor(ctrl);
	int ix;
	if(IsNull(lspc))
		ix = r.left + push;
	else {
		ix = p.x + push;
		p.x += sz1.cx;
		p.x += lspc;
	}
	int iy = push + (r.top + r.bottom - sz1.cy) / 2;

	if(sz1.cx) {
		if(IsNull(lcolor))
			w.DrawImage(ix, iy, DisabledImage(limg, disabled));
		else
			w.DrawImage(ix, iy, limg, lcolor);
	}
	iy = push + (r.top + r.bottom - sz2.cy) / 2;
	ix = (IsNull(rspc) ? r.right - sz2.cx : p.x + txtsz.cx + rspc) + push;
	if(sz2.cx) {
		if(IsNull(rcolor))
			w.DrawImage(ix, iy, DisabledImage(rimg, disabled));
		else
			w.DrawImage(ix, iy, rimg, rcolor);
	}
	paintrect.Paint(w, p.x + push, p.y + push, txtsz.cx, isz.cy, color, Null);

	if(*text) {
		if(disabled && *text != '\1')
			DrawSmartText(w, p.x + push + 1, p.y + push + 1,
			              donowrap ? INT_MAX/2 : txtcx, text, font, SColorPaper);
		DrawSmartText(w, p.x + push, p.y + push, donowrap ? INT_MAX/2 : txtcx,
		              text, font, color, visibleaccesskey ? accesskey : 0);
		if(focus)
			DrawFocus(w, p.x - 2, p.y, txtsz.cx + 5, isz.cy);
	}

	return isz;
}

Size DrawLabel::Paint(Ctrl *ctrl, Draw& w, int x, int y, int cx, int cy, bool visibleaccesskey) const
{
	return Paint(ctrl, w, RectC(x, y, cx, cy), visibleaccesskey);
}

Size DrawLabel::Paint(Draw& w, const Rect& r, bool visibleaccesskey) const
{
	return Paint(NULL, w, r, visibleaccesskey);
}

Size DrawLabel::Paint(Draw& w, int x, int y, int cx, int cy, bool vak) const
{
	return Paint(w, RectC(x, y, cx, cy), vak);
}

void LabelBase::LabelUpdate() {}

LabelBase& LabelBase::SetLeftImage(const Image& img, int spc, bool never_hide) {
	lbl.limg = img;
	lbl.lspc = spc;
	lbl.limg_never_hide = never_hide;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetRightImage(const Image& img, int spc, bool never_hide) {
	lbl.rimg = img;
	lbl.rspc = spc;
	lbl.rimg_never_hide = never_hide;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetPaintRect(const PaintRect& paintrect) {
	lbl.paintrect = paintrect;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetText(const char *text) {
	lbl.text = text;
	LabelUpdate();
	return *this;
}

LabelBase& LabelBase::SetFont(Font font) {
	if(lbl.font != font) {
		lbl.font = font;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::NoWrap(bool b)
{
	if(lbl.nowrap != b) {
		lbl.nowrap = b;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetInk(Color ink, Color disabledink) {
	if(lbl.ink != ink || lbl.disabledink != disabledink) {
		lbl.ink = ink;
		lbl.disabledink = disabledink;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetAlign(int align) {
	if(lbl.align != align) {
		lbl.align = align;
		LabelUpdate();
	}
	return *this;
}

LabelBase& LabelBase::SetVAlign(int valign) {
	if(lbl.valign != valign) {
		lbl.valign = valign;
		LabelUpdate();
	}
	return *this;
}

Size LabelBase::PaintLabel(Ctrl *ctrl, Draw& w, const Rect& r, bool disabled, bool push, bool focus, bool vak)
{
	DrawLabel lbl1 = lbl;
	lbl1.disabled = disabled;
	lbl1.push = push;
	lbl1.focus = focus;
	return lbl1.Paint(ctrl, w, r, vak);
}

Size LabelBase::PaintLabel(Ctrl *ctrl, Draw& w, int x, int y, int cx, int cy, bool disabled, bool push, bool focus, bool vak)
{
	return PaintLabel(ctrl, w, RectC(x, y, cx, cy), disabled, push, focus, vak);
}

Size LabelBase::PaintLabel(Draw& w, const Rect& r, bool disabled, bool push, bool focus, bool vak)
{
	return PaintLabel(NULL, w, r, disabled, push, focus, vak);
}

Size LabelBase::PaintLabel(Draw& w, int x, int y, int cx, int cy, bool disabled, bool push, bool focus, bool vak)
{
	return PaintLabel(w, RectC(x, y, cx, cy), disabled, push, focus, vak);
}

Size LabelBase::GetLabelSize() const
{
	return lbl.GetSize();
}

void LinkToolTipIn__();

LabelBase::~LabelBase() {
	LinkToolTipIn__();
}

void DrawFocus(Draw& w, int x, int y, int cx, int cy, Color c) {
	w.Clipoff(x, y, cx, cy);
	for(int a = 0; a < cx; a += CtrlImg::focus_h().GetWidth()) {
		w.DrawImage(a, 0, CtrlImg::focus_h(), c);
		w.DrawImage(a, cy - DPI(1), CtrlImg::focus_h(), c);
	}
	for(int a = 0; a < cy; a += CtrlImg::focus_v().GetHeight()) {
		w.DrawImage(0, a, CtrlImg::focus_v(), c);
		w.DrawImage(cx - DPI(1), a, CtrlImg::focus_v(), c);
	}
	w.End();
}

void DrawFocus(Draw& w, const Rect& r, Color c) {
	DrawFocus(w, r.left, r.top, r.Width(), r.Height(), c);
}

void DrawHorzDrop(Draw& w, int x, int y, int cx)
{
	w.DrawRect(x, y, cx, 2, SColorHighlight);
	w.DrawRect(x, y - 2, 1, 6, SColorHighlight);
	w.DrawRect(x + cx - 1, y - 2, 1, 6, SColorHighlight);
	w.DrawRect(x + 1, y - 1, 1, 4, SColorHighlight);
	w.DrawRect(x + cx - 2, y - 1, 1, 4, SColorHighlight);
}

void DrawVertDrop(Draw& w, int x, int y, int cy)
{
	w.DrawRect(x, y, 2, cy, SColorHighlight);
	w.DrawRect(x - 2, y, 6, 1, SColorHighlight);
	w.DrawRect(x - 2, y + cy - 1, 6, 1, SColorHighlight);
	w.DrawRect(x - 1, y + 1, 4, 1, SColorHighlight);
	w.DrawRect(x - 1, y + cy - 2, 4, 1, SColorHighlight);
}

Point GetDragScroll(Ctrl *ctrl, Point p, Size max)
{
	if(ctrl->IsReadOnly())
		return Point(0, 0);
	Size sz = ctrl->GetSize();
	Size sd = min(sz / 6, Size(16, 16));
	Point d(0, 0);
	if(p.x < sd.cx)
		d.x = p.x - sd.cx;
	if(p.x > sz.cx - sd.cx)
		d.x = p.x - sz.cx + sd.cx;
	if(p.y < sd.cy)
		d.y = p.y - sd.cy;
	if(p.y > sz.cy - sd.cy)
		d.y = p.y - sz.cy + sd.cy;
	d.x = minmax(d.x, -max.cx, max.cx);
	d.y = minmax(d.y, -max.cy, max.cy);
	return d;
}

Point GetDragScroll(Ctrl *ctrl, Point p, int max)
{
	return GetDragScroll(ctrl, p, Size(max, max));
}

Rect LookMargins(const Rect& r, const Value& ch)
{
	Rect m = ChMargins(ch);
	int fcy = GetStdFontCy();
	if(m.top + m.bottom + fcy > r.GetHeight())
		m.top = m.bottom = max((r.GetHeight() - fcy) / 2, 0);
	return m;
}

void ActiveEdgeFrame::FrameLayout(Rect& r)
{
	Rect m = LookMargins(r, edge[0]);
	r.left += m.left;
	r.right -= m.right;
	r.top += m.top;
	r.bottom -= m.bottom;
}

void ActiveEdgeFrame::FramePaint(Draw& w, const Rect& r)
{
	int i = 0;
	if(ctrl) {
		i = !ctrl->IsEnabled() || ctrl->IsReadOnly() ? CTRL_DISABLED
		    : button ? push : ctrl->HasFocus() ? CTRL_PRESSED
		    : mousein ? CTRL_HOT
		    : CTRL_NORMAL;
	}
	ChPaintEdge(w, r, edge[i]);
	if(!IsNull(coloredge))
		ChPaintEdge(w, r, coloredge, color);
}

void ActiveEdgeFrame::FrameAddSize(Size& sz)
{
	Rect m = ChMargins(edge[0]);
	sz.cx += m.left + m.right;
	sz.cy += m.top + m.bottom;
}

void ActiveEdgeFrame::Set(const Ctrl *ctrl_, const Value *edge_, bool active)
{
	ctrl = active ? ctrl_ : NULL;
	edge = edge_;
}

}


{
    delete [] szName;

    delete miModifyImageLeft;
    delete miModifyImageRight;

    goodRandomItems.clear();

    std::vector<SF_ListItem*>::iterator iterateAll = items.begin(), lim = items.end();

    while (iterateAll != lim) {
        delete *iterateAll;
        iterateAll++;
    }

    items.clear();
}

#include "MI_SelectField.h"

#include "ResourceManager.h"
extern CResourceManager* rm;

#include <cstring>

/**************************************
 * MI_SelectField Class
 **************************************/

MI_SelectField::MI_SelectField(gfxSprite * nspr, short x, short y, const char * name, short width, short indent) :
    UI_Control(x, y)
{
    spr = nspr;

    szName = new char[strlen(name) + 1];
    strcpy(szName, name);

    iWidth = width;
    iIndent = indent;

    mcItemChangedCode = MENU_CODE_NONE;
    mcControlSelectedCode = MENU_CODE_NONE;

    fAutoAdvance = false;
    fNoWrap = false;

    iValue = NULL;
    sValue = NULL;
    fValue = NULL;

    iIndex = 0;

    miModifyImageLeft = new MI_Image(nspr, ix + indent - 26, iy + 4, 32, 64, 26, 24, 4, 1, 8);
    miModifyImageLeft->Show(false);

    miModifyImageRight = new MI_Image(nspr, ix + iWidth - 16, iy + 4, 32, 88, 26, 24, 4, 1, 8);
    miModifyImageRight->Show(false);

    iAdjustmentY = width > 256 ? 0 : 128;

    fFastScroll = false;
}

MI_SelectField::MI_SelectField(const MI_SelectField& other)
    : UI_Control(other)
{
    spr = other.spr;
    szName = new char[strlen(other.szName) + 1];
    strcpy(szName, other.szName);

    SetData(other.iValue, other.sValue, other.fValue);

    for (unsigned i = 0; i < other.items.size(); i++)
        items.push_back(new SF_ListItem(*other.items[i]));

    SetIndex(other.iIndex); // sets current and index

    for (unsigned i = 0; i < other.goodRandomItems.size(); i++)
        goodRandomItems.push_back(new SF_ListItem(*other.goodRandomItems[i]));

    iWidth = other.iWidth;
    iIndent = other.iIndent;

    miModifyImageLeft = new MI_Image(spr, ix + iIndent - 26, iy + 4, 32, 64, 26, 24, 4, 1, 8);
    miModifyImageLeft->Show(false);

    miModifyImageRight = new MI_Image(spr, ix + iWidth - 16, iy + 4, 32, 88, 26, 24, 4, 1, 8);
    miModifyImageRight->Show(false);

    mcItemChangedCode = other.mcItemChangedCode;
    mcControlSelectedCode = other.mcControlSelectedCode;

    fAutoAdvance = other.fAutoAdvance;
    fNoWrap = other.fNoWrap;
    iAdjustmentY = other.iAdjustmentY;
    fFastScroll = other.fFastScroll;
}

MI_SelectField::~MI_SelectField()

void MI_SelectField::SetTitle(char * name)
{
    delete [] szName;
    szName = new char[strlen(name) + 1];
    strcpy(szName, name);
}

//Sets current selected item based on the items int value
bool MI_SelectField::SetKey(short iID)
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator search = items.begin();

    iIndex  = 0;
    while (search != items.end()) {
        if ((*search)->iValue == iID) {
            current = search;
            SetValues();
            return true;
        }

        ++search;
        ++iIndex;
    }

    return false;
}

bool MI_SelectField::SetIndex(unsigned short index)
{
    if (index >= items.size())
        return false;

    current = items.begin();

    for (unsigned int iSearch = 0; iSearch < index; iSearch++) {
        ++current;
    }

    iIndex = index;

    SetValues();

    return true;
}

void MI_SelectField::Add(std::string name, short ivalue, std::string svalue, bool fvalue, bool fhidden, bool fGoodRandom, short iIconOverride)
{
    SF_ListItem * item = new SF_ListItem(name, ivalue, svalue, fvalue, fhidden, iIconOverride);
    items.push_back(item);

    if (fGoodRandom)
        goodRandomItems.push_back(item);

    if (items.size() >= 1) {
        current = items.begin();
        iIndex = 0;
    }
}

bool MI_SelectField::HideItem(short iID, bool fhide)
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator search = items.begin(), lim = items.end();

    while (search != lim) {
        if ((*search)->iValue == iID) {
            if (current == search && fhide) {
                if (!MoveNext())
                    MovePrev();
            }

            (*search)->fHidden = fhide;
            return true;
        }

        ++search;
    }

    return false;
}

void MI_SelectField::HideAllItems(bool fHide)
{
    if (items.empty())
        return;

    std::vector<SF_ListItem*>::iterator itr = items.begin();

    while (itr != items.end()) {
        (*itr)->fHidden = fHide;
        ++itr;
    }
}

MenuCodeEnum MI_SelectField::Modify(bool modify)
{
    if (fDisable)
        return MENU_CODE_UNSELECT_ITEM;

    if (fAutoAdvance && modify) {
        if (!items.empty()) {
            if (current == --items.end()) {
                current = items.begin();
                iIndex = 0;
            } else {
                ++current;
                ++iIndex;
            }

            //Saves the values to the pointed to values
            SetValues();
        }

        return mcItemChangedCode;
    }

    if (MENU_CODE_NONE != mcControlSelectedCode)
        return mcControlSelectedCode;

    miModifyImageLeft->Show(modify);
    miModifyImageRight->Show(modify);
    fModifying = modify;
    return MENU_CODE_MODIFY_ACCEPTED;
}

MenuCodeEnum MI_SelectField::SendInput(CPlayerInput * playerInput)
{
    for (int iPlayer = 0; iPlayer < 4; iPlayer++) {
        short iNumMoves = 1;
        if (fFastScroll && playerInput->outputControls[iPlayer].menu_scrollfast.fDown)
            iNumMoves = 10;

        if (playerInput->outputControls[iPlayer].menu_right.fPressed || playerInput->outputControls[iPlayer].menu_down.fPressed) {
            bool fMoved = false;

            for (short iMove = 0; iMove < iNumMoves; iMove++)
                fMoved |= MoveNext();

            if (fMoved)
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_left.fPressed || playerInput->outputControls[iPlayer].menu_up.fPressed) {
            bool fMoved = false;

            for (short iMove = 0; iMove < iNumMoves; iMove++)
                fMoved |= MovePrev();

            if (fMoved)
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_random.fPressed) {
            if (MoveRandom())
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_select.fPressed || playerInput->outputControls[iPlayer].menu_cancel.fPressed) {
            miModifyImageLeft->Show(false);
            miModifyImageRight->Show(false);

            fModifying = false;

            SetValues();

            return MENU_CODE_UNSELECT_ITEM;
        }
    }

    return MENU_CODE_NONE;
}

void MI_SelectField::Update()
{
    miModifyImageRight->Update();
    miModifyImageLeft->Update();
}

void MI_SelectField::Draw()
{
    if (!fShow)
        return;

    if (iIndent == 0) {
        short iHalfWidth = iWidth >> 1;
        spr->draw(ix, iy, 0, (fSelected ? 32 : 0) + iAdjustmentY, iHalfWidth, 32);
        spr->draw(ix + iHalfWidth, iy, 512 - iHalfWidth, (fSelected ? 32 : 0) + iAdjustmentY, iWidth - iHalfWidth, 32);
    } else {
        spr->draw(ix, iy, 0, (fSelected ? 32 : 0) + iAdjustmentY, iIndent - 16, 32);
        spr->draw(ix + iIndent - 16, iy, 0, (fSelected ? 96 : 64), 32, 32);
        spr->draw(ix + iIndent + 16, iy, 528 - iWidth + iIndent, (fSelected ? 32 : 0) + iAdjustmentY, iWidth - iIndent - 16, 32);
    }

    if (iIndent> 0)
        rm->menu_font_large.drawChopRight(ix + 16, iy + 5, iIndent - 8, szName);

	// RFC
    if (!items.empty()) {
        if (iIndent > 0)
            rm->menu_font_large.drawChopRight(ix + iIndent + 8, iy + 5, iWidth - iIndent - 24, (*current)->sName.c_str());
        else
            rm->menu_font_large.drawChopRight(ix + 16, iy + 5, iWidth - 32, (*current)->sName.c_str());
    }

    //TODO: invert order
	bool drawLeft = true;

	if ((items.begin() != items.end() && current == items.begin()))
		drawLeft = false;

	if (!fNoWrap || drawLeft)
        miModifyImageLeft->Draw();

    //TODO: invert order

	bool drawRight = true;

		if (items.end() != items.begin()) {

			if (items.begin() != --items.end()) {

				if (current == --items.end())
					drawRight = false;
			}
		}

    if (drawRight || !fNoWrap)
        miModifyImageRight->Draw();
}

void MI_SelectField::SetValues()
{
    if (iValue)
        *iValue = (*current)->iValue;

    if (sValue)
        *sValue = (*current)->sValue;

    if (fValue)
        *fValue = (*current)->fValue;
}

bool MI_SelectField::MoveNext()
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator findNext = current;
    short iFindIndex = iIndex;

    while (true) {
        if (findNext == --items.end()) {
            if (fNoWrap) {
                return false;
            } else {
                findNext = items.begin();
                iFindIndex = 0;
            }
        } else {
            findNext++;
            iFindIndex++;
        }

        if (findNext == current)
            return false;

        if (!(*findNext)->fHidden) {
            current = findNext;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    return false;
}

bool MI_SelectField::MovePrev()
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator findPrev = current;
    short iFindIndex = iIndex;

    while (true) {
        if (findPrev == items.begin()) {
            if (fNoWrap) {
                return false;
            } else {
                findPrev = --items.end();
                iFindIndex = (signed short)items.size() - 1;
            }
        } else {
            findPrev--;
            iFindIndex--;
        }

        if (findPrev == current)
            return false;

        if (!(*findPrev)->fHidden) {
            current = findPrev;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    return false;
}

bool MI_SelectField::MoveRandom()
{
    //Can't pick a random item from a list of 1
    if (items.size() <= 1)
        return false;

    std::vector<SF_ListItem*>::iterator searchHidden = items.begin();

    short iHiddenCount = 0;
    while (searchHidden != items.end()) {
        if ((*searchHidden)->fHidden) {
            iHiddenCount++;
        }

        ++searchHidden;
    }

    //Can't pick a random item from a list containing only 1 non-hidden item
    if (items.size() - iHiddenCount <= 1)
        return false;

    std::vector<SF_ListItem*>::iterator findRandom = current;
    short iFindIndex = iIndex;

    while (true) {
        int index = RANDOM_INT(items.size());

        for (int k = 0; k < index; k++) {
            if (findRandom == --items.end()) {
                findRandom = items.begin();
                iFindIndex = 0;
            } else {
                ++findRandom;
                ++iFindIndex;
            }
        }

        if (findRandom != current && !(*findRandom)->fHidden) {
            current = findRandom;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    SetValues();
    return true;
}

MenuCodeEnum MI_SelectField::MouseClick(short iMouseX, short iMouseY)
{
    if (fDisable)
        return MENU_CODE_NONE;

    //If we are modifying this control, see if we clicked on a next/prev button
    if (fModifying) {
        short x, y, w, h;
        miModifyImageLeft->GetPositionAndSize(&x, &y, &w, &h);

        if (iMouseX >= x && iMouseX < x + w &&
                iMouseY >= y && iMouseY < y + h) {
            if (MovePrev()) {
                if (mcItemChangedCode == MENU_CODE_NONE)
                    return MENU_CODE_CLICKED;

                return mcItemChangedCode;
            }
        }

        miModifyImageRight->GetPositionAndSize(&x, &y, &w, &h);

        if (iMouseX >= x && iMouseX < x + w &&
                iMouseY >= y && iMouseY < y + h) {
            if (MoveNext()) {
                if (mcItemChangedCode == MENU_CODE_NONE)
                    return MENU_CODE_CLICKED;

                return mcItemChangedCode;
            }
        }
    }

    //Otherwise just check to see if we clicked on the whole control
    if (iMouseX >= ix && iMouseX < ix + iWidth && iMouseY >= iy && iMouseY < iy + 32)
        return MENU_CODE_CLICKED;

    //Otherwise this control wasn't clicked at all
    return MENU_CODE_NONE;
}

void MI_SelectField::Refresh()
{
    if (iValue)
        SetKey(*iValue);
    else if (fValue)
        SetKey(*fValue ? 1 : 0);
}


bool MI_SelectField::SetKey(short iID)
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator search = items.begin();

    iIndex  = 0;
    while (search != items.end()) {
        if ((*search)->iValue == iID) {
            current = search;
            SetValues();
            return true;
        }

        ++search;
        ++iIndex;
    }

    return false;
}

#include "MI_SelectField.h"

#include "ResourceManager.h"
extern CResourceManager* rm;

#include <cstring>

/**************************************
 * MI_SelectField Class
 **************************************/

MI_SelectField::MI_SelectField(gfxSprite * nspr, short x, short y, const char * name, short width, short indent) :
    UI_Control(x, y)
{
    spr = nspr;

    szName = new char[strlen(name) + 1];
    strcpy(szName, name);

    iWidth = width;
    iIndent = indent;

    mcItemChangedCode = MENU_CODE_NONE;
    mcControlSelectedCode = MENU_CODE_NONE;

    fAutoAdvance = false;
    fNoWrap = false;

    iValue = NULL;
    sValue = NULL;
    fValue = NULL;

    iIndex = 0;

    miModifyImageLeft = new MI_Image(nspr, ix + indent - 26, iy + 4, 32, 64, 26, 24, 4, 1, 8);
    miModifyImageLeft->Show(false);

    miModifyImageRight = new MI_Image(nspr, ix + iWidth - 16, iy + 4, 32, 88, 26, 24, 4, 1, 8);
    miModifyImageRight->Show(false);

    iAdjustmentY = width > 256 ? 0 : 128;

    fFastScroll = false;
}

MI_SelectField::MI_SelectField(const MI_SelectField& other)
    : UI_Control(other)
{
    spr = other.spr;
    szName = new char[strlen(other.szName) + 1];
    strcpy(szName, other.szName);

    SetData(other.iValue, other.sValue, other.fValue);

    for (unsigned i = 0; i < other.items.size(); i++)
        items.push_back(new SF_ListItem(*other.items[i]));

    SetIndex(other.iIndex); // sets current and index

    for (unsigned i = 0; i < other.goodRandomItems.size(); i++)
        goodRandomItems.push_back(new SF_ListItem(*other.goodRandomItems[i]));

    iWidth = other.iWidth;
    iIndent = other.iIndent;

    miModifyImageLeft = new MI_Image(spr, ix + iIndent - 26, iy + 4, 32, 64, 26, 24, 4, 1, 8);
    miModifyImageLeft->Show(false);

    miModifyImageRight = new MI_Image(spr, ix + iWidth - 16, iy + 4, 32, 88, 26, 24, 4, 1, 8);
    miModifyImageRight->Show(false);

    mcItemChangedCode = other.mcItemChangedCode;
    mcControlSelectedCode = other.mcControlSelectedCode;

    fAutoAdvance = other.fAutoAdvance;
    fNoWrap = other.fNoWrap;
    iAdjustmentY = other.iAdjustmentY;
    fFastScroll = other.fFastScroll;
}

MI_SelectField::~MI_SelectField()
{
    delete [] szName;

    delete miModifyImageLeft;
    delete miModifyImageRight;

    goodRandomItems.clear();

    std::vector<SF_ListItem*>::iterator iterateAll = items.begin(), lim = items.end();

    while (iterateAll != lim) {
        delete *iterateAll;
        iterateAll++;
    }

    items.clear();
}

void MI_SelectField::SetTitle(char * name)
{
    delete [] szName;
    szName = new char[strlen(name) + 1];
    strcpy(szName, name);
}

//Sets current selected item based on the items int value

bool MI_SelectField::SetIndex(unsigned short index)
{
    if (index >= items.size())
        return false;

    current = items.begin();

    for (unsigned int iSearch = 0; iSearch < index; iSearch++) {
        ++current;
    }

    iIndex = index;

    SetValues();

    return true;
}

void MI_SelectField::Add(std::string name, short ivalue, std::string svalue, bool fvalue, bool fhidden, bool fGoodRandom, short iIconOverride)
{
    SF_ListItem * item = new SF_ListItem(name, ivalue, svalue, fvalue, fhidden, iIconOverride);
    items.push_back(item);

    if (fGoodRandom)
        goodRandomItems.push_back(item);

    if (items.size() >= 1) {
        current = items.begin();
        iIndex = 0;
    }
}

bool MI_SelectField::HideItem(short iID, bool fhide)
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator search = items.begin(), lim = items.end();

    while (search != lim) {
        if ((*search)->iValue == iID) {
            if (current == search && fhide) {
                if (!MoveNext())
                    MovePrev();
            }

            (*search)->fHidden = fhide;
            return true;
        }

        ++search;
    }

    return false;
}

void MI_SelectField::HideAllItems(bool fHide)
{
    if (items.empty())
        return;

    std::vector<SF_ListItem*>::iterator itr = items.begin();

    while (itr != items.end()) {
        (*itr)->fHidden = fHide;
        ++itr;
    }
}

MenuCodeEnum MI_SelectField::Modify(bool modify)
{
    if (fDisable)
        return MENU_CODE_UNSELECT_ITEM;

    if (fAutoAdvance && modify) {
        if (!items.empty()) {
            if (current == --items.end()) {
                current = items.begin();
                iIndex = 0;
            } else {
                ++current;
                ++iIndex;
            }

            //Saves the values to the pointed to values
            SetValues();
        }

        return mcItemChangedCode;
    }

    if (MENU_CODE_NONE != mcControlSelectedCode)
        return mcControlSelectedCode;

    miModifyImageLeft->Show(modify);
    miModifyImageRight->Show(modify);
    fModifying = modify;
    return MENU_CODE_MODIFY_ACCEPTED;
}

MenuCodeEnum MI_SelectField::SendInput(CPlayerInput * playerInput)
{
    for (int iPlayer = 0; iPlayer < 4; iPlayer++) {
        short iNumMoves = 1;
        if (fFastScroll && playerInput->outputControls[iPlayer].menu_scrollfast.fDown)
            iNumMoves = 10;

        if (playerInput->outputControls[iPlayer].menu_right.fPressed || playerInput->outputControls[iPlayer].menu_down.fPressed) {
            bool fMoved = false;

            for (short iMove = 0; iMove < iNumMoves; iMove++)
                fMoved |= MoveNext();

            if (fMoved)
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_left.fPressed || playerInput->outputControls[iPlayer].menu_up.fPressed) {
            bool fMoved = false;

            for (short iMove = 0; iMove < iNumMoves; iMove++)
                fMoved |= MovePrev();

            if (fMoved)
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_random.fPressed) {
            if (MoveRandom())
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_select.fPressed || playerInput->outputControls[iPlayer].menu_cancel.fPressed) {
            miModifyImageLeft->Show(false);
            miModifyImageRight->Show(false);

            fModifying = false;

            SetValues();

            return MENU_CODE_UNSELECT_ITEM;
        }
    }

    return MENU_CODE_NONE;
}

void MI_SelectField::Update()
{
    miModifyImageRight->Update();
    miModifyImageLeft->Update();
}

void MI_SelectField::Draw()
{
    if (!fShow)
        return;

    if (iIndent == 0) {
        short iHalfWidth = iWidth >> 1;
        spr->draw(ix, iy, 0, (fSelected ? 32 : 0) + iAdjustmentY, iHalfWidth, 32);
        spr->draw(ix + iHalfWidth, iy, 512 - iHalfWidth, (fSelected ? 32 : 0) + iAdjustmentY, iWidth - iHalfWidth, 32);
    } else {
        spr->draw(ix, iy, 0, (fSelected ? 32 : 0) + iAdjustmentY, iIndent - 16, 32);
        spr->draw(ix + iIndent - 16, iy, 0, (fSelected ? 96 : 64), 32, 32);
        spr->draw(ix + iIndent + 16, iy, 528 - iWidth + iIndent, (fSelected ? 32 : 0) + iAdjustmentY, iWidth - iIndent - 16, 32);
    }

    if (iIndent> 0)
        rm->menu_font_large.drawChopRight(ix + 16, iy + 5, iIndent - 8, szName);

	// RFC
    if (!items.empty()) {
        if (iIndent > 0)
            rm->menu_font_large.drawChopRight(ix + iIndent + 8, iy + 5, iWidth - iIndent - 24, (*current)->sName.c_str());
        else
            rm->menu_font_large.drawChopRight(ix + 16, iy + 5, iWidth - 32, (*current)->sName.c_str());
    }

    //TODO: invert order
	bool drawLeft = true;

	if ((items.begin() != items.end() && current == items.begin()))
		drawLeft = false;

	if (!fNoWrap || drawLeft)
        miModifyImageLeft->Draw();

    //TODO: invert order

	bool drawRight = true;

		if (items.end() != items.begin()) {

			if (items.begin() != --items.end()) {

				if (current == --items.end())
					drawRight = false;
			}
		}

    if (drawRight || !fNoWrap)
        miModifyImageRight->Draw();
}

void MI_SelectField::SetValues()
{
    if (iValue)
        *iValue = (*current)->iValue;

    if (sValue)
        *sValue = (*current)->sValue;

    if (fValue)
        *fValue = (*current)->fValue;
}

bool MI_SelectField::MoveNext()
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator findNext = current;
    short iFindIndex = iIndex;

    while (true) {
        if (findNext == --items.end()) {
            if (fNoWrap) {
                return false;
            } else {
                findNext = items.begin();
                iFindIndex = 0;
            }
        } else {
            findNext++;
            iFindIndex++;
        }

        if (findNext == current)
            return false;

        if (!(*findNext)->fHidden) {
            current = findNext;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    return false;
}

bool MI_SelectField::MovePrev()
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator findPrev = current;
    short iFindIndex = iIndex;

    while (true) {
        if (findPrev == items.begin()) {
            if (fNoWrap) {
                return false;
            } else {
                findPrev = --items.end();
                iFindIndex = (signed short)items.size() - 1;
            }
        } else {
            findPrev--;
            iFindIndex--;
        }

        if (findPrev == current)
            return false;

        if (!(*findPrev)->fHidden) {
            current = findPrev;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    return false;
}

bool MI_SelectField::MoveRandom()
{
    //Can't pick a random item from a list of 1
    if (items.size() <= 1)
        return false;

    std::vector<SF_ListItem*>::iterator searchHidden = items.begin();

    short iHiddenCount = 0;
    while (searchHidden != items.end()) {
        if ((*searchHidden)->fHidden) {
            iHiddenCount++;
        }

        ++searchHidden;
    }

    //Can't pick a random item from a list containing only 1 non-hidden item
    if (items.size() - iHiddenCount <= 1)
        return false;

    std::vector<SF_ListItem*>::iterator findRandom = current;
    short iFindIndex = iIndex;

    while (true) {
        int index = RANDOM_INT(items.size());

        for (int k = 0; k < index; k++) {
            if (findRandom == --items.end()) {
                findRandom = items.begin();
                iFindIndex = 0;
            } else {
                ++findRandom;
                ++iFindIndex;
            }
        }

        if (findRandom != current && !(*findRandom)->fHidden) {
            current = findRandom;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    SetValues();
    return true;
}

MenuCodeEnum MI_SelectField::MouseClick(short iMouseX, short iMouseY)
{
    if (fDisable)
        return MENU_CODE_NONE;

    //If we are modifying this control, see if we clicked on a next/prev button
    if (fModifying) {
        short x, y, w, h;
        miModifyImageLeft->GetPositionAndSize(&x, &y, &w, &h);

        if (iMouseX >= x && iMouseX < x + w &&
                iMouseY >= y && iMouseY < y + h) {
            if (MovePrev()) {
                if (mcItemChangedCode == MENU_CODE_NONE)
                    return MENU_CODE_CLICKED;

                return mcItemChangedCode;
            }
        }

        miModifyImageRight->GetPositionAndSize(&x, &y, &w, &h);

        if (iMouseX >= x && iMouseX < x + w &&
                iMouseY >= y && iMouseY < y + h) {
            if (MoveNext()) {
                if (mcItemChangedCode == MENU_CODE_NONE)
                    return MENU_CODE_CLICKED;

                return mcItemChangedCode;
            }
        }
    }

    //Otherwise just check to see if we clicked on the whole control
    if (iMouseX >= ix && iMouseX < ix + iWidth && iMouseY >= iy && iMouseY < iy + 32)
        return MENU_CODE_CLICKED;

    //Otherwise this control wasn't clicked at all
    return MENU_CODE_NONE;
}

void MI_SelectField::Refresh()
{
    if (iValue)
        SetKey(*iValue);
    else if (fValue)
        SetKey(*fValue ? 1 : 0);
}


while (search != items.end()) {
        if ((*search)->iValue == iID) {
            current = search;
            SetValues();
            return true;
        }

        ++search;
        ++iIndex;
    }

#include "MI_SelectField.h"

#include "ResourceManager.h"
extern CResourceManager* rm;

#include <cstring>

/**************************************
 * MI_SelectField Class
 **************************************/

MI_SelectField::MI_SelectField(gfxSprite * nspr, short x, short y, const char * name, short width, short indent) :
    UI_Control(x, y)
{
    spr = nspr;

    szName = new char[strlen(name) + 1];
    strcpy(szName, name);

    iWidth = width;
    iIndent = indent;

    mcItemChangedCode = MENU_CODE_NONE;
    mcControlSelectedCode = MENU_CODE_NONE;

    fAutoAdvance = false;
    fNoWrap = false;

    iValue = NULL;
    sValue = NULL;
    fValue = NULL;

    iIndex = 0;

    miModifyImageLeft = new MI_Image(nspr, ix + indent - 26, iy + 4, 32, 64, 26, 24, 4, 1, 8);
    miModifyImageLeft->Show(false);

    miModifyImageRight = new MI_Image(nspr, ix + iWidth - 16, iy + 4, 32, 88, 26, 24, 4, 1, 8);
    miModifyImageRight->Show(false);

    iAdjustmentY = width > 256 ? 0 : 128;

    fFastScroll = false;
}

MI_SelectField::MI_SelectField(const MI_SelectField& other)
    : UI_Control(other)
{
    spr = other.spr;
    szName = new char[strlen(other.szName) + 1];
    strcpy(szName, other.szName);

    SetData(other.iValue, other.sValue, other.fValue);

    for (unsigned i = 0; i < other.items.size(); i++)
        items.push_back(new SF_ListItem(*other.items[i]));

    SetIndex(other.iIndex); // sets current and index

    for (unsigned i = 0; i < other.goodRandomItems.size(); i++)
        goodRandomItems.push_back(new SF_ListItem(*other.goodRandomItems[i]));

    iWidth = other.iWidth;
    iIndent = other.iIndent;

    miModifyImageLeft = new MI_Image(spr, ix + iIndent - 26, iy + 4, 32, 64, 26, 24, 4, 1, 8);
    miModifyImageLeft->Show(false);

    miModifyImageRight = new MI_Image(spr, ix + iWidth - 16, iy + 4, 32, 88, 26, 24, 4, 1, 8);
    miModifyImageRight->Show(false);

    mcItemChangedCode = other.mcItemChangedCode;
    mcControlSelectedCode = other.mcControlSelectedCode;

    fAutoAdvance = other.fAutoAdvance;
    fNoWrap = other.fNoWrap;
    iAdjustmentY = other.iAdjustmentY;
    fFastScroll = other.fFastScroll;
}

MI_SelectField::~MI_SelectField()
{
    delete [] szName;

    delete miModifyImageLeft;
    delete miModifyImageRight;

    goodRandomItems.clear();

    std::vector<SF_ListItem*>::iterator iterateAll = items.begin(), lim = items.end();

    while (iterateAll != lim) {
        delete *iterateAll;
        iterateAll++;
    }

    items.clear();
}

void MI_SelectField::SetTitle(char * name)
{
    delete [] szName;
    szName = new char[strlen(name) + 1];
    strcpy(szName, name);
}

//Sets current selected item based on the items int value
bool MI_SelectField::SetKey(short iID)
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator search = items.begin();

    iIndex  = 0;

    return false;
}

bool MI_SelectField::SetIndex(unsigned short index)
{
    if (index >= items.size())
        return false;

    current = items.begin();

    for (unsigned int iSearch = 0; iSearch < index; iSearch++) {
        ++current;
    }

    iIndex = index;

    SetValues();

    return true;
}

void MI_SelectField::Add(std::string name, short ivalue, std::string svalue, bool fvalue, bool fhidden, bool fGoodRandom, short iIconOverride)
{
    SF_ListItem * item = new SF_ListItem(name, ivalue, svalue, fvalue, fhidden, iIconOverride);
    items.push_back(item);

    if (fGoodRandom)
        goodRandomItems.push_back(item);

    if (items.size() >= 1) {
        current = items.begin();
        iIndex = 0;
    }
}

bool MI_SelectField::HideItem(short iID, bool fhide)
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator search = items.begin(), lim = items.end();

    while (search != lim) {
        if ((*search)->iValue == iID) {
            if (current == search && fhide) {
                if (!MoveNext())
                    MovePrev();
            }

            (*search)->fHidden = fhide;
            return true;
        }

        ++search;
    }

    return false;
}

void MI_SelectField::HideAllItems(bool fHide)
{
    if (items.empty())
        return;

    std::vector<SF_ListItem*>::iterator itr = items.begin();

    while (itr != items.end()) {
        (*itr)->fHidden = fHide;
        ++itr;
    }
}

MenuCodeEnum MI_SelectField::Modify(bool modify)
{
    if (fDisable)
        return MENU_CODE_UNSELECT_ITEM;

    if (fAutoAdvance && modify) {
        if (!items.empty()) {
            if (current == --items.end()) {
                current = items.begin();
                iIndex = 0;
            } else {
                ++current;
                ++iIndex;
            }

            //Saves the values to the pointed to values
            SetValues();
        }

        return mcItemChangedCode;
    }

    if (MENU_CODE_NONE != mcControlSelectedCode)
        return mcControlSelectedCode;

    miModifyImageLeft->Show(modify);
    miModifyImageRight->Show(modify);
    fModifying = modify;
    return MENU_CODE_MODIFY_ACCEPTED;
}

MenuCodeEnum MI_SelectField::SendInput(CPlayerInput * playerInput)
{
    for (int iPlayer = 0; iPlayer < 4; iPlayer++) {
        short iNumMoves = 1;
        if (fFastScroll && playerInput->outputControls[iPlayer].menu_scrollfast.fDown)
            iNumMoves = 10;

        if (playerInput->outputControls[iPlayer].menu_right.fPressed || playerInput->outputControls[iPlayer].menu_down.fPressed) {
            bool fMoved = false;

            for (short iMove = 0; iMove < iNumMoves; iMove++)
                fMoved |= MoveNext();

            if (fMoved)
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_left.fPressed || playerInput->outputControls[iPlayer].menu_up.fPressed) {
            bool fMoved = false;

            for (short iMove = 0; iMove < iNumMoves; iMove++)
                fMoved |= MovePrev();

            if (fMoved)
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_random.fPressed) {
            if (MoveRandom())
                return mcItemChangedCode;
        }

        if (playerInput->outputControls[iPlayer].menu_select.fPressed || playerInput->outputControls[iPlayer].menu_cancel.fPressed) {
            miModifyImageLeft->Show(false);
            miModifyImageRight->Show(false);

            fModifying = false;

            SetValues();

            return MENU_CODE_UNSELECT_ITEM;
        }
    }

    return MENU_CODE_NONE;
}

void MI_SelectField::Update()
{
    miModifyImageRight->Update();
    miModifyImageLeft->Update();
}

void MI_SelectField::Draw()
{
    if (!fShow)
        return;

    if (iIndent == 0) {
        short iHalfWidth = iWidth >> 1;
        spr->draw(ix, iy, 0, (fSelected ? 32 : 0) + iAdjustmentY, iHalfWidth, 32);
        spr->draw(ix + iHalfWidth, iy, 512 - iHalfWidth, (fSelected ? 32 : 0) + iAdjustmentY, iWidth - iHalfWidth, 32);
    } else {
        spr->draw(ix, iy, 0, (fSelected ? 32 : 0) + iAdjustmentY, iIndent - 16, 32);
        spr->draw(ix + iIndent - 16, iy, 0, (fSelected ? 96 : 64), 32, 32);
        spr->draw(ix + iIndent + 16, iy, 528 - iWidth + iIndent, (fSelected ? 32 : 0) + iAdjustmentY, iWidth - iIndent - 16, 32);
    }

    if (iIndent> 0)
        rm->menu_font_large.drawChopRight(ix + 16, iy + 5, iIndent - 8, szName);

	// RFC
    if (!items.empty()) {
        if (iIndent > 0)
            rm->menu_font_large.drawChopRight(ix + iIndent + 8, iy + 5, iWidth - iIndent - 24, (*current)->sName.c_str());
        else
            rm->menu_font_large.drawChopRight(ix + 16, iy + 5, iWidth - 32, (*current)->sName.c_str());
    }

    //TODO: invert order
	bool drawLeft = true;

	if ((items.begin() != items.end() && current == items.begin()))
		drawLeft = false;

	if (!fNoWrap || drawLeft)
        miModifyImageLeft->Draw();

    //TODO: invert order

	bool drawRight = true;

		if (items.end() != items.begin()) {

			if (items.begin() != --items.end()) {

				if (current == --items.end())
					drawRight = false;
			}
		}

    if (drawRight || !fNoWrap)
        miModifyImageRight->Draw();
}

void MI_SelectField::SetValues()
{
    if (iValue)
        *iValue = (*current)->iValue;

    if (sValue)
        *sValue = (*current)->sValue;

    if (fValue)
        *fValue = (*current)->fValue;
}

bool MI_SelectField::MoveNext()
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator findNext = current;
    short iFindIndex = iIndex;

    while (true) {
        if (findNext == --items.end()) {
            if (fNoWrap) {
                return false;
            } else {
                findNext = items.begin();
                iFindIndex = 0;
            }
        } else {
            findNext++;
            iFindIndex++;
        }

        if (findNext == current)
            return false;

        if (!(*findNext)->fHidden) {
            current = findNext;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    return false;
}

bool MI_SelectField::MovePrev()
{
    if (items.empty())
        return false;

    std::vector<SF_ListItem*>::iterator findPrev = current;
    short iFindIndex = iIndex;

    while (true) {
        if (findPrev == items.begin()) {
            if (fNoWrap) {
                return false;
            } else {
                findPrev = --items.end();
                iFindIndex = (signed short)items.size() - 1;
            }
        } else {
            findPrev--;
            iFindIndex--;
        }

        if (findPrev == current)
            return false;

        if (!(*findPrev)->fHidden) {
            current = findPrev;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    return false;
}

bool MI_SelectField::MoveRandom()
{
    //Can't pick a random item from a list of 1
    if (items.size() <= 1)
        return false;

    std::vector<SF_ListItem*>::iterator searchHidden = items.begin();

    short iHiddenCount = 0;
    while (searchHidden != items.end()) {
        if ((*searchHidden)->fHidden) {
            iHiddenCount++;
        }

        ++searchHidden;
    }

    //Can't pick a random item from a list containing only 1 non-hidden item
    if (items.size() - iHiddenCount <= 1)
        return false;

    std::vector<SF_ListItem*>::iterator findRandom = current;
    short iFindIndex = iIndex;

    while (true) {
        int index = RANDOM_INT(items.size());

        for (int k = 0; k < index; k++) {
            if (findRandom == --items.end()) {
                findRandom = items.begin();
                iFindIndex = 0;
            } else {
                ++findRandom;
                ++iFindIndex;
            }
        }

        if (findRandom != current && !(*findRandom)->fHidden) {
            current = findRandom;
            iIndex = iFindIndex;
            SetValues();
            return true;
        }
    }

    SetValues();
    return true;
}

MenuCodeEnum MI_SelectField::MouseClick(short iMouseX, short iMouseY)
{
    if (fDisable)
        return MENU_CODE_NONE;

    //If we are modifying this control, see if we clicked on a next/prev button
    if (fModifying) {
        short x, y, w, h;
        miModifyImageLeft->GetPositionAndSize(&x, &y, &w, &h);

        if (iMouseX >= x && iMouseX < x + w &&
                iMouseY >= y && iMouseY < y + h) {
            if (MovePrev()) {
                if (mcItemChangedCode == MENU_CODE_NONE)
                    return MENU_CODE_CLICKED;

                return mcItemChangedCode;
            }
        }

        miModifyImageRight->GetPositionAndSize(&x, &y, &w, &h);

        if (iMouseX >= x && iMouseX < x + w &&
                iMouseY >= y && iMouseY < y + h) {
            if (MoveNext()) {
                if (mcItemChangedCode == MENU_CODE_NONE)
                    return MENU_CODE_CLICKED;

                return mcItemChangedCode;
            }
        }
    }

    //Otherwise just check to see if we clicked on the whole control
    if (iMouseX >= ix && iMouseX < ix + iWidth && iMouseY >= iy && iMouseY < iy + 32)
        return MENU_CODE_CLICKED;

    //Otherwise this control wasn't clicked at all
    return MENU_CODE_NONE;
}

void MI_SelectField::Refresh()
{
    if (iValue)
        SetKey(*iValue);
    else if (fValue)
        SetKey(*fValue ? 1 : 0);
}


#include "cr_routine_info_options_tab.h"
#include "models/forms/routine_form.h"

namespace meow {
namespace ui {
namespace main_window {
namespace central_right {
namespace routine_info {

OptionsTab::OptionsTab(models::forms::RoutineForm *form,
                       QWidget *parent)
    : QWidget(parent)
    , _form(form)
{
    createWidgets();
    validateControls();
}

void OptionsTab::refreshData()
{
    fillDataFromForm();
    validateControls();
}

void OptionsTab::createWidgets()
{
    QGridLayout * mainGridLayout = new QGridLayout();
    int row = 0;

    // Name
    _nameLabel = new QLabel(tr("Name:"));
    mainGridLayout->addWidget(_nameLabel, row, 0);

    _nameEdit = new QLineEdit();
    _nameLabel->setBuddy(_nameEdit);
    connect(_nameEdit, &QLineEdit::textEdited,
            [=](const QString &name) {
                if (!_form->isEditingSupported()) return;
                _form->setName(name);
            });
    mainGridLayout->addWidget(_nameEdit, row, 1);

    // Definer
    _definerLabel = new QLabel(tr("Definer:"));
    mainGridLayout->addWidget(_definerLabel, row, 2);

    _definerCombobox = new QComboBox();
    _definerLabel->setBuddy(_definerCombobox);
    connect(_definerCombobox,
            &QComboBox::currentTextChanged,
            [=](const QString & definer) {
                if (!_form->isEditingSupported()) {
                    _definerCombobox->setCurrentIndex(
                        _definerCombobox->findText(_form->definer())
                    );
                    return;
                }
                _form->setDefiner(definer);
            });
    mainGridLayout->addWidget(_definerCombobox, row, 3);

    row++;

    // Comment
    _commentLabel = new QLabel(tr("Comment:"));
    mainGridLayout->addWidget(_commentLabel, row, 0);

    _commentEdit = new QLineEdit();
    _commentLabel->setBuddy(_commentEdit);
    connect(_commentEdit, &QLineEdit::textEdited,
            [=](const QString &comment) {
                if (!_form->isEditingSupported()) return;
                _form->setComment(comment);
            });
    mainGridLayout->addWidget(_commentEdit, row, 1, 1, 3);

    row++;

    // Routine type
    _routineTypeLabel = new QLabel(tr("Type:"));
    mainGridLayout->addWidget(_routineTypeLabel, row, 0);

    _routineTypeCombobox = new QComboBox();
    _routineTypeLabel->setBuddy(_routineTypeCombobox);
    connect(_routineTypeCombobox,
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
            QOverload<int>::of(&QComboBox::currentIndexChanged),
#else
            static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
#endif
            [=](int index) {
                if (!_form->isEditingSupported()) {
                    // QMap is ordered so use position of value as index in cb
                    const auto & types = _form->typeNames();
                    const auto it = types.constFind(_form->type());
                    if (it == types.constEnd()) return;
                    int formValueIndex = std::distance(types.constBegin(), it);
                    _routineTypeCombobox->setCurrentIndex(formValueIndex);
                    return;
                }
                const auto & types = _form->typeNames();
                auto it = types.constBegin();
                std::advance(it, index);
                if (it != types.constEnd()) {
                    _form->setType(it.key());
                }
                validateControls();
            });
    mainGridLayout->addWidget(_routineTypeCombobox, row, 1);

    // Data access
    _dataAccessLabel = new QLabel(tr("Data access:"));
    mainGridLayout->addWidget(_dataAccessLabel, row, 2);

    _dataAccessCombobox = new QComboBox();
    _dataAccessLabel->setBuddy(_dataAccessCombobox);
    connect(_dataAccessCombobox,
            &QComboBox::currentTextChanged,
            [=](const QString & access) {
                if (!_form->isEditingSupported()) {
                    _dataAccessCombobox->setCurrentIndex(
                        _dataAccessCombobox->findText(_form->dataAccess())
                    );
                    return;
                }
                _form->setDataAccess(access);
            });
    mainGridLayout->addWidget(_dataAccessCombobox, row, 3);

    row++;

    // Return type
    _returnTypeLabel = new QLabel(tr("Returns:"));
    mainGridLayout->addWidget(_returnTypeLabel, row, 0);

    _returnTypeCombobox = new QComboBox();
    _returnTypeLabel->setBuddy(_returnTypeCombobox);
    connect(_returnTypeCombobox,
            &QComboBox::currentTextChanged,
            [=](const QString & returnType) {
                if (!_form->isEditingSupported()) {
                    _returnTypeCombobox->setCurrentIndex(
                        _returnTypeCombobox->findText(_form->returnType())
                    );
                    return;
                }
                _form->setReturnType(returnType);
            });
    mainGridLayout->addWidget(_returnTypeCombobox, row, 1);

    // SQL security
    _sqlSecurityLabel = new QLabel(tr("SQL Security:"));
    mainGridLayout->addWidget(_sqlSecurityLabel, row, 2);

    _sqlSecurityCombobox = new QComboBox();
    _sqlSecurityLabel->setBuddy(_sqlSecurityCombobox);
    connect(_sqlSecurityCombobox,
            &QComboBox::currentTextChanged,
            [=](const QString & security) {
                if (!_form->isEditingSupported()) {
                    _sqlSecurityCombobox->setCurrentIndex(
                        _sqlSecurityCombobox->findText(_form->security())
                    );
                    return;
                }
                _form->setSecurity(security);
            });
    mainGridLayout->addWidget(_sqlSecurityCombobox, row, 3);

    row++;

    // Deterministic
    _deterministicChecbox = new QCheckBox(tr("Deterministic"));
    connect(_deterministicChecbox, &QCheckBox::stateChanged,
            [=](int state){
                Q_UNUSED(state);
                if (!_form->isEditingSupported()) {
                    _deterministicChecbox->setCheckState(
                        _form->isDeterministic() ?
                        Qt::CheckState::Checked : Qt::CheckState::Unchecked);
                    return;
                }
                _form->setDeterministic(
                static_cast<Qt::CheckState>(state) == Qt::CheckState::Checked);
            });
    mainGridLayout->addWidget(_deterministicChecbox, row, 1);

    mainGridLayout->setColumnStretch(0, 1);
    mainGridLayout->setColumnStretch(1, 4);
    mainGridLayout->setColumnStretch(2, 1);
    mainGridLayout->setColumnStretch(3, 6);

    mainGridLayout->setHorizontalSpacing(15);

    mainGridLayout->setAlignment(Qt::AlignTop);

    setLayout(mainGridLayout);
}

void OptionsTab::fillDataFromForm()
{
    _nameEdit->blockSignals(true);
    _nameEdit->setText(_form->name());
    _nameEdit->setReadOnly(!_form->isEditingSupported());
    _nameEdit->blockSignals(false);

    _definerCombobox->blockSignals(true);
    _definerCombobox->clear();
    _definerCombobox->addItems(
        _form->allDefinerOptions(_form->isEditingSupported())
     ); // TODO: lazy load
    _definerCombobox->setCurrentIndex(
        _definerCombobox->findText(_form->definer()));
    _definerCombobox->setEditable(_form->isEditingSupported());
    _definerCombobox->blockSignals(false);

    _commentEdit->blockSignals(true);
    _commentEdit->setText(_form->comment());
    _commentEdit->setReadOnly(!_form->isEditingSupported());
    _commentEdit->blockSignals(false);

    _routineTypeCombobox->blockSignals(true);
    _routineTypeCombobox->clear();
    {
        QStringList routineTypeNames = _form->typeNames().values();
        _routineTypeCombobox->addItems(routineTypeNames);
        const auto & routineTypes = _form->typeNames();
        const auto it = routineTypes.constFind(_form->type());
        if (it != routineTypes.constEnd()) {
            int formValueIndex = std::distance(routineTypes.constBegin(), it);
            _routineTypeCombobox->setCurrentIndex(formValueIndex);
        }
    }
    _routineTypeCombobox->blockSignals(false);

    _dataAccessCombobox->blockSignals(true);
    _dataAccessCombobox->clear();
    _dataAccessCombobox->addItems(_form->dataAccessList());
    _dataAccessCombobox->setCurrentIndex(
        _dataAccessCombobox->findText(_form->dataAccess()));
    _dataAccessCombobox->blockSignals(false);

    _returnTypeCombobox->blockSignals(true);
    _returnTypeCombobox->clear();
    _returnTypeCombobox->addItems(_form->returnTypes());
    _returnTypeCombobox->setCurrentIndex(
        _returnTypeCombobox->findText(_form->returnType()));
    _returnTypeCombobox->setEditable(_form->isEditingSupported());
    _returnTypeCombobox->blockSignals(false);

    _sqlSecurityCombobox->blockSignals(true);
    _sqlSecurityCombobox->clear();
    _sqlSecurityCombobox->addItems(_form->securityOptions());
    _sqlSecurityCombobox->setCurrentIndex(
        _sqlSecurityCombobox->findText(_form->security()));
    _sqlSecurityCombobox->setEnabled(_form->supportsSecurity());
    _sqlSecurityCombobox->blockSignals(false);

    _deterministicChecbox->blockSignals(true);
    _deterministicChecbox->setCheckState(
        _form->isDeterministic() ?
        Qt::CheckState::Checked : Qt::CheckState::Unchecked);
    _deterministicChecbox->blockSignals(false);
}

void OptionsTab::validateControls()
{
    _returnTypeLabel->setEnabled(_form->supportsReturnType());
    _returnTypeCombobox->setEnabled(_form->supportsReturnType());
}

void OptionsTab::onBeforeEntityEditing()

} // namespace routine_info
} // namespace central_right
} // namespace main_window
} // namespace ui
} // namespace meow


{
    return m_hashImpl->Calculate(str);
}

/**
 */

#include <aws/core/utils/crypto/Sha256.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/utils/crypto/Factories.h>

using namespace Aws::Utils::Crypto;

Sha256::Sha256() :
    m_hashImpl(CreateSha256Implementation())
{
}

Sha256::~Sha256()
{
}

HashResult Sha256::Calculate(const Aws::String& str)

HashResult Sha256::Calculate(Aws::IStream& stream)
{
    return m_hashImpl->Calculate(stream);
}


{
  drawFunction(applyCurrentCurve, offset);

  uint8_t i = 0;
  do {
    point_t point = getPoint(i);
    i++;
    if (point.x == 0) break;
    lcdDrawFilledRect(point.x-offset, point.y-1, 3, 3, SOLID, FORCE); // do markup square
  } while (1);
}

/*
 *
 * Based on code named
 *   th9x - http://example.com
 *   er9x - http://example.com
 *   gruvin9x - http://example.com
 *
 *
 */

#include "opentx.h"

#if defined(GVARS_IN_CURVES_SCREEN)
  #warning "define still not added to CMakeLists.txt"
  #define CURVE_SELECTED() (sub >= 0 && sub < MAX_CURVES)
  #define GVAR_SELECTED()  (sub >= MAX_CURVES)
#else
  #define CURVE_SELECTED() (sub >= 0)
#endif

void drawCurve(coord_t offset)

void menuModelCurvesAll(event_t event)
{
#if defined(GVARS_IN_CURVES_SCREEN)
  SIMPLE_MENU(STR_MENUCURVES, menuTabModel, MENU_MODEL_CURVES, HEADER_LINE+MAX_CURVES+MAX_GVARS);
#else
  SIMPLE_MENU(STR_MENUCURVES, menuTabModel, MENU_MODEL_CURVES, HEADER_LINE+MAX_CURVES);
#endif

  int8_t sub = menuVerticalPosition - HEADER_LINE;

  switch (event) {
#if defined(ROTARY_ENCODER_NAVIGATION)
    case EVT_ROTARY_BREAK:
#endif
#if !defined(PCBTARANIS)
    case EVT_KEY_FIRST(KEY_RIGHT):
#endif
    case EVT_KEY_FIRST(KEY_ENTER):
      if (CURVE_SELECTED() && !READ_ONLY()) {
        s_currIdxSubMenu = sub;
        s_currSrcRaw = MIXSRC_NONE;
        pushMenu(menuModelCurveOne);
      }
      break;
  }

  for (uint8_t i=0; i<LCD_LINES-1; i++) {
    coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
    uint8_t k = i + menuVerticalOffset;
    LcdFlags attr = (sub == k ? INVERS : 0);
#if defined(GVARS_IN_CURVES_SCREEN)
    if (k >= MAX_CURVES) {
      drawStringWithIndex(0, y, STR_GV, k-MAX_CURVES+1);
      if (GVAR_SELECTED()) {
        if (attr && s_editMode>0) attr |= BLINK;
        lcdDrawNumber(10*FW, y, GVAR_VALUE(k-MAX_CURVES, -1), attr);
        if (attr) g_model.gvars[k-MAX_CURVES] = checkIncDec(event, g_model.gvars[k-MAX_CURVES], -1000, 1000, EE_MODEL);
      }
    }
    else
#endif
    {
      drawStringWithIndex(0, y, STR_CV, k+1, attr);
      CurveData & crv = g_model.curves[k];
      editName(4*FW, y, crv.name, sizeof(crv.name), 0, 0);
#if LCD_W >= 212
      lcdDrawNumber(11*FW, y, 5+crv.points, LEFT);
      lcdDrawText(lcdLastRightPos, y, STR_PTS, 0);
#endif
    }
  }

  if (CURVE_SELECTED()) {
    s_currIdxSubMenu = sub;
#if LCD_W >= 212
    drawCurve(23);
#else
    drawCurve(10);
#endif
  }
}

void editCurveRef(coord_t x, coord_t y, CurveRef & curve, event_t event, LcdFlags flags)
{
  coord_t x1 = x;
  LcdFlags flags1 = flags;
  if (flags & RIGHT) {
    x1 -= 9*FW;
    flags1 -= RIGHT;
  }
  else {
    x += 5*FW;
  }

  uint8_t active = (flags & INVERS);

  if (menuHorizontalPosition == 0) {
    flags = flags & RIGHT;
  }
  else {
    flags1 = 0;
  }

  lcdDrawTextAtIndex(x1, y, "\004DiffExpoFuncCstm", curve.type, flags1);

  if (active && menuHorizontalPosition==0) {
    CHECK_INCDEC_MODELVAR_ZERO(event, curve.type, CURVE_REF_CUSTOM);
    if (checkIncDec_Ret) curve.value = 0;
  }
  switch (curve.type) {
    case CURVE_REF_DIFF:
    case CURVE_REF_EXPO:
      curve.value = GVAR_MENU_ITEM(x, y, curve.value, -100, 100, LEFT | flags, 0, event);
      break;
    case CURVE_REF_FUNC:
      lcdDrawTextAtIndex(x, y, STR_VCURVEFUNC, curve.value, flags);
      if (active && menuHorizontalPosition==1) CHECK_INCDEC_MODELVAR_ZERO(event, curve.value, CURVE_BASE-1);
      break;
    case CURVE_REF_CUSTOM:
      drawCurveName(x, y, curve.value, flags);
      if (active && menuHorizontalPosition == 1) {
        if (event == EVT_KEY_LONG(KEY_ENTER) && curve.value != 0) {
          s_currIdxSubMenu = abs(curve.value) - 1;
          pushMenu(menuModelCurveOne);
        }
        else {
          CHECK_INCDEC_MODELVAR(event, curve.value, -MAX_CURVES, MAX_CURVES);
        }
      }
      break;
  }
}

void drawFunction(FnFuncP fn, uint8_t offset)
{
  lcdDrawVerticalLine(CURVE_CENTER_X - offset, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, CURVE_SIDE_WIDTH * 2, 0xee);
  lcdDrawHorizontalLine(CURVE_CENTER_X - CURVE_SIDE_WIDTH - offset, CURVE_CENTER_Y, CURVE_SIDE_WIDTH * 2, 0xee);

  coord_t prev_yv = (coord_t) - 1;

  for (int xv = -CURVE_SIDE_WIDTH; xv <= CURVE_SIDE_WIDTH; xv++) {
    coord_t yv = (CURVE_SIDE_HEIGHT - 1) - (((uint16_t)RESX + fn(xv * (RESX/CURVE_SIDE_WIDTH))) / 2 * (CURVE_SIDE_HEIGHT - 1) / RESX) + (LCD_H - CURVE_SIDE_HEIGHT) / 2;
    if (prev_yv != (coord_t) - 1) {
      if (abs((int8_t)yv-prev_yv) <= 1) {
        lcdDrawPoint(CURVE_CENTER_X + xv - offset - 1, prev_yv, FORCE);
      }
      else {
        uint8_t tmp = (prev_yv < yv ? 0 : 1);
        lcdDrawSolidVerticalLine(CURVE_CENTER_X + xv - offset - 1, yv + tmp, prev_yv - yv);
      }
    }
    prev_yv = yv;
  }
}

void drawCursor(FnFuncP fn, uint8_t offset)
{
  int x512 = getValue(s_currSrcRaw);
  if (s_currSrcRaw >= MIXSRC_FIRST_TELEM) {
    if (s_currScale > 0)
      x512 = (x512 * 1024) / convertTelemValue(s_currSrcRaw - MIXSRC_FIRST_TELEM + 1, s_currScale);
    drawSensorCustomValue(LCD_W - FW - offset, 6 * FH, (s_currSrcRaw - MIXSRC_FIRST_TELEM) / 3, x512, 0);
  }
  else {
    lcdDrawNumber(LCD_W - FW - offset, 6*FH, calcRESXto1000(x512), RIGHT | PREC1);
  }
  x512 = limit(-1024, x512, 1024);
  int y512 = fn(x512);
  y512 = limit(-1024, y512, 1024);
  lcdDrawNumber(CURVE_CENTER_X - FWNUM - offset, 1*FH, calcRESXto1000(y512), RIGHT | PREC1);

  x512 = CURVE_CENTER_X + x512/(RESX / CURVE_SIDE_WIDTH);
  y512 = (CURVE_SIDE_HEIGHT - 1) - ((y512 + RESX) / 2) * (CURVE_SIDE_HEIGHT - 1) / RESX  + (LCD_H - CURVE_SIDE_HEIGHT) / 2;

  lcdDrawSolidVerticalLine(x512 - offset, y512-3, 3 * 2 + 1);
  lcdDrawSolidHorizontalLine(x512 - 3 - offset, y512, 3 * 2 + 1);
}


switch (event) {
#if defined(ROTARY_ENCODER_NAVIGATION)
    case EVT_ROTARY_BREAK:
#endif
#if !defined(PCBTARANIS)
    case EVT_KEY_FIRST(KEY_RIGHT):
#endif
    case EVT_KEY_FIRST(KEY_ENTER):
      if (CURVE_SELECTED() && !READ_ONLY()) {
        s_currIdxSubMenu = sub;
        s_currSrcRaw = MIXSRC_NONE;
        pushMenu(menuModelCurveOne);
      }
      break;
  }

/*
 *
 * Based on code named
 *   th9x - http://example.com
 *   er9x - http://example.com
 *   gruvin9x - http://example.com
 *
 *
 */

#include "opentx.h"

#if defined(GVARS_IN_CURVES_SCREEN)
  #warning "define still not added to CMakeLists.txt"
  #define CURVE_SELECTED() (sub >= 0 && sub < MAX_CURVES)
  #define GVAR_SELECTED()  (sub >= MAX_CURVES)
#else
  #define CURVE_SELECTED() (sub >= 0)
#endif

void drawCurve(coord_t offset)
{
  drawFunction(applyCurrentCurve, offset);

  uint8_t i = 0;
  do {
    point_t point = getPoint(i);
    i++;
    if (point.x == 0) break;
    lcdDrawFilledRect(point.x-offset, point.y-1, 3, 3, SOLID, FORCE); // do markup square
  } while (1);
}

void menuModelCurvesAll(event_t event)
{
#if defined(GVARS_IN_CURVES_SCREEN)
  SIMPLE_MENU(STR_MENUCURVES, menuTabModel, MENU_MODEL_CURVES, HEADER_LINE+MAX_CURVES+MAX_GVARS);
#else
  SIMPLE_MENU(STR_MENUCURVES, menuTabModel, MENU_MODEL_CURVES, HEADER_LINE+MAX_CURVES);
#endif

  int8_t sub = menuVerticalPosition - HEADER_LINE;

  for (uint8_t i=0; i<LCD_LINES-1; i++) {
    coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
    uint8_t k = i + menuVerticalOffset;
    LcdFlags attr = (sub == k ? INVERS : 0);
#if defined(GVARS_IN_CURVES_SCREEN)
    if (k >= MAX_CURVES) {
      drawStringWithIndex(0, y, STR_GV, k-MAX_CURVES+1);
      if (GVAR_SELECTED()) {
        if (attr && s_editMode>0) attr |= BLINK;
        lcdDrawNumber(10*FW, y, GVAR_VALUE(k-MAX_CURVES, -1), attr);
        if (attr) g_model.gvars[k-MAX_CURVES] = checkIncDec(event, g_model.gvars[k-MAX_CURVES], -1000, 1000, EE_MODEL);
      }
    }
    else
#endif
    {
      drawStringWithIndex(0, y, STR_CV, k+1, attr);
      CurveData & crv = g_model.curves[k];
      editName(4*FW, y, crv.name, sizeof(crv.name), 0, 0);
#if LCD_W >= 212
      lcdDrawNumber(11*FW, y, 5+crv.points, LEFT);
      lcdDrawText(lcdLastRightPos, y, STR_PTS, 0);
#endif
    }
  }

  if (CURVE_SELECTED()) {
    s_currIdxSubMenu = sub;
#if LCD_W >= 212
    drawCurve(23);
#else
    drawCurve(10);
#endif
  }
}

void editCurveRef(coord_t x, coord_t y, CurveRef & curve, event_t event, LcdFlags flags)
{
  coord_t x1 = x;
  LcdFlags flags1 = flags;
  if (flags & RIGHT) {
    x1 -= 9*FW;
    flags1 -= RIGHT;
  }
  else {
    x += 5*FW;
  }

  uint8_t active = (flags & INVERS);

  if (menuHorizontalPosition == 0) {
    flags = flags & RIGHT;
  }
  else {
    flags1 = 0;
  }

  lcdDrawTextAtIndex(x1, y, "\004DiffExpoFuncCstm", curve.type, flags1);

  if (active && menuHorizontalPosition==0) {
    CHECK_INCDEC_MODELVAR_ZERO(event, curve.type, CURVE_REF_CUSTOM);
    if (checkIncDec_Ret) curve.value = 0;
  }
  switch (curve.type) {
    case CURVE_REF_DIFF:
    case CURVE_REF_EXPO:
      curve.value = GVAR_MENU_ITEM(x, y, curve.value, -100, 100, LEFT | flags, 0, event);
      break;
    case CURVE_REF_FUNC:
      lcdDrawTextAtIndex(x, y, STR_VCURVEFUNC, curve.value, flags);
      if (active && menuHorizontalPosition==1) CHECK_INCDEC_MODELVAR_ZERO(event, curve.value, CURVE_BASE-1);
      break;
    case CURVE_REF_CUSTOM:
      drawCurveName(x, y, curve.value, flags);
      if (active && menuHorizontalPosition == 1) {
        if (event == EVT_KEY_LONG(KEY_ENTER) && curve.value != 0) {
          s_currIdxSubMenu = abs(curve.value) - 1;
          pushMenu(menuModelCurveOne);
        }
        else {
          CHECK_INCDEC_MODELVAR(event, curve.value, -MAX_CURVES, MAX_CURVES);
        }
      }
      break;
  }
}

void drawFunction(FnFuncP fn, uint8_t offset)
{
  lcdDrawVerticalLine(CURVE_CENTER_X - offset, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, CURVE_SIDE_WIDTH * 2, 0xee);
  lcdDrawHorizontalLine(CURVE_CENTER_X - CURVE_SIDE_WIDTH - offset, CURVE_CENTER_Y, CURVE_SIDE_WIDTH * 2, 0xee);

  coord_t prev_yv = (coord_t) - 1;

  for (int xv = -CURVE_SIDE_WIDTH; xv <= CURVE_SIDE_WIDTH; xv++) {
    coord_t yv = (CURVE_SIDE_HEIGHT - 1) - (((uint16_t)RESX + fn(xv * (RESX/CURVE_SIDE_WIDTH))) / 2 * (CURVE_SIDE_HEIGHT - 1) / RESX) + (LCD_H - CURVE_SIDE_HEIGHT) / 2;
    if (prev_yv != (coord_t) - 1) {
      if (abs((int8_t)yv-prev_yv) <= 1) {
        lcdDrawPoint(CURVE_CENTER_X + xv - offset - 1, prev_yv, FORCE);
      }
      else {
        uint8_t tmp = (prev_yv < yv ? 0 : 1);
        lcdDrawSolidVerticalLine(CURVE_CENTER_X + xv - offset - 1, yv + tmp, prev_yv - yv);
      }
    }
    prev_yv = yv;
  }
}

void drawCursor(FnFuncP fn, uint8_t offset)
{
  int x512 = getValue(s_currSrcRaw);
  if (s_currSrcRaw >= MIXSRC_FIRST_TELEM) {
    if (s_currScale > 0)
      x512 = (x512 * 1024) / convertTelemValue(s_currSrcRaw - MIXSRC_FIRST_TELEM + 1, s_currScale);
    drawSensorCustomValue(LCD_W - FW - offset, 6 * FH, (s_currSrcRaw - MIXSRC_FIRST_TELEM) / 3, x512, 0);
  }
  else {
    lcdDrawNumber(LCD_W - FW - offset, 6*FH, calcRESXto1000(x512), RIGHT | PREC1);
  }
  x512 = limit(-1024, x512, 1024);
  int y512 = fn(x512);
  y512 = limit(-1024, y512, 1024);
  lcdDrawNumber(CURVE_CENTER_X - FWNUM - offset, 1*FH, calcRESXto1000(y512), RIGHT | PREC1);

  x512 = CURVE_CENTER_X + x512/(RESX / CURVE_SIDE_WIDTH);
  y512 = (CURVE_SIDE_HEIGHT - 1) - ((y512 + RESX) / 2) * (CURVE_SIDE_HEIGHT - 1) / RESX  + (LCD_H - CURVE_SIDE_HEIGHT) / 2;

  lcdDrawSolidVerticalLine(x512 - offset, y512-3, 3 * 2 + 1);
  lcdDrawSolidHorizontalLine(x512 - 3 - offset, y512, 3 * 2 + 1);
}


{
    form.fields() << d->unknownFields;
}

/*
 *
 * Author:
 *  Linus Jahn
 *
 * Source:
 *  http://example.com
 *
 * This file is a part of QXmpp library.
 *
 *
 */

#include "QXmppDataFormBase.h"

#include "QXmppDataForm.h"

#include <QDateTime>

/// QXmppDataFormBase is an abstract class types that can be serialized to data
/// QXmppDataFormBase based types can easily be converted to QXmppDataForms, it
/// is as simple as this:
/// \code
/// \endcode
/// To make this work, you will need to at least implement the toDataForm()
/// method. For parsing your type you should also create a static creator
/// \code
/// \endcode
/// \since QXmpp 1.5

/// Serializes all fields to a QXmppDataForm.
QXmppDataForm QXmppDataFormBase::toDataForm() const
{
    QXmppDataForm form(QXmppDataForm::Form);

    // add FORM_TYPE
    if (const auto type = formType(); !type.isEmpty()) {
        form.fields() << QXmppDataForm::Field(QXmppDataForm::Field::HiddenField,
                                              QStringLiteral("FORM_TYPE"),
                                              type);
    }

    // manual serialization parts
    serializeForm(form);

    return form;
}

/// Parses the QXmppDataForm.
bool QXmppDataFormBase::fromDataForm(const QXmppDataForm &form, QXmppDataFormBase &output)
{
    output.parseForm(form);
    return true;
}

/// \fn QXmppDataFormBase::formType
/// Returns the 'FORM_TYPE' value of the parsed form.

/// \fn QXmppDataFormBase::parseForm
/// This is called when a QXmppDataForm is parsed. You can parse all values from
/// the given form and its fields.

/// \fn QXmppDataFormBase::serializeForm
/// This is called the object is serialized to a QXmppDataForm. You need to
/// create a new QXmppDataForm and serialize all fields and values.

/// \fn QXmppDataFormBase::parseUInt
/// Parses an unsigned int from a QVariant (QString). Returns std::nullopt if
/// the no number could be parsed.

/// \fn QXmppDataFormBase::parseULongLong
/// Parses an unsigned long long from a QVariant (QString). Returns std::nullopt
/// if the no number could be parsed.

/// \fn QXmppDataFormBase::parseBool
/// Returns the contained boolean value if the QVariant contains a bool.

/// \fn QXmppDataFormBase::serializeValue
/// Adds a new field to the form with the given field type, field name and value.

/// \fn QXmppDataFormBase::serializeNullable
/// Adds a new field to the form if \code !value.isNull() \endcode.

/// \fn QXmppDataFormBase::serializeEmptyable
/// Adds a new field to the form if \code !value.isEmpty() \endcode.

/// \fn QXmppDataFormBase::serializeOptional
/// Adds a new field to the form if \code optional.has_value() \endcode.

/// \fn QXmppDataFormBase::serializeOptionalNumber
/// Adds a new field to the form if \code optional.has_value() \endcode.
/// Converts the optional's value to QString using QString::number().

/// Adds a new field to the form if the passed QDateTime is valid and formats it
/// as ISO timestamp and always uses UTC.
void QXmppDataFormBase::serializeDatetime(QXmppDataForm &form, const QString &name, const QDateTime &datetime, QXmppDataForm::Field::Type type)
{
    if (datetime.isValid()) {
        serializeValue(form, type, name, datetime.toUTC().toString(Qt::ISODate));
    }
}

/// This class is used for parsing a QXmppDataForm in an extensible way with
/// inheritance and keeping additional unknown fields.
/// When inheriting you need to reimplement parseField(), serializeForm() and
/// formType(). Also you should add a static parsing function (e.g.
/// \since QXmpp 1.5

class QXmppExtensibleDataFormBasePrivate : public QSharedData
{
public:
    QList<QXmppDataForm::Field> unknownFields;
};

QXmppExtensibleDataFormBase::QXmppExtensibleDataFormBase()
    : d(new QXmppExtensibleDataFormBasePrivate)
{
}

/// \cond
QXmppExtensibleDataFormBase::QXmppExtensibleDataFormBase(const QXmppExtensibleDataFormBase &) = default;
QXmppExtensibleDataFormBase::~QXmppExtensibleDataFormBase() = default;
QXmppExtensibleDataFormBase &QXmppExtensibleDataFormBase::operator=(const QXmppExtensibleDataFormBase &) = default;
/// \endcond

/// Returns all fields that couldn't be parsed.
QList<QXmppDataForm::Field> QXmppExtensibleDataFormBase::unknownFields() const
{
    return d->unknownFields;
}

/// Sets all additional fields to be serialized.
void QXmppExtensibleDataFormBase::setUnknownFields(const QList<QXmppDataForm::Field> &unknownFields)
{
    d->unknownFields = unknownFields;
}

void QXmppExtensibleDataFormBase::parseForm(const QXmppDataForm &form)
{
    const auto fields = form.fields();
    for (const auto &field : fields) {
        // FORM_TYPE fields are not saved (override this function to save them)
        if (!parseField(field) &&
                !(field.type() == QXmppDataForm::Field::HiddenField &&
                  field.key() == QStringLiteral("FORM_TYPE"))) {
            d->unknownFields << field;
        }
    }
}

void QXmppExtensibleDataFormBase::serializeForm(QXmppDataForm &form) const

/// Returns true if a field has been parsed.
/// Should be reimplemented to do actual parsing. All fields that can't be
/// parsed end up as unknownFields().
bool QXmppExtensibleDataFormBase::parseField(const QXmppDataForm::Field &)
{
    return false;
}


QXmppExtensibleDataFormBase::QXmppExtensibleDataFormBase(const QXmppExtensibleDataFormBase &) = default;

/*
 *
 * Author:
 *  Linus Jahn
 *
 * Source:
 *  http://example.com
 *
 * This file is a part of QXmpp library.
 *
 *
 */

#include "QXmppDataFormBase.h"

#include "QXmppDataForm.h"

#include <QDateTime>

/// QXmppDataFormBase is an abstract class types that can be serialized to data
/// QXmppDataFormBase based types can easily be converted to QXmppDataForms, it
/// is as simple as this:
/// \code
/// \endcode
/// To make this work, you will need to at least implement the toDataForm()
/// method. For parsing your type you should also create a static creator
/// \code
/// \endcode
/// \since QXmpp 1.5

/// Serializes all fields to a QXmppDataForm.
QXmppDataForm QXmppDataFormBase::toDataForm() const
{
    QXmppDataForm form(QXmppDataForm::Form);

    // add FORM_TYPE
    if (const auto type = formType(); !type.isEmpty()) {
        form.fields() << QXmppDataForm::Field(QXmppDataForm::Field::HiddenField,
                                              QStringLiteral("FORM_TYPE"),
                                              type);
    }

    // manual serialization parts
    serializeForm(form);

    return form;
}

/// Parses the QXmppDataForm.
bool QXmppDataFormBase::fromDataForm(const QXmppDataForm &form, QXmppDataFormBase &output)
{
    output.parseForm(form);
    return true;
}

/// \fn QXmppDataFormBase::formType
/// Returns the 'FORM_TYPE' value of the parsed form.

/// \fn QXmppDataFormBase::parseForm
/// This is called when a QXmppDataForm is parsed. You can parse all values from
/// the given form and its fields.

/// \fn QXmppDataFormBase::serializeForm
/// This is called the object is serialized to a QXmppDataForm. You need to
/// create a new QXmppDataForm and serialize all fields and values.

/// \fn QXmppDataFormBase::parseUInt
/// Parses an unsigned int from a QVariant (QString). Returns std::nullopt if
/// the no number could be parsed.

/// \fn QXmppDataFormBase::parseULongLong
/// Parses an unsigned long long from a QVariant (QString). Returns std::nullopt
/// if the no number could be parsed.

/// \fn QXmppDataFormBase::parseBool
/// Returns the contained boolean value if the QVariant contains a bool.

/// \fn QXmppDataFormBase::serializeValue
/// Adds a new field to the form with the given field type, field name and value.

/// \fn QXmppDataFormBase::serializeNullable
/// Adds a new field to the form if \code !value.isNull() \endcode.

/// \fn QXmppDataFormBase::serializeEmptyable
/// Adds a new field to the form if \code !value.isEmpty() \endcode.

/// \fn QXmppDataFormBase::serializeOptional
/// Adds a new field to the form if \code optional.has_value() \endcode.

/// \fn QXmppDataFormBase::serializeOptionalNumber
/// Adds a new field to the form if \code optional.has_value() \endcode.
/// Converts the optional's value to QString using QString::number().

/// Adds a new field to the form if the passed QDateTime is valid and formats it
/// as ISO timestamp and always uses UTC.
void QXmppDataFormBase::serializeDatetime(QXmppDataForm &form, const QString &name, const QDateTime &datetime, QXmppDataForm::Field::Type type)
{
    if (datetime.isValid()) {
        serializeValue(form, type, name, datetime.toUTC().toString(Qt::ISODate));
    }
}

/// This class is used for parsing a QXmppDataForm in an extensible way with
/// inheritance and keeping additional unknown fields.
/// When inheriting you need to reimplement parseField(), serializeForm() and
/// formType(). Also you should add a static parsing function (e.g.
/// \since QXmpp 1.5

class QXmppExtensibleDataFormBasePrivate : public QSharedData
{
public:
    QList<QXmppDataForm::Field> unknownFields;
};

QXmppExtensibleDataFormBase::QXmppExtensibleDataFormBase()
    : d(new QXmppExtensibleDataFormBasePrivate)
{
}

/// \cond

QXmppExtensibleDataFormBase::~QXmppExtensibleDataFormBase() = default;
QXmppExtensibleDataFormBase &QXmppExtensibleDataFormBase::operator=(const QXmppExtensibleDataFormBase &) = default;
/// \endcond

/// Returns all fields that couldn't be parsed.
QList<QXmppDataForm::Field> QXmppExtensibleDataFormBase::unknownFields() const
{
    return d->unknownFields;
}

/// Sets all additional fields to be serialized.
void QXmppExtensibleDataFormBase::setUnknownFields(const QList<QXmppDataForm::Field> &unknownFields)
{
    d->unknownFields = unknownFields;
}

void QXmppExtensibleDataFormBase::parseForm(const QXmppDataForm &form)
{
    const auto fields = form.fields();
    for (const auto &field : fields) {
        // FORM_TYPE fields are not saved (override this function to save them)
        if (!parseField(field) &&
                !(field.type() == QXmppDataForm::Field::HiddenField &&
                  field.key() == QStringLiteral("FORM_TYPE"))) {
            d->unknownFields << field;
        }
    }
}

void QXmppExtensibleDataFormBase::serializeForm(QXmppDataForm &form) const
{
    form.fields() << d->unknownFields;
}

/// Returns true if a field has been parsed.
/// Should be reimplemented to do actual parsing. All fields that can't be
/// parsed end up as unknownFields().
bool QXmppExtensibleDataFormBase::parseField(const QXmppDataForm::Field &)
{
    return false;
}