]> uap-core.de Git - note.git/commitdiff
update toolkit
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 6 Apr 2025 12:48:43 +0000 (14:48 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 6 Apr 2025 12:48:43 +0000 (14:48 +0200)
15 files changed:
ui/common/context.c
ui/common/context.h
ui/common/types.c
ui/gtk/container.c
ui/gtk/menu.c
ui/gtk/menu.h
ui/motif/list.c
ui/motif/menu.c
ui/motif/menu.h
ui/motif/text.c
ui/motif/text.h
ui/motif/widget.c
ui/qt/button.cpp
ui/qt/toolkit.cpp
ui/qt/widget.cpp

index 626f149f2e2a0b4cd0bf1d4b995498a2aea89ea6..db9d85038c902dca47d72188717d219269bf86cb 100644 (file)
@@ -58,6 +58,7 @@ UiContext* uic_context(UiObject *toplevel, CxMempool *mp) {
     memset(ctx, 0, sizeof(UiContext));
     ctx->mp = mp;
     ctx->allocator = mp->allocator;
+    ctx->destroy_handler = cxArrayListCreate(ctx->allocator, NULL, sizeof(UiDestroyHandler), 16);
     ctx->obj = toplevel;
     ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16);
     
@@ -82,6 +83,13 @@ UiContext* uic_root_context(UiContext *ctx) {
     return ctx->parent ? uic_root_context(ctx->parent) : ctx;
 }
 
+void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *data) {
+    UiDestroyHandler handler;
+    handler.destructor = func;
+    handler.data = data;
+    cxListAdd(ctx->destroy_handler, &handler);
+}
+
 void uic_context_prepare_close(UiContext *ctx) {
     cxListClear(ctx->groups);
     cxListClear(ctx->group_widgets);
@@ -120,11 +128,12 @@ static void uic_context_unbind_vars(UiContext *ctx) {
     CxMapIterator mi = cxMapIterator(ctx->vars);
     cx_foreach(CxMapEntry*, entry, mi) {
         UiVar *var = entry->value;
-        if(var->from && var->from_ctx && var->from_ctx != ctx) {
+        // var->from && var->from_ctx && var->from_ctx != ctx
+        if(var->from) {
             uic_save_var2(var);
             uic_copy_binding(var, var->from, FALSE);
-            cxMapPut(var->from_ctx->vars_unbound, *entry->key, var->from);
-            var->from_ctx = ctx;
+            cxMapPut(var->from->from_ctx->vars_unbound, *entry->key, var->from);
+            var->from = NULL;
         }
     }
     
@@ -209,6 +218,7 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
     var = ui_malloc(ctx, sizeof(UiVar));
     var->type = type;
     var->value = uic_create_value(ctx, type);
+    var->original_value = NULL;
     var->from = NULL;
     var->from_ctx = ctx;
 
@@ -227,6 +237,7 @@ UiVar* uic_create_value_var(UiContext* ctx, void* value) {
     var->from = NULL;
     var->from_ctx = ctx;
     var->value = value;
+    var->original_value = NULL;
     var->type = UI_VAR_SPECIAL;
     return var;
 }
@@ -286,10 +297,19 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
     }
     
     void *fromvalue = from->value;
+    void *tovalue = to->value;
     // update var
     if(copytodoc) {
-        to->from = from;
-        to->from_ctx = from->from_ctx;
+        to->from = from; // from which UiVar are the bindings copied
+        from->original_value = fromvalue; // save original value otherwise it would be lost
+        // widgets store a reference to the UiVar with their value
+        // the UiVar object must be updated to contain the current value object
+        from->value = tovalue;
+    } else {
+        if(to->original_value) {
+            to->value = to->original_value;
+            tovalue = to->value;
+        }
     }
     
     ui_setop_enable(TRUE);
@@ -301,7 +321,7 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
         case UI_VAR_SPECIAL: break;
         case UI_VAR_INTEGER: {
             UiInteger *f = fromvalue;
-            UiInteger *t = to->value;
+            UiInteger *t = tovalue;
             if(!f->obj) break;
             uic_int_copy(f, t);
             t->set(t, t->value);
@@ -309,7 +329,7 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
         }
         case UI_VAR_DOUBLE: {
             UiDouble *f = fromvalue;
-            UiDouble *t = to->value;
+            UiDouble *t = tovalue;
             if(!f->obj) break;
             uic_double_copy(f, t);
             t->set(t, t->value);
@@ -317,48 +337,32 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
         }
         case UI_VAR_STRING: {
             UiString *f = fromvalue;
-            UiString *t = to->value;
+            UiString *t = tovalue;
             if(!f->obj) break;
             uic_string_copy(f, t);
             char *tvalue = t->value.ptr ? t->value.ptr : "";
+            char *fvalue = f->value.ptr ? f->value.ptr : "";
             t->set(t, tvalue);
             break;
         }
         case UI_VAR_TEXT: {
             UiText *f = fromvalue;
-            UiText *t = to->value;
+            UiText *t = tovalue;
             if(!f->obj) break;
             uic_text_copy(f, t);
             t->restore(t);
             break;
         }
-        case UI_VAR_LIST: {
-            // TODO: not sure how correct this is
-
-            UiList *f = from->value;
-            UiList *t = to->value;
-            if (f->obj) {
-                t->obj = f->obj;
-                t->update = f->update;
-                t->getselection = f->getselection;
-                t->setselection = f->setselection;
-            }
-
-            UiVar tmp = *from;
-            *from = *to;
-            *to = tmp;
-
-            UiList* t2 = to->value;
-            if(t->update) {
-                t->update(t, -1);
-            }
-            ui_notify(t2->observers, NULL); // TODO: why not t?
-            
+        case UI_VAR_LIST: {         
+            UiList *f = fromvalue;
+            UiList *t = tovalue;
+            uic_list_copy(f, t);
+            ui_list_update(t);
             break;
         }
         case UI_VAR_RANGE: {
             UiRange *f = fromvalue;
-            UiRange *t = to->value;
+            UiRange *t = tovalue;
             if(!f->obj) break;
             uic_range_copy(f, t);
             t->setextent(t, t->extent);
@@ -368,7 +372,7 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
         }
         case UI_VAR_GENERIC: {
             UiGeneric *f = fromvalue;
-            UiGeneric *t = to->value;
+            UiGeneric *t = tovalue;
             if(!f->obj) break;
             uic_generic_copy(f, t);
             t->set(t, t->value, t->type);
@@ -473,6 +477,10 @@ void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) {
 }
 
 UIEXPORT void ui_context_destroy(UiContext *ctx) {
+    CxIterator i = cxListIterator(ctx->destroy_handler);
+    cx_foreach(UiDestroyHandler *, h, i) {
+        h->destructor(h->data);
+    }
     cxMempoolFree(ctx->mp);
 }
 
index f07b6da02eaae547ba5a63fc12b0b0104e9cb08d..e0ce3deb9cb2430ddbd012c605bf8f8ce1512b0a 100644 (file)
 extern "C" {
 #endif
 
-typedef struct UiVar         UiVar;
-typedef struct UiListPtr     UiListPtr;
-typedef struct UiListVar     UiListVar;
-typedef struct UiGroupWidget UiGroupWidget;
+typedef struct UiVar            UiVar;
+typedef struct UiListPtr        UiListPtr;
+typedef struct UiListVar        UiListVar;
+typedef struct UiGroupWidget    UiGroupWidget;
+typedef struct UiDestroyHandler UiDestroyHandler;
 
 typedef enum UiVarType {
     UI_VAR_SPECIAL = 0,
@@ -61,6 +62,7 @@ struct UiContext {
     UiObject      *obj;
     CxMempool *mp;
     const CxAllocator *allocator;
+    CxList *destroy_handler;
     
     void          *document;
     CxList        *documents;
@@ -93,8 +95,9 @@ struct UiContext {
 // UiVar replacement, rename it to UiVar when finished
 struct UiVar {
     void      *value;
+    void      *original_value;
     UiVarType type;
-    UiVar    *from;
+    UiVar     *from;
     UiContext *from_ctx;
 };
 
@@ -105,11 +108,17 @@ struct UiGroupWidget {
     int           numgroups;
 };
 
+struct UiDestroyHandler {
+    cx_destructor_func destructor;
+    void *data;
+};
+
 
 void uic_init_global_context(void);
 
 UiContext* uic_context(UiObject *toplevel, CxMempool *mp);
 UiContext* uic_root_context(UiContext *ctx);
+void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *data);
 void uic_context_set_document(UiContext *ctx, void *document); // deprecated
 void uic_context_detach_document(UiContext *ctx); // deprecated
 
@@ -117,6 +126,8 @@ void uic_context_prepare_close(UiContext *ctx);
 
 void uic_context_attach_document(UiContext *ctx, void *document);
 void uic_context_detach_document2(UiContext *ctx, void *document);
+void uic_context_attach_context(UiContext *ctx, UiContext *doc_ctx);
+void uic_context_detach_context(UiContext *ctx, UiContext *doc_ctx);
 void uic_context_detach_all(UiContext *ctx);
 
 UiVar* uic_get_var(UiContext *ctx, const char *name);
index 8bd07a6f3f548b0f743a146c5bb507f72880b89e..a704b8593f5e18198499207167e44a9035415c6e 100644 (file)
@@ -452,6 +452,8 @@ void uic_range_copy(UiRange *from, UiRange *to) {
 
 void uic_list_copy(UiList *from, UiList *to) {
     to->update = from->update;
+    to->getselection = from->getselection;
+    to->setselection = from->setselection;
     to->obj = from->obj;
 }
 
index 054a7ceb880802a780fc4d40c4315a45f58ff0df..41c7924c45fec35c37547057e59aa9b53260e6a0 100644 (file)
@@ -1116,7 +1116,7 @@ static void remove_item(void *data, void *item) {
 
 static void update_itemlist(UiList *list, int c) {
     UiGtkItemListContainer *ct = list->obj;
-      
+    
     CxMap *new_items = cxHashMapCreateSimple(CX_STORE_POINTERS);
     new_items->collection.advanced_destructor = remove_item;
     new_items->collection.destructor_data = ct;
@@ -1146,7 +1146,7 @@ static void update_itemlist(UiList *list, int c) {
     // add all items
     int index = 0;
     elm = list->first(list);
-    while(elm) {   
+    while(elm) {
         CxHashKey key = cx_hash_key(&elm, sizeof(void*));
         UiObject *item_obj = cxMapGet(ct->current_items, key);
         if(item_obj) {
@@ -1225,7 +1225,7 @@ UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs args) {
                 "destroy",
                 G_CALLBACK(destroy_itemlist_container),
                 container);
-       
+    
     return box;
 }
 
index 085c4db00dd4e5ed09145c16bfbdaf04539e7d26..a54a34b86b20b4e74accbe5e724eb0c1acfb4f59 100644 (file)
@@ -233,6 +233,20 @@ void add_checkitemnv_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject
 }
 */
 
+static void menuitem_list_remove_binding(void *obj) {
+    UiActiveMenuItemList *ls = obj;
+    UiList *list = ls->var->value;
+    CxList *bindings = list->obj;
+    if(bindings) {
+        (void)cxListFindRemove(bindings, obj);
+        if(cxListSize(bindings) == 0) {
+            cxListFree(bindings);
+            list->obj = NULL;
+            list->update = NULL;
+        }
+    }
+}
+
 void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
     UiMenuItemList *il = (UiMenuItemList*)item;
     const CxAllocator *a = obj->ctx->allocator;
@@ -247,21 +261,45 @@ void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObje
     ls->oldcount = 0;
     ls->getvalue = il->getvalue;
     
-    UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
-    ls->list = var->value;
+    //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
+    ls->var = var;
+    if(var) {
+        UiList *list = var->value;
+        list->update = ui_menulist_update;
+        list->getselection = NULL;
+        list->setselection = NULL;
+        
+        // It is possible, that the UiVar is from a global shared context,
+        // used by multiple windows. To support this usecase, the list->obj
+        // binding object is a list of all connected UiActiveMenuItemList.
+        CxList *bindings = list->obj;
+        if(!bindings) {
+            bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+            list->obj = bindings;
+        }
+        cxListAdd(bindings, ls);
+        
+        // The destruction of the toplevel obj must remove the menulist binding
+        uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls);
+        
+        ui_update_menuitem_list(ls);
+    }
     
     ls->callback = il->callback;
     ls->userdata = il->userdata;
-    
-    UiObserver *observer = ui_observer_new((ui_callback)ui_update_menuitem_list, ls);
-    ls->list->observers = ui_obsvlist_add(ls->list->observers, observer);
-    uic_list_register_observer_destructor(obj->ctx, ls->list, observer);
-    
-    ui_update_menuitem_list(NULL, ls);
+}
+
+void ui_menulist_update(UiList *list, int ignored) {
+    CxList *bindings = list->obj;
+    CxIterator i = cxListIterator(bindings);
+    cx_foreach(UiActiveMenuItemList *, ls, i) {
+        ui_update_menuitem_list(ls);
+    }
 }
 
 
-void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) {    
+void ui_update_menuitem_list(UiActiveMenuItemList *list) {    
     // remove old items
     if(list->oldcount > 0) {
         int i = 0;
@@ -276,7 +314,9 @@ void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) {
         }
     }
     
-    void* elm = ui_list_first(list->list);
+    UiList *ls = list->var->value;
+    
+    void* elm = ui_list_first(ls);
     if(elm) {
         GtkWidget *widget = gtk_separator_menu_item_new();
         gtk_menu_shell_insert(list->menu, widget, list->index);
@@ -312,7 +352,7 @@ void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) {
                 event);
         }
         
-        elm = ui_list_next(list->list);
+        elm = ui_list_next(ls);
         i++;
     }
     
@@ -506,6 +546,20 @@ void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *ob
     
 }
 
+static void menuitem_list_remove_binding(void *obj) {
+    UiActiveGMenuItemList *ls = obj;
+    UiList *list = ls->var->value;
+    CxList *bindings = list->obj;
+    if(bindings) {
+        (void)cxListFindRemove(bindings, obj);
+        if(cxListSize(bindings) == 0) {
+            cxListFree(bindings);
+            list->obj = NULL;
+            list->update = NULL;
+        }
+    }
+}
+
 void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) {
     UiMenuItemList *il = (UiMenuItemList*)item; 
     
@@ -521,17 +575,34 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject
     ls->oldcount = 0;
     ls->getvalue = il->getvalue;
     
-    UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
     ls->var = var;
-    UiList *list = var->value;
+    if(var) {
+        UiList *list = var->value;
+        list->update = ui_menulist_update;
+        list->getselection = NULL;
+        list->setselection = NULL;
+        
+        // It is possible, that the UiVar is from a global shared context,
+        // used by multiple windows. To support this usecase, the list->obj
+        // binding object is a list of all connected UiActiveMenuItemList.
+        CxList *bindings = list->obj;
+        if(!bindings) {
+            bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+            list->obj = bindings;
+        }
+        cxListAdd(bindings, ls);
+        
+        // The destruction of the toplevel obj must remove the menulist binding
+        uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls);
+        
+        ui_update_gmenu_item_list(ls);
+    }
     
     ls->callback = il->callback;
     ls->userdata = il->userdata;
     
-    UiObserver *observer = ui_observer_new((ui_callback)ui_update_gmenu_item_list, ls);
-    list->observers = ui_obsvlist_add(list->observers, observer);
-    uic_list_register_observer_destructor(obj->ctx, list, observer);
-    
     GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i"));
     g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
     snprintf(ls->action, 32, "win.%s", item->id);
@@ -554,8 +625,6 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject
             "destroy",
             G_CALLBACK(ui_destroy_userdata),
             event);
-    
-    ui_update_gmenu_item_list(NULL, ls);
 }
 
 void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
@@ -588,7 +657,15 @@ void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* par
     
 }
 
-void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list) {
+void ui_menulist_update(UiList *list, int ignored) {
+    CxList *bindings = list->obj;
+    CxIterator i = cxListIterator(bindings);
+    cx_foreach(UiActiveGMenuItemList *, ls, i) {
+        ui_update_gmenu_item_list(ls);
+    }
+}
+
+void ui_update_gmenu_item_list(UiActiveGMenuItemList *list) {
     // remove old items
     for(int i=0;i<list->oldcount;i++) {
         g_menu_remove(list->menu, list->index);
index b8bff034d3b4485092d774689cf5faca53dbfa0e..dc6240cdb55dd409c929150f09748bdb410ec600 100644 (file)
@@ -55,10 +55,10 @@ struct UiActiveMenuItemList {
     GtkMenuShell     *menu;
     int              index;
     int              oldcount;
-    UiList           *list;
-    ui_getvaluefunc getvalue;
-    ui_callback     callback;
-    void            *userdata;
+    UiVar            *var;
+    ui_getvaluefunc  getvalue;
+    ui_callback      callback;
+    void             *userdata;
 };
 
 void ui_add_menu_items(GtkWidget *parent, int i, UiMenu *menu, UiObject *obj);
@@ -72,7 +72,8 @@ void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *
 void add_checkitemnv_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj);
 void add_menuitem_list_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj);
 
-void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list);
+void ui_menulist_update(UiList *list, int ignored);
+void ui_update_menuitem_list(UiActiveMenuItemList *list);
 void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event);
 void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event);
 int64_t ui_checkitem_get(UiInteger *i);
