winamp/Src/Wasabi/api/wndmgr/skinembed.cpp
2024-09-24 14:54:57 +02:00

734 lines
18 KiB
C++

#include <precomp.h>
#include "skinembed.h"
#include <api/wndmgr/layout.h>
#include <api/wnd/wndclass/wndholder.h>
#include <api/skin/skinparse.h>
#include <api/wnd/wndtrack.h>
#include <api/config/items/cfgitemi.h>
#include <api/config/items/attrint.h>
#include <bfc/named.h>
#include <api/wndmgr/autopopup.h>
#include <api/syscb/callbacks/wndcb.h>
#include <api/wndmgr/skinwnd.h>
#include <api/script/scriptmgr.h>
#include <bfc/critsec.h>
CriticalSection skinembed_cs;
SkinEmbedder *skinEmbedder = NULL;
SkinEmbedder::SkinEmbedder()
{
ASSERTPR(skinEmbedder == NULL, "only one skin embedder please!");
skinEmbedder = this;
}
SkinEmbedder::~SkinEmbedder()
{
inserted.deleteAll();
allofthem.deleteAll();
}
int SkinEmbedder::toggle(GUID g, const wchar_t *prefered_container, int container_flag, RECT *r, int transcient)
{
ifc_window *w = enumItem(g, 0);
if (w != NULL)
{
destroy(w, r);
return 0;
}
ifc_window *wnd = create(g, NULL, prefered_container, container_flag, r, transcient);
if (wnd == NULL)
{
#ifdef ON_CREATE_EXTERNAL_WINDOW_GUID
int y;
ON_CREATE_EXTERNAL_WINDOW_GUID(g, y);
#endif
}
return 1;
}
int SkinEmbedder::toggle(const wchar_t *groupid, const wchar_t *prefered_container, int container_flag, RECT *r, int transcient)
{
ifc_window *w = enumItem(groupid, 0);
if (w != NULL)
{
destroy(w, r);
return 0;
}
create(INVALID_GUID, groupid, prefered_container, container_flag, r, transcient);
return 1;
}
ifc_window *SkinEmbedder::create(GUID g, const wchar_t *prefered_container, int container_flag, RECT *r, int transcient, int starthidden, int *isnew)
{
return create(g, NULL, prefered_container, container_flag, r, transcient, starthidden, isnew);
}
ifc_window *SkinEmbedder::create(const wchar_t *groupid, const wchar_t *prefered_container, int container_flag, RECT *r, int transcient, int starthidden, int *isnew)
{
return create(INVALID_GUID, groupid, prefered_container, container_flag, r, transcient, starthidden, isnew);
}
WindowHolder *SkinEmbedder::getSuitableWindowHolder(GUID g, const wchar_t *group_id, Container *cont, Layout *lay, int _visible, int _dynamic, int _empty, int _hasself, int _autoavail)
{
foreach(wndholders)
WindowHolder *h = wndholders.getfor();
ifc_window *w = h->getRootWndPtr()->getDesktopParent();
int dyn = 1;
int visible = 0;
int hasself = 0;
int empty = 0;
int autoavail = h->wndholder_isAutoAvailable();
Layout *l = (NULL != w) ? static_cast<Layout *>(w->getInterface(layoutGuid)) : NULL;
Container *c = NULL;
if (l)
{
c = l->getParentContainer();
if (c)
{
dyn = c->isDynamic();
}
}
if (NULL == w || !w->isInited()) visible = -1;
else
{
if (_visible == 1)
{
if (h->getRootWndPtr()->isVisible()) visible = 1;
}
else if (_visible == 0)
{
if (w->isVisible()) visible = 1;
}
}
if (!h->getCurRootWnd()) empty = 1;
if (g != INVALID_GUID)
{
if (g == h->getCurGuid())
hasself = 1;
}
else if (group_id && h->getCurGroupId())
{
if (!WCSICMP(group_id, h->getCurGroupId()))
hasself = 1;
}
if (_visible != -1)
{
if (visible != _visible) continue;
}
if (_dynamic != -1)
{
if (_dynamic != dyn) continue;
}
if (_empty != -1)
{
if (empty != _empty) continue;
}
if (_hasself != -1)
{
if (hasself != _hasself) continue;
}
if (_autoavail != -1)
{
if (autoavail != _autoavail) continue;
}
if (cont != NULL)
{
if (c != cont) continue;
}
if (lay != NULL)
{
if (l != lay) continue;
}
if (g != INVALID_GUID)
{
if (h->wantGuid(g)) return h;
}
else if (group_id)
{
if (h->wantGroup(group_id)) return h;
}
endfor;
return NULL;
}
ifc_window *SkinEmbedder::create(GUID g, const wchar_t *groupid, const wchar_t *prefered_container, int container_flag, RECT *r, int transcient, int starthidden, int *isnew)
{
// InCriticalSection in_cs(&skinembed_cs);
foreach(in_deferred_callback)
SkinEmbedEntry *e = in_deferred_callback.getfor();
if ((e->guid != INVALID_GUID && e->guid == g) || WCSCASEEQLSAFE(groupid, e->groupid))
{
#ifdef _DEBUG
DebugStringW(L"trying to show a window that is being destroyed, hm, try again later\n");
#endif
// too soon! try later dude
return NULL;
}
endfor;
RECT destrect = {0};
if (isnew) *isnew = 0;
WindowHolder *wh = NULL;
// todo: get from c++ callback
if (SOM::checkAbortShowHideWindow(g, 1))
return NULL;
if (g != INVALID_GUID)
wh = SOM::getSuitableWindowHolderFromScript(g);
if (!wh) wh = getSuitableWindowHolder(g, groupid, NULL, NULL, 1 /*visible*/, 0 /*static*/, 1 /*empty*/, -1 /*donttest*/, -1 /*donttest*/);
if (!wh) wh = getSuitableWindowHolder(g, groupid, NULL, NULL, 0 /*hidden*/, 0 /*static*/, 0 /*notempty*/, 1 /*hasself*/, -1 /*donttest*/);
if (!wh) wh = getSuitableWindowHolder(g, groupid, NULL, NULL, 0 /*hidden*/, 0 /*static*/, 1 /*empty*/, -1 /*donttest*/, 1 /*autoavail*/);
if (!wh) wh = getSuitableWindowHolder(g, groupid, NULL, NULL, 1 /*visible*/, 1 /*dynamic*/, 1 /*empty*/, -1 /*donttest*/, -1 /*donttest*/);
ifc_window *whrw = wh ? wh->getRootWndPtr() : NULL;
Container *cont = NULL;
Layout *lay = NULL;
ifc_window *wnd = NULL;
int newcont = 0;
if (!wh)
{ // no hidden static container, so lets create a dynamic one
if (isnew) *isnew = 1;
if (container_flag == 0 && (prefered_container == NULL || !*prefered_container))
{
prefered_container = AutoPopup::getDefaultContainerParams(groupid, g, &container_flag);
}
if (container_flag == 0 && (prefered_container == NULL || !*prefered_container))
{
prefered_container = WASABI_DEFAULT_STDCONTAINER;
}
cont = SkinParser::loadContainerForWindowHolder(groupid, g, 0, transcient, prefered_container, container_flag);
if (cont)
{
cont->setTranscient(transcient);
newcont = 1;
lay = cont->enumLayout(0);
/* if (prefered_layout) { // find its target layout
cont->setDefaultLayout(prefered_layout);
lay = cont->getLayout(prefered_layout);
if (!lay) {
if (!layout_flag) // see if we can fallback
lay = cont->enumLayout(0);
else {
destroyContainer(cont); // deferred
api->console_outputString(9, StringPrintf("could not find the requested layout (%s) to host the window", prefered_container));
return NULL;
}
}*/
}
else
{
DebugStringW(L"no container found to hold the window... not even a default one :/\n");
}
wh = getSuitableWindowHolder(g, groupid, cont, lay, -1 /*donttest*/, -1 /*donttest*/, 0 /*notempty*/, 1 /*hasself*/, -1 /*donttest*/);
if (!wh) wh = getSuitableWindowHolder(g, groupid, cont, lay, -1 /*donttest*/, -1 /*donttest*/, 1 /*empty*/, -1 /*donttest*/, -1 /*donttest*/);
whrw = wh ? wh->getRootWndPtr() : NULL;
}
if (wh)
{
GuiObject *o = static_cast<GuiObject *>(whrw->getInterface(guiObjectGuid));
ASSERT(o != NULL);
if (o)
{
lay = o->guiobject_getParentLayout();
if (lay)
cont = lay->getParentContainer();
}
if ((g != INVALID_GUID && (g == wh->getCurGuid())) || (groupid && (wh->getCurGroupId() == groupid)))
wnd = wh->getCurRootWnd();
else
{
if (wh->getCurRootWnd())
wh->onRemoveWindow();
wnd = wh->onInsertWindow(g, groupid);
}
}
if (!wnd)
{
if (cont)
{
if (!WCSCASEEQLSAFE(cont->getId(), L"main"))
cont->close();
}
return NULL;
}
//int anim = 1;
//if (wh && !whrw->getAnimatedRects()) anim = 0; // FIXME!!
if (lay && r)
{
lay->getWindowRect(&destrect);
}
if (cont /*&& cont->isDynamic()*/)
{
const wchar_t *text = wnd->getRootWndName();
cont->setTranscient(transcient);
if (text != NULL)
if (cont->isDynamic()) cont->setName(text);
if (cont->isDynamic() && !transcient) cont->resetLayouts();
if (newcont) cont->onInit(1);
}
#ifdef WASABI_COMPILE_CONFIG
// {280876CF-48C0-40bc-8E86-73CE6BB462E5}
const GUID options_guid =
{ 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } };
CfgItem *cfgitem = WASABI_API_CONFIG->config_getCfgItemByGuid(options_guid);
int findopenrect = _int_getValue(cfgitem, L"Find open rect");
#else
int findopenrect = WASABI_WNDMGR_FINDOPENRECT;
#endif
if (wh && findopenrect)
{
if (lay)
{
RECT rl;
lay->getWindowRect(&rl);
RECT nr = windowTracker->findOpenRect(rl, lay);
destrect = nr; // rewrite destrect
int w = rl.right - rl.left;
int h = rl.bottom - rl.top;
lay->divRatio(&w, &h);
nr.right = nr.left + w;
nr.bottom = nr.top + h;
lay->resizeToRect(&nr);
}
}
if (wh)
{
if (r)
/*if (anim)*/ WASABI_API_WNDMGR->drawAnimatedRects(r, &destrect);
/*else
{
RECT r;
r.left = destrect.left + (destrect.right - destrect.left) / 2 - 1;
r.top = destrect.top + (destrect.bottom - destrect.top) / 2 + 1;
r.right = r.left + 2;
r.bottom = r.top + 2;
if (anim) WASABI_API_WNDMGR->drawAnimatedRects(&r, &destrect);
}*/
}
if (!starthidden)
{
if (cont && cont->isVisible())
{ // we were already shown, duh
// cont->setVisible(0);
}
else
{
if (cont) cont->setVisible(1);
else if (lay) lay->setVisible(1);
}
}
if (wnd && newcont && !transcient)
{
inserted.addItem(new SkinEmbedEntry(wnd->getDependencyPtr(), wnd, g, groupid, prefered_container, container_flag, cont, wh));
viewer_addViewItem(wnd->getDependencyPtr());
}
if (!transcient && wnd)
allofthem.addItem(new SkinEmbedEntry(wnd->getDependencyPtr(), wnd, g, groupid, prefered_container, container_flag, cont, wh));
return wnd;
}
void SkinEmbedder::destroy(ifc_window *w, RECT *r)
{
int donecheck = 0;
SkinEmbedEntry *e = NULL;
for (int i = 0; i < allofthem.getNumItems();i++)
{
e = allofthem.enumItem(i);
if (e->wnd == w)
{
donecheck = 1;
if (SOM::checkAbortShowHideWindow(e->guid, 0))
return ;
break;
}
}
if (WASABI_API_WND->rootwndIsValid(w))
{
if (!donecheck)
{
ifc_window *ww = w->findWindowByInterface(windowHolderGuid);
if (ww)
{
WindowHolder *wh = static_cast<WindowHolder*>(ww->getInterface(windowHolderGuid));
if (wh)
{
GUID g = wh->getCurGuid();
if (g != INVALID_GUID)
{
donecheck = 1;
if (SOM::checkAbortShowHideWindow(g, 0))
return ;
}
}
}
}
}
// checkAbort can render w invalid !!! check again
if (WASABI_API_WND->rootwndIsValid(w))
{
ifc_window *wnd = w->getDesktopParent();
GuiObject *go = static_cast<GuiObject *>(wnd->getInterface(guiObjectGuid));
if (go)
{
Layout *l = go->guiobject_getParentLayout();
if (l)
{
Container *c = l->getParentContainer();
if (c)
{
if (!WCSCASEEQLSAFE(c->getId(), L"main"))
{
c->close(); // deferred if needed
}
else
{
softclose:
ifc_window *wnd = w->findWindowByInterface(windowHolderGuid);
if (wnd != NULL)
{
WindowHolder *wh = static_cast<WindowHolder *>(wnd->getInterface(windowHolderGuid));
if (wh != NULL)
{
wh->onRemoveWindow(1);
}
}
}
}
else goto softclose;
}
}
}
}
int SkinEmbedder::getNumItems(GUID g)
{
int n = 0;
for (int i = 0;i < wndholders.getNumItems();i++)
if (wndholders.enumItem(i)->getRootWndPtr()->isVisible() && wndholders.enumItem(i)->getCurGuid() == g) n++;
return n;
}
int SkinEmbedder::getNumItems(const wchar_t *groupid)
{
int n = 0;
for (int i = 0;i < wndholders.getNumItems();i++)
{
WindowHolder *wh = wndholders.enumItem(i);
if (wh->getRootWndPtr()->isVisible()
&& WCSCASEEQLSAFE(wh->getCurGroupId(), groupid))
n++;
}
return n;
}
ifc_window *SkinEmbedder::enumItem(GUID g, int n)
{
ASSERT(n >= 0);
for (int i = 0;i < wndholders.getNumItems();i++)
{
WindowHolder *wh = wndholders.enumItem(i);
if (wh->getCurGuid() == g)
{
ifc_window *w = wh->getRootWndPtr();
Container *c = NULL;
if (w)
{
w = w->getDesktopParent();
if (w)
{
Layout *l = static_cast<Layout *>(w->getInterface(layoutGuid));
if (l)
{
c = l->getParentContainer();
}
}
}
if (c && c->isVisible() || (!c && wndholders.enumItem(i)->getRootWndPtr()->isVisible()))
{
if (n == 0)
return wndholders.enumItem(i)->getCurRootWnd();
n--;
}
}
}
return NULL;
}
ifc_window *SkinEmbedder::enumItem(const wchar_t *groupid, int n)
{
ASSERT(n >= 0);
for (int i = 0;i < wndholders.getNumItems();i++)
{
WindowHolder *wh = wndholders.enumItem(i);
const wchar_t *curgroupid = wndholders[i]->getCurGroupId();
if (WCSCASEEQLSAFE(curgroupid, groupid))
{
ifc_window *w = wh->getRootWndPtr();
Container *c = NULL;
if (w)
{
w = w->getDesktopParent();
if (w)
{
Layout *l = static_cast<Layout *>(w->getInterface(layoutGuid));
if (l)
{
c = l->getParentContainer();
}
}
}
if (c && c->isVisible() || (!c && wndholders.enumItem(i)->getRootWndPtr()->isVisible()))
{
if (n == 0)
return wndholders.enumItem(i)->getCurRootWnd();
n--;
}
}
}
return NULL;
}
void SkinEmbedder::registerWindowHolder(WindowHolder *wh)
{
if (wndholders.haveItem(wh)) return ;
wndholders.addItem(wh);
}
void SkinEmbedder::unregisterWindowHolder(WindowHolder *wh)
{
if (!wndholders.haveItem(wh)) return ;
wndholders.removeItem(wh);
}
int SkinEmbedder::viewer_onItemDeleted(api_dependent *item)
{
foreach(in_deferred_callback)
if (in_deferred_callback.getfor()->dep == item)
in_deferred_callback.removeByPos(foreach_index);
endfor
foreach(inserted)
SkinEmbedEntry *e = inserted.getfor();
if (e->dep == item)
{
inserted.removeItem(e);
allofthem.removeItem(e);
delete e;
break;
}
endfor;
foreach(allofthem)
SkinEmbedEntry *e = allofthem.getfor();
if (e->dep == item)
{
allofthem.removeItem(e);
delete e;
break;
}
endfor;
return 1;
}
void SkinEmbedder::cancelDestroyContainer(Container *c)
{
if (!cancel_deferred_destroy.haveItem(c)) cancel_deferred_destroy.addItem(c);
}
void SkinEmbedder::destroyContainer(Container *o)
{
ASSERT(o);
//fg>disabled as of 11/4/2003, side effect of cancelling fullscreen video (ie, notifier while playing video fullscreen)
//delt with the problem in basewnd directly
//#ifdef WIN32
//SetFocus(NULL); // FG> this avoids Win32 calling directly WM_KILLFOCUS into a child's wndproc (couldn't they just have posted the damn msg ?), who would then call some of his parent's virtual functions while it is being deleted
// perhaps it would be good to add a generic way to disable most child wnd messages while the parent is being deleted
//#endif
cancel_deferred_destroy.removeItem(o);
foreach(inserted)
SkinEmbedEntry *e = inserted.getfor();
if (e->container == o)
{
if (!in_deferred_callback.haveItem(e))
in_deferred_callback.addItem(e);
break;
}
endfor
deferred_destroy.addItem(o);
timerclient_setTimer(CB_DESTROYCONTAINER, 20);
//o->setVisible(0);
//timerclient_postDeferredCallback(CB_DESTROYCONTAINER, (intptr_t)o);
}
void SkinEmbedder::timerclient_timerCallback(int id)
{
if (id == CB_DESTROYCONTAINER)
{
foreach(deferred_destroy)
Container *c = deferred_destroy.getfor();
foreach(in_deferred_callback)
if (in_deferred_callback.getfor()->container == c)
{
in_deferred_callback.removeByPos(foreach_index);
}
endfor;
if (cancel_deferred_destroy.haveItem(c))
{
continue;
}
if (SkinParser::isContainer(c)) // otherwise i'ts already gone while we were waiting for the callback, duh!
delete c;
endfor
cancel_deferred_destroy.removeAll();
deferred_destroy.removeAll();
timerclient_killTimer(CB_DESTROYCONTAINER);
return ;
}
TimerClientDI::timerclient_timerCallback(id);
}
int SkinEmbedder::timerclient_onDeferredCallback(intptr_t p1, intptr_t p2)
{
return TimerClientDI::timerclient_onDeferredCallback(p1, p2);
}
#ifdef WASABI_COMPILE_CONFIG
void SkinEmbedder::saveState()
{
int i = 0;
wchar_t t[1024] = {0};
foreach(inserted)
SkinEmbedEntry *e = inserted.getfor();
if (e->guid != INVALID_GUID)
{
nsGUID::toCharW(e->guid, t);
}
else
{
WCSCPYN(t, e->groupid, 1024);
}
WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"wndstatus/id/%d", i), t);
WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"wndstatus/layout/%d", i), !e->layout.isempty() ? e->layout.getValue() : L"");
WASABI_API_CONFIG->setIntPrivate(StringPrintfW(L"wndstatus/flag/%d", i), e->required);
i++;
endfor;
WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"wndstatus/id/%d", i), L"");
WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"wndstatus/layout/%d", i), L"");
WASABI_API_CONFIG->setIntPrivate(StringPrintfW(L"wndstatus/flag/%d", i), 0);
}
void SkinEmbedder::restoreSavedState()
{
int i = 0;
wchar_t t[1024] = {0};
wchar_t l[256] = {0};
while (1)
{
WASABI_API_CONFIG->getStringPrivate(StringPrintfW(L"wndstatus/id/%d", i), t, 1024, L"");
if (!*t) break;
WASABI_API_CONFIG->getStringPrivate(StringPrintfW(L"wndstatus/layout/%d", i), l, 256, L"");
int flag = WASABI_API_CONFIG->getIntPrivate(StringPrintfW(L"wndstatus/flag/%d", i), 0);
GUID g = nsGUID::fromCharW(t);
//ifc_window *created = NULL;
//int tried = 0;
if (g != INVALID_GUID)
{
ifc_window *w = enumItem(g, 0);
if (w == NULL)
{
//tried = 1;
/*created = */create(g, l, flag);
}
}
else
{
ifc_window *w = enumItem(t, 0);
if (w == NULL)
{
//tried = 1;
/*created = */create(t, l, flag);
}
}
/* if (tried && !created) {
for (int j=0;j<inserted.getNumItems();j++) {
SkinEmbedEntry *e = inserted.enumItem(j);
int yes = 0;
if (g != INVALID_GUID) {
if (e->guid == g && e->required == flag && STRCASEEQLSAFE(e->layout, l))
yes = 1;
} else {
if (STRCASEEQLSAFE(e->groupid, t) && e->required == flag && STRCASEEQLSAFE(e->layout, l))
yes = 1;
}
if (yes) {
inserted.removeByPos(j);
j--;
delete e;
}
}
}*/
i++;
}
}
#endif
void SkinEmbedder::attachToSkin(ifc_window *w, int side, int size)
{
RECT r;
if (w == NULL) return ;
ifc_window *_w = w->getDesktopParent();
if (_w == NULL) _w = w;
SkinParser::getSkinRect(&r, _w);
switch (side)
{
case SKINWND_ATTACH_RIGHT:
_w->resize(r.right, r.top, size, r.bottom - r.top);
break;
case SKINWND_ATTACH_TOP:
_w->resize(r.left, r.top - size, r.right - r.left, size);
break;
case SKINWND_ATTACH_LEFT:
_w->resize(r.left - size, r.top, size, r.bottom - r.top);
break;
case SKINWND_ATTACH_BOTTOM:
_w->resize(r.left, r.bottom, r.right - r.left, size);
break;
}
}
PtrList<SkinEmbedEntry> SkinEmbedder::in_deferred_callback;
PtrList<Container> SkinEmbedder::cancel_deferred_destroy;
PtrList<Container> SkinEmbedder::deferred_destroy;