iup-stack/iup/srcglcontrols/iup_glcanvasbox.c

558 lines
14 KiB
C
Raw Normal View History

2023-02-20 16:44:45 +00:00
/** \file
* \brief GLCanvasBox Control.
*
* See Copyright Notice in "iup.h"
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include "iup.h"
#include "iupcbs.h"
#include "iupgl.h"
#include "iup_object.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_register.h"
#include "iup_glcontrols.h"
#include "iup_glfont.h"
#include "iup_glsubcanvas.h"
#include "iup_varg.h"
static Ihandle* iGLCanvasBoxPickChild(Ihandle* ih, int x, int y, int top)
{
Ihandle* child = ih->firstchild;
if (child)
{
/* ih is a container then must check first for the client area */
int client_x = 0, client_y = 0, client_w = 0, client_h = 0;
IupGetIntInt(ih, "CLIENTSIZE", &client_w, &client_h);
IupGetIntInt(ih, "CLIP_MIN", &client_x, &client_y);
if (!top)
{
client_x += ih->x;
client_y += ih->y;
}
if (x >= client_x && x < client_x + client_w &&
y >= client_y && y < client_y + client_h)
{
Ihandle** child_array = (Ihandle**)malloc(sizeof(Ihandle*) * IupGetChildCount(ih));
int i=0;
while (child)
{
child_array[i] = child;
child = child->brother;
i++;
}
while (i > 0)
{
child = child_array[i - 1]; /* start with the last child */
if (iupAttribGetInt(child, "VISIBLE") &&
x >= child->x && x < child->x + child->currentwidth &&
y >= child->y && y < child->y + child->currentheight)
{
free(child_array);
ih = iGLCanvasBoxPickChild(child, x, y, 0);
if (ih)
return ih;
else
return child;
}
i--;
}
free(child_array);
}
}
return NULL;
}
static void iGLCanvasBoxCallGLChildAction(Ihandle* ih, Ihandle* gl_parent)
{
Ihandle* child = ih->firstchild;
while (child)
{
IFn cb = (IFn)IupGetCallback(child, "GL_ACTION");
if (cb && iupAttribGetInt(child, "VISIBLE"))
{
if (iupGLSubCanvasSetTransform(child, gl_parent))
cb(child);
}
iGLCanvasBoxCallGLChildAction(child, gl_parent);
child = child->brother;
}
}
static int iGLCanvasBoxSwapBuffers_CB(Ihandle* ih)
{
/* called before the actual SwapBuffers */
iupGLSubCanvasSaveState(ih);
/* redraw all GL children */
iGLCanvasBoxCallGLChildAction(ih, ih);
iupGLSubCanvasRestoreState(ih);
return IUP_DEFAULT;
}
static int iGLCanvasBoxACTION(Ihandle* ih, float posx, float posy)
{
IFnff cb;
IupGLMakeCurrent(ih);
cb = (IFnff)IupGetCallback(ih, "APP_ACTION");
if (cb)
cb(ih, posx, posy);
/* if double buffer is disabled must manually call our SwapBuffers callback, assuming IupGLSwapBuffers is not called inside APP_ACTION */
if (!cb || !iupStrEqualNoCase(iupAttribGetStr(ih, "BUFFER"), "DOUBLE"))
iGLCanvasBoxSwapBuffers_CB(ih);
return IUP_DEFAULT;
}
static int iGLCanvasBoxBUTTON_CB(Ihandle* ih, int button, int pressed, int x, int y, char* status)
{
IFniiiis cb;
Ihandle* child;
child = iGLCanvasBoxPickChild(ih, x, y, 1);
if (child || !pressed)
iupAttribSet(ih, "_IUP_GLBOX_SELFBUTTON", NULL);
else
iupAttribSet(ih, "_IUP_GLBOX_SELFBUTTON", "1");
if (child && iupAttribGetInt(child, "ACTIVE"))
{
int ret = IUP_DEFAULT;
if (pressed)
iupAttribSet(ih, "_IUP_GLBOX_LASTBUTTON", (char*)child);
else
iupAttribSet(ih, "_IUP_GLBOX_LASTBUTTON", NULL);
if (button == IUP_BUTTON1)
{
if (pressed)
iupAttribSet(child, "PRESSED", "1");
else
iupAttribSet(child, "PRESSED", NULL);
}
cb = (IFniiiis)IupGetCallback(child, "GL_BUTTON_CB");
if (cb)
ret = cb(child, button, pressed, x - child->x, y - child->y, status);
if (ret != IUP_CONTINUE)
return IUP_DEFAULT;
}
else
iupAttribSet(ih, "_IUP_GLBOX_LASTBUTTON", NULL);
cb = (IFniiiis)IupGetCallback(ih, "APP_BUTTON_CB");
if (cb)
return cb(ih, button, pressed, x, y, status);
return IUP_DEFAULT;
}
static void iGLCanvasBoxEnterChild(Ihandle* ih, Ihandle* child, int x, int y)
{
Ihandle* last_child = (Ihandle*)iupAttribGet(ih, "_IUP_GLBOX_LAST_ENTER");
if (last_child && last_child != child)
{
if (iupAttribGetInt(last_child, "ACTIVE"))
{
IFn cb;
char* value;
value = iupAttribGet(ih, "_IUPGLBOX_TIP_SET");
if (value)
{
value = iupAttribGet(ih, "_IUPGLBOX_TIP");
IupSetStrAttribute(ih, "TIP", value); /* reset attribute if it was set */
iupAttribSet(ih, "_IUPGLBOX_TIP", NULL);
iupAttribSet(ih, "_IUPGLBOX_TIP_SET", NULL);
}
iupAttribSet(last_child, "HIGHLIGHT", NULL);
iupAttribSet(last_child, "PRESSED", NULL);
cb = (IFn)IupGetCallback(last_child, "GL_LEAVEWINDOW_CB");
if (cb)
cb(last_child);
value = iupAttribGet(ih, "_IUPGLBOX_CURSOR");
if (value)
{
IupSetStrAttribute(ih, "CURSOR", value); /* reset attribute if it was set */
iupAttribSet(ih, "_IUPGLBOX_CURSOR", NULL);
}
}
iupAttribSet(ih, "_IUP_GLBOX_LAST_ENTER", NULL);
}
if (child && child != last_child)
{
if (iupAttribGetInt(child, "ACTIVE"))
{
IFnii cb;
char* value;
value = iupAttribGet(child, "TIP");
if (value)
{
iupAttribSet(ih, "_IUPGLBOX_TIP_SET", "1"); /* TIP can be NULL */
iupAttribSetStr(ih, "_IUPGLBOX_TIP", IupGetAttribute(ih, "TIP"));
IupSetStrAttribute(ih, "TIP", value);
IupSetAttribute(ih, "TIPVISIBLE", "Yes");
}
iupAttribSet(child, "HIGHLIGHT", "1");
cb = (IFnii)IupGetCallback(child, "GL_ENTERWINDOW_CB");
if (cb)
cb(child, x, y);
value = iupAttribGet(child, "CURSOR");
if (value)
{
iupAttribSetStr(ih, "_IUPGLBOX_CURSOR", IupGetAttribute(ih, "CURSOR"));
IupSetStrAttribute(ih, "CURSOR", value);
}
}
iupAttribSet(ih, "_IUP_GLBOX_LAST_ENTER", (char*)child);
}
}
static int iGLCanvasBoxMOTION_CB(Ihandle* ih, int x, int y, char *status)
{
IFniis cb;
/* only handle child if not pressed at self */
if (!iupAttribGet(ih, "_IUP_GLBOX_SELFBUTTON"))
{
Ihandle* child = (Ihandle*)iupAttribGet(ih, "_IUP_GLBOX_LASTBUTTON");
if (!child)
child = iGLCanvasBoxPickChild(ih, x, y, 1);
if (child)
iGLCanvasBoxEnterChild(ih, child, x - child->x, y - child->y);
else
iGLCanvasBoxEnterChild(ih, NULL, 0, 0);
if (child && iupAttribGetInt(child, "ACTIVE"))
{
int ret = IUP_DEFAULT;
cb = (IFniis)IupGetCallback(child, "GL_MOTION_CB");
if (cb)
ret = cb(child, x - child->x, y - child->y, status);
if (ret != IUP_CONTINUE)
return IUP_DEFAULT;
}
}
cb = (IFniis)IupGetCallback(ih, "APP_MOTION_CB");
if (cb)
return cb(ih, x, y, status);
return IUP_DEFAULT;
}
static int iGLCanvasBoxWHEEL_CB(Ihandle* ih, float delta, int x, int y, char *status)
{
IFnfiis cb;
Ihandle* child;
child = iGLCanvasBoxPickChild(ih, x, y, 1);
if (child)
{
int ret = IUP_DEFAULT;
cb = (IFnfiis)IupGetCallback(child, "GL_WHEEL_CB");
if (cb && iupAttribGetInt(child, "ACTIVE"))
ret = cb(child, delta, x - child->x, y - child->y, status);
if (ret != IUP_CONTINUE)
return IUP_DEFAULT;
}
cb = (IFnfiis)IupGetCallback(ih, "APP_WHEEL_CB");
if (cb)
return cb(ih, delta, x, y, status);
return IUP_DEFAULT;
}
static int iGLCanvasBoxLEAVEWINDOW_CB(Ihandle* ih)
{
IFn app_cb;
iGLCanvasBoxEnterChild(ih, NULL, 0, 0);
app_cb = (IFn)IupGetCallback(ih, "APP_LEAVEWINDOW_CB");
if (app_cb)
app_cb(ih);
return IUP_DEFAULT;
}
static int iGLCanvasBoxSetRedrawAttrib(Ihandle* ih, const char* value)
{
IupRedraw(ih, 0);
(void)value;
return 0;
}
static void iGLCanvasBoxComputeNaturalSizeMethod(Ihandle* ih, int *w, int *h, int *children_expand)
{
Ihandle* child;
for (child = ih->firstchild; child; child = child->brother)
{
/* update child natural size only */
iupBaseComputeNaturalSize(child);
}
/* Also set expand to its own expand so it will not depend on children */
*children_expand = ih->expand;
(void)w;
(void)h;
}
static void iGLCanvasBoxSetChildrenCurrentSizeMethod(Ihandle* ih, int shrink)
{
Ihandle* child;
for (child = ih->firstchild; child; child = child->brother)
{
/* update children to their own natural size */
int width = child->naturalwidth;
int height = child->naturalheight;
if (iupAttribGetBoolean(child, "EXPANDHORIZONTAL"))
width = ih->currentwidth;
if (iupAttribGetBoolean(child, "EXPANDVERTICAL"))
height = ih->currentheight;
iupBaseSetCurrentSize(child, width, height, shrink);
}
}
static int iGLCanvasBoxGetVerticalAlign(Ihandle* child)
{
char* value = iupAttribGet(child, "VERTICALALIGN");
if (!value)
return -1; /* FLOAT */
if (iupStrEqualNoCase(value, "ABOTTOM"))
return IUP_ALIGN_ABOTTOM;
else if (iupStrEqualNoCase(value, "ACENTER"))
return IUP_ALIGN_ACENTER;
else if (iupStrEqualNoCase(value, "ATOP"))
return IUP_ALIGN_ATOP;
return -1; /* FLOAT */
}
static int iGLCanvasBoxGetHorizontalAlign(Ihandle* child)
{
char* value = iupAttribGet(child, "HORIZONTALALIGN");
if (!value)
return -1; /* FLOAT */
if (iupStrEqualNoCase(value, "ARIGHT"))
return IUP_ALIGN_ARIGHT;
else if (iupStrEqualNoCase(value, "ACENTER"))
return IUP_ALIGN_ACENTER;
else if (iupStrEqualNoCase(value, "ALEFT"))
return IUP_ALIGN_ALEFT;
return -1; /* FLOAT */
}
static void iGLCanvasBoxSetChildrenPositionMethod(Ihandle* ih, int x, int y)
{
Ihandle* child;
int horiz_margin = 0, vert_margin = 0;
IupGetIntInt(ih, "MARGIN", &horiz_margin, &vert_margin);
/* since GLCanvas is a native container ignore parameters x and y */
for (child = ih->firstchild; child; child = child->brother)
{
int border = iupAttribGetBoolean(ih, "BORDER");
int vert_pos = iGLCanvasBoxGetVerticalAlign(child);
int horiz_pos = iGLCanvasBoxGetHorizontalAlign(child);
if (vert_pos == IUP_ALIGN_ACENTER)
y = (ih->currentheight - 2* border - child->currentheight) / 2;
else if (vert_pos == IUP_ALIGN_ABOTTOM)
y = ih->currentheight - 2 * border - child->currentheight - vert_margin;
else if (vert_pos == IUP_ALIGN_ATOP)
y = vert_margin;
else /* FLOAT */
y = child->y;
if (horiz_pos == IUP_ALIGN_ACENTER)
x = (ih->currentwidth - 2 * border - child->currentwidth) / 2;
else if (horiz_pos == IUP_ALIGN_ARIGHT)
x = ih->currentwidth - 2 * border - child->currentwidth - horiz_margin;
else if (horiz_pos == IUP_ALIGN_ALEFT)
x = horiz_margin;
else /* FLOAT */
x = child->x;
/* do not include border in the positioning. It is already considered in CLIENTOFFSET. */
iupBaseSetPosition(child, x, y);
}
}
#define CB_NAMES_COUNT 5
static const char* iglcanvasbox_cb_names[CB_NAMES_COUNT] = {
"ACTION",
"BUTTON_CB",
"MOTION_CB",
"WHEEL_CB",
"LEAVEWINDOW_CB" };
static Icallback iglcanvasbox_cbs[CB_NAMES_COUNT] = {
(Icallback)iGLCanvasBoxACTION,
(Icallback)iGLCanvasBoxBUTTON_CB,
(Icallback)iGLCanvasBoxMOTION_CB,
(Icallback)iGLCanvasBoxWHEEL_CB,
(Icallback)iGLCanvasBoxLEAVEWINDOW_CB };
static int iGLCanvasBoxMapMethod(Ihandle* ih)
{
int i;
char new_name[50];
for (i = 0; i < CB_NAMES_COUNT; i++)
{
Icallback cb = IupGetCallback(ih, iglcanvasbox_cb_names[i]);
if (cb)
{
/* save the application callbacks */
sprintf(new_name, "APP_%s", iglcanvasbox_cb_names[i]);
IupSetCallback(ih, new_name, cb);
}
/* always set GL_* callbacks */
IupSetCallback(ih, iglcanvasbox_cb_names[i], iglcanvasbox_cbs[i]);
}
return IUP_NOERROR;
}
static void iGLCanvasBoxUnMapMethod(Ihandle* ih)
{
iupGLFontRelease(ih);
}
static int iGLCanvasBoxCreateMethod(Ihandle* ih, void** params)
{
int i;
char new_name[50];
for (i = 0; i < CB_NAMES_COUNT; i++)
{
/* save the internal callbacks */
sprintf(new_name, "GLBOX_%s", iglcanvasbox_cb_names[i]);
IupSetCallback(ih, new_name, iglcanvasbox_cbs[i]);
}
IupSetCallback(ih, "SWAPBUFFERS_CB", iGLCanvasBoxSwapBuffers_CB);
if (params)
{
Ihandle** iparams = (Ihandle**)params;
while (*iparams)
{
IupAppend(ih, *iparams);
iparams++;
}
}
return IUP_NOERROR;
}
Iclass* iupGLCanvasBoxNewClass(void)
{
Iclass* ic = iupClassNew(iupRegisterFindClass("glcanvas"));
ic->name = "glcanvasbox";
ic->cons = "GLCanvasBox";
ic->format = "g"; /* array of Ihandle */
ic->nativetype = IUP_TYPECANVAS;
ic->childtype = IUP_CHILDMANY; /* can have children */
ic->is_interactive = 1;
ic->New = iupGLCanvasBoxNewClass;
ic->Create = iGLCanvasBoxCreateMethod;
ic->Map = iGLCanvasBoxMapMethod;
ic->UnMap = iGLCanvasBoxUnMapMethod;
ic->ComputeNaturalSize = iGLCanvasBoxComputeNaturalSizeMethod;
ic->SetChildrenCurrentSize = iGLCanvasBoxSetChildrenCurrentSizeMethod;
ic->SetChildrenPosition = iGLCanvasBoxSetChildrenPositionMethod;
/* Base Container */
/* DO not set the default container behavior for EXPAND */
/* iupClassRegisterAttribute(ic, "EXPAND", iupBaseContainerGetExpandAttrib, NULL, IUPAF_SAMEASSYSTEM, "YES", IUPAF_NOT_MAPPED | IUPAF_NO_INHERIT); */
iupClassRegisterAttribute(ic, "CLIENTOFFSET", iupBaseCanvasGetClientOffsetAttrib, NULL, NULL, NULL, IUPAF_NOT_MAPPED | IUPAF_READONLY | IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "CLIENTSIZE", iupBaseCanvasGetClientSizeAttrib, NULL, NULL, NULL, IUPAF_NOT_MAPPED | IUPAF_READONLY | IUPAF_NO_INHERIT);
/* Native Container */
iupClassRegisterAttribute(ic, "CHILDOFFSET", NULL, NULL, NULL, NULL, IUPAF_NOT_MAPPED | IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "REDRAW", NULL, iGLCanvasBoxSetRedrawAttrib, NULL, NULL, IUPAF_WRITEONLY | IUPAF_NO_INHERIT);
iupClassRegisterAttribute(ic, "MARGIN", NULL, NULL, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NO_INHERIT);
/* At Children:
VERTICALALIGN
HORIZONTALALIGN
*/
return ic;
}
Ihandle* IupGLCanvasBoxv(Ihandle** children)
{
return IupCreatev("glcanvasbox", (void**)children);
}
Ihandle* IupGLCanvasBoxV(Ihandle* child, va_list arglist)
{
return IupCreateV("glcanvasbox", child, arglist);
}
Ihandle* IupGLCanvasBox(Ihandle* child, ...)
{
Ihandle* ih;
va_list arglist;
va_start(arglist, child);
ih = IupCreateV("glcanvasbox", child, arglist);
va_end(arglist);
return ih;
}