1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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;
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
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
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
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
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
315
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
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
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
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