510 lines
10 KiB
C
510 lines
10 KiB
C
|
#include <cdk_int.h>
|
||
|
|
||
|
#ifdef HAVE_SETLOCALE
|
||
|
#include <locale.h>
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* $Author: tom $
|
||
|
* $Date: 2016/12/04 15:41:50 $
|
||
|
* $Revision: 1.91 $
|
||
|
*/
|
||
|
|
||
|
typedef struct _all_screens
|
||
|
{
|
||
|
struct _all_screens *link;
|
||
|
CDKSCREEN *screen;
|
||
|
}
|
||
|
ALL_SCREENS;
|
||
|
|
||
|
static ALL_SCREENS *all_screens;
|
||
|
|
||
|
typedef struct _all_objects
|
||
|
{
|
||
|
struct _all_objects *link;
|
||
|
CDKOBJS *object;
|
||
|
}
|
||
|
ALL_OBJECTS;
|
||
|
|
||
|
static ALL_OBJECTS *all_objects;
|
||
|
|
||
|
static boolean validObjType (CDKOBJS *obj, EObjectType type)
|
||
|
{
|
||
|
bool valid = FALSE;
|
||
|
|
||
|
if (obj != 0 && ObjTypeOf (obj) == type)
|
||
|
{
|
||
|
switch (type)
|
||
|
{
|
||
|
case vALPHALIST:
|
||
|
case vBUTTON:
|
||
|
case vBUTTONBOX:
|
||
|
case vCALENDAR:
|
||
|
case vDIALOG:
|
||
|
case vDSCALE:
|
||
|
case vENTRY:
|
||
|
case vFSCALE:
|
||
|
case vFSELECT:
|
||
|
case vFSLIDER:
|
||
|
case vGRAPH:
|
||
|
case vHISTOGRAM:
|
||
|
case vITEMLIST:
|
||
|
case vLABEL:
|
||
|
case vMARQUEE:
|
||
|
case vMATRIX:
|
||
|
case vMENTRY:
|
||
|
case vMENU:
|
||
|
case vRADIO:
|
||
|
case vSCALE:
|
||
|
case vSCROLL:
|
||
|
case vSELECTION:
|
||
|
case vSLIDER:
|
||
|
case vSWINDOW:
|
||
|
case vTEMPLATE:
|
||
|
case vUSCALE:
|
||
|
case vUSLIDER:
|
||
|
case vVIEWER:
|
||
|
valid = TRUE;
|
||
|
break;
|
||
|
case vTRAVERSE: /* not really an object */
|
||
|
case vNULL:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return valid;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Set indices so the screen and object point to each other.
|
||
|
*/
|
||
|
static void setScreenIndex (CDKSCREEN *screen, int number, CDKOBJS *obj)
|
||
|
{
|
||
|
(obj)->screenIndex = number;
|
||
|
(obj)->screen = screen;
|
||
|
screen->object[number] = obj;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Returns true if we have done a "new" on this object but no "destroy"
|
||
|
*/
|
||
|
bool validCDKObject (CDKOBJS *obj)
|
||
|
{
|
||
|
bool result = FALSE;
|
||
|
if (obj != 0)
|
||
|
{
|
||
|
ALL_OBJECTS *ptr;
|
||
|
|
||
|
for (ptr = all_objects; ptr != 0; ptr = ptr->link)
|
||
|
{
|
||
|
if (ptr->object == obj)
|
||
|
{
|
||
|
result = validObjType (obj, ObjTypeOf (obj));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Create a new object beginning with a CDKOBJS struct. The whole object is
|
||
|
* initialized to zeroes except for special cases which have known values.
|
||
|
*/
|
||
|
void *_newCDKObject (unsigned size, const CDKFUNCS * funcs)
|
||
|
{
|
||
|
ALL_OBJECTS *item;
|
||
|
CDKOBJS *result = 0;
|
||
|
if ((item = typeCalloc (ALL_OBJECTS)) != 0)
|
||
|
{
|
||
|
if ((result = (CDKOBJS *)calloc (1, size)) != 0)
|
||
|
{
|
||
|
result->fn = funcs;
|
||
|
result->hasFocus = TRUE;
|
||
|
result->isVisible = TRUE;
|
||
|
|
||
|
item->link = all_objects;
|
||
|
item->object = result;
|
||
|
all_objects = item;
|
||
|
|
||
|
/* set default line-drawing characters */
|
||
|
result->ULChar = ACS_ULCORNER;
|
||
|
result->URChar = ACS_URCORNER;
|
||
|
result->LLChar = ACS_LLCORNER;
|
||
|
result->LRChar = ACS_LRCORNER;
|
||
|
result->HZChar = ACS_HLINE;
|
||
|
result->VTChar = ACS_VLINE;
|
||
|
result->BXAttr = A_NORMAL;
|
||
|
|
||
|
/* set default exit-types */
|
||
|
result->exitType = vNEVER_ACTIVATED;
|
||
|
result->earlyExit = vNEVER_ACTIVATED;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
free (item);
|
||
|
}
|
||
|
}
|
||
|
return (void *)result;
|
||
|
}
|
||
|
|
||
|
void _destroyCDKObject (CDKOBJS *obj)
|
||
|
{
|
||
|
if (validCDKObject (obj))
|
||
|
{
|
||
|
ALL_OBJECTS *p, *q;
|
||
|
|
||
|
for (p = all_objects, q = 0; p != 0; q = p, p = p->link)
|
||
|
{
|
||
|
if (p->object == obj)
|
||
|
{
|
||
|
/* delink it first, to avoid problems with recursion */
|
||
|
if (q != 0)
|
||
|
q->link = p->link;
|
||
|
else
|
||
|
all_objects = p->link;
|
||
|
|
||
|
MethodPtr (obj, destroyObj) (obj);
|
||
|
free (obj);
|
||
|
free (p);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This creates a new CDK screen.
|
||
|
*/
|
||
|
CDKSCREEN *initCDKScreen (WINDOW *window)
|
||
|
{
|
||
|
ALL_SCREENS *item;
|
||
|
CDKSCREEN *screen = 0;
|
||
|
|
||
|
/* initialization, for the first time */
|
||
|
if (all_screens == 0 || stdscr == 0 || window == 0)
|
||
|
{
|
||
|
/* Set up basic curses settings. */
|
||
|
#ifdef HAVE_SETLOCALE
|
||
|
setlocale (LC_ALL, "");
|
||
|
#endif
|
||
|
/* Initialize curses after setting the locale, since curses depends
|
||
|
* on having a correct locale to reflect the terminal's encoding.
|
||
|
*/
|
||
|
if (stdscr == 0 || window == 0)
|
||
|
{
|
||
|
window = initscr ();
|
||
|
}
|
||
|
noecho ();
|
||
|
cbreak ();
|
||
|
}
|
||
|
|
||
|
if ((item = typeMalloc (ALL_SCREENS)) != 0)
|
||
|
{
|
||
|
if ((screen = typeCalloc (CDKSCREEN)) != 0)
|
||
|
{
|
||
|
item->link = all_screens;
|
||
|
item->screen = screen;
|
||
|
all_screens = item;
|
||
|
|
||
|
/* Initialize the CDKSCREEN pointer. */
|
||
|
screen->objectCount = 0;
|
||
|
screen->objectLimit = 2;
|
||
|
screen->object = typeMallocN (CDKOBJS *, screen->objectLimit);
|
||
|
screen->window = window;
|
||
|
|
||
|
/* OK, we are done. */
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
free (item);
|
||
|
}
|
||
|
}
|
||
|
return (screen);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This registers a CDK object with a screen.
|
||
|
*/
|
||
|
void registerCDKObject (CDKSCREEN *screen, EObjectType cdktype, void *object)
|
||
|
{
|
||
|
CDKOBJS *obj = (CDKOBJS *)object;
|
||
|
|
||
|
if (screen->objectCount + 1 >= screen->objectLimit)
|
||
|
{
|
||
|
screen->objectLimit += 2;
|
||
|
screen->objectLimit *= 2;
|
||
|
screen->object = typeReallocN (CDKOBJS *, screen->object, screen->objectLimit);
|
||
|
}
|
||
|
if (validObjType (obj, cdktype))
|
||
|
{
|
||
|
setScreenIndex (screen, screen->objectCount++, obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This registers a CDK object with a screen.
|
||
|
*/
|
||
|
void reRegisterCDKObject (EObjectType cdktype, void *object)
|
||
|
{
|
||
|
CDKOBJS *obj = (CDKOBJS *)object;
|
||
|
|
||
|
registerCDKObject (obj->screen, cdktype, object);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This removes an object from the CDK screen.
|
||
|
*/
|
||
|
void unregisterCDKObject (EObjectType cdktype, void *object)
|
||
|
{
|
||
|
CDKOBJS *obj = (CDKOBJS *)object;
|
||
|
|
||
|
if (validObjType (obj, cdktype) && obj->screenIndex >= 0)
|
||
|
{
|
||
|
CDKSCREEN *screen = (obj)->screen;
|
||
|
|
||
|
if (screen != 0)
|
||
|
{
|
||
|
int Index = (obj)->screenIndex;
|
||
|
int x;
|
||
|
|
||
|
obj->screenIndex = -1;
|
||
|
|
||
|
/*
|
||
|
* Resequence the objects.
|
||
|
*/
|
||
|
for (x = Index; x < screen->objectCount - 1; x++)
|
||
|
{
|
||
|
setScreenIndex (screen, x, screen->object[x + 1]);
|
||
|
}
|
||
|
|
||
|
if (screen->objectCount <= 1)
|
||
|
{
|
||
|
/* if no more objects, remove the array */
|
||
|
freeAndNull (screen->object);
|
||
|
screen->objectCount = 0;
|
||
|
screen->objectLimit = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Reduce the list by one object. */
|
||
|
screen->object[screen->objectCount--] = 0;
|
||
|
|
||
|
/*
|
||
|
* Update the object-focus
|
||
|
*/
|
||
|
if (screen->objectFocus == Index)
|
||
|
{
|
||
|
screen->objectFocus--;
|
||
|
(void)setCDKFocusNext (screen);
|
||
|
}
|
||
|
else if (screen->objectFocus > Index)
|
||
|
{
|
||
|
screen->objectFocus--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define validIndex(screen, n) ((n) >= 0 && (n) < (screen)->objectCount)
|
||
|
|
||
|
static void swapCDKIndices (CDKSCREEN *screen, int n1, int n2)
|
||
|
{
|
||
|
if (n1 != n2 && validIndex (screen, n1) && validIndex (screen, n2))
|
||
|
{
|
||
|
CDKOBJS *o1 = screen->object[n1];
|
||
|
CDKOBJS *o2 = screen->object[n2];
|
||
|
setScreenIndex (screen, n1, o2);
|
||
|
setScreenIndex (screen, n2, o1);
|
||
|
|
||
|
if (screen->objectFocus == n1)
|
||
|
screen->objectFocus = n2;
|
||
|
else if (screen->objectFocus == n2)
|
||
|
screen->objectFocus = n1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This 'brings' a CDK object to the top of the stack.
|
||
|
*/
|
||
|
void raiseCDKObject (EObjectType cdktype, void *object)
|
||
|
{
|
||
|
CDKOBJS *obj = (CDKOBJS *)object;
|
||
|
|
||
|
if (validObjType (obj, cdktype))
|
||
|
{
|
||
|
CDKSCREEN *screen = obj->screen;
|
||
|
swapCDKIndices (screen, obj->screenIndex, screen->objectCount - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This 'lowers' an object.
|
||
|
*/
|
||
|
void lowerCDKObject (EObjectType cdktype, void *object)
|
||
|
{
|
||
|
CDKOBJS *obj = (CDKOBJS *)object;
|
||
|
|
||
|
if (validObjType (obj, cdktype))
|
||
|
{
|
||
|
CDKSCREEN *screen = obj->screen;
|
||
|
swapCDKIndices (screen, obj->screenIndex, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This calls refreshCDKScreen. (made consistent with widgets)
|
||
|
*/
|
||
|
void drawCDKScreen (CDKSCREEN *cdkscreen)
|
||
|
{
|
||
|
refreshCDKScreen (cdkscreen);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Refresh one CDK window.
|
||
|
* FIXME: this should be rewritten to use the panel library, so it would not
|
||
|
* be necessary to touch the window to ensure that it covers other windows.
|
||
|
*/
|
||
|
void refreshCDKWindow (WINDOW *win)
|
||
|
{
|
||
|
touchwin (win);
|
||
|
wrefresh (win);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This refreshes all the objects in the screen.
|
||
|
*/
|
||
|
void refreshCDKScreen (CDKSCREEN *cdkscreen)
|
||
|
{
|
||
|
int objectCount = cdkscreen->objectCount;
|
||
|
int x;
|
||
|
int focused = -1;
|
||
|
int visible = -1;
|
||
|
|
||
|
refreshCDKWindow (cdkscreen->window);
|
||
|
|
||
|
/* We erase all the invisible objects, then only
|
||
|
* draw it all back, so that the objects
|
||
|
* can overlap, and the visible ones will always
|
||
|
* be drawn after all the invisible ones are erased */
|
||
|
for (x = 0; x < objectCount; x++)
|
||
|
{
|
||
|
CDKOBJS *obj = cdkscreen->object[x];
|
||
|
|
||
|
if (validObjType (obj, ObjTypeOf (obj)))
|
||
|
{
|
||
|
if (obj->isVisible)
|
||
|
{
|
||
|
if (visible < 0)
|
||
|
visible = x;
|
||
|
if (obj->hasFocus && focused < 0)
|
||
|
focused = x;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
obj->fn->eraseObj (obj);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (x = 0; x < objectCount; x++)
|
||
|
{
|
||
|
CDKOBJS *obj = cdkscreen->object[x];
|
||
|
|
||
|
if (validObjType (obj, ObjTypeOf (obj)))
|
||
|
{
|
||
|
obj->hasFocus = (x == focused);
|
||
|
|
||
|
if (obj->isVisible)
|
||
|
{
|
||
|
obj->fn->drawObj (obj, obj->box);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This clears all the objects in the screen.
|
||
|
*/
|
||
|
void eraseCDKScreen (CDKSCREEN *cdkscreen)
|
||
|
{
|
||
|
int objectCount = cdkscreen->objectCount;
|
||
|
int x;
|
||
|
|
||
|
/* We just call the drawObject function. */
|
||
|
for (x = 0; x < objectCount; x++)
|
||
|
{
|
||
|
CDKOBJS *obj = cdkscreen->object[x];
|
||
|
|
||
|
if (validObjType (obj, ObjTypeOf (obj)))
|
||
|
{
|
||
|
obj->fn->eraseObj (obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Refresh the screen. */
|
||
|
wrefresh (cdkscreen->window);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Destroy all of the objects on a screen
|
||
|
*/
|
||
|
void destroyCDKScreenObjects (CDKSCREEN *cdkscreen)
|
||
|
{
|
||
|
int x;
|
||
|
|
||
|
for (x = 0; x < cdkscreen->objectCount; x++)
|
||
|
{
|
||
|
CDKOBJS *obj = cdkscreen->object[x];
|
||
|
int before = cdkscreen->objectCount;
|
||
|
|
||
|
if (validObjType (obj, ObjTypeOf (obj)))
|
||
|
{
|
||
|
MethodPtr (obj, eraseObj) (obj);
|
||
|
_destroyCDKObject (obj);
|
||
|
x -= (cdkscreen->objectCount - before);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This destroys a CDK screen.
|
||
|
*/
|
||
|
void destroyCDKScreen (CDKSCREEN *screen)
|
||
|
{
|
||
|
ALL_SCREENS *p, *q;
|
||
|
|
||
|
for (p = all_screens, q = 0; p != 0; q = p, p = p->link)
|
||
|
{
|
||
|
if (screen == p->screen)
|
||
|
{
|
||
|
if (q != 0)
|
||
|
q->link = p->link;
|
||
|
else
|
||
|
all_screens = p->link;
|
||
|
free (p);
|
||
|
free (screen);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This is added to remain consistent.
|
||
|
*/
|
||
|
void endCDK (void)
|
||
|
{
|
||
|
/* Turn echoing back on. */
|
||
|
echo ();
|
||
|
|
||
|
/* Turn off cbreak. */
|
||
|
nocbreak ();
|
||
|
|
||
|
/* End the curses windows. */
|
||
|
endwin ();
|
||
|
|
||
|
#ifdef HAVE_XCURSES
|
||
|
XCursesExit ();
|
||
|
#endif
|
||
|
}
|