@@ -108,7 +109,8 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject
 
 void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event);
 void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event);
-void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list);
+void ui_menulist_update(UiList *list, int ignored);
+void ui_update_gmenu_item_list(UiActiveGMenuItemList *list);
 
 #endif
 
index 5da5a0b4d38c5d3d369a37fd268e39c9ec9d8646..f53cbb7b66b159742490afdc561d2ef411073f75 100644 (file)
@@ -206,18 +206,19 @@ static void ui_dropdown_selection(
         UiListView *listview,
         XmComboBoxCallbackStruct *cb)
 {
-    UiListSelection sel = { 0, NULL };
-    if(cb->item_position > 0) {
-        sel.count = 1;
-        sel.rows = malloc(sizeof(int));
-        sel.rows[0] = cb->item_position-1;
+    int index = cb->item_position;
+    void *elm = NULL;
+    if(listview->var) {
+        UiList *list = listview->var->value;
+        elm = ui_list_get(list, index);
     }
+    
     UiEvent event;
     event.obj = listview->obj;
     event.window = event.obj->window;
     event.document = event.obj->ctx->document;
-    event.eventdata = &sel;
-    event.intval = 0;
+    event.eventdata = elm;
+    event.intval = index;
     if(listview->onactivate) {
         listview->onactivate(&event, listview->onactivatedata);
     }
index 15fa1824ae2b11ed021335c3f0f2bc0fd560a7f0..c75d64af38ed7945adfec75cc06a7cf3e7fa28ff 100644 (file)
@@ -202,6 +202,20 @@ void add_radioitem_widget(Widget p, int index, UiMenuItemI *item, UiObject *obj)
     ui_bind_radiobutton(obj, button, NULL, it->varname, it->callback, it->userdata, 0);
 }
 
+static void menuitem_list_remove_binding(void *obj) {
+    UiActiveMenuItemList *ls = obj;
+    UiList *list = ls->var->value;
+    CxList *bindings = list->obj;
+    if(bindings) {
+        (void)cxListFindRemove(bindings, obj);
+        if(cxListSize(bindings) == 0) {
+            cxListFree(bindings);
+            list->obj = NULL;
+            list->update = NULL;
+        }
+    }
+}
+
 void add_menuitem_list_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) {
     UiMenuItemList *il = (UiMenuItemList*)item;
     const CxAllocator *a = obj->ctx->allocator;
@@ -218,17 +232,40 @@ void add_menuitem_list_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj)
     ls->userdata = il->userdata;
     ls->addseparator = il->addseparator;
     
