#include <nuttx/config.h>
#include <cstdbool>
#include <cassert>
#include <mqueue.h>
#include "graphics/nxwidgets/cwidgetcontrol.hxx"
#include "graphics/nxwidgets/cnxwindow.hxx"
#include "graphics/nxwidgets/cnxtkwindow.hxx"
#include "graphics/nxwidgets/cnxtoolbar.hxx"
#include "graphics/twm4nx/twm4nx_config.hxx"
#include "graphics/twm4nx/ctwm4nx.hxx"
#include "graphics/twm4nx/cbackground.hxx"
#include "graphics/twm4nx/cwindow.hxx"
#include "graphics/twm4nx/cwindowfactory.hxx"
#include "graphics/twm4nx/ciconmgr.hxx"
#include "graphics/twm4nx/cmainmenu.hxx"
#include "graphics/twm4nx/twm4nx_events.hxx"
using namespace Twm4Nx;
* CWindowFactory Constructor
*
* @param twm4nx. Twm4Nx session
*/
CWindowFactory::CWindowFactory(FAR CTwm4Nx *twm4nx)
{
m_twm4nx = twm4nx;
m_windowHead = (FAR struct SWindow *)0;
m_winpos.x = 50;
m_winpos.y = 50;
}
* CWindowFactory Destructor
*/
CWindowFactory::~CWindowFactory(void)
{
}
* Add Icon Manager menu items to the Main menu. This is really part
* of the instance initialization, but cannot be executed until the
* Main Menu logic is ready.
*
* @return True on success
*/
bool CWindowFactory::addMenuItems(void)
{
FAR CMainMenu *cmain = m_twm4nx->getMainMenu();
if (!cmain->addApplication(&m_desktopItem))
{
twmerr("ERROR: Failed to add to the Main Menu\n");
return false;
}
return true;
}
* Create a new window and add it to the window list.
*
* The window is initialized with all application events disabled.
* The CWindows::configureEvents() method may be called as a second
* initialization step in order to enable application events.
*
* @param name The window name
* @param sbitmap The Icon bitmap
* @param iconMgr Pointer to icon manager instance
* @param flags Toolbar customizations see WFLAGS_NO_* definitions
* @return Reference to the allocated CWindow instance
*/
FAR CWindow *
CWindowFactory::createWindow(FAR NXWidgets::CNxString &name,
FAR const struct NXWidgets::SRlePaletteBitmap *sbitmap,
FAR CIconMgr *iconMgr, uint8_t flags)
{
twminfo("flags=%02x\n", flags);
FAR struct SWindow *win =
(FAR struct SWindow *)std::zalloc(sizeof(struct SWindow));
if (win == (FAR struct SWindow *)0)
{
twmerr("ERROR: Unable to allocate memory to manage window\n");
return (FAR CWindow *)0;
}
win->cwin = new CWindow(m_twm4nx);
if (win->cwin == (FAR CWindow *)0)
{
twmerr("ERROR: Failed to create CWindow\n");
std::free(win);
return (FAR CWindow *)0;
}
struct nxgl_size_s displaySize;
m_twm4nx->getDisplaySize(&displaySize);
struct nxgl_size_s winsize;
winsize.w = displaySize.w / 2;
winsize.h = displaySize.h / 2;
if ((m_winpos.x + winsize.w) > displaySize.w)
{
m_winpos.x = 50;
}
if ((m_winpos.x + winsize.w) > (displaySize.w - 16))
{
winsize.w = displaySize.w - m_winpos.x - 16;
}
if ((m_winpos.y + winsize.h) > displaySize.h)
{
m_winpos.y = 50;
}
if ((m_winpos.y + winsize.h) > (displaySize.h - 16))
{
winsize.h = displaySize.h - m_winpos.y - 16;
}
twminfo("Position window at (%d,%d), size (%d,%d)\n",
m_winpos.x, m_winpos.y, winsize.w, winsize.h);
if (!win->cwin->initialize(name, &m_winpos, &winsize, sbitmap,
iconMgr, flags))
{
twmerr("ERROR: Failed to initialize CWindow\n");
delete win->cwin;
std::free(win);
return (FAR CWindow *)0;
}
m_winpos.x += 30;
m_winpos.y += 30;
addWindowContainer(win);
if (!WFLAGS_IS_ICONMGR(flags) && !WFLAGS_IS_MENU(flags))
{
if (iconMgr == (FAR CIconMgr *)0)
{
iconMgr = m_twm4nx->getIconMgr();
}
iconMgr->addWindow(win->cwin);
}
return win->cwin;
}
* Handle the EVENT_WINDOW_DELETE event. The logic sequence is as
* follows:
*
* 1. The TERMINATE button in pressed in the Window Toolbar and
* CWindow::handleActionEvent() catches the button event on the
* event listener thread and generates the EVENT_WINDOW_TERMINATE
* 2. CWindows::event receives the widget event, EVENT_WINDOW_TERMINATE,
* on the Twm4NX manin threadand requests to halt the NX Server
* messages queues.
* 3. when server responds, the CwindowsEvent::handleBlockedEvent
* generates the EVENT_WINDOW_DELETE which is caught by
* CWindows::event() and which, in turn calls this function.
*
* @param cwin The CWindow instance. This will be deleted and its
* associated container will be freed.
*/
void CWindowFactory::destroyWindow(FAR CWindow *cwin)
{
FAR struct SWindow *win = findWindow(cwin);
if (win == (FAR struct SWindow *)0)
{
twmerr("ERROR: Failed to find CWindow container\n");
}
else
{
removeWindowContainer(win);
}
CIconMgr *iconmgr = cwin->getIconMgr();
DEBUGASSERT(iconmgr != (CIconMgr *)0);
iconmgr->removeWindow(cwin);
delete cwin;
free(win);
}
* Pick a position for a new Icon on the desktop. Tries to avoid
* collisions with other Icons and reserved areas on the background
*
* @param cwin The window being iconified.
* @param defPos The default position to use if there is no free
* region on the desktop.
* @param iconPos The selected Icon position. Might be the same as
* the default position.
* @return True is returned on success
*/
bool CWindowFactory::placeIcon(FAR CWindow *cwin,
FAR const struct nxgl_point_s &defPos,
FAR struct nxgl_point_s &iconPos)
{
bool success = false;
if (cwin->hasIcon())
{
struct nxgl_size_s displaySize;
m_twm4nx->getDisplaySize(&displaySize);
struct nxgl_size_s iconSize;
cwin->getIconWidgetSize(iconSize);
FAR CBackground *backgd = m_twm4nx->getBackground();
struct nxgl_point_s tmpPos;
tmpPos.x = CONFIG_TWM4NX_ICON_HSPACING;
nxgl_coord_t iconWidth;
for (; tmpPos.x < (displaySize.w - iconSize.w); tmpPos.x += iconWidth)
{
tmpPos.y = CONFIG_TWM4NX_ICON_VSPACING;
iconWidth = 0;
nxgl_coord_t iconHeight;
for (; tmpPos.y < (displaySize.h - iconSize.h); tmpPos.y += iconHeight)
{
struct nxgl_rect_s iconBounds;
iconBounds.pt1.x = tmpPos.x;
iconBounds.pt1.y = tmpPos.y;
iconBounds.pt2.x = tmpPos.x + iconSize.w - 1;
iconBounds.pt2.y = tmpPos.y + iconSize.h - 1;
struct nxgl_rect_s collision;
if (backgd->checkCollision(iconBounds, collision))
{
if (iconWidth < 20)
{
iconWidth = 20;
}
iconHeight = collision.pt2.y - tmpPos.y +
CONFIG_TWM4NX_ICON_VSPACING + 1;
}
else if (checkCollision(cwin, iconBounds, collision))
{
nxgl_coord_t tmpWidth = collision.pt2.x - tmpPos.x + 1;
if (tmpWidth > iconWidth)
{
iconWidth = tmpWidth;
}
iconHeight = collision.pt2.y - tmpPos.y +
CONFIG_TWM4NX_ICON_VSPACING + 1;
}
else
{
iconPos.x = tmpPos.x;
iconPos.y = tmpPos.y;
return true;
}
}
iconWidth += CONFIG_TWM4NX_ICON_HSPACING;
}
iconPos.x = defPos.x;
iconPos.y = defPos.y;
success = true;
}
return success;
}
* Redraw icons. The icons are drawn on the background window. When
* the background window receives a redraw request, it will call this
* method in order to redraw any effected icons drawn in the
* background.
*
* @param nxRect The region in the background to be redrawn
*/
void CWindowFactory::redrawIcons(FAR const nxgl_rect_s *nxRect)
{
twminfo("Redrawing...\n");
for (FAR struct SWindow *win = m_windowHead;
win != (FAR struct SWindow *)0;
win = win->flink)
{
FAR CWindow *cwin = win->cwin;
if (cwin->hasIcon() && cwin->isIconified())
{
struct nxgl_size_s iconSize;
cwin->getIconWidgetSize(iconSize);
struct nxgl_point_s iconPos;
cwin->getIconWidgetPosition(iconPos);
struct nxgl_rect_s iconBounds;
iconBounds.pt1.x = iconPos.x;
iconBounds.pt1.y = iconPos.y;
iconBounds.pt2.x = iconPos.x + iconSize.w - 1;
iconBounds.pt2.y = iconPos.y + iconSize.h - 1;
struct nxgl_rect_s intersection;
nxgl_rectintersect(&intersection, nxRect, &iconBounds);
if (!nxgl_nullrect(&intersection))
{
twminfo("Redraw icon\n");
cwin->redrawIcon();
}
}
}
}
* Check if the icon within iconBounds collides with any other icon on the
* desktop.
*
* @param cwin The window containing the Icon of interest
* @param iconBounds The candidate Icon bounding box
* @param collision The bounding box of the icon that the candidate collides
* with
* @return Returns true if there is a collision
*/
bool CWindowFactory::checkCollision(FAR CWindow *cwin,
FAR const struct nxgl_rect_s &iconBounds,
FAR struct nxgl_rect_s &collision)
{
for (FAR struct SWindow *win = m_windowHead;
win != (FAR struct SWindow *)0;
win = win->flink)
{
if (win->cwin != cwin && win->cwin->hasIcon() &&
win->cwin->isIconified())
{
struct nxgl_size_s iconSize;
win->cwin->getIconWidgetSize(iconSize);
struct nxgl_point_s iconPos;
win->cwin->getIconWidgetPosition(iconPos);
collision.pt1.x = iconPos.x;
collision.pt1.y = iconPos.y;
collision.pt2.x = iconPos.x + iconSize.w - 1;
collision.pt2.y = iconPos.y + iconSize.h - 1;
if (nxgl_intersecting(&iconBounds, &collision))
{
return true;
}
}
}
return false;
}
* Handle WINDOW events.
*
* @param eventmsg. The received NxWidget WINDOW event message.
* @return True if the message was properly handled. false is
* return on any failure.
*/
bool CWindowFactory::event(FAR struct SEventMsg *eventmsg)
{
twminfo("eventID: %d\n", eventmsg->eventID);
bool success = true;
switch (eventmsg->eventID)
{
case EVENT_TOOLBAR_XYINPUT:
{
FAR struct SXyInputEventMsg *nxmsg =
(FAR struct SXyInputEventMsg *)eventmsg;
FAR CWindow *cwin = (FAR CWindow *)nxmsg->obj;
DEBUGASSERT(cwin != (FAR CWindow *)0);
success = cwin->pollToolbarEvents();
}
break;
case EVENT_WINDOW_DESKTOP:
success = showDesktop();
break;
default:
{
FAR CWindow *cwin = (FAR CWindow *)eventmsg->obj;
DEBUGASSERT(cwin != (FAR CWindow *)0);
success = cwin->event(eventmsg);
}
break;
}
return success;
}
* Add a window container to the window list.
*
* @param win. The window container to be added to the list.
*/
void CWindowFactory::addWindowContainer(FAR struct SWindow *win)
{
win->blink = (FAR struct SWindow *)0;
win->flink = m_windowHead;
if (m_windowHead != (FAR struct SWindow *)0)
{
m_windowHead->blink = win;
}
m_windowHead = win;
}
* Remove a window container from the window list.
*
* @param win. The window container to be removed from the list.
*/
void CWindowFactory::removeWindowContainer(FAR struct SWindow *win)
{
FAR struct SWindow *prev = win->blink;
FAR struct SWindow *next = win->flink;
if (prev == (FAR struct SWindow *)0)
{
m_windowHead = next;
}
else
{
prev->flink = next;
}
if (next != (FAR struct SWindow *)0)
{
next->blink = prev;
}
win->flink = NULL;
win->blink = NULL;
}
* Find the window container that contains the specified window.
*
* @param cwin. The window whose container is needed.
* @return On success, the container of the specific window is returned;
* NULL is returned on failure.
*/
FAR struct SWindow *CWindowFactory::findWindow(FAR CWindow *cwin)
{
for (FAR struct SWindow *win = m_windowHead;
win != (FAR struct SWindow *)0;
win = win->flink)
{
if (win->cwin == cwin)
{
return win;
}
}
return (FAR struct SWindow *)0;
}
* This is the function that responds to the EVENT_WINDOW_DESKTOP. It
* iconifies all windows so that the desktop is visible.
*/
bool CWindowFactory::showDesktop(void)
{
for (FAR struct SWindow *win = m_windowHead;
win != (FAR struct SWindow *)0;
win = win->flink)
{
win->cwin->iconify();
}
return true;
}