#include <nuttx/config.h>
#include <cstdint>
#include <cstdbool>
#include <cerrno>
#include <sys/types.h>
#include <fcntl.h>
#include <mqueue.h>
#include <nuttx/nx/nxglib.h>
#include "graphics/nxwidgets/cgraphicsport.hxx"
#include "graphics/nxwidgets/cwidgeteventargs.hxx"
#include "graphics/nxwidgets/ibitmap.hxx"
#include "graphics/twm4nx/twm4nx_config.hxx"
#include "graphics/twm4nx/ctwm4nx.hxx"
#include "graphics/twm4nx/cfonts.hxx"
#include "graphics/twm4nx/cwindow.hxx"
#include "graphics/twm4nx/cwindowfactory.hxx"
#include "graphics/twm4nx/cbackground.hxx"
#include "graphics/twm4nx/ciconwidget.hxx"
#include "graphics/twm4nx/twm4nx_events.hxx"
#include "graphics/twm4nx/twm4nx_cursor.hxx"
#define ICONWIDGET_IMAGE_VSPACING 2
#define ICONWIDGET_TEXT_VSPACING 0
using namespace Twm4Nx;
* Constructor. Note that the group determines its width and height
* from the position and dimensions of its children.
*
* @param widgetControl The controlling widget for the display.
* @param x The x coordinate of the group.
* @param y The y coordinate of the group.
* @param style The style that the button should use. If this is not
* specified, the button will use the global default widget
* style.
*/
CIconWidget::CIconWidget(FAR CTwm4Nx *twm4nx,
FAR NXWidgets::CWidgetControl *widgetControl,
nxgl_coord_t x, nxgl_coord_t y,
FAR NXWidgets::CWidgetStyle *style)
: CNxWidget(widgetControl, x, y, 0, 0, WIDGET_BORDERLESS, style)
{
m_twm4nx = twm4nx;
m_parent = (FAR CWindow *)0;
m_widgetControl = widgetControl;
m_eventq = (mqd_t)-1;
m_dragging = false;
m_moved = false;
setBorderless(true);
setDraggable(true);
enable();
setRaisesEvents(true);
disableDrawing();
}
* Destructor.
*/
CIconWidget::~CIconWidget(void)
{
if (m_eventq != (mqd_t)-1)
{
mq_close(m_eventq);
m_eventq = (mqd_t)-1;
}
}
* Perform widget initialization that could fail and so it not appropriate
* for the constructor
*
* @param parent The parent window. Needed for de-iconification.
* @param ibitmap The bitmap image representing the icon
* @param title The icon title string
* @return True is returned if the widget is successfully initialized.
*/
bool CIconWidget::initialize(FAR CWindow *parent,
FAR NXWidgets::IBitmap *ibitmap,
FAR const NXWidgets::CNxString &title)
{
FAR const char *mqname = m_twm4nx->getEventQueueName();
m_eventq = mq_open(mqname, O_WRONLY | O_NONBLOCK);
if (m_eventq == (mqd_t)-1)
{
twmerr("ERROR: Failed open message queue '%s': %d\n",
mqname, errno);
return false;
}
struct nxgl_size_s iconImageSize;
iconImageSize.w = ibitmap->getWidth();
iconImageSize.h = ibitmap->getHeight();
FAR CFonts *fonts = m_twm4nx->getFonts();
FAR NXWidgets::CNxFont *iconFont = fonts->getIconFont();
struct nxgl_size_s titleSize;
titleSize.w = iconFont->getStringWidth(title);
titleSize.h = iconFont->getHeight();
FAR NXWidgets::CNxString topString;
struct nxgl_size_s iconTopLabelSize;
FAR NXWidgets::CNxString bottomString;
struct nxgl_size_s iconBottomLabelSize;
iconBottomLabelSize.w = 0;
iconBottomLabelSize.h = 0;
int sIndex = title.indexOf(' ');
if (titleSize.w <= iconImageSize.w || sIndex < 0)
{
topString.setText(title);
iconTopLabelSize.w = titleSize.w;
iconTopLabelSize.h = titleSize.h;
}
else
{
nxgl_coord_t halfWidth = titleSize.w / 2;
nxgl_coord_t sWidth =
iconFont->getStringWidth(title.subString(0, sIndex));
nxgl_coord_t error = halfWidth - sWidth;
if (error < 0)
{
error = -error;
}
int index;
while ((index = title.indexOf(' ', sIndex + 1)) > 0)
{
nxgl_coord_t width =
iconFont->getStringWidth(title.subString(0, index));
nxgl_coord_t tmperr = halfWidth - width;
if (tmperr < 0)
{
tmperr = -tmperr;
}
if (tmperr >= error)
{
break;
}
error = tmperr;
sIndex = index;
}
topString.setText(title.subString(0, sIndex));
iconTopLabelSize.w = iconFont->getStringWidth(topString);
iconTopLabelSize.h = iconFont->getHeight();
bottomString.setText(title.subString(sIndex + 1));
iconBottomLabelSize.w = iconFont->getStringWidth(bottomString);
iconBottomLabelSize.h = iconFont->getHeight();
}
nxgl_coord_t maxLabelWidth = ngl_max(iconTopLabelSize.w,
iconBottomLabelSize.w);
struct nxgl_size_s iconWidgetSize;
iconWidgetSize.w = ngl_max(iconImageSize.w, maxLabelWidth);
iconWidgetSize.h = iconImageSize.h + iconTopLabelSize.h +
ICONWIDGET_IMAGE_VSPACING;
if (iconBottomLabelSize.h > 0)
{
iconWidgetSize.h += iconBottomLabelSize.h + ICONWIDGET_TEXT_VSPACING;
}
resize(iconWidgetSize.w, iconWidgetSize.h);
struct nxgl_point_s iconImagePos;
iconImagePos.x = 0;
iconImagePos.y = 0;
if (iconImageSize.w < (maxLabelWidth + 1))
{
iconImagePos.x = (maxLabelWidth - iconImageSize.w) / 2;
}
FAR CIconImage *image =
new CIconImage(m_widgetControl, iconImagePos.x,
iconImagePos.y, iconImageSize.w, iconImageSize.h,
ibitmap, &m_style);
if (image == (FAR CIconImage *)0)
{
twmerr("ERROR: Failed to create image\n");
return false;
}
image->setBorderless(true);
image->enable();
image->disableDrawing();
image->setRaisesEvents(true);
image->setDraggable(true);
image->addWidgetEventHandler(this);
addWidget(image);
struct nxgl_point_s iconTopLabelPos;
iconTopLabelPos.x = 0;
iconTopLabelPos.y = iconImageSize.h + ICONWIDGET_IMAGE_VSPACING;
if (iconWidgetSize.w > (iconTopLabelSize.w + 1))
{
iconTopLabelPos.x = (iconWidgetSize.w - iconTopLabelSize.w) / 2;
}
FAR CIconLabel *topLabel =
new CIconLabel(m_widgetControl, iconTopLabelPos.x,
iconTopLabelPos.y, iconTopLabelSize.w,
iconTopLabelSize.h, topString, &m_style);
if (topLabel == (FAR CIconLabel *)0)
{
twmerr("ERROR: Failed to create icon topLabel\n");
delete image;
return false;
}
topLabel->setFont(iconFont);
topLabel->setBorderless(true);
topLabel->enable();
topLabel->disableDrawing();
topLabel->setRaisesEvents(true);
topLabel->setDraggable(true);
topLabel->addWidgetEventHandler(this);
addWidget(topLabel);
if (iconBottomLabelSize.h > 0)
{
struct nxgl_point_s iconBottomLabelPos;
iconBottomLabelPos.x = 0;
iconBottomLabelPos.y = iconImageSize.h + iconTopLabelSize.h +
ICONWIDGET_IMAGE_VSPACING +
ICONWIDGET_TEXT_VSPACING;
if (iconWidgetSize.w > (iconBottomLabelSize.w + 1))
{
iconBottomLabelPos.x = (iconWidgetSize.w - iconBottomLabelSize.w) / 2;
}
FAR CIconLabel *bottomLabel =
new CIconLabel(m_widgetControl, iconBottomLabelPos.x,
iconBottomLabelPos.y, iconBottomLabelSize.w,
iconBottomLabelSize.h, bottomString,&m_style);
if (bottomLabel == (FAR CIconLabel *)0)
{
twmerr("ERROR: Failed to create icon bottomLabel\n");
delete topLabel;
delete image;
return false;
}
bottomLabel->setFont(iconFont);
bottomLabel->setBorderless(true);
bottomLabel->enable();
bottomLabel->disableDrawing();
bottomLabel->setRaisesEvents(true);
bottomLabel->setDraggable(true);
bottomLabel->addWidgetEventHandler(this);
addWidget(bottomLabel);
}
m_parent = parent;
return true;
}
* Insert the dimensions that this widget wants to have into the rect
* passed in as a parameter. All coordinates are relative to the
* widget's parent. Value is based on the length of the largest string
* in the set of options.
*
* @param rect Reference to a rect to populate with data.
*/
void CIconWidget::getPreferredDimensions(NXWidgets::CRect &rect) const
{
struct nxgl_size_s widgetSize;
getSize(widgetSize);
struct nxgl_point_s widgetPos;
getPos(widgetPos);
rect.setX(widgetPos.x);
rect.setY(widgetPos.y);
rect.setWidth(widgetSize.w);
rect.setHeight(widgetSize.h);
}
* Handle ICONWIDGET events.
*
* @param eventmsg. The received NxWidget ICON event message.
* @return True if the message was properly handled. false is
* return on any failure.
*/
bool CIconWidget::event(FAR struct SEventMsg *eventmsg)
{
bool success = true;
switch (eventmsg->eventID)
{
case EVENT_ICONWIDGET_GRAB:
success = iconGrab(eventmsg);
break;
case EVENT_ICONWIDGET_DRAG:
success = iconDrag(eventmsg);
break;
case EVENT_ICONWIDGET_UNGRAB:
success = iconUngrab(eventmsg);
break;
default:
success = false;
break;
}
return success;
}
* After the widget has been grabbed, it may be dragged then dropped,
* or it may be simply "un-grabbed". Both cases are handled here.
*
* NOTE: Unlike the other event handlers, this does NOT override any
* virtual event handling methods. It just combines some common event-
* handling logic.
*
* @param e The event data.
*/
void CIconWidget::handleUngrabEvent(const NXWidgets::CWidgetEventArgs &e)
{
struct SEventMsg msg;
msg.eventID = EVENT_ICONWIDGET_UNGRAB;
msg.obj = (FAR void *)this;
msg.pos.x = e.getX();
msg.pos.y = e.getY();
msg.context = EVENT_CONTEXT_ICONWIDGET;
msg.handler = (FAR void *)0;
int ret = mq_send(m_eventq, (FAR const char *)&msg,
sizeof(struct SEventMsg), 100);
if (ret < 0)
{
twmerr("ERROR: mq_send failed: %d\n", errno);
}
}
* Override the mouse button drag event.
*
* @param e The event data.
*/
void CIconWidget::handleDragEvent(const NXWidgets::CWidgetEventArgs &e)
{
if (m_dragging)
{
struct SEventMsg msg;
msg.eventID = EVENT_ICONWIDGET_DRAG;
msg.obj = (FAR void *)this;
msg.pos.x = e.getX();
msg.pos.y = e.getY();
msg.context = EVENT_CONTEXT_ICONWIDGET;
msg.handler = (FAR void *)0;
int ret = mq_send(m_eventq, (FAR const char *)&msg,
sizeof(struct SEventMsg), 100);
if (ret < 0)
{
twmerr("ERROR: mq_send failed: %d\n", errno);
}
}
}
* Override a drop event, triggered when the widget has been dragged-
* and-dropped.
*
* @param e The event data.
*/
void CIconWidget::handleDropEvent(const NXWidgets::CWidgetEventArgs &e)
{
if (m_dragging)
{
handleUngrabEvent(e);
}
}
* Handle a mouse click event.
*
* @param e The event data.
*/
void CIconWidget::handleClickEvent(const NXWidgets::CWidgetEventArgs &e)
{
if (!m_dragging)
{
struct SEventMsg msg;
msg.eventID = EVENT_ICONWIDGET_GRAB;
msg.obj = (FAR void *)this;
msg.pos.x = e.getX();
msg.pos.y = e.getY();
msg.context = EVENT_CONTEXT_ICONWIDGET;
msg.handler = (FAR void *)0;
int ret = mq_send(m_eventq, (FAR const char *)&msg,
sizeof(struct SEventMsg), 100);
if (ret < 0)
{
twmerr("ERROR: mq_send failed: %d\n", errno);
}
}
}
* Override the virtual CWidgetEventHandler::handleReleaseEvent. This
* event will fire when the widget is released. isClicked() will
* return false for the widget.
*
* @param e The event data.
*/
void CIconWidget::handleReleaseEvent(const NXWidgets::CWidgetEventArgs &e)
{
if (m_dragging)
{
handleUngrabEvent(e);
}
}
* Handle a mouse button release event that occurred outside the bounds of
* the source widget.
*
* @param e The event data.
*/
void CIconWidget::handleReleaseOutsideEvent(const NXWidgets::CWidgetEventArgs &e)
{
if (m_dragging)
{
handleUngrabEvent(e);
}
}
* Handle the EVENT_ICONWIDGET_GRAB event. That corresponds to a left
* mouse click on the icon widtet
*
* @param eventmsg. The received NxWidget event message.
* @return True if the message was properly handled. false is
* return on any failure.
*/
bool CIconWidget::iconGrab(FAR struct SEventMsg *eventmsg)
{
m_dragging = true;
m_moved = false;
m_collision = false;
getPos(m_dragPos);
m_dragOffset.x = m_dragPos.x - eventmsg->pos.x;
m_dragOffset.y = m_dragPos.y - eventmsg->pos.y;
#ifdef CONFIG_TWM4NX_MOUSE
m_twm4nx->setCursorImage(&CONFIG_TWM4NX_GBCURSOR_IMAGE);
m_dragCSize.w = CONFIG_TWM4NX_GBCURSOR_IMAGE.size.w;
m_dragCSize.h = CONFIG_TWM4NX_GBCURSOR_IMAGE.size.h;
#else
m_dragCSize.w = 16;
m_dragCSize.h = 16;
#endif
return true;
}
* Handle the EVENT_ICONWIDGET_DRAG event. That corresponds to a mouse
* movement when the icon is in a grabbed state.
*
* @param eventmsg. The received NxWidget event message.
* @return True if the message was properly handled. false is
* return on any failure.
*/
bool CIconWidget::iconDrag(FAR struct SEventMsg *eventmsg)
{
if (m_dragging)
{
struct nxgl_point_s newpos;
newpos.x = eventmsg->pos.x + m_dragOffset.x;
newpos.y = eventmsg->pos.y + m_dragOffset.y;
struct nxgl_size_s displaySize;
m_twm4nx->getDisplaySize(&displaySize);
if (newpos.x < 0)
{
newpos.x = 0;
}
else if (newpos.x + m_dragCSize.w > displaySize.w)
{
newpos.x = displaySize.w - m_dragCSize.w;
}
if (newpos.y < 0)
{
newpos.y = 0;
}
else if (newpos.y + m_dragCSize.h > displaySize.h)
{
newpos.y = displaySize.h - m_dragCSize.h;
}
struct nxgl_point_s oldpos;
getPos(oldpos);
if (oldpos.x != newpos.x || oldpos.y != newpos.y)
{
disableDrawing();
if (!moveTo(newpos.x, newpos.y))
{
twmerr("ERROR: moveTo() failed\n");
return false;
}
struct nxgl_size_s widgetSize;
getSize(widgetSize);
struct nxgl_rect_s bounds;
bounds.pt1.x = oldpos.x;
bounds.pt1.y = oldpos.y;
bounds.pt2.x = oldpos.x + widgetSize.w - 1;
bounds.pt2.y = oldpos.y + widgetSize.h - 1;
FAR CBackground *backgd = m_twm4nx->getBackground();
if (!backgd->redrawBackgroundWindow(&bounds, false))
{
twmerr("ERROR: redrawBackgroundWindow() failed\n");
return false;
}
enableDrawing();
redraw();
bounds.pt1.x = newpos.x;
bounds.pt1.y = newpos.y;
bounds.pt2.x = newpos.x + widgetSize.w - 1;
bounds.pt2.y = newpos.y + widgetSize.h - 1;
struct nxgl_rect_s collision;
if (backgd->checkCollision(bounds, collision))
{
m_collision = true;
}
else
{
FAR CWindowFactory *factory = m_twm4nx->getWindowFactory();
if (factory->checkCollision(m_parent, bounds, collision))
{
m_collision = true;
}
else
{
m_dragPos.x = newpos.x;
m_dragPos.y = newpos.y;
m_collision = false;
}
}
m_moved = true;
}
return true;
}
return false;
}
* Handle the EVENT_ICONWIDGET_UNGRAB event. The corresponds to a mouse
* left button release while in the grabbed state.
*
* @param eventmsg. The received NxWidget event message.
* @return True if the message was properly handled. false is
* return on any failure.
*/
bool CIconWidget::iconUngrab(FAR struct SEventMsg *eventmsg)
{
if (m_moved && !iconDrag(eventmsg))
{
return false;
}
#ifdef CONFIG_TWM4NX_MOUSE
m_twm4nx->setCursorImage(&CONFIG_TWM4NX_CURSOR_IMAGE);
#endif
bool success = true;
if (m_dragging && !m_moved)
{
m_parent->deIconify();
}
else if (m_dragging && m_collision)
{
struct nxgl_point_s oldpos;
getPos(oldpos);
disableDrawing();
if (!moveTo(m_dragPos.x, m_dragPos.y))
{
twmerr("ERROR: moveTo() failed\n");
success = false;
}
else
{
struct nxgl_size_s widgetSize;
getSize(widgetSize);
struct nxgl_rect_s bounds;
bounds.pt1.x = oldpos.x;
bounds.pt1.y = oldpos.y;
bounds.pt2.x = oldpos.x + widgetSize.w - 1;
bounds.pt2.y = oldpos.y + widgetSize.h - 1;
FAR CBackground *backgd = m_twm4nx->getBackground();
if (!backgd->redrawBackgroundWindow(&bounds, false))
{
twmerr("ERROR: redrawBackgroundWindow() failed\n");
return false;
}
enableDrawing();
redraw();
}
}
m_dragging = false;
m_collision = false;
m_moved = false;
return success;
}