/* * Copyright (c) 2004-2007 Hypertriton, Inc. * 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. */ #include #include "menu.h" #include "primitive.h" #include "icons.h" /* * Selection has moved over the specified item. If the item is a submenu, * the submenu timer is initiated. */ static void SelectItem(AG_MenuItem *pitem, AG_MenuItem *subitem) { AG_Menu *m = pitem->pmenu; AG_MenuView *mview = pitem->view; if (pitem->sel_subitem != NULL && pitem->sel_subitem->view != NULL) { AG_MenuCollapse(m, pitem->sel_subitem); } pitem->sel_subitem = subitem; if (subitem == NULL) return; AG_LockTimeouts(m); AG_DelTimeout(mview, &mview->submenu_to); if (subitem != NULL && subitem->nsubitems > 0) { AG_ScheduleTimeout(mview, &mview->submenu_to, 200); mview->submenu_to.arg = subitem; } AG_UnlockTimeouts(m); } static Uint32 SubmenuTimeout(void *obj, Uint32 ival, void *arg) { AG_MenuView *mview = obj; AG_MenuItem *item = arg; AG_Menu *m = mview->pmenu; #ifdef AG_DEBUG if (item != mview->pitem->sel_subitem) AG_FatalError("AG_Menu: Subitem mismatch in timeout"); #endif AG_MenuExpand(m, item, WIDGET(mview)->rView.x2, WIDGET(mview)->rView.y1 + item->y); return (0); } static void MouseMotion(AG_Event *event) { AG_MenuView *mview = AG_SELF(); AG_MenuItem *pitem = mview->pitem; AG_Menu *m = mview->pmenu; int mx = AG_INT(1); int my = AG_INT(2); int y = mview->tPad, i; if (my < 0) return; if (mx < 0 || mx > WIDGET(mview)->w) goto selnone; for (i = 0; i < pitem->nsubitems; i++) { AG_MenuItem *subitem = &pitem->subitems[i]; AG_MenuUpdateItem(subitem); y += m->itemh; if (my < y) { if (mx > WIDGET(mview)->w && subitem->nsubitems == 0) { goto selnone; } if (subitem->flags & AG_MENU_ITEM_SEPARATOR) { goto selnone; } if (pitem->sel_subitem != subitem && (subitem->flags & AG_MENU_ITEM_NOSELECT) == 0) { SelectItem(pitem, subitem); } return; } } selnone: if (pitem->sel_subitem != NULL && pitem->sel_subitem->nsubitems == 0) SelectItem(pitem, NULL); } static int GetItemBoolValue(AG_MenuItem *mi) { int val = 0; switch (mi->bind_type) { case AG_MENU_INT_BOOL: val = *(int *)mi->bind_p; break; case AG_MENU_INT8_BOOL: val = *(Uint8 *)mi->bind_p; break; case AG_MENU_INT_FLAGS: val = *(int *)mi->bind_p & mi->bind_flags; break; case AG_MENU_INT8_FLAGS: val = *(Uint8 *)mi->bind_p & mi->bind_flags; break; case AG_MENU_INT16_FLAGS: val = *(Uint16 *)mi->bind_p & mi->bind_flags; break; case AG_MENU_INT32_FLAGS: val = *(Uint32 *)mi->bind_p & mi->bind_flags; break; default: break; } return (mi->bind_invert ? !val : val); } static void SetItemBoolValue(AG_MenuItem *mi) { switch (mi->bind_type) { case AG_MENU_NO_BINDING: break; case AG_MENU_INT_BOOL: { int *boolp = (int *)mi->bind_p; *boolp = !(*boolp); } break; case AG_MENU_INT8_BOOL: { Uint8 *boolp = (Uint8 *) mi->bind_p; *boolp = !(*boolp); } break; case AG_MENU_INT_FLAGS: { int *flags = (int *)mi->bind_p; AG_INVFLAGS(*flags, mi->bind_flags); } break; case AG_MENU_INT8_FLAGS: { Uint8 *flags = (Uint8 *)mi->bind_p; AG_INVFLAGS(*flags, mi->bind_flags); } break; case AG_MENU_INT16_FLAGS: { Uint16 *flags = (Uint16 *)mi->bind_p; AG_INVFLAGS(*flags, mi->bind_flags); } break; case AG_MENU_INT32_FLAGS: { Uint32 *flags = (Uint32 *)mi->bind_p; AG_INVFLAGS(*flags, mi->bind_flags); } break; } } static void MouseButtonUp(AG_Event *event) { AG_MenuView *mview = AG_SELF(); AG_MenuItem *pitem = mview->pitem; AG_Menu *m = mview->pmenu; int mx = AG_INT(2); int my = AG_INT(3); int y = mview->tPad; int i; if (my < 0 || mx < 0) { goto collapse; } for (i = 0; i < pitem->nsubitems; i++) { AG_MenuItem *item = &pitem->subitems[i]; AG_MenuUpdateItem(item); y += m->itemh; if (my < y && mx >= 0 && mx <= WIDGET(mview)->w) { if (item->state == 0) { goto collapse; } if (item->clickFn != NULL) { AG_ExecEventFn(m, item->clickFn); } else if (item->bind_type != AG_MENU_NO_BINDING) { if (item->bind_lock != NULL) AG_MutexLock(item->bind_lock); SetItemBoolValue(item); if (item->bind_lock != NULL) AG_MutexUnlock(item->bind_lock); } goto collapse; } } if (i == pitem->nsubitems) { goto collapse; } return; collapse: AG_MenuCollapse(m, pitem); SelectItem(pitem, NULL); m->itemSel = NULL; m->selecting = 0; } static void Init(void *obj) { AG_MenuView *mview = obj; WIDGET(mview)->flags |= AG_WIDGET_UNFOCUSED_MOTION| AG_WIDGET_UNFOCUSED_BUTTONUP; mview->panel = NULL; mview->pmenu = NULL; mview->pitem = NULL; mview->spIconLbl = 8; mview->spLblArrow = 16; mview->lPad = 8; mview->rPad = 8; mview->tPad = 4; mview->bPad = 4; AG_SetEvent(mview, "window-mousemotion", MouseMotion, NULL); AG_SetEvent(mview, "window-mousebuttonup", MouseButtonUp, NULL); AG_SetTimeout(&mview->submenu_to, SubmenuTimeout, NULL, 0); /* XXX wasteful */ AG_WidgetMapSurface(mview, AG_DupSurface(agIconSmallArrowRight.s)); } static void Draw(void *obj) { AG_MenuView *mview = obj; AG_MenuItem *pitem = mview->pitem; AG_Menu *m = mview->pmenu; AG_Rect r; int i; STYLE(mview)->MenuBackground(mview, AG_RECT(0, 0, WIDTH(mview), HEIGHT(mview))); r.x = 0; r.y = mview->tPad; r.w = WIDGET(mview)->w; r.h = m->itemh; for (i = 0; i < pitem->nsubitems; i++) { AG_MenuItem *item = &pitem->subitems[i]; int x = mview->lPad; AG_MenuUpdateItem(item); if (item->icon == -1 && item->iconSrc != NULL) { item->icon = AG_WidgetMapSurface(m, item->iconSrc); } STYLE(mview)->MenuItemBackground(mview, r, x, m, item->icon, (item == pitem->sel_subitem && item->state == 1), (item->value != -1) ? item->value : GetItemBoolValue(item)); if (pitem->flags & AG_MENU_ITEM_ICONS) { x += m->itemh + mview->spIconLbl; } if (item->flags & AG_MENU_ITEM_SEPARATOR) { STYLE(mview)->MenuItemSeparator(mview, mview->lPad, WIDTH(mview) - mview->rPad - 1, r.y, m->itemh); } else { int lbl = item->state ? item->lblEnabled : item->lblDisabled; if (item->state == 1) { if (item->lblEnabled == -1) { AG_TextColor(MENU_TXT_COLOR); item->lblEnabled = (item->text == NULL) ? -1 : AG_WidgetMapSurface(m, AG_TextRender(item->text)); } lbl = item->lblEnabled; } else { if (item->lblDisabled == -1) { AG_TextColor(MENU_TXT_DISABLED_COLOR); item->lblDisabled = (item->text == NULL) ? -1 : AG_WidgetMapSurface(m, AG_TextRender(item->text)); } lbl = item->lblDisabled; } AG_WidgetBlitFrom(mview, m, lbl, NULL, x, r.y + m->itemh/2 - agTextFontHeight/2 + 1); x += WSURFACE(m,lbl)->w; } if (item->nsubitems > 0) { x += mview->spLblArrow; AG_WidgetBlitSurface(mview, 0, x, r.y + m->itemh/2 - agIconSmallArrowRight.s->h/2 -1); } r.y += m->itemh; } } static void SizeRequest(void *obj, AG_SizeReq *r) { AG_MenuView *mview = obj; AG_MenuItem *pitem = mview->pitem; AG_Menu *m = mview->pmenu; int i; r->w = 0; r->h = mview->tPad + mview->bPad; for (i = 0; i < pitem->nsubitems; i++) { AG_MenuItem *item = &pitem->subitems[i]; int wReq = mview->lPad + mview->rPad; int wLbl; AG_MenuUpdateItem(item); if (item->icon != -1) { wReq += WSURFACE(m,item->icon)->w; } if (pitem->flags & AG_MENU_ITEM_ICONS) wReq += m->itemh + mview->spIconLbl; if (item->lblEnabled != -1) { wLbl = WSURFACE(m,item->lblEnabled)->w; } else if (item->lblDisabled != -1) { wLbl = WSURFACE(m,item->lblDisabled)->w; } else { AG_TextSize(item->text, &wLbl, NULL); } wReq += wLbl; if (item->nsubitems > 0) { wReq += mview->spLblArrow + agIconSmallArrowRight.s->w; } if (wReq > r->w) { r->w = wReq; } r->h += m->itemh; } } static int SizeAllocate(void *obj, const AG_SizeAlloc *a) { if (a->w < 4 || a->h < 4) { return (-1); } return (0); } AG_WidgetClass agMenuViewClass = { { "Agar(Widget:MenuView)", sizeof(AG_MenuView), { 0,0 }, Init, NULL, /* free */ NULL, /* destroy */ NULL, /* load */ NULL, /* save */ NULL /* edit */ }, Draw, SizeRequest, SizeAllocate };