UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2014 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <stdarg.h> 32 33 #include "menu.h" 34 #include "button.h" 35 #include "toolkit.h" 36 #include "stock.h" 37 #include "container.h" 38 #include "../common/context.h" 39 #include "../ui/window.h" 40 41 UcxList *menus; 42 UcxList *current; 43 44 void ui_menu(char *label) { 45 // free current menu hierarchy 46 ucx_list_free(current); 47 48 // create menu 49 UiMenu *menu = malloc(sizeof(UiMenu)); 50 menu->item.add_to = (ui_menu_add_f)add_menu_widget; 51 52 menu->label = label; 53 menu->items = NULL; 54 menu->parent = NULL; 55 56 current = ucx_list_prepend(NULL, menu); 57 menus = ucx_list_append(menus, menu); 58 59 } 60 61 void ui_submenu(char *label) { 62 UiMenu *menu = malloc(sizeof(UiMenu)); 63 menu->item.add_to = (ui_menu_add_f)add_menu_widget; 64 65 menu->label = label; 66 menu->items = NULL; 67 menu->parent = NULL; 68 69 // add submenu to current menu 70 UiMenu *cm = current->data; 71 cm->items = ucx_list_append(cm->items, menu); 72 73 // set the submenu to current menu 74 current = ucx_list_prepend(current, menu); 75 } 76 77 void ui_submenu_end() { 78 if(ucx_list_size(current) < 2) { 79 return; 80 } 81 current = ucx_list_remove(current, current); 82 } 83 84 void ui_menuitem(char *label, ui_callback f, void *userdata) { 85 ui_menuitem_gr(label, f, userdata, -1); 86 } 87 88 void ui_menuitem_st(char *stockid, ui_callback f, void *userdata) { 89 ui_menuitem_stgr(stockid, f, userdata, -1); 90 } 91 92 void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...) { 93 if(!current) { 94 return; 95 } 96 97 UiMenuItem *item = malloc(sizeof(UiMenuItem)); 98 item->item.add_to = (ui_menu_add_f)add_menuitem_widget; 99 100 item->label = label; 101 item->userdata = userdata; 102 item->callback = f; 103 item->groups = NULL; 104 105 // add groups 106 va_list ap; 107 va_start(ap, userdata); 108 int group; 109 while((group = va_arg(ap, int)) != -1) { 110 item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); 111 } 112 va_end(ap); 113 114 UiMenu *cm = current->data; 115 cm->items = ucx_list_append(cm->items, item); 116 } 117 118 void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) { 119 if(!current) { 120 return; 121 } 122 123 UiStMenuItem *item = malloc(sizeof(UiStMenuItem)); 124 item->item.add_to = (ui_menu_add_f)add_menuitem_st_widget; 125 126 item->stockid = stockid; 127 item->userdata = userdata; 128 item->callback = f; 129 item->groups = NULL; 130 131 // add groups 132 va_list ap; 133 va_start(ap, userdata); 134 int group; 135 while((group = va_arg(ap, int)) != -1) { 136 item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); 137 } 138 va_end(ap); 139 140 UiMenu *cm = current->data; 141 cm->items = ucx_list_append(cm->items, item); 142 } 143 144 void ui_menuseparator() { 145 if(!current) { 146 return; 147 } 148 149 UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); 150 item->add_to = (ui_menu_add_f)add_menuseparator_widget; 151 152 UiMenu *cm = current->data; 153 cm->items = ucx_list_append(cm->items, item); 154 } 155 156 157 void ui_checkitem(char *label, ui_callback f, void *userdata) { 158 if(!current) { 159 return; 160 } 161 162 UiCheckItem *item = malloc(sizeof(UiCheckItem)); 163 item->item.add_to = (ui_menu_add_f)add_checkitem_widget; 164 item->label = label; 165 item->callback = f; 166 item->userdata = userdata; 167 168 UiMenu *cm = current->data; 169 cm->items = ucx_list_append(cm->items, item); 170 } 171 172 void ui_checkitem_nv(char *label, char *vname) { 173 if(!current) { 174 return; 175 } 176 177 UiCheckItemNV *item = malloc(sizeof(UiCheckItemNV)); 178 item->item.add_to = (ui_menu_add_f)add_checkitemnv_widget; 179 item->varname = vname; 180 item->label = label; 181 182 UiMenu *cm = current->data; 183 cm->items = ucx_list_append(cm->items, item); 184 } 185 186 void ui_menuitem_list(UiList *items, ui_callback f, void *userdata) { 187 if(!current) { 188 return; 189 } 190 191 UiMenuItemList *item = malloc(sizeof(UiMenuItemList)); 192 item->item.add_to = (ui_menu_add_f)add_menuitem_list_widget; 193 item->callback = f; 194 item->userdata = userdata; 195 item->list = items; 196 197 UiMenu *cm = current->data; 198 cm->items = ucx_list_append(cm->items, item); 199 } 200 201 202 // private menu functions 203 void ui_create_menubar(UiObject *obj) { 204 if(!menus) { 205 return; 206 } 207 208 Widget menubar = XmCreateMenuBar(obj->widget, "main_list", NULL, 0); 209 XtManageChild(menubar); 210 211 UcxList *ls = menus; 212 int menu_index = 0; 213 while(ls) { 214 UiMenu *menu = ls->data; 215 menu_index += menu->item.add_to(menubar, menu_index, &menu->item, obj); 216 217 ls = ls->next; 218 } 219 } 220 221 int add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { 222 UiMenu *menu = (UiMenu*)item; 223 224 Widget menuItem = XtVaCreateManagedWidget( 225 menu->label, 226 xmCascadeButtonWidgetClass, 227 parent, 228 NULL); 229 Widget m = XmVaCreateSimplePulldownMenu(parent, menu->label, i, NULL, NULL); 230 231 UcxList *ls = menu->items; 232 int menu_index = 0; 233 while(ls) { 234 UiMenuItemI *mi = ls->data; 235 menu_index += mi->add_to(m, menu_index, mi, obj); 236 ls = ls->next; 237 } 238 239 return 1; 240 } 241 242 int add_menuitem_widget( 243 Widget parent, 244 int i, 245 UiMenuItemI *item, 246 UiObject *obj) 247 { 248 UiMenuItem *mi = (UiMenuItem*)item; 249 250 Arg args[1]; 251 XmString label = XmStringCreateLocalized(mi->label); 252 XtSetArg(args[0], XmNlabelString, label); 253 254 Widget mitem = XtCreateManagedWidget( 255 "menubutton", 256 xmPushButtonWidgetClass, 257 parent, 258 args, 259 1); 260 XmStringFree(label); 261 262 if(mi->callback != NULL) { 263 UiEventData *event = ucx_mempool_malloc( 264 obj->ctx->mempool, 265 sizeof(UiEventData)); 266 event->obj = obj; 267 event->userdata = mi->userdata; 268 event->callback = mi->callback; 269 event->value = 0; 270 XtAddCallback( 271 mitem, 272 XmNactivateCallback, 273 (XtCallbackProc)ui_push_button_callback, 274 event); 275 } 276 277 if(mi->groups) { 278 uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups); 279 } 280 281 return 1; 282 } 283 284 int add_menuitem_st_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { 285 UiStMenuItem *mi = (UiStMenuItem*)item; 286 287 UiStockItem *si = ui_get_stock_item(mi->stockid); 288 if(!si) { 289 fprintf(stderr, "UI Error: unknown stock id: %s\n", mi->stockid); 290 return 0; 291 } 292 293 int n = 0; 294 Arg args[4]; 295 XmString label = XmStringCreateLocalized(si->label); 296 XmString at = NULL; 297 298 XtSetArg(args[n], XmNlabelString, label); 299 n++; 300 if(si->accelerator) { 301 XtSetArg(args[n], XmNaccelerator, si->accelerator); 302 n++; 303 } 304 if(si->accelerator_label) { 305 at = XmStringCreateLocalized(si->accelerator_label); 306 XtSetArg(args[n], XmNacceleratorText, at); 307 n++; 308 } 309 310 Widget mitem = XtCreateManagedWidget( 311 "menubutton", 312 xmPushButtonWidgetClass, 313 parent, 314 args, 315 n); 316 XmStringFree(label); 317 if(at) { 318 XmStringFree(at); 319 } 320 321 if(mi->callback != NULL) { 322 UiEventData *event = ucx_mempool_malloc( 323 obj->ctx->mempool, 324 sizeof(UiEventData)); 325 event->obj = obj; 326 event->userdata = mi->userdata; 327 event->callback = mi->callback; 328 event->value = 0; 329 XtAddCallback( 330 mitem, 331 XmNactivateCallback, 332 (XtCallbackProc)ui_push_button_callback, 333 event); 334 } 335 336 if(mi->groups) { 337 uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups); 338 } 339 340 return 1; 341 } 342 343 int add_menuseparator_widget( 344 Widget parent, 345 int i, 346 UiMenuItemI *item, 347 UiObject *obj) 348 { 349 Widget s = XmCreateSeparatorGadget (parent, "menu_separator", NULL, 0); 350 XtManageChild(s); 351 return 1; 352 } 353 354 int add_checkitem_widget( 355 Widget parent, 356 int i, 357 UiMenuItemI *item, 358 UiObject *obj) 359 { 360 UiCheckItem *ci = (UiCheckItem*)item; 361 362 Arg args[3]; 363 XmString label = XmStringCreateLocalized(ci->label); 364 XtSetArg(args[0], XmNlabelString, label); 365 XtSetArg(args[1], XmNvisibleWhenOff, 1); 366 Widget checkbox = XtCreateManagedWidget( 367 "menutogglebutton", 368 xmToggleButtonWidgetClass, 369 parent, 370 args, 371 2); 372 XmStringFree(label); 373 374 if(ci->callback) { 375 UiEventData *event = ucx_mempool_malloc( 376 obj->ctx->mempool, 377 sizeof(UiEventData)); 378 event->obj = obj; 379 event->userdata = ci->userdata; 380 event->callback = ci->callback; 381 XtAddCallback( 382 checkbox, 383 XmNvalueChangedCallback, 384 (XtCallbackProc)ui_toggle_button_callback, 385 event); 386 } 387 388 return 1; 389 } 390 391 int add_checkitemnv_widget( 392 Widget parent, 393 int i, 394 UiMenuItemI *item, 395 UiObject *obj) 396 { 397 UiCheckItemNV *ci = (UiCheckItemNV*)item; 398 399 Arg args[3]; 400 XmString label = XmStringCreateLocalized(ci->label); 401 XtSetArg(args[0], XmNlabelString, label); 402 XtSetArg(args[1], XmNvisibleWhenOff, 1); 403 Widget checkbox = XtCreateManagedWidget( 404 "menutogglebutton", 405 xmToggleButtonWidgetClass, 406 parent, 407 args, 408 2); 409 XmStringFree(label); 410 411 UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER); 412 if(var) { 413 UiInteger *value = var->value; 414 value->obj = checkbox; 415 value->get = ui_toggle_button_get; 416 value->set = ui_toggle_button_set; 417 value = 0; 418 } else { 419 // TODO: error 420 } 421 422 return 1; 423 } 424 425 int add_menuitem_list_widget( 426 Widget parent, 427 int i, 428 UiMenuItemI *item, 429 UiObject *obj) 430 { 431 UiMenuItemList *il = (UiMenuItemList*)item; 432 UcxMempool *mp = obj->ctx->mempool; 433 434 UiActiveMenuItemList *ls = ucx_mempool_malloc( 435 mp, 436 sizeof(UiActiveMenuItemList)); 437 438 ls->object = obj; 439 ls->menu = parent; 440 ls->index = i; 441 ls->oldcount = 0; 442 ls->list = il->list; 443 ls->callback = il->callback; 444 ls->userdata = il->userdata; 445 446 ls->list->observers = ui_add_observer( 447 ls->list->observers, 448 (ui_callback)ui_update_menuitem_list, 449 ls); 450 451 ui_update_menuitem_list(NULL, ls); 452 453 return 0; 454 } 455 456 void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) { 457 Arg args[4]; 458 459 // remove old items 460 if(list->oldcount > 0) { 461 Widget *children; 462 int nc; 463 464 XtVaGetValues( 465 list->menu, 466 XmNchildren, 467 &children, 468 XmNnumChildren, 469 &nc, 470 NULL); 471 472 for(int i=0;i<list->oldcount;i++) { 473 XtDestroyWidget(children[list->index + i]); 474 } 475 } 476 477 char *str = ui_list_first(list->list); 478 if(str) { 479 // add separator 480 XtSetArg(args[0], XmNpositionIndex, list->index); 481 Widget s = XmCreateSeparatorGadget (list->menu, "menu_separator", args, 1); 482 XtManageChild(s); 483 } 484 int i = 1; 485 while(str) { 486 XmString label = XmStringCreateLocalized(str); 487 XtSetArg(args[0], XmNlabelString, label); 488 XtSetArg(args[1], XmNpositionIndex, list->index + i); 489 490 Widget mitem = XtCreateManagedWidget( 491 "menubutton", 492 xmPushButtonWidgetClass, 493 list->menu, 494 args, 495 2); 496 XmStringFree(label); 497 498 if(list->callback) { 499 // TODO: use mempool 500 UiEventData *event = malloc(sizeof(UiEventData)); 501 event->obj = list->object; 502 event->userdata = list->userdata; 503 event->callback = list->callback; 504 event->value = i - 1; 505 506 XtAddCallback( 507 mitem, 508 XmNactivateCallback, 509 (XtCallbackProc)ui_push_button_callback, 510 event); 511 } 512 513 str = ui_list_next(list->list); 514 i++; 515 } 516 517 list->oldcount = i; 518 } 519 520 void ui_menu_event_wrapper(Widget widget, XtPointer udata, XtPointer cdata) { 521 UiEventData *event = udata; 522 UiEvent e; 523 e.obj = event->obj; 524 e.window = event->obj->window; 525 e.document = event->obj->ctx->document; 526 e.intval = 0; 527 event->callback(&e, event->userdata); 528 } 529 530 531 /* 532 * widget menu functions 533 */ 534 535 static void ui_popup_handler(Widget widget, XtPointer data, XEvent *event, Boolean *c) { 536 Widget menu = data; 537 XmMenuPosition(menu, (XButtonPressedEvent *)event); 538 XtManageChild(menu); 539 540 *c = FALSE; 541 } 542 543 UIMENU ui_contextmenu(UiObject *obj) { 544 UiContainer *ct = uic_get_current_container(obj); 545 if(ct->current) { 546 return ui_contextmenu_w(obj, ct->current); 547 } else { 548 return NULL; // TODO: warn 549 } 550 } 551 552 UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) { 553 UiContainer *ct = uic_get_current_container(obj); 554 555 Widget menu = XmCreatePopupMenu(widget, "popup_menu", NULL, 0); 556 ct->menu = menu; 557 558 XtAddEventHandler(widget, ButtonPressMask, FALSE, ui_popup_handler, menu); 559 560 return menu; 561 } 562 563 void ui_contextmenu_popup(UIMENU menu) { 564 565 } 566 567 void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) { 568 ui_widget_menuitem_gr(obj, label, f, userdata, -1); 569 } 570 571 void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) { 572 UiContainer *ct = uic_get_current_container(obj); 573 if(!ct->menu) { 574 return; 575 } 576 577 // add groups 578 UcxList *groups = NULL; 579 va_list ap; 580 va_start(ap, userdata); 581 int group; 582 while((group = va_arg(ap, int)) != -1) { 583 ucx_list_append(groups, (void*)(intptr_t)group); 584 } 585 va_end(ap); 586 587 // create menuitem 588 Arg args[4]; 589 XmString labelstr = XmStringCreateLocalized(label); 590 XtSetArg(args[0], XmNlabelString, labelstr); 591 592 Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1); 593 XtManageChild(item); 594 XmStringFree(labelstr); 595 } 596 597 void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) { 598 ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1); 599 } 600 601 void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) { 602 UiContainer *ct = uic_get_current_container(obj); 603 if(!ct->menu) { 604 return; 605 } 606 607 // add groups 608 UcxList *groups = NULL; 609 va_list ap; 610 va_start(ap, userdata); 611 int group; 612 while((group = va_arg(ap, int)) != -1) { 613 ucx_list_append(groups, (void*)(intptr_t)group); 614 } 615 va_end(ap); 616 617 // create menuitem 618 UiStockItem *stockItem = ui_get_stock_item(stockid); 619 Arg args[4]; 620 XmString labelstr = XmStringCreateLocalized(stockItem->label); 621 XtSetArg(args[0], XmNlabelString, labelstr); 622 623 Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1); 624 XtManageChild(item); 625 XmStringFree(labelstr); 626 } 627