-    ls->var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); //uic_widget_var(obj->ctx, obj->ctx, NULL, il->varname, UI_VAR_LIST);
-    UiList *list = ls->var->value;
-    
-    UiObserver *observer = ui_observer_new((ui_callback)ui_update_menuitem_list, ls);
-    list->observers = ui_obsvlist_add(list->observers, observer);
-    uic_list_register_observer_destructor(obj->ctx, list, observer);
-    
-    ui_update_menuitem_list(NULL, ls);
+    //ls->var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    ls->var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
+    if(ls->var) {
+        UiList *list = ls->var->value;
+        list->update = ui_menulist_update;
+        list->getselection = NULL;
+        list->setselection = NULL;
+        
+        // It is possible, that the UiVar is from a global shared context,
+        // used by multiple windows. To support this usecase, the list->obj
+        // binding object is a list of all connected UiActiveMenuItemList.
+        CxList *bindings = list->obj;
+        if(!bindings) {
+            bindings = cxLinkedListCreate(ls->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+            list->obj = bindings;
+        }
+        cxListAdd(bindings, ls);
+        
+        // The destruction of the toplevel obj must remove the menulist binding
+        uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls);
+        
+        ui_update_menuitem_list(ls);
+    }
+}
+
+void ui_menulist_update(UiList *list, int ignored) {
+    CxList *bindings = list->obj;
+    CxIterator i = cxListIterator(bindings);
+    cx_foreach(UiActiveMenuItemList *, ls, i) {
+        ui_update_menuitem_list(ls);
+    }
 }
 
