UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2017 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 <inttypes.h> 32 #include <stdarg.h> 33 34 #include "menu.h" 35 #include "toolkit.h" 36 #include "../common/context.h" 37 #include "../ui/properties.h" 38 #include "../ui/window.h" 39 #include "container.h" 40 41 static UcxList *menus; 42 static 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 //UcxList *c = current; 83 } 84 85 void ui_menuitem(char *label, ui_callback f, void *userdata) { 86 ui_menuitem_gr(label, f, userdata, -1); 87 } 88 89 void ui_menuitem_st(char *stockid, ui_callback f, void *userdata) { 90 ui_menuitem_stgr(stockid, f, userdata, -1); 91 } 92 93 void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...) { 94 if(!current) { 95 return; 96 } 97 98 UiMenuItem *item = malloc(sizeof(UiMenuItem)); 99 item->item.add_to = (ui_menu_add_f)add_menuitem_widget; 100 101 item->label = label; 102 item->userdata = userdata; 103 item->callback = f; 104 item->groups = NULL; 105 106 // add groups 107 va_list ap; 108 va_start(ap, userdata); 109 int group; 110 while((group = va_arg(ap, int)) != -1) { 111 item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); 112 } 113 va_end(ap); 114 115 UiMenu *cm = current->data; 116 cm->items = ucx_list_append(cm->items, item); 117 } 118 119 void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) { 120 if(!current) { 121 return; 122 } 123 124 UiStMenuItem *item = malloc(sizeof(UiStMenuItem)); 125 item->item.add_to = (ui_menu_add_f)add_menuitem_st_widget; 126 127 item->stockid = stockid; 128 item->userdata = userdata; 129 item->callback = f; 130 item->groups = NULL; 131 132 // add groups 133 va_list ap; 134 va_start(ap, userdata); 135 int group; 136 while((group = va_arg(ap, int)) != -1) { 137 item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); 138 } 139 va_end(ap); 140 141 UiMenu *cm = current->data; 142 cm->items = ucx_list_append(cm->items, item); 143 } 144 145 void ui_menuseparator() { 146 if(!current) { 147 return; 148 } 149 150 UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); 151 item->add_to = (ui_menu_add_f)add_menuseparator_widget; 152 153 UiMenu *cm = current->data; 154 cm->items = ucx_list_append(cm->items, item); 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 // private menu functions 202 GtkWidget *ui_create_menubar(UiObject *obj) { 203 if(menus == NULL) { 204 return NULL; 205 } 206 207 GtkWidget *mb = gtk_menu_bar_new(); 208 209 UcxList *ls = menus; 210 while(ls) { 211 UiMenu *menu = ls->data; 212 menu->item.add_to(mb, 0, &menu->item, obj); 213 214 ls = ls->next; 215 } 216 217 return mb; 218 } 219 220 void add_menu_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj) { 221 UiMenu *menu = (UiMenu*)item; 222 223 GtkWidget *menu_widget = gtk_menu_new(); 224 GtkWidget *menu_item = gtk_menu_item_new_with_mnemonic(menu->label); 225 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu_widget); 226 227 UcxList *ls = menu->items; 228 int index = 0; 229 while(ls) { 230 UiMenuItemI *i = ls->data; 231 i->add_to(menu_widget, index, i, obj); 232 233 ls = ls->next; 234 index++; 235 } 236 237 gtk_menu_shell_append(GTK_MENU_SHELL(parent), menu_item); 238 } 239 240 void add_menuitem_widget(GtkWidget *parent, int index, UiMenuItemI *item, UiObject *obj) { 241 UiMenuItem *i = (UiMenuItem*)item; 242 243 //GtkWidget *widget = gtk_menu_item_new_with_label(i->title); 244 GtkWidget *widget = gtk_menu_item_new_with_mnemonic(i->label); 245 246 if(i->callback != NULL) { 247 UiEventData *event = malloc(sizeof(UiEventData)); 248 event->obj = obj; 249 event->userdata = i->userdata; 250 event->callback = i->callback; 251 event->value = 0; 252 253 g_signal_connect( 254 widget, 255 "activate", 256 G_CALLBACK(ui_menu_event_wrapper), 257 event); 258 g_signal_connect( 259 widget, 260 "destroy", 261 G_CALLBACK(ui_destroy_userdata), 262 event); 263 } 264 265 gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget); 266 267 if(i->groups) { 268 uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, i->groups); 269 } 270 } 271 272 void add_menuitem_st_widget( 273 GtkWidget *parent, 274 int index, 275 UiMenuItemI *item, 276 UiObject *obj) 277 { 278 UiStMenuItem *i = (UiStMenuItem*)item; 279 280 GtkWidget *widget = gtk_image_menu_item_new_from_stock(i->stockid, obj->ctx->accel_group); 281 282 if(i->callback != NULL) { 283 UiEventData *event = malloc(sizeof(UiEventData)); 284 event->obj = obj; 285 event->userdata = i->userdata; 286 event->callback = i->callback; 287 event->value = 0; 288 289 g_signal_connect( 290 widget, 291 "activate", 292 G_CALLBACK(ui_menu_event_wrapper), 293 event); 294 g_signal_connect( 295 widget, 296 "destroy", 297 G_CALLBACK(ui_destroy_userdata), 298 event); 299 } 300 301 gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget); 302 303 if(i->groups) { 304 uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, i->groups); 305 } 306 } 307 308 void add_menuseparator_widget( 309 GtkWidget *parent, 310 int index, 311 UiMenuItemI *item, 312 UiObject *obj) 313 { 314 gtk_menu_shell_append( 315 GTK_MENU_SHELL(parent), 316 gtk_separator_menu_item_new()); 317 } 318 319 void add_checkitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { 320 UiCheckItem *ci = (UiCheckItem*)item; 321 GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label); 322 gtk_menu_shell_append(GTK_MENU_SHELL(p), widget); 323 324 if(ci->callback) { 325 UiEventData *event = malloc(sizeof(UiEventData)); 326 event->obj = obj; 327 event->userdata = ci->userdata; 328 event->callback = ci->callback; 329 event->value = 0; 330 331 g_signal_connect( 332 widget, 333 "toggled", 334 G_CALLBACK(ui_menu_event_toggled), 335 event); 336 g_signal_connect( 337 widget, 338 "destroy", 339 G_CALLBACK(ui_destroy_userdata), 340 event); 341 } 342 } 343 344 void add_checkitemnv_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { 345 UiCheckItemNV *ci = (UiCheckItemNV*)item; 346 GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label); 347 gtk_menu_shell_append(GTK_MENU_SHELL(p), widget); 348 349 UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER); 350 if(var) { 351 UiInteger *value = var->value; 352 value->obj = widget; 353 value->get = ui_checkitem_get; 354 value->set = ui_checkitem_set; 355 value = 0; 356 } else { 357 // TODO: error 358 } 359 } 360 361 void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { 362 UiMenuItemList *il = (UiMenuItemList*)item; 363 UcxMempool *mp = obj->ctx->mempool; 364 365 UiActiveMenuItemList *ls = ucx_mempool_malloc( 366 mp, 367 sizeof(UiActiveMenuItemList)); 368 369 ls->object = obj; 370 ls->menu = GTK_MENU_SHELL(p); 371 ls->index = index; 372 ls->oldcount = 0; 373 ls->list = il->list; 374 ls->callback = il->callback; 375 ls->userdata = il->userdata; 376 377 ls->list->observers = ui_add_observer( 378 ls->list->observers, 379 (ui_callback)ui_update_menuitem_list, 380 ls); 381 382 ui_update_menuitem_list(NULL, ls); 383 } 384 385 386 void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) { 387 // remove old items 388 if(list->oldcount > 0) { 389 int i = 0; 390 GList *mi = gtk_container_get_children(GTK_CONTAINER(list->menu)); 391 while(mi) { 392 if(i >= list->index && i < list->index + list->oldcount) { 393 //gtk_container_remove(GTK_CONTAINER(list->menu), mi->data); 394 gtk_widget_destroy(mi->data); 395 } 396 mi = mi->next; 397 i++; 398 } 399 } 400 401 char *str = ui_list_first(list->list); 402 if(str) { 403 GtkWidget *widget = gtk_separator_menu_item_new(); 404 gtk_menu_shell_insert(list->menu, widget, list->index); 405 gtk_widget_show(widget); 406 } 407 int i = 1; 408 while(str) { 409 GtkWidget *widget = gtk_menu_item_new_with_label(str); 410 gtk_menu_shell_insert(list->menu, widget, list->index + i); 411 gtk_widget_show(widget); 412 413 if(list->callback) { 414 // TODO: use mempool 415 UiEventData *event = malloc(sizeof(UiEventData)); 416 event->obj = list->object; 417 event->userdata = list->userdata; 418 event->callback = list->callback; 419 event->value = i - 1; 420 421 g_signal_connect( 422 widget, 423 "activate", 424 G_CALLBACK(ui_menu_event_wrapper), 425 event); 426 g_signal_connect( 427 widget, 428 "destroy", 429 G_CALLBACK(ui_destroy_userdata), 430 event); 431 } 432 433 str = ui_list_next(list->list); 434 i++; 435 } 436 437 list->oldcount = i; 438 } 439 440 void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event) { 441 UiEvent evt; 442 evt.obj = event->obj; 443 evt.window = event->obj->window; 444 evt.document = event->obj->ctx->document; 445 evt.eventdata = NULL; 446 evt.intval = event->value; 447 event->callback(&evt, event->userdata); 448 } 449 450 void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) { 451 UiEvent evt; 452 evt.obj = event->obj; 453 evt.window = event->obj->window; 454 evt.document = event->obj->ctx->document; 455 evt.eventdata = NULL; 456 evt.intval = gtk_check_menu_item_get_active(ci); 457 event->callback(&evt, event->userdata); 458 } 459 460 int64_t ui_checkitem_get(UiInteger *i) { 461 int state = gtk_check_menu_item_get_active(i->obj); 462 i->value = state; 463 return state; 464 } 465 466 void ui_checkitem_set(UiInteger *i, int64_t value) { 467 i->value = value; 468 gtk_check_menu_item_set_active(i->obj, value); 469 } 470 471 472 /* 473 * widget menu functions 474 */ 475 476 static gboolean ui_button_press_event(GtkWidget *widget, GdkEvent *event, GtkMenu *menu) { 477 if(event->type == GDK_BUTTON_PRESS) { 478 GdkEventButton *e = (GdkEventButton*)event; 479 if(e->button == 3) { 480 gtk_widget_show_all(GTK_WIDGET(menu)); 481 ui_contextmenu_popup(menu); 482 return TRUE; 483 } 484 } 485 return FALSE; 486 } 487 488 UIMENU ui_contextmenu(UiObject *obj) { 489 UiContainer *ct = uic_get_current_container(obj); 490 return ui_contextmenu_w(obj, ct->current); 491 } 492 493 UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) { 494 UiContainer *ct = uic_get_current_container(obj); 495 496 GtkMenu *menu = GTK_MENU(gtk_menu_new()); 497 g_signal_connect(widget, "button-press-event", (GCallback) ui_button_press_event, menu); 498 499 ct->menu = menu; 500 return menu; 501 } 502 503 void ui_contextmenu_popup(UIMENU menu) { 504 #if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16 505 gtk_menu_popup_at_pointer(menu, NULL); 506 #else 507 gtk_menu_popup(menu, NULL, NULL, 0, 0, 0, gtk_get_current_event_time()); 508 #endif 509 } 510 511 void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) { 512 ui_widget_menuitem_gr(obj, label, f, userdata, -1); 513 } 514 515 void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) { 516 UiContainer *ct = uic_get_current_container(obj); 517 if(!ct->menu) { 518 return; 519 } 520 521 // add groups 522 UcxList *groups = NULL; 523 va_list ap; 524 va_start(ap, userdata); 525 int group; 526 while((group = va_arg(ap, int)) != -1) { 527 ucx_list_append(groups, (void*)(intptr_t)group); 528 } 529 va_end(ap); 530 531 // create menuitem 532 GtkWidget *widget = gtk_menu_item_new_with_mnemonic(label); 533 gtk_widget_show(widget); 534 535 if(f) { 536 UiEventData *event = malloc(sizeof(UiEventData)); 537 event->obj = obj; 538 event->userdata = userdata; 539 event->callback = f; 540 event->value = 0; 541 542 g_signal_connect( 543 widget, 544 "activate", 545 G_CALLBACK(ui_menu_event_wrapper), 546 event); 547 g_signal_connect( 548 widget, 549 "destroy", 550 G_CALLBACK(ui_destroy_userdata), 551 event); 552 } 553 554 gtk_menu_shell_append(GTK_MENU_SHELL(ct->menu), widget); 555 556 if(groups) { 557 uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); 558 } 559 } 560 561 void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) { 562 ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1); 563 } 564 565 void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) { 566 UiContainer *ct = uic_get_current_container(obj); 567 if(!ct->menu) { 568 return; 569 } 570 571 // add groups 572 UcxList *groups = NULL; 573 va_list ap; 574 va_start(ap, userdata); 575 int group; 576 while((group = va_arg(ap, int)) != -1) { 577 ucx_list_append(groups, (void*)(intptr_t)group); 578 } 579 va_end(ap); 580 581 // create menuitem 582 GtkWidget *widget = gtk_image_menu_item_new_from_stock(stockid, obj->ctx->accel_group); 583 gtk_widget_show(widget); 584 585 if(f) { 586 UiEventData *event = malloc(sizeof(UiEventData)); 587 event->obj = obj; 588 event->userdata = userdata; 589 event->callback = f; 590 event->value = 0; 591 592 g_signal_connect( 593 widget, 594 "activate", 595 G_CALLBACK(ui_menu_event_wrapper), 596 event); 597 g_signal_connect( 598 widget, 599 "destroy", 600 G_CALLBACK(ui_destroy_userdata), 601 event); 602 } 603 604 gtk_menu_shell_append(GTK_MENU_SHELL(ct->menu), widget); 605 606 if(groups) { 607 uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); 608 } 609 } 610