1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
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 // -------------------------------------------------------------------------*/
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
,
28 SJME_SCRITCHUI_TYPE_MENU_BAR
,
29 (sjme_scritchui_coreGeneric_commonNewImplFunc
)
30 inState
->impl
->menuBarNew
,
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
)
41 sjme_list_sjme_scritchui_uiMenuKind
* childList
;
42 sjme_list_sjme_scritchui_uiMenuKind
* newList
;
43 sjme_scritchui_uiMenuHasChildren parentMenu
;
44 sjme_scritchui_uiMenuHasParent childMenu
;
47 if (inState
== NULL
|| intoMenu
== NULL
|| childItem
== NULL
)
48 return SJME_ERROR_NULL_ARGUMENTS
;
51 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
53 /* Get parent information. */
55 if (sjme_error_is(error
= inState
->intern
->getMenuHasChildren(
56 inState
, SJME_SUI_CAST_MENU_KIND(intoMenu
),
59 return sjme_error_default(error
);
61 /* Get child information. */
63 if (sjme_error_is(error
= inState
->intern
->getMenuHasParent(
64 inState
, SJME_SUI_CAST_MENU_KIND(childItem
),
67 return sjme_error_default(error
);
69 /* Already has a parent? */
70 if (childMenu
->parent
!= NULL
)
71 return SJME_ERROR_HAS_PARENT
;
74 childList
= parentMenu
->children
;
75 n
= parentMenu
->numChildren
;
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 */
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
)
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
];
106 parentMenu
->children
= newList
;
109 if (childList
!= NULL
)
111 if (sjme_error_is(error
= sjme_alloc_free(childList
)))
112 return sjme_error_default(error
);
116 /* We are using this list now. */
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
,
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
,
173 SJME_SCRITCHUI_TYPE_MENU
,
174 (sjme_scritchui_coreGeneric_commonNewImplFunc
)
175 inState
->impl
->menuNew
,
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
;
191 if (inState
== NULL
|| fromMenu
== NULL
)
192 return SJME_ERROR_NULL_ARGUMENTS
;
195 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
197 /* Get parent information. */
199 if (sjme_error_is(error
= inState
->intern
->getMenuHasChildren(
200 inState
, SJME_SUI_CAST_MENU_KIND(fromMenu
),
203 return sjme_error_default(error
);
205 /* Not implemented? */
206 if (inState
->impl
->menuRemove
== NULL
)
207 return sjme_error_notImplemented(0);
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
;
219 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
221 /* Obtain the child menu. */
222 childAt
= childList
->elements
[atIndex
];
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
);
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
,
236 return sjme_error_default(error
);
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
;
247 parentMenu
->numChildren
= n
- 1;
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
;
277 if (sjme_error_is(error
= inState
->api
->menuRemove(inState
,
279 return sjme_error_default(error
);
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
;
297 if (SJME_SUI_CAST_MENU_KIND(inMenuKind
) == NULL
)
298 return SJME_ERROR_INVALID_ARGUMENT
;
300 /* Depends on the type. */
302 switch (inMenuKind
->common
.type
)
304 case SJME_SCRITCHUI_TYPE_MENU
:
305 result
= &((sjme_scritchui_uiMenu
)inMenuKind
)->children
;
308 case SJME_SCRITCHUI_TYPE_MENU_BAR
:
309 result
= &((sjme_scritchui_uiMenuBar
)inMenuKind
)->children
;
314 return SJME_ERROR_INVALID_ARGUMENT
;
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
;
333 if (SJME_SUI_CAST_MENU_KIND(inMenuKind
) == NULL
)
334 return SJME_ERROR_INVALID_ARGUMENT
;
336 /* Depends on the type. */
338 switch (inMenuKind
->common
.type
)
340 case SJME_SCRITCHUI_TYPE_MENU
:
341 result
= &((sjme_scritchui_uiMenu
)inMenuKind
)->parent
;
344 case SJME_SCRITCHUI_TYPE_MENU_ITEM
:
345 result
= &((sjme_scritchui_uiMenuItem
)inMenuKind
)->parent
;
350 return SJME_ERROR_INVALID_ARGUMENT
;
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. */
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
;
388 infoUser
= &SJME_SCRITCHUI_LISTENER_USER(window
,
391 /* Execute if available. */
392 if (infoUser
->callback
!= NULL
)
393 return infoUser
->callback(inState
, window
,
397 /* Ignore otherwise. */
398 return SJME_ERROR_NONE
;
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
;
433 return SJME_ERROR_INVALID_ARGUMENT
;
437 sjme_message("menuItemActivateById(%p, %p, %p, %d)",
438 inState
, inWindow
, atRover
, itemActivated
);
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. */
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
)
456 menuItem
= (sjme_scritchui_uiMenuItem
)atRover
;
458 /* Forward to callback if this is the item. */
459 opaqueId
= menuItem
->opaqueId
;
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
;
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
)
500 else if (sjme_error_is(error
))
501 return sjme_error_default(error
);
503 /* We found the menu item. */
508 /* Nothing left to check. */
509 return SJME_ERROR_NONE
;