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 <string.h> 32 #include <stdbool.h> 33 34 #include "toolkit.h" 35 #include "toolbar.h" 36 #include "icon.h" 37 #include "../common/document.h" 38 #include "../common/properties.h" 39 #include "../common/menu.h" 40 #include "../common/toolbar.h" 41 #include "../common/threadpool.h" 42 43 #include <cx/utils.h> 44 #include <cx/string.h> 45 #include <cx/printf.h> 46 47 #include <pthread.h> 48 49 #ifdef UI_APPLICATION 50 UI_APPLICATION app; 51 #endif 52 53 static const char *application_name; 54 55 static ui_callback startup_func; 56 static void *startup_data; 57 static ui_callback open_func; 58 void *open_data; 59 static ui_callback exit_func; 60 void *exit_data; 61 62 static ui_callback appclose_fnc; 63 static void *appclose_udata; 64 65 static UiObject *active_window; 66 67 static int scale_factor = 1; 68 69 UIEXPORT void ui_init(const char *appname, int argc, char **argv) { 70 application_name = appname; 71 uic_init_global_context(); 72 73 #if GTK_MAJOR_VERSION >= 4 74 gtk_init(); 75 #else 76 gtk_init(&argc, &argv); 77 #endif 78 79 ui_css_init(); 80 uic_docmgr_init(); 81 uic_menu_init(); 82 uic_toolbar_init(); 83 ui_image_init(); 84 uic_load_app_properties(); 85 86 #if GTK_MAJOR_VERSION >= 4 87 scale_factor = 1; // TODO 88 #elif defined(UI_SUPPORTS_SCALE) 89 scale_factor = gdk_monitor_get_scale_factor( 90 gdk_display_get_primary_monitor(gdk_display_get_default())); 91 #endif 92 } 93 94 const char* ui_appname() { 95 return application_name; 96 } 97 98 void ui_onstartup(ui_callback f, void *userdata) { 99 startup_func = f; 100 startup_data = userdata; 101 } 102 103 void ui_onopen(ui_callback f, void *userdata) { 104 open_func = f; 105 open_data = userdata; 106 } 107 108 void ui_onexit(ui_callback f, void *userdata) { 109 exit_func = f; 110 exit_data = userdata; 111 } 112 113 114 #ifndef UI_GTK2 115 static void app_startup(GtkApplication* app, gpointer userdata) { 116 if(startup_func) { 117 startup_func(NULL, startup_data); 118 } 119 } 120 121 static void app_activate(GtkApplication* app, gpointer userdata) { 122 printf("activate\n"); 123 } 124 #endif 125 126 void ui_main() { 127 #ifdef UI_APPLICATION 128 cxmutstr appid = cx_asprintf( 129 "ui.%s", 130 application_name ? application_name : "application1"); 131 app = UI_APPLICATION_NEW(appid.ptr); 132 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); 133 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 134 g_application_run(G_APPLICATION (app), 0, NULL); 135 g_object_unref (app); 136 137 free(appid.ptr); 138 #else 139 if(startup_func) { 140 startup_func(NULL, startup_data); 141 } 142 gtk_main(); 143 #endif 144 if(exit_func) { 145 exit_func(NULL, exit_data); 146 } 147 uic_store_app_properties(); 148 } 149 150 #ifndef UI_GTK2 151 void ui_app_quit() { 152 g_application_quit(G_APPLICATION(app)); 153 } 154 155 GtkApplication* ui_get_application() { 156 return GTK_APPLICATION(app); 157 } 158 #endif 159 160 void ui_show(UiObject *obj) { 161 gboolean visible = gtk_widget_is_visible(obj->widget); 162 163 uic_check_group_widgets(obj->ctx); 164 #if GTK_MAJOR_VERSION >= 4 165 gtk_window_present(GTK_WINDOW(obj->widget)); 166 #elif GTK_MAJOR_VERSION <= 3 167 gtk_widget_show_all(obj->widget); 168 #endif 169 170 if(!visible) { 171 obj->ref++; 172 } 173 } 174 175 void ui_close(UiObject *obj) { 176 uic_context_prepare_close(obj->ctx); 177 #if GTK_CHECK_VERSION(4, 0, 0) 178 gtk_window_close(GTK_WINDOW(obj->widget)); 179 #else 180 gtk_widget_destroy(obj->widget); 181 #endif 182 } 183 184 185 static gboolean ui_job_finished(void *data) { 186 UiJob *job = data; 187 188 UiEvent event; 189 event.obj = job->obj; 190 event.window = job->obj->window; 191 event.document = job->obj->ctx->document; 192 event.intval = 0; 193 event.eventdata = NULL; 194 195 job->finish_callback(&event, job->finish_data); 196 free(job); 197 return FALSE; 198 } 199 200 static void* ui_jobthread(void *data) { 201 UiJob *job = data; 202 int result = job->job_func(job->job_data); 203 if(!result && job->finish_callback) { 204 g_idle_add(ui_job_finished, job); 205 } else { 206 free(job); 207 } 208 return NULL; 209 } 210 211 static gboolean ui_idle_func(void *data) { 212 UiJob *job = data; 213 job->job_func(job->job_data); 214 free(job); 215 return FALSE; 216 } 217 218 void ui_call_mainthread(ui_threadfunc tf, void* td) { 219 UiJob *job = malloc(sizeof(UiJob)); 220 job->job_func = tf; 221 job->job_data = td; 222 job->finish_callback = NULL; 223 job->finish_data = NULL; 224 job->obj = NULL; 225 g_idle_add(ui_idle_func, job); 226 } 227 228 void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { 229 UiJob *job = malloc(sizeof(UiJob)); 230 job->obj = obj; 231 job->job_func = tf; 232 job->job_data = td; 233 job->finish_callback = f; 234 job->finish_data = fd; 235 pthread_t pid; 236 pthread_create(&pid, NULL, ui_jobthread, job); 237 } 238 239 void ui_set_enabled(UIWIDGET widget, int enabled) { 240 gtk_widget_set_sensitive(widget, enabled); 241 } 242 243 void ui_set_show_all(UIWIDGET widget, int value) { 244 // TODO: gtk4 245 #if GTK_MAJOR_VERSION <= 3 246 gtk_widget_set_no_show_all(widget, !value); 247 #endif 248 } 249 250 void ui_set_visible(UIWIDGET widget, int visible) { 251 // TODO: gtk4 252 #if GTK_MAJOR_VERSION <= 3 253 if(visible) { 254 gtk_widget_set_no_show_all(widget, FALSE); 255 gtk_widget_show_all(widget); 256 } else { 257 gtk_widget_hide(widget); 258 } 259 #endif 260 } 261 262 void ui_clipboard_set(char *str) { 263 #if GTK_MAJOR_VERSION >= 4 264 // TODO: gtk4: needs widget 265 #else 266 GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); 267 gtk_clipboard_set_text(cb, str, strlen(str)); 268 #endif 269 } 270 271 char* ui_clipboard_get() { 272 #if GTK_MAJOR_VERSION >= 4 273 // TODO: gtk4: needs widget 274 return NULL; 275 #else 276 GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); 277 char *str = gtk_clipboard_wait_for_text(cb); 278 if(str) { 279 char *copy = strdup(str); 280 g_free(str); 281 return copy; 282 } else { 283 return NULL; 284 } 285 #endif 286 } 287 288 int ui_get_scalefactor() { 289 return scale_factor; 290 } 291 292 void ui_destroy_userdata(GtkWidget *object, void *userdata) { 293 free(userdata); 294 } 295 296 void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data) { 297 if(data->var) { 298 ui_destroy_boundvar(data->obj->ctx, data->var); 299 } 300 free(data); 301 } 302 303 void ui_destroy_widget_var(GtkWidget *object, UiVar *var) { 304 ui_destroy_boundvar(NULL, var); 305 } 306 307 void ui_destroy_boundvar(UiContext *ctx, UiVar *var) { 308 uic_unbind_var(var); 309 310 if(var->type == UI_VAR_SPECIAL) { 311 ui_free(var->from_ctx, var); 312 } else { 313 ui_free(var->from_ctx, var); 314 // TODO: free or unbound 315 //uic_remove_bound_var(ctx, var); 316 } 317 } 318 319 void ui_set_active_window(UiObject *obj) { 320 active_window = obj; 321 } 322 323 UiObject *ui_get_active_window() { 324 return active_window; 325 } 326 327 328 #if GTK_MAJOR_VERSION >= 3 329 330 static GtkCssProvider* ui_gtk_css_provider; 331 332 #if GTK_MAJOR_VERSION == 4 333 static const char *ui_gtk_css = 334 "#path-textfield-box {\n" 335 " background-color: alpha(currentColor, 0.1);" 336 " border-radius: 6px;" 337 " padding: 0px;" 338 "}\n" 339 ".pathbar-extra-button {\n" 340 " border-top-right-radius: 6px;" 341 " border-bottom-right-radius: 6px;" 342 " border-top-left-radius: 0px;" 343 " border-bottom-left-radius: 0px;" 344 "}\n" 345 "#pathbar button {\n" 346 " margin: 3px;" 347 " border-radius: 4px;" 348 " padding-top: 0px;" 349 " padding-bottom: 0px;" 350 " padding-left: 8px;" 351 " padding-right: 8px;" 352 "}\n" 353 "#path-textfield-box entry {\n" 354 " background-color: #00000000;" 355 " border-top-left-radius: 6px;" 356 " border-bottom-left-radius: 6px;" 357 " border-top-right-radius: 0px;" 358 " border-bottom-right-radius: 0px;" 359 "}\n" 360 ".pathbar-button-inactive {\n" 361 " color: alpha(currentColor, 0.5);" 362 "}\n" 363 ".ui_test {\n" 364 " background-color: red;\n" 365 "}\n" 366 ".ui_label_title {\n" 367 " font-weight: bold;\n" 368 "}\n" 369 ".ui-listbox-header {\n" 370 " font-weight: bold;\n" 371 " margin-left: 10px;\n" 372 " margin-top: 12px;\n" 373 " margin-bottom: 10px;\n" 374 "}\n" 375 ".ui-listbox-header-first {\n" 376 " font-weight: bold;\n" 377 " margin-left: 10px;\n" 378 " margin-top: 4px;\n" 379 " margin-bottom: 10px;\n" 380 "}\n" 381 ; 382 383 #elif GTK_MAJOR_VERSION == 3 384 static const char *ui_gtk_css = 385 "#path-textfield-box {\n" 386 " background-color: @theme_base_color;\n" 387 " border-radius: 5px;\n" 388 " padding: 0px;\n" 389 "}\n" 390 ".pathbar-button-inactive {\n" 391 " color: alpha(currentColor, 0.5);" 392 "}\n" 393 ".ui_test {\n" 394 " background-color: red;\n" 395 "}\n" 396 ".ui_label_title {\n" 397 " font-weight: bold;\n" 398 "}\n" 399 "placessidebar row {\n" 400 " padding-left: 10px;\n" 401 "}\n" 402 ".ui-listbox-header {\n" 403 " font-weight: bold;\n" 404 " margin-left: 10px;\n" 405 " margin-top: 12px;\n" 406 " margin-bottom: 10px;\n" 407 "}\n" 408 ".ui-listbox-header-first {\n" 409 " font-weight: bold;\n" 410 " margin-left: 10px;\n" 411 " margin-top: 4px;\n" 412 " margin-bottom: 10px;\n" 413 "}\n" 414 ; 415 #endif 416 417 void ui_css_init(void) { 418 ui_gtk_css_provider = gtk_css_provider_new(); 419 420 #ifdef UI_GTK3 421 gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1, NULL); 422 423 GdkScreen *screen = gdk_screen_get_default(); 424 gtk_style_context_add_provider_for_screen( 425 screen, 426 GTK_STYLE_PROVIDER(ui_gtk_css_provider), 427 GTK_STYLE_PROVIDER_PRIORITY_USER); 428 #endif /* UI_GTK3 */ 429 430 #ifdef UI_GTK4 431 432 433 #if GTK_MINOR_VERSION < 12 434 gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1); 435 #else 436 gtk_css_provider_load_from_string(ui_gtk_css_provider, ui_gtk_css); 437 #endif /* GTK_MINOR_VERSION < 12 */ 438 439 GdkDisplay *display = gdk_display_get_default(); 440 gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(ui_gtk_css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); 441 442 #endif /* UI_GTK4 */ 443 } 444 445 446 447 #endif 448 449 void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style_classes) { 450 if(name) { 451 gtk_widget_set_name(widget, name); 452 } 453 if(style_classes) { 454 cxstring *cls = NULL; 455 size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), CX_STR(" "), 128, &cls); 456 for(int i=0;i<numClasses;i++) { 457 cxmutstr m = cx_strdup(cls[i]); 458 #if GTK_MAJOR_VERSION >= 4 459 gtk_widget_add_css_class(widget, m.ptr); 460 #elif GTK_MAJOR_VERSION >= 3 461 GtkStyleContext *ctx = gtk_widget_get_style_context(widget); 462 gtk_style_context_add_class(ctx, m.ptr); 463 #endif 464 free(m.ptr); 465 } 466 free(cls); 467 468 } 469 } 470 471 void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups) { 472 if(!groups) { 473 return; 474 } 475 size_t ngroups = uic_group_array_size(groups); 476 ui_set_widget_ngroups(ctx, widget, groups, ngroups); 477 } 478 479 void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups) { 480 if(ngroups > 0) { 481 uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); 482 ui_set_enabled(widget, FALSE); 483 } 484 } 485