/** \file * \brief GLCanvasBox Control. * * See Copyright Notice in "iup.h" */ #include #include #include #include #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; }