947 lines
29 KiB
C++
947 lines
29 KiB
C++
|
#include <precomp.h>
|
||
|
#include "wa2wndembed.h"
|
||
|
#include "wa2frontend.h"
|
||
|
#include "wa2buckitems.h"
|
||
|
#include "embedwndguid.h"
|
||
|
#include "main.h"
|
||
|
#include <api/wnd/bucketitem.h>
|
||
|
#include "resource.h"
|
||
|
#include <api/wnd/wndclass/wndholder.h>
|
||
|
#include <api/wndmgr/layout.h>
|
||
|
#include "wa2cfgitems.h"
|
||
|
#include "gen.h"
|
||
|
#include "../Agave/Language/api_language.h"
|
||
|
|
||
|
extern TList<HWND> forcedoffwnds;
|
||
|
|
||
|
#define BUCKETITEM_WNDTYPE L"buck"
|
||
|
#define WINAMP_OPTIONS_WINDOWSHADE_PL 40266
|
||
|
ReentryFilterObject wndMsgFilter;
|
||
|
|
||
|
int embedTable[] = {
|
||
|
IPC_GETWND_PE,
|
||
|
#ifdef MINIBROWSER_SUPPORT
|
||
|
IPC_GETWND_MB,
|
||
|
#endif
|
||
|
|
||
|
IPC_GETWND_VIDEO};
|
||
|
|
||
|
extern int switching_skin;
|
||
|
extern int going_fixedform;
|
||
|
extern int going_freeform;
|
||
|
extern HINSTANCE hInstance;
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
void WaOsWndHost::onBeforeReparent(int host)
|
||
|
{
|
||
|
#if defined(_WIN64)
|
||
|
embedWindowState *ws = (embedWindowState *)GetWindowLong(getHWND(), GWLP_USERDATA);
|
||
|
#else
|
||
|
embedWindowState* ws = (embedWindowState*)GetWindowLong(getHWND(), GWL_USERDATA);
|
||
|
#endif
|
||
|
// 0x49474541 is related to keeping windows shown on litestep desktops
|
||
|
if (ws == NULL || (int)ws == 0x49474541)
|
||
|
{
|
||
|
HWND w = getHWND();
|
||
|
if (w == wa2.getWnd(IPC_GETWND_VIDEO))
|
||
|
{
|
||
|
// this tells the video to not trigger its callback on windowposchanged, otherwise it will generate a new IPC_ONSHOW
|
||
|
SendMessageW(w, WM_USER + 0x2, 0, 1);
|
||
|
}
|
||
|
return ;
|
||
|
}
|
||
|
ws->extra_data[EMBED_STATE_EXTRA_REPARENTING] = 1; // tell winamp to ignore show/hide events
|
||
|
if (!host)
|
||
|
{
|
||
|
ShowWindow(getHWND(), SW_HIDE);
|
||
|
if (!transfer && ((switching_skin && !Wa2WndEmbed::hadRememberedWndVisible(getHWND())) || !switching_skin))
|
||
|
{
|
||
|
PostMessage(getHWND(), WM_USER + 101, 0, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
void WaOsWndHost::onAfterReparent(int host)
|
||
|
{
|
||
|
#if defined(_WIN64)
|
||
|
embedWindowState *ws = (embedWindowState *)GetWindowLong(getHWND(), GWLP_USERDATA);
|
||
|
#else
|
||
|
embedWindowState* ws = (embedWindowState*)GetWindowLong(getHWND(), GWL_USERDATA);
|
||
|
#endif
|
||
|
// 0x49474541 is related to keeping windows shown on litestep desktops
|
||
|
if (ws == NULL || (int)ws == 0x49474541)
|
||
|
{
|
||
|
HWND w = getHWND();
|
||
|
if (w == wa2.getWnd(IPC_GETWND_VIDEO))
|
||
|
{
|
||
|
// stop preventing handling of video windowposchanged
|
||
|
SendMessageW(w, WM_USER + 0x2, 0, 0);
|
||
|
}
|
||
|
return ;
|
||
|
}
|
||
|
ws->extra_data[EMBED_STATE_EXTRA_REPARENTING] = 0; // tell winamp NOT to ignore show/hide events anymore
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
int WaOsWndHost::onGetFocus()
|
||
|
{
|
||
|
XuiOSWndHost::onGetFocus();
|
||
|
ifc_window *z = findWindowByInterface(windowHolderGuid);
|
||
|
if (z)
|
||
|
{
|
||
|
WindowHolder *wh = static_cast<WindowHolder*>(z->getInterface(windowHolderGuid));
|
||
|
if (wh && wh->wndholder_wantAutoFocus())
|
||
|
{
|
||
|
HWND w = getHWND();
|
||
|
if (IsWindow(w)) SetFocus(w);
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
int WaOsWndHost::wantFocus()
|
||
|
{
|
||
|
ifc_window *w = findWindowByInterface(windowHolderGuid);
|
||
|
if (w)
|
||
|
{
|
||
|
WindowHolder *wh = static_cast<WindowHolder*>(w->getInterface(windowHolderGuid));
|
||
|
if (wh)
|
||
|
{
|
||
|
return wh->wndholder_wantAutoFocus();
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
int WaOsWndHost::onMouseWheelUp(int click, int lines)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
int WaOsWndHost::onMouseWheelDown(int click, int lines)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
void VideoLayoutMonitor::hook_onResize(int x, int y, int w, int h)
|
||
|
{
|
||
|
SendMessageW(wa2.getWnd(IPC_GETWND_VIDEO), WM_TIMER, 12345, 0);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
void VideoLayoutMonitor::hook_onMove()
|
||
|
{
|
||
|
SendMessageW(wa2.getWnd(IPC_GETWND_VIDEO), WM_TIMER, 12345, 0);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
Wa2WndEmbed::Wa2WndEmbed()
|
||
|
{
|
||
|
WASABI_API_SYSCB->syscb_registerCallback(static_cast<WndCallbackI*>(this));
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
Wa2WndEmbed::~Wa2WndEmbed()
|
||
|
{
|
||
|
WASABI_API_SYSCB->syscb_deregisterCallback(static_cast<WndCallbackI*>(this));
|
||
|
wa2wndstatus.deleteAll();
|
||
|
}
|
||
|
|
||
|
extern int we_have_ml;
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
int Wa2WndEmbed::testGuid(GUID g)
|
||
|
{
|
||
|
if (embedWndGuidMgr.testGuid(g)) return 1;
|
||
|
|
||
|
/* if (embedWndGuids.Data2 == g.Data2 && // embed wnd check :)
|
||
|
embedWndGuids.Data3 == g.Data3 &&
|
||
|
!memcmp(embedWndGuids.Data4,g.Data4,8)) return 1;*/
|
||
|
|
||
|
return (g == pleditWndGuid || g == videoWndGuid
|
||
|
#ifdef MINIBROWSER_SUPPORT
|
||
|
|| g == minibrowserWndGuid
|
||
|
#endif
|
||
|
|| (we_have_ml && g == library_guid)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
int make_sure_library_is_here_at_startup = 0;
|
||
|
extern int m_loading_at_startup;
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
ifc_window *Wa2WndEmbed::createWindowByGuid(GUID g, ifc_window *parent)
|
||
|
{
|
||
|
if (m_loading_at_startup)
|
||
|
if (g == library_guid)
|
||
|
make_sure_library_is_here_at_startup = 1;
|
||
|
WaOsWndHost *oldhost = NULL;
|
||
|
if (embedWndGuidMgr.testGuid(g) && !embedWndGuidMgr.getEmbedWindowState(g))
|
||
|
return NULL;
|
||
|
// first check if this window is already open in a host, and if so, remove it from the wndholder
|
||
|
foreach(wndhosts)
|
||
|
if (wndhosts.getfor()->g == g)
|
||
|
{
|
||
|
WaOsWndHost *host = wndhosts.getfor()->host;
|
||
|
oldhost = host;
|
||
|
host->setTransfering(1);
|
||
|
host->oswndhost_unhost();
|
||
|
Layout *l = static_cast<Layout *>(host->getDesktopParent());
|
||
|
if (l)
|
||
|
{
|
||
|
Container *c = l->getParentContainer();
|
||
|
if (c)
|
||
|
{
|
||
|
if (!WCSCASEEQLSAFE(c->getId(), L"main"))
|
||
|
{
|
||
|
c->close(); // deferred if needed
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
softclose:
|
||
|
ifc_window *wnd = host->findWindowByInterface(windowHolderGuid);
|
||
|
if (wnd != NULL)
|
||
|
{
|
||
|
WindowHolder *wh = static_cast<WindowHolder *>(wnd->getInterface(windowHolderGuid));
|
||
|
if (wh != NULL)
|
||
|
{
|
||
|
wh->onRemoveWindow(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else goto softclose;
|
||
|
}
|
||
|
}
|
||
|
endfor;
|
||
|
// now host the wnd in a new host
|
||
|
WaOsWndHost *host = new WaOsWndHost();
|
||
|
viewer_addViewItem(host);
|
||
|
EmbedEntry *ee = new EmbedEntry();
|
||
|
wndhosts.addItem(ee);
|
||
|
ee->g = g;
|
||
|
ee->host = host;
|
||
|
ee->monitor = NULL;
|
||
|
ee->dep = host->getDependencyPtr();
|
||
|
ee->cmds = NULL;
|
||
|
if (g == pleditWndGuid)
|
||
|
{
|
||
|
RECT r = {10, 20, 5, 38};
|
||
|
host->oswndhost_setRegionOffsets(&r);
|
||
|
host->oswndhost_host(wa2.getWnd(IPC_GETWND_PE));
|
||
|
ee->whichwnd = IPC_GETWND_PE;
|
||
|
host->setName((L"Playlist Editor")/*(WASABI_API_LNGSTRINGW(IDS_PLAYLIST_EDITOR)*/);
|
||
|
GuiObject *go = parent->getGuiObject();
|
||
|
PlaylistAppCmds *plEditAppCmds = new PlaylistAppCmds();
|
||
|
ee->cmds = plEditAppCmds;
|
||
|
go->guiobject_addAppCmds(plEditAppCmds);
|
||
|
plWnd = parent; //parent->getDesktopParent();
|
||
|
//ShowWindow(host->getHWND(), SW_NORMAL);
|
||
|
}
|
||
|
else if (g == videoWndGuid)
|
||
|
{
|
||
|
RECT r = {11, 20, 8, 38};
|
||
|
host->oswndhost_setRegionOffsets(&r);
|
||
|
#ifdef VIDDEBUG
|
||
|
DebugString("Video : Window service creates the host\n");
|
||
|
#endif
|
||
|
HWND vid = wa2.getWnd(IPC_GETWND_VIDEO);
|
||
|
host->oswndhost_host(vid);
|
||
|
((WaOsWndHost *)host)->setNoTransparency();
|
||
|
ee->whichwnd = IPC_GETWND_VIDEO;
|
||
|
host->setName(WASABI_API_LNGSTRINGW(IDS_VIDEO));
|
||
|
ifc_window *lw = parent->getDesktopParent();
|
||
|
if (lw)
|
||
|
{
|
||
|
GuiObject *o = lw->getGuiObject();
|
||
|
if (o)
|
||
|
{
|
||
|
ee->monitor = new VideoLayoutMonitor(o->guiobject_getScriptObject());
|
||
|
}
|
||
|
}
|
||
|
SetTimer(vid, 12345, 250, NULL);
|
||
|
GuiObject *go = parent->getGuiObject();
|
||
|
VideoAppCmds *videoAppCmds = new VideoAppCmds();
|
||
|
ee->cmds = videoAppCmds;
|
||
|
go->guiobject_addAppCmds(videoAppCmds);
|
||
|
vidWnd = parent; //parent->getDesktopParent();
|
||
|
//ShowWindow(host->getHWND(), SW_NORMAL);
|
||
|
#ifdef MINIBROWSER_SUPPORT
|
||
|
|
||
|
}
|
||
|
else if (g == minibrowserWndGuid)
|
||
|
{
|
||
|
RECT r = {10, 20, 5, 38};
|
||
|
host->oswndhost_setRegionOffsets(&r);
|
||
|
host->oswndhost_host(wa2.getWnd(IPC_GETWND_MB));
|
||
|
ee->whichwnd = IPC_GETWND_MB;
|
||
|
host->setName("Minibrowser");
|
||
|
GuiObject *go = parent->getGuiObject();
|
||
|
mbWnd = parent; //parent->getDesktopParent();
|
||
|
MinibrowserAppCmds *mbAppCmds = new MinibrowserAppCmds();
|
||
|
ee->cmds = mbAppCmds;
|
||
|
go->guiobject_addAppCmds(mbAppCmds);
|
||
|
//ShowWindow(host->getHWND(), SW_NORMAL);
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
else if (embedWndGuidMgr.testGuid(g)) /*(embedWndGuids.Data2 == g.Data2 &&
|
||
|
embedWndGuids.Data3 == g.Data3 &&
|
||
|
!memcmp(embedWndGuids.Data4,g.Data4,8))*/
|
||
|
{
|
||
|
embedWindowState *ws = embedWndGuidMgr.getEmbedWindowState(g);
|
||
|
ASSERT(ws != NULL);
|
||
|
|
||
|
if (0 == (WS_BORDER & GetWindowLongPtrW(ws->me, GWL_STYLE)))
|
||
|
{
|
||
|
RECT r = {11, 20, 8, 14};
|
||
|
host->oswndhost_setRegionOffsets(&r);
|
||
|
}
|
||
|
else
|
||
|
host->oswndhost_setRegionOffsets(NULL);
|
||
|
|
||
|
ws->extra_data[EMBED_STATE_EXTRA_HOSTCOUNT]++;
|
||
|
ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = (intptr_t)parent; //parent->getDesktopParent();
|
||
|
ee->whichwnd = (intptr_t)ws;
|
||
|
if (ws->flags & EMBED_FLAGS_NOTRANSPARENCY) host->setNoTransparency();
|
||
|
host->oswndhost_host(ws->me);
|
||
|
wchar_t buf[512] = {0};
|
||
|
GetWindowTextW(ws->me, buf, 512);
|
||
|
host->setName(buf);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wndhosts.removeItem(ee);
|
||
|
delete host;
|
||
|
delete ee;
|
||
|
return NULL;
|
||
|
}
|
||
|
wa2.setOnTop(cfg_options_alwaysontop.getValueAsInt());
|
||
|
return host;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
int Wa2WndEmbed::testType(const wchar_t *windowtype)
|
||
|
{
|
||
|
return !_wcsicmp(windowtype, BUCKETITEM_WNDTYPE) || !_wcsicmp(windowtype, L"plsc");
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
ifc_window *Wa2WndEmbed::createWindowOfType(const wchar_t *windowtype, ifc_window *parent, int n)
|
||
|
{
|
||
|
if (!_wcsicmp(windowtype, BUCKETITEM_WNDTYPE))
|
||
|
{
|
||
|
switch (n)
|
||
|
{
|
||
|
case 0:
|
||
|
{
|
||
|
PlBucketItem *bi = new PlBucketItem();
|
||
|
bi->setBitmaps(L"winamp.thinger.pledit", NULL, L"winamp.thinger.pledit.hilited", L"winamp.thinger.pledit.selected");
|
||
|
bucketitems.addItem(bi);
|
||
|
return bi;
|
||
|
}
|
||
|
case 1:
|
||
|
{
|
||
|
MlBucketItem *bi = new MlBucketItem();
|
||
|
bi->setBitmaps(L"winamp.thinger.library", NULL, L"winamp.thinger.library.hilited", L"winamp.thinger.library.selected");
|
||
|
bucketitems.addItem(bi);
|
||
|
return bi;
|
||
|
}
|
||
|
case 2:
|
||
|
{
|
||
|
VidBucketItem *bi = new VidBucketItem();
|
||
|
bi->setBitmaps(L"winamp.thinger.video", NULL, L"winamp.thinger.video.hilited", L"winamp.thinger.video.selected");
|
||
|
bucketitems.addItem(bi);
|
||
|
return bi;
|
||
|
}
|
||
|
case 3:
|
||
|
{
|
||
|
VisBucketItem *bi = new VisBucketItem();
|
||
|
bi->setBitmaps(L"winamp.thinger.vis", NULL, L"winamp.thinger.vis.hilited", L"winamp.thinger.vis.selected");
|
||
|
bucketitems.addItem(bi);
|
||
|
return bi;
|
||
|
}
|
||
|
// cases must be contiguous, enumerator stops at first NULL
|
||
|
#ifdef MINIBROWSER_SUPPORT
|
||
|
case 4:
|
||
|
{
|
||
|
MbBucketItem *bi = new MbBucketItem();
|
||
|
bi->setBitmaps(hInstance, IDB_MB_TAB_NORMAL, NULL, IDB_MB_TAB_HILITED, IDB_MB_TAB_SELECTED);
|
||
|
bucketitems.addItem(bi);
|
||
|
return bi;
|
||
|
}
|
||
|
#endif
|
||
|
// n is enumertor, not whichwnd
|
||
|
// we also need some way for the embeddedwnd to expose at least one bitmap (ideally 3) so we can make a nice bucketitem here (this code uses a pledit icon)
|
||
|
/* default:
|
||
|
if (n > 1024)
|
||
|
{
|
||
|
EmbedBucketItem *bi = new EmbedBucketItem();
|
||
|
bi->setBitmaps(hInstance, IDB_PLEDIT_TAB_NORMAL, NULL, IDB_PLEDIT_TAB_HILITED, IDB_PLEDIT_TAB_SELECTED);
|
||
|
bucketitems.addItem(bi);
|
||
|
return bi;
|
||
|
}
|
||
|
break;*/
|
||
|
}
|
||
|
}
|
||
|
else if (!_wcsicmp(windowtype, L"plsc"))
|
||
|
{
|
||
|
switch (n)
|
||
|
{
|
||
|
case 0:
|
||
|
pldirs.addItem(new PlDirObject);
|
||
|
return pldirs.getLast();
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
int Wa2WndEmbed::destroyWindow(ifc_window *w)
|
||
|
{
|
||
|
foreach(bucketitems)
|
||
|
Wa2BucketItem *i = bucketitems.getfor();
|
||
|
ifc_window *rw = i;
|
||
|
if (rw == w)
|
||
|
{
|
||
|
delete i;
|
||
|
return 1;
|
||
|
}
|
||
|
endfor;
|
||
|
foreach(wndhosts)
|
||
|
EmbedEntry *ee = wndhosts.getfor();
|
||
|
WaOsWndHost *x = ee->host;
|
||
|
if (WASABI_API_WND->rootwndIsValid(x))
|
||
|
{
|
||
|
ifc_window *rw = x;
|
||
|
if (rw == w)
|
||
|
{
|
||
|
ReentryFilter f(&wndMsgFilter, ee->whichwnd);
|
||
|
if (!f.mustLeave())
|
||
|
{
|
||
|
// this would hide the winamp window, which is probably not what we want to do (it should remain visible if it
|
||
|
// was visible, no?
|
||
|
|
||
|
// well, no, because we don't only run this in skin unloading, but also when a window gets destroyed (this is the wndcreation
|
||
|
// service being called to free what it created) -- this won't happen for mmd3/pledit because mmd3 has a static window for
|
||
|
// everything, which means that when you click close on it, it doesn't destroy it but hides it, so this code isn't called. but
|
||
|
// if you load another skin (ie: NonStep), and you close the pledit, it immediately reappears with the wa2 look since oswndhost_unhost
|
||
|
// reset the flags, region and parent to what they were before the window was embedded
|
||
|
|
||
|
// i think that what we need is to save which windows were visible (and their location) before switching to freeform
|
||
|
// and to restore them when we go back to wa2 mode. this will also be more consistant with the freeform behavior of
|
||
|
// remembering visible status and coordinates on a per skin basis (since otherwise freeform dockings get screwed)
|
||
|
// it also makes sense when we consider that we are going to need to remove all windowshade modes from the embedded
|
||
|
// windows when going freeform.
|
||
|
|
||
|
// see new functions: rememberVisibleWindows() and restoreVisibleWindows()
|
||
|
|
||
|
// in any case, we need to hide the window here, at least temporarily in the case of skin unloading
|
||
|
{
|
||
|
if (ee->whichwnd > 1024)
|
||
|
{
|
||
|
embedWindowState *ws = NULL;
|
||
|
//embedWindowState *ws = (embedWindowState *)ee->whichwnd;
|
||
|
HWND hHost, hContent;
|
||
|
hHost = (NULL != x) ? x->getHWND() : NULL;
|
||
|
hContent = (NULL != hHost) ? (HWND)SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)hHost, IPC_FF_GETCONTENTWND) : NULL;
|
||
|
|
||
|
if (NULL != hContent)
|
||
|
{
|
||
|
ws = (embedWindowState *)GetWindowLongPtrW(hContent, GWLP_USERDATA);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
embedWndGuidMgr.retireEmbedWindowState((embedWindowState *)ee->whichwnd);
|
||
|
}
|
||
|
|
||
|
if (NULL != ws &&
|
||
|
!(wa2.isValidEmbedWndState(ws) && --ws->hostcount != 0))
|
||
|
{
|
||
|
if (0 != (EMBED_FLAGS_FFCALLBACK & ws->flags) &&
|
||
|
NULL != ws->callback)
|
||
|
{
|
||
|
ws->callback(ws, FFC_DESTROYEMBED, (LPARAM)w);
|
||
|
}
|
||
|
|
||
|
x->oswndhost_unhost();
|
||
|
if (wa2.isValidEmbedWndState(ws))
|
||
|
ws->wasabi_window = NULL;
|
||
|
|
||
|
if (!x->isTransfering() && wa2.isValidEmbedWndState(ws))
|
||
|
{
|
||
|
if (IsWindow(x->getHWND()))
|
||
|
{
|
||
|
SendMessageW(ws->me, WM_USER + 101, 0, 0);
|
||
|
}
|
||
|
embedWndGuidMgr.retireEmbedWindowState(ws);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (ee->whichwnd == IPC_GETWND_VIDEO) KillTimer(wa2.getWnd(ee->whichwnd), 12345);
|
||
|
x->oswndhost_unhost();
|
||
|
if (!x->isTransfering())
|
||
|
wa2.setWindowVisible(ee->whichwnd, 0);
|
||
|
#ifdef VIDDEBUG
|
||
|
if (ee->whichwnd == IPC_GETWND_VIDEO) DebugString("Video : Window service asks WA2 to close the window\n");
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
wndhosts.removeItem(ee);
|
||
|
|
||
|
embedWindowState *ws = NULL;
|
||
|
HWND thiswnd = NULL;
|
||
|
if (ee->whichwnd > 1024)
|
||
|
{
|
||
|
if (IsWindow(x->getHWND()))
|
||
|
thiswnd = x->getHWND();
|
||
|
//ws=(embedWindowState *)ee->whichwnd;
|
||
|
//thiswnd=ws->me;
|
||
|
}
|
||
|
else thiswnd = wa2.getWnd(ee->whichwnd);
|
||
|
//moved to xuioswndhost
|
||
|
//SetWindowLong(thiswnd,GWL_STYLE,GetWindowLong(thiswnd,GWL_STYLE)&~(WS_CHILDWINDOW));
|
||
|
switch (ee->whichwnd)
|
||
|
{
|
||
|
case IPC_GETWND_PE: plWnd = NULL; break;
|
||
|
#ifdef MINIBROWSER_SUPPORT
|
||
|
case IPC_GETWND_MB: mbWnd = NULL; break;
|
||
|
#endif
|
||
|
case IPC_GETWND_VIDEO:
|
||
|
#ifdef VIDDEBUG
|
||
|
DebugString("Video : Window service destroys host\n");
|
||
|
#endif
|
||
|
vidWnd = NULL;
|
||
|
break;
|
||
|
default:
|
||
|
if (ee->whichwnd > 1024 && ws && thiswnd != NULL)
|
||
|
{
|
||
|
ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = 0;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
if (ee->cmds)
|
||
|
{
|
||
|
GuiObject *o = w->getParent()->getGuiObject();
|
||
|
o->guiobject_removeAppCmds(ee->cmds);
|
||
|
}
|
||
|
x->oswndhost_unhost(); // ignored if already done by reentryfiltered code
|
||
|
delete ee->monitor;
|
||
|
delete ee->cmds;
|
||
|
delete x;
|
||
|
|
||
|
|
||
|
if (ee->whichwnd > 1024 && ws)
|
||
|
{
|
||
|
if (forcedoffwnds.haveItem(ws->me))
|
||
|
{
|
||
|
RECT r;
|
||
|
GetWindowRect(ws->me, &r);
|
||
|
SetWindowPos(ws->me, NULL, r.left + 20000, r.top + 20000, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOZORDER);
|
||
|
forcedoffwnds.delItem(ws->me);
|
||
|
}
|
||
|
}
|
||
|
delete ee;
|
||
|
SetFocus(wa2.getMainWindow());
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
endfor;
|
||
|
foreach (pldirs)
|
||
|
PlDirObject *pldir = pldirs.getfor();
|
||
|
if (pldir == w)
|
||
|
{
|
||
|
delete pldir;
|
||
|
return 1;
|
||
|
}
|
||
|
endfor;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
int Wa2WndEmbed::viewer_onEvent(ifc_window *item, int event, intptr_t param, void *ptr, size_t ptrlen)
|
||
|
{
|
||
|
if (event == ifc_window::Event_SETVISIBLE)
|
||
|
{
|
||
|
/* if (!param) {
|
||
|
// the wnd may be going away, but then again, it might just be hidden to show an alternate layout of the same
|
||
|
// container, so before continuing, we need to check if it's actually going away. There is of course an exception
|
||
|
// in that if the window is hosted by a wndholder with autoclose="1", we should mirror the hiding state regardless
|
||
|
// of the container state
|
||
|
|
||
|
api_window *whr = item->getParent();
|
||
|
int except = 0;
|
||
|
if (whr) {
|
||
|
GuiObject *go = whr->getGuiObject();
|
||
|
if (go) {
|
||
|
const char *par = go->guiobject_getXmlParam("autoclose");
|
||
|
if (!par || (par && ATOI(par) == 1)) except = 1;
|
||
|
}
|
||
|
}
|
||
|
if (!except) {
|
||
|
api_window *lr = item->getDesktopParent();
|
||
|
if (lr) {
|
||
|
Layout *l = static_cast<Layout *>(lr->getInterface(layoutGuid));
|
||
|
if (l) {
|
||
|
Container *c = l->getParentContainer();
|
||
|
if (c) {
|
||
|
if (c->isVisible()) return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}*/
|
||
|
foreach(wndhosts)
|
||
|
EmbedEntry *ee = wndhosts.getfor();
|
||
|
XuiOSWndHost *x = ee->host;
|
||
|
ifc_window *rw = x;
|
||
|
if (rw == item)
|
||
|
{
|
||
|
{
|
||
|
ReentryFilter f(&wndMsgFilter, ee->whichwnd);
|
||
|
if (f.mustLeave()) continue;
|
||
|
}
|
||
|
if (ee->whichwnd > 1024)
|
||
|
{
|
||
|
embedWindowState *ws = (embedWindowState *)ee->whichwnd;
|
||
|
if (!param && wa2.isValidEmbedWndState(ws))
|
||
|
{
|
||
|
if (IsWindow(ws->me))
|
||
|
SendMessageW(ws->me, WM_USER + 101, 0, 0);
|
||
|
ifc_window *rwh = x->findWindowByInterface(windowHolderGuid);
|
||
|
if (rwh != NULL)
|
||
|
{
|
||
|
WindowHolder *wh = static_cast<WindowHolder *>(rwh->getInterface(windowHolderGuid));
|
||
|
if (wh != NULL)
|
||
|
{
|
||
|
wh->onRemoveWindow(1);
|
||
|
}
|
||
|
}
|
||
|
if (wa2.isValidEmbedWndState(ws)) ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = NULL;
|
||
|
}
|
||
|
else if (wa2.isValidEmbedWndState(ws))
|
||
|
{
|
||
|
ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = (intptr_t)item->getParent();
|
||
|
ShowWindow(ws->me, SW_NORMAL);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ReentryFilter f(&wndMsgFilter, ee->whichwnd);
|
||
|
#ifdef VIDDEBUG
|
||
|
if (ee->whichwnd == IPC_GETWND_VIDEO && param != wa2.isWindowVisible(ee->whichwnd)) DebugString("Video : Detected that the host is %s, syncing\n", param ? "shown" : "hidden");
|
||
|
#endif
|
||
|
wa2.setWindowVisible(ee->whichwnd, param);
|
||
|
}
|
||
|
}
|
||
|
endfor;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int Wa2WndEmbed::onShowWindow(Container *c, GUID guid, const wchar_t *groupid)
|
||
|
{
|
||
|
foreach(wndhosts)
|
||
|
EmbedEntry *ee = wndhosts.getfor();
|
||
|
if (ee->g == guid)
|
||
|
{
|
||
|
ReentryFilter f(&wndMsgFilter, ee->whichwnd);
|
||
|
if (f.mustLeave()) return 1;
|
||
|
if (guid == videoWndGuid) wa2.setWindowVisible(IPC_GETWND_VIDEO, 1);
|
||
|
#ifdef MINIBROWSER_SUPPORT
|
||
|
else if (guid == minibrowserWndGuid) wa2.setWindowVisible(IPC_GETWND_MB, 1);
|
||
|
#endif
|
||
|
else if (guid == pleditWndGuid) wa2.setWindowVisible(IPC_GETWND_PE, 1);
|
||
|
}
|
||
|
endfor;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int Wa2WndEmbed::onHideWindow(Container *c, GUID guid, const wchar_t *groupid)
|
||
|
{
|
||
|
/* if (guid == INVALID_GUID) return 1;
|
||
|
embedWindowState *ws = embedWndGuidMgr.getEmbedWindowState(guid);
|
||
|
if (ws != NULL && wa2.isValidEmbedWndState(ws)) {
|
||
|
if (IsWindow(ws->me))
|
||
|
SendMessageW(ws->me,WM_USER+101,0,0);
|
||
|
api_window *x = (api_window*)ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND];
|
||
|
if (x && WASABI_API_WND->rootwndIsValid(x)) {
|
||
|
api_window *rwh = x->findWindowByInterface(windowHolderGuid);
|
||
|
if (rwh != NULL) {
|
||
|
WindowHolder *wh = static_cast<WindowHolder *>(rwh->getInterface(windowHolderGuid));
|
||
|
if (wh != NULL) {
|
||
|
wh->onRemoveWindow(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = NULL;
|
||
|
}
|
||
|
*/
|
||
|
foreach(wndhosts)
|
||
|
EmbedEntry *ee = wndhosts.getfor();
|
||
|
if (ee->g == guid)
|
||
|
{
|
||
|
ReentryFilter f(&wndMsgFilter, ee->whichwnd);
|
||
|
if (f.mustLeave()) return 1;
|
||
|
if (ee->host->isTransfering()) return 1;
|
||
|
ifc_window *dp = ee->host->getDesktopParent();
|
||
|
if (dp)
|
||
|
{
|
||
|
Layout *l = static_cast<Layout*>(dp->getInterface(layoutGuid));
|
||
|
if (l)
|
||
|
{
|
||
|
if (l->getParentContainer() != c) return 1;
|
||
|
}
|
||
|
}
|
||
|
if (guid == videoWndGuid) wa2.setWindowVisible(IPC_GETWND_VIDEO, 0);
|
||
|
#ifdef MINIBROWSER_SUPPORT
|
||
|
else if (guid == minibrowserWndGuid) wa2.setWindowVisible(IPC_GETWND_MB, 0);
|
||
|
#endif
|
||
|
else if (guid == pleditWndGuid) wa2.setWindowVisible(IPC_GETWND_PE, 0);
|
||
|
}
|
||
|
endfor;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
extern wchar_t *INI_FILE;
|
||
|
|
||
|
int Wa2WndEmbed::embedRememberProc(embedWindowState *p, embedEnumStruct *parms)
|
||
|
{
|
||
|
WndStatus *ws = new WndStatus;
|
||
|
ws->wndcode = -1; // if you insert a wnd that is not in embedTable, put -1 as wndcode
|
||
|
ws->wnd = p->me;
|
||
|
ws->visible = IsWindowVisible(p->me);
|
||
|
GetWindowRect(p->me, &ws->position);
|
||
|
// ws->position=p->r;
|
||
|
wa2wndstatus.addItem(ws);
|
||
|
|
||
|
// only store the ml window position if not loading on startup
|
||
|
if(going_freeform && !m_loading_at_startup)
|
||
|
{
|
||
|
HWND mlwnd = wa2.getMediaLibrary();
|
||
|
if(GetWindow(p->me, GW_CHILD) == mlwnd)
|
||
|
{
|
||
|
WritePrivateProfileStringW(L"gen_ff", L"classicmlwidth", StringPrintfW(L"%d", ws->position.right - ws->position.left), INI_FILE);
|
||
|
WritePrivateProfileStringW(L"gen_ff", L"classicmlheight", StringPrintfW(L"%d", ws->position.bottom - ws->position.top), INI_FILE);
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
extern int m_loading_at_startup;
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
// todo: remember and restore windowshade modes
|
||
|
void Wa2WndEmbed::rememberVisibleWindows()
|
||
|
{
|
||
|
wa2wndstatus.deleteAll();
|
||
|
for (int i = 0;i < sizeof(embedTable) / sizeof(embedTable[0]);i++)
|
||
|
{
|
||
|
HWND w = wa2.getWnd(embedTable[i]);
|
||
|
WndStatus *ws = new WndStatus;
|
||
|
ws->wndcode = embedTable[i]; // if you insert a wnd that is not in embedTable, put -1 as wndcode
|
||
|
ws->wnd = w;
|
||
|
ws->visible = wa2.isWindowVisible(embedTable[i]);
|
||
|
GetWindowRect(w, &ws->position);
|
||
|
if (going_freeform)
|
||
|
{
|
||
|
if (embedTable[i] == IPC_GETWND_PE)
|
||
|
{
|
||
|
int peheight = ws->position.bottom - ws->position.top;
|
||
|
int pewidth = ws->position.right - ws->position.left;
|
||
|
if (!m_loading_at_startup)
|
||
|
{
|
||
|
WritePrivateProfileStringW(L"gen_ff", L"classicplwidth", StringPrintfW(L"%d", pewidth), INI_FILE);
|
||
|
WritePrivateProfileStringW(L"gen_ff", L"classicplheight", StringPrintfW(L"%d", peheight), INI_FILE);
|
||
|
}
|
||
|
int classicpews = wa2.isWindowShade(IPC_GETWND_PE);
|
||
|
if (!m_loading_at_startup || GetPrivateProfileIntW(L"gen_ff", L"classicplws", -1, INI_FILE) == -1)
|
||
|
WritePrivateProfileStringW(L"gen_ff", L"classicplws", classicpews ? L"1" : L"0", INI_FILE);
|
||
|
if (classicpews)
|
||
|
SendMessageW(wa2.getMainWindow(), WM_COMMAND, WINAMP_OPTIONS_WINDOWSHADE_PL, 0);
|
||
|
GetWindowRect(w, &ws->position);
|
||
|
}
|
||
|
}
|
||
|
wa2wndstatus.addItem(ws);
|
||
|
}
|
||
|
embedEnumStruct cs = { embedRememberProc, 0};
|
||
|
SendMessageW(wa2.getMainWindow(), WM_WA_IPC, (WPARAM)&cs, IPC_EMBED_ENUM);
|
||
|
}
|
||
|
|
||
|
int Wa2WndEmbed::hadRememberedWndVisible(HWND w)
|
||
|
{
|
||
|
int n = wa2wndstatus.getNumItems();
|
||
|
for (int i = 0;i < n;i++)
|
||
|
{
|
||
|
WndStatus *ws = wa2wndstatus.enumItem(i);
|
||
|
if (ws->wnd == w && ws->visible)
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void Wa2WndEmbed::restoreVisibleWindows()
|
||
|
{
|
||
|
int n = wa2wndstatus.getNumItems();
|
||
|
HWND mlwnd = wa2.getMediaLibrary();
|
||
|
for (int i = 0;i < n;i++)
|
||
|
{
|
||
|
WndStatus *ws = wa2wndstatus.enumItem(i);
|
||
|
if (going_fixedform && !m_loading_at_startup)
|
||
|
{
|
||
|
if (embedTable[i] == IPC_GETWND_PE)
|
||
|
{
|
||
|
int classicpews = GetPrivateProfileIntW(L"gen_ff", L"classicplws", 0, INI_FILE);
|
||
|
if (classicpews)
|
||
|
{
|
||
|
SendMessageW(wa2.getMainWindow(), WM_COMMAND, WINAMP_OPTIONS_WINDOWSHADE_PL, 0);
|
||
|
}
|
||
|
int classicwidth = GetPrivateProfileIntW(L"gen_ff", L"classicplwidth", 275, INI_FILE);
|
||
|
int classicheight = GetPrivateProfileIntW(L"gen_ff", L"classicplheight", 145, INI_FILE);
|
||
|
wa2.setPlEditWidthHeight(classicwidth, classicheight);
|
||
|
}
|
||
|
|
||
|
if(GetWindow(ws->wnd, GW_CHILD) == mlwnd)
|
||
|
{
|
||
|
// only restore the ml window size if we were able to read in saved values
|
||
|
int mlwidth = GetPrivateProfileIntW(L"gen_ff", L"classicmlwidth", -1, INI_FILE);
|
||
|
int mlheight = GetPrivateProfileIntW(L"gen_ff", L"classicmlheight", -1, INI_FILE);
|
||
|
if(mlwidth != -1 && mlheight != -1)
|
||
|
SetWindowPos(ws->wnd, 0, 0, 0, mlwidth, mlheight, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
|
||
|
}
|
||
|
}
|
||
|
// FG> as of oct19, this function only restores state for windows that WERE visible
|
||
|
// because there is no reason to hide one, since this function is designed to bring
|
||
|
// back those windows that were here in one mode, but aren't so anymore in another
|
||
|
if (ws->visible)
|
||
|
{
|
||
|
if (ws->wndcode != -1)
|
||
|
{
|
||
|
wa2.setWindowVisible(ws->wndcode, ws->visible);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ShowWindow(ws->wnd, ws->visible ? SW_SHOWNA : SW_HIDE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PtrList<WndStatus> Wa2WndEmbed::wa2wndstatus;
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
PlaylistAppCmds::PlaylistAppCmds()
|
||
|
: addCmd(L"Add", PL_ADD, AppCmds::SIDE_LEFT, 0),
|
||
|
remCmd(L"Rem", PL_REM, AppCmds::SIDE_LEFT, 0),
|
||
|
selCmd(L"Sel", PL_SEL, AppCmds::SIDE_LEFT, 0),
|
||
|
miscCmd(L"Misc", PL_MISC, AppCmds::SIDE_LEFT, 0),
|
||
|
listCmd(L"List", PL_LIST, AppCmds::SIDE_RIGHT, 0)
|
||
|
{
|
||
|
appcmds_addCmd(&addCmd);
|
||
|
appcmds_addCmd(&remCmd);
|
||
|
appcmds_addCmd(&selCmd);
|
||
|
appcmds_addCmd(&miscCmd);
|
||
|
appcmds_addCmd(&listCmd);
|
||
|
}
|
||
|
|
||
|
void PlaylistAppCmds::appcmds_onCommand(int id, const RECT *buttonRect, int which_button)
|
||
|
{
|
||
|
switch (id)
|
||
|
{
|
||
|
case PL_ADD:
|
||
|
wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_ADD, buttonRect->left, buttonRect->top, TPM_BOTTOMALIGN | TPM_LEFTALIGN);
|
||
|
break;
|
||
|
case PL_REM:
|
||
|
wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_REM, buttonRect->left, buttonRect->top, TPM_BOTTOMALIGN | TPM_LEFTALIGN);
|
||
|
break;
|
||
|
case PL_SEL:
|
||
|
wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_SEL, buttonRect->left, buttonRect->top, TPM_BOTTOMALIGN | TPM_LEFTALIGN);
|
||
|
break;
|
||
|
case PL_MISC:
|
||
|
wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_MISC, buttonRect->left, buttonRect->top, TPM_BOTTOMALIGN | TPM_LEFTALIGN);
|
||
|
break;
|
||
|
case PL_LIST:
|
||
|
wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_LIST, buttonRect->right, buttonRect->top, TPM_BOTTOMALIGN | TPM_RIGHTALIGN);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef MINIBROWSER_SUPPORT
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
MinibrowserAppCmds::MinibrowserAppCmds()
|
||
|
{
|
||
|
appcmds_addCmd(new CmdRec("Back", MB_BACK, AppCmds::SIDE_LEFT, 1));
|
||
|
appcmds_addCmd(new CmdRec("Forward", MB_FORWARD, AppCmds::SIDE_LEFT, 1));
|
||
|
appcmds_addCmd(new CmdRec("Stop", MB_STOP, AppCmds::SIDE_LEFT, 1));
|
||
|
appcmds_addCmd(new CmdRec("Reload", MB_RELOAD, AppCmds::SIDE_LEFT, 1));
|
||
|
appcmds_addCmd(new CmdRec("Misc", MB_MISC, AppCmds::SIDE_RIGHT, 1));
|
||
|
}
|
||
|
|
||
|
void MinibrowserAppCmds::appcmds_onCommand(int id, const RECT *buttonRect, int which_button)
|
||
|
{
|
||
|
switch (id)
|
||
|
{
|
||
|
case MB_BACK:
|
||
|
wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBCMD_BACK);
|
||
|
break;
|
||
|
case MB_FORWARD:
|
||
|
wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBCMD_FORWARD);
|
||
|
break;
|
||
|
case MB_STOP:
|
||
|
wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBCMD_STOP);
|
||
|
break;
|
||
|
case MB_RELOAD:
|
||
|
wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBCMD_RELOAD);
|
||
|
break;
|
||
|
case MB_MISC:
|
||
|
wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBPOPUP_MISC, buttonRect->right, buttonRect->top, TPM_BOTTOMALIGN | TPM_RIGHTALIGN);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
VideoAppCmds::VideoAppCmds()
|
||
|
{
|
||
|
appcmds_addCmd(new CmdRec(L"Fullscreen", VID_FULLSCREEN, AppCmds::SIDE_LEFT, 1));
|
||
|
appcmds_addCmd(new CmdRec(L"1x", VID_1X, AppCmds::SIDE_LEFT, 1));
|
||
|
appcmds_addCmd(new CmdRec(L"2x", VID_2X, AppCmds::SIDE_LEFT, 1));
|
||
|
appcmds_addCmd(new CmdRec(L"TV", VID_LIB, AppCmds::SIDE_LEFT, 1));
|
||
|
appcmds_addCmd(new CmdRec(L"Misc", VID_MISC, AppCmds::SIDE_RIGHT, 1));
|
||
|
}
|
||
|
|
||
|
void VideoAppCmds::appcmds_onCommand(int id, const RECT *buttonRect, int which_button)
|
||
|
{
|
||
|
switch (id)
|
||
|
{
|
||
|
case VID_FULLSCREEN:
|
||
|
wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_FULLSCREEN);
|
||
|
break;
|
||
|
case VID_1X:
|
||
|
wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_1X);
|
||
|
break;
|
||
|
case VID_2X:
|
||
|
wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_2X);
|
||
|
break;
|
||
|
case VID_LIB:
|
||
|
wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_LIB);
|
||
|
break;
|
||
|
case VID_MISC:
|
||
|
wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDPOPUP_MISC, buttonRect->right, buttonRect->top, TPM_BOTTOMALIGN | TPM_RIGHTALIGN);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|