Indentations break the feed.
[SquirrelJME.git] / nanocoat / lib / scritchui / scritchMenu.c
blob68ec5ca7f433ebe66d479245d3bac0543fa77f5d
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include <string.h>
12 #include "lib/scritchui/scritchuiTypes.h"
13 #include "lib/scritchui/core/core.h"
14 #include "sjme/alloc.h"
15 #include "lib/scritchui/core/coreGeneric.h"
17 sjme_errorCode sjme_scritchui_core_menuBarNew(
18 sjme_attrInNotNull sjme_scritchui inState,
19 sjme_attrInOutNotNull sjme_scritchui_uiMenuBar* outMenuBar)
21 if (inState == NULL || outMenuBar == NULL)
22 return SJME_ERROR_NULL_ARGUMENTS;
24 /* Use generic function. */
25 return sjme_scritchui_coreGeneric_commonNew(inState,
26 (sjme_scritchui_uiCommon*)outMenuBar,
27 sizeof(**outMenuBar),
28 SJME_SCRITCHUI_TYPE_MENU_BAR,
29 (sjme_scritchui_coreGeneric_commonNewImplFunc)
30 inState->impl->menuBarNew,
31 NULL);
34 sjme_errorCode sjme_scritchui_core_menuInsert(
35 sjme_attrInNotNull sjme_scritchui inState,
36 sjme_attrInNotNull sjme_scritchui_uiMenuKind intoMenu,
37 sjme_attrInPositive sjme_jint atIndex,
38 sjme_attrInNotNull sjme_scritchui_uiMenuKind childItem)
40 sjme_errorCode error;
41 sjme_list_sjme_scritchui_uiMenuKind* childList;
42 sjme_list_sjme_scritchui_uiMenuKind* newList;
43 sjme_scritchui_uiMenuHasChildren parentMenu;
44 sjme_scritchui_uiMenuHasParent childMenu;
45 sjme_jint i, o, n;
47 if (inState == NULL || intoMenu == NULL || childItem == NULL)
48 return SJME_ERROR_NULL_ARGUMENTS;
50 if (atIndex < 0)
51 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
53 /* Get parent information. */
54 parentMenu = NULL;
55 if (sjme_error_is(error = inState->intern->getMenuHasChildren(
56 inState, SJME_SUI_CAST_MENU_KIND(intoMenu),
57 &parentMenu)) ||
58 parentMenu == NULL)
59 return sjme_error_default(error);
61 /* Get child information. */
62 childMenu = NULL;
63 if (sjme_error_is(error = inState->intern->getMenuHasParent(
64 inState, SJME_SUI_CAST_MENU_KIND(childItem),
65 &childMenu)) ||
66 childMenu == NULL)
67 return sjme_error_default(error);
69 /* Already has a parent? */
70 if (childMenu->parent != NULL)
71 return SJME_ERROR_HAS_PARENT;
73 /* Out of bounds? */
74 childList = parentMenu->children;
75 n = parentMenu->numChildren;
76 if (atIndex > n)
77 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
79 /* Make sure child is not already in here. */
80 if (childList != NULL)
81 for (i = 0; i < n; i++)
82 if (SJME_SUI_CAST_MENU_KIND(childList->elements[i]) ==
83 SJME_SUI_CAST_MENU_KIND(childItem))
84 return SJME_ERROR_MEMBER_EXISTS;
86 /* Not implemented? Fail here so we go through all validity checks */
87 /* first. */
88 if (inState->impl->menuInsert == NULL)
89 return sjme_error_notImplemented(0);
91 /* Do we need to make a new list? */
92 if (childList == NULL || (n + 1) > childList->length)
94 /* Setup new list. */
95 newList = NULL;
96 if (sjme_error_is(error = sjme_list_alloc(inState->pool, n + 1,
97 &newList, sjme_scritchui_uiMenuKind, 0)) || newList == NULL)
98 return sjme_error_default(error);
100 /* Copy all items over? */
101 if (childList != NULL)
102 for (i = 0; i < n; i++)
103 newList->elements[i] = childList->elements[i];
105 /* Use this list. */
106 parentMenu->children = newList;
108 /* Free old list. */
109 if (childList != NULL)
111 if (sjme_error_is(error = sjme_alloc_free(childList)))
112 return sjme_error_default(error);
113 childList = NULL;
116 /* We are using this list now. */
117 childList = newList;
120 /* Move entries up. */
121 for (i = n - 1, o = n; i > atIndex; i--, o--)
122 childList->elements[o] = childList->elements[i];
123 childList->elements[atIndex] = childItem;
125 /* List is now this big. */
126 parentMenu->numChildren = n + 1;
128 /* Associate parent. */
129 childMenu->parent = SJME_SUI_CAST_MENU_KIND(intoMenu);
131 /* Recount index position. */
132 for (i = 0; i <= n; i++)
133 childList->elements[i]->index = i;
135 /* Forward to native implementation. */
136 return inState->impl->menuInsert(inState, intoMenu, atIndex, childItem);
139 sjme_errorCode sjme_scritchui_core_menuItemNew(
140 sjme_attrInNotNull sjme_scritchui inState,
141 sjme_attrInOutNotNull sjme_scritchui_uiMenuItem* outMenuItem)
143 sjme_scritchui_impl_initParamMenuItem init;
145 if (inState == NULL || outMenuItem == NULL)
146 return SJME_ERROR_NULL_ARGUMENTS;
148 /* Set whatever next menu ID there is. */
149 memset(&init, 0, sizeof(init));
150 init.opaqueId = ++inState->nextMenuItemId;
152 /* Use generic function. */
153 return sjme_scritchui_coreGeneric_commonNew(inState,
154 (sjme_scritchui_uiCommon*)outMenuItem,
155 sizeof(**outMenuItem),
156 SJME_SCRITCHUI_TYPE_MENU_ITEM,
157 (sjme_scritchui_coreGeneric_commonNewImplFunc)
158 inState->impl->menuItemNew,
159 &init);
162 sjme_errorCode sjme_scritchui_core_menuNew(
163 sjme_attrInNotNull sjme_scritchui inState,
164 sjme_attrInOutNotNull sjme_scritchui_uiMenu* outMenu)
166 if (inState == NULL || outMenu == NULL)
167 return SJME_ERROR_NULL_ARGUMENTS;
169 /* Use generic function. */
170 return sjme_scritchui_coreGeneric_commonNew(inState,
171 (sjme_scritchui_uiCommon*)outMenu,
172 sizeof(**outMenu),
173 SJME_SCRITCHUI_TYPE_MENU,
174 (sjme_scritchui_coreGeneric_commonNewImplFunc)
175 inState->impl->menuNew,
176 NULL);
179 sjme_errorCode sjme_scritchui_core_menuRemove(
180 sjme_attrInNotNull sjme_scritchui inState,
181 sjme_attrInNotNull sjme_scritchui_uiMenuKind fromMenu,
182 sjme_attrInPositive sjme_jint atIndex)
184 sjme_errorCode error;
185 sjme_scritchui_uiMenuHasChildren parentMenu;
186 sjme_scritchui_uiMenuHasParent childMenu;
187 sjme_list_sjme_scritchui_uiMenuKind* childList;
188 sjme_scritchui_uiMenuKind childAt;
189 sjme_jint i, o, n;
191 if (inState == NULL || fromMenu == NULL)
192 return SJME_ERROR_NULL_ARGUMENTS;
194 if (atIndex < 0)
195 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
197 /* Get parent information. */
198 parentMenu = NULL;
199 if (sjme_error_is(error = inState->intern->getMenuHasChildren(
200 inState, SJME_SUI_CAST_MENU_KIND(fromMenu),
201 &parentMenu)) ||
202 parentMenu == NULL)
203 return sjme_error_default(error);
205 /* Not implemented? */
206 if (inState->impl->menuRemove == NULL)
207 return sjme_error_notImplemented(0);
209 /* Empty? */
210 childList = parentMenu->children;
211 if (childList == NULL)
212 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
214 /* Limit to the actual number of children. */
215 n = parentMenu->numChildren;
217 /* Out of bounds? */
218 if (atIndex >= n)
219 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
221 /* Obtain the child menu. */
222 childAt = childList->elements[atIndex];
223 childMenu = NULL;
224 if (sjme_error_is(error = inState->intern->getMenuHasParent(
225 inState, SJME_SUI_CAST_MENU_KIND(childAt),
226 &childMenu)) || childMenu == NULL)
227 return sjme_error_default(error);
229 /* Nothing here? */
230 if (SJME_SUI_CAST_MENU_KIND(childAt) == NULL)
231 return SJME_ERROR_ILLEGAL_STATE;
233 /* Call native removal of item. */
234 if (sjme_error_is(error = inState->impl->menuRemove(inState,
235 fromMenu, atIndex)))
236 return sjme_error_default(error);
238 /* Disassociate. */
239 childMenu->parent = NULL;
241 /* Remove from list and move down everything. */
242 for (o = atIndex, i = atIndex + 1; i < n;)
243 childList->elements[o++] = childList->elements[i++];
244 childList->elements[n - 1] = NULL;
246 /* Count down. */
247 parentMenu->numChildren = n - 1;
249 /* Success! */
250 return SJME_ERROR_NONE;
253 sjme_errorCode sjme_scritchui_core_menuRemoveAll(
254 sjme_attrInNotNull sjme_scritchui inState,
255 sjme_attrInNotNull sjme_scritchui_uiMenuKind fromMenu)
257 sjme_errorCode error;
258 sjme_scritchui_uiMenuHasChildren parentMenu;
260 if (inState == NULL || fromMenu == NULL)
261 return SJME_ERROR_NULL_ARGUMENTS;
263 /* Get parent information. */
264 if (sjme_error_is(error = inState->intern->getMenuHasChildren(
265 inState, SJME_SUI_CAST_MENU_KIND(fromMenu),
266 &parentMenu)) || parentMenu == NULL)
267 return sjme_error_default(error);
269 /* Keep clearing out until nothing is left. */
270 while (parentMenu->numChildren > 0)
272 /* Double check, there should be an item here. */
273 if (SJME_SUI_CAST_MENU_KIND(parentMenu->children->elements[0]) == NULL)
274 return SJME_ERROR_ILLEGAL_STATE;
276 /* Remove it. */
277 if (sjme_error_is(error = inState->api->menuRemove(inState,
278 fromMenu, 0)))
279 return sjme_error_default(error);
282 /* Success! */
283 return SJME_ERROR_NONE;
286 sjme_errorCode sjme_scritchui_core_intern_getMenuHasChildren(
287 sjme_attrInNotNull sjme_scritchui inState,
288 sjme_attrInNotNull sjme_scritchui_uiMenuKind inMenuKind,
289 sjme_attrInOutNotNull sjme_scritchui_uiMenuHasChildren* outHasChildren)
291 sjme_scritchui_uiMenuHasChildren result;
293 if (inState == NULL || inMenuKind == NULL || outHasChildren == NULL)
294 return SJME_ERROR_NULL_ARGUMENTS;
296 /* Check. */
297 if (SJME_SUI_CAST_MENU_KIND(inMenuKind) == NULL)
298 return SJME_ERROR_INVALID_ARGUMENT;
300 /* Depends on the type. */
301 result = NULL;
302 switch (inMenuKind->common.type)
304 case SJME_SCRITCHUI_TYPE_MENU:
305 result = &((sjme_scritchui_uiMenu)inMenuKind)->children;
306 break;
308 case SJME_SCRITCHUI_TYPE_MENU_BAR:
309 result = &((sjme_scritchui_uiMenuBar)inMenuKind)->children;
310 break;
312 /* Invalid. */
313 default:
314 return SJME_ERROR_INVALID_ARGUMENT;
317 /* Success! */
318 *outHasChildren = result;
319 return SJME_ERROR_NONE;
322 sjme_errorCode sjme_scritchui_core_intern_getMenuHasParent(
323 sjme_attrInNotNull sjme_scritchui inState,
324 sjme_attrInNotNull sjme_scritchui_uiMenuKind inMenuKind,
325 sjme_attrInOutNotNull sjme_scritchui_uiMenuHasParent* outHasParent)
327 sjme_scritchui_uiMenuHasParent result;
329 if (inState == NULL || inMenuKind == NULL || outHasParent == NULL)
330 return SJME_ERROR_NULL_ARGUMENTS;
332 /* Check. */
333 if (SJME_SUI_CAST_MENU_KIND(inMenuKind) == NULL)
334 return SJME_ERROR_INVALID_ARGUMENT;
336 /* Depends on the type. */
337 result = NULL;
338 switch (inMenuKind->common.type)
340 case SJME_SCRITCHUI_TYPE_MENU:
341 result = &((sjme_scritchui_uiMenu)inMenuKind)->parent;
342 break;
344 case SJME_SCRITCHUI_TYPE_MENU_ITEM:
345 result = &((sjme_scritchui_uiMenuItem)inMenuKind)->parent;
346 break;
348 /* Invalid. */
349 default:
350 return SJME_ERROR_INVALID_ARGUMENT;
353 /* Success! */
354 *outHasParent = result;
355 return SJME_ERROR_NONE;
358 sjme_errorCode sjme_scritchui_intern_menuItemActivate(
359 sjme_attrInNotNull sjme_scritchui inState,
360 sjme_attrInNotNull sjme_scritchui_uiMenuKind atRover,
361 sjme_attrInNotNull sjme_scritchui_uiMenuKind itemActivated)
363 sjme_errorCode error;
364 sjme_scritchui_uiMenuHasParent parent;
365 sjme_scritchui_uiMenuBar menuBar;
366 sjme_scritchui_uiWindow window;
367 sjme_scritchui_listener_menuItemActivate* infoUser;
369 if (inState == NULL || atRover == NULL || itemActivated == NULL)
370 return SJME_ERROR_NULL_ARGUMENTS;
372 /* Check to see if this has a parent. */
373 parent = NULL;
374 if (sjme_error_is(error = inState->intern->getMenuHasParent(inState,
375 atRover, &parent)) || parent == NULL)
377 /* There is no parent possible, so we might be at the top level. */
378 if (error == SJME_ERROR_INVALID_ARGUMENT &&
379 atRover->common.type == SJME_SCRITCHUI_TYPE_MENU_BAR)
381 menuBar = (sjme_scritchui_uiMenuBar)atRover;
383 /* Activate menu item in the window. */
384 window = menuBar->window;
385 if (window != NULL)
387 /* Get callback. */
388 infoUser = &SJME_SCRITCHUI_LISTENER_USER(window,
389 menuItemActivate);
391 /* Execute if available. */
392 if (infoUser->callback != NULL)
393 return infoUser->callback(inState, window,
394 itemActivated);
397 /* Ignore otherwise. */
398 return SJME_ERROR_NONE;
401 /* Failed! */
402 return sjme_error_default(error);
405 /* It does, so if it has a parent, recurse up the chain! */
406 if (parent->parent != NULL)
407 return inState->intern->menuItemActivate(inState,
408 parent->parent, itemActivated);
410 /* It does not, so stop. */
411 return SJME_ERROR_NONE;
414 sjme_errorCode sjme_scritchui_intern_menuItemActivateById(
415 sjme_attrInNotNull sjme_scritchui inState,
416 sjme_attrInNotNull sjme_scritchui_uiWindow inWindow,
417 sjme_attrInNotNull sjme_scritchui_uiMenuKind atRover,
418 sjme_attrInNotNull sjme_jint itemActivated,
419 sjme_attrInValue sjme_jint itemMask)
421 sjme_errorCode error;
422 sjme_scritchui_listener_menuItemActivate* infoCore;
423 sjme_scritchui_uiMenuHasChildren child;
424 sjme_scritchui_uiMenuItem menuItem;
425 sjme_jint i, n, opaqueId;
426 sjme_scritchui_uiMenuKind* children;
427 sjme_scritchui_uiMenuKind tryChild;
429 if (inState == NULL || inWindow == NULL)
430 return SJME_ERROR_NULL_ARGUMENTS;
432 if (itemMask == 0)
433 return SJME_ERROR_INVALID_ARGUMENT;
435 #if 0
436 /* Debug. */
437 sjme_message("menuItemActivateById(%p, %p, %p, %d)",
438 inState, inWindow, atRover, itemActivated);
439 #endif
441 /* If the window has no listener or no bar, ignore. */
442 infoCore = &SJME_SCRITCHUI_LISTENER_USER(inWindow, menuItemActivate);
443 if (infoCore->callback == NULL || inWindow->menuBar == NULL)
444 return SJME_ERROR_NONE;
446 /* Check to see if this has a child. */
447 child = NULL;
448 if (sjme_error_is(error = inState->intern->getMenuHasChildren(inState,
449 atRover, &child)) || child == NULL)
451 /* There is no child, so this is a menu item. */
452 if (error == SJME_ERROR_INVALID_ARGUMENT &&
453 atRover->common.type == SJME_SCRITCHUI_TYPE_MENU_ITEM)
455 /* Recover item. */
456 menuItem = (sjme_scritchui_uiMenuItem)atRover;
458 /* Forward to callback if this is the item. */
459 opaqueId = menuItem->opaqueId;
460 if (opaqueId != 0 &&
461 (opaqueId & itemMask) == (itemActivated & itemMask))
462 return infoCore->callback(inState, inWindow,
463 (sjme_scritchui_uiMenuKind)menuItem);
465 /* Continue otherwise. */
466 return SJME_ERROR_CONTINUE;
469 /* Some other kind of menu item? */
470 if (error == SJME_ERROR_INVALID_ARGUMENT)
471 return SJME_ERROR_CONTINUE;
473 /* Failed. */
474 return sjme_error_default(error);
477 /* Make copy of children, so it does not change in the middle. */
478 n = child->numChildren;
479 children = sjme_alloca(sizeof(*children) * n);
480 if (children == NULL)
481 return sjme_error_outOfMemory(NULL, n);
482 memmove(children, child->children->elements,
483 sizeof(*children) * n);
485 /* Go through each child. */
486 for (i = 0; i < n; i++)
488 /* Get child here. */
489 tryChild = children[i];
491 /* Recursive search for the menu command. */
492 error = inState->intern->menuItemActivateById(inState, inWindow,
493 tryChild, itemActivated, itemMask);
495 /* Try next child? */
496 if (error == SJME_ERROR_CONTINUE)
497 continue;
499 /* Fail? */
500 else if (sjme_error_is(error))
501 return sjme_error_default(error);
503 /* We found the menu item. */
504 else
505 break;
508 /* Nothing left to check. */
509 return SJME_ERROR_NONE;