/* * Copyright (c) 2007-2023 Julien Nadeau Carriere * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Socket widget. Holds a drag-and-droppable AG_Icon(3). */ #include #ifdef AG_WIDGETS #include #include #include #include #include #include #include AG_Socket * AG_SocketNew(void *parent, Uint flags) { AG_Socket *sock; sock = Malloc(sizeof(AG_Socket)); AG_ObjectInit(sock, &agSocketClass); if (flags & AG_SOCKET_HFILL) { WIDGET(sock)->flags |= AG_WIDGET_HFILL; } if (flags & AG_SOCKET_VFILL) { WIDGET(sock)->flags |= AG_WIDGET_VFILL; } sock->flags |= flags; AG_ObjectAttach(parent, sock); return (sock); } AG_Socket * AG_SocketFromSurface(void *parent, Uint flags, AG_Surface *S) { AG_Socket *sock; sock = AG_SocketNew(parent, flags); AG_SocketBgPixmap(sock, S); return (sock); } AG_Socket * AG_SocketFromBMP(void *parent, Uint flags, const char *bmpfile) { AG_Socket *sock; AG_Surface *bmp; if ((bmp = AG_SurfaceFromBMP(bmpfile)) == NULL) { AG_FatalError(NULL); } sock = AG_SocketNew(parent, flags); AG_SocketBgPixmapNODUP(sock, bmp); return (sock); } void AG_SocketInsertFn(AG_Socket *sock, int (*fn)(AG_Socket *, AG_Icon *)) { AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_ObjectLock(sock); sock->insertFn = fn; AG_ObjectUnlock(sock); } void AG_SocketRemoveFn(AG_Socket *sock, void (*fn)(AG_Socket *, AG_Icon *)) { AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_ObjectLock(sock); sock->removeFn = fn; AG_ObjectUnlock(sock); } void AG_SocketOverlayFn(AG_Socket *sock, AG_EventFn fn, const char *fmt, ...) { AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_ObjectLock(sock); if (fn) { sock->overlayFn = AG_SetEvent(sock, NULL, fn, NULL); if (fmt) { va_list ap; va_start(ap, fmt); AG_EventGetArgs(sock->overlayFn, fmt, ap); va_end(ap); } } else { sock->overlayFn = NULL; } AG_ObjectUnlock(sock); } static void SizeRequest(void *_Nonnull obj, AG_SizeReq *_Nonnull r) { AG_Socket *sock = obj; AG_Surface *S; r->w = WIDGET(sock)->paddingLeft + WIDGET(sock)->paddingRight; r->h = WIDGET(sock)->paddingTop + WIDGET(sock)->paddingBottom; switch (sock->bgType) { case AG_SOCKET_PIXMAP: S = WSURFACE(sock, sock->bgData.pixmap.s); r->w += S->w; r->h += S->h; break; case AG_SOCKET_RECT: r->w += sock->bgData.rect.w; r->h += sock->bgData.rect.h; break; case AG_SOCKET_CIRCLE: r->w += (sock->bgData.circle.r << 1); r->h += (sock->bgData.circle.r << 1); break; } } static __inline__ int GetState(AG_Variable *_Nonnull binding, void *_Nonnull p) { switch (AG_VARIABLE_TYPE(binding)) { case AG_VARIABLE_INT: return *(int *)p; case AG_VARIABLE_UINT8: return (int)(*(Uint8 *)p); case AG_VARIABLE_UINT16: return (int)(*(Uint16 *)p); case AG_VARIABLE_UINT32: return (int)(*(Uint32 *)p); case AG_VARIABLE_P_FLAG: return (int)(*(Uint *)p & binding->info.bitmask.u); case AG_VARIABLE_P_FLAG8: return (int)(*(Uint8 *)p & binding->info.bitmask.u8); case AG_VARIABLE_P_FLAG16: return (int)(*(Uint16 *)p & binding->info.bitmask.u16); case AG_VARIABLE_P_FLAG32: return (int)(*(Uint32 *)p & binding->info.bitmask.u32); default: return (0); } return (0); } #if 0 static __inline__ int GetCount(AG_Variable *_Nonnull binding, void *_Nonnull p) { switch (AG_VARIABLE_TYPE(binding)) { case AG_VARIABLE_UINT: return (Uint)(*(int *)p); case AG_VARIABLE_INT: return *(int *)p; case AG_VARIABLE_UINT8: return (int)(*(Uint8 *)p); case AG_VARIABLE_SINT8: return (int)(*(Sint8 *)p); case AG_VARIABLE_UINT16: return (int)(*(Uint16 *)p); case AG_VARIABLE_SINT16: return (int)(*(Sint16 *)p); case AG_VARIABLE_UINT32: return (int)(*(Uint32 *)p); case AG_VARIABLE_SINT32: return (int)(*(Sint32 *)p); default: } return (-1); } #endif static void Draw(void *_Nonnull obj) { AG_Socket *sock = obj; AG_Variable *binding; void *pBinding; int state; binding = AG_GetVariable(sock, "state", &pBinding); state = GetState(binding, pBinding); AG_UnlockVariable(binding); #if 0 binding = AG_GetVariable(sock, "count", &pBinding); count = GetCount(binding, pBinding); AG_UnlockVariable(binding); #endif /* Draw background */ switch (sock->bgType) { case AG_SOCKET_PIXMAP: { const AG_Surface *S = WSURFACE(sock, sock->bgData.pixmap.s); Uint32 Amask; if ((Amask = S->format.Amask) != 0) AG_PushBlendingMode(sock, AG_ALPHA_SRC, AG_ALPHA_ONE_MINUS_SRC); AG_WidgetBlitSurface(sock, sock->bgData.pixmap.s, WIDGET(sock)->paddingLeft, WIDGET(sock)->paddingTop); if (Amask != 0) AG_PopBlendingMode(sock); } break; case AG_SOCKET_RECT: AG_DrawBoxSunk(sock, &WIDGET(sock)->r, &WCOLOR(sock,FG_COLOR)); break; case AG_SOCKET_CIRCLE: AG_DrawCircle(sock, WIDTH(sock) >> 1, HEIGHT(sock) >> 1, sock->bgData.circle.r, &WCOLOR(sock,FG_COLOR)); break; } if (sock->icon) { WIDGET_OPS(sock->icon)->draw(sock->icon); } if (sock->overlayFn) { AG_PostEventByPtr(sock, sock->overlayFn, NULL); } else { switch (sock->bgType) { case AG_SOCKET_PIXMAP: /* TODO */ case AG_SOCKET_RECT: if (state) { AG_Rect r; r.x = WIDGET(sock)->paddingLeft; r.y = WIDGET(sock)->paddingTop; r.w = WIDTH(sock) - r.x - WIDGET(sock)->paddingRight; r.h = HEIGHT(sock) - r.y - WIDGET(sock)->paddingBottom; AG_DrawRectOutline(sock, &r, &WCOLOR(sock, LINE_COLOR)); } break; case AG_SOCKET_CIRCLE: if (state) { AG_DrawCircle(sock, WIDTH(sock) >> 1, HEIGHT(sock) >> 1, (sock->bgData.circle.r - WIDGET(sock)->paddingLeft - WIDGET(sock)->paddingRight), &WCOLOR(sock, LINE_COLOR)); } break; } } } static void SetState(AG_Socket *_Nonnull sock, AG_Variable *_Nonnull binding, void *_Nonnull p, int v) { switch (AG_VARIABLE_TYPE(binding)) { case AG_VARIABLE_INT: *(int *)p = v; break; case AG_VARIABLE_UINT8: *(Uint8 *)p = v; break; case AG_VARIABLE_UINT16: *(Uint16 *)p = v; break; case AG_VARIABLE_UINT32: *(Uint32 *)p = v; break; case AG_VARIABLE_P_FLAG: AG_SETFLAGS(*(int *)p, binding->info.bitmask.u, v); break; case AG_VARIABLE_P_FLAG8: AG_SETFLAGS(*(Uint8 *)p, binding->info.bitmask.u8, v); break; case AG_VARIABLE_P_FLAG16: AG_SETFLAGS(*(Uint16 *)p, binding->info.bitmask.u16, v); break; case AG_VARIABLE_P_FLAG32: AG_SETFLAGS(*(Uint32 *)p, binding->info.bitmask.u32, v); break; default: break; } AG_Redraw(sock); } #if 0 static void SetCount(AG_Variable *_Nonnull binding, void *_Nonnull p, int v) { switch (AG_VARIABLE_TYPE(binding)) { case AG_VARIABLE_UINT: *(Uint *)p = v; break; case AG_VARIABLE_INT: *(int *)p = v; break; case AG_VARIABLE_UINT8: *(Uint8 *)p = v; break; case AG_VARIABLE_SINT8: *(Sint8 *)p = v; break; case AG_VARIABLE_UINT16: *(Uint16 *)p = v; break; case AG_VARIABLE_SINT16: *(Sint16 *)p = v; break; case AG_VARIABLE_UINT32: *(Uint32 *)p = v; break; case AG_VARIABLE_SINT32: *(Sint32 *)p = v; break; } } #endif static void MouseMotion(void *obj, int x, int y, int dx, int dy) { AG_Socket *sock = obj; AG_Variable *binding; void *pState; if (AG_WidgetDisabled(sock)) return; binding = AG_GetVariable(sock, "state", &pState); if (!AG_WidgetRelativeArea(sock, x, y)) { if ((sock->flags & AG_SOCKET_STICKY_STATE) == 0 && GetState(binding, pState) == 1) { SetState(sock, binding, pState, 0); } if (sock->flags & AG_SOCKET_MOUSEOVER) { sock->flags &= ~(AG_SOCKET_MOUSEOVER); AG_PostEvent(sock, "socket-mouseoverlap", "%i", 0); AG_Redraw(sock); } } else { sock->flags |= AG_SOCKET_MOUSEOVER; AG_PostEvent(sock, "socket-mouseoverlap", "%i", 1); AG_Redraw(sock); } AG_UnlockVariable(binding); } static void IconMotion(AG_Event *_Nonnull event) { AG_Icon *icon = AG_ICON_PTR(1); const int xRel = AG_INT(4); const int yRel = AG_INT(5); AG_Window *wDND = icon->wDND; AG_WindowMove(wDND, xRel, yRel); } static void IconButtonUp(AG_Event *_Nonnull event) { AG_Icon *icon = AG_ICON_PTR(1); AG_Window *wDND = icon->wDND; const int x = WIDGET(wDND)->rView.x1; const int y = WIDGET(wDND)->rView.y1; AG_Socket *sock; int detach = 1; sock = AG_WidgetFindRect("AG_Widget:AG_Socket:*", x,y, WIDTH(wDND), HEIGHT(wDND)); if (sock) { AG_ObjectLock(sock); if (sock->insertFn) { detach = sock->insertFn(sock, icon); } else { if (icon->sock) { AG_SocketRemoveIcon(icon->sock); } AG_SocketInsertIcon(sock, icon); } AG_ObjectUnlock(sock); } if (detach) AG_ObjectDetach(wDND); } static void MouseButtonDown(void *obj, AG_MouseButton button, int x, int y) { AG_Socket *sock = obj; AG_Window *winParent = WIDGET(sock)->window; AG_Variable *binding; void *pState; int newState; AG_Icon *icon; if (AG_WidgetDisabled(sock) || button != AG_MOUSE_LEFT) { return; } if (!AG_WidgetIsFocused(sock)) AG_WidgetFocus(sock); binding = AG_GetVariable(sock, "state", &pState); if (!(sock->flags & AG_SOCKET_STICKY_STATE)) { SetState(sock, binding, pState, 1); } else { newState = !GetState(binding, pState); SetState(sock, binding, pState, newState); AG_PostEvent(sock, "socket-click", "%i", newState); } AG_UnlockVariable(binding); if ((icon = sock->icon) != NULL) { AG_Pixmap *px; AG_Window *wDND; wDND = icon->wDND = AG_WindowNew(AG_WINDOW_MODAL | AG_WINDOW_PLAIN | AG_WINDOW_NOBACKGROUND | AG_WINDOW_KEEPABOVE); wDND->wmType = AG_WINDOW_WM_DND; px = AG_PixmapFromSurface(wDND, 0, WSURFACE(icon,icon->surface)); /* winParent->widExclMotion = WIDGET(px); */ AG_ObjectLock(px); WIDGET(px)->flags |= AG_WIDGET_UNFOCUSED_MOTION | AG_WIDGET_UNFOCUSED_BUTTONUP; AG_AddEvent(px, "mouse-motion", IconMotion,"%p",icon); AG_AddEvent(px, "mouse-button-up", IconButtonUp,"%p",icon); AG_ObjectUnlock(px); if (AGDRIVER_SINGLE(WIDGET(sock)->drv)) { AG_WindowSetGeometry(wDND, WIDGET(icon)->rView.x1, WIDGET(icon)->rView.y1, WIDTH(icon), HEIGHT(icon)); } else { AG_WindowSetGeometry(wDND, WIDGET(winParent)->x + WIDGET(icon)->rView.x1, WIDGET(winParent)->y + WIDGET(icon)->rView.y1, WIDTH(icon), HEIGHT(icon)); } AG_WindowShow(wDND); } } static void MouseButtonUp(void *obj, AG_MouseButton button, int x, int y) { AG_Socket *sock = obj; AG_Variable *binding; void *pState; if (AG_WidgetDisabled(sock) || x < 0 || y < 0 || x > WIDTH(sock) || y > HEIGHT(sock)) return; binding = AG_GetVariable(sock, "state", &pState); if (GetState(binding, pState) && button == AG_MOUSE_LEFT && !(sock->flags & AG_SOCKET_STICKY_STATE)) { SetState(sock, binding, pState, 0); AG_PostEvent(sock, "socket-click", "%i", 0); } AG_UnlockVariable(binding); } /* Render as a rectangle of w x h pixels. */ void AG_SocketBgRect(AG_Socket *sock, Uint w, Uint h) { AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_ObjectLock(sock); sock->bgType = AG_SOCKET_RECT; sock->bgData.rect.w = w; sock->bgData.rect.h = h; AG_Redraw(sock); AG_ObjectUnlock(sock); } /* Render as a circle of radius r in pixels. */ void AG_SocketBgCircle(AG_Socket *sock, Uint r) { AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_ObjectLock(sock); sock->bgType = AG_SOCKET_CIRCLE; sock->bgData.circle.r = r; AG_Redraw(sock); AG_ObjectUnlock(sock); } /* Render as a given surface (duplicated). */ void AG_SocketBgPixmap(AG_Socket *sock, const AG_Surface *S) { AG_Surface *Sdup = S ? AG_SurfaceDup(S) : NULL; AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_ObjectLock(sock); sock->bgType = AG_SOCKET_PIXMAP; sock->bgData.pixmap.s = AG_WidgetMapSurface(sock, Sdup); AG_Redraw(sock); AG_ObjectUnlock(sock); } /* Render as a given surface without duplicating (potentially unsafe). */ void AG_SocketBgPixmapNODUP(AG_Socket *sock, AG_Surface *S) { AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_ObjectLock(sock); sock->bgType = AG_SOCKET_PIXMAP; sock->bgData.pixmap.s = AG_WidgetMapSurface(sock, S); AG_Redraw(sock); AG_ObjectUnlock(sock); } /* Insert an icon in the socket (reparenting it). */ void AG_SocketInsertIcon(AG_Socket *sock, AG_Icon *icon) { AG_SizeAlloc a; AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_LockVFS(sock); AG_ObjectLock(sock); AG_OBJECT_ISA(icon, "AG_Widget:AG_Icon:*"); AG_ObjectLock(icon); AG_ObjectAttach(sock, icon); /* Reparent */ sock->icon = icon; icon->sock = sock; a.x = 0; a.y = 0; a.w = WIDTH(sock); a.h = HEIGHT(sock); AG_WidgetSizeAlloc(icon, &a); AG_WidgetUpdateCoords(icon, a.x, a.y); AG_WidgetCompileStyle(sock); AG_Redraw(sock); AG_WindowUpdate(AG_ParentWindow(sock)); AG_ObjectUnlock(icon); AG_ObjectUnlock(sock); AG_UnlockVFS(sock); } /* Remove an icon from the socket (detaching it). */ void AG_SocketRemoveIcon(AG_Socket *sock) { AG_OBJECT_ISA(sock, "AG_Widget:AG_Socket:*"); AG_LockVFS(sock); AG_ObjectLock(sock); if (sock->icon != NULL) { AG_ObjectLock(sock->icon); if (sock->removeFn != NULL) { sock->removeFn(sock, sock->icon); } else { sock->icon->sock = NULL; AG_ObjectDetach(sock->icon); AG_ObjectUnlock(sock->icon); } } sock->icon = NULL; AG_Redraw(sock); AG_ObjectUnlock(sock); AG_UnlockVFS(sock); } static void Init(void *_Nonnull obj) { AG_Socket *sock = obj; WIDGET(sock)->flags |= AG_WIDGET_FOCUSABLE | AG_WIDGET_UNFOCUSED_MOTION | AG_WIDGET_UNFOCUSED_BUTTONUP; sock->flags = 0; sock->state = 0; sock->count = 0; sock->bgType = AG_SOCKET_RECT; sock->bgData.rect.w = 32; sock->bgData.rect.h = 32; sock->lblJustify = AG_TEXT_LEFT; sock->icon = NULL; sock->insertFn = NULL; sock->removeFn = NULL; sock->overlayFn = NULL; AG_BindInt(sock, "state", &sock->state); AG_BindInt(sock, "count", &sock->count); AG_RedrawOnChange(sock, 100, "state"); AG_RedrawOnChange(sock, 500, "count"); } #ifdef AG_LEGACY void AG_SocketSetPadding(AG_Socket *sock, int lPad, int rPad, int tPad, int bPad) { AG_SetStyleF(sock, "padding", "%d %d %d %d", (tPad != -1) ? tPad : 0, (rPad != -1) ? rPad : 0, (bPad != -1) ? bPad : 0, (lPad != -1) ? lPad : 0); } #endif /* AG_LEGACY */ AG_WidgetClass agSocketClass = { { "Agar(Widget:Socket)", sizeof(AG_Socket), { 1,0, AGC_SOCKET, 0xE02E }, Init, NULL, /* reset */ NULL, /* destroy */ NULL, /* load */ NULL, /* save */ NULL /* edit */ }, Draw, SizeRequest, NULL, /* size_allocate */ MouseButtonDown, MouseButtonUp, MouseMotion, NULL, /* key_down */ NULL, /* key_up */ NULL, /* touch */ NULL, /* ctrl */ NULL /* joy */ }; #endif /* AG_WIDGETS */