From: Olaf Wintermann Date: Sun, 30 Nov 2025 17:20:13 +0000 (+0100) Subject: update libidav X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=f039660d5c5650dc23a5fb61b4fc210095a7399e;p=note.git update libidav --- diff --git a/libidav/crypto.c b/libidav/crypto.c index 240406f..fe3651a 100644 --- a/libidav/crypto.c +++ b/libidav/crypto.c @@ -381,12 +381,12 @@ void dav_sha256_init(DAV_SHA_CTX *ctx) { SHA256_Init(ctx); } -void dav_sha256_update(DAV_SHA_CTX *ctx, const void *data, size_t length) { +void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t length) { SHA256_Update(ctx, data, length); } -void dav_sha256_final(char *md, DAV_SHA_CTX *ctx) { - SHA256_Final(md, ctx); +void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf) { + SHA256_Final(buf, ctx); } #else @@ -848,13 +848,16 @@ DAV_SHA_CTX* dav_sha256_create(void) { return ctx; } +void dav_sha256_init(DAV_SHA_CTX *ctx) { + CC_SHA256_Init(ctx); +} + void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t len) { CC_SHA256_Update(ctx, data, len); } void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf) { CC_SHA256_Final(buf, ctx); - free(ctx); } DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) { @@ -1422,7 +1425,7 @@ char* dav_create_hash(const char *data, size_t len) { DAV_SHA_CTX *ctx = dav_sha256_create(); if(ctx) { dav_sha256_update(ctx, data, len); - dav_sha256_final(ctx, hash); + dav_sha256_final_free(ctx, hash); } return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH); } @@ -1448,7 +1451,6 @@ void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf) { // cleanup cng_cleanup(ctx->hAlg, NULL, ctx->hHash, ctx->pbHashObject); - free(ctx); } DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) { @@ -1514,7 +1516,10 @@ DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, } #endif - +void dav_sha256_final_free(DAV_SHA_CTX *ctx, unsigned char *buf) { + dav_sha256_final(ctx, buf); + free(ctx); +} CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key) { CxBuffer *encbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); diff --git a/libidav/crypto.h b/libidav/crypto.h index 3bc5937..a56512a 100644 --- a/libidav/crypto.h +++ b/libidav/crypto.h @@ -159,6 +159,7 @@ void dav_sha256_init(DAV_SHA_CTX *ctx); DAV_SHA_CTX* dav_sha256_create(void); void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t len); void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf); +void dav_sha256_final_free(DAV_SHA_CTX *ctx, unsigned char *buf); DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc); diff --git a/libidav/davqlexec.c b/libidav/davqlexec.c index 83cf720..afe62d1 100644 --- a/libidav/davqlexec.c +++ b/libidav/davqlexec.c @@ -31,7 +31,6 @@ #include #include -#include #include #include #include diff --git a/libidav/davqlparser.c b/libidav/davqlparser.c index 22fa594..eb2dd46 100644 --- a/libidav/davqlparser.c +++ b/libidav/davqlparser.c @@ -27,7 +27,6 @@ */ #include "davqlparser.h" -#include #include #include #include diff --git a/libidav/methods.c b/libidav/methods.c index cbe0685..90e7597 100644 --- a/libidav/methods.c +++ b/libidav/methods.c @@ -36,7 +36,6 @@ #include "session.h" #include "xml.h" -#include #include #include @@ -481,10 +480,6 @@ int hrefeq(DavSession *sn, const char *href1, const char *href2) { DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response) { char *url = NULL; curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url); - if(!root) { - printf("methods.c: TODO: remove\n"); - root = dav_resource_new_href(sn, util_url_path(url)); // TODO: remove - } //printf("%.*s\n\n", response->size, response->space); xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0); diff --git a/libidav/pwdstore.c b/libidav/pwdstore.c index ba97c09..6a134d6 100644 --- a/libidav/pwdstore.c +++ b/libidav/pwdstore.c @@ -34,7 +34,8 @@ #include #include -#include +#include + #include #ifdef _WIN32 @@ -263,7 +264,7 @@ static int read_pwdentry(PwdStore *p, CxBuffer *in) { } static void remove_list_entries(PwdStore *s, const char *id) { - CxIterator i = cxListMutIterator(s->locations); + CxIterator i = cxListIterator(s->locations); cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { cxIteratorFlagRemoval(i); @@ -271,7 +272,7 @@ static void remove_list_entries(PwdStore *s, const char *id) { break; } } - i = cxListMutIterator(s->noloc); + i = cxListIterator(s->noloc); cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { cxIteratorFlagRemoval(i); diff --git a/libidav/resource.c b/libidav/resource.c index 19ddffa..b82473b 100644 --- a/libidav/resource.c +++ b/libidav/resource.c @@ -37,7 +37,6 @@ #include "methods.h" #include "crypto.h" #include -#include #include #include #include @@ -835,7 +834,7 @@ static int dav_seek_h(void *stream, long offset, int whence) { HashStream *s = stream; if(offset == 0 && whence == SEEK_SET) { unsigned char buf[DAV_SHA256_DIGEST_LENGTH]; - dav_sha256_final(s->sha, buf); + dav_sha256_final_free(s->sha, buf); s->sha = NULL; } else { s->error = 1; @@ -938,7 +937,7 @@ int dav_store(DavResource *res) { data->length); if(hstr.sha) { - dav_sha256_final(hstr.sha, (unsigned char*)data->hash); + dav_sha256_final_free(hstr.sha, (unsigned char*)data->hash); char *hash = util_hexstr((unsigned char*)data->hash, 32); dav_set_string_property_ns(res, DAV_NS, "content-hash", hash); free(hash); @@ -1599,7 +1598,7 @@ CxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) { property->value = n->children ? dav_convert_xml(sn, n->children) : NULL; cxmutstr propkey = dav_property_key(property->ns->name, property->name); - cxMapPut(map, cx_hash_key_cxstr(propkey), property); + cxMapPut(map, propkey, property); cx_strfree(&propkey); } n = n->next; diff --git a/libidav/utils.c b/libidav/utils.c index c6f1eb1..767f52a 100644 --- a/libidav/utils.c +++ b/libidav/utils.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/libidav/webdav.c b/libidav/webdav.c index 262e15c..ea5aac1 100644 --- a/libidav/webdav.c +++ b/libidav/webdav.c @@ -36,7 +36,6 @@ #include "session.h" #include "methods.h" #include -#include #include #include #include @@ -353,8 +352,8 @@ int dav_context_remove_session(DavContext *context, DavSession *sn) { int ret = 0; dav_context_lock(context); CxList *sessions = context->sessions; - ssize_t i = cxListFind(sessions, sn); - if(i >= 0) { + size_t i = cxListFind(sessions, sn); + if(cxListIndexValid(sessions, i)) { cxListRemove(sessions, i); } else { ret = 1; diff --git a/libidav/xml.c b/libidav/xml.c index 32221ad..cfea172 100644 --- a/libidav/xml.c +++ b/libidav/xml.c @@ -30,7 +30,6 @@ #include #include -#include #include #include "xml.h" diff --git a/ui/cocoa/MainWindow.h b/ui/cocoa/MainWindow.h index 8cf15e7..7c8cbf6 100644 --- a/ui/cocoa/MainWindow.h +++ b/ui/cocoa/MainWindow.h @@ -31,6 +31,8 @@ @interface MainWindow : NSWindow +@property UiObject *obj; +@property (strong) NSSplitView *splitview; @property (strong) NSView *sidebar; @property (strong) NSView *leftPanel; @property (strong) NSView *rightPanel; diff --git a/ui/cocoa/MainWindow.m b/ui/cocoa/MainWindow.m index 4793365..c62079f 100644 --- a/ui/cocoa/MainWindow.m +++ b/ui/cocoa/MainWindow.m @@ -50,13 +50,9 @@ NSWindowStyleMaskMiniaturizable backing:NSBackingStoreBuffered defer:false]; + _obj = obj; - if(uic_toolbar_isenabled()) { - UiToolbar *toolbar = [[UiToolbar alloc]initWithObject:obj]; - [self setToolbar:toolbar]; - } - int top = 4; NSView *content = self.contentView; @@ -72,6 +68,7 @@ splitview.dividerStyle = NSSplitViewDividerStyleThin; splitview.translatesAutoresizingMaskIntoConstraints = false; [self.contentView addSubview:splitview]; + _splitview = splitview; [NSLayoutConstraint activateConstraints:@[ [splitview.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0], @@ -135,6 +132,12 @@ } _topOffset = top; + if(uic_toolbar_isenabled()) { + UiToolbar *toolbar = [[UiToolbar alloc]initWithWindow:self]; + [self setToolbar:toolbar]; + } + + return self; } diff --git a/ui/cocoa/Toolbar.h b/ui/cocoa/Toolbar.h index 4e50e7c..7ec7bbd 100644 --- a/ui/cocoa/Toolbar.h +++ b/ui/cocoa/Toolbar.h @@ -28,6 +28,7 @@ #import "toolkit.h" #import "../common/toolbar.h" +#import "MainWindow.h" /* * UiToolbarDelegate @@ -49,9 +50,10 @@ NSMutableArray *defaultItems; } +@property MainWindow *window; @property UiObject *obj; -- (UiToolbar*) initWithObject:(UiObject*)object; +- (UiToolbar*) initWithWindow:(MainWindow*)window; @end diff --git a/ui/cocoa/Toolbar.m b/ui/cocoa/Toolbar.m index 04941cb..8ab2ad8 100644 --- a/ui/cocoa/Toolbar.m +++ b/ui/cocoa/Toolbar.m @@ -45,13 +45,21 @@ void ui_toolbar_init(void) { @implementation UiToolbar -- (UiToolbar*) initWithObject:(UiObject*)object { +- (UiToolbar*) initWithWindow:(MainWindow*)window { self = [super initWithIdentifier:@"UiToolbar"]; - _obj = object; + _window = window; + _obj = window.obj; allowedItems = [[NSMutableArray alloc]initWithCapacity:16]; defaultItems = [[NSMutableArray alloc]initWithCapacity:16]; + if(window.sidebar) { + [allowedItems addObject:@"sidebar_separator"]; + } + if(window.leftPanel) { + [allowedItems addObject:@"splitview_separator"]; + } + CxMap *toolbarItems = uic_get_toolbar_items(); CxMapIterator i = cxMapIteratorKeys(toolbarItems); cx_foreach(CxHashKey *, key, i) { @@ -61,18 +69,59 @@ void ui_toolbar_init(void) { [allowedItems addObject: NSToolbarFlexibleSpaceItemIdentifier]; [allowedItems addObject: NSToolbarSpaceItemIdentifier]; - CxList *tbitems[3]; - tbitems[0] = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); - tbitems[1] = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); - tbitems[2] = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); - for(int t=0;t<3;t++) { - CxIterator iter = cxListIterator(tbitems[t]); + // UI_TOOLBAR_LEFT = 0, + // UI_TOOLBAR_CENTER, + // UI_TOOLBAR_RIGHT, + // UI_TOOLBAR_SIDEBAR_LEFT, + // UI_TOOLBAR_SIDEBAR_RIGHT, + // UI_TOOLBAR_RIGHTPANEL_LEFT, + // UI_TOOLBAR_RIGHTPANEL_CENTER, + // UI_TOOLBAR_RIGHTPANEL_RIGHT + CxList *tbitems[8]; + for(int i=0;i<8;i++) { + tbitems[i] = uic_get_toolbar_defaults(i); + } + + if(window.sidebar) { + CxIterator iter = cxListIterator(tbitems[UI_TOOLBAR_SIDEBAR_LEFT]); cx_foreach(char *, name, iter) { NSString *s = [[NSString alloc] initWithUTF8String:name]; [defaultItems addObject:s]; } + + CxList *sidebarRight = tbitems[UI_TOOLBAR_SIDEBAR_RIGHT]; + if(cxListSize(sidebarRight) > 0) { + [defaultItems addObject:NSToolbarFlexibleSpaceItemIdentifier]; + iter = cxListIterator(sidebarRight); + cx_foreach(char *, name, iter) { + NSString *s = [[NSString alloc] initWithUTF8String:name]; + [defaultItems addObject:s]; + } + } + + [defaultItems addObject:@"sidebar_separator"]; } + int start_pos = UI_TOOLBAR_LEFT; + for(int x=0;x<2;x++) { + for(int t=start_pos;t 0) { + [defaultItems addObject:NSToolbarFlexibleSpaceItemIdentifier]; + } + } + + if(x == 0 && window.rightPanel) { + [defaultItems addObject:@"splitview_separator"]; + } + start_pos = UI_TOOLBAR_RIGHTPANEL_LEFT; + } + + [self setDelegate:self]; [self setAllowsUserCustomization:YES]; return self; @@ -94,6 +143,18 @@ void ui_toolbar_init(void) { CxMap *items = uic_get_toolbar_items(); UiToolbarItemI *item = cxMapGet(items, itemIdentifier.UTF8String); if(!item) { + if([itemIdentifier isEqualToString:@"sidebar_separator"]) { + NSTrackingSeparatorToolbarItem *sep = [NSTrackingSeparatorToolbarItem trackingSeparatorToolbarItemWithIdentifier:itemIdentifier + splitView:_window.splitview + dividerIndex:0]; + return sep; + } else if([itemIdentifier isEqualToString:@"splitview_separator"]) { + int dividerIndex = _window.sidebar != nil ? 1 : 0; + NSTrackingSeparatorToolbarItem *sep = [NSTrackingSeparatorToolbarItem trackingSeparatorToolbarItemWithIdentifier:itemIdentifier + splitView:_window.splitview + dividerIndex:dividerIndex]; + return sep; + } return nil; } diff --git a/ui/cocoa/button.m b/ui/cocoa/button.m index 4a7f259..faa852d 100644 --- a/ui/cocoa/button.m +++ b/ui/cocoa/button.m @@ -29,6 +29,7 @@ #import "button.h" #import "EventData.h" #import "Container.h" +#import "image.h" #import #import @@ -41,6 +42,9 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) { NSString *label = [[NSString alloc] initWithUTF8String:args->label]; button.title = label; } + if(args->icon) { + button.image = ui_cocoa_named_icon(args->icon);; + } if(args->onclick) { EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata]; @@ -72,6 +76,9 @@ UIWIDGET togglebutton_create(UiObject* obj, UiToggleArgs *args, enum NSButtonTyp NSString *label = [[NSString alloc] initWithUTF8String:args->label]; button.title = label; } + if(args->icon) { + button.image = ui_cocoa_named_icon(args->icon); + } UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); if(var) { diff --git a/ui/cocoa/image.m b/ui/cocoa/image.m index e00c39a..7adf301 100644 --- a/ui/cocoa/image.m +++ b/ui/cocoa/image.m @@ -30,21 +30,71 @@ static NSDictionary *standardIconNames; +#define UI_ICON_ENTRY(x) @#x : x + void ui_icon_init(void) { standardIconNames = @{ - @"NSImageNameActionTemplate": NSImageNameActionTemplate, - @"NSImageNameAddTemplate": NSImageNameAddTemplate, - @"NSImageNameAdvanced": NSImageNameAdvanced, - @"NSImageNameApplicationIcon": NSImageNameApplicationIcon, - @"NSImageNameBluetoothTemplate": NSImageNameBluetoothTemplate, - @"NSImageNameBonjour": NSImageNameBonjour, - @"NSImageNameBookmarksTemplate": NSImageNameBookmarksTemplate, - @"NSImageNameCaution": NSImageNameCaution, - // TODO - @"NSImageNameRefreshTemplate": NSImageNameRefreshTemplate, - @"NSImageNameFolder": NSImageNameFolder, - @"NSImageNameGoForwardTemplate": NSImageNameGoForwardTemplate, - @"NSImageNameGoBackTemplate": NSImageNameGoBackTemplate + UI_ICON_ENTRY(NSImageNameAddTemplate), + UI_ICON_ENTRY(NSImageNameBluetoothTemplate), + UI_ICON_ENTRY(NSImageNameBonjour), + UI_ICON_ENTRY(NSImageNameBookmarksTemplate), + UI_ICON_ENTRY(NSImageNameCaution), + UI_ICON_ENTRY(NSImageNameComputer), + UI_ICON_ENTRY(NSImageNameEnterFullScreenTemplate), + UI_ICON_ENTRY(NSImageNameExitFullScreenTemplate), + UI_ICON_ENTRY(NSImageNameFolder), + UI_ICON_ENTRY(NSImageNameFolderBurnable), + UI_ICON_ENTRY(NSImageNameFolderSmart), + UI_ICON_ENTRY(NSImageNameFollowLinkFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameHomeTemplate), + UI_ICON_ENTRY(NSImageNameIChatTheaterTemplate), + UI_ICON_ENTRY(NSImageNameLockLockedTemplate), + UI_ICON_ENTRY(NSImageNameLockUnlockedTemplate), + UI_ICON_ENTRY(NSImageNameNetwork), + UI_ICON_ENTRY(NSImageNamePathTemplate), + UI_ICON_ENTRY(NSImageNameQuickLookTemplate), + UI_ICON_ENTRY(NSImageNameRefreshFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameRefreshTemplate), + UI_ICON_ENTRY(NSImageNameRemoveTemplate), + UI_ICON_ENTRY(NSImageNameRevealFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameShareTemplate), + UI_ICON_ENTRY(NSImageNameSlideshowTemplate), + UI_ICON_ENTRY(NSImageNameStatusAvailable), + UI_ICON_ENTRY(NSImageNameStatusNone), + UI_ICON_ENTRY(NSImageNameStatusPartiallyAvailable), + UI_ICON_ENTRY(NSImageNameStatusUnavailable), + UI_ICON_ENTRY(NSImageNameStopProgressFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameStopProgressTemplate), + UI_ICON_ENTRY(NSImageNameTrashEmpty), + UI_ICON_ENTRY(NSImageNameTrashFull), + UI_ICON_ENTRY(NSImageNameActionTemplate), + UI_ICON_ENTRY(NSImageNameSmartBadgeTemplate), + UI_ICON_ENTRY(NSImageNameIconViewTemplate), + UI_ICON_ENTRY(NSImageNameListViewTemplate), + UI_ICON_ENTRY(NSImageNameColumnViewTemplate), + UI_ICON_ENTRY(NSImageNameFlowViewTemplate), + UI_ICON_ENTRY(NSImageNameInvalidDataFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameGoForwardTemplate), + UI_ICON_ENTRY(NSImageNameGoBackTemplate), + UI_ICON_ENTRY(NSImageNameGoRightTemplate), + UI_ICON_ENTRY(NSImageNameGoLeftTemplate), + UI_ICON_ENTRY(NSImageNameRightFacingTriangleTemplate), + UI_ICON_ENTRY(NSImageNameLeftFacingTriangleTemplate), + UI_ICON_ENTRY(NSImageNameMobileMe), + UI_ICON_ENTRY(NSImageNameMultipleDocuments), + UI_ICON_ENTRY(NSImageNameUserAccounts), + UI_ICON_ENTRY(NSImageNamePreferencesGeneral), + UI_ICON_ENTRY(NSImageNameAdvanced), + UI_ICON_ENTRY(NSImageNameInfo), + UI_ICON_ENTRY(NSImageNameFontPanel), + UI_ICON_ENTRY(NSImageNameColorPanel), + UI_ICON_ENTRY(NSImageNameUser), + UI_ICON_ENTRY(NSImageNameUserGroup), + UI_ICON_ENTRY(NSImageNameEveryone), + UI_ICON_ENTRY(NSImageNameUserGuest), + UI_ICON_ENTRY(NSImageNameMenuOnStateTemplate), + UI_ICON_ENTRY(NSImageNameMenuMixedStateTemplate), + UI_ICON_ENTRY(NSImageNameApplicationIcon) }; } diff --git a/ui/common/args.h b/ui/common/args.h index e533193..ab66471 100644 --- a/ui/common/args.h +++ b/ui/common/args.h @@ -137,7 +137,7 @@ UIEXPORT void ui_container_args_set_margin(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_set_margin_left(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_set_margin_top(UiContainerArgs *args, int value); -UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value); +UIEXPORT void ui_container_args_set_margin_bottom(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_set_colspan(UiContainerArgs *args, int colspan); UIEXPORT void ui_container_args_set_rowspan(UiContainerArgs *args, int rowspan); UIEXPORT void ui_container_args_set_def_hexpand(UiContainerArgs *args, UiBool value); diff --git a/ui/common/context.c b/ui/common/context.c index 6c0cbcc..40c1544 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -114,7 +114,7 @@ void uic_context_attach_document(UiContext *ctx, void *document) { UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); if(docvar) { // bind var to document var - uic_copy_binding(var, docvar, TRUE); + uic_copy_var_binding(var, docvar, TRUE); cxIteratorFlagRemoval(i); } } @@ -124,16 +124,19 @@ void uic_context_attach_document(UiContext *ctx, void *document) { } static void uic_context_unbind_vars(UiContext *ctx) { + ui_onchange_events_enable(FALSE); CxMapIterator mi = cxMapIterator(ctx->vars); cx_foreach(CxMapEntry*, entry, mi) { + printf("detach %.*s\n", (int)entry->key->len, (char*)entry->key->data); UiVar *var = entry->value; // var->from && var->from_ctx && var->from_ctx != ctx uic_save_var(var); if(var->from) { - uic_copy_binding(var, var->from, FALSE); + uic_copy_var_binding(var, var->from, FALSE); cxMapPut(var->from->from_ctx->vars, *entry->key, var->from); var->from = NULL; } + uic_unbind_var(var); } if(ctx->documents) { @@ -143,6 +146,8 @@ static void uic_context_unbind_vars(UiContext *ctx) { uic_context_unbind_vars(subctx); } } + + ui_onchange_events_enable(TRUE); } void uic_context_detach_document(UiContext *ctx, void *document) { @@ -213,8 +218,9 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { var->original_value = NULL; var->from = NULL; var->from_ctx = ctx; - - cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); + var->bound = FALSE; + + cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); // TODO: use another destructor that cleans the value (UiString free, UiText destroy, ...) cxMapPut(ctx->vars, name, var); @@ -266,6 +272,40 @@ void* uic_create_value(UiContext *ctx, UiVarType type) { return val; } +// destroys a value, that was created by uic_create_value +void uic_destroy_value(UiContext *ctx, UiVarType type, void *value) { + switch(type) { + default: { + ui_free(ctx, value); + break; + } + case UI_VAR_SPECIAL: break; + case UI_VAR_STRING: { + UiString *s = value; + if(s->value.free) { + s->value.free(s->value.ptr); + } + ui_free(ctx, value); + } + case UI_VAR_TEXT: { + UiText *t = value; + if(t->value.free) { + t->value.free(t->value.ptr); + t->value.free = NULL; + t->value.ptr = NULL; + } + if(t->destroy) { + t->destroy(t); + } + ui_free(ctx, value); + } + case UI_VAR_LIST: { + ui_list_free(ctx, value); + break; + } + } +} + UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type) { if (value) { @@ -278,7 +318,7 @@ UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, cons } -void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { +void uic_copy_var_binding(UiVar *from, UiVar *to, UiBool copytodoc) { // check type if(from->type != to->type) { fprintf(stderr, "UI Error: var has incompatible type.\n"); @@ -301,32 +341,36 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { } } - ui_setop_enable(TRUE); + uic_copy_value_binding(from->type, fromvalue, tovalue); +} + +void uic_copy_value_binding(UiVarType type, void *from, void *to) { + ui_setop_enable(TRUE); // copy binding - // we don't copy the observer, because the from var has never one - switch(from->type) { + // we don't copy the observer, because the from value never has oberservers + switch(type) { default: fprintf(stderr, "uic_copy_binding: wtf!\n"); break; case UI_VAR_SPECIAL: break; case UI_VAR_INTEGER: { - UiInteger *f = fromvalue; - UiInteger *t = tovalue; + UiInteger *f = from; + UiInteger *t = to; if(!f->obj) break; uic_int_copy(f, t); t->set(t, t->value); break; } case UI_VAR_DOUBLE: { - UiDouble *f = fromvalue; - UiDouble *t = tovalue; + UiDouble *f = from; + UiDouble *t = to; if(!f->obj) break; uic_double_copy(f, t); t->set(t, t->value); break; } case UI_VAR_STRING: { - UiString *f = fromvalue; - UiString *t = tovalue; + UiString *f = from; + UiString *t = to; if(!f->obj) break; uic_string_copy(f, t); char *tvalue = t->value.ptr ? t->value.ptr : ""; @@ -335,23 +379,23 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { break; } case UI_VAR_TEXT: { - UiText *f = fromvalue; - UiText *t = tovalue; + UiText *f = from; + UiText *t = to; if(!f->obj) break; uic_text_copy(f, t); t->restore(t); break; } case UI_VAR_LIST: { - UiList *f = fromvalue; - UiList *t = tovalue; + UiList *f = from; + UiList *t = to; uic_list_copy(f, t); ui_list_update(t); break; } case UI_VAR_RANGE: { - UiRange *f = fromvalue; - UiRange *t = tovalue; + UiRange *f = from; + UiRange *t = to; if(!f->obj) break; uic_range_copy(f, t); t->setextent(t, t->extent); @@ -360,8 +404,8 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { break; } case UI_VAR_GENERIC: { - UiGeneric *f = fromvalue; - UiGeneric *t = tovalue; + UiGeneric *f = from; + UiGeneric *t = to; if(!f->obj) break; uic_generic_copy(f, t); t->set(t, t->value, t->type); @@ -398,58 +442,47 @@ void uic_unbind_var(UiVar *var) { } } +const char *uic_type2str(UiVarType type) { + switch(type) { + default: return ""; + case UI_VAR_INTEGER: return "int"; + case UI_VAR_DOUBLE: return "double"; + case UI_VAR_STRING: return "string"; + case UI_VAR_TEXT: return "text"; + case UI_VAR_LIST: return "list"; + case UI_VAR_RANGE: return "range"; + case UI_VAR_GENERIC: return "generic"; + } +} + void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value) { - // TODO: do we need/want this? Why adding vars to a context after - // widgets reference these? Workarounds: - // 1. add vars to ctx before creating ui - // 2. create ui, create new document with vars, attach doc - // also it would be possible to create a function, that scans unbound vars - // and connects them to available vars - /* - UiContext *rootctx = uic_root_context(ctx); - UiVar *b = NULL; - if(rootctx->bound) { - // some widgets are already bound to some vars - b = ucx_map_cstr_get(rootctx->bound, name); - if(b) { - // a widget is bound to a var with this name - // if ctx is the root context we can remove the var from bound - // because set_doc or detach can't fuck things up - if(ctx == rootctx) { - ucx_map_cstr_remove(ctx->bound, name); - // TODO: free stuff - } + UiVar *var = cxMapGet(ctx->vars, name); + if(!var) { + // create new var and add it to the context var map + var = ui_malloc(ctx, sizeof(UiVar)); + cxMapPut(ctx->vars, name, var); + } else { + // override var with new value + if(var->type != type) { + fprintf(stderr, "Error: var %s type mismatch: %s - %s\n", name, uic_type2str(var->type), uic_type2str(type)); + return; } + if(var->bound) { + fprintf(stderr, "Error: var %s already bound\n", name); + return; + } + UiInteger *prev_value = var->value; + uic_copy_value_binding(type, prev_value, value); + uic_destroy_value(var->from_ctx, var->type, var->value); } - */ - // create new var and add it to doc's vars - UiVar *var = ui_malloc(ctx, sizeof(UiVar)); var->type = type; var->value = value; var->from = NULL; var->from_ctx = ctx; - size_t oldcount = cxMapSize(ctx->vars); - cxMapPut(ctx->vars, name, var); - if(cxMapSize(ctx->vars) != oldcount + 1) { - fprintf(stderr, "UiError: var '%s' already exists\n", name); - } - - // TODO: remove? - // a widget is already bound to a var with this name - // copy the binding (like uic_context_set_document) - /* - if(b) { - uic_copy_binding(b, var, TRUE); - } - */ -} - -void uic_remove_bound_var(UiContext *ctx, UiVar *var) { - // TODO + var->bound = TRUE; } - // public API void ui_attach_document(UiContext *ctx, void *document) { @@ -549,7 +582,7 @@ void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, cxListFree(groups); } -void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups) { +void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups) { if(enable == NULL) { enable = (ui_enablefunc)ui_set_enabled; } @@ -561,7 +594,7 @@ void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable cxListFree(ls); } -void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates) { +void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates) { ui_widget_set_groups2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates); } @@ -622,7 +655,7 @@ void* ui_realloc(UiContext *ctx, void *ptr, size_t size) { return ctx ? cxRealloc(ctx->allocator, ptr, size) : NULL; } -UIEXPORT char* ui_strdup(UiContext *ctx, const char *str) { +char* ui_strdup(UiContext *ctx, const char *str) { if(!ctx) { return NULL; } @@ -630,3 +663,11 @@ UIEXPORT char* ui_strdup(UiContext *ctx, const char *str) { cxmutstr d = cx_strdup_a(ctx->allocator, s); return d.ptr; } + +void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr) { + cxMempoolRegister(ctx->mp, data, (cx_destructor_func)destr); +} + +void ui_set_destructor(void *mem, ui_destructor_func destr) { + cxMempoolSetDestructor(mem, (cx_destructor_func)destr); +} diff --git a/ui/common/context.h b/ui/common/context.h index f517090..15e7700 100644 --- a/ui/common/context.h +++ b/ui/common/context.h @@ -97,6 +97,7 @@ struct UiVar { UiVarType type; UiVar *from; UiContext *from_ctx; + UiBool bound; }; struct UiGroupWidget { @@ -130,17 +131,18 @@ UiVar* uic_get_var(UiContext *ctx, const char *name); UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type); UiVar* uic_create_value_var(UiContext *ctx, void *value); void* uic_create_value(UiContext *ctx, UiVarType type); +void uic_destroy_value(UiContext *ctx, UiVarType type, void *value); UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type); -void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc); +void uic_copy_var_binding(UiVar *from, UiVar *to, UiBool copytodoc); +void uic_copy_value_binding(UiVarType type, void *from, void *to); void uic_save_var(UiVar *var); void uic_unbind_var(UiVar *var); +const char *uic_type2str(UiVarType type); void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value); -void uic_remove_bound_var(UiContext *ctx, UiVar *var); - size_t uic_group_array_size(const int *groups); void uic_check_group_widgets(UiContext *ctx); void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups); diff --git a/ui/common/menu.c b/ui/common/menu.c index cdf3a8c..b0cfc46 100644 --- a/ui/common/menu.c +++ b/ui/common/menu.c @@ -41,6 +41,22 @@ static UiMenuBuilder global_builder; static int menu_item_counter = 0; +static void *tmp_eventdata; +static int tmp_eventdata_type; + +void uic_set_tmp_eventdata(void *eventdata, int type) { + tmp_eventdata = eventdata; + tmp_eventdata_type = type; +} + +void* uic_get_tmp_eventdata(void) { + return tmp_eventdata; +} + +int uic_get_tmp_eventdata_type(void) { + return tmp_eventdata_type; +} + void uic_menu_init(void) { global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); current_builder = &global_builder; diff --git a/ui/common/menu.h b/ui/common/menu.h index e9dce38..f44f6b4 100644 --- a/ui/common/menu.h +++ b/ui/common/menu.h @@ -131,6 +131,10 @@ void uic_add_menu_to_stack(UiMenu* menu); int* uic_copy_groups(const int* groups, size_t *ngroups); +void uic_set_tmp_eventdata(void *eventdata, int type); +void* uic_get_tmp_eventdata(void); +int uic_get_tmp_eventdata_type(void); + #ifdef __cplusplus } #endif diff --git a/ui/common/object.c b/ui/common/object.c index 4c41e28..9b0a8b7 100644 --- a/ui/common/object.c +++ b/ui/common/object.c @@ -32,6 +32,7 @@ #include "context.h" #include +#include #include "../ui/container.h" @@ -108,7 +109,7 @@ void uic_object_destroy(UiObject *obj) { UiObject* uic_object_new_toplevel(void) { fflush(stdout); CxMempool *mp = cxMempoolCreateSimple(256); - UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObjectPrivate)); fflush(stdout); obj->ctx = uic_context(obj, mp); obj->ctx->parent = ui_global_context(); @@ -119,7 +120,7 @@ UiObject* uic_object_new_toplevel(void) { } UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget) { - UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObject)); + UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObjectPrivate)); newobj->ctx = ctx; newobj->widget = widget; uic_object_created(newobj); @@ -144,4 +145,48 @@ void uic_object_pop_container(UiObject *toplevel) { } else { toplevel->container_begin = NULL; } + + // TODO: free container? +} + +/* + * This might look like a weird function, but in case a container creates a + * sub-container, 2 container objects are added to the list, however we want + * only one container, otherwise ui_container_finish() would not work + */ +void uic_object_remove_second_last_container(UiObject *toplevel) { + if(toplevel->container_end && toplevel->container_end->prev) { + UiContainerX *end = toplevel->container_end; + UiContainerX *rm = toplevel->container_end->prev; + + end->prev = rm->prev; + if(rm->prev) { + rm->prev->next = end; + } else { + toplevel->container_begin = end; + } + + // TODO: free container? + } else { + fprintf(stderr, "Error: uic_object_remove_second_last_container expected at least 2 containers\n"); + } +} + +// public API + +void ui_object_set(UiObject *obj, const char *key, void *data) { + UiObjectPrivate *p = (UiObjectPrivate*)obj; + if(!p->ext) { + p->ext = cxHashMapCreate(obj->ctx->mp->allocator, CX_STORE_POINTERS, 4); + } + if(data) { + cxMapPut(p->ext, key, data); + } else { + cxMapRemove(p->ext, key); + } +} + +void* ui_object_get(UiObject *obj, const char *key) { + UiObjectPrivate *p = (UiObjectPrivate*)obj; + return p->ext ? cxMapGet(p->ext, key) : NULL; } diff --git a/ui/common/object.h b/ui/common/object.h index a899a83..4327875 100644 --- a/ui/common/object.h +++ b/ui/common/object.h @@ -30,10 +30,16 @@ #define UIC_OBJECT_H #include "../ui/toolkit.h" +#include #ifdef __cplusplus extern "C" { #endif + +typedef struct UiObjectPrivate { + UiObject obj; + CxMap *ext; +} UiObjectPrivate; typedef void (*ui_object_callback)(UiObject *obj, void *userdata); @@ -51,6 +57,7 @@ UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget); void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer); void uic_object_pop_container(UiObject *toplevel); +void uic_object_remove_second_last_container(UiObject *toplevel); diff --git a/ui/common/objs.mk b/ui/common/objs.mk index 4d401b5..176180c 100644 --- a/ui/common/objs.mk +++ b/ui/common/objs.mk @@ -42,6 +42,7 @@ COMMON_OBJ += threadpool$(OBJ_EXT) COMMON_OBJ += condvar$(OBJ_EXT) COMMON_OBJ += args$(OBJ_EXT) COMMON_OBJ += wrapper$(OBJ_EXT) +COMMON_OBJ += utils$(OBJ_EXT) TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)uic_%) TOOLKITSOURCE += $(COMMON_OBJ:%$(OBJ_EXT)=common/%.c) diff --git a/ui/common/types.c b/ui/common/types.c index 3d3d94f..53fbbec 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -40,6 +40,8 @@ static ui_list_init_func default_list_init; static void *default_list_init_userdata; +static ui_list_destroy_func default_list_destroy; +static void *default_list_destroy_userdata; UiObserver* ui_observer_new(ui_callback f, void *data) { UiObserver *observer = malloc(sizeof(UiObserver)); @@ -105,6 +107,11 @@ void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused) { list->count = ui_list_count; } +void uic_ucx_list_destroy(UiContext *ctx, UiList *list, void *unused) { + cxListFree(list->data); + ui_free(ctx, list); +} + UiList* ui_list_new(UiContext *ctx, const char *name) { return ui_list_new2(ctx, name, default_list_init ? default_list_init : uic_ucx_list_init, default_list_init_userdata); } @@ -121,9 +128,13 @@ UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func listini return list; } -void ui_list_free(UiList *list) { - cxListFree(list->data); - free(list); +void ui_list_free(UiContext *ctx, UiList *list) { + if(!default_list_destroy) { + uic_ucx_list_destroy(ctx, list, NULL); + } else { + default_list_destroy(ctx, list, default_list_destroy_userdata); + } + } void* ui_list_first(UiList *list) { @@ -313,18 +324,32 @@ UiDouble* ui_double_new(UiContext *ctx, const char *name) { return d; } +static void string_destroy(UiString *s) { + if(s->value.free && s->value.ptr) { + s->value.free(s->value.ptr); + } +} + UiString* ui_string_new(UiContext *ctx, const char *name) { UiString *s = ui_malloc(ctx, sizeof(UiString)); memset(s, 0, sizeof(UiString)); + ui_set_destructor(s, (ui_destructor_func)string_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_STRING, s); } return s; } +static void text_destroy(UiText *t) { + if(t->destroy) { + t->destroy(t); + } +} + UiText* ui_text_new(UiContext *ctx, const char *name) { UiText *t = ui_malloc(ctx, sizeof(UiText)); memset(t, 0, sizeof(UiText)); + ui_set_destructor(t, (ui_destructor_func)text_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_TEXT, t); } @@ -340,9 +365,16 @@ UiRange* ui_range_new(UiContext *ctx, const char *name) { return r; } -UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, const char *name) { +static void generic_destroy(UiGeneric *g) { + if(g->destroy) { + g->destroy(g); + } +} + +UiGeneric* ui_generic_new(UiContext *ctx, const char *name) { UiGeneric *g = ui_malloc(ctx, sizeof(UiGeneric)); memset(g, 0, sizeof(UiGeneric)); + ui_set_destructor(g, (ui_destructor_func)generic_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_GENERIC, g); } @@ -766,6 +798,7 @@ void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObser } static int ui_set_op = 0; +static int ui_onchange_events_enabled = TRUE; void ui_setop_enable(int set) { ui_set_op = set; @@ -775,6 +808,14 @@ int ui_get_setop(void) { return ui_set_op; } +void ui_onchange_events_enable(UiBool enable) { + ui_onchange_events_enabled = enable; +} + +UiBool ui_onchange_events_is_enabled(void) { + return ui_onchange_events_enabled; +} + /* ---------------- List initializers and wrapper functions ---------------- */ void ui_global_list_initializer(ui_list_init_func func, void *userdata) { @@ -782,6 +823,11 @@ void ui_global_list_initializer(ui_list_init_func func, void *userdata) { default_list_init_userdata = userdata; } +void ui_global_list_destructor(ui_list_destroy_func func, void *userdata) { + default_list_destroy = func; + default_list_destroy_userdata = userdata; +} + void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list)) { list->first = first; } diff --git a/ui/common/types.h b/ui/common/types.h index 46d6c2e..2f4a524 100644 --- a/ui/common/types.h +++ b/ui/common/types.h @@ -36,6 +36,7 @@ extern "C" { #endif void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused); +void uic_ucx_list_destroy(UiContext *ctx, UiList *list, void *unused); void uic_int_copy(UiInteger *from, UiInteger *to); void uic_double_copy(UiDouble *from, UiDouble *to); diff --git a/ui/common/wrapper.c b/ui/common/wrapper.c index 50ecf40..575dd29 100644 --- a/ui/common/wrapper.c +++ b/ui/common/wrapper.c @@ -125,6 +125,11 @@ void ui_srclist_remove(UiList *list, int index) { cxListRemove(cxlist, index); } +void ui_srclist_swap(UiList *list, int i1, int i2) { + CxList *cxlist = list->data; + cxListSwap(cxlist, i1, i2); +} + void ui_srclist_clear(UiList *list) { CxList *cxlist = list->data; cxListClear(cxlist); diff --git a/ui/common/wrapper.h b/ui/common/wrapper.h index 88fd8c8..6ce7c9e 100644 --- a/ui/common/wrapper.h +++ b/ui/common/wrapper.h @@ -56,6 +56,7 @@ UIEXPORT UiList* ui_srclist_new(UiContext *ctx, const char *name); UIEXPORT void ui_srclist_add(UiList *list, UiSubList *item); UIEXPORT void ui_srclist_insert(UiList *list, int index, UiSubList *item); UIEXPORT void ui_srclist_remove(UiList *list, int index); +UIEXPORT void ui_srclist_swap(UiList *list, int i1, int i2); UIEXPORT void ui_srclist_clear(UiList *list); UIEXPORT int ui_srclist_size(UiList *list); UIEXPORT void ui_srclist_generate_sublist_num_data(UiList *list); diff --git a/ui/gtk/container.c b/ui/gtk/container.c index 9ca07f9..16f7c21 100644 --- a/ui/gtk/container.c +++ b/ui/gtk/container.c @@ -1140,6 +1140,7 @@ UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init) { UiSplitPane *ct = malloc(sizeof(UiSplitPane)); + memset(ct, 0, sizeof(UiSplitPane)); ct->current_pane = pane; ct->orientation = orientation; ct->max = max; diff --git a/ui/gtk/list.c b/ui/gtk/list.c index e4612bf..abb576c 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -2160,6 +2160,8 @@ static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *subli UiList *list = uisublist.var->value; list->obj = sublist_ptr; list->update = ui_listbox_list_update; + list->getselection = ui_listbox_list_getselection; + list->setselection = ui_listbox_list_setselection; } } @@ -2223,6 +2225,8 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { UiList *list = var->value; list->obj = uilistbox; list->update = ui_listbox_dynamic_update; + list->getselection = ui_listbox_dynamic_getselection; + list->setselection = ui_listbox_dynamic_setselection; ui_listbox_dynamic_update(list, -1); } @@ -2294,6 +2298,32 @@ void ui_listbox_dynamic_update(UiList *list, int x) { ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists)); } +void ui_listbox_dynamic_setselection(UiList *list, UiListSelection sel) { + UiListBox *uilistbox = list->obj; + gtk_list_box_unselect_all(uilistbox->listbox); + if(sel.count > 0) { + int index = sel.rows[0]; + if(index >= 0) { + GtkListBoxRow *row = gtk_list_box_get_row_at_index(uilistbox->listbox, index); + if(row) { + gtk_list_box_select_row(uilistbox->listbox, row); + } + } + } +} + +UiListSelection ui_listbox_dynamic_getselection(UiList *list) { + UiListSelection sel = { 0, NULL }; + UiListBox *uilistbox = list->obj; + GtkListBoxRow *row = gtk_list_box_get_selected_row(uilistbox->listbox); + if(row) { + sel.count = 1; + sel.rows = malloc(sizeof(int)); + sel.rows[0] = gtk_list_box_row_get_index(row); + } + return sel; +} + void ui_listbox_update(UiListBox *listbox, int from, int to) { CxIterator i = cxListIterator(listbox->sublists); size_t pos = 0; @@ -2318,19 +2348,19 @@ void ui_listbox_update(UiListBox *listbox, int from, int to) { static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) { UiListBoxSubList *sublist = data->customdata0; - UiSubListEventData eventdata; - eventdata.list = sublist->var->value; - eventdata.sublist_index = sublist->index; - eventdata.row_index = data->value0; - eventdata.sublist_userdata = sublist->userdata; - eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index); - eventdata.event_data = data->customdata2; + UiSubListEventData *eventdata = &sublist->listbox->current_eventdata; + eventdata->list = sublist->var->value; + eventdata->sublist_index = sublist->index; + eventdata->row_index = data->value0; + eventdata->sublist_userdata = sublist->userdata; + eventdata->row_data = eventdata->list->get(eventdata->list, eventdata->row_index); + eventdata->event_data = data->customdata2; UiEvent event; event.obj = data->obj; event.window = event.obj->window; event.document = event.obj->ctx->document; - event.eventdata = &eventdata; + event.eventdata = eventdata; event.eventdatatype = UI_EVENT_DATA_SUBLIST; event.intval = data->value0; event.set = ui_get_setop(); @@ -2340,13 +2370,15 @@ static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) { } if(data->customdata3) { + uic_set_tmp_eventdata(eventdata, UI_EVENT_DATA_SUBLIST); + UIMENU menu = data->customdata3; g_object_set_data(G_OBJECT(button), "ui-button-popup", menu); gtk_popover_popup(GTK_POPOVER(menu)); } } -#if GTK_CHECK_VERSION(3, 0, 0) +#if GTK_CHECK_VERSION(4, 0, 0) static void button_popover_closed(GtkPopover *popover, GtkWidget *button) { g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL); if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) { @@ -2354,6 +2386,14 @@ static void button_popover_closed(GtkPopover *popover, GtkWidget *button) { gtk_widget_set_visible(button, FALSE); } } +#else +static void popup_hide(GtkWidget *self, GtkWidget *button) { + g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL); + if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) { + g_object_set_data(G_OBJECT(button), "ui-button-invisible", NULL); + gtk_widget_set_visible(button, FALSE); + } +} #endif static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) { @@ -2433,7 +2473,11 @@ static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubLis if(item->button_menu) { UIMENU menu = ui_contextmenu_create(item->button_menu, listbox->obj, button); event->customdata3 = menu; - g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button); +#if GTK_CHECK_VERSION(4, 0, 0) + g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button); +#else + g_signal_connect(menu, "hide", G_CALLBACK(popup_hide), button); +#endif ui_menubuilder_unref(item->button_menu); } } @@ -2645,6 +2689,39 @@ void ui_listbox_list_update(UiList *list, int i) { ui_sourcelist_update_finished(); } +void ui_listbox_list_setselection(UiList *list, UiListSelection sel) { + UiListBoxSubList *sublist = list->obj; + UiListBox *uilistbox = sublist->listbox; + gtk_list_box_unselect_all(uilistbox->listbox); + if(sel.count > 0) { + int index = sel.rows[0]; + if(index >= 0 && index < sublist->numitems) { + int global_index = sublist->startpos + index; + GtkListBoxRow *row = gtk_list_box_get_row_at_index(uilistbox->listbox, global_index); + if(row) { + gtk_list_box_select_row(uilistbox->listbox, row); + } + } + } +} + +UiListSelection ui_listbox_list_getselection(UiList *list) { + UiListSelection sel = { 0, NULL }; + UiListBoxSubList *sublist = list->obj; + UiListBox *uilistbox = sublist->listbox; + GtkListBoxRow *row = gtk_list_box_get_selected_row(uilistbox->listbox); + if(row) { + int index = gtk_list_box_row_get_index(row); + size_t startpos = sublist->startpos; + if(index >= startpos && index < startpos+sublist->numitems) { + sel.count = 1; + sel.rows = malloc(sizeof(int)); + sel.rows[0] = index - startpos; + } + } + return sel; +} + void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata"); if(!data) { diff --git a/ui/gtk/list.h b/ui/gtk/list.h index 06f7fac..635ac58 100644 --- a/ui/gtk/list.h +++ b/ui/gtk/list.h @@ -127,6 +127,7 @@ struct UiListBox { void *onbuttonclickdata; GtkListBoxRow *first_row; UiBool header_is_item; + UiSubListEventData current_eventdata; }; @@ -189,9 +190,14 @@ UiListSelection ui_combobox_getselection(UiList *list); void ui_combobox_setselection(UiList *list, UiListSelection selection); void ui_listbox_dynamic_update(UiList *list, int i); +void ui_listbox_dynamic_setselection(UiList *list, UiListSelection sel); +UiListSelection ui_listbox_dynamic_getselection(UiList *list); + void ui_listbox_update(UiListBox *listbox, int from, int to); void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index); void ui_listbox_list_update(UiList *list, int i); +void ui_listbox_list_setselection(UiList *list, UiListSelection sel); +UiListSelection ui_listbox_list_getselection(UiList *list); void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data); diff --git a/ui/gtk/menu.c b/ui/gtk/menu.c index c399d85..74597ae 100644 --- a/ui/gtk/menu.c +++ b/ui/gtk/menu.c @@ -284,6 +284,7 @@ void ui_update_menuitem_list(UiActiveMenuItemList *list) { event->callback = list->callback; event->value = i - 1; event->customdata = elm; + event->customint = UI_EVENT_DATA_LIST_ELM; g_signal_connect( widget, @@ -309,9 +310,17 @@ void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event) { evt.obj = event->obj; evt.window = event->obj->window; evt.document = event->obj->ctx->document; + if(event->customdata) { + evt.eventdata = event->customdata; + evt.eventdatatype = event->customint; + } else { + evt.eventdata = uic_get_tmp_eventdata(); + evt.eventdatatype = uic_get_tmp_eventdata_type(); + } evt.eventdata = event->customdata; evt.intval = event->value; - event->callback(&evt, event->userdata); + event->callback(&evt, event->userdata); + uic_set_tmp_eventdata(NULL, 0); } void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) { @@ -747,10 +756,16 @@ void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEvent evt.obj = event->obj; evt.window = event->obj->window; evt.document = event->obj->ctx->document; - evt.eventdata = event->customdata; - evt.eventdatatype = event->customint; + if(event->customdata) { + evt.eventdata = event->customdata; + evt.eventdatatype = event->customint; + } else { + evt.eventdata = uic_get_tmp_eventdata(); + evt.eventdatatype = uic_get_tmp_eventdata_type(); + } evt.intval = intval; - event->callback(&evt, event->userdata); + event->callback(&evt, event->userdata); + uic_set_tmp_eventdata(NULL, 0); } void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) { diff --git a/ui/gtk/text.c b/ui/gtk/text.c index 0859642..ccace09 100644 --- a/ui/gtk/text.c +++ b/ui/gtk/text.c @@ -335,6 +335,10 @@ void ui_textarea_realize_event(GtkWidget *widget, gpointer data) { void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea) { + if(!ui_onchange_events_is_enabled()) { + return; + } + UiText *value = textarea->var->value; UiEvent e; @@ -698,14 +702,18 @@ void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield) { } void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) { - UiString *value = textfield->var->value; + if(!ui_onchange_events_is_enabled()) { + return; + } + + UiString *value = textfield->var ? textfield->var->value : NULL; UiEvent e; e.obj = textfield->obj; e.window = e.obj->window; e.document = textfield->obj->ctx->document; e.eventdata = value; - e.eventdatatype = UI_EVENT_DATA_TEXT_VALUE; + e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0; e.intval = 0; e.set = ui_get_setop(); @@ -720,12 +728,14 @@ void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) { void ui_textfield_activate(GtkEntry* self, UiTextField *textfield) { if(textfield->onactivate) { + UiString *value = textfield->var ? textfield->var->value : NULL; + UiEvent e; e.obj = textfield->obj; e.window = e.obj->window; e.document = textfield->obj->ctx->document; - e.eventdata = NULL; - e.eventdatatype = 0; + e.eventdata = value; + e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0; e.intval = 0; e.set = ui_get_setop(); textfield->onactivate(&e, textfield->onactivatedata); diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index b22b66c..f5a2545 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -40,7 +40,6 @@ #include "../common/toolbar.h" #include "../common/threadpool.h" -#include #include #include @@ -165,7 +164,7 @@ void ui_main() { #ifndef UI_GTK2 void ui_app_quit() { - g_application_quit(G_APPLICATION(app)); + g_application_quit(G_APPLICATION(app)); // TODO: fix, does not work } GtkApplication* ui_get_application() { @@ -264,7 +263,7 @@ void ui_set_show_all(UIWIDGET widget, int value) { #endif } -void ui_set_visible(UIWIDGET widget, int visible) { +void ui_set_visible(UIWIDGET widget, UiBool visible) { #if GTK_MAJOR_VERSION >= 4 gtk_widget_set_visible(widget, visible); #else diff --git a/ui/gtk/window.c b/ui/gtk/window.c index f90997f..13c521e 100644 --- a/ui/gtk/window.c +++ b/ui/gtk/window.c @@ -35,6 +35,7 @@ #include "../common/context.h" #include "../common/menu.h" #include "../common/toolbar.h" +#include "../common/utils.h" #include @@ -169,16 +170,7 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side int window_width = window_default_width; int window_height = window_default_height; if(!simple) { - const char *width = ui_get_property("ui.window.width"); - const char *height = ui_get_property("ui.window.height"); - if(width && height) { - int w = atoi(width); - int h = atoi(height); - if(w > 0 && h > 0) { - window_width = w; - window_height = h; - } - } + ui_get_window_default_width(&window_width, &window_height); } gtk_window_set_default_size( GTK_WINDOW(obj->widget), diff --git a/ui/motif/Grid.c b/ui/motif/Grid.c index a286f8a..da74124 100644 --- a/ui/motif/Grid.c +++ b/ui/motif/Grid.c @@ -60,7 +60,7 @@ static XtResource resources[] = XmRDimension, sizeof (Dimension), XtOffsetOf( GridRec, - mywidget.columnspacing), + grid.columnspacing), XmRImmediate, (XtPointer) 0 }, @@ -70,17 +70,47 @@ static XtResource resources[] = XmRDimension, sizeof (Dimension), XtOffsetOf( GridRec, - mywidget.rowspacing), + grid.rowspacing), XmRImmediate, (XtPointer) 0 }, { - gridMargin, - gridMargin, + gridPaddingLeft, + gridPaddingLeft, XmRDimension, sizeof (Dimension), XtOffsetOf( GridRec, - mywidget.margin), + grid.padding_left), + XmRImmediate, + (XtPointer) 0 + }, + { + gridPaddingRight, + gridPaddingRight, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridRec, + grid.padding_right), + XmRImmediate, + (XtPointer) 0 + }, + { + gridPaddingTop, + gridPaddingTop, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridRec, + grid.padding_top), + XmRImmediate, + (XtPointer) 0 + }, + { + gridPaddingBottom, + gridPaddingBottom, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridRec, + grid.padding_bottom), XmRImmediate, (XtPointer) 0 } @@ -305,8 +335,8 @@ void grid_class_initialize(void) { void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) { Grid mn = (Grid)new; - mn->mywidget.max_col = 0; - mn->mywidget.max_row = 0; + mn->grid.max_col = 0; + mn->grid.max_row = 0; } void grid_realize(Widget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) { @@ -364,17 +394,17 @@ XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, X } void GridChangeManaged(Widget widget) { - + grid_place_children((Grid)widget); } Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { GridConstraintRec *constraints = neww->core.constraints; Grid grid = (Grid)XtParent(neww); - if(constraints->grid.x > grid->mywidget.max_col) { - grid->mywidget.max_col = constraints->grid.x; + if(constraints->grid.x > grid->grid.max_col) { + grid->grid.max_col = constraints->grid.x; } - if(constraints->grid.y > grid->mywidget.max_row) { - grid->mywidget.max_row = constraints->grid.y; + if(constraints->grid.y > grid->grid.max_row) { + grid->grid.max_row = constraints->grid.y; } } @@ -389,25 +419,31 @@ void grid_constraint_init( GridConstraintRec *constraints = neww->core.constraints; Grid grid = (Grid)XtParent(neww); - if(constraints->grid.x > grid->mywidget.max_col) { - grid->mywidget.max_col = constraints->grid.x; + if(constraints->grid.x > grid->grid.max_col) { + grid->grid.max_col = constraints->grid.x; } - if(constraints->grid.y > grid->mywidget.max_row) { - grid->mywidget.max_row = constraints->grid.y; + if(constraints->grid.y > grid->grid.max_row) { + grid->grid.max_row = constraints->grid.y; } constraints->grid.pref_width = neww->core.width; constraints->grid.pref_height = neww->core.height; } void grid_place_children(Grid w) { - int ncols = w->mywidget.max_col+1; - int nrows = w->mywidget.max_row+1; + if(!XtIsRealized((Widget)w)) { + return; + } + + int ncols = w->grid.max_col+1; + int nrows = w->grid.max_row+1; GridDef *cols = calloc(ncols, sizeof(GridDef)); GridDef *rows = calloc(nrows, sizeof(GridDef)); int num_cols_expanding = 0; int num_rows_expanding = 0; - int req_width = 0; - int req_height = 0; + int req_width = w->grid.padding_left + w->grid.padding_right; + int req_height = w->grid.padding_top + w->grid.padding_bottom; + int width = w->core.width; + int height = w->core.height; //printf("container width: %d\n", (int)w->core.width); @@ -428,6 +464,12 @@ void grid_place_children(Grid w) { if(constraints->grid.pref_width < constraints->grid.min_width) { constraints->grid.pref_width = constraints->grid.min_width; } + int elm_width = constraints->grid.pref_width + constraints->grid.margin_left + constraints->grid.margin_right; + int elm_height = constraints->grid.pref_height + constraints->grid.margin_top + constraints->grid.margin_bottom; + if(!XtIsManaged(child)) { + elm_width = 0; + elm_height = 0; + } if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) { continue; @@ -489,12 +531,12 @@ void grid_place_children(Grid w) { span_width = last_col->size; } - int diff = constraints->grid.pref_width - span_width; + int diff = elm_width - span_width; if(diff > 0) { last_col->size += diff; } - } else if(constraints->grid.pref_width > col->size) { - col->size = constraints->grid.pref_width; + } else if(elm_width > col->size) { + col->size = elm_width; } // row size if(constraints->grid.rowspan > 1) { @@ -505,12 +547,12 @@ void grid_place_children(Grid w) { span_height = last_row->size; } - int diff = constraints->grid.pref_height - span_height; + int diff = elm_height - span_height; if(diff > 0) { last_row->size += diff; } - } else if(constraints->grid.pref_height > row->size) { - row->size = constraints->grid.pref_height; + } else if(elm_height > row->size) { + row->size = elm_height; } } span_max = 50000; // not sure if this is unreasonable low or high @@ -530,10 +572,23 @@ void grid_place_children(Grid w) { req_height += rows[i].size; } + int total_colspacing = 0; + int total_rowspacing = 0; + for(int i=0;i+1 0) { + total_colspacing += w->grid.columnspacing; + } + } + for(int i=0;i+1 0) { + total_rowspacing += w->grid.rowspacing; + } + } + if(req_width > 0 && req_height > 0) { // add col/row spacing - req_width += (ncols-1)*w->mywidget.columnspacing; - req_height += (nrows-1)*w->mywidget.rowspacing; + req_width += total_colspacing; //(ncols-1)*w->grid.columnspacing; + req_height += total_rowspacing; //(nrows-1)*w->grid.rowspacing; Widget parent = w->core.parent; Dimension rwidth = req_width; @@ -545,9 +600,9 @@ void grid_place_children(Grid w) { //rheight = w->core.height; } - if(!w->mywidget.sizerequest) { + if(!w->grid.sizerequest) { Dimension actual_width, actual_height; - w->mywidget.sizerequest = TRUE; + w->grid.sizerequest = TRUE; //printf("sizerequest: %d x %d\n", (int)req_width, (int)req_height); @@ -558,7 +613,7 @@ void grid_place_children(Grid w) { //XtGeometryResult result = XtMakeGeometryRequest((Widget)w, &request, &reply); XtMakeResizeRequest((Widget)w, req_width, req_height, &actual_width, &actual_height); - w->mywidget.sizerequest = FALSE; + w->grid.sizerequest = FALSE; //printf("size request: %d %d\n", (int)actual_width, (int)actual_height); } @@ -568,37 +623,41 @@ void grid_place_children(Grid w) { // how much space can we add to each expanding col/row int hexpand = 0; - int width_diff = (int)w->core.width - req_width; + int width_diff = width - req_width; int hexpand2 = 0; if(width_diff > 0 && num_cols_expanding > 0) { hexpand = width_diff / num_cols_expanding; hexpand2 = width_diff-hexpand*num_cols_expanding; } - int x = 0; + int x = w->grid.padding_left; for(int i=0;imywidget.columnspacing; + if(cols[i].size > 0) { + x += cols[i].size + w->grid.columnspacing; + } hexpand2 = 0; } int vexpand = 0; - int height_diff = (int)w->core.height - req_height; + int height_diff = height - req_height; int vexpand2 = 0; if(height_diff > 0 && num_rows_expanding > 0) { vexpand = height_diff / num_rows_expanding; vexpand2 = height_diff-vexpand*num_rows_expanding; } - int y = 0; + int y = w->grid.padding_bottom; for(int i=0;imywidget.rowspacing; + if(rows[i].size > 0) { + y += rows[i].size += w->grid.rowspacing; + } vexpand2 = 0; } @@ -608,8 +667,8 @@ void grid_place_children(Grid w) { GridConstraintRec *constraints = child->core.constraints; GridDef c = cols[constraints->grid.x]; GridDef r = rows[constraints->grid.y]; - int x = c.pos; - int y = r.pos; + int x = c.pos + constraints->grid.margin_left; + int y = r.pos + constraints->grid.margin_top; int width = constraints->grid.pref_width; int height = constraints->grid.pref_height; if(constraints->grid.hfill) { @@ -617,12 +676,12 @@ void grid_place_children(Grid w) { Dimension cwidth = 0; for(int j=0;jgrid.colspan;j++) { if(constraints->grid.x+j < ncols) { - cwidth += cols[constraints->grid.x+j].size + (j > 0 ? w->mywidget.columnspacing : 0); + cwidth += cols[constraints->grid.x+j].size + (j > 0 ? w->grid.columnspacing : 0); } } width = cwidth; } else { - width = c.size - w->mywidget.columnspacing; + width = c.size - w->grid.columnspacing - constraints->grid.margin_left - constraints->grid.margin_right; } } if(constraints->grid.vfill) { @@ -630,15 +689,15 @@ void grid_place_children(Grid w) { Dimension cheight = 0; for(int j=0;jgrid.rowspan;j++) { if(constraints->grid.y+j < nrows) { - cheight += rows[constraints->grid.y+j].size + (j > 0 ? w->mywidget.rowspacing : 0); + cheight += rows[constraints->grid.y+j].size + (j > 0 ? w->grid.rowspacing : 0); } } height = cheight; } else { - height = r.size - w->mywidget.rowspacing; + height = r.size - w->grid.rowspacing - constraints->grid.margin_top - constraints->grid.margin_bottom; } } - + if(width > 0 && height > 0) { XtConfigureWidget(child, x, y, width, height, child->core.border_width); } diff --git a/ui/motif/Grid.h b/ui/motif/Grid.h index f135cbd..af75b41 100644 --- a/ui/motif/Grid.h +++ b/ui/motif/Grid.h @@ -42,8 +42,11 @@ extern "C" { // resources #define gridColumnSpacing "gridColumnSpacing" -#define gridRowSpacing "gridRowSpacing" -#define gridMargin "gridMargin" +#define gridRowSpacing "gridRowSpacing" +#define gridPaddingLeft "gridPaddingLeft" +#define gridPaddingRight "gridPaddingRight" +#define gridPaddingTop "gridPaddingTop" +#define gridPaddingBottom "gridPaddingBottom" // constraints #define gridColumn "gridColumn" @@ -75,21 +78,20 @@ typedef struct GridClassRec { CoreClassPart core_class; CompositeClassPart composite_class; ConstraintClassPart constraint_class; - XmManagerClassPart manager_class; - GridClassPart mywidgetclass; + XmManagerClassPart manager_class; + GridClassPart gridwidgetclass; } GridClassRec; typedef struct GridPart { - int margin_left; - int margin_right; - int margin_top; - int margin_bottom; + int padding_left; + int padding_right; + int padding_top; + int padding_bottom; int max_col; int max_row; Dimension columnspacing; Dimension rowspacing; - Dimension margin; Boolean sizerequest; } GridPart; @@ -99,7 +101,7 @@ typedef struct GridRec { CompositePart composite; ConstraintPart constraint; XmManagerPart manager; - GridPart mywidget; + GridPart grid; } GridRec; typedef struct GridContraintPart { diff --git a/ui/motif/button.c b/ui/motif/button.c index d2528c4..6393e76 100644 --- a/ui/motif/button.c +++ b/ui/motif/button.c @@ -77,7 +77,7 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) { XtAddCallback( button, XmNdestroyCallback, - (XtCallbackProc)ui_destroy_eventdata, + (XtCallbackProc)ui_destroy_data, eventdata); } @@ -224,7 +224,7 @@ void ui_bind_togglebutton( XtAddCallback( widget, XmNdestroyCallback, - (XtCallbackProc)ui_destroy_eventdata, + (XtCallbackProc)ui_destroy_data, event); } @@ -341,7 +341,7 @@ void ui_bind_radiobutton(UiObject *obj, Widget rbutton, UiInteger *value, const XtAddCallback( rbutton, XmNdestroyCallback, - (XtCallbackProc)ui_destroy_eventdata, + (XtCallbackProc)ui_destroy_data, event); } @@ -411,7 +411,7 @@ UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) { XtAddCallback( button, XmNdestroyCallback, - (XtCallbackProc)ui_destroy_eventdata, + (XtCallbackProc)ui_destroy_data, event); XmStringFree(label); diff --git a/ui/motif/container.c b/ui/motif/container.c index 444bedb..3464862 100644 --- a/ui/motif/container.c +++ b/ui/motif/container.c @@ -55,6 +55,18 @@ void ui_container_add(UiContainerPrivate *container, Widget widget) { container->add(container, widget); } +void ui_container_apply_grid_margin( + Arg *args, + int *n, + int margin_left, int margin_right, int margin_top, int margin_bottom) +{ + int c = *n; + XtSetArg(args[c], gridMarginLeft, margin_left); c++; + XtSetArg(args[c], gridMarginRight, margin_right); c++; + XtSetArg(args[c], gridMarginTop, margin_top); c++; + XtSetArg(args[c], gridMarginBottom, margin_bottom); c++; + *n = c; +} /* ---------------------------- Box Container ---------------------------- */ @@ -66,9 +78,9 @@ static UIWIDGET box_create(UiObject *obj, UiContainerArgs *args, UiBoxOrientatio int n = 0; if(orientation == UI_BOX_VERTICAL) { - //XtSetArg(xargs[n], gridRowSpacing, args->spacing); n++; + XtSetArg(xargs[n], gridRowSpacing, args->spacing); n++; } else { - //XtSetArg(xargs[n], gridColumnSpacing, args->spacing); n++; + XtSetArg(xargs[n], gridColumnSpacing, args->spacing); n++; } Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); @@ -102,7 +114,7 @@ UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orie } static Widget ui_box_container_prepare(UiBoxContainer *box, UiLayout *layout, Arg *args, int *n) { - int a = *n; + ui_container_apply_grid_margin(args, n, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); box->n++; return box->container.widget; } @@ -151,7 +163,6 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { UiLayout layout = UI_ARGS2LAYOUT(args); Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); - XtSetArg(xargs[n], gridMargin, args->margin); n++; XtSetArg(xargs[n], gridColumnSpacing, args->columnspacing); n++; XtSetArg(xargs[n], gridRowSpacing, args->rowspacing); n++; Widget grid = XtCreateManagedWidget(args->name ? args->name : "gridcontainer", gridClass, parent, xargs, n); @@ -218,6 +229,7 @@ Widget ui_grid_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg } *n = a; + ui_container_apply_grid_margin(args, n, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); return ctn->widget; } @@ -227,6 +239,103 @@ void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget) { grid->container.container.newline = FALSE; } +/* -------------------------- Frame Container -------------------------- */ + +UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UiLayout layout = UI_ARGS2LAYOUT(args); + + Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); + if(args->width > 0) { + XtSetArg(xargs[n], XmNwidth, args->width); n++; + } + if(args->height > 0) { + XtSetArg(xargs[n], XmNheight, args->height); n++; + } + + char *name = args->name ? (char*)args->name : "frame"; + Widget frame = XmCreateFrame(parent, name, xargs, n); + XtManageChild(frame); + ui_container_add(ctn, frame); + + if(args->label) { + XmString s = XmStringCreateLocalized((char*)args->label); + n = 0; + XtSetArg(xargs[n], XmNlabelString, s); n++; + XtSetArg(xargs[n], XmNchildType, XmFRAME_TITLE_CHILD); n++; + Widget label = XmCreateLabel(frame, "frame_label", xargs, n); + XtManageChild(label); + XmStringFree(s); + } + + UiContainerX *container = ui_frame_container(obj, frame); + uic_object_push_container(obj, container); + + UiContainerArgs sub_args = { + .spacing = args->spacing, + .columnspacing = args->columnspacing, + .rowspacing = args->rowspacing, + .margin = args->padding + }; + switch(args->subcontainer) { + default: break; + case UI_CONTAINER_VBOX: { + ui_vbox_create(obj, &sub_args); + uic_object_remove_second_last_container(obj); + break; + } + case UI_CONTAINER_HBOX: { + ui_hbox_create(obj, &sub_args); + uic_object_remove_second_last_container(obj); + break; + } + case UI_CONTAINER_GRID: { + ui_grid_create(obj, &sub_args); + uic_object_remove_second_last_container(obj); + break; + } + } + + + return frame; +} + +UiContainerX* ui_frame_container(UiObject *obj, Widget frame) { + UiContainerPrivate *ctn = ui_malloc(obj->ctx, sizeof(UiContainerPrivate)); + memset(ctn, 0, sizeof(UiContainerPrivate)); + ctn->prepare = ui_frame_container_prepare; + ctn->add = ui_frame_container_add; + ctn->widget = frame; + return (UiContainerX*)ctn; +} + +Widget ui_frame_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) { + int a = *n; + XtSetArg(args[a], XmNchildType, XmFRAME_WORKAREA_CHILD); + *n = a+1; + return ctn->widget; +} + +void ui_frame_container_add(UiContainerPrivate *ctn, Widget widget) { + // NOOP +} + +/* -------------------------- SplitPane -------------------------- */ + +UIWIDGET ui_splitpane_create(UiObject *obj, UiSplitPaneArgs *args, int orientation) { + return NULL; // TODO +} + +UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { + return ui_splitpane_create(obj, args, XmHORIZONTAL); +} + +UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { + return ui_splitpane_create(obj, args, XmVERTICAL); +} /* -------------------------- TabView Container -------------------------- */ @@ -244,9 +353,10 @@ static void ui_tabbar_resize(Widget widget, XtPointer udata, XtPointer cdata) { if(numbuttons == 0) { return; } + width--; int button_width = width / numbuttons; int x = 0; - + CxIterator i = cxListIterator(tabview->tabs); cx_foreach(UiTab *, tab, i) { if(i.index + 1 == numbuttons) { @@ -342,6 +452,7 @@ UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) { tabview->current_index = -1; UiTabViewContainer *ct = ui_malloc(obj->ctx, sizeof(UiTabViewContainer)); + memset(ct, 0, sizeof(UiTabViewContainer)); ct->container.widget = form; ct->container.type = UI_CONTAINER_TABVIEW; ct->container.prepare = ui_tabview_container_prepare; @@ -592,6 +703,12 @@ UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs *args) { int n = 0; XtSetArg(xargs[n], XmNscrollingPolicy, XmAUTOMATIC); n++; + if(args->width > 0) { + XtSetArg(xargs[n], XmNwidth, args->width); n++; + } + if(args->height > 0) { + XtSetArg(xargs[n], XmNheight, args->height); n++; + } Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); Widget scrolledwindow = XmCreateScrolledWindow(parent, "scrolledwindow", xargs, n); diff --git a/ui/motif/container.h b/ui/motif/container.h index 9dd73cc..30262e5 100644 --- a/ui/motif/container.h +++ b/ui/motif/container.h @@ -129,6 +129,10 @@ typedef struct UiTabViewContainer { Widget ui_container_prepare(UiContainerPrivate *container, UiLayout *layout, Arg *args, int *n); void ui_container_add(UiContainerPrivate *container, Widget widget); +void ui_container_apply_grid_margin( + Arg *args, + int *n, + int margin_left, int margin_right, int margin_top, int margin_bottom); void ui_motif_tabview_select(UiMotifTabView *tabview, int tab); void ui_motif_tabview_add_tab(UiMotifTabView *tabview, int index, const char *name, Widget child); @@ -156,6 +160,10 @@ UiContainerX* ui_grid_container( Widget ui_grid_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n); void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget); +UiContainerX* ui_frame_container(UiObject *obj, Widget frame); +Widget ui_frame_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n); +void ui_frame_container_add(UiContainerPrivate *ctn, Widget widget); + #ifdef __cplusplus } #endif diff --git a/ui/motif/graphics.c b/ui/motif/graphics.c index fab9930..2863695 100644 --- a/ui/motif/graphics.c +++ b/ui/motif/graphics.c @@ -34,3 +34,87 @@ #include "graphics.h" #include "container.h" + +UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UiLayout layout = UI_ARGS2LAYOUT(args); + + Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); + char *name = args->name ? (char*)args->name : "drawingarea"; + + Widget widget = XmCreateDrawingArea(parent, name, xargs, n); + XtManageChild(widget); + ui_container_add(ctn, widget); + + UiDrawingArea *drawingarea = malloc(sizeof(UiDrawingArea)); + drawingarea->obj = obj; + drawingarea->draw = args->draw; + drawingarea->drawdata = args->drawdata; + drawingarea->onclick = args->onclick; + drawingarea->onclickdata = args->onclickdata; + drawingarea->onmotion = args->onmotion; + drawingarea->onmotiondata = args->onmotiondata; + drawingarea->gc = NULL; + + XtAddCallback( + widget, + XmNdestroyCallback, + (XtCallbackProc)ui_drawingarea_destroy, + drawingarea); + XtAddCallback( + widget, + XmNexposeCallback, + (XtCallbackProc)ui_drawingarea_expose, + drawingarea); + + return widget; +} + +void ui_drawingarea_destroy(Widget w, UiDrawingArea *drawingarea, XtPointer d) { + if(drawingarea->gc) { + XFreeGC(XtDisplay(w), drawingarea->gc); + } + free(drawingarea); +} + +void ui_drawingarea_expose(Widget w, UiDrawingArea *drawingarea, XtPointer d) { + Display *dp = XtDisplay(w); + + if(!drawingarea->gc) { + XGCValues gcvals; + gcvals.foreground = BlackPixelOfScreen(XtScreen(w)); + drawingarea->gc = XCreateGC(dp, XtWindow(w), (GCForeground), &gcvals); + } + + if(drawingarea->draw) { + UiEvent event; + event.obj = drawingarea->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = 0; + event.set = 0; + + UiXlibGraphics g; + g.g.width = w->core.width; + g.g.height = w->core.height; + g.widget = w; + g.display = dp; + g.colormap = w->core.colormap; + g.gc = drawingarea->gc; + + drawingarea->draw(&event, (UiGraphics*)&g, drawingarea->drawdata); + } +} + +void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height) { + +} + +void ui_drawingarea_redraw(UIWIDGET drawingarea) { + +} diff --git a/ui/motif/graphics.h b/ui/motif/graphics.h index addec59..82b533e 100644 --- a/ui/motif/graphics.h +++ b/ui/motif/graphics.h @@ -35,7 +35,30 @@ #ifdef __cplusplus extern "C" { #endif + +typedef struct UiDrawingArea { + UiObject *obj; + ui_drawfunc draw; + void *drawdata; + ui_callback onclick; + void *onclickdata; + ui_callback onmotion; + void *onmotiondata; + + GC gc; +} UiDrawingArea; +typedef struct UiXlibGraphics { + UiGraphics g; + Display *display; + Widget widget; + Colormap colormap; + GC gc; +} UiXlibGraphics; + +void ui_drawingarea_destroy(Widget w, UiDrawingArea *drawingarea, XtPointer d); + +void ui_drawingarea_expose(Widget w, UiDrawingArea *drawingarea, XtPointer d); #ifdef __cplusplus diff --git a/ui/motif/label.c b/ui/motif/label.c index 749c9e3..2acb2c3 100644 --- a/ui/motif/label.c +++ b/ui/motif/label.c @@ -47,17 +47,34 @@ static UIWIDGET label_create(UiObject *obj, UiLabelArgs *args, int align) { XtSetArg(xargs[n], XmNalignment, align); n++; XmString label = NULL; - if(args->label) { - label = XmStringCreateLocalized((char*)args->label); - XtSetArg(xargs[n], XmNlabelString, label); n++; + char *lbl = (char*)args->label; + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); + if(var) { + UiString *s = var->value; + lbl = s->value.ptr; + } + + if(lbl) { + label = XmStringCreateLocalized(lbl); + } else { + label = XmStringCreateLocalized(""); } + XtSetArg(xargs[n], XmNlabelString, label); n++; char *name = args->name ? (char*)args->name : "label"; Widget w = XmCreateLabel(parent, name, xargs, n); XtManageChild(w); ui_container_add(ctn, w); XmStringFree(label); + + if(var) { + UiString *s = var->value; + s->obj = w; + s->get = ui_label_get; + s->set = ui_label_set; + } + return w; } @@ -73,6 +90,36 @@ UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs *args) { return label_create(obj, args, XmALIGNMENT_END); } +char* ui_label_get(UiString *s) { + if(s->value.free) { + s->value.free(s->value.ptr); + s->value.free = NULL; + s->value.ptr = NULL; + } + Widget w = s->obj; + XmString s1 = NULL; + XtVaGetValues(w, XmNlabelString, &s1, NULL); + if(s1) { + char *value; + if(XmStringGetLtoR(s1, XmFONTLIST_DEFAULT_TAG, &value)) { + s->value.ptr = value; + s->value.free = (cx_destructor_func)XtFree; + } + } + return s->value.ptr; +} + +void ui_label_set(UiString *s, const char *str) { + Widget w = s->obj; + XmString s1 = XmStringCreateLocalized(str ? (char*)str : ""); + XtVaSetValues(w, XmNlabelString, s1, NULL); + XmStringFree(s1); + if(s->value.free) { + s->value.free(s->value.ptr); + s->value.free = NULL; + s->value.ptr = NULL; + } +} /* -------------------------- progressbar/spiner -------------------------- */ diff --git a/ui/motif/label.h b/ui/motif/label.h index a79dd64..8d7ca0f 100644 --- a/ui/motif/label.h +++ b/ui/motif/label.h @@ -46,6 +46,8 @@ typedef struct UiProgressBar { Pixel color; } UiProgressBar; +char* ui_label_get(UiString *s); +void ui_label_set(UiString *s, const char *str); double ui_progressbar_get(UiDouble *d); void ui_progressbar_set(UiDouble *d, double value); diff --git a/ui/motif/list.c b/ui/motif/list.c index 0bb1cbe..b1e96ee 100644 --- a/ui/motif/list.c +++ b/ui/motif/list.c @@ -28,6 +28,7 @@ #include #include +#include #include "container.h" @@ -62,6 +63,9 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) { } else { XtSetArg(xargs[n], XmNselectionPolicy, XmSINGLE_SELECT); n++; } + if(args->height > 0) { + XtSetArg(xargs[n], XmNheight, args->height); n++; + } char *name = args->name ? (char*)args->name : "listview"; Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); @@ -144,8 +148,12 @@ static void listview_save_selection(UiListView *listview, XmListCallbackStruct * UiListSelection sel = { cb->selected_item_count, NULL }; if(sel.count > 0) { sel.rows = calloc(sel.count, sizeof(int)); - for(int i=0;iselected_item_positions[i]-1; + if(sel.count == 1) { + sel.rows[0] = cb->item_position-1; + } else if(cb->selected_item_positions) { + for(int i=0;iselected_item_positions[i]-1; + } } } free(listview->current_selection.rows); @@ -209,10 +217,18 @@ void ui_listview_update(UiList *list, int i) { UiListSelection ui_listview_getselection(UiList *list) { UiListView *listview = list->obj; - UiListSelection sel = { listview->current_selection.count, NULL }; - if(sel.count > 0) { - sel.rows = calloc(sel.count, sizeof(int)); - memcpy(sel.rows, listview->current_selection.rows, sel.count*sizeof(int)); + UiListSelection sel = { 0, NULL }; + int *selpositions = NULL; + int numpos = 0; + XtVaGetValues(listview->widget, XmNselectedPositions, &selpositions, XmNselectedPositionCount, &numpos, NULL); + if(numpos > 0) { + sel.rows = calloc(numpos, sizeof(int)); + sel.count = numpos; + memcpy(sel.rows, selpositions, numpos*sizeof(int)); + // motif selected positions start at index 1 -> translate positions + for(int i=0;ivalue; list->obj = listview; list->update = ui_listview_update; - list->getselection = ui_listview_getselection; - list->setselection = ui_listview_setselection; + list->getselection = ui_dropdown_getselection; + list->setselection = ui_dropdown_setselection; ui_listview_update(list, 0); } @@ -317,3 +333,25 @@ UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) { return widget; } + +void ui_dropdown_setselection(UiList *list, UiListSelection selection) { + UiListView *listview = list->obj; + if(selection.count > 0) { + XtVaSetValues(listview->widget, XmNselectedPosition, selection.rows[0], NULL); + } else { + XtVaSetValues(listview->widget, XmNselectedPosition, 0, NULL); + } +} + +UiListSelection ui_dropdown_getselection(UiList *list) { + UiListView *listview = list->obj; + int pos = -1; + XtVaGetValues(listview->widget, XmNselectedPosition, &pos, NULL); + UiListSelection sel = { 0, NULL }; + if(pos >= 0) { + sel.rows = malloc(sizeof(int)); + sel.rows[0] = pos; + sel.count = 1; + } + return sel; +} diff --git a/ui/motif/list.h b/ui/motif/list.h index 2d88c72..04756a2 100644 --- a/ui/motif/list.h +++ b/ui/motif/list.h @@ -69,6 +69,9 @@ void ui_listview_update(UiList *list, int i); UiListSelection ui_listview_getselection(UiList *list); void ui_listview_setselection(UiList *list, UiListSelection selection); +void ui_dropdown_setselection(UiList *list, UiListSelection selection); +UiListSelection ui_dropdown_getselection(UiList *list); + void* ui_strmodel_getvalue(void *elm, int column); #ifdef __cplusplus diff --git a/ui/motif/menu.c b/ui/motif/menu.c index c2038a8..f3e1f41 100644 --- a/ui/motif/menu.c +++ b/ui/motif/menu.c @@ -55,10 +55,10 @@ static ui_menu_add_f createMenuItem[] = { /* UI_MENU_SEPARATOR */ add_menuseparator_widget }; -void ui_create_menubar(UiObject *obj, Widget window) { +Widget ui_create_menubar(UiObject *obj, Widget window) { UiMenu *menus_begin = uic_get_menu_list(); if(!menus_begin) { - return; + return NULL; } Widget menubar = XmCreateMenuBar(window, "menubar", NULL, 0); @@ -70,6 +70,8 @@ void ui_create_menubar(UiObject *obj, Widget window) { add_menu_widget(menubar, 0, &menu->item, obj); ls = (UiMenu*)ls->item.next; } + + return menubar; } void ui_add_menu_items(Widget parent, int i, UiMenu *menu, UiObject *obj) { @@ -93,16 +95,15 @@ void add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { XtSetArg(args[n], XmNlabelString, s); n++; } - Widget submenu = XmVaCreateSimplePulldownMenu(parent, "menu_pulldown", i, NULL, NULL); + Widget submenu = XmCreatePulldownMenu(parent, "menu_pulldown", NULL, 0); XtSetArg(args[n], XmNsubMenuId, submenu); n++; - Widget menuItem = XtCreateManagedWidget( + (void)XtCreateManagedWidget( "menuitem", xmCascadeButtonWidgetClass, parent, args, n); - if(s) { XmStringFree(s); } @@ -145,7 +146,7 @@ void add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) XtAddCallback( mitem, XmNdestroyCallback, - (XtCallbackProc)ui_destroy_eventdata, + (XtCallbackProc)ui_destroy_data, eventdata); } @@ -337,7 +338,7 @@ void ui_update_menuitem_list(UiActiveMenuItemList *list) { XtAddCallback( mitem, XmNdestroyCallback, - (XtCallbackProc)ui_destroy_eventdata, + (XtCallbackProc)ui_destroy_data, eventdata); } diff --git a/ui/motif/menu.h b/ui/motif/menu.h index efa99ce..4c40d79 100644 --- a/ui/motif/menu.h +++ b/ui/motif/menu.h @@ -52,7 +52,7 @@ struct UiActiveMenuItemList { typedef void(*ui_menu_add_f)(Widget, int, UiMenuItemI*, UiObject*); -void ui_create_menubar(UiObject *obj, Widget window); +Widget ui_create_menubar(UiObject *obj, Widget window); void ui_add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); void add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); diff --git a/ui/motif/objs.mk b/ui/motif/objs.mk index b1b51b2..8d5d0fd 100644 --- a/ui/motif/objs.mk +++ b/ui/motif/objs.mk @@ -39,12 +39,15 @@ MOTIFOBJ += toolbar.o MOTIFOBJ += button.o MOTIFOBJ += label.o MOTIFOBJ += text.o +MOTIFOBJ += pathbar.o MOTIFOBJ += list.o MOTIFOBJ += graphics.o MOTIFOBJ += range.o MOTIFOBJ += dnd.o MOTIFOBJ += image.o MOTIFOBJ += Grid.o +MOTIFOBJ += entry.o +MOTIFOBJ += Fsb.o TOOLKITOBJS += $(MOTIFOBJ:%=$(MOTIF_OBJPRE)%) TOOLKITSOURCE += $(MOTIFOBJ:%.o=motif/%.c) diff --git a/ui/motif/text.c b/ui/motif/text.c index 5c5db1f..52f4f16 100644 --- a/ui/motif/text.c +++ b/ui/motif/text.c @@ -32,6 +32,9 @@ #include "text.h" #include "container.h" +#include "pathbar.h" + +#include "../common/utils.h" #include @@ -407,8 +410,18 @@ static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, int frame ui_set_widget_groups(obj->ctx, textfield, args->groups); + UiEventDataExt *eventdata = malloc(sizeof(UiEventDataExt)); + memset(eventdata, 0, sizeof(UiEventDataExt)); + eventdata->obj = obj; + eventdata->callback = args->onactivate; + eventdata->userdata = args->onactivatedata; + eventdata->callback2 = args->onchange; + eventdata->userdata2 = args->onchangedata; + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); if(var) { + eventdata->customdata0 = var; + UiString *value = (UiString*)var->value; value->obj = textfield; value->get = ui_textfield_get; @@ -419,9 +432,54 @@ static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, int frame } } + XtAddCallback( + textfield, + XmNactivateCallback, + (XtCallbackProc)ui_textfield_activate, + eventdata); + XtAddCallback( + textfield, + XmNvalueChangedCallback, + (XtCallbackProc)ui_textfield_value_changed, + eventdata); + XtAddCallback( + textfield, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_data, + eventdata); + return textfield; } +static void textfield_event(UiEventDataExt *eventdata, ui_callback callback, void *userdata) { + if(callback) { + UiVar *var = eventdata->customdata0; + UiString *value = var ? var->value : NULL; + + UiEvent e; + e.obj = eventdata->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = value; + e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0; + e.intval = 0; + e.set = ui_get_setop(); + callback(&e, userdata); + } +} + +void ui_textfield_activate(Widget widget, XtPointer ud, XtPointer cb) { + UiEventDataExt *eventdata = ud; + textfield_event(ud, eventdata->callback, eventdata->userdata); +} + +void ui_textfield_value_changed(Widget widget, XtPointer ud, XtPointer cb) { + UiEventDataExt *eventdata = ud; + if(ui_onchange_events_is_enabled()) { + textfield_event(ud, eventdata->callback2, eventdata->userdata2); + } +} + UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args) { return create_textfield(obj, args, FALSE, FALSE); } @@ -453,459 +511,6 @@ void ui_textfield_set(UiString *str, const char *value) { } - - - -/* -------------------- path bar -------------------- */ - -#define XNECreateText(parent,name,args,count) XmCreateTextField(parent,name,args,count) -#define XNETextSetString(widget,value) XmTextFieldSetString(widget,value) -#define XNETextGetString(widget) XmTextFieldGetString(widget) -#define XNETextGetLastPosition(widget) XmTextFieldGetLastPosition(widget) -#define XNETextSetInsertionPosition(widget, i) XmTextFieldSetInsertionPosition(widget, i) -#define XNETextSetSelection(w, f, l, t) XmTextFieldSetSelection(w, f, l, t) - -typedef void(*updatedir_callback)(void*,char*,int); - -typedef struct PathBar { - Widget widget; - Widget textfield; - - Widget focus_widget; - - Widget left; - Widget right; - Dimension lw; - Dimension rw; - - int shift; - - UiPathElm *current_pathelms; - Widget *pathSegments; - size_t numSegments; - size_t segmentAlloc; - - char *path; - int selection; - Boolean input; - - int focus; - - updatedir_callback updateDir; - void *updateDirData; - - ui_pathelm_func getpathelm; - void *getpathelmdata; -} PathBar; - -void PathBarSetPath(PathBar *bar, const char *path); - -void pathbar_resize(Widget w, PathBar *p, XtPointer d) -{ - Dimension width, height; - XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL); - - Dimension *segW = (void*)XtCalloc(p->numSegments, sizeof(Dimension)); - - Dimension maxHeight = 0; - - /* get width/height from all widgets */ - Dimension pathWidth = 0; - for(int i=0;inumSegments;i++) { - Dimension segWidth; - Dimension segHeight; - XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL); - segW[i] = segWidth; - pathWidth += segWidth; - if(segHeight > maxHeight) { - maxHeight = segHeight; - } - } - Dimension tfHeight; - XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL); - if(tfHeight > maxHeight) { - maxHeight = tfHeight; - } - - Boolean arrows = False; - if(pathWidth + 10 > width) { - arrows = True; - pathWidth += p->lw + p->rw; - } - - /* calc max visible widgets */ - int start = 0; - if(arrows) { - Dimension vis = p->lw+p->rw; - for(int i=p->numSegments;i>0;i--) { - Dimension segWidth = segW[i-1]; - if(vis + segWidth + 10 > width) { - start = i; - arrows = True; - break; - } - vis += segWidth; - } - } else { - p->shift = 0; - } - - int leftShift = 0; - if(p->shift < 0) { - if(start + p->shift < 0) { - leftShift = start; - start = 0; - p->shift = -leftShift; - } else { - leftShift = -p->shift; /* negative shift */ - start += p->shift; - } - } - - int x = 0; - if(arrows) { - XtManageChild(p->left); - XtManageChild(p->right); - x = p->lw; - } else { - XtUnmanageChild(p->left); - XtUnmanageChild(p->right); - } - - for(int i=0;inumSegments;i++) { - if(i >= start && i < p->numSegments - leftShift && !p->input) { - XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); - x += segW[i]; - XtManageChild(p->pathSegments[i]); - } else { - XtUnmanageChild(p->pathSegments[i]); - } - } - - if(arrows) { - XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL); - XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); - } - - free(segW); - - Dimension rw, rh; - XtMakeResizeRequest(w, width, maxHeight, &rw, &rh); - - XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh, NULL); -} - -static void pathbarActivateTF(PathBar *p) -{ - XtUnmanageChild(p->left); - XtUnmanageChild(p->right); - XNETextSetSelection(p->textfield, 0, XNETextGetLastPosition(p->textfield), 0); - XtManageChild(p->textfield); - p->input = 1; - - XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT); - - pathbar_resize(p->widget, p, NULL); -} - -void PathBarActivateTextfield(PathBar *p) -{ - p->focus = 1; - pathbarActivateTF(p); -} - -void pathbar_input(Widget w, PathBar *p, XtPointer c) -{ - XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; - XEvent *xevent = cbs->event; - - if (cbs->reason == XmCR_INPUT) { - if (xevent->xany.type == ButtonPress) { - p->focus = 0; - pathbarActivateTF(p); - } - } -} - -void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c) -{ - if(--p->focus < 0) { - p->input = False; - XtUnmanageChild(p->textfield); - } -} - -static cxmutstr concat_path_s(cxstring base, cxstring path) { - if(!path.ptr) { - path = CX_STR(""); - } - - int add_separator = 0; - if(base.length != 0 && base.ptr[base.length-1] == '/') { - if(path.ptr[0] == '/') { - base.length--; - } - } else { - if(path.length == 0 || path.ptr[0] != '/') { - add_separator = 1; - } - } - - cxmutstr url; - if(add_separator) { - url = cx_strcat(3, base, CX_STR("/"), path); - } else { - url = cx_strcat(2, base, path); - } - - return url; -} - -static char* ConcatPath(const char *path1, const char *path2) { - return concat_path_s(cx_str(path1), cx_str(path2)).ptr; -} - -void pathbar_pathinput(Widget w, PathBar *p, XtPointer d) -{ - char *newpath = XNETextGetString(p->textfield); - if(newpath) { - if(newpath[0] == '~') { - char *p = newpath+1; - char *home = getenv("HOME"); - char *cp = ConcatPath(home, p); - XtFree(newpath); - newpath = cp; - } else if(newpath[0] != '/') { - char curdir[2048]; - curdir[0] = 0; - getcwd(curdir, 2048); - char *cp = ConcatPath(curdir, newpath); - XtFree(newpath); - newpath = cp; - } - - /* update path */ - PathBarSetPath(p, newpath); - if(p->updateDir) { - p->updateDir(p->updateDirData, newpath, -1); - } - XtFree(newpath); - - /* hide textfield and show path as buttons */ - XtUnmanageChild(p->textfield); - pathbar_resize(p->widget, p, NULL); - - if(p->focus_widget) { - XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT); - } - } -} - -void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) -{ - p->shift--; - pathbar_resize(p->widget, p, NULL); -} - -void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) -{ - if(p->shift < 0) { - p->shift++; - } - pathbar_resize(p->widget, p, NULL); -} - -static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { - PathBar *pb = data; - if(event->type == KeyReleaseMask) { - if(event->xkey.keycode == 9) { - XtUnmanageChild(pb->textfield); - pathbar_resize(pb->widget, pb, NULL); - *dispatch = False; - } else if(event->xkey.keycode == 36) { - pathbar_pathinput(pb->textfield, pb, NULL); - *dispatch = False; - } - } -} - -PathBar* CreatePathBar(Widget parent, ArgList args, int n) -{ - PathBar *bar = (PathBar*)XtMalloc(sizeof(PathBar)); - bar->path = NULL; - bar->updateDir = NULL; - bar->updateDirData = NULL; - - bar->focus_widget = NULL; - - bar->getpathelm = NULL; - bar->getpathelmdata = NULL; - bar->current_pathelms = NULL; - - bar->shift = 0; - - XtSetArg(args[n], XmNmarginWidth, 0); n++; - XtSetArg(args[n], XmNmarginHeight, 0); n++; - bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n); - XtAddCallback( - bar->widget, - XmNresizeCallback, - (XtCallbackProc)pathbar_resize, - bar); - XtAddCallback( - bar->widget, - XmNinputCallback, - (XtCallbackProc)pathbar_input, - bar); - - Arg a[4]; - XtSetArg(a[0], XmNshadowThickness, 0); - XtSetArg(a[1], XmNx, 0); - XtSetArg(a[2], XmNy, 0); - bar->textfield = XNECreateText(bar->widget, "pbtext", a, 3); - bar->input = 0; - XtAddCallback( - bar->textfield, - XmNlosingFocusCallback, - (XtCallbackProc)pathbar_losingfocus, - bar); - XtAddCallback(bar->textfield, XmNactivateCallback, - (XtCallbackProc)pathbar_pathinput, bar); - XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar); - - XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT); - bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT); - bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); - XtAddCallback( - bar->left, - XmNactivateCallback, - (XtCallbackProc)pathbar_shift_left, - bar); - XtAddCallback( - bar->right, - XmNactivateCallback, - (XtCallbackProc)pathbar_shift_right, - bar); - - Pixel bg; - XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL); - XtVaSetValues(bar->widget, XmNbackground, bg, NULL); - - XtManageChild(bar->left); - XtManageChild(bar->right); - - XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL); - XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL); - - bar->segmentAlloc = 16; - bar->numSegments = 0; - bar->pathSegments = (Widget*)XtCalloc(16, sizeof(Widget)); - - bar->selection = 0; - - return bar; -} - -void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c) -{ - XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False); - - int i; - for(i=0;inumSegments;i++) { - if(bar->pathSegments[i] == w) { - bar->selection = i; - XmToggleButtonSetState(w, True, False); - break; - } - } - - UiPathElm elm = bar->current_pathelms[i]; - cxmutstr path = cx_strdup(cx_strn(elm.path, elm.path_len)); - if(bar->updateDir) { - XNETextSetString(bar->textfield, path.ptr); - bar->updateDir(bar->updateDirData, path.ptr, i); - } - free(path.ptr); -} - -static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { - for(int i=0;ipath) { - free(bar->path); - } - bar->path = strdup(path); - - for(int i=0;inumSegments;i++) { - XtDestroyWidget(bar->pathSegments[i]); - } - XtUnmanageChild(bar->textfield); - XtManageChild(bar->left); - XtManageChild(bar->right); - bar->input = False; - - Arg args[4]; - XmString str; - - bar->numSegments = 0; - - ui_pathelm_destroy(bar->current_pathelms, bar->numSegments); - size_t nelm = 0; - UiPathElm* path_elm = bar->getpathelm(bar->path, strlen(bar->path), &nelm, bar->getpathelmdata); - if (!path_elm) { - return; - } - bar->current_pathelms = path_elm; - bar->numSegments = nelm; - bar->pathSegments = realloc(bar->pathSegments, nelm * sizeof(Widget*)); - - for(int i=0;iwidget, "pbbutton", args, 3); - XtAddCallback( - button, - XmNvalueChangedCallback, - (XtCallbackProc)PathBarChangeDir, - bar); - XmStringFree(str); - - bar->pathSegments[i] = button; - } - - bar->selection = bar->numSegments-1; - XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False); - - XNETextSetString(bar->textfield, (char*)path); - XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield)); - - pathbar_resize(bar->widget, bar, NULL); -} - -void PathBarDestroy(PathBar *pathbar) { - if(pathbar->path) { - XtFree(pathbar->path); - } - XtFree((void*)pathbar->pathSegments); - XtFree((void*)pathbar); -} - - /* ---------------------------- Path Text Field ---------------------------- */ static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) { @@ -915,47 +520,6 @@ static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) { XtFree((void*)pathbar); } -// TODO: move to common -static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { - cxstring *pathelms; - size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); - - if (nelm == 0) { - *ret_nelm = 0; - return NULL; - } - - UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); - size_t n = nelm; - int j = 0; - for (int i = 0; i < nelm; i++) { - cxstring c = pathelms[i]; - if (c.length == 0) { - if (i == 0) { - c.length = 1; - } - else { - n--; - continue; - } - } - - cxmutstr m = cx_strdup(c); - elms[j].name = m.ptr; - elms[j].name_len = m.length; - - size_t elm_path_len = c.ptr + c.length - full_path; - cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); - elms[j].path = elm_path.ptr; - elms[j].path_len = elm_path.length; - - j++; - } - *ret_nelm = n; - - return elms; -} - static void pathbar_activate(void *data, char *path, int index) { UiEventData *event = data; UiEvent evt; @@ -981,7 +545,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { PathBar *pathbar = CreatePathBar(parent, xargs, n); if(!args->getpathelm) { - pathbar->getpathelm= default_pathelm_func; + pathbar->getpathelm= ui_default_pathelm_func; } else { pathbar->getpathelm = args->getpathelm; pathbar->getpathelmdata = args->getpathelmdata; @@ -1018,7 +582,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { XtAddCallback( pathbar->widget, XmNdestroyCallback, - (XtCallbackProc)ui_destroy_eventdata, + (XtCallbackProc)ui_destroy_data, eventdata); } diff --git a/ui/motif/text.h b/ui/motif/text.h index a7e4f85..9f2932e 100644 --- a/ui/motif/text.h +++ b/ui/motif/text.h @@ -86,6 +86,9 @@ void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data); int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen); void ui_free_textbuf_op(UiTextBufOp *op); +void ui_textfield_activate(Widget widget, XtPointer ud, XtPointer cb); +void ui_textfield_value_changed(Widget widget, XtPointer ud, XtPointer cb); + char* ui_textfield_get(UiString *str); void ui_textfield_set(UiString *str, const char *value); diff --git a/ui/motif/toolkit.c b/ui/motif/toolkit.c index c2a1f2a..17b4726 100644 --- a/ui/motif/toolkit.c +++ b/ui/motif/toolkit.c @@ -84,11 +84,17 @@ static String fallback[] = { NULL }; +static String *fallback_resources = fallback; + void input_proc(XtPointer data, int *source, XtInputId *iid) { void *ptr; read(event_pipe[0], &ptr, sizeof(void*)); } +void ui_motif_set_fallback_resources(String *fallbackres) { + fallback_resources = fallbackres; +} + void ui_init(const char *appname, int argc, char **argv) { application_name = appname; uic_init_global_context(); @@ -96,7 +102,7 @@ void ui_init(const char *appname, int argc, char **argv) { XtToolkitInitialize(); XtSetLanguageProc(NULL, NULL, NULL); app = XtCreateApplicationContext(); - XtAppSetFallbackResources(app, fallback); + XtAppSetFallbackResources(app, fallback_resources); display = XtOpenDisplay(app, NULL, appname, appname, NULL, 0, &argc, argv); @@ -120,6 +126,10 @@ const char* ui_appname() { return application_name; } +XtAppContext ui_motif_get_app(void) { + return app; +} + Display* ui_motif_get_display() { return display; } @@ -157,7 +167,7 @@ void ui_main() { } } -void ui_exit_mainloop() { +void ui_app_quit() { XtAppSetExitFlag(app); } @@ -216,6 +226,15 @@ static Boolean ui_job_finished(void *data) { return TRUE; } +static Boolean ui_mainthread_job(void *data) { + UiJob *job = data; + if(job->job_func) { + job->job_func(job->job_data); + } + free(job); + return TRUE; +} + static void* ui_jobthread(void *data) { UiJob *job = data; int result = job->job_func(job->job_data); @@ -227,6 +246,15 @@ static void* ui_jobthread(void *data) { return NULL; } +void ui_call_mainthread(ui_threadfunc tf, void* td) { + UiJob *job = malloc(sizeof(UiJob)); + memset(job, 0, sizeof(UiJob)); + job->job_func = tf; + job->job_data = td; + write(event_pipe[1], &job, sizeof(void*)); // hack + XtAppAddWorkProc(app, ui_mainthread_job, job); +} + void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { UiJob *job = malloc(sizeof(UiJob)); job->obj = obj; @@ -321,7 +349,7 @@ void ui_window_dark_theme(Display *dp, Window window) { 4); } -void ui_destroy_eventdata(Widget w, XtPointer data, XtPointer d) { +void ui_destroy_data(Widget w, XtPointer data, XtPointer d) { free(data); } diff --git a/ui/motif/toolkit.h b/ui/motif/toolkit.h index a2397e8..069e31c 100644 --- a/ui/motif/toolkit.h +++ b/ui/motif/toolkit.h @@ -82,9 +82,9 @@ typedef struct UiJob { typedef enum UiOrientation UiOrientation; enum UiOrientation { UI_HORIZONTAL = 0, UI_VERTICAL }; -void ui_exit_mainloop(); - +XtAppContext ui_motif_get_app(void); Display* ui_motif_get_display(void); +void ui_motif_set_fallback_resources(String *fallback); void ui_set_active_window(Widget w); Widget ui_get_active_window(); @@ -92,7 +92,7 @@ Widget ui_get_active_window(); void ui_secondary_event_loop(int *loop); void ui_window_dark_theme(Display *dp, Window window); -void ui_destroy_eventdata(Widget w, XtPointer data, XtPointer d); +void ui_destroy_data(Widget w, XtPointer data, XtPointer d); void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) ; void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups); diff --git a/ui/motif/window.c b/ui/motif/window.c index 7c3f638..9489f07 100644 --- a/ui/motif/window.c +++ b/ui/motif/window.c @@ -28,15 +28,22 @@ #include #include +#include +#include +#include + +#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 @@ -50,7 +57,7 @@ void ui_window_widget_destroy(UiObject *obj) { uic_object_destroy(obj); nwindows--; if(nwindows == 0) { - ui_exit_mainloop(); + ui_app_quit(); } } @@ -74,13 +81,22 @@ static UiObject* create_window(const char *title, void *window_data, Boolean sim 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 +126,7 @@ static UiObject* create_window(const char *title, void *window_data, Boolean sim // menu if(!simple) { - ui_create_menubar(obj, window); + appwindow->menubar = ui_create_menubar(obj, window); } // content frame @@ -142,3 +158,154 @@ UiObject* ui_window(const char *title, void *window_data) { UiObject* ui_simple_window(const char *title, void *window_data) { return create_window(title, window_data, 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); + } +} + +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); + } +} diff --git a/ui/motif/window.h b/ui/motif/window.h index 33bb66d..8c2c5ee 100644 --- a/ui/motif/window.h +++ b/ui/motif/window.h @@ -29,11 +29,18 @@ #ifndef WINDOW_H #define WINDOW_H +#include "../ui/window.h" +#include "../ui/widget.h" + #ifdef __cplusplus extern "C" { #endif + +#define UI_MOTIF_APP_WINDOW 0xabcd - +typedef struct UiMotifAppWindow { + Widget menubar; +} UiMotifAppWindow; #ifdef __cplusplus diff --git a/ui/ui/container.h b/ui/ui/container.h index 3355850..0bc42b7 100644 --- a/ui/ui/container.h +++ b/ui/ui/container.h @@ -98,6 +98,8 @@ typedef struct UiFrameArgs { int margin_bottom; int colspan; int rowspan; + int width; + int height; const char *name; const char *style_class; diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 6c24330..7b8309a 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -256,6 +256,9 @@ typedef void(*ui_freefunc)(void*); typedef void(*ui_enablefunc)(void*, int); +typedef void (*ui_destructor_func)(void *memory); + + struct UiObject { /* * native widget @@ -405,6 +408,7 @@ struct UiGeneric { void* (*get)(UiGeneric*); const char* (*get_type)(UiGeneric*); int (*set)(UiGeneric*, void *, const char *type); + void (*destroy)(UiGeneric*); void *obj; void *value; @@ -413,6 +417,7 @@ struct UiGeneric { }; typedef void (*ui_list_init_func)(UiContext *ctx, UiList *list, void *userdata); +typedef void (*ui_list_destroy_func)(UiContext *ctx, UiList *list, void *userdata); /* * abstract list @@ -537,6 +542,7 @@ UIEXPORT int ui_app_save_settings(void); UIEXPORT void ui_app_exit_on_shutdown(UiBool exitapp); UIEXPORT void ui_main(void); +UIEXPORT void ui_app_quit(void); UIEXPORT void ui_show(UiObject *obj); UIEXPORT void ui_close(UiObject *obj); @@ -557,8 +563,8 @@ UIEXPORT void ui_attach_document(UiContext *ctx, void *document); UIEXPORT void ui_detach_document(UiContext *ctx, void *document); UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...); -UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups); -UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates); +UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups); +UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates); UIEXPORT void ui_set_group(UiContext *ctx, int group); UIEXPORT void ui_unset_group(UiContext *ctx, int group); @@ -572,6 +578,8 @@ UIEXPORT void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize); UIEXPORT void ui_free(UiContext *ctx, void *ptr); UIEXPORT void* ui_realloc(UiContext *ctx, void *ptr, size_t size); UIEXPORT char* ui_strdup(UiContext *ctx, const char *str); +UIEXPORT void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr); +UIEXPORT void ui_set_destructor(void *mem, ui_destructor_func destr); // types @@ -629,7 +637,7 @@ UIEXPORT void ui_notify_evt(UiObserver *observer, UiEvent *event); UIEXPORT UiList* ui_list_new(UiContext *ctx, const char *name); UIEXPORT UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func init, void *userdata); -UIEXPORT void ui_list_free(UiList *list); +UIEXPORT void ui_list_free(UiContext *ctx, UiList *list); UIEXPORT void* ui_list_first(UiList *list); UIEXPORT void* ui_list_next(UiList *list); UIEXPORT void* ui_list_get(UiList *list, int i); @@ -674,9 +682,12 @@ UIEXPORT void ui_condvar_destroy(UiCondVar *var); UIEXPORT void ui_setop_enable(int set); UIEXPORT int ui_get_setop(void); +UIEXPORT void ui_onchange_events_enable(UiBool enable); +UIEXPORT UiBool ui_onchange_events_is_enabled(void); UIEXPORT void ui_global_list_initializer(ui_list_init_func func, void *userdata); +UIEXPORT void ui_global_list_destructor(ui_list_destroy_func func, void *userdata); UIEXPORT void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list)); UIEXPORT void ui_list_class_set_next(UiList *list, void*(*next)(UiList *list)); UIEXPORT void ui_list_class_set_get(UiList *list, void*(*get)(UiList *list, int i)); @@ -684,6 +695,9 @@ UIEXPORT void ui_list_class_set_count(UiList *list, int(*count)(UiList *list)); UIEXPORT void ui_list_class_set_data(UiList *list, void *data); UIEXPORT void ui_list_class_set_iter(UiList *list, void *iter); +UIEXPORT void ui_object_set(UiObject *obj, const char *key, void *data); +UIEXPORT void* ui_object_get(UiObject *obj, const char *key); + #ifdef __cplusplus } #endif diff --git a/ui/ui/tree.h b/ui/ui/tree.h index 33a903c..a71092b 100644 --- a/ui/ui/tree.h +++ b/ui/ui/tree.h @@ -132,9 +132,9 @@ struct UiListArgs { const char *name; const char *style_class; - UiList* list; + UiList *list; const char* varname; - UiModel* model; + UiModel *model; char **static_elements; size_t static_nelm; ui_getvaluefunc getvalue; @@ -143,15 +143,15 @@ struct UiListArgs { ui_getstylefunc getstyle; void *getstyledata; ui_callback onactivate; - void* onactivatedata; + void *onactivatedata; ui_callback onselection; - void* onselectiondata; + void *onselectiondata; ui_callback ondragstart; - void* ondragstartdata; + void *ondragstartdata; ui_callback ondragcomplete; - void* ondragcompletedata; + void *ondragcompletedata; ui_callback ondrop; - void* ondropdata; + void *ondropdata; UiBool multiselection; UiMenuBuilder *contextmenu; ui_list_savefunc onsave; @@ -300,10 +300,10 @@ UIEXPORT void ui_model_free(UiContext *ctx, UiModel *mi); #define ui_breadcrumbbar(obj, ...) ui_breadcrumbbar_create(obj, &(UiListArgs) { __VA_ARGS__ } ) #define ui_sourcelist(obj, ...) ui_sourcelist_create(obj, &(UiSourceListArgs) { __VA_ARGS__ } ) -UIEXPORT UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject *obj, UiListArgs *args); UIEXPORT void ui_listview_select(UIWIDGET listview, int index); UIEXPORT void ui_combobox_select(UIWIDGET dropdown, int index); diff --git a/ui/ui/webview.h b/ui/ui/webview.h index 90d5268..9636165 100644 --- a/ui/ui/webview.h +++ b/ui/ui/webview.h @@ -36,8 +36,16 @@ extern "C" { #endif +/* + * WebView type string used by UiGeneric + */ #define UI_WEBVIEW_OBJECT_TYPE "webview" - + +/* + * UiWebViewData* is returned by a webviews UiGeneric->get function + */ +typedef struct UiWebViewData UiWebViewData; + typedef struct UiWebviewArgs { UiBool fill; UiBool hexpand; @@ -63,11 +71,11 @@ typedef struct UiWebviewArgs { #define ui_webview(obj, ...) ui_webview_create(obj, &(UiWebviewArgs){ __VA_ARGS__ } ) -UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args); +UIEXPORT UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args); -void ui_webview_load_url(UiGeneric *g, const char *url); +UIEXPORT void ui_webview_load_url(UiGeneric *g, const char *url); -void ui_webview_load_content( +UIEXPORT void ui_webview_load_content( UiGeneric *g, const char *uri, const char *content, @@ -75,16 +83,20 @@ void ui_webview_load_content( const char *mimetype, const char *encoding); +/* + * Frees a UiWebViewData object returned by a webviews UiGeneric->get function + */ +UIEXPORT void ui_webview_data_free(UiWebViewData *data); -void ui_webview_reload(UiGeneric *g); -UiBool ui_webview_can_go_back(UiGeneric *g); -UiBool ui_webview_can_go_forward(UiGeneric *g); -void ui_webview_go_back(UiGeneric *g); -void ui_webview_go_forward(UiGeneric *g); -const char * ui_webview_get_uri(UiGeneric *g); -void ui_webview_enable_javascript(UiGeneric *g, UiBool enable); -void ui_webview_set_zoom(UiGeneric *g, double zoom); -double ui_webview_get_zoom(UiGeneric *g); +UIEXPORT void ui_webview_reload(UiGeneric *g); +UIEXPORT UiBool ui_webview_can_go_back(UiGeneric *g); +UIEXPORT UiBool ui_webview_can_go_forward(UiGeneric *g); +UIEXPORT void ui_webview_go_back(UiGeneric *g); +UIEXPORT void ui_webview_go_forward(UiGeneric *g); +UIEXPORT const char * ui_webview_get_uri(UiGeneric *g); +UIEXPORT void ui_webview_enable_javascript(UiGeneric *g, UiBool enable); +UIEXPORT void ui_webview_set_zoom(UiGeneric *g, double zoom); +UIEXPORT double ui_webview_get_zoom(UiGeneric *g); #ifdef __cplusplus diff --git a/ui/ui/win32.h b/ui/ui/win32.h index 54d0543..1137265 100644 --- a/ui/ui/win32.h +++ b/ui/ui/win32.h @@ -49,7 +49,7 @@ struct W32Size { }; struct W32WidgetClass { - void (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + int (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void (*show)(W32Widget *widget, BOOLEAN show); void (*enable)(W32Widget *widget, BOOLEAN enable); W32Size (*get_preferred_size)(W32Widget *widget); diff --git a/ui/ui/window.h b/ui/ui/window.h index 83e85a3..3619b3a 100644 --- a/ui/ui/window.h +++ b/ui/ui/window.h @@ -83,6 +83,9 @@ UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs UIEXPORT void ui_window_size(UiObject *obj, int width, int height); UIEXPORT void ui_window_default_size(int width, int height); +UIEXPORT void ui_window_menubar_set_visible(UiObject *obj, UiBool visible); + +UIEXPORT void ui_window_fullscreen(UiObject *obj, UiBool fullscreen); UIEXPORT void ui_splitview_window_set_pos(UiObject *obj, int pos); UIEXPORT int ui_splitview_window_get_pos(UiObject *obj); diff --git a/ui/win32/button.c b/ui/win32/button.c index ed6520a..fca53bd 100644 --- a/ui/win32/button.c +++ b/ui/win32/button.c @@ -32,6 +32,8 @@ #include #include +#include + #include static W32WidgetClass button_widget_class = { @@ -42,6 +44,22 @@ static W32WidgetClass button_widget_class = { .destroy = w32_widget_default_destroy }; +static W32WidgetClass togglebutton_widget_class = { + .eventproc = ui_togglebutton_eventproc, + .enable = w32_widget_default_enable, + .show = w32_widget_default_show, + .get_preferred_size = ui_button_get_preferred_size, + .destroy = w32_widget_default_destroy +}; + +static W32WidgetClass radiobutton_widget_class = { + .eventproc = ui_radiobutton_eventproc, + .enable = w32_widget_default_enable, + .show = w32_widget_default_show, + .get_preferred_size = ui_button_get_preferred_size, + .destroy = w32_widget_default_destroy +}; + UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) { HINSTANCE hInstance = GetModuleHandle(NULL); UiContainerPrivate *container = ui_obj_container(obj); @@ -51,10 +69,10 @@ UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) { HWND hwnd = CreateWindow( "BUTTON", args->label, - WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, + WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 0, 100, 30, parent, - (HMENU)0, + (HMENU)1, hInstance, NULL); ui_win32_set_ui_font(hwnd); @@ -82,9 +100,13 @@ W32Size ui_button_get_preferred_size(W32Widget *widget) { return size; } -void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { +int ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { UiWidget *w = (UiWidget*)widget; + if (uMsg != WM_COMMAND) { + return 0; + } + UiEvent e; e.obj = w->obj; e.document = e.obj->ctx->document; @@ -97,4 +119,200 @@ void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, if (w->callback) { w->callback(&e, w->callbackdata); } + + return 0; +} + +static UIWIDGET create_togglebutton(UiObject *obj, UiToggleArgs *args, unsigned long type) { + HINSTANCE hInstance = GetModuleHandle(NULL); + UiContainerPrivate *container = ui_obj_container(obj); + HWND parent = ui_container_get_parent(container); + UiLayout layout = UI_ARGS2LAYOUT(args); + + HWND hwnd = CreateWindow( + "BUTTON", + args->label, + WS_VISIBLE | WS_CHILD | type, + 0, 0, 100, 30, + parent, + (HMENU)1, + hInstance, + NULL); + ui_win32_set_ui_font(hwnd); + + W32Widget *widget = w32_widget_create(&togglebutton_widget_class, hwnd, sizeof(UiWidget)); + ui_container_add(container, widget, &layout); + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); + if (var) { + UiInteger *i = var->value; + i->obj = widget; + i->get = ui_togglebutton_get; + i->set = ui_togglebutton_set; + if (i->value != 0) { + SendMessage(hwnd, BM_SETCHECK, BST_CHECKED, 0); + } + } + + UiWidget *btn = (UiWidget*)widget; + btn->obj = obj; + btn->var = var; + btn->callback = args->onchange; + btn->callbackdata = args->onchangedata; + + return widget; +} + +UIWIDGET ui_togglebutton_create(UiObject *obj, UiToggleArgs *args) { + return create_togglebutton(obj, args, BS_AUTOCHECKBOX | BS_PUSHLIKE); +} + +UIWIDGET ui_checkbox_create(UiObject *obj, UiToggleArgs *args) { + return create_togglebutton(obj, args, BS_AUTOCHECKBOX); +} + +UIWIDGET ui_switch_create(UiObject *obj, UiToggleArgs *args) { + return create_togglebutton(obj, args, BS_AUTOCHECKBOX); +} + +int64_t ui_togglebutton_get(UiInteger *i) { + UiWidget *btn = (UiWidget*)i->obj; + LRESULT state = SendMessage(btn->widget.hwnd, BM_GETCHECK, 0, 0); + i->value = state; + return state; +} + +void ui_togglebutton_set(UiInteger *i, int64_t v) { + UiWidget *btn = (UiWidget*)i->obj; + WPARAM state = v ? BST_CHECKED : BST_UNCHECKED; + SendMessage(btn->widget.hwnd, BM_SETCHECK, state, 0); + i->value = v; +} + +int ui_togglebutton_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg != WM_COMMAND) { + return 0; + } + UiWidget *w = (UiWidget*)widget; + + UiEvent e; + e.obj = w->obj; + e.document = e.obj->ctx->document; + e.window = e.obj->window; + e.eventdata = NULL; + e.eventdatatype = 0; + e.intval = SendMessage(w->widget.hwnd, BM_GETCHECK, 0, 0); + e.set = ui_get_setop(); + + if (w->callback) { + w->callback(&e, w->callbackdata); + } + + return 0; +} + + +UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) { + HINSTANCE hInstance = GetModuleHandle(NULL); + UiContainerPrivate *container = ui_obj_container(obj); + HWND parent = ui_container_get_parent(container); + UiLayout layout = UI_ARGS2LAYOUT(args); + + HWND hwnd = CreateWindow( + "BUTTON", + args->label, + WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON | WS_GROUP , + 0, 0, 100, 30, + parent, + (HMENU)1, + hInstance, + NULL); + ui_win32_set_ui_font(hwnd); + + W32Widget *widget = w32_widget_create(&radiobutton_widget_class, hwnd, sizeof(UiWidget)); + ui_container_add(container, widget, &layout); + UiWidget *btn = (UiWidget*)widget; + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); + if (var) { + UiInteger *i = var->value; + // Use a CxList as binding object (i->obj) and add all radiobuttons to it + // The first radiobutton, which binds to this var, creates the CxList + CxList *group = NULL; + if (i->obj) { + group = i->obj; + } else { + group = cxArrayListCreate(obj->ctx->allocator, NULL, CX_STORE_POINTERS, 8); + i->obj = group; + } + + cxListAdd(group, btn); + if (i->value == cxListSize(group)) { + // TODO: select + } + } + + btn->obj = obj; + btn->var = var; + btn->callback = args->onchange; + btn->callbackdata = args->onchangedata; + + return widget; +} + +int ui_radiobutton_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg != WM_COMMAND) { + return 0; + } + UiWidget *w = (UiWidget*)widget; + + int checked = SendMessage(w->widget.hwnd, BM_GETCHECK, 0, 0); + if (!checked) { + return 0; // ignore uncheck events + } + + int b = 0; + if (w->var) { + UiInteger *i = w->var->value; + CxList *group = i->obj; + // Find selected index and uncheck all radiobuttons in this group + // that are not this event widget + CxIterator iter = cxListIterator(group); + cx_foreach(UiWidget *, radiobutton, iter) { + if (radiobutton == w) { + i->value = iter.index+1; + b = i->value; + } else { + SendMessage(radiobutton->widget.hwnd, BM_SETCHECK, BST_UNCHECKED, 0); + } + } + } + + UiEvent e; + e.obj = w->obj; + e.document = e.obj->ctx->document; + e.window = e.obj->window; + e.eventdata = NULL; + e.eventdatatype = 0; + e.intval = b; + e.set = ui_get_setop(); + + if (w->callback) { + w->callback(&e, w->callbackdata); + } + + return 0; +} + +int64_t ui_radiobutton_get(UiInteger *i) { + return i->value; +} + +void ui_radiobutton_set(UiInteger *i, int64_t v) { + CxList *group = i->obj; + CxIterator iter = cxListIterator(group); + cx_foreach(UiWidget *, radiobutton, iter) { + SendMessage(radiobutton->widget.hwnd, BM_SETCHECK, iter.index+1 == v ? BST_CHECKED : BST_UNCHECKED, 0); + } + i->value = v; } diff --git a/ui/win32/button.h b/ui/win32/button.h index 8770e89..d35a067 100644 --- a/ui/win32/button.h +++ b/ui/win32/button.h @@ -34,6 +34,14 @@ W32Size ui_button_get_preferred_size(W32Widget *widget); -void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +int ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +int64_t ui_togglebutton_get(UiInteger *i); +void ui_togglebutton_set(UiInteger *i, int64_t v); +int ui_togglebutton_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +int64_t ui_radiobutton_get(UiInteger *i); +void ui_radiobutton_set(UiInteger *i, int64_t v); +int ui_radiobutton_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); #endif //BUTTON_H diff --git a/ui/win32/container.c b/ui/win32/container.c index 9ae6f26..889ad5f 100644 --- a/ui/win32/container.c +++ b/ui/win32/container.c @@ -35,7 +35,7 @@ static W32WidgetClass grid_layout_widget_class = { - .eventproc = NULL, + .eventproc = ui_grid_widget_event, .enable = NULL, .show = w32_widget_default_show, .get_preferred_size = ui_grid_layout_get_preferred_size, @@ -62,6 +62,18 @@ void ui_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layo ctn->container.newline = FALSE; } +int ui_grid_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_ERASEBKGND) { + HDC hdc = (HDC)wParam; + UiGridWidget *grid = (UiGridWidget*)widget; + RECT rc; + GetClientRect(hwnd, &rc); + FillRect(hdc, &rc, grid->brush); + return 1; + } + return 0; +} + W32Size ui_grid_layout_get_preferred_size(W32Widget *widget) { UiGridLayout *grid = widget->layoutmanager; W32Size size; @@ -134,25 +146,27 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { TEXT("STATIC"), NULL, WS_CHILD | WS_VISIBLE, - 0, 0, 100, 100, + 0, 0, 300, 300, parent, NULL, hInstance, NULL); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)ui_default_eventproc); - W32Widget *widget = w32_widget_new(&grid_layout_widget_class, hwnd); - ui_container_add(container, widget, &layout); + UiGridWidget *widget = w32_widget_create(&grid_layout_widget_class, hwnd, sizeof(UiGridWidget)); + ui_container_add(container, (W32Widget*)widget, &layout); UiContainerX *gridContainer = ui_grid_container_create(obj, hwnd, args->columnspacing, args->rowspacing, INSETS_ZERO); uic_object_push_container(obj, gridContainer); UiGridLayoutContainer *grid = (UiGridLayoutContainer*)gridContainer; - widget->layout = (W32LayoutFunc)ui_grid_layout; - widget->layoutmanager = grid->layout; + widget->widget.layout = (W32LayoutFunc)ui_grid_layout; + widget->widget.layoutmanager = grid->layout; + widget->brush = GetSysColorBrush(COLOR_BTNFACE); grid->layout->preferred_width = 200; grid->layout->preferred_height = 200; - return widget; + return (W32Widget*)widget; } UiContainerX* ui_grid_container_create(UiObject *obj, HWND hwnd, short columnspacing, short rowspacing, GridEdgeInsets padding) { diff --git a/ui/win32/container.h b/ui/win32/container.h index 8809ab2..2d0ca9d 100644 --- a/ui/win32/container.h +++ b/ui/win32/container.h @@ -40,6 +40,7 @@ extern "C" { typedef struct UiContainerPrivate UiContainerPrivate; typedef struct UiGridLayoutContainer UiGridLayoutContainer; typedef struct UiBoxContainer UiBoxContainer; +typedef struct UiGridWidget UiGridWidget; enum UiBoxOrientation { UI_BOX_VERTICAL = 0, @@ -88,10 +89,16 @@ struct UiGridLayoutContainer { UiBool def_vfill; }; +struct UiGridWidget { + W32Widget widget; + HBRUSH brush; +}; + UiContainerPrivate* ui_obj_container(UiObject *obj); HWND ui_container_get_parent(UiContainerPrivate *ctn); void ui_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout); +int ui_grid_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); W32Size ui_grid_layout_get_preferred_size(W32Widget *widget); UiContainerX* ui_box_container_create(UiObject *obj, HWND hwnd, UiBoxOrientation orientation, short spacing, GridEdgeInsets padding); diff --git a/ui/win32/grid.c b/ui/win32/grid.c index 023a16b..aad2d5d 100644 --- a/ui/win32/grid.c +++ b/ui/win32/grid.c @@ -293,7 +293,9 @@ void ui_grid_layout(UiGridLayout *grid, int width, int height) { child_x = col->pos + elm->layout.margin.left; child_y = row->pos + elm->layout.margin.top; - SetWindowPos(elm->widget->hwnd, NULL, child_x, child_y, child_width, child_height, SWP_NOZORDER); + SetWindowPos(elm->widget->hwnd, NULL, child_x, child_y, child_width, child_height, SWP_NOZORDER | SWP_NOACTIVATE); + InvalidateRect(elm->widget->hwnd, NULL, TRUE); + //UpdateWindow(elm->widget->hwnd); if (elm->widget->layout) { elm->widget->layout(elm->widget->layoutmanager, child_width, child_height); } diff --git a/ui/win32/objs.mk b/ui/win32/objs.mk index 1241582..85a6fb3 100644 --- a/ui/win32/objs.mk +++ b/ui/win32/objs.mk @@ -37,6 +37,8 @@ WIN32OBJ += image.obj WIN32OBJ += container.obj WIN32OBJ += button.obj WIN32OBJ += grid.obj +WIN32OBJ += text.obj +WIN32OBJ += list.obj TOOLKITOBJS += $(WIN32OBJ:%=$(WIN32_OBJPRE)%) TOOLKITSOURCE += $(WIN32OBJ:%.obj=win32/%.c) diff --git a/ui/win32/toolkit.c b/ui/win32/toolkit.c index 1f13f95..f73cc5b 100644 --- a/ui/win32/toolkit.c +++ b/ui/win32/toolkit.c @@ -62,9 +62,10 @@ void ui_init(const char *appname, int argc, char **argv) { uic_toolbar_init(); uic_load_app_properties(); - ui_window_init(); - - INITCOMMONCONTROLSEX icex = { sizeof(icex), ICC_WIN95_CLASSES }; + INITCOMMONCONTROLSEX icex = { + sizeof(icex), + ICC_WIN95_CLASSES | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES | ICC_TAB_CLASSES | ICC_STANDARD_CLASSES | ICC_USEREX_CLASSES + }; InitCommonControlsEx(&icex); SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); @@ -72,6 +73,8 @@ void ui_init(const char *appname, int argc, char **argv) { NONCLIENTMETRICS ncm = { sizeof(ncm) }; SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE); ui_font = CreateFontIndirect(&ncm.lfMessageFont); + + ui_window_init(); } HFONT ui_win32_get_font(void) { @@ -123,4 +126,47 @@ void ui_main() { void ui_show(UiObject *obj) { ui_set_visible(obj->widget, TRUE); +} + +LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + W32Widget *widget = (W32Widget*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (widget && widget->wclass->eventproc) { + if (widget->wclass->eventproc(widget, hwnd, uMsg, wParam, lParam)) { + return 1; + } + } + + switch(uMsg) { + case WM_DESTROY: { + PostQuitMessage(0); + break; + } + case WM_COMMAND: { + HWND hwndCtrl = (HWND)lParam; + W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA); + if (cmdWidget && cmdWidget->wclass->eventproc) { + cmdWidget->wclass->eventproc(cmdWidget, hwnd, uMsg, wParam, lParam); + } + break; + } + case WM_NOTIFY: { + LPNMHDR hdr = (LPNMHDR)lParam; + HWND hwndCtrl = hdr->hwndFrom; + W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA); + if (cmdWidget && cmdWidget->wclass->eventproc) { + cmdWidget->wclass->eventproc(cmdWidget, hwnd, uMsg, wParam, lParam); + } + break; + } + case WM_SIZE: { + int width = LOWORD(lParam); + int height = HIWORD(lParam); + if (widget->layout) { + widget->layout(widget->layoutmanager, width, height); + } + break; + } + default: break;//return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + return DefWindowProc(hwnd, uMsg, wParam, lParam);; } \ No newline at end of file diff --git a/ui/win32/toolkit.h b/ui/win32/toolkit.h index f3be04c..53e2b3e 100644 --- a/ui/win32/toolkit.h +++ b/ui/win32/toolkit.h @@ -53,6 +53,8 @@ typedef struct UiWidget { int64_t intvalue; } UiWidget; +LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + HFONT ui_win32_get_font(void); void ui_win32_set_ui_font(HWND control); diff --git a/ui/win32/window.c b/ui/win32/window.c index 98d587f..6fbe7af 100644 --- a/ui/win32/window.c +++ b/ui/win32/window.c @@ -52,41 +52,12 @@ static HINSTANCE hInstance; static const char *mainWindowClass = "UiMainWindow"; -LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - W32Widget *widget = (W32Widget*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (widget && widget->wclass->eventproc) { - widget->wclass->eventproc(widget, hwnd, uMsg, wParam, lParam); - } - switch(uMsg) { - case WM_DESTROY: { - PostQuitMessage(0); - break; - } - case WM_COMMAND: { - HWND hwndCtrl = (HWND)lParam; - W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA); - if (cmdWidget && cmdWidget->wclass->eventproc) { - cmdWidget->wclass->eventproc(cmdWidget, hwnd, uMsg, wParam, lParam); - } - } - case WM_SIZE: { - int width = LOWORD(lParam); - int height = HIWORD(lParam); - if (widget->layout) { - widget->layout(widget->layoutmanager, width, height); - } - break; - } - default: return DefWindowProc(hwnd, uMsg, wParam, lParam); - } - return 0; -} void ui_window_init(void) { hInstance = GetModuleHandle(NULL); WNDCLASSEX wc = { sizeof(WNDCLASSEX) }; - wc.lpfnWndProc = WindowProc; + wc.lpfnWndProc = ui_default_eventproc; wc.hInstance = hInstance; wc.lpszClassName = mainWindowClass; wc.hCursor = LoadCursor(NULL, IDC_ARROW); @@ -137,8 +108,9 @@ UiObject *ui_window(const char *title, void *window_data) { } -void ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { +int ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { //UiWindow *window = (UiWindow*)widget; + return 0; } void ui_window_widget_show(W32Widget *w, BOOLEAN show) { diff --git a/ui/win32/window.h b/ui/win32/window.h index 5d4c8f7..4562b01 100644 --- a/ui/win32/window.h +++ b/ui/win32/window.h @@ -48,7 +48,7 @@ typedef struct UiWindow { void ui_window_init(void); -void ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +int ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void ui_window_widget_show(W32Widget *w, BOOLEAN show); #ifdef __cplusplus