#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "toolkit.h"
#include "toolbar.h"
#include "icon.h"
#include "../common/document.h"
#include "../common/properties.h"
#include "../common/menu.h"
#include "../common/toolbar.h"
#include "../common/threadpool.h"
#include <cx/utils.h>
#include <cx/string.h>
#include <cx/printf.h>
#include <pthread.h>
#ifdef UI_APPLICATION
UI_APPLICATION app;
#endif
static const char *application_name;
static ui_callback startup_func;
static void *startup_data;
static ui_callback open_func;
void *open_data;
static ui_callback exit_func;
void *exit_data;
static ui_callback appclose_fnc;
static void *appclose_udata;
static UiObject *active_window;
static int scale_factor =
1;
UIEXPORT void ui_init(
const char *appname,
int argc,
char **argv) {
application_name = appname;
uic_init_global_context();
#if GTK_MAJOR_VERSION >=
4
gtk_init();
#else
gtk_init(&argc, &argv);
#endif
ui_css_init();
uic_docmgr_init();
uic_menu_init();
uic_toolbar_init();
ui_image_init();
uic_load_app_properties();
#if GTK_MAJOR_VERSION >=
4
scale_factor =
1;
#elif defined(
UI_SUPPORTS_SCALE)
scale_factor = gdk_monitor_get_scale_factor(
gdk_display_get_primary_monitor(gdk_display_get_default()));
#endif
}
const char* ui_appname() {
return application_name;
}
void ui_onstartup(ui_callback f,
void *userdata) {
startup_func = f;
startup_data = userdata;
}
void ui_onopen(ui_callback f,
void *userdata) {
open_func = f;
open_data = userdata;
}
void ui_onexit(ui_callback f,
void *userdata) {
exit_func = f;
exit_data = userdata;
}
#ifndef UI_GTK2
static void app_startup(GtkApplication* app, gpointer userdata) {
if(startup_func) {
startup_func(
NULL, startup_data);
}
}
static void app_activate(GtkApplication* app, gpointer userdata) {
printf(
"activate\n");
}
#endif
void ui_main() {
#ifdef UI_APPLICATION
cxmutstr appid = cx_asprintf(
"ui.%s",
application_name ? application_name :
"application1");
app =
UI_APPLICATION_NEW(appid.ptr);
g_signal_connect (app,
"startup",
G_CALLBACK (app_startup),
NULL);
g_signal_connect (app,
"activate",
G_CALLBACK (app_activate),
NULL);
g_application_run(
G_APPLICATION (app),
0,
NULL);
g_object_unref (app);
free(appid.ptr);
#else
if(startup_func) {
startup_func(
NULL, startup_data);
}
gtk_main();
#endif
if(exit_func) {
exit_func(
NULL, exit_data);
}
uic_store_app_properties();
}
#ifndef UI_GTK2
void ui_app_quit() {
g_application_quit(
G_APPLICATION(app));
}
GtkApplication* ui_get_application() {
return GTK_APPLICATION(app);
}
#endif
void ui_show(UiObject *obj) {
gboolean visible = gtk_widget_is_visible(obj->widget);
uic_check_group_widgets(obj->ctx);
#if GTK_MAJOR_VERSION >=
4
gtk_window_present(
GTK_WINDOW(obj->widget));
#elif GTK_MAJOR_VERSION <=
3
gtk_widget_show_all(obj->widget);
#endif
if(!visible) {
obj->ref++;
}
}
void ui_close(UiObject *obj) {
uic_context_prepare_close(obj->ctx);
#if GTK_CHECK_VERSION(
4,
0,
0)
gtk_window_close(
GTK_WINDOW(obj->widget));
#else
gtk_widget_destroy(obj->widget);
#endif
}
static gboolean ui_job_finished(
void *data) {
UiJob *job = data;
UiEvent event;
event.obj = job->obj;
event.window = job->obj->window;
event.document = job->obj->ctx->document;
event.intval =
0;
event.eventdata =
NULL;
job->finish_callback(&event, job->finish_data);
free(job);
return FALSE;
}
static void* ui_jobthread(
void *data) {
UiJob *job = data;
int result = job->job_func(job->job_data);
if(!result && job->finish_callback) {
g_idle_add(ui_job_finished, job);
}
else {
free(job);
}
return NULL;
}
static gboolean ui_idle_func(
void *data) {
UiJob *job = data;
job->job_func(job->job_data);
free(job);
return FALSE;
}
void ui_call_mainthread(ui_threadfunc tf,
void* td) {
UiJob *job = malloc(
sizeof(UiJob));
job->job_func = tf;
job->job_data = td;
job->finish_callback =
NULL;
job->finish_data =
NULL;
job->obj =
NULL;
g_idle_add(ui_idle_func, job);
}
void ui_job(UiObject *obj, ui_threadfunc tf,
void *td, ui_callback f,
void *fd) {
UiJob *job = malloc(
sizeof(UiJob));
job->obj = obj;
job->job_func = tf;
job->job_data = td;
job->finish_callback = f;
job->finish_data = fd;
pthread_t pid;
pthread_create(&pid,
NULL, ui_jobthread, job);
}
void ui_set_enabled(
UIWIDGET widget,
int enabled) {
gtk_widget_set_sensitive(widget, enabled);
}
void ui_set_show_all(
UIWIDGET widget,
int value) {
#if GTK_MAJOR_VERSION <=
3
gtk_widget_set_no_show_all(widget, !value);
#endif
}
void ui_set_visible(
UIWIDGET widget,
int visible) {
#if GTK_MAJOR_VERSION <=
3
if(visible) {
gtk_widget_set_no_show_all(widget,
FALSE);
gtk_widget_show_all(widget);
}
else {
gtk_widget_hide(widget);
}
#endif
}
void ui_clipboard_set(
char *str) {
#if GTK_MAJOR_VERSION >=
4
#else
GtkClipboard *cb = gtk_clipboard_get(
GDK_SELECTION_CLIPBOARD);
gtk_clipboard_set_text(cb, str, strlen(str));
#endif
}
char* ui_clipboard_get() {
#if GTK_MAJOR_VERSION >=
4
return NULL;
#else
GtkClipboard *cb = gtk_clipboard_get(
GDK_SELECTION_CLIPBOARD);
char *str = gtk_clipboard_wait_for_text(cb);
if(str) {
char *copy = strdup(str);
g_free(str);
return copy;
}
else {
return NULL;
}
#endif
}
int ui_get_scalefactor() {
return scale_factor;
}
void ui_destroy_userdata(GtkWidget *object,
void *userdata) {
free(userdata);
}
void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data) {
if(data->var) {
ui_destroy_boundvar(data->obj->ctx, data->var);
}
free(data);
}
void ui_destroy_widget_var(GtkWidget *object, UiVar *var) {
ui_destroy_boundvar(
NULL, var);
}
void ui_destroy_boundvar(UiContext *ctx, UiVar *var) {
uic_unbind_var(var);
if(var->type ==
UI_VAR_SPECIAL) {
ui_free(var->from_ctx, var);
}
else {
ui_free(var->from_ctx, var);
}
}
void ui_set_active_window(UiObject *obj) {
active_window = obj;
}
UiObject *ui_get_active_window() {
return active_window;
}
#if GTK_MAJOR_VERSION >=
3
static GtkCssProvider* ui_gtk_css_provider;
#if GTK_MAJOR_VERSION ==
4
static const char *ui_gtk_css =
"#path-textfield-box {\n"
" background-color: alpha(currentColor, 0.1);"
" border-radius: 6px;"
" padding: 0px;"
"}\n"
".pathbar-extra-button {\n"
" border-top-right-radius: 6px;"
" border-bottom-right-radius: 6px;"
" border-top-left-radius: 0px;"
" border-bottom-left-radius: 0px;"
"}\n"
"#pathbar button {\n"
" margin: 3px;"
" border-radius: 4px;"
" padding-top: 0px;"
" padding-bottom: 0px;"
" padding-left: 8px;"
" padding-right: 8px;"
"}\n"
"#path-textfield-box entry {\n"
" background-color: #00000000;"
" border-top-left-radius: 6px;"
" border-bottom-left-radius: 6px;"
" border-top-right-radius: 0px;"
" border-bottom-right-radius: 0px;"
"}\n"
".pathbar-button-inactive {\n"
" color: alpha(currentColor, 0.5);"
"}\n"
".ui_test {\n"
" background-color: red;\n"
"}\n"
".ui_label_title {\n"
" font-weight: bold;\n"
"}\n"
".ui-listbox-header {\n"
" font-weight: bold;\n"
" margin-left: 10px;\n"
" margin-top: 12px;\n"
" margin-bottom: 10px;\n"
"}\n"
".ui-listbox-header-first {\n"
" font-weight: bold;\n"
" margin-left: 10px;\n"
" margin-top: 4px;\n"
" margin-bottom: 10px;\n"
"}\n"
;
#elif GTK_MAJOR_VERSION ==
3
static const char *ui_gtk_css =
"#path-textfield-box {\n"
" background-color: @theme_base_color;\n"
" border-radius: 5px;\n"
" padding: 0px;\n"
"}\n"
".pathbar-button-inactive {\n"
" color: alpha(currentColor, 0.5);"
"}\n"
".ui_test {\n"
" background-color: red;\n"
"}\n"
".ui_label_title {\n"
" font-weight: bold;\n"
"}\n"
"placessidebar row {\n"
" padding-left: 10px;\n"
"}\n"
".ui-listbox-header {\n"
" font-weight: bold;\n"
" margin-left: 10px;\n"
" margin-top: 12px;\n"
" margin-bottom: 10px;\n"
"}\n"
".ui-listbox-header-first {\n"
" font-weight: bold;\n"
" margin-left: 10px;\n"
" margin-top: 4px;\n"
" margin-bottom: 10px;\n"
"}\n"
;
#endif
void ui_css_init(
void) {
ui_gtk_css_provider = gtk_css_provider_new();
#ifdef UI_GTK3
gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -
1,
NULL);
GdkScreen *screen = gdk_screen_get_default();
gtk_style_context_add_provider_for_screen(
screen,
GTK_STYLE_PROVIDER(ui_gtk_css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
#endif
#ifdef UI_GTK4
#if GTK_MINOR_VERSION <
12
gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -
1);
#else
gtk_css_provider_load_from_string(ui_gtk_css_provider, ui_gtk_css);
#endif
GdkDisplay *display = gdk_display_get_default();
gtk_style_context_add_provider_for_display(display,
GTK_STYLE_PROVIDER(ui_gtk_css_provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
#endif
}
#endif
void ui_set_name_and_style(GtkWidget *widget,
const char *name,
const char *style_classes) {
if(name) {
gtk_widget_set_name(widget, name);
}
if(style_classes) {
cxstring *cls =
NULL;
size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes),
CX_STR(
" "),
128, &cls);
for(
int i=
0;i<numClasses;i++) {
cxmutstr m = cx_strdup(cls[i]);
#if GTK_MAJOR_VERSION >=
4
gtk_widget_add_css_class(widget, m.ptr);
#elif GTK_MAJOR_VERSION >=
3
GtkStyleContext *ctx = gtk_widget_get_style_context(widget);
gtk_style_context_add_class(ctx, m.ptr);
#endif
free(m.ptr);
}
free(cls);
}
}
void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget,
const int *groups) {
if(!groups) {
return;
}
size_t ngroups = uic_group_array_size(groups);
ui_set_widget_ngroups(ctx, widget, groups, ngroups);
}
void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget,
const int *groups,
size_t ngroups) {
if(ngroups >
0) {
uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
ui_set_enabled(widget,
FALSE);
}
}