--- a/ui/motif/window.c Sun Dec 07 20:00:33 2025 +0100 +++ b/ui/motif/window.c Sat Dec 13 15:58:58 2025 +0100 @@ -28,15 +28,22 @@ #include <stdio.h> #include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> + +#include "window.h" #include "toolkit.h" #include "menu.h" #include "toolbar.h" #include "container.h" -#include "../ui/window.h" +#include "pathbar.h" #include "../common/context.h" +#include "../common/utils.h" #include "Grid.h" +#include "Fsb.h" #include <cx/mempool.h> @@ -50,7 +57,7 @@ uic_object_destroy(obj); nwindows--; if(nwindows == 0) { - ui_exit_mainloop(); + ui_app_quit(); } } @@ -67,20 +74,28 @@ } -static UiObject* create_window(const char *title, void *window_data, Boolean simple) { +static UiObject* create_window(const char *title, Boolean simple) { CxMempool *mp = cxMempoolCreateSimple(256); const CxAllocator *a = mp->allocator; UiObject *obj = uic_object_new_toplevel(); - obj->window = window_data; obj->destroy = ui_window_widget_destroy; + int window_width = window_default_width; + int window_height = window_default_height; + if(!simple) { + ui_get_window_default_width(&window_width, &window_height); + } + + UiMotifAppWindow *appwindow = cxZalloc(a, sizeof(UiMotifAppWindow)); + ui_object_set(obj, "ui_motif_app_window", appwindow); + Arg args[16]; int n = 0; XtSetArg(args[n], XmNtitle, title); n++; XtSetArg(args[n], XmNminWidth, 100); n++; XtSetArg(args[n], XmNminHeight, 50); n++; - XtSetArg(args[n], XmNwidth, window_default_width); n++; - XtSetArg(args[n], XmNheight, window_default_height); n++; + XtSetArg(args[n], XmNwidth, window_width); n++; + XtSetArg(args[n], XmNheight, window_height); n++; Widget toplevel = XtAppCreateShell( ui_appname(), @@ -110,7 +125,7 @@ // menu if(!simple) { - ui_create_menubar(obj, window); + appwindow->menubar = ui_create_menubar(obj, window); } // content frame @@ -135,10 +150,161 @@ return obj; } -UiObject* ui_window(const char *title, void *window_data) { - return create_window(title, window_data, FALSE); +UiObject* ui_window(const char *title) { + return create_window(title, FALSE); +} + +UiObject* ui_simple_window(const char *title) { + return create_window(title, TRUE); +} + +void ui_window_size(UiObject *obj, int width, int height) { + XtVaSetValues(obj->widget, XmNwidth, width, XmNheight, height, NULL); +} + +void ui_window_default_size(int width, int height) { + window_default_width = width; + window_default_height = height; +} + +void ui_window_menubar_set_visible(UiObject *obj, UiBool visible) { + UiMotifAppWindow *window = ui_object_get(obj, "ui_motif_app_window"); + if(window) { + if(window->menubar) { + ui_set_visible(window->menubar, visible); + } + } else { + fprintf(stderr, "Error: obj is not an application window\n"); + } +} + +static Atom net_wm_state; +static Atom net_wm_state_fullscreen; +static int net_wm_atoms_initialized = 0; + +void ui_window_fullscreen(UiObject *obj, UiBool fullscreen) { + Display *dpy = XtDisplay(obj->widget); + + // init net_wm_state atoms + if(!net_wm_atoms_initialized) { + net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); + net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + net_wm_atoms_initialized = 1; + } + + XEvent ev; + memset(&ev, 0, sizeof(XEvent)); + ev.type = ClientMessage; + ev.xclient.window = XtWindow(obj->widget); + ev.xclient.message_type = net_wm_state; + ev.xclient.format = 32; + ev.xclient.data.l[0] = fullscreen ? 1 : 0; + ev.xclient.data.l[1] = net_wm_state_fullscreen; + ev.xclient.data.l[2] = 0; + XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask | SubstructureRedirectMask, &ev); +} + +static void filedialog_event(UiEventData *event, int result, UiFileList flist) { + UiEvent evt; + evt.obj = event->obj; + evt.document = evt.obj->ctx->document; + evt.window = evt.obj->window; + evt.intval = result; + + evt.eventdata = &flist; + evt.eventdatatype = UI_EVENT_DATA_FILE_LIST; + + if(event->callback) { + event->callback(&evt, event->userdata); + } } -UiObject* ui_simple_window(const char *title, void *window_data) { - return create_window(title, window_data, TRUE); +static void filedialog_select( + Widget widget, + UiEventData *data, + XmFileSelectionBoxCallbackStruct *selection) +{ + UiFileList flist; + + char *value = NULL; + XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value); + flist.files = &value; + flist.nfiles = 1; + + filedialog_event(data, 1, flist); + + XtFree(value); + + XtUnmanageChild(widget); + XtDestroyWidget(widget); +} + +static void filedialog_cancel( + Widget widget, + UiEventData *data, + XmFileSelectionBoxCallbackStruct *selection) +{ + UiFileList flist; + flist.files = NULL; + flist.nfiles = 0; + filedialog_event(data, 0, flist); + + XtUnmanageChild(widget); + XtDestroyWidget(widget); } + +void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { + Widget dialog = XnCreateFileSelectionDialog(obj->widget, "dialog", NULL, 0); + + UiEventData *data = malloc(sizeof(UiEventData)); + memset(data, 0, sizeof(UiEventData)); + data->obj = obj; + data->callback = file_selected_callback; + data->userdata = cbdata; + + XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, data); + XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_cancel, data); + //XtAddCallback(dialog, XmNhelpCallback, (XtCallbackProc)filedialog_help, wd); + + XtManageChild(dialog); +} + +void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata) { + Arg args[16]; + int n = 0; + + // Save File Dialog needs this parameter + XtSetArg(args[n], XnNfsbType, FILEDIALOG_SAVE); n++; + char *selectedpath = (char*)name; + if(name) { + if(name[0] != '/') { + char cwd[PATH_MAX]; + if(getcwd(cwd, PATH_MAX)) { + pathbar_concat_path(cwd, name); + } else { + fprintf(stderr, "Error: getcwd failed: %s\n", strerror(errno)); + selectedpath = NULL; + } + } + if(selectedpath) { + XtSetArg(args[n], XnNselectedPath, selectedpath); n++; + } + } + Widget dialog = XnCreateFileSelectionDialog(obj->widget, "dialog", args, n); + + UiEventData *data = malloc(sizeof(UiEventData)); + memset(data, 0, sizeof(UiEventData)); + data->obj = obj; + data->callback = file_selected_callback; + data->userdata = cbdata; + + XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, data); + XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_cancel, data); + //XtAddCallback(dialog, XmNhelpCallback, (XtCallbackProc)filedialog_help, wd); + + XtManageChild(dialog); + + if(selectedpath != name) { + free(selectedpath); + } +}