-void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) {
+void ui_update_menuitem_list(UiActiveMenuItemList *list) {
     XmString s = NULL;
     Arg args[4];
     int n;
index 20f0a9870629a5d8b3a394e8df626ba5a028b432..efa99cefc5d20cf88b8832f6be3d16ac9b7c792b 100644 (file)
@@ -64,7 +64,8 @@ void add_radioitem_widget(Widget p, int index, UiMenuItemI *item, UiObject *obj)
 void add_checkitemnv_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj);
 void add_menuitem_list_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj);
 
-void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list);
+void ui_menulist_update(UiList *list, int ignored);
+void ui_update_menuitem_list(UiActiveMenuItemList *list);
 
 #ifdef __cplusplus
 }
index 82d75fc6f27e86c9d7039b63af11dce0d9787beb..bcb9be4efe79cc45fd7b73db29fcec36d868a128 100644 (file)
@@ -68,6 +68,9 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) {
             value->value.ptr = NULL;
         }
         
+        value->save = ui_textarea_save;
+        value->restore = ui_textarea_restore;
+        value->destroy = ui_textarea_text_destroy;
         value->set = ui_textarea_set;
         value->get = ui_textarea_get;
         value->getsubstr = ui_textarea_getsubstr;
@@ -79,8 +82,8 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) {
         value->value.ptr = NULL;
         value->obj = widget;
         
-        if(!value->undomgr) {
-            value->undomgr = ui_create_undomgr();
+        if(!value->data2) {
+            value->data2 = ui_create_undomgr();
         }
         
         XtAddCallback(
@@ -103,6 +106,25 @@ char* ui_textarea_get(UiText *text) {
     return str;
 }
 
+void ui_textarea_save(UiText *text) {
+    (void)ui_textarea_get(text);
+}
+
+void ui_textarea_restore(UiText *text) {
+    if(text->value.ptr) {
+        ui_textarea_set(text, text->value.ptr);
+    }
+}
+
+void ui_textarea_text_destroy(UiText *text) {
+    if(text->value.free) {
+        text->value.free(text->value.ptr);
+    }
+    if(text->data2) {
+        ui_destroy_undomgr(text->data2);
+    }
+}
+
 void ui_textarea_set(UiText *text, const char *str) {
     XmTextSetString(text->obj, (char*)str);
     if(text->value.ptr) {
@@ -201,13 +223,13 @@ void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data) {
         // TODO: bug, fix
         return;
     }
-    if(!value->undomgr) {
-        value->undomgr = ui_create_undomgr();
+    if(!value->data2) {
+        value->data2 = ui_create_undomgr();
     }
     
     XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data;
     int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE;
-    UiUndoMgr *mgr = value->undomgr;
+    UiUndoMgr *mgr = value->data2;
     if(!mgr->event) {
         return;
     }
@@ -307,7 +329,7 @@ void ui_free_textbuf_op(UiTextBufOp *op) {
 
 
 void ui_text_undo(UiText *value) {
-    UiUndoMgr *mgr = value->undomgr;
+    UiUndoMgr *mgr = value->data2;
     
     if(mgr->cur) {
         UiTextBufOp *op = mgr->cur;
@@ -328,7 +350,7 @@ void ui_text_undo(UiText *value) {
 }
 
 void ui_text_redo(UiText *value) {
-    UiUndoMgr *mgr = value->undomgr;
+    UiUndoMgr *mgr = value->data2;
     
     UiTextBufOp *elm = NULL;
     if(mgr->cur) {
@@ -411,7 +433,9 @@ UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args) {
 }
 
 char* ui_textfield_get(UiString *str) {
-    str->value.free(str->value.ptr);
+    if(str->value.free) {
+        str->value.free(str->value.ptr);
+    }
     char *value = XmTextFieldGetString(str->obj);
     str->value.ptr = value;
     str->value.free = (ui_freefunc)XtFree;
@@ -420,8 +444,10 @@ char* ui_textfield_get(UiString *str) {
 
 void ui_textfield_set(UiString *str, const char *value) {
     XmTextFieldSetString(str->obj, (void*)value);
+    if(str->value.free) {
+        str->value.free(str->value.ptr);
+    }
     str->value.ptr = NULL;
-    str->value.free(str->value.ptr);
 }
 
 
index d681b30c1b04ff2afb4756e66e5258e0ea65b1d3..a7e4f8572cad3f0eea41899c25aed8b266b03a7a 100644 (file)
@@ -64,6 +64,9 @@ typedef struct UiTextArea {
     int last_selection_state;
 } UiTextArea;
 
+void ui_textarea_save(UiText *text);
+void ui_textarea_restore(UiText *text);
+void ui_textarea_text_destroy(UiText *text);
 char* ui_textarea_get(UiText *text);
 void ui_textarea_set(UiText *text, const char *str);
 char* ui_textarea_getsubstr(UiText *text, int begin, int end);
index 4475e6e470f4ff276978a820d5f6c720e57c067f..e8b566aafc16a9722ea80ff0085819eef949167e 100644 (file)
@@ -69,3 +69,11 @@ UIEXPORT UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
     
     return widget;
 }
+
+void ui_widget_set_size(UIWIDGET w, int width, int height) {
+    
+}
+
+void ui_widget_redraw(UIWIDGET w) {
+    
+}
index 311c611430e289fc4e8dfb1fa66971738363fa24..1ca6f4e8c3d83a77649cf990a753806765fb682f 100644 (file)
@@ -163,3 +163,64 @@ void ui_checkbox_set(UiInteger *value, int64_t i) {
         button->setChecked(true);
     }
 }
+
+
+static void radiobutton_event(UiEvent *event, UiEventWrapper *wrapper) {
+    if(wrapper->var) {
+        UiInteger *value = wrapper->var->value;
+        event->eventdata = value;
+        event->intval = ui_get(value);
+    }
+}
+
+UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs args) {
+    UiContainerPrivate *ctn = ui_obj_container(obj);
+    UI_APPLY_LAYOUT(ctn->layout, args);
+    
+    QString str = QString::fromUtf8(args.label);
+    QRadioButton *button = new QRadioButton(str);
+    button->setAutoExclusive(false);
+    
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER);
+    if(var) {
+        UiInteger *value = (UiInteger*)var->value;
+        QButtonGroup *buttonGroup = (QButtonGroup*)value->obj;
+        if(!buttonGroup) {
+            buttonGroup = new QButtonGroup();
+            value->obj = buttonGroup;
+        }
+        int id = buttonGroup->buttons().size()+1;
+        buttonGroup->addButton(button, id);
+        if(value->value == id) {
+            button->setChecked(true);
+        }
+        value->get = ui_radiobutton_get;
+        value->set = ui_radiobutton_set;
+    }
+    
+    UiEventWrapper *event = new UiEventWrapper(obj, args.onchange, args.onchangedata);
+    event->var = var;
+    event->customdata1 = button;
+    event->prepare_event = togglebutton_event;
+    button->connect(button, SIGNAL(clicked()), event, SLOT(slot()));
+    button->connect(button, SIGNAL(destroyed()), event, SLOT(destroy()));
+    
+    ctn->add(button, false);
+    
+    return button;
+}
+
+int64_t ui_radiobutton_get(UiInteger *value) {
+    QButtonGroup *buttonGroup = (QButtonGroup*)value->obj;
+    value->value = buttonGroup->checkedId();
+    return value->value;
+}
+
+void ui_radiobutton_set(UiInteger *value, int64_t i) {
+    QButtonGroup *buttonGroup = (QButtonGroup*)value->obj;
+    QAbstractButton *button = buttonGroup->button(i);
+    if(button) {
+        button->setChecked(true);
+        value->value = i;
+    }
+}
index 3db92034b7cd0095bbaf5e31b37a5933aa6f910b..7f915c8b8ae6122490445c705f2e474b09d75764 100644 (file)
@@ -161,7 +161,7 @@ UiAction::UiAction(UiObject *obj, QString &label, ui_callback f, void *userdata)
 }
 
 UiAction::~UiAction() {
-    
+    // TODO: unbind var
 }
 
 void UiAction::trigger() {
index 283043b5d7a02970fe3d25264200e11c393fdb68..25fb84bd04b6320d45243d4129c2886563d91345 100644 (file)
@@ -53,7 +53,7 @@ UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
 }
 
 void ui_widget_set_size(UIWIDGET w, int width, int height) {
-    w->resize(width >= 0 ? width : w->width(), height >= 0 ? w->height());
+    w->resize(width >= 0 ? width : w->width(), height >= 0 ? height : w->height());
 }
 
 void ui_widget_redraw(UIWIDGET w) {