#include "CtrlCore.h"

namespace Upp {

#define LLOG(x)   // DLOG(x)

#define IMAGECLASS CtrlCoreImg
#define IMAGEFILE <CtrlCore/CtrlCore.iml>
#include <Draw/iml_source.h>

static bool StdDisplayErrorFn(const Value& e)
{
	GuiLock __;
	if(!e.IsError())
		return false;
	String s = GetErrorText(e);
#ifdef PLATFORM_WIN32
	MessageBox(NULL, s, GetExeTitle(), MB_OK | MB_ICONQUESTION);
#else
	fputs(String().Cat() << GetExeTitle() << ": " << s << '\n', stderr);
#endif
	return true;
}

bool (*&DisplayErrorFn())(const Value& v)
{
	static bool (*errfn)(const Value& v) = &StdDisplayErrorFn;
	return errfn;
}

int64 Ctrl::eventid;
int   Ctrl::EventLevel;

Ctrl *Ctrl::LoopCtrl;
int   Ctrl::LoopLevel;
int64 Ctrl::EventLoopNo;
int64 Ctrl::EndSessionLoopNo;

bool Ctrl::MemoryCheck;

bool Ctrl::painting = false;

void   Ctrl::SetData(const Value&) {}
Value  Ctrl::GetData() const       { return Value(); }

void Ctrl::Paint(Draw& draw)                        {}
int  Ctrl::OverPaint() const                        { return 0; }

void Ctrl::Activate()                               {}
void Ctrl::Deactivate()                             {}
void Ctrl::DeactivateBy(Ctrl *)                     {}

void Ctrl::CancelMode()                             {}
void Ctrl::MouseEnter(Point p, dword keyflags)      {}
void Ctrl::LeftDown(Point p, dword keyflags)        {}
void Ctrl::RightDown(Point p, dword keyflags)       {}
void Ctrl::LeftRepeat(Point p, dword keyflags)      {}
void Ctrl::RightRepeat(Point p, dword keyflags)     {}
void Ctrl::MouseMove(Point p, dword keyflags)       {}
void Ctrl::LeftUp(Point, dword keyflags)            {}
void Ctrl::RightUp(Point p, dword keyflags)         {}
void Ctrl::MouseLeave()                             {}
void Ctrl::LeftDrag(Point p, dword keyflags)        {}
void Ctrl::LeftHold(Point p, dword keyflags)        {}
void Ctrl::RightDrag(Point p, dword keyflags)       {}
void Ctrl::RightHold(Point p, dword keyflags)       {}
void Ctrl::MiddleDown(Point p, dword keyflags)      {}
void Ctrl::MiddleDouble(Point p, dword keyflags)    {}
void Ctrl::MiddleTriple(Point p, dword keyflags)    {}
void Ctrl::MiddleRepeat(Point p, dword keyflags)    {}
void Ctrl::MiddleDrag(Point p, dword keyflags)      {}
void Ctrl::MiddleHold(Point p, dword keyflags)      {}
void Ctrl::MiddleUp(Point p, dword keyflags)        {}

void Ctrl::Pen(Point p, const PenInfo& pen, dword keyflags) {}

void Ctrl::Layout()                                 {}

void Ctrl::PostInput()
{
	GuiLock __;
	Ctrl *parent = GetParent();
	if(parent)
		parent->PostInput();
}

void Ctrl::LeftDouble(Point p, dword keyflags)
{
	LeftDown(p, keyflags);
}

void Ctrl::LeftTriple(Point p, dword keyflags)
{
	LeftDown(p, keyflags);
}

void Ctrl::RightDouble(Point p, dword keyflags)
{
	RightDown(p, keyflags);
}

void Ctrl::RightTriple(Point p, dword keyflags)
{
	RightDown(p, keyflags);
}

void Ctrl::ChildGotFocus()
{
	GuiLock __;
	Ctrl *parent = GetParent();
	if(parent)
		parent->ChildGotFocus();
}

void Ctrl::ChildLostFocus()
{
	GuiLock __;
	Ctrl *parent = GetParent();
	if(parent)
		parent->ChildLostFocus();
}

void Ctrl::ChildAdded(Ctrl *q)
{
	GuiLock __;
	Ctrl *parent = GetParent();
	if(parent)
		parent->ChildAdded(q);
}

void Ctrl::ChildRemoved(Ctrl *q)
{
	GuiLock __;
	Ctrl *parent = GetParent();
	if(parent)
		parent->ChildRemoved(q);
}

void Ctrl::ParentChange() {}

bool Ctrl::Key(dword key, int count)
{
	return false;
}

void Ctrl::GotFocus()                               {}
void Ctrl::LostFocus()                              {}

dword  Ctrl::AccessKeyBit(int accesskey)
{
	accesskey &= 255;
	if(accesskey >= 'A' && accesskey <= 'Z')
		return (uint64)2 << (accesskey - 'A');
	return !!accesskey;
}

dword Ctrl::GetAccessKeysDeep() const
{
	GuiLock __;
	dword used = GetAccessKeys();
	for(Ctrl *ctrl = GetFirstChild(); ctrl; ctrl = ctrl->GetNext())
		used |= ctrl->GetAccessKeysDeep();
	return used;
}

void Ctrl::AssignAccessKeys(dword used)
{
	GuiLock __;
	for(Ctrl *ctrl = GetFirstChild(); ctrl; ctrl = ctrl->GetNext()) {
		ctrl->AssignAccessKeys(used);
		used |= ctrl->GetAccessKeys();
	}
}

dword Ctrl::GetAccessKeys() const
{
	return 0;
}

void Ctrl::DistributeAccessKeys()
{
	AssignAccessKeys(GetAccessKeysDeep());
}

bool Ctrl::VisibleAccessKeys()
{
	GuiLock __;
	if(GUI_AltAccessKeys())
		return GetAlt() && GetTopCtrl() == GetActiveCtrl();
	return true;
}

void Ctrl::State(int) {}

void Ctrl::StateDeep(int reason)
{
	GuiLock __;
	if(destroying)
		return;
	for(Ctrl *q = GetFirstChild(); q; q = q->GetNext())
		q->StateDeep(reason);
	State(reason);
}

void Ctrl::StateH(int reason)
{
	GuiLock __;
	for(int i = 0; i < statehook().GetCount(); i++)
		if((*statehook()[i])(this, reason))
			return;
	StateDeep(reason);
	if(reason == OPEN)
		DoSkin();
	FullRefreshCleanup();
}

bool   Ctrl::Accept()
{
	GuiLock __;
	if(!IsEnabled() || !IsShown())
		return true;
	if(DisplayError(GetData())) {
		SetWantFocus();
		return false;
	}
	for(Ctrl *q = GetFirstChild(); q; q = q->GetNext())
		if(!q->Accept()) return false;
	return true;
}

void   Ctrl::Reject()
{
	GuiLock __;
	for(Ctrl *q = GetFirstChild(); q; q = q->GetNext())
		q->Reject();
}

void   Ctrl::Serialize(Stream& s)
{
	GuiLock __;
	Value x;
	if(s.IsStoring())
		x = GetData();
	s % x;
	if(s.IsLoading())
		SetData(x);
	for(Ctrl *q = GetFirstChild(); q; q = q->GetNext())
		q->Serialize(s);
}

void Ctrl::Jsonize(JsonIO& jio)
{
	GuiLock __;
	Value x;
	if(jio.IsStoring())
		x = GetData();
	x.Jsonize(jio);
	if(jio.IsLoading())
		SetData(x);
}

void Ctrl::Xmlize(XmlIO& xio)
{
	GuiLock __;
	Value x;
	if(xio.IsStoring())
		x = GetData();
	x.Xmlize(xio);
	if(xio.IsLoading())
		SetData(x);
}

void Ctrl::Updated() {}

bool Ctrl::IsForeground() const
{
	GuiLock __;
	return GetTopCtrl()->IsWndForeground();
}

void Ctrl::SetForeground()
{
	GuiLock __;
	GetTopCtrl()->SetWndForeground();
}

bool Ctrl::IsOpen() const
{
	GuiLock __;
	const Ctrl *q = GetTopCtrl();
	return q->isopen && q->IsWndOpen();
}

void Ctrl::Show(bool ashow) {
	GuiLock __;
	if(visible != ashow) {
		visible = true;
		fullrefresh = false;
		RefreshFrame();
		visible = ashow;
		fullrefresh = false;
		RefreshFrame();
		Ctrl *parent = GetParent();
		if(parent)
			StateH(SHOW);
		if(top)
			WndShow(visible);
		if(InFrame() && parent)
			RefreshParentLayout();
	}
}

bool Ctrl::IsVisible() const {
	GuiLock __;
	const Ctrl *q = this;
	for(;;) {
		if(!q->visible) return false;
		Ctrl *p = q->GetParent();
		if(!p) break;
		q = p;
	}
	return q->visible;
}

void Ctrl::Enable(bool aenable) {
	GuiLock __;
	if(enabled != aenable) {
		enabled = aenable;
		if(top) WndEnable(enabled);
		if(!enabled && GetParent() && HasFocusDeep())
			IterateFocusForward(this, GetTopCtrl());
		RefreshFrame();
		StateH(ENABLE);
		SyncCaret();
	}
}

bool Ctrl::IsShowEnabled() const {
	GuiLock __;
	Ctrl *parent = GetParent();
	return IsEnabled() && (!parent || parent->IsShowEnabled());
}

Ctrl& Ctrl::SetEditable(bool aeditable) {
	GuiLock __;
	if(editable != aeditable) {
		editable = aeditable;
		RefreshFrame();
		StateH(EDITABLE);
	}
	return *this;
}

void Ctrl::SetModify()
{
	GuiLock __;
	modify = true;
	CancelMyPreedit();
}

void Ctrl::ClearModify()
{
	GuiLock __;
	modify = false;
	CancelMyPreedit();
}

void Ctrl::ClearModifyDeep()
{
	GuiLock __;
	ClearModify();
	for(Ctrl& q : *this)
		q.ClearModifyDeep();
}


bool Ctrl::IsModified() const
{
	GuiLock __;
	return modify;
}

bool Ctrl::IsModifiedDeep() const
{
	GuiLock __;
	if(IsModified()) return true;
	for(const Ctrl& q : *this)
		if(q.IsModified()) return true;
	return false;
}

Rect Ctrl::GetCaret() const
{
	return Null;
}

bool  Ctrl::SetWantFocus() {
	GuiLock __;
	if(IsWantFocus() && IsEnabled() && IsVisible() && IsOpen())
		return SetFocus();
	return false;
}

void Ctrl::UpdateRefresh() {
	Update();
	Refresh();
}

void Ctrl::Update() {
	SetModify();
	Updated();
}

void Ctrl::Action()
{
	Event<> h = WhenAction; // we make copy of action just in case widget is destroyed during the call
	h();
}

void Ctrl::UpdateAction() {
	Update();
	Action();
}

void Ctrl::UpdateActionRefresh() {
	Update();
	Action();
	Refresh();
};

void  Ctrl::CancelModeDeep() {
	GuiLock __;
	CancelMode();
	for(Ctrl& q : *this)
		q.CancelModeDeep();
}

String Ctrl::GetDesc() const
{
	return "";
}


String Name(const Ctrl *ctrl)
{
	return ctrl ? ctrl->Name() : String("NULL");
}

String Desc(const Ctrl *ctrl)
{
	if(!ctrl)
		return "NULL";
	String s;
	s << typeid(*ctrl).name();
	String q = ctrl->GetDesc();
	if(IsNull(q)) {
		if(ctrl->GetPrev()) {
			q = ctrl->GetPrev()->GetDesc();
			if(!IsNull(q))
			  s << " <<\"" << q << "\">>";
		}
	}
	else
	  s << " \"" << q << '\"';
	const Ctrl *top = ctrl->GetTopWindow();
	if(top && top != ctrl) {
		String q = top->GetDesc();
		if(IsNull(q))
			s << " (" << typeid(*top).name() << ")";
		else
			s << " (\"" << q << "\")";
	}
	return s;
}

#ifdef _DEBUG

#define sFLAG(x)  (x ? #x" " : "")
#define LG(x)     s << x << '\n'

void Ctrl::Dump(Stream& s) const {
	GuiLock __;
	LG(Name());
	LG(sFLAG(backpaint) << sFLAG(inframe) << sFLAG(visible) << sFLAG(enabled) <<
	   sFLAG(wantfocus) << sFLAG(editable) << sFLAG(IsModified()) << sFLAG(transparent));
	LG("Rect:   " << GetRect());
	LG("View:   " << GetView());
	for(int i = 0; i < GetFrameCount(); i++)
		LG("Frame " << i << ": " << typeid(decltype(*GetFrame0(i).frame)).name() << " - " << GetFrame0(i).GetView());
	LG("Data: " << GetData().ToString());
	if(children) {
		LG("Children");
		s << LOG_BEGIN;
		for(const Ctrl& q : *this) {
			q.Dump(s);
			LG("------");
		}
		s << LOG_END;
	}
	else
		LG("No child");
}

void Ctrl::Dump() const {
	Dump(VppLog());
}

void   Dump(const Ctrl *ctrl)
{
	if(ctrl)
		ctrl->Dump();
	else
		LOG("NULL");
}

void Ctrl::SetTitle(const char *s)
{
	TopWindow *w = GetTopWindow();
	if(w) w->Title(s);
}

#endif

bool Ctrl::IsOcxChild()
{
	return false;
}

Ctrl::Ctrl() {
	ONCELOCK {
		InstallPanicBox();
	}
	ASSERT_(IsMainRunning(), "GUI widgets cannot be global variables");
	ASSERT_(ThreadHasGuiLock(), "GUI widgets cannot be initialized in non-main thread without GuiLock");
	GuiLock __; // Beware: Even if we have ThreadHasGuiLock ASSERT, we still can be the main thread!
	LLOG("Ctrl::Ctrl");
	GuiPlatformConstruct();
	destroying = false;
	multi_frame = false;
	frame.frame = &NullFrame();
	frame.SetView(Null);
	enabled = visible = wantfocus = initfocus = true;
	editable = true;
	backpaint = IsCompositedGui() ? FULLBACKPAINT : TRANSPARENTBACKPAINT;
	inframe = false;
	ignoremouse = transparent = false;
	pos.x = PosLeft(0, 0);
	pos.y = PosTop(0, 0);
	rect = Rect(0, 0, 0, 0);
	inloop = popup = isopen = false;
	modify = false;
	unicode = false;
	popupgrab = false;
	fullrefresh = false;
	akv = false;
	layout_id_literal = false;
	top = false;
	uparent = nullptr;
	megarect = false;
}

void KillTimeCallbacks(void *id, void *idlim);

void Ctrl::DoRemove() {
	GuiLock __;
	if(!IsOpen()) return;
	ReleaseCapture();
	if(HasChildDeep(captureCtrl))
		captureCtrl->ReleaseCapture();
	CancelModeDeep();
	if(HasChildDeep(mouseCtrl) || mouseCtrl == this)
		mouseCtrl = NULL;
	LLOG("DoRemove " << Name() << " focusCtrl: " << UPP::Name(focusCtrl));
	GuiPlatformRemove();
	Ctrl *parent = GetParent();
	if(HasFocusDeep()) {
		LLOG("DoRemove - HasFocusDeep");
		if(destroying) {
			if(parent) {
				LLOG("parent - deferred SetFocus / ChildLostFocus; parent = " << UPP::Name(parent));
				defferedSetFocus = parent;
				defferedChildLostFocus.Add(parent);
			}
			else
				if(IsPopUp()) {
					LLOG("Remove Popup");
					Ctrl *owner = GetOwner();
					if(owner && owner->IsEnabled())
						owner->ActivateWnd();
				}
			NoWantFocus();
		}
		else {
			Ptr<Ctrl> fc = focusCtrl;
			focusCtrl = NULL;
			DoKillFocus(fc, NULL);
			if(parent) {
				LLOG("DoRemove -> SetFocus(" << UPP::Name(parent) << "), focusCtrl = " << UPP::Name(focusCtrl) << ", fc = " << UPP::Name(fc));
				bool b = IsWantFocus();
				NoWantFocus();
				parent->SetFocus0(false);
				WantFocus(b);
			}
			else
				if(IsPopUp()) {
					LLOG("Remove Popup");
					Ctrl *owner = GetOwner();
					if(owner && owner->IsEnabled()) {
						LLOG("Remove popup -> owner->ActivateWnd");
						owner->ActivateWnd();
					}
				}
		}
		SyncCaret();
	}
	LLOG("//DoRemove " << Name() << " focusCtrl: " << UPP::Name(focusCtrl));
}

void Ctrl::Close()
{
	GuiLock __;
	Ctrl *q = GetTopCtrl();
	if(!q->top) return;
	DoRemove();
	if(GetParent()) return;
	StateH(CLOSE);
	USRLOG("   CLOSE " + Desc(this));
	WndDestroy();
	visible = true;
	popup = false;
}

Ctrl::~Ctrl() {
	GuiLock __;
	LLOG("Ctrl::~Ctrl");
	destroying = true;
	while(GetFirstChild())
		RemoveChild(GetFirstChild());
	Ctrl *parent = GetParent();
	if(parent)
		parent->RemoveChild(this);
	Close();
	KillTimeCallbacks(this, (byte *) this + sizeof(Ctrl));
	ClearInfo();
	FreeFrames();
}

Vector<Ctrl::MouseHook>& Ctrl::mousehook() { static Vector<Ctrl::MouseHook> h; return h; }
Vector<Ctrl::KeyHook>&   Ctrl::keyhook() { static Vector<Ctrl::KeyHook> h; return h; }
Vector<Ctrl::StateHook>& Ctrl::statehook() { static Vector<Ctrl::StateHook> h; return h; }

void Ctrl::InstallMouseHook(MouseHook hook)
{
	GuiLock __;
	mousehook().Add(hook);
}

void Ctrl::DeinstallMouseHook(MouseHook hook)
{
	GuiLock __;
	int q = FindIndex(mousehook(), hook);
	if(q >= 0) mousehook().Remove(q);
}

void Ctrl::InstallKeyHook(KeyHook hook)
{
	GuiLock __;
	keyhook().Add(hook);
}

void Ctrl::DeinstallKeyHook(KeyHook hook)
{
	GuiLock __;
	int q = FindIndex(keyhook(), hook);
	if(q >= 0) keyhook().Remove(q);
}

void Ctrl::InstallStateHook(StateHook hook)
{
	GuiLock __;
	statehook().Add(hook);
}

void Ctrl::DeinstallStateHook(StateHook hook)
{
	GuiLock __;
	int q = FindIndex(statehook(), hook);
	if(q >= 0) statehook().Remove(q);
}

static char sZoomText[] = "OK Cancel Exit Retry";

const char *Ctrl::GetZoomText()
{
	GuiLock __;
	return sZoomText;
}

Size Ctrl::Bsize;
Size Ctrl::Dsize;
Size Ctrl::Csize;
bool Ctrl::IsNoLayoutZoom;

void InitRichTextZoom()
{
	Size h = 96 * Ctrl::Bsize / Ctrl::Dsize;
	SetRichTextStdScreenZoom(min(h.cx, h.cy), 600);
	Ctrl::ReSkin();
}


void Ctrl::Csizeinit()
{
	GuiLock __;
	if(Csize.cx == 0 || Dsize.cx == 0) {
		bool bigger = GetStdFontCy() > 20;
		if(Csize.cx == 0) {
			Csize = GetTextSize(sZoomText, StdFont());
			Csize.cy += 4 * bigger; // this is intended to compensate for editfield / droplist edges - in any case looks better on UHD
		}
		Bsize = Csize;
		if(Dsize.cx == 0)
			Dsize = Size(99, 13 + 4 * bigger);
		Csize.cx = max(Csize.cx, Dsize.cx);
		Csize.cy = max(Csize.cy, Dsize.cy);
		InitRichTextZoom();
	}
}

void Ctrl::SetZoomSize(Size sz, Size bsz)
{
	GuiLock __;
	Csize = sz;
	Dsize = bsz;
	IsNoLayoutZoom = false;
	ReSkin();
}

void Ctrl::NoLayoutZoom()
{
	GuiLock __;
	IsNoLayoutZoom = true;
	Csize = Dsize = Size(1, 1);
	ReSkin();
}

void Ctrl::GetZoomRatio(Size& m, Size& d)
{
	GuiLock __;
	m = Csize;
	d = Dsize;
}

int  Ctrl::HorzLayoutZoom(int cx)
{
	Csizeinit();
	return cx > -16000 ? Csize.cx * cx / Dsize.cx : cx;
}

double  Ctrl::HorzLayoutZoomf(double cx)
{
	Csizeinit();
	return cx > -16000 ? Csize.cx * cx / Dsize.cx : cx;
}

int  Ctrl::VertLayoutZoom(int cy)
{
	Csizeinit();
	return cy > -16000 ? Csize.cy * cy / Dsize.cy : cy;
}

Size Ctrl::LayoutZoom(int cx, int cy)
{
	return Size(HorzLayoutZoom(cx), VertLayoutZoom(cy));
}

Size Ctrl::LayoutZoom(Size sz)
{
	return LayoutZoom(sz.cx, sz.cy);
}

Font FontZ(int face, int height)
{
	return Font(face, Ctrl::VertLayoutZoom(height));
}

bool ApplicationUHDEnabled = true;

void Ctrl::SetUHDEnabled(bool set)
{
	ApplicationUHDEnabled = set;
	ReSkin();
}

bool Ctrl::IsUHDEnabled()
{
	if(GetEnv("UPP_DISABLE_UHD__") == "1")
		return false;
	return ApplicationUHDEnabled;
}

bool ApplicationDarkThemeEnabled = true;

void Ctrl::SetDarkThemeEnabled(bool set)
{
	ApplicationDarkThemeEnabled = set;
	ReSkin();
}

bool Ctrl::IsDarkThemeEnabled()
{
	return ApplicationDarkThemeEnabled;
}

Font StdFontZ(int height)   { return FontZ(Font::STDFONT, height); }
Font SansSerifZ(int height) { return FontZ(Font::SANSSERIF, height); }
Font SerifZ(int height)     { return FontZ(Font::SERIF, height); }
Font MonospaceZ(int height) { return FontZ(Font::MONOSPACE, height); }

Font ScreenSansZ(int height) { return FontZ(Font::SCREEN_SANS, height); }
Font ScreenSerifZ(int height) { return FontZ(Font::SCREEN_SERIF, height); }
Font ScreenFixedZ(int height) { return FontZ(Font::SCREEN_FIXED, height); }
Font RomanZ(int height) { return FontZ(Font::ROMAN, height); }
Font ArialZ(int height) { return FontZ(Font::ARIAL, height); }
Font CourierZ(int height) { return FontZ(Font::COURIER, height); }

String Ctrl::appname;

void Ctrl::SetAppName(const String& nm)
{
	GuiLock __;
	appname = nm;
}

String Ctrl::GetAppName()
{
	GuiLock __;
	if(appname.IsEmpty())
		appname = GetExeTitle();
	return appname;
}

static bool _ClickFocus;
bool Ctrl::ClickFocus() { return _ClickFocus; }
void Ctrl::ClickFocus(bool cf) { _ClickFocus = cf; }


Vector<Ctrl *> Ctrl::GetTopWindows()
{
	GuiLock __;
	Vector<Ctrl *> c = GetTopCtrls();
	Vector<Ctrl *> r;
	for(int i = 0; i < c.GetCount(); i++)
		if(!c[i]->IsPopUp())
			r.Add(c[i]);
	return r;
}

void Ctrl::CloseTopCtrls()
{
	GuiLock __;
	Vector<Ctrl *> tc = Ctrl::GetTopCtrls();
	for(int i = 0; i < tc.GetCount(); i++)
		tc[i]->Close();
}

bool xpstyle;

bool IsOrOwnedBy(Ctrl *q, Ctrl *window)
{
	while(q) {
		if(q == window)
			return true;
		q = q->GetOwner();
	}
	return false;
}

Vector< Ptr<Ctrl> > DisableCtrls(const Vector<Ctrl *>& ctrl, Ctrl *exclude)
{
	Vector< Ptr<Ctrl> > disabled;
	for(int i = 0; i < ctrl.GetCount(); i++) {
		Ctrl *q = ctrl[i];
		if(q && q->IsEnabled() && !IsOrOwnedBy(q, exclude)) {
			q->Disable();
			disabled.Add(q);
		}
	}
	return disabled;
}

void EnableCtrls(const Vector< Ptr<Ctrl> >& ctrl)
{
	for(int i = ctrl.GetCount(); --i >= 0;) {
		Ctrl *q = ctrl[i];
		if(q) q->Enable();
	}
}

void Modality::Begin(Ctrl *modal, bool fo)
{
	active = Ctrl::GetFocusCtrl();
	fore_only = fo;
	LLOG("Active " << Name(active));
	enable = DisableCtrls(Ctrl::GetTopWindows(), modal);
}

void Modality::End()
{
	EnableCtrls(enable);
	if(active && (!fore_only || active->IsForeground()))
		active->SetFocus();
	enable.Clear();
	active = NULL;
}

extern void (*whenSetStdFont)();

INITBLOCK {
	whenSetStdFont = &Ctrl::ReSkin;
}

void (**Ctrl::skin)();
int Ctrl::skini;

void CtrlSetDefaultSkin(void (**_skin)())
{
	Ctrl::skin = _skin;
}

void Ctrl::SetSkin(void (*_skin)())
{
	GuiLock __;
	skin[0] = _skin;
	ReSkin();
}

void Ctrl::ReSkin()
{
	GuiLock __;
	static int lock;
	if(lock)
		return;
	lock++;
	ChReset();
	Csize.cx = Dsize.cx = IsNoLayoutZoom;
	Iml::SkinAll();
	if(skin[skini])
		(*skin[skini])();
	Csize.cx = Dsize.cx = IsNoLayoutZoom;
	Csizeinit();
	ChFinish();
	Vector<Ctrl *> ctrl = GetTopCtrls();
	for(int i = 0; i < ctrl.GetCount(); i++) {
		ctrl[i]->RefreshLayoutDeep();
		ctrl[i]->DoSkin();
		ctrl[i]->RefreshFrame();
	}
	lock--;
}

static bool s_skin_change_sensitive;

void Ctrl::SkinChangeSensitive(bool b)
{
	s_skin_change_sensitive = b;
}

void Ctrl::PostReSkin()
{ // use timer so that it is done just once if there are multiple windows
	static TimeCallback tm;
	if(s_skin_change_sensitive)
		tm.KillPost([=] { ReSkin(); });
}

void Ctrl::DoSkin()
{
	for(Ctrl& q : *this)
		q.DoSkin();
	Skin();
}

void Ctrl::SwapDarkLight()
{
	skini = skini ? 0 : IsDarkTheme() ? 1 : 2;
	ReSkin();
}

CH_INT(GUI_GlobalStyle, GUISTYLE_CLASSIC);
CH_INT(GUI_DragFullWindow, 1);
CH_INT(GUI_PopUpEffect, GUIEFFECT_SLIDE);
CH_INT(GUI_ToolTips, 1);
CH_INT(GUI_ToolTipDelay, 1000);
CH_INT(GUI_DropShadows, 1);
CH_INT(GUI_AltAccessKeys, 1);
CH_INT(GUI_AKD_Conservative, 0);
CH_INT(GUI_DragDistance, 4);
CH_INT(GUI_DblClickTime, 500);
CH_INT(GUI_WheelScrollLines, 3);

String Ctrl::Name0() const {
	GuiLock __;
	String s = CppDemangle(typeid(*this).name());
	const Ctrl *q = this;
	String path;
	while(q) {
		String id = q->GetLayoutId();
		if(id.GetCount())
			path = '.' + q->GetLayoutId() + path;
		q = q->GetParent();
	}
	s << ' ' << path;
#ifdef CPU_64
	s << " : 0x" + FormatIntHex(this);
#else
	s << " : " + Format("0x%x", (int) this);
#endif
	Ctrl *parent = GetParent();
	if(IsChild())
		s << " (parent " << CppDemangle(typeid(*parent).name()) << ")";
	return s;
}

String Ctrl::Name(Ctrl *ctrl)
{
	return Upp::Name(ctrl);
}

void   Ctrl::EndLoop()
{
	GuiLock __;
	inloop = false;
	SysEndLoop();
}

void   Ctrl::EndLoop(int code)
{
	GuiLock __;
	ASSERT(!GetParent());
	TopWindow *w = GetTopWindow();
	if(w)
		w->exitcode = code;
	EndLoop();
}

bool   Ctrl::InLoop() const
{
	GuiLock __;
	return inloop;
}

bool   Ctrl::InCurrentLoop() const
{
	GuiLock __;
	return GetLoopCtrl() == this;
}

#ifdef HAS_TopFrameDraw

ViewDraw::ViewDraw(Ctrl *ctrl, const Rect& r)
:	TopFrameDraw(ctrl, (ctrl->GetScreenView() & (r + ctrl->GetScreenView().TopLeft()))
                       - ctrl->GetTopCtrl()->GetScreenRect().TopLeft())
{
	Point p = r.TopLeft();
	Offset(min(p.x, 0), min(p.y, 0));
}

#endif

}