/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Widget.c * * The main interface functions to the widget library */ #include #include "lib/framework/frame.h" #include "lib/framework/frameint.h" #include "widget.h" #include "widgint.h" #include "form.h" #include "label.h" #include "button.h" #include "editbox.h" #include "bar.h" #include "slider.h" #include "tip.h" #include /* The initial and extension number of strings to allocate in the string heap */ #define WIDG_STRINIT 100 #define WIDG_STREXT 10 /* the widget to be returned by widgRunScreen */ static WIDGET *psRetWidget; static BOOL bWidgetsActive = TRUE; /* The widget the mouse is over this update */ static WIDGET *psMouseOverWidget; static UDWORD pressed, released; static WIDGET_AUDIOCALLBACK AudioCallback = NULL; static SWORD HilightAudioID = -1; static SWORD ClickedAudioID = -1; /* Function prototypes */ void widgHiLite(WIDGET *psWidget, W_CONTEXT *psContext); void widgHiLiteLost(WIDGET *psWidget, W_CONTEXT *psContext); static void widgClicked(WIDGET *psWidget, UDWORD key, W_CONTEXT *psContext); static void widgReleased(WIDGET *psWidget, UDWORD key, W_CONTEXT *psContext); static void widgRun(WIDGET *psWidget, W_CONTEXT *psContext); static void widgDisplayForm(W_FORM *psForm, UDWORD xOffset, UDWORD yOffset); /* The heap for widget strings */ static OBJ_HEAP *psStrHeap; /* Buffer to return strings in */ static char aStringRetBuffer[WIDG_MAXSTR]; /* Initialise the widget module */ BOOL widgInitialise(W_HEAPINIT *psInit) { #if W_USE_STRHEAP // Create the string heap if (!HEAP_CREATE(&psStrHeap, WIDG_MAXSTR, WIDG_STRINIT, WIDG_STREXT)) { return FALSE; } #endif tipInitialise(); #if W_USE_MALLOC psInit = psInit; #else // Create the widget heaps if (!HEAP_CREATE(&psBarHeap, sizeof(W_BARGRAPH), psInit->barInit, psInit->barExt)) { return FALSE; } if (!HEAP_CREATE(&psButHeap, sizeof(W_BUTTON), psInit->butInit, psInit->butExt)) { return FALSE; } if (!HEAP_CREATE(&psEdbHeap, sizeof(W_EDITBOX), psInit->edbInit, psInit->edbExt)) { return FALSE; } if (!HEAP_CREATE(&psFormHeap, sizeof(W_FORM), psInit->formInit, psInit->formExt)) { return FALSE; } if (!HEAP_CREATE(&psCFormHeap, sizeof(W_CLICKFORM), psInit->cFormInit, psInit->cFormExt)) { return FALSE; } if (!HEAP_CREATE(&psTFormHeap, sizeof(W_TABFORM), psInit->tFormInit, psInit->tFormExt)) { return FALSE; } if (!HEAP_CREATE(&psLabHeap, sizeof(W_LABEL), psInit->labInit, psInit->labExt)) { return FALSE; } if (!HEAP_CREATE(&psSldHeap, sizeof(W_SLIDER), psInit->sldInit, psInit->sldExt)) { return FALSE; } #endif return TRUE; } // Reset the widgets. // void widgReset(void) { tipInitialise(); } /* Shut down the widget module */ void widgShutDown(void) { #if W_USE_STRHEAP HEAP_DESTROY(psStrHeap); #endif #if !W_USE_MALLOC HEAP_DESTROY(psBarHeap); HEAP_DESTROY(psButHeap); HEAP_DESTROY(psEdbHeap); HEAP_DESTROY(psFormHeap); HEAP_DESTROY(psCFormHeap); HEAP_DESTROY(psTFormHeap); HEAP_DESTROY(psLabHeap); HEAP_DESTROY(psSldHeap); #endif } /* Get a string from the string heap */ BOOL widgAllocString(char **ppStr) { if (!HEAP_ALLOC(psStrHeap, (void**) ppStr)) { return FALSE; } return TRUE; } /* Copy one string to another * The string to copy will be truncated if it is longer than WIDG_MAXSTR. */ void widgCopyString(char *pDest, const char *pSrc) { /* See if we need to clip the string, then copy */ if (strlen(pSrc) >= WIDG_MAXSTR) { memcpy(pDest, pSrc, WIDG_MAXSTR - 1); *(pDest + WIDG_MAXSTR-1) = 0; } else { strcpy(pDest, pSrc); } } /* Get a string from the heap and copy in some data. * The string to copy will be truncated if it is too long. */ BOOL widgAllocCopyString(char **ppDest, char *pSrc) { if (!HEAP_ALLOC(psStrHeap, (void**) ppDest)) { *ppDest = NULL; return FALSE; } widgCopyString(*ppDest, pSrc); return TRUE; } /* Return a string to the string heap */ void widgFreeString(char *pStr) { HEAP_FREE(psStrHeap, pStr); } /* Create an empty widget screen */ BOOL widgCreateScreen(W_SCREEN **ppsScreen) { W_FORM *psForm; W_FORMINIT sInit; *ppsScreen = (W_SCREEN *)MALLOC(sizeof(W_SCREEN)); if (*ppsScreen == NULL) { ASSERT( FALSE, "Out of memory" ); return FALSE; } memset(&sInit, 0, sizeof(W_FORMINIT)); sInit.id = 0; sInit.style = WFORM_PLAIN | WFORM_INVISIBLE; sInit.x = 0; sInit.y = 0; sInit.width = (UWORD)(screenWidth - 1); sInit.height = (UWORD)(screenHeight - 1); if (!formCreate(&psForm, &sInit)) { return FALSE; } (*ppsScreen)->psForm = (WIDGET *)psForm; (*ppsScreen)->psFocus = NULL; (*ppsScreen)->TipFontID = 0; return TRUE; } /* Release a list of widgets */ void widgReleaseWidgetList(WIDGET *psWidgets) { WIDGET *psCurr, *psNext; for(psCurr = psWidgets; psCurr; psCurr = psNext) { psNext = psCurr->psNext; switch(psCurr->type) { case WIDG_FORM: formFree((W_FORM *)psCurr); break; case WIDG_LABEL: labelFree((W_LABEL *)psCurr); break; case WIDG_BUTTON: buttonFree((W_BUTTON *)psCurr); break; case WIDG_EDITBOX: editBoxFree((W_EDITBOX *)psCurr); break; case WIDG_BARGRAPH: barGraphFree((W_BARGRAPH *)psCurr); break; case WIDG_SLIDER: sliderFree((W_SLIDER *)psCurr); break; default: ASSERT( FALSE,"widgReleaseWidgetList: Unknown widget type" ); break; } } } /* Release a screen and all its associated data */ void widgReleaseScreen(W_SCREEN *psScreen) { ASSERT( psScreen != NULL, "widgReleaseScreen: Invalid screen pointer" ); formFree((W_FORM *)psScreen->psForm); FREE(psScreen); } /* Release a widget */ void widgRelease(WIDGET *psWidget) { switch(psWidget->type) { case WIDG_FORM: formFree((W_FORM *)psWidget); break; case WIDG_LABEL: labelFree((W_LABEL *)psWidget); break; case WIDG_BUTTON: buttonFree((W_BUTTON *)psWidget); break; case WIDG_EDITBOX: editBoxFree((W_EDITBOX *)psWidget); break; case WIDG_BARGRAPH: barGraphFree((W_BARGRAPH *)psWidget); break; case WIDG_SLIDER: sliderFree((W_SLIDER *)psWidget); break; default: ASSERT( FALSE,"widgRelease: Unknown widget type" ); break; } } /* Check whether an ID has been used on a form */ static BOOL widgCheckIDForm(W_FORM *psForm, UDWORD id) { WIDGET *psCurr; W_FORMGETALL sGetAll; /* Check the widgets on the form */ formInitGetAllWidgets(psForm, &sGetAll); psCurr = formGetAllWidgets(&sGetAll); while (psCurr != NULL) { if (psCurr->id == id) { return TRUE; } if (psCurr->type == WIDG_FORM) { /* Another form so recurse */ if (widgCheckIDForm((W_FORM *)psCurr, id)) { return TRUE; } } psCurr = psCurr->psNext; if (!psCurr) { /* Got to the end of this list see if there is another */ psCurr = formGetAllWidgets(&sGetAll); } } return FALSE; } #if 0 /* Check whether an ID number has been used on a screen */ static BOOL widgCheckID(W_SCREEN *psScreen, UDWORD id) { return widgCheckIDForm((W_FORM *)psScreen->psForm, id); } #endif /* Set the tool tip font for a screen */ void widgSetTipFont(W_SCREEN *psScreen, int FontID) { ASSERT( psScreen != NULL, "widgSetTipFont: Invalid screen pointer" ); psScreen->TipFontID = FontID; } /* Add a form to the widget screen */ BOOL widgAddForm(W_SCREEN *psScreen, W_FORMINIT *psInit) { W_FORM *psParent, *psForm; ASSERT( psScreen != NULL, "widgAddForm: Invalid screen pointer" ); if (widgCheckIDForm((W_FORM *)psScreen->psForm,psInit->id)) { ASSERT( FALSE, "widgAddForm: ID number has already been used (%d)", psInit->id ); return FALSE; } /* Find the form to add the widget to */ if (psInit->formID == 0) { /* Add to the base form */ psParent = (W_FORM *)psScreen->psForm; } else { psParent = (W_FORM *)widgGetFromID(psScreen, psInit->formID); if (!psParent || psParent->type != WIDG_FORM) { ASSERT( FALSE, "widgAddForm: Could not find parent form from formID" ); return FALSE; } } /* Create the form structure */ if (!formCreate(&psForm, psInit)) { return FALSE; } /* Add it to the screen */ if (!formAddWidget(psParent, (WIDGET *)psForm, (W_INIT *)psInit)) { return FALSE; } return TRUE; } /* Add a label to the widget screen */ BOOL widgAddLabel(W_SCREEN *psScreen, W_LABINIT *psInit) { W_LABEL *psLabel; W_FORM *psForm; ASSERT( psScreen != NULL, "widgAddLabel: Invalid screen pointer" ); if (widgCheckIDForm((W_FORM *)psScreen->psForm,psInit->id)) { ASSERT( FALSE, "widgAddLabel: ID number has already been used (%d)", psInit->id ); return FALSE; } /* Find the form to put the button on */ if (psInit->formID == 0) { psForm = (W_FORM *)psScreen->psForm; } else { psForm = (W_FORM *)widgGetFromID(psScreen, psInit->formID); if (psForm == NULL || psForm->type != WIDG_FORM) { ASSERT( FALSE, "widgAddLabel: Could not find parent form from formID" ); return FALSE; } } /* Create the button structure */ if (!labelCreate(&psLabel, psInit)) { return FALSE; } /* Add it to the form */ if (!formAddWidget(psForm, (WIDGET *)psLabel, (W_INIT *)psInit)) { return FALSE; } return TRUE; } /* Add a button to the widget screen */ BOOL widgAddButton(W_SCREEN *psScreen, W_BUTINIT *psInit) { W_BUTTON *psButton; W_FORM *psForm; ASSERT( psScreen != NULL, "widgAddButton: Invalid screen pointer" ); if (widgCheckIDForm((W_FORM *)psScreen->psForm,psInit->id)) { ASSERT( FALSE, "widgAddButton: ID number has already been used(%d)", psInit->id ); return FALSE; } /* Find the form to put the button on */ if (psInit->formID == 0) { psForm = (W_FORM *)psScreen->psForm; } else { psForm = (W_FORM *)widgGetFromID(psScreen, psInit->formID); if (psForm == NULL || psForm->type != WIDG_FORM) { ASSERT( FALSE, "widgAddButton: Could not find parent form from formID" ); return FALSE; } } /* Create the button structure */ if (!buttonCreate(&psButton, psInit)) { return FALSE; } /* Add it to the form */ if (!formAddWidget(psForm, (WIDGET *)psButton, (W_INIT *)psInit)) { return FALSE; } return TRUE; } /* Add an edit box to the widget screen */ BOOL widgAddEditBox(W_SCREEN *psScreen, W_EDBINIT *psInit) { W_EDITBOX *psEdBox; W_FORM *psForm; ASSERT( psScreen != NULL, "widgAddEditBox: Invalid screen pointer" ); if (widgCheckIDForm((W_FORM *)psScreen->psForm,psInit->id)) { ASSERT( FALSE, "widgAddEditBox: ID number has already been used (%d)", psInit->id ); return FALSE; } /* Find the form to put the edit box on */ if (psInit->formID == 0) { psForm = (W_FORM *)psScreen->psForm; } else { psForm = (W_FORM *)widgGetFromID(psScreen, psInit->formID); if (!psForm || psForm->type != WIDG_FORM) { ASSERT( FALSE, "widgAddEditBox: Could not find parent form from formID" ); return FALSE; } } /* Create the edit box structure */ if (!editBoxCreate(&psEdBox, psInit)) { return FALSE; } /* Add it to the form */ if (!formAddWidget(psForm, (WIDGET *)psEdBox, (W_INIT *)psInit)) { return FALSE; } return TRUE; } /* Add a bar graph to the widget screen */ BOOL widgAddBarGraph(W_SCREEN *psScreen, W_BARINIT *psInit) { W_BARGRAPH *psBarGraph; W_FORM *psForm; ASSERT( psScreen != NULL, "widgAddEditBox: Invalid screen pointer" ); if (widgCheckIDForm((W_FORM *)psScreen->psForm,psInit->id)) { ASSERT( FALSE, "widgAddBarGraph: ID number has already been used (%d)", psInit->id ); return FALSE; } /* Find the form to put the bar graph on */ if (psInit->formID == 0) { psForm = (W_FORM *)psScreen->psForm; } else { psForm = (W_FORM *)widgGetFromID(psScreen, psInit->formID); if (!psForm || psForm->type != WIDG_FORM) { ASSERT( FALSE, "widgAddBarGraph: Could not find parent form from formID" ); return FALSE; } } /* Create the bar graph structure */ if (!barGraphCreate(&psBarGraph, psInit)) { return FALSE; } /* Add it to the form */ if (!formAddWidget(psForm, (WIDGET *)psBarGraph, (W_INIT *)psInit)) { return FALSE; } return TRUE; } /* Add a slider to a form */ BOOL widgAddSlider(W_SCREEN *psScreen, W_SLDINIT *psInit) { W_SLIDER *psSlider; W_FORM *psForm; ASSERT( psScreen != NULL, "widgAddEditBox: Invalid screen pointer" ); if (widgCheckIDForm((W_FORM *)psScreen->psForm, psInit->id)) { ASSERT( FALSE, "widgSlider: ID number has already been used (%d)", psInit->id ); return FALSE; } /* Find the form to put the slider on */ if (psInit->formID == 0) { psForm = (W_FORM *)psScreen->psForm; } else { psForm = (W_FORM *)widgGetFromID(psScreen, psInit->formID); if (!psForm || psForm->type != WIDG_FORM) { ASSERT( FALSE, "widgAddSlider: Could not find parent form from formID" ); return FALSE; } } /* Create the slider structure */ if (!sliderCreate(&psSlider, psInit)) { return FALSE; } /* Add it to the form */ if (!formAddWidget(psForm, (WIDGET *)psSlider, (W_INIT *)psInit)) { return FALSE; } return TRUE; } /* Delete a widget from a form */ static BOOL widgDeleteFromForm(W_FORM *psForm, UDWORD id, W_CONTEXT *psContext) { WIDGET *psPrev = NULL, *psCurr, *psNext; W_TABFORM *psTabForm; UDWORD minor,major; W_MAJORTAB *psMajor; W_MINORTAB *psMinor; W_CONTEXT sNewContext; /* Clear the last hilite if necessary */ if ((psForm->psLastHiLite != NULL) && (psForm->psLastHiLite->id == id)) { widgHiLiteLost(psForm->psLastHiLite, psContext); psForm->psLastHiLite = NULL; } if (psForm->style & WFORM_TABBED) { psTabForm = (W_TABFORM *)psForm; ASSERT( psTabForm != NULL, "widgDeleteFromForm: Invalid form pointer" ); /* loop through all the tabs */ psMajor = psTabForm->asMajor; for(major=0; major < psTabForm->numMajor; major++) { psMinor = psMajor->asMinor; for(minor=0; minor < psMajor->numMinor; minor++) { if (psMinor->psWidgets && psMinor->psWidgets->id == id) { /* The widget is the first on this tab */ psNext = psMinor->psWidgets->psNext; widgRelease(psMinor->psWidgets); psMinor->psWidgets = psNext; return TRUE; } else { for(psCurr = psMinor->psWidgets; psCurr; psCurr = psCurr->psNext) { if (psCurr->id == id) { psPrev->psNext = psCurr->psNext; widgRelease(psCurr); return TRUE; } if (psCurr->type == WIDG_FORM) { /* Recurse down to other form */ sNewContext.psScreen = psContext->psScreen; sNewContext.psForm = (W_FORM *)psCurr; sNewContext.xOffset = psContext->xOffset - psCurr->x; sNewContext.yOffset = psContext->yOffset - psCurr->y; sNewContext.mx = psContext->mx - psCurr->x; sNewContext.my = psContext->my - psCurr->y; if (widgDeleteFromForm((W_FORM *)psCurr, id, &sNewContext)) { return TRUE; } } psPrev = psCurr; } } psMinor++; } psMajor++; } } else { ASSERT( psForm != NULL, "widgDeleteFromForm: Invalid form pointer" ); /* Delete from a normal form */ if (psForm->psWidgets && psForm->psWidgets->id == id) { /* The widget is the first in the list */ psNext = psForm->psWidgets->psNext; widgRelease(psForm->psWidgets); psForm->psWidgets = psNext; return TRUE; } else { /* Search the rest of the list */ for(psCurr = psForm->psWidgets; psCurr; psCurr = psCurr->psNext) { if (psCurr->id == id) { psPrev->psNext = psCurr->psNext; widgRelease(psCurr); return TRUE; } if (psCurr->type == WIDG_FORM) { /* Recurse down to other form */ sNewContext.psScreen = psContext->psScreen; sNewContext.psForm = (W_FORM *)psCurr; sNewContext.xOffset = psContext->xOffset - psCurr->x; sNewContext.yOffset = psContext->yOffset - psCurr->y; sNewContext.mx = psContext->mx - psCurr->x; sNewContext.my = psContext->my - psCurr->y; if (widgDeleteFromForm((W_FORM *)psCurr, id, &sNewContext)) { return TRUE; } } psPrev = psCurr; } } } return FALSE; } /* Delete a widget from the screen */ void widgDelete(W_SCREEN *psScreen, UDWORD id) { W_CONTEXT sContext; ASSERT( psScreen != NULL, "widgDelete: Invalid screen pointer" ); /* Clear the keyboard focus if necessary */ if ((psScreen->psFocus != NULL) && (psScreen->psFocus->id == id)) { screenClearFocus(psScreen); } /* Set up the initial context */ sContext.psScreen = psScreen; sContext.psForm = (W_FORM *)psScreen->psForm; sContext.xOffset = 0; sContext.yOffset = 0; sContext.mx = mouseX(); sContext.my = mouseY(); (void)widgDeleteFromForm((W_FORM *)psScreen->psForm, id, &sContext); } /* Initialise a form and all it's widgets */ static void widgStartForm(W_FORM *psForm) { WIDGET *psCurr; W_FORMGETALL sGetAll; /* Initialise this form */ formInitialise(psForm); /*Initialise the widgets on the form */ formInitGetAllWidgets(psForm, &sGetAll); psCurr = formGetAllWidgets(&sGetAll); while (psCurr != NULL) { switch (psCurr->type) { case WIDG_FORM: widgStartForm((W_FORM *)psCurr); break; case WIDG_LABEL: break; case WIDG_BUTTON: buttonInitialise((W_BUTTON *)psCurr); break; case WIDG_EDITBOX: editBoxInitialise((W_EDITBOX *)psCurr); break; case WIDG_BARGRAPH: break; case WIDG_SLIDER: sliderInitialise((W_SLIDER *)psCurr); break; default: ASSERT( FALSE,"widgStartScreen: Unknown widget type" ); break; } psCurr = psCurr->psNext; if (!psCurr) { /* Got to the end of this list see if there is another */ psCurr = formGetAllWidgets(&sGetAll); } } } /* Initialise the set of widgets that make up a screen */ void widgStartScreen(W_SCREEN *psScreen) { psScreen->psFocus = NULL; widgStartForm((W_FORM *)psScreen->psForm); } /* Clean up after a screen has been run */ void widgEndScreen(W_SCREEN *psScreen) { (void)psScreen; } /* Find a widget on a form from it's id number */ static WIDGET *widgFormGetFromID(W_FORM *psForm, UDWORD id) { WIDGET *psCurr, *psFound; W_FORMGETALL sGetAll; /* See if the form matches the ID */ if (psForm->id == id) { return (WIDGET *)psForm; } /* Now search the widgets on the form */ psFound = NULL; formInitGetAllWidgets(psForm,&sGetAll); psCurr = formGetAllWidgets(&sGetAll); while (psCurr && !psFound) { if (psCurr->id == id) { psFound = psCurr; } else if (psCurr->type == WIDG_FORM) { psFound = widgFormGetFromID((W_FORM *)psCurr, id); } psCurr = psCurr->psNext; if (!psCurr) { /* Got to the end of this list see if there is another */ psCurr = formGetAllWidgets(&sGetAll); } } return psFound; } /* Find a widget in a screen from its ID number */ WIDGET *widgGetFromID(W_SCREEN *psScreen, UDWORD id) { ASSERT( psScreen != NULL, "widgGetFromID: Invalid screen pointer" ); return widgFormGetFromID((W_FORM *)psScreen->psForm, id); } /* Hide a widget */ void widgHide(W_SCREEN *psScreen, UDWORD id) { WIDGET *psWidget; psWidget = widgGetFromID(psScreen, id); ASSERT( psWidget != NULL, "widgHide: couldn't find widget from id" ); if (psWidget) { psWidget->style |= WIDG_HIDDEN; } } /* Reveal a widget */ void widgReveal(W_SCREEN *psScreen, UDWORD id) { WIDGET *psWidget; psWidget = widgGetFromID(psScreen, id); ASSERT( psWidget != NULL, "widgReveal: couldn't find widget from id" ); if (psWidget) { psWidget->style &= ~WIDG_HIDDEN; } } /* Get the current position of a widget */ void widgGetPos(W_SCREEN *psScreen, UDWORD id, SWORD *pX, SWORD *pY) { WIDGET *psWidget; /* Find the widget */ psWidget = widgGetFromID(psScreen, id); if (psWidget != NULL) { *pX = psWidget->x; *pY = psWidget->y; } else { ASSERT( FALSE, "widgGetPos: Couldn't find widget from ID" ); *pX = 0; *pY = 0; } } /* Return the ID of the widget the mouse was over this frame */ UDWORD widgGetMouseOver(W_SCREEN *psScreen) { /* Don't actually need the screen parameter at the moment - but it might be handy if psMouseOverWidget needs to stop being a static and moves into the screen structure */ (void)psScreen; if (psMouseOverWidget == NULL) { return 0; } return psMouseOverWidget->id; } /* Return the user data for a widget */ void *widgGetUserData(W_SCREEN *psScreen, UDWORD id) { WIDGET *psWidget; psWidget = widgGetFromID(psScreen, id); if (psWidget) { return psWidget->pUserData; } return NULL; } /* Return the user data for a widget */ UDWORD widgGetUserData2(W_SCREEN *psScreen, UDWORD id) { WIDGET *psWidget; psWidget = widgGetFromID(psScreen, id); if (psWidget) { return psWidget->UserData; } return 0; } /* Set user data for a widget */ void widgSetUserData(W_SCREEN *psScreen, UDWORD id,void *UserData) { WIDGET *psWidget; psWidget = widgGetFromID(psScreen, id); if (psWidget) { psWidget->pUserData = UserData; } } /* Set user data for a widget */ void widgSetUserData2(W_SCREEN *psScreen, UDWORD id,UDWORD UserData) { WIDGET *psWidget; psWidget = widgGetFromID(psScreen, id); if (psWidget) { psWidget->UserData = UserData; } } /* Return the user data for the returned widget */ void *widgGetLastUserData(W_SCREEN *psScreen) { /* Don't actually need the screen parameter at the moment - but it might be handy if psRetWidget needs to stop being a static and moves into the screen structure */ (void)psScreen; if (psRetWidget) { return psRetWidget->pUserData; } return NULL; } /* Set tip string for a widget */ void widgSetTip( W_SCREEN *psScreen, UDWORD id, char *pTip ) { WIDGET *psWidget; psWidget = widgGetFromID(psScreen, id); if ( psWidget ) { switch (psWidget->type) { case WIDG_FORM: if (psWidget->style & WFORM_CLICKABLE) { ((W_CLICKFORM *) psWidget)->pTip = pTip; } else if (psWidget->style & WFORM_TABBED) { ASSERT( FALSE, "widgSetTip: tabbed forms do not have a tip" ); } else { ASSERT( FALSE, "widgSetTip: plain forms do not have a tip" ); } break; case WIDG_LABEL: ((W_LABEL *) psWidget)->pTip = pTip; break; case WIDG_BUTTON: ((W_BUTTON *) psWidget)->pTip = pTip; break; case WIDG_BARGRAPH: ((W_BARGRAPH *) psWidget)->pTip = pTip; break; case WIDG_SLIDER: ((W_SLIDER *) psWidget)->pTip = pTip; break; case WIDG_EDITBOX: ASSERT( FALSE, "widgSetTip: edit boxes do not have a tip" ); break; default: ASSERT( FALSE,"widgSetTip: Unknown widget type" ); break; } } } /* Return which key was used to press the last returned widget */ UDWORD widgGetButtonKey(W_SCREEN *psScreen) { /* Don't actually need the screen parameter at the moment - but it might be handy if released needs to stop being a static and moves into the screen structure */ (void)psScreen; return released; } /* Get a button or clickable form's state */ UDWORD widgGetButtonState(W_SCREEN *psScreen, UDWORD id) { WIDGET *psWidget; /* Get the button */ psWidget = widgGetFromID(psScreen, id); if (psWidget == NULL) { ASSERT( FALSE, "widgGetButtonState: Couldn't find button/click form from ID" ); } else if (psWidget->type == WIDG_BUTTON) { return buttonGetState((W_BUTTON *)psWidget); } else if ((psWidget->type == WIDG_FORM) && (psWidget->style & WFORM_CLICKABLE)) { return formGetClickState((W_CLICKFORM *)psWidget); } else { ASSERT( FALSE, "widgGetButtonState: Couldn't find button/click form from ID" ); } return 0; } void widgSetButtonFlash(W_SCREEN *psScreen, UDWORD id) { WIDGET *psWidget; /* Get the button */ psWidget = widgGetFromID(psScreen, id); if (psWidget == NULL) { ASSERT( FALSE, "widgSetButtonFlash: Couldn't find button/click form from ID" ); } else if (psWidget->type == WIDG_BUTTON) { buttonSetFlash((W_BUTTON *)psWidget); } else if ((psWidget->type == WIDG_FORM) && (psWidget->style & WFORM_CLICKABLE)) { formSetFlash((W_FORM *)psWidget); } else if ((psWidget->type == WIDG_EDITBOX)) { // editBoxSetState((W_EDITBOX *)psWidget, state); } else { ASSERT( FALSE, "widgSetButtonFlash: Couldn't find button/click form from ID" ); } } void widgClearButtonFlash(W_SCREEN *psScreen, UDWORD id) { WIDGET *psWidget; /* Get the button */ psWidget = widgGetFromID(psScreen, id); if (psWidget == NULL) { ASSERT( FALSE, "widgSetButtonFlash: Couldn't find button/click form from ID" ); } else if (psWidget->type == WIDG_BUTTON) { buttonClearFlash((W_BUTTON *)psWidget); } else if ((psWidget->type == WIDG_FORM) && (psWidget->style & WFORM_CLICKABLE)) { formClearFlash((W_FORM *)psWidget); } else if ((psWidget->type == WIDG_EDITBOX)) { } else { ASSERT( FALSE, "widgClearButtonFlash: Couldn't find button/click form from ID" ); } } /* Set a button or clickable form's state */ void widgSetButtonState(W_SCREEN *psScreen, UDWORD id, UDWORD state) { WIDGET *psWidget; /* Get the button */ psWidget = widgGetFromID(psScreen, id); if (psWidget == NULL) { ASSERT( FALSE, "widgSetButtonState: Couldn't find button/click form from ID" ); } else if (psWidget->type == WIDG_BUTTON) { buttonSetState((W_BUTTON *)psWidget, state); } else if ((psWidget->type == WIDG_FORM) && (psWidget->style & WFORM_CLICKABLE)) { formSetClickState((W_CLICKFORM *)psWidget, state); } else if ((psWidget->type == WIDG_EDITBOX)) { editBoxSetState((W_EDITBOX *)psWidget, state); } else { ASSERT( FALSE, "widgSetButtonState: Couldn't find button/click form from ID" ); } } /* Return a pointer to a buffer containing the current string of a widget. * NOTE: The string must be copied out of the buffer */ char *widgGetString(W_SCREEN *psScreen, UDWORD id) { WIDGET *psWidget; ASSERT( psScreen != NULL, "widgGetString: Invalid screen pointer" ); /* Get the widget */ psWidget = widgGetFromID(psScreen, id); if (psWidget != NULL) { switch (psWidget->type) { case WIDG_FORM: ASSERT( FALSE, "widgGetString: Forms do not have a string" ); aStringRetBuffer[0]=0; break; case WIDG_LABEL: strcpy(aStringRetBuffer, ((W_LABEL *)psWidget)->aText); break; case WIDG_BUTTON: if (((W_BUTTON *)psWidget)->pText) { strcpy(aStringRetBuffer, ((W_BUTTON *)psWidget)->pText); } else { aStringRetBuffer[0]=0; } break; case WIDG_EDITBOX: strcpy(aStringRetBuffer, ((W_EDITBOX *)psWidget)->aText); break; case WIDG_BARGRAPH: ASSERT( FALSE, "widgGetString: Bar Graphs do not have a string" ); aStringRetBuffer[0]=0; break; case WIDG_SLIDER: ASSERT( FALSE, "widgGetString: Sliders do not have a string" ); aStringRetBuffer[0]=0; break; default: ASSERT( FALSE,"widgGetString: Unknown widget type" ); aStringRetBuffer[0]=0; break; } } else { ASSERT( FALSE, "widgGetString: couldn't get widget from id" ); aStringRetBuffer[0]=0; } return aStringRetBuffer; } /* Set the text in a widget */ void widgSetString(W_SCREEN *psScreen, UDWORD id, const char *pText) { WIDGET *psWidget; ASSERT( psScreen != NULL, "widgSetString: Invalid screen pointer" ); /* Get the widget */ psWidget = widgGetFromID(psScreen, id); if (psWidget != NULL) { switch (psWidget->type) { case WIDG_FORM: ASSERT( FALSE, "widgSetString: forms do not have a string" ); break; case WIDG_LABEL: widgCopyString(((W_LABEL *)psWidget)->aText, pText); break; case WIDG_BUTTON: #if W_USE_STRHEAP if (((W_BUTTON *)psWidget)->pText) { widgCopyString(((W_BUTTON *)psWidget)->pText, pText); } else { widgAllocCopyString(&((W_BUTTON *)psWidget)->pText, pText); } #else ((W_BUTTON *)psWidget)->pText = pText; #endif break; case WIDG_EDITBOX: if (psScreen->psFocus == psWidget) { screenClearFocus(psScreen); } editBoxSetString((W_EDITBOX *)psWidget, pText); break; case WIDG_BARGRAPH: ASSERT( FALSE, "widgGetString: Bar graphs do not have a string" ); break; case WIDG_SLIDER: ASSERT( FALSE, "widgGetString: Sliders do not have a string" ); break; default: ASSERT( FALSE,"widgSetString: Unknown widget type" ); break; } } else { ASSERT( FALSE, "widgSetString: couldn't get widget from id" ); } } /* Call any callbacks for the widgets on a form */ static void widgProcessCallbacks(W_CONTEXT *psContext) { WIDGET *psCurr; W_CONTEXT sFormContext, sWidgContext; SDWORD xOrigin, yOrigin; W_FORMGETALL sFormCtl; /* Initialise the form context */ sFormContext.psScreen = psContext->psScreen; /* Initialise widget context */ formGetOrigin(psContext->psForm, &xOrigin, &yOrigin); sWidgContext.psScreen = psContext->psScreen; sWidgContext.psForm = psContext->psForm; sWidgContext.mx = psContext->mx - xOrigin; sWidgContext.my = psContext->my - yOrigin; sWidgContext.xOffset = psContext->xOffset + xOrigin; sWidgContext.yOffset = psContext->yOffset + yOrigin; /* Go through all the widgets on the form */ formInitGetAllWidgets(psContext->psForm, &sFormCtl); psCurr = formGetAllWidgets(&sFormCtl); while (psCurr) { for(;psCurr; psCurr = psCurr->psNext) { /* Skip any hidden widgets */ /* Not sure if we want to skip hidden widgets or not .... if (psCurr->style & WIDG_HIDDEN) { continue; }*/ /* Call the callback */ if (psCurr->callback) { psCurr->callback(psCurr, &sWidgContext); } /* and then recurse */ if (psCurr->type == WIDG_FORM) { sFormContext.psForm = (W_FORM *)psCurr; sFormContext.mx = sWidgContext.mx - psCurr->x; sFormContext.my = sWidgContext.my - psCurr->y; sFormContext.xOffset = sWidgContext.xOffset + psCurr->x; sFormContext.yOffset = sWidgContext.yOffset + psCurr->y; widgProcessCallbacks(&sFormContext); } } /* See if the form has any more widgets on it */ psCurr = formGetAllWidgets(&sFormCtl); } } /* Process all the widgets on a form. * mx and my are the coords of the mouse relative to the form origin. */ static void widgProcessForm(W_CONTEXT *psContext) { WIDGET *psCurr, *psOver; SDWORD mx,my, omx,omy, xOffset,yOffset, xOrigin,yOrigin; W_FORM *psForm; W_CONTEXT sFormContext, sWidgContext; /* Note current form */ psForm = psContext->psForm; // if(psForm->disableChildren == TRUE) { // return; // } /* Note the current mouse position */ mx = psContext->mx; my = psContext->my; /* Note the current offset */ xOffset = psContext->xOffset; yOffset = psContext->yOffset; /* Initialise the form context */ sFormContext.psScreen = psContext->psScreen; /* Initialise widget context */ formGetOrigin(psForm, &xOrigin, &yOrigin); sWidgContext.psScreen = psContext->psScreen; sWidgContext.psForm = psForm; sWidgContext.mx = mx - xOrigin; sWidgContext.my = my - yOrigin; sWidgContext.xOffset = xOffset + xOrigin; sWidgContext.yOffset = yOffset + yOrigin; /* Process the form's widgets */ psOver = NULL; for(psCurr = formGetWidgets(psForm); psCurr; psCurr = psCurr->psNext) { /* Skip any hidden widgets */ if (psCurr->style & WIDG_HIDDEN) { continue; } if (psCurr->type == WIDG_FORM) { /* Found a sub form, so set up the context */ sFormContext.psForm = (W_FORM *)psCurr; sFormContext.mx = mx - psCurr->x - xOrigin; sFormContext.my = my - psCurr->y - yOrigin; sFormContext.xOffset = xOffset + psCurr->x + xOrigin; sFormContext.yOffset = yOffset + psCurr->y + yOrigin; /* Process it */ widgProcessForm(&sFormContext); } else { /* Run the widget */ widgRun(psCurr, &sWidgContext); } } /* Now check for mouse clicks */ omx = mx - xOrigin; omy = my - yOrigin; if (mx >= 0 && mx <= psForm->width && my >= 0 && my <= psForm->height) { /* Update for the origin */ /* Mouse is over the form - is it over any of the widgets */ for(psCurr = formGetWidgets(psForm); psCurr; psCurr = psCurr->psNext) { /* Skip any hidden widgets */ if (psCurr->style & WIDG_HIDDEN) { continue; } if (omx >= psCurr->x && omy >= psCurr->y && omx <= psCurr->x + psCurr->width && omy <= psCurr->y + psCurr->height) { /* Note the widget the mouse is over */ if (!psMouseOverWidget) { psMouseOverWidget = (WIDGET *)psCurr; } psOver = psCurr; /* Don't check the widgets if it is a clickable form */ if (!(psForm->style & WFORM_CLICKABLE)) { if (pressed != WKEY_NONE && psCurr->type != WIDG_FORM) { /* Tell the widget it has been clicked */ widgClicked(psCurr, pressed, &sWidgContext); } if (released != WKEY_NONE && psCurr->type != WIDG_FORM) { /* Tell the widget the mouse button has gone up */ widgReleased(psCurr, released, &sWidgContext); } } } } /* Note that the mouse is over this form */ if (!psMouseOverWidget) { psMouseOverWidget = (WIDGET *)psForm; } /* Only send the Clicked or Released messages if a widget didn't get the message */ if (pressed != WKEY_NONE && (psOver == NULL || (psForm->style & WFORM_CLICKABLE))) { /* Tell the form it has been clicked */ widgClicked((WIDGET *)psForm, pressed, psContext); } if (released != WKEY_NONE && (psOver == NULL || (psForm->style & WFORM_CLICKABLE))) { /* Tell the form the mouse button has gone up */ widgReleased((WIDGET *)psForm, released, psContext); } } /* See if the mouse has moved onto or off a widget */ if (psForm->psLastHiLite != psOver) { if (psOver != NULL) { widgHiLite(psOver, &sWidgContext); } if (psForm->psLastHiLite != NULL) { widgHiLiteLost(psForm->psLastHiLite, &sWidgContext); } psForm->psLastHiLite = psOver; } /* Run this form */ widgRun((WIDGET *)psForm, psContext); } /* Execute a set of widgets for one cycle. * Return the id of the widget that was activated, or 0 for none. */ UDWORD widgRunScreen(W_SCREEN *psScreen) { W_CONTEXT sContext; UDWORD returnID; psRetWidget = NULL; // Note which keys have been pressed pressed = WKEY_NONE; if(getWidgetsStatus()) { if (mousePressed(MOUSE_LMB)) { pressed = WKEY_PRIMARY; } else if (mousePressed(MOUSE_RMB)) { pressed = WKEY_SECONDARY; } released = WKEY_NONE; if (mouseReleased(MOUSE_LMB)) { released = WKEY_PRIMARY; } else if (mouseReleased(MOUSE_RMB)) { released = WKEY_SECONDARY; } } /* Initialise the context */ sContext.psScreen = psScreen; sContext.psForm = (W_FORM *)psScreen->psForm; sContext.xOffset = 0; sContext.yOffset = 0; sContext.mx = mouseX(); sContext.my = mouseY(); psMouseOverWidget = NULL; /* Process the screen's widgets */ widgProcessForm(&sContext); /* Process any user callback functions */ widgProcessCallbacks(&sContext); /* Return the ID of a pressed button or finished edit box if any */ if (psRetWidget) { returnID = psRetWidget->id; } else { returnID = 0; } return returnID; } /* Set the id number for widgRunScreen to return */ void widgSetReturn(WIDGET *psWidget) { psRetWidget = psWidget; } /* Display the widgets on a form */ static void widgDisplayForm(W_FORM *psForm, UDWORD xOffset, UDWORD yOffset) { WIDGET *psCurr = NULL; SDWORD xOrigin = 0, yOrigin = 0; /* Display the form */ psForm->display( (WIDGET *)psForm, xOffset, yOffset, psForm->aColours ); if(psForm->disableChildren == TRUE) { return; } /* Update the offset from the current form's position */ formGetOrigin(psForm, &xOrigin, &yOrigin); xOffset += psForm->x + xOrigin; yOffset += psForm->y + yOrigin; /* If this is a clickable form, the widgets on it have to move when it's down */ if(!(psForm->style & WFORM_NOCLICKMOVE)) { if ((psForm->style & WFORM_CLICKABLE) && (((W_CLICKFORM *)psForm)->state & (WCLICK_DOWN | WCLICK_LOCKED | WCLICK_CLICKLOCK))) { xOffset += 1; yOffset += 1; } } /* Display the widgets on the form */ for(psCurr = formGetWidgets(psForm); psCurr; psCurr = psCurr->psNext) { /* Skip any hidden widgets */ if (psCurr->style & WIDG_HIDDEN) { continue; } if (psCurr->type == WIDG_FORM) { widgDisplayForm((W_FORM *)psCurr, xOffset, yOffset); } else { psCurr->display(psCurr, xOffset, yOffset, psForm->aColours); } } } /* Display the screen's widgets in their current state * (Call after calling widgRunScreen, this allows the input * processing to be seperated from the display of the widgets). */ void widgDisplayScreen(W_SCREEN *psScreen) { /* Display the widgets */ widgDisplayForm((W_FORM *)psScreen->psForm, 0,0); /* Display the tool tip if there is one */ tipDisplay(); } /* Set the keyboard focus for the screen */ void screenSetFocus(W_SCREEN *psScreen, WIDGET *psWidget) { if (psScreen->psFocus != NULL) { widgFocusLost(psScreen->psFocus); } psScreen->psFocus = psWidget; } /* Clear the keyboard focus */ void screenClearFocus(W_SCREEN *psScreen) { if (psScreen->psFocus != NULL) { widgFocusLost(psScreen->psFocus); psScreen->psFocus = NULL; } } /* Call the correct function for loss of focus */ void widgFocusLost(WIDGET *psWidget) { switch (psWidget->type) { case WIDG_FORM: break; case WIDG_LABEL: break; case WIDG_BUTTON: break; case WIDG_EDITBOX: editBoxFocusLost((W_EDITBOX *)psWidget); break; case WIDG_BARGRAPH: break; case WIDG_SLIDER: break; default: ASSERT( FALSE,"widgFocusLost: Unknown widget type" ); break; } } /* Call the correct function for mouse over */ void widgHiLite(WIDGET *psWidget, W_CONTEXT *psContext) { (void)psContext; switch (psWidget->type) { case WIDG_FORM: formHiLite((W_FORM *)psWidget, psContext); break; case WIDG_LABEL: labelHiLite((W_LABEL *)psWidget, psContext); break; case WIDG_BUTTON: buttonHiLite((W_BUTTON *)psWidget, psContext); break; case WIDG_EDITBOX: editBoxHiLite((W_EDITBOX *)psWidget); break; case WIDG_BARGRAPH: barGraphHiLite((W_BARGRAPH *)psWidget, psContext); break; case WIDG_SLIDER: sliderHiLite((W_SLIDER *)psWidget); break; default: ASSERT( FALSE,"widgHiLite: Unknown widget type" ); break; } } /* Call the correct function for mouse moving off */ void widgHiLiteLost(WIDGET *psWidget, W_CONTEXT *psContext) { (void)psContext; switch (psWidget->type) { case WIDG_FORM: formHiLiteLost((W_FORM *)psWidget, psContext); break; case WIDG_LABEL: labelHiLiteLost((W_LABEL *)psWidget); break; case WIDG_BUTTON: buttonHiLiteLost((W_BUTTON *)psWidget); break; case WIDG_EDITBOX: editBoxHiLiteLost((W_EDITBOX *)psWidget); break; case WIDG_BARGRAPH: barGraphHiLiteLost((W_BARGRAPH *)psWidget); break; case WIDG_SLIDER: sliderHiLiteLost((W_SLIDER *)psWidget); break; default: ASSERT( FALSE,"widgHiLiteLost: Unknown widget type" ); break; } } /* Call the correct function for mouse pressed */ static void widgClicked(WIDGET *psWidget, UDWORD key, W_CONTEXT *psContext) { switch (psWidget->type) { case WIDG_FORM: formClicked((W_FORM *)psWidget, key); break; case WIDG_LABEL: break; case WIDG_BUTTON: buttonClicked((W_BUTTON *)psWidget, key); break; case WIDG_EDITBOX: editBoxClicked((W_EDITBOX *)psWidget, psContext); break; case WIDG_BARGRAPH: break; case WIDG_SLIDER: sliderClicked((W_SLIDER *)psWidget, psContext); break; default: ASSERT( FALSE,"widgClicked: Unknown widget type" ); break; } } /* Call the correct function for mouse released */ static void widgReleased(WIDGET *psWidget, UDWORD key, W_CONTEXT *psContext) { switch (psWidget->type) { case WIDG_FORM: formReleased((W_FORM *)psWidget, key, psContext); break; case WIDG_LABEL: break; case WIDG_BUTTON: buttonReleased((W_BUTTON *)psWidget, key); break; case WIDG_EDITBOX: editBoxReleased((W_EDITBOX *)psWidget); break; case WIDG_BARGRAPH: break; case WIDG_SLIDER: sliderReleased((W_SLIDER *)psWidget); break; default: ASSERT( FALSE,"widgReleased: Unknown widget type" ); break; } } /* Call the correct function to run a widget */ static void widgRun(WIDGET *psWidget, W_CONTEXT *psContext) { switch (psWidget->type) { case WIDG_FORM: formRun((W_FORM *)psWidget, psContext); break; case WIDG_LABEL: break; case WIDG_BUTTON: buttonRun((W_BUTTON *)psWidget); break; case WIDG_EDITBOX: editBoxRun((W_EDITBOX *)psWidget, psContext); break; case WIDG_BARGRAPH: break; case WIDG_SLIDER: sliderRun((W_SLIDER *)psWidget, psContext); break; default: ASSERT( FALSE,"widgRun: Unknown widget type" ); break; } } void WidgSetAudio(WIDGET_AUDIOCALLBACK Callback,SWORD HilightID,SWORD ClickedID) { AudioCallback = Callback; HilightAudioID = HilightID; ClickedAudioID = ClickedID; } WIDGET_AUDIOCALLBACK WidgGetAudioCallback(void) { return AudioCallback; } SWORD WidgGetHilightAudioID(void) { return HilightAudioID; } SWORD WidgGetClickedAudioID(void) { return ClickedAudioID; } void setWidgetsStatus(BOOL var) { bWidgetsActive = var; } BOOL getWidgetsStatus( void ) { return(bWidgetsActive); }