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 32 #include "button.h" 33 #include "container.h" 34 #include <cx/allocator.h> 35 #include "../common/context.h" 36 #include "../common/object.h" 37 38 void ui_button_set_icon_name(GtkWidget *button, const char *icon) { 39 if(!icon) { 40 return; 41 } 42 43 #ifdef UI_GTK4 44 gtk_button_set_icon_name(GTK_BUTTON(button), icon); 45 #else 46 #if GTK_CHECK_VERSION(2, 6, 0) 47 GtkWidget *image = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON); 48 if(image) { 49 gtk_button_set_image(GTK_BUTTON(button), image); 50 } 51 #else 52 // TODO 53 #endif 54 #endif 55 } 56 57 GtkWidget* ui_create_button( 58 UiObject *obj, 59 const char *label, 60 const char *icon, 61 ui_callback onclick, 62 void *userdata, 63 int event_value, 64 bool activate_event) 65 { 66 GtkWidget *button = gtk_button_new_with_label(label); 67 ui_button_set_icon_name(button, icon); 68 69 if(onclick) { 70 UiEventData *event = malloc(sizeof(UiEventData)); 71 event->obj = obj; 72 event->userdata = userdata; 73 event->callback = onclick; 74 event->value = event_value; 75 event->customdata = NULL; 76 77 g_signal_connect( 78 button, 79 "clicked", 80 G_CALLBACK(ui_button_clicked), 81 event); 82 g_signal_connect( 83 button, 84 "destroy", 85 G_CALLBACK(ui_destroy_userdata), 86 event); 87 if(activate_event) { 88 g_signal_connect( 89 button, 90 "activate", 91 G_CALLBACK(ui_button_clicked), 92 event); 93 } 94 } 95 96 return button; 97 } 98 99 UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs args) { 100 UiObject* current = uic_current_obj(obj); 101 GtkWidget *button = ui_create_button(obj, args.label, args.icon, args.onclick, args.onclickdata, 0, FALSE); 102 ui_set_name_and_style(button, args.name, args.style_class); 103 ui_set_widget_groups(obj->ctx, button, args.groups); 104 UI_APPLY_LAYOUT1(current, args); 105 current->container->add(current->container, button, FALSE); 106 return button; 107 } 108 109 110 void ui_button_clicked(GtkWidget *widget, UiEventData *event) { 111 UiEvent e; 112 e.obj = event->obj; 113 e.window = event->obj->window; 114 e.document = event->obj->ctx->document; 115 e.eventdata = NULL; 116 e.intval = event->value; 117 event->callback(&e, event->userdata); 118 } 119 120 int64_t ui_toggle_button_get(UiInteger *integer) { 121 GtkToggleButton *button = integer->obj; 122 integer->value = (int)gtk_toggle_button_get_active(button); 123 return integer->value; 124 } 125 126 void ui_toggle_button_set(UiInteger *integer, int64_t value) { 127 GtkToggleButton *button = integer->obj; 128 integer->value = value; 129 gtk_toggle_button_set_active(button, value != 0 ? TRUE : FALSE); 130 } 131 132 void ui_toggled_obs(void *widget, UiVarEventData *event) { 133 UiInteger *i = event->var->value; 134 UiEvent e; 135 e.obj = event->obj; 136 e.window = event->obj->window; 137 e.document = event->obj->ctx->document; 138 e.eventdata = event->var->value; 139 e.intval = i->get(i); 140 141 ui_notify_evt(i->observers, &e); 142 } 143 144 static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) { 145 UiEvent e; 146 e.obj = event->obj; 147 e.window = event->obj->window; 148 e.document = event->obj->ctx->document; 149 e.eventdata = NULL; 150 e.intval = gtk_toggle_button_get_active(widget); 151 event->callback(&e, event->userdata); 152 } 153 154 static void ui_togglebutton_enable_state_callback(GtkToggleButton *widget, UiEventData *event) { 155 if(gtk_toggle_button_get_active(widget)) { 156 ui_set_group(event->obj->ctx, event->value); 157 } else { 158 ui_unset_group(event->obj->ctx, event->value); 159 } 160 } 161 162 void ui_setup_togglebutton( 163 UiObject *obj, 164 GtkWidget *togglebutton, 165 const char *label, 166 const char *icon, 167 const char *varname, 168 UiInteger *value, 169 ui_callback onchange, 170 void *onchangedata, 171 int enable_state) 172 { 173 if(label) { 174 gtk_button_set_label(GTK_BUTTON(togglebutton), label); 175 } 176 ui_button_set_icon_name(togglebutton, icon); 177 178 ui_bind_togglebutton( 179 obj, 180 togglebutton, 181 ui_toggle_button_get, 182 ui_toggle_button_set, 183 varname, 184 value, 185 (ui_toggled_func)ui_toggled_callback, 186 onchange, 187 onchangedata, 188 (ui_toggled_func)ui_togglebutton_enable_state_callback, 189 enable_state 190 ); 191 } 192 193 void ui_bind_togglebutton( 194 UiObject *obj, 195 GtkWidget *widget, 196 int64_t (*getfunc)(UiInteger*), 197 void (*setfunc)(UiInteger*, int64_t), 198 const char *varname, 199 UiInteger *value, 200 void (*toggled_callback)(void*, void*), 201 ui_callback onchange, 202 void *onchangedata, 203 void (*enable_state_func)(void*, void*), 204 int enable_state) 205 { 206 UiObject* current = uic_current_obj(obj); 207 UiVar* var = uic_widget_var(obj->ctx, current->ctx, value, varname, UI_VAR_INTEGER); 208 if (var) { 209 UiInteger* value = (UiInteger*)var->value; 210 value->obj = widget; 211 value->get = getfunc; 212 value->set = setfunc; 213 214 UiVarEventData *event = malloc(sizeof(UiVarEventData)); 215 event->obj = obj; 216 event->var = var; 217 event->observers = NULL; 218 event->callback = NULL; 219 event->userdata = NULL; 220 221 g_signal_connect( 222 widget, 223 "toggled", 224 G_CALLBACK(ui_toggled_obs), 225 event); 226 g_signal_connect( 227 widget, 228 "destroy", 229 G_CALLBACK(ui_destroy_vardata), 230 event); 231 } 232 233 if(onchange) { 234 UiEventData *event = malloc(sizeof(UiEventData)); 235 event->obj = obj; 236 event->userdata = onchangedata; 237 event->callback = onchange; 238 event->value = 0; 239 event->customdata = NULL; 240 241 g_signal_connect( 242 widget, 243 "toggled", 244 G_CALLBACK(toggled_callback), 245 event); 246 g_signal_connect( 247 widget, 248 "destroy", 249 G_CALLBACK(ui_destroy_userdata), 250 event); 251 } 252 253 if(enable_state > 0) { 254 UiEventData *event = malloc(sizeof(UiEventData)); 255 event->obj = obj; 256 event->userdata = NULL; 257 event->callback = NULL; 258 event->value = enable_state; 259 event->customdata = NULL; 260 261 g_signal_connect( 262 widget, 263 "toggled", 264 G_CALLBACK(enable_state_func), 265 event); 266 g_signal_connect( 267 widget, 268 "destroy", 269 G_CALLBACK(ui_destroy_userdata), 270 event); 271 } 272 } 273 274 static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs args) { 275 UiObject* current = uic_current_obj(obj); 276 277 ui_setup_togglebutton( 278 current, 279 widget, 280 args.label, 281 args.icon, 282 args.varname, 283 args.value, 284 args.onchange, 285 args.onchangedata, 286 args.enable_group); 287 ui_set_name_and_style(widget, args.name, args.style_class); 288 ui_set_widget_groups(obj->ctx, widget, args.groups); 289 290 UI_APPLY_LAYOUT1(current, args); 291 current->container->add(current->container, widget, FALSE); 292 293 return widget; 294 } 295 296 UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) { 297 return togglebutton_create(obj, gtk_toggle_button_new(), args); 298 } 299 300 #if GTK_MAJOR_VERSION >= 4 301 302 int64_t ui_check_button_get(UiInteger *integer) { 303 GtkCheckButton *button = integer->obj; 304 integer->value = (int)gtk_check_button_get_active(button); 305 return integer->value; 306 } 307 308 void ui_check_button_set(UiInteger *integer, int64_t value) { 309 GtkCheckButton *button = integer->obj; 310 integer->value = value; 311 gtk_check_button_set_active(button, value != 0 ? TRUE : FALSE); 312 } 313 314 static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) { 315 UiEvent e; 316 e.obj = event->obj; 317 e.window = event->obj->window; 318 e.document = event->obj->ctx->document; 319 e.eventdata = NULL; 320 e.intval = gtk_check_button_get_active(widget); 321 event->callback(&e, event->userdata); 322 } 323 324 static void ui_checkbox_enable_state(GtkCheckButton *widget, UiEventData *event) { 325 if(gtk_check_button_get_active(widget)) { 326 ui_set_group(event->obj->ctx, event->value); 327 } else { 328 ui_unset_group(event->obj->ctx, event->value); 329 } 330 } 331 332 UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { 333 UiObject* current = uic_current_obj(obj); 334 335 GtkWidget *widget = gtk_check_button_new_with_label(args.label); 336 ui_bind_togglebutton( 337 obj, 338 widget, 339 ui_check_button_get, 340 ui_check_button_set, 341 args.varname, 342 args.value, 343 (ui_toggled_func)ui_checkbox_callback, 344 args.onchange, 345 args.onchangedata, 346 (ui_toggled_func)ui_checkbox_enable_state, 347 args.enable_group); 348 349 ui_set_name_and_style(widget, args.name, args.style_class); 350 ui_set_widget_groups(obj->ctx, widget, args.groups); 351 352 UI_APPLY_LAYOUT1(current, args); 353 current->container->add(current->container, widget, FALSE); 354 355 return widget; 356 } 357 358 #else 359 UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { 360 return togglebutton_create(obj, gtk_check_button_new(), args); 361 } 362 #endif 363 364 UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) { 365 #ifdef UI_GTK3 366 return NULL; // TODO 367 #else 368 return ui_checkbox_create(obj, args); 369 #endif 370 } 371 372 #if GTK_MAJOR_VERSION >= 4 373 #define RADIOBUTTON_NEW(group, label) gtk_check_button_new_with_label(label) 374 #define RADIOBUTTON_SET_GROUP(button, group) 375 #define RADIOBUTTON_GET_GROUP(button) GTK_CHECK_BUTTON(button) 376 #define RADIOBUTTON_GET_ACTIVE(button) gtk_check_button_get_active(GTK_CHECK_BUTTON(button)) 377 #else 378 #define RADIOBUTTON_NEW(group, label) gtk_radio_button_new_with_label(group, label) 379 #define RADIOBUTTON_SET_GROUP(button, group) /* noop */ 380 #define RADIOBUTTON_GET_GROUP(button) gtk_radio_button_get_group(GTK_RADIO_BUTTON(button)) 381 #define RADIOBUTTON_GET_ACTIVE(button) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) 382 #endif 383 384 static void radiobutton_toggled(void *widget, UiEventData *event) { 385 UiEvent e; 386 e.obj = event->obj; 387 e.window = event->obj->window; 388 e.document = event->obj->ctx->document; 389 e.eventdata = NULL; 390 e.intval = RADIOBUTTON_GET_ACTIVE(widget); 391 event->callback(&e, event->userdata); 392 } 393 394 typedef struct UiRadioButtonData { 395 UiInteger *value; 396 UiVarEventData *eventdata; 397 UiBool first; 398 } UiRadioButtonData; 399 400 static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) { 401 if(data->first) { 402 ui_destroy_vardata(w, data->eventdata); 403 g_slist_free(data->value->obj); 404 data->value->obj = NULL; 405 data->value->get = NULL; 406 data->value->set = NULL; 407 } else { 408 free(data->eventdata); 409 } 410 free(data); 411 } 412 413 UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs args) { 414 UiObject* current = uic_current_obj(obj); 415 416 GSList *rg = NULL; 417 UiInteger *rgroup; 418 419 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); 420 421 UiBool first = FALSE; 422 if(var) { 423 rgroup = var->value; 424 rg = rgroup->obj; 425 if(!rg) { 426 first = TRUE; 427 } 428 } 429 430 GtkWidget *rbutton = RADIOBUTTON_NEW(rg, args.label); 431 ui_set_name_and_style(rbutton, args.name, args.style_class); 432 ui_set_widget_groups(obj->ctx, rbutton, args.groups); 433 if(rgroup) { 434 #if GTK_MAJOR_VERSION >= 4 435 if(rg) { 436 gtk_check_button_set_group(GTK_CHECK_BUTTON(rbutton), rg->data); 437 } 438 rg = g_slist_prepend(rg, rbutton); 439 #else 440 gtk_radio_button_set_group(GTK_RADIO_BUTTON(rbutton), rg); 441 rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton)); 442 #endif 443 444 rgroup->obj = rg; 445 rgroup->get = ui_radiobutton_get; 446 rgroup->set = ui_radiobutton_set; 447 448 ui_radiobutton_set(rgroup, rgroup->value); 449 450 UiVarEventData *event = malloc(sizeof(UiVarEventData)); 451 event->obj = obj; 452 event->var = var; 453 event->observers = NULL; 454 event->callback = NULL; 455 event->userdata = NULL; 456 457 UiRadioButtonData *rbdata = malloc(sizeof(UiRadioButtonData)); 458 rbdata->value = rgroup; 459 rbdata->eventdata = event; 460 rbdata->first = first; 461 462 g_signal_connect( 463 rbutton, 464 "toggled", 465 G_CALLBACK(ui_radio_obs), 466 event); 467 g_signal_connect( 468 rbutton, 469 "destroy", 470 G_CALLBACK(destroy_radiobutton), 471 rbdata); 472 } 473 474 if(args.onchange) { 475 UiEventData *event = malloc(sizeof(UiEventData)); 476 event->obj = obj; 477 event->userdata = args.onchangedata; 478 event->callback = args.onchange; 479 event->value = 0; 480 event->customdata = NULL; 481 482 g_signal_connect( 483 rbutton, 484 "toggled", 485 G_CALLBACK(radiobutton_toggled), 486 event); 487 g_signal_connect( 488 rbutton, 489 "destroy", 490 G_CALLBACK(ui_destroy_userdata), 491 event); 492 } 493 494 UI_APPLY_LAYOUT1(current, args); 495 current->container->add(current->container, rbutton, FALSE); 496 497 return rbutton; 498 } 499 500 void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event) { 501 UiInteger *i = event->var->value; 502 503 UiEvent e; 504 e.obj = event->obj; 505 e.window = event->obj->window; 506 e.document = event->obj->ctx->document; 507 e.eventdata = NULL; 508 e.intval = i->get(i); 509 510 ui_notify_evt(i->observers, &e); 511 } 512 513 #if GTK_MAJOR_VERSION >= 4 514 int64_t ui_radiobutton_get(UiInteger *value) { 515 int selection = 0; 516 GSList *ls = value->obj; 517 int i = 0; 518 guint len = g_slist_length(ls); 519 while(ls) { 520 if(gtk_check_button_get_active(GTK_CHECK_BUTTON(ls->data))) { 521 selection = len - i - 1; 522 break; 523 } 524 ls = ls->next; 525 i++; 526 } 527 528 value->value = selection; 529 return selection; 530 } 531 532 void ui_radiobutton_set(UiInteger *value, int64_t i) { 533 GSList *ls = value->obj; 534 int s = g_slist_length(ls) - 1 - i; 535 int j = 0; 536 while(ls) { 537 if(j == s) { 538 gtk_check_button_set_active(GTK_CHECK_BUTTON(ls->data), TRUE); 539 break; 540 } 541 ls = ls->next; 542 j++; 543 } 544 545 value->value = i; 546 } 547 #else 548 int64_t ui_radiobutton_get(UiInteger *value) { 549 int selection = 0; 550 GSList *ls = value->obj; 551 int i = 0; 552 guint len = g_slist_length(ls); 553 while(ls) { 554 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ls->data))) { 555 selection = len - i - 1; 556 break; 557 } 558 ls = ls->next; 559 i++; 560 } 561 562 value->value = selection; 563 return selection; 564 } 565 566 void ui_radiobutton_set(UiInteger *value, int64_t i) { 567 GSList *ls = value->obj; 568 int s = g_slist_length(ls) - 1 - i; 569 int j = 0; 570 while(ls) { 571 if(j == s) { 572 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ls->data), TRUE); 573 break; 574 } 575 ls = ls->next; 576 j++; 577 } 578 579 value->value = i; 580 } 581 #endif 582