From 7143b89a82aa15a532468e59fe6e9b1ecc175e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Sat, 11 Dec 2010 12:42:52 +0000 Subject: [PATCH] prop: Add a property grouper than can group child property tree based on a node path glw: Add $clone.* property namespace that is local to each clone glw: Add propGrouper() and propSorter() GLW methods service: Remove the fixed set of service type and sorting, grouping, etc of service from service core glw: Let home.view group and sort services by itself --- Makefile | 1 + glwthemes/mono/pages/home.view | 63 ++---- src/backend/dvd/linux_dvd.c | 4 +- src/backend/htsp/htsp.c | 4 +- src/backend/search.c | 3 +- src/bookmarks.c | 12 +- src/fileaccess/fa_scanner.c | 2 +- src/js/js_page.c | 2 +- src/js/js_service.c | 2 +- src/prop/prop.h | 7 +- src/prop/prop_core.c | 72 ++++--- src/prop/prop_grouper.c | 345 +++++++++++++++++++++++++++++++++ src/prop/prop_grouper.h | 33 ++++ src/prop/prop_i.h | 4 + src/prop/prop_nodefilter.c | 67 ++++--- src/prop/prop_nodefilter.h | 6 +- src/sd/sd.c | 27 +-- src/service.c | 84 +------- src/service.h | 29 +-- src/settings.c | 3 +- src/ui/glw/glw.h | 5 +- src/ui/glw/glw_keyintercept.c | 6 +- src/ui/glw/glw_slider.c | 6 +- src/ui/glw/glw_text_bitmap.c | 6 +- src/ui/glw/glw_view.c | 2 +- src/ui/glw/glw_view.h | 2 +- src/ui/glw/glw_view_eval.c | 220 +++++++++++++++++---- 27 files changed, 728 insertions(+), 289 deletions(-) create mode 100644 src/prop/prop_grouper.c create mode 100644 src/prop/prop_grouper.h diff --git a/Makefile b/Makefile index c4ddbd4f77..95b0f1357d 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ SRCS += src/main.c \ src/prop/prop_nodefilter.c \ src/prop/prop_tags.c \ src/prop/prop_vector.c \ + src/prop/prop_grouper.c \ ifeq ($(PLATFORM), linux) SRCS += src/arch/linux.c diff --git a/glwthemes/mono/pages/home.view b/glwthemes/mono/pages/home.view index 68712a01d0..aaf55ba093 100644 --- a/glwthemes/mono/pages/home.view +++ b/glwthemes/mono/pages/home.view @@ -5,14 +5,13 @@ $page.glw.title = "Home"; #define COL() {[0.45, 0.45, 0.5]} -#define SERVICE_TYPE_LISTER(TITLE, NODES, HIDDEN, ICON, COLOR, P1) { +#define SERVICE_TYPE_LISTER(TITLE, NODES, ICON, COLOR) { widget(container_y, { .align = center; - (P1) = isFocused(); + $clone.focus = isFocused(); .width = $ui.fontsize * 10; - .hidden = HIDDEN; widget(image, { - .color = select(P1, [1.0, 1.0, 1.0], COLOR); + .color = select($clone.focus, [1.0, 1.0, 1.0], COLOR); .shadow = true; .source = ICON; .height = $ui.fontsize * 4; @@ -22,7 +21,7 @@ $page.glw.title = "Home"; widget(list_y, { .height = 128; - cloner(NODES, backdrop, { + cloner(propSorter(NODES, "node.title"), backdrop, { PLATE_GFX(); @@ -41,16 +40,6 @@ $page.glw.title = "Home"; .caption = $self.title; .color = select(isFocused(), [1.0, 1.0, 1.0], COLOR); }); -/* - widget(icon, { - .align = right; - .shadow = true; - .source = $self.icon; - .sizeBias = -4; - .hqScaling = true; - .alpha = select(isFocused(), 1.0, 0.2); - }); -*/ }); }); }); @@ -108,33 +97,23 @@ widget(container_y, { widget(container_x, { .align = center; - - SERVICE_TYPE_LISTER("Music", $global.services.music.nodes, - !count($global.services.music.nodes), - "theme://icons/mediasources/music.png", - PLATE_COLOR_AUDIO(), $view.musicFocused); - - - SERVICE_TYPE_LISTER("Video", $global.services.video.nodes, - !count($global.services.video.nodes), - "theme://icons/mediasources/movies.png", - PLATE_COLOR_VIDEO(), $view.videoFocused); - - SERVICE_TYPE_LISTER("TV", $global.services.tv.nodes, - !count($global.services.tv.nodes), - "theme://icons/mediasources/tv.png", - PLATE_COLOR_TV(), $view.tvFocused); - - SERVICE_TYPE_LISTER("Photos", $global.services.image.nodes, - !count($global.services.image.nodes), - "theme://icons/mediasources/pictures.png", - PLATE_COLOR_IMAGE(), $view.photosFocused); - - SERVICE_TYPE_LISTER("Other", $global.services.other.nodes, - !count($global.services.other.nodes), - "theme://icons/mediasources/other.png", - PLATE_COLOR_OTHER(), $view.otherFocused); - }); + cloner(propSorter(propGrouper($global.sources, "node.type"), "node.name"), + container_z, { + SERVICE_TYPE_LISTER($self.name, $self.nodes, + "theme://icons/mediasources/" + + translate($self.name, "other.png", + "music", "music.png", + "video", "movies.png", + "tv", "tv.png", + "photos", "pictures.png"), + + translate($self.name, PLATE_COLOR_OTHER(), + "music", PLATE_COLOR_AUDIO(), + "video", PLATE_COLOR_VIDEO(), + "tv", PLATE_COLOR_TV(), + "photos", PLATE_COLOR_IMAGE())); + }); + }); HORIZONTAL_SEPARATOR(); diff --git a/src/backend/dvd/linux_dvd.c b/src/backend/dvd/linux_dvd.c index b9e80da9e5..7c1cb86833 100644 --- a/src/backend/dvd/linux_dvd.c +++ b/src/backend/dvd/linux_dvd.c @@ -82,14 +82,14 @@ set_status(disc_scanner_t *ds, disc_status_t status, const char *title) case DISC_AUDIO: snprintf(buf, sizeof(buf), "Audio CD"); snprintf(url, sizeof(url), "audiocd:%s", ds->ds_dev); - ds->ds_svc = service_create(buf, url, SVC_TYPE_MUSIC, NULL, 0); + ds->ds_svc = service_create(buf, url, "music", NULL, 0); break; case DISC_ISOFS: snprintf(buf, sizeof(buf), "DVD: %s", title); snprintf(url, sizeof(url), "dvd:%s", ds->ds_dev); - ds->ds_svc = service_create(buf, url, SVC_TYPE_VIDEO, NULL, 0); + ds->ds_svc = service_create(buf, url, "video", NULL, 0); break; case DISC_UNKNOWN_TYPE: diff --git a/src/backend/htsp/htsp.c b/src/backend/htsp/htsp.c index 018809bd53..8dca9a0b3c 100644 --- a/src/backend/htsp/htsp.c +++ b/src/backend/htsp/htsp.c @@ -1120,7 +1120,7 @@ make_model(prop_t *parent, const char *title, prop_t *nodes) pnf = prop_nf_create(prop_create(model, "nodes"), nodes, prop_create(model, "filter"), - NULL); + NULL, PROP_NF_AUTODESTROY); prop_set_int(prop_create(model, "canFilter"), 1); prop_nf_release(pnf); @@ -1145,7 +1145,7 @@ make_model2(prop_t *parent, prop_t *sourcemodel) pnf = prop_nf_create(prop_create(model, "nodes"), prop_create(sourcemodel, "nodes"), prop_create(model, "filter"), - NULL); + NULL, PROP_NF_AUTODESTROY); prop_set_int(prop_create(model, "canFilter"), 1); prop_nf_release(pnf); diff --git a/src/backend/search.c b/src/backend/search.c index 57911ac0a3..65e700de78 100644 --- a/src/backend/search.c +++ b/src/backend/search.c @@ -123,7 +123,8 @@ search_open(prop_t *page, const char *url0) pnf = prop_nf_create(prop_create(model, "nodes"), prop_create(source, "nodes"), - NULL, "node.metadata.title"); + NULL, "node.metadata.title", + PROP_NF_AUTODESTROY); prop_nf_pred_int_add(pnf, "node.entries", PROP_NF_CMP_EQ, 0, NULL, diff --git a/src/bookmarks.c b/src/bookmarks.c index ef4988c8a0..633121b12c 100644 --- a/src/bookmarks.c +++ b/src/bookmarks.c @@ -120,10 +120,7 @@ static void set_type(void *opaque, const char *str) { bookmark_t *bm = opaque; - service_type_t type = service_str2type(str); - if(type == -1) - type = SVC_TYPE_OTHER; - service_set_type(bm->bm_service, type); + service_set_type(bm->bm_service, str); bookmark_save(); } @@ -150,20 +147,17 @@ bookmark_add_prop(prop_t *parent, const char *name, const char *value, * */ static void -bookmark_add(const char *title, const char *url, const char *svctype) +bookmark_add(const char *title, const char *url, const char *type) { bookmark_t *bm = calloc(1, sizeof(bookmark_t)); prop_t *p = prop_create(NULL, NULL); prop_t *src = prop_create(p, "model"); - service_type_t type = service_str2type(svctype); - if(type == -1) - type = SVC_TYPE_OTHER; prop_set_string(prop_create(src, "type"), "bookmark"); bm->bm_title_sub = bookmark_add_prop(src, "title", title, bm, set_title); bm->bm_url_sub = bookmark_add_prop(src, "url", url, bm, set_url); - bm->bm_type_sub = bookmark_add_prop(src, "svctype", svctype, bm, set_type); + bm->bm_type_sub = bookmark_add_prop(src, "svctype", type, bm, set_type); bm->bm_service = service_create(title, url, type, NULL, 1); diff --git a/src/fileaccess/fa_scanner.c b/src/fileaccess/fa_scanner.c index a0d497c230..b1ab281b88 100644 --- a/src/fileaccess/fa_scanner.c +++ b/src/fileaccess/fa_scanner.c @@ -647,7 +647,7 @@ fa_scanner(const char *url, prop_t *model, const char *playme) prop_nf_release(prop_nf_create(prop_create(model, "nodes"), source, prop_create(model, "filter"), - "node.filename")); + "node.filename", PROP_NF_AUTODESTROY)); prop_set_int(prop_create(model, "canFilter"), 1); diff --git a/src/js/js_page.c b/src/js/js_page.c index b08d416425..ffc2233dcd 100644 --- a/src/js/js_page.c +++ b/src/js/js_page.c @@ -494,7 +494,7 @@ init_model_props(js_model_t *jm, prop_t *model) pnf = prop_nf_create(prop_create(model, "nodes"), jm->jm_nodes, prop_create(model, "filter"), - NULL); + NULL, PROP_NF_AUTODESTROY); prop_set_int(prop_create(model, "canFilter"), 1); diff --git a/src/js/js_service.c b/src/js/js_service.c index 674e48e616..5291576ebf 100644 --- a/src/js/js_service.c +++ b/src/js/js_service.c @@ -58,7 +58,7 @@ js_createService(JSContext *cx, JSObject *obj, uintN argc, &title, &url, &type, &icon)) return JS_FALSE; - s = service_create(title, url, service_str2type(type), icon, 0); + s = service_create(title, url, type, icon, 0); robj = JS_NewObjectWithGivenProto(cx, &service_class, NULL, NULL); JS_SetPrivate(cx, robj, s); *rval = OBJECT_TO_JSVAL(robj); diff --git a/src/prop/prop.h b/src/prop/prop.h index 3d9c4d8b55..b5994dd749 100644 --- a/src/prop/prop.h +++ b/src/prop/prop.h @@ -115,7 +115,7 @@ void prop_init(void); #define PROP_SUB_EXPEDITE 0x20 #define PROP_SUB_MULTI 0x40 #define PROP_SUB_INTERNAL 0x80 -#define PROP_SUB_NOLOCK 0x100 +#define PROP_SUB_DONTLOCK 0x100 enum { PROP_TAG_END = 0, @@ -362,11 +362,14 @@ void prop_tag_set(prop_t *p, void *key, void *value); void *prop_tag_clear(prop_t *p, void *key); -/* DEBUGish */ const char *propname(prop_t *p); void prop_print_tree(prop_t *p, int followlinks); void prop_test(void); +#ifdef PROP_DEBUG +extern int prop_trace; +#endif + #endif /* PROP_H__ */ diff --git a/src/prop/prop_core.c b/src/prop/prop_core.c index 5dc65ac1e7..d58ae4f47d 100644 --- a/src/prop/prop_core.c +++ b/src/prop/prop_core.c @@ -32,6 +32,10 @@ #include "misc/string.h" #include "event.h" +#ifdef PROP_DEBUG +int prop_trace; +#endif + hts_mutex_t prop_mutex; hts_mutex_t prop_tag_mutex; static prop_t *prop_global; @@ -972,7 +976,6 @@ prop_build_notify_child(prop_sub_t *s, prop_t *p, prop_event_t event, if(direct || s->hps_flags & PROP_SUB_INTERNAL) { prop_callback_t *cb = s->hps_callback; prop_trampoline_t *pt = s->hps_trampoline; - if(pt != NULL) pt(s, event, p, flags); else @@ -1463,6 +1466,20 @@ prop_destroy0(prop_t *p) prop_t *c, *next, *parent; prop_sub_t *s; +#ifdef PROP_DEBUG + if(prop_trace) { + int csubs = 0, psubs = 0; + LIST_FOREACH(s, &p->hp_canonical_subscriptions, hps_canonical_prop_link) + csubs++; + LIST_FOREACH(s, &p->hp_value_subscriptions, hps_value_prop_link) + psubs++; + + printf("Entering prop_destroy0(%s) [type=%d, refcnt=%d, xref=%d, csubs=%d, psubs=%d]\n", + propname(p), p->hp_type, p->hp_refcount, p->hp_xref, + csubs, psubs); + } +#endif + if(p->hp_type == PROP_ZOMBIE) return 0; @@ -1524,6 +1541,12 @@ prop_destroy0(prop_t *p) if(p->hp_originator != NULL) prop_remove_from_originator(p); +#ifdef PROP_DEBUG + if(prop_trace) + printf("Leaving prop_destroy0(%s) parent=%p\n", propname(p), + p->hp_parent); +#endif + if(p->hp_parent != NULL) { prop_notify_child(p, p->hp_parent, PROP_DEL_CHILD, NULL, 0); parent = p->hp_parent; @@ -1857,7 +1880,7 @@ prop_subscribe(int flags, ...) struct prop_root_list proproots; void *cb = NULL; prop_trampoline_t *trampoline = NULL; - int dolock = !(flags & PROP_SUB_NOLOCK); + int dolock = !(flags & PROP_SUB_DONTLOCK); va_list ap; va_start(ap, flags); @@ -2155,38 +2178,19 @@ prop_set_epilogue(prop_sub_t *skipme, prop_t *p, const char *origin) } - -/** - * - */ void -prop_set_string_ex(prop_t *p, prop_sub_t *skipme, const char *str, - prop_str_type_t type) +prop_set_string_exl(prop_t *p, prop_sub_t *skipme, const char *str, + prop_str_type_t type) { - if(p == NULL) - return; - - if(str == NULL) { - prop_set_void_ex(p, skipme); - return; - } - - hts_mutex_lock(&prop_mutex); - - if(p->hp_type == PROP_ZOMBIE) { - hts_mutex_unlock(&prop_mutex); + if(p->hp_type == PROP_ZOMBIE) return; - } if(p->hp_type != PROP_STRING) { - if(prop_clean(p)) { - hts_mutex_unlock(&prop_mutex); + if(prop_clean(p)) return; - } } else if(!strcmp(rstr_get(p->hp_rstring), str)) { - hts_mutex_unlock(&prop_mutex); return; } else { rstr_release(p->hp_rstring); @@ -2196,8 +2200,24 @@ prop_set_string_ex(prop_t *p, prop_sub_t *skipme, const char *str, p->hp_type = PROP_STRING; p->hp_rstrtype = type; + prop_notify_value(p, skipme, "prop_set_string()"); +} - prop_set_epilogue(skipme, p, "prop_set_string()"); +/** + * + */ +void +prop_set_string_ex(prop_t *p, prop_sub_t *skipme, const char *str, + prop_str_type_t type) +{ + if(str == NULL) { + prop_set_void_ex(p, skipme); + return; + } + + hts_mutex_lock(&prop_mutex); + prop_set_string_exl(p, skipme, str, type); + hts_mutex_unlock(&prop_mutex); } diff --git a/src/prop/prop_grouper.c b/src/prop/prop_grouper.c new file mode 100644 index 0000000000..68037dfdd2 --- /dev/null +++ b/src/prop/prop_grouper.c @@ -0,0 +1,345 @@ +/* + * Property trees + * Copyright (C) 2008 Andreas Öman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "showtime.h" +#include "prop_i.h" +#include "prop_grouper.h" +#include "misc/string.h" + +LIST_HEAD(pg_node_list, pg_node); +LIST_HEAD(pg_group_list, pg_group); + +/** + * + */ +typedef struct pg_node { + prop_t *pgn_in; + prop_t *pgn_out; + prop_sub_t *pgn_sub; + + struct prop_grouper *pgn_grouper; + LIST_ENTRY(pg_node) pgn_grouper_link; + + struct pg_group *pgn_group; + LIST_ENTRY(pg_node) pgn_group_link; + +} pg_node_t; + + +/** + * + */ +typedef struct pg_group { + char *pgg_name; + LIST_ENTRY(pg_group) pgg_link; + prop_t *pgg_root; + prop_t *pgg_nodes; + struct pg_node_list pgg_entries; + +} pg_group_t; + + +/** + * + */ +struct prop_grouper { + + struct pg_node_list pg_nodes; + + struct pg_group_list pg_groups; + + char **pg_groupingpath; + prop_sub_t *pg_srcsub; + + prop_t *pg_dst; + +}; + + +/** + * + */ +static pg_group_t * +group_find(prop_grouper_t *pg, const char *name) +{ + pg_group_t *pgg; + + LIST_FOREACH(pgg, &pg->pg_groups, pgg_link) + if(!strcmp(pgg->pgg_name, name)) + return pgg; + pgg = calloc(1, sizeof(pg_group_t)); + LIST_INSERT_HEAD(&pg->pg_groups, pgg, pgg_link); + pgg->pgg_name = strdup(name); + pgg->pgg_root = prop_create0(pg->pg_dst, NULL, NULL, 0); + prop_set_string_exl(prop_create0(pgg->pgg_root, "name", NULL, 0), + NULL, name, PROP_STR_UTF8); + pgg->pgg_nodes = prop_create0(pgg->pgg_root, "nodes", NULL, 0); + return pgg; +} + + +/** + * + */ +static void +group_destroy(pg_group_t *pgg) +{ + prop_destroy0(pgg->pgg_root); + LIST_REMOVE(pgg, pgg_link); + free(pgg->pgg_name); + free(pgg); +} + + +/** + * + */ +static void +node_unset(pg_node_t *pgn) +{ + if(pgn->pgn_out != NULL) + prop_destroy0(pgn->pgn_out); + + if(pgn->pgn_group != NULL) { + LIST_REMOVE(pgn, pgn_group_link); + if(LIST_FIRST(&pgn->pgn_group->pgg_entries) == NULL) + group_destroy(pgn->pgn_group); + } +} + + +/** + * + */ +static void +node_update_group(pg_node_t *pgn, const char *group) +{ + node_unset(pgn); + + if(group == NULL) { + pgn->pgn_out = NULL; + pgn->pgn_group = NULL; + return; + } + + pgn->pgn_group = group_find(pgn->pgn_grouper, group); + LIST_INSERT_HEAD(&pgn->pgn_group->pgg_entries, pgn, pgn_group_link); + + pgn->pgn_out = prop_create0(NULL, NULL, NULL, 0); + prop_link0(pgn->pgn_in, pgn->pgn_out, NULL, 0); + + prop_set_parent0(pgn->pgn_out, pgn->pgn_group->pgg_nodes, NULL, NULL); +} + + +/** + * + */ +static void +node_set_group(void *opaque, prop_event_t event, ...) +{ + pg_node_t *pgn = opaque; + char buf[32]; + va_list ap; + + va_start(ap, event); + const char *group; + + switch(event) { + case PROP_SET_RSTRING: + case PROP_SET_RLINK: + group = rstr_get(va_arg(ap, rstr_t *)); + break; + + case PROP_SET_INT: + snprintf(buf, sizeof(buf), "%d", va_arg(ap, int)); + group = buf; + break; + + case PROP_SET_FLOAT: + snprintf(buf, sizeof(buf), "%f", va_arg(ap, double)); + group = buf; + break; + + default: + group = NULL; + break; + } + node_update_group(pgn, group); +} + + +/** + * + */ +static void +pg_add_node(prop_grouper_t *pg, prop_t *node) +{ + pg_node_t *pgn = calloc(1, sizeof(pg_node_t)); + LIST_INSERT_HEAD(&pg->pg_nodes, pgn, pgn_grouper_link); + pgn->pgn_grouper = pg; + prop_tag_set(node, pg, pgn); + pgn->pgn_in = node; + + pgn->pgn_sub = prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK, + PROP_TAG_CALLBACK, node_set_group, pgn, + PROP_TAG_NAMED_ROOT, node, "node", + PROP_TAG_NAME_VECTOR, pg->pg_groupingpath, + NULL); +} + + +/** + * + */ +static void +pg_add_nodes(prop_grouper_t *pg, prop_vec_t *pv) +{ + int i; + for(i = 0; i < prop_vec_len(pv); i++) + pg_add_node(pg, prop_vec_get(pv, i)); +} + + +/** + * + */ +static void +pg_del_node(prop_grouper_t *pg, pg_node_t *pgn) +{ + LIST_REMOVE(pgn, pgn_grouper_link); + node_unset(pgn); + prop_unsubscribe0(pgn->pgn_sub); + free(pgn); +} + + +/** + * + */ +static void +pg_clear(prop_grouper_t *pg) +{ + pg_node_t *pgn; + + while((pgn = LIST_FIRST(&pg->pg_nodes)) != NULL) { + prop_tag_clear(pgn->pgn_in, pg); + pg_del_node(pg, pgn); + } +} + + +/** + * + */ +static void +src_cb(void *opaque, prop_event_t event, ...) +{ + prop_grouper_t *pg = opaque; + va_list ap; + va_start(ap, event); + + switch(event) { + case PROP_ADD_CHILD: + case PROP_ADD_CHILD_BEFORE: + pg_add_node(pg, va_arg(ap, prop_t *)); + break; + + case PROP_ADD_CHILD_VECTOR: + pg_add_nodes(pg, va_arg(ap, prop_vec_t *)); + break; + + case PROP_DEL_CHILD: + pg_del_node(pg, prop_tag_clear(va_arg(ap, prop_t *), pg)); + break; + + case PROP_MOVE_CHILD: + break; + + case PROP_SET_DIR: + break; + + case PROP_SET_VOID: + pg_clear(pg); + break; + + case PROP_REQ_DELETE_VECTOR: + break; + + default: + abort(); + } +} + + + +/** + * + */ +prop_grouper_t * +prop_grouper_create(prop_t *dst, prop_t *src, const char *groupkey, + int flags) +{ + prop_grouper_t *pg = calloc(1, sizeof(prop_grouper_t)); + + pg->pg_dst = flags & PROP_GROUPER_TAKE_DST_OWNERSHIP + ? dst : prop_xref_addref(dst); + + pg->pg_groupingpath = strvec_split(groupkey, '.'); + + hts_mutex_lock(&prop_mutex); + + pg->pg_srcsub = prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK, + PROP_TAG_CALLBACK, src_cb, pg, + PROP_TAG_ROOT, src, + NULL); + hts_mutex_unlock(&prop_mutex); + return pg; +} + + + +/** + * + */ +void +prop_grouper_destroy(prop_grouper_t *pg) +{ + hts_mutex_lock(&prop_mutex); + + pg_clear(pg); + prop_unsubscribe0(pg->pg_srcsub); + prop_destroy0(pg->pg_dst); + + assert(LIST_FIRST(&pg->pg_nodes) == NULL); + assert(LIST_FIRST(&pg->pg_groups) == NULL); + hts_mutex_unlock(&prop_mutex); + + strvec_free(pg->pg_groupingpath); + free(pg); +} diff --git a/src/prop/prop_grouper.h b/src/prop/prop_grouper.h new file mode 100644 index 0000000000..fbfc7cc825 --- /dev/null +++ b/src/prop/prop_grouper.h @@ -0,0 +1,33 @@ +/* + * Property grouper + * Copyright (C) 2010 Andreas Öman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PROP_GROUPER_H__ +#define PROP_GROUPER_H__ + +#include "prop.h" + +#define PROP_GROUPER_TAKE_DST_OWNERSHIP 0x1 + +typedef struct prop_grouper prop_grouper_t; + +prop_grouper_t *prop_grouper_create(prop_t *dst, prop_t *src, + const char *group_key, int flags); + +void prop_grouper_destroy(prop_grouper_t *pg); + +#endif // PROP_NODEFILTER_H__ diff --git a/src/prop/prop_i.h b/src/prop/prop_i.h index 7d63f36582..cc33bf7ef5 100644 --- a/src/prop/prop_i.h +++ b/src/prop/prop_i.h @@ -324,4 +324,8 @@ void prop_have_more_childs0(prop_t *p); void prop_want_more_childs0(prop_sub_t *s); + +void prop_set_string_exl(prop_t *p, prop_sub_t *skipme, const char *str, + prop_str_type_t type); + #endif // PROP_I_H__ diff --git a/src/prop/prop_nodefilter.c b/src/prop/prop_nodefilter.c index ee09b9aea3..4cdf530b72 100644 --- a/src/prop/prop_nodefilter.c +++ b/src/prop/prop_nodefilter.c @@ -97,6 +97,7 @@ typedef struct prop_nf_pred { typedef struct prop_nf { int pnf_refcount; + int flags; prop_t *src; prop_t *dst; @@ -342,7 +343,7 @@ nf_update_multisub(prop_nf_t *nf, nfnode_t *nfn) if(nf->filter) { nfn->multisub = - prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_MULTI | PROP_SUB_NOLOCK, + prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_MULTI | PROP_SUB_DONTLOCK, PROP_TAG_CALLBACK, nf_multi_filter, nfn, PROP_TAG_ROOT, nfn->in, NULL); @@ -429,14 +430,14 @@ nfn_insert_preds(prop_nf_t *nf, nfnode_t *nfn) if(pnp->pnp_str != NULL) { nfnp->nfnp_sub = - prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_NOLOCK, + prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK, PROP_TAG_CALLBACK_STRING, nfnp_update_str, nfnp, PROP_TAG_NAMED_ROOT, nfn->in, "node", PROP_TAG_NAME_VECTOR, pnp->pnp_path, NULL); } else { nfnp->nfnp_sub = - prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_NOLOCK, + prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK, PROP_TAG_CALLBACK_INT, nfnp_update_int, nfnp, PROP_TAG_NAMED_ROOT, nfn->in, "node", PROP_TAG_NAME_VECTOR, pnp->pnp_path, @@ -508,7 +509,7 @@ nf_update_order(prop_nf_t *nf, nfnode_t *nfn) } else { nfn->sortsub = - prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_NOLOCK | + prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK | PROP_SUB_DIRECT_UPDATE, PROP_TAG_CALLBACK, nf_set_sortkey, nfn, PROP_TAG_NAMED_ROOT, nfn->in, "node", @@ -672,6 +673,23 @@ nf_destroy_preds(prop_nf_t *nf) } + +/** + * + */ +static void +nf_clear(prop_nf_t *nf) +{ + nfnode_t *nfn; + + while((nfn = TAILQ_FIRST(&nf->in)) != NULL) { + prop_tag_clear(nfn->in, nf); + nf_del_node(nf, nfn); + } + nf->pos_valid = 1; +} + + /** * */ @@ -682,6 +700,12 @@ prop_nf_release0(struct prop_nf *pnf) if(pnf->pnf_refcount > 0) return; + if(pnf->srcsub != NULL) + prop_unsubscribe0(pnf->srcsub); + + if(!(pnf->flags & PROP_NF_AUTODESTROY)) + nf_clear(pnf); + prop_unsubscribe0(pnf->dstsub); prop_destroy0(pnf->dst); @@ -702,20 +726,6 @@ prop_nf_release0(struct prop_nf *pnf) } -/** - * - */ -static void -nf_clear(prop_nf_t *nf) -{ - nfnode_t *nfn; - - while((nfn = TAILQ_FIRST(&nf->in)) != NULL) - nf_del_node(nf, nfn); - nf->pos_valid = 1; -} - - /** * */ @@ -872,14 +882,15 @@ nf_set_filter(void *opaque, const char *str) */ struct prop_nf * prop_nf_create(prop_t *dst, prop_t *src, prop_t *filter, - const char *defsortpath) + const char *defsortpath, int flags) { + prop_nf_t *nf = calloc(1, sizeof(prop_nf_t)); - + nf->flags = flags; TAILQ_INIT(&nf->in); TAILQ_INIT(&nf->out); - nf->dst = prop_xref_addref(dst); + nf->dst = flags & PROP_NF_TAKE_DST_OWNERSHIP ? dst : prop_xref_addref(dst); nf->src = src; nf->defsortpath = defsortpath ? strvec_split(defsortpath, '.') : NULL; @@ -887,24 +898,24 @@ prop_nf_create(prop_t *dst, prop_t *src, prop_t *filter, hts_mutex_lock(&prop_mutex); if(filter != NULL) - nf->filtersub = prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_NOLOCK, + nf->filtersub = prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK, PROP_TAG_CALLBACK_STRING, nf_set_filter, nf, PROP_TAG_ROOT, filter, NULL); - nf->dstsub = prop_subscribe(PROP_SUB_INTERNAL | - PROP_SUB_NOLOCK, + nf->dstsub = prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK, PROP_TAG_CALLBACK, prop_nf_dst_cb, nf, PROP_TAG_ROOT, nf->dst, NULL); - nf->srcsub = prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_TRACK_DESTROY | - PROP_SUB_NOLOCK, + nf->srcsub = prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK | + (flags & PROP_NF_AUTODESTROY ? + PROP_SUB_TRACK_DESTROY : 0), PROP_TAG_CALLBACK, prop_nf_src_cb, nf, PROP_TAG_ROOT, src, NULL); - nf->pnf_refcount = 2; + nf->pnf_refcount = 1 + (flags & PROP_NF_AUTODESTROY ? 1 : 0); hts_mutex_unlock(&prop_mutex); @@ -965,7 +976,7 @@ prop_nf_pred_add(struct prop_nf *nf, if(enable != NULL) { pnp->pnp_enable_sub = - prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_NOLOCK, + prop_subscribe(PROP_SUB_INTERNAL | PROP_SUB_DONTLOCK, PROP_TAG_CALLBACK_INT, pnp_set_enable, pnp, PROP_TAG_ROOT, enable, NULL); diff --git a/src/prop/prop_nodefilter.h b/src/prop/prop_nodefilter.h index 204aefeed8..272e49004d 100644 --- a/src/prop/prop_nodefilter.h +++ b/src/prop/prop_nodefilter.h @@ -34,6 +34,9 @@ typedef enum { PROP_NF_MODE_EXCLUDE, } prop_nf_mode_t; +#define PROP_NF_TAKE_DST_OWNERSHIP 0x1 +#define PROP_NF_AUTODESTROY 0x2 + void prop_nf_pred_str_add(struct prop_nf *nf, const char *path, prop_nf_cmp_t cf, const char *str, prop_t *enable, @@ -45,7 +48,8 @@ void prop_nf_pred_int_add(struct prop_nf *nf, prop_nf_mode_t mode); struct prop_nf *prop_nf_create(prop_t *dst, prop_t *src, - prop_t *filter, const char *defsortpath); + prop_t *filter, const char *defsortpath, + int flags); void prop_nf_release(struct prop_nf *nf); diff --git a/src/sd/sd.c b/src/sd/sd.c index 079be68a48..912ce27131 100644 --- a/src/sd/sd.c +++ b/src/sd/sd.c @@ -82,27 +82,10 @@ sd_add_service_htsp(service_instance_t *si, const char *name, return; snprintf(url, sizeof(url), "htsp://%s:%d", host, port); - si->si_services[0] = - service_create(name, url, SVC_TYPE_TV, NULL, 0); + si->si_services[0] = service_create(name, url, "tv", NULL, 0); } -static struct strtab contents2type_tab[] = { - { "music", SVC_TYPE_MUSIC }, - { "audio", SVC_TYPE_MUSIC }, - { "video", SVC_TYPE_VIDEO }, - { "videos", SVC_TYPE_VIDEO }, - { "movie", SVC_TYPE_VIDEO }, - { "movies", SVC_TYPE_VIDEO }, - { "image", SVC_TYPE_IMAGE }, - { "images", SVC_TYPE_IMAGE }, - { "photo", SVC_TYPE_IMAGE }, - { "photos", SVC_TYPE_IMAGE }, - { "pictures", SVC_TYPE_IMAGE }, - { "picture", SVC_TYPE_IMAGE }, -}; - - /** * Webdav service creator */ @@ -112,7 +95,6 @@ sd_add_service_webdav(service_instance_t *si, const char *name, const char *contents) { char url[URL_MAX]; - service_type_t type; if(si->si_services[0] != NULL) return; @@ -121,12 +103,7 @@ sd_add_service_webdav(service_instance_t *si, const char *name, host, port, path == NULL || path[0] != '/' ? "/" : "", path ? path : ""); - type = contents ? str2val(contents, contents2type_tab) : -1; - - if(type == -1) - type = SVC_TYPE_OTHER; - - si->si_services[0] = service_create(name, url, type, NULL, 1); + si->si_services[0] = service_create(name, url, contents, NULL, 1); } /** diff --git a/src/service.c b/src/service.c index 958a39501a..3124c7cc05 100644 --- a/src/service.c +++ b/src/service.c @@ -23,25 +23,17 @@ #include "service.h" #include "misc/strtab.h" #include "prop/prop_nodefilter.h" +#include "prop/prop_grouper.h" #include "backend/backend.h" -static prop_t *all_services; - - +LIST_HEAD(service_type_list, service_type); -static const char *type2str[] = { - [SVC_TYPE_MUSIC] = "music", - [SVC_TYPE_IMAGE] = "image", - [SVC_TYPE_VIDEO] = "video", - [SVC_TYPE_TV] = "tv", - [SVC_TYPE_OTHER] = "other" -}; +static prop_t *all_services; struct service_list services; hts_mutex_t service_mutex; static hts_cond_t service_cond; - /** * */ @@ -56,23 +48,6 @@ static struct strtab status_tab[] = { static void *service_probe_loop(void *aux); -/** - * - */ -static void -add_service_type(prop_t *root, service_type_t type) -{ - struct prop_nf *pnf; - const char *name = type2str[type]; - - pnf = prop_nf_create(prop_create(prop_create(root, name), "nodes"), - all_services, NULL, "node.title"); - - prop_nf_pred_str_add(pnf, "node.type", - PROP_NF_CMP_NEQ, name, NULL, - PROP_NF_MODE_EXCLUDE); -} - /** * @@ -85,18 +60,7 @@ service_init(void) hts_thread_create_detached("service probe", service_probe_loop, NULL); - all_services = prop_create(NULL, NULL); - - prop_nf_create(prop_create(prop_get_global(), "sources"), - all_services, NULL, "node.title"); - - prop_t *p = prop_create(prop_get_global(), "services"); - - add_service_type(p, SVC_TYPE_MUSIC); - add_service_type(p, SVC_TYPE_IMAGE); - add_service_type(p, SVC_TYPE_VIDEO); - add_service_type(p, SVC_TYPE_TV); - add_service_type(p, SVC_TYPE_OTHER); + all_services = prop_create(prop_get_global(), "sources"); } @@ -155,14 +119,13 @@ seturl(service_t *s, const char *url) service_t * service_create(const char *title, const char *url, - service_type_t type, + const char *type, const char *icon, int probe) { service_t *s = calloc(1, sizeof(service_t)); prop_t *p; s->s_ref = 1; - s->s_type = type; p = s->s_root = prop_create(NULL, NULL); seturl(s, url); @@ -172,7 +135,7 @@ service_create(const char *title, prop_set_string(prop_create(p, "url"), url); s->s_prop_type = prop_create(p, "type"); - prop_set_string(s->s_prop_type, type2str[type]); + prop_set_string(s->s_prop_type, type); s->s_prop_status = prop_create(p, "status"); prop_set_string(s->s_prop_status, "ok"); @@ -215,9 +178,9 @@ service_get_statustxt_prop(service_t *s) * */ void -service_set_type(service_t *s, service_type_t type) +service_set_type(service_t *s, const char *type) { - prop_set_string(s->s_prop_type, type2str[type]); + prop_set_string(s->s_prop_type, type); } @@ -321,34 +284,3 @@ service_probe_loop(void *aux) } return NULL; } - - -/** - * - */ -static struct strtab strtype_tab[] = { - { "music", SVC_TYPE_MUSIC }, - { "video", SVC_TYPE_VIDEO }, - { "tv", SVC_TYPE_TV }, - { "image", SVC_TYPE_IMAGE }, - { "other", SVC_TYPE_OTHER }, -}; - -/** - * - */ -service_type_t -service_str2type(const char *str) -{ - return str ? str2val(str, strtype_tab) : SVC_TYPE_OTHER; -} - - -/** - * - */ -const char * -service_type2str(service_type_t type) -{ - return val2str(type, strtype_tab); -} diff --git a/src/service.h b/src/service.h index a6746ae026..db8ffa1f8b 100644 --- a/src/service.h +++ b/src/service.h @@ -5,20 +5,15 @@ #include "misc/queue.h" #include "prop/prop.h" +LIST_HEAD(service_list, service); +extern struct service_list services; + +extern hts_mutex_t service_mutex; + /** * */ -typedef enum { - SVC_TYPE_MUSIC, - SVC_TYPE_IMAGE, - SVC_TYPE_VIDEO, - SVC_TYPE_TV, - SVC_TYPE_OTHER, - SVC_num, -} service_type_t; - - typedef struct service { int s_ref; int s_zombie; @@ -32,16 +27,10 @@ typedef struct service { char *s_url; - service_type_t s_type; - int s_do_probe; int s_need_probe; } service_t; -LIST_HEAD(service_list, service); -extern struct service_list services; - -extern hts_mutex_t service_mutex; @@ -64,11 +53,11 @@ void service_destroy(service_t *s); service_t *service_create(const char *title, const char *url, - service_type_t type, + const char *type, const char *icon, int probe); -void service_set_type(service_t *svc, service_type_t type); +void service_set_type(service_t *svc, const char *type); void service_set_title(service_t *svc, const char *title); @@ -84,8 +73,4 @@ prop_t *service_get_statustxt_prop(service_t *s); void service_init(void); -service_type_t service_str2type(const char *str); - -const char *service_type2str(service_type_t type); - #endif // SERVICE_H__ diff --git a/src/settings.c b/src/settings.c index ce2f0d4942..6b54d5754e 100644 --- a/src/settings.c +++ b/src/settings.c @@ -548,7 +548,8 @@ settings_init(void) settings_nodes = prop_create(settings_root, "sources"); prop_nf_create(prop_create(settings_root, "nodes"), - settings_nodes, NULL, "node.model.title"); + settings_nodes, NULL, "node.model.title", + PROP_NF_AUTODESTROY); prop_set_string(prop_create(settings_root, "type"), "settings"); set_title(settings_root, "Global settings"); diff --git a/src/ui/glw/glw.h b/src/ui/glw/glw.h index 320df47a00..9e09bfeae3 100644 --- a/src/ui/glw/glw.h +++ b/src/ui/glw/glw.h @@ -107,7 +107,7 @@ typedef enum { GLW_ATTRIB_PROPROOTS, GLW_ATTRIB_TRANSITION_EFFECT, GLW_ATTRIB_EXPANSION, - GLW_ATTRIB_BIND_TO_PROPERTY, + GLW_ATTRIB_BIND_TO_PROPERTY5, GLW_ATTRIB_BIND_TO_ID, GLW_ATTRIB_SIZE_SCALE, GLW_ATTRIB_SIZE_BIAS, @@ -874,7 +874,8 @@ do { \ (void)va_arg(ap, void *); \ (void)va_arg(ap, int); \ break; \ - case GLW_ATTRIB_BIND_TO_PROPERTY: \ + case GLW_ATTRIB_BIND_TO_PROPERTY5: \ + (void)va_arg(ap, void *); \ (void)va_arg(ap, void *); \ (void)va_arg(ap, void *); \ case GLW_ATTRIB_PARENT_BEFORE: \ diff --git a/src/ui/glw/glw_keyintercept.c b/src/ui/glw/glw_keyintercept.c index 472365502a..662dc11eb9 100644 --- a/src/ui/glw/glw_keyintercept.c +++ b/src/ui/glw/glw_keyintercept.c @@ -202,17 +202,18 @@ glw_keyintercept_set(glw_t *w, int init, va_list ap) { glw_keyintercept_t *ki = (glw_keyintercept_t *)w; glw_attribute_t attrib; - prop_t *p, *pname, *view, *args; + prop_t *p, *pname, *view, *args, *clone; do { attrib = va_arg(ap, int); switch(attrib) { - case GLW_ATTRIB_BIND_TO_PROPERTY: + case GLW_ATTRIB_BIND_TO_PROPERTY5: p = va_arg(ap, prop_t *); pname = va_arg(ap, void *); view = va_arg(ap, prop_t *); args = va_arg(ap, prop_t *); + clone = va_arg(ap, prop_t *); ki_unbind(ki); @@ -224,6 +225,7 @@ glw_keyintercept_set(glw_t *w, int init, va_list ap) PROP_TAG_NAMED_ROOT, p, "self", PROP_TAG_NAMED_ROOT, view, "view", PROP_TAG_NAMED_ROOT, args, "args", + PROP_TAG_NAMED_ROOT, clone, "clone", PROP_TAG_ROOT, w->glw_root->gr_uii.uii_prop, NULL); diff --git a/src/ui/glw/glw_slider.c b/src/ui/glw/glw_slider.c index 8a7ec13787..4cf082a9fc 100644 --- a/src/ui/glw/glw_slider.c +++ b/src/ui/glw/glw_slider.c @@ -471,7 +471,7 @@ glw_slider_set(glw_t *w, int init, va_list ap) { glw_slider_t *s = (glw_slider_t *)w; glw_attribute_t attrib; - prop_t *p, *view, *args; + prop_t *p, *view, *args, *clone; const char **pname; const char *n; @@ -495,11 +495,12 @@ glw_slider_set(glw_t *w, int init, va_list ap) slider_bind_by_id(s, n); break; - case GLW_ATTRIB_BIND_TO_PROPERTY: + case GLW_ATTRIB_BIND_TO_PROPERTY5: p = va_arg(ap, prop_t *); pname = va_arg(ap, void *); view = va_arg(ap, void *); args = va_arg(ap, void *); + clone = va_arg(ap, void *); slider_unbind(s); @@ -510,6 +511,7 @@ glw_slider_set(glw_t *w, int init, va_list ap) PROP_TAG_NAMED_ROOT, p, "self", PROP_TAG_NAMED_ROOT, view, "view", PROP_TAG_NAMED_ROOT, args, "args", + PROP_TAG_NAMED_ROOT, clone, "clone", PROP_TAG_ROOT, w->glw_root->gr_uii.uii_prop, NULL); break; diff --git a/src/ui/glw/glw_text_bitmap.c b/src/ui/glw/glw_text_bitmap.c index f6ef90fa27..8c7a27c248 100644 --- a/src/ui/glw/glw_text_bitmap.c +++ b/src/ui/glw/glw_text_bitmap.c @@ -1293,7 +1293,7 @@ glw_text_bitmap_set(glw_t *w, int init, va_list ap) glw_root_t *gr = w->glw_root; glw_attribute_t attrib; int update = 0; - prop_t *p, *view, *args; + prop_t *p, *view, *args, *clone; const char **pname, *caption; if(init) { @@ -1383,11 +1383,12 @@ glw_text_bitmap_set(glw_t *w, int init, va_list ap) update = 1; break; - case GLW_ATTRIB_BIND_TO_PROPERTY: + case GLW_ATTRIB_BIND_TO_PROPERTY5: p = va_arg(ap, prop_t *); pname = va_arg(ap, void *); view = va_arg(ap, prop_t *); args = va_arg(ap, prop_t *); + clone = va_arg(ap, prop_t *); gtb_unbind(gtb); @@ -1399,6 +1400,7 @@ glw_text_bitmap_set(glw_t *w, int init, va_list ap) PROP_TAG_NAMED_ROOT, p, "self", PROP_TAG_NAMED_ROOT, view, "view", PROP_TAG_NAMED_ROOT, args, "args", + PROP_TAG_NAMED_ROOT, clone, "clone", PROP_TAG_ROOT, w->glw_root->gr_uii.uii_prop, NULL); diff --git a/src/ui/glw/glw_view.c b/src/ui/glw/glw_view.c index 2ba7ac46bc..2ab48eb28f 100644 --- a/src/ui/glw/glw_view.c +++ b/src/ui/glw/glw_view.c @@ -198,7 +198,7 @@ glw_view_create(glw_root_t *gr, const char *src, ec.prop = prop; ec.prop_parent = prop_parent; ec.prop_args = args; - v->viewprop = ec.prop_view = prop_create(NULL, NULL); + v->viewprop = ec.prop_viewx = prop_create(NULL, NULL); ec.sublist = &ec.w->glw_prop_subscriptions; if(glw_view_eval_block(t, &ec)) { diff --git a/src/ui/glw/glw_view.h b/src/ui/glw/glw_view.h index 4622b12751..80113c8ef8 100644 --- a/src/ui/glw/glw_view.h +++ b/src/ui/glw/glw_view.h @@ -195,7 +195,7 @@ typedef struct glw_view_eval_context { errorinfo_t *ei; token_t *alloc; struct glw *w; - struct prop *prop, *prop_parent, *prop_view, *prop_args; + struct prop *prop, *prop_parent, *prop_viewx, *prop_args, *prop_clone; struct glw_root *gr; int dynamic_eval; diff --git a/src/ui/glw/glw_view_eval.c b/src/ui/glw/glw_view_eval.c index 2b25832339..c66173e4db 100644 --- a/src/ui/glw/glw_view_eval.c +++ b/src/ui/glw/glw_view_eval.c @@ -28,6 +28,8 @@ #include "backend/backend.h" #include "misc/pixmap.h" #include "settings.h" +#include "prop/prop_grouper.h" +#include "prop/prop_nodefilter.h" LIST_HEAD(clone_list, clone); @@ -133,6 +135,8 @@ typedef struct clone { char c_evaluated; char c_active; // Set if widget is visible on screen + prop_t *c_clone_root; + } clone_t; @@ -283,7 +287,8 @@ token_resolve_ex(glw_view_eval_context_t *ec, token_t *t, int type) return NULL; } - if(t->type == TOKEN_PROPERTY_VALUE_NAME && subscribe_prop(ec, t, type)) + if((t->type == TOKEN_PROPERTY_VALUE_NAME || + t->type == TOKEN_PROPERTY_REF) && subscribe_prop(ec, t, type)) return NULL; if(t->type == TOKEN_PROPERTY_SUBSCRIPTION) { @@ -675,7 +680,8 @@ resolve_property_name(glw_view_eval_context_t *ec, token_t *a, int follow_links) p = prop_get_by_name(pname, follow_links, PROP_TAG_NAMED_ROOT, ec->prop, "self", PROP_TAG_NAMED_ROOT, ec->prop_parent, "parent", - PROP_TAG_NAMED_ROOT, ec->prop_view, "view", + PROP_TAG_NAMED_ROOT, ec->prop_viewx, "view", + PROP_TAG_NAMED_ROOT, ec->prop_clone, "clone", PROP_TAG_NAMED_ROOT, ec->prop_args, "args", PROP_TAG_ROOT, ui, NULL); @@ -773,7 +779,8 @@ eval_assign(glw_view_eval_context_t *ec, struct token *self) n.w = ec->w; n.prop = ec->prop; n.prop_parent = ec->prop_parent; - n.prop_view = ec->prop_view; + n.prop_viewx = ec->prop_viewx; + n.prop_clone = ec->prop_clone; n.prop_args = ec->prop_args; n.ei = ec->ei; n.gr = ec->gr; @@ -978,7 +985,8 @@ clone_eval(clone_t *c) memset(&n, 0, sizeof(n)); n.prop = c->c_prop; n.prop_parent = sc->sc_originating_prop; - n.prop_view = sc->sc_view_prop; + n.prop_viewx = sc->sc_view_prop; + n.prop_clone = c->c_clone_root; n.prop_args = sc->sc_view_args; n.gr = c->c_w->glw_root; @@ -1135,6 +1143,8 @@ cloner_add_child0(sub_cloner_t *sc, prop_t *p, prop_t *before, sc->sc_entries++; + c->c_clone_root = prop_create(NULL, NULL); + c->c_w = glw_create_i(parent->glw_root, sc->sc_cloner_class, GLW_ATTRIB_PARENT_BEFORE, parent, b, GLW_ATTRIB_PROPROOTS, p, sc->sc_originating_prop, @@ -1244,6 +1254,7 @@ clone_free(clone_t *c) glw_signal_handler_unregister(w, cloner_sig_handler, c); LIST_REMOVE(c, c_link); prop_ref_dec(c->c_prop); + prop_destroy(c->c_clone_root); free(c); } @@ -1605,15 +1616,26 @@ subscribe_prop(glw_view_eval_context_t *ec, struct token *self, int type) token_t *t; const char *propname[16]; prop_callback_t *cb; + prop_t *prop = NULL; if(w == NULL) return glw_view_seterr(ec->ei, self, "Properties can not be mapped in this scope"); - - for(t = self; t != NULL && i < 15; t = t->child) - propname[i++] = rstr_get(t->t_rstring); - propname[i] = NULL; + switch(self->type) { + case TOKEN_PROPERTY_VALUE_NAME: + for(t = self; t != NULL && i < 15; t = t->child) + propname[i++] = rstr_get(t->t_rstring); + propname[i] = NULL; + break; + + case TOKEN_PROPERTY_REF: + prop = self->t_prop; + break; + + default: + abort(); + } switch(type) { @@ -1632,9 +1654,9 @@ subscribe_prop(glw_view_eval_context_t *ec, struct token *self, int type) if(ec->prop != NULL) prop_ref_inc(ec->prop); - sc->sc_view_prop = ec->prop_view; - if(ec->prop_view != NULL) - prop_ref_inc(ec->prop_view); + sc->sc_view_prop = ec->prop_viewx; + if(ec->prop_viewx != NULL) + prop_ref_inc(ec->prop_viewx); sc->sc_view_args = ec->prop_args; if(ec->prop_args != NULL) @@ -1669,16 +1691,28 @@ subscribe_prop(glw_view_eval_context_t *ec, struct token *self, int type) if(ec->debug || ec->w->glw_flags & GLW_DEBUG) f |= PROP_SUB_DEBUG; - s = prop_subscribe(f, - PROP_TAG_CALLBACK, cb, gps, - PROP_TAG_NAME_VECTOR, propname, - PROP_TAG_COURIER, w->glw_root->gr_courier, - PROP_TAG_NAMED_ROOT, ec->prop, "self", - PROP_TAG_NAMED_ROOT, ec->prop_parent, "parent", - PROP_TAG_NAMED_ROOT, ec->prop_view, "view", - PROP_TAG_NAMED_ROOT, ec->prop_args, "args", - PROP_TAG_ROOT, w->glw_root->gr_uii.uii_prop, - NULL); + if(prop != NULL) { + + s = prop_subscribe(f, + PROP_TAG_CALLBACK, cb, gps, + PROP_TAG_COURIER, w->glw_root->gr_courier, + PROP_TAG_ROOT, prop, + NULL); + + } else { + + s = prop_subscribe(f, + PROP_TAG_CALLBACK, cb, gps, + PROP_TAG_NAME_VECTOR, propname, + PROP_TAG_COURIER, w->glw_root->gr_courier, + PROP_TAG_NAMED_ROOT, ec->prop, "self", + PROP_TAG_NAMED_ROOT, ec->prop_parent, "parent", + PROP_TAG_NAMED_ROOT, ec->prop_viewx, "view", + PROP_TAG_NAMED_ROOT, ec->prop_args, "args", + PROP_TAG_NAMED_ROOT, ec->prop_clone, "clone", + PROP_TAG_ROOT, w->glw_root->gr_uii.uii_prop, + NULL); + } gps->gps_sub = s; @@ -1687,13 +1721,14 @@ subscribe_prop(glw_view_eval_context_t *ec, struct token *self, int type) gps->gps_rpn = ec->passive_subscriptions ? NULL : ec->rpn; - rstr_release(self->t_rstring); - self->propsubr = gps; + if(self->type == TOKEN_PROPERTY_VALUE_NAME) { + rstr_release(self->t_rstring); + glw_view_free_chain(self->child); + self->child = NULL; + } + self->propsubr = gps; self->type = TOKEN_PROPERTY_SUBSCRIPTION; - - glw_view_free_chain(self->child); - self->child = NULL; return 0; } @@ -1860,7 +1895,8 @@ glw_view_eval_rpn(token_t *t, glw_view_eval_context_t *pec, int *copyp) ec.ei = pec->ei; ec.prop = pec->prop; ec.prop_parent = pec->prop_parent; - ec.prop_view = pec->prop_view; + ec.prop_viewx = pec->prop_viewx; + ec.prop_clone = pec->prop_clone; ec.prop_args = pec->prop_args; ec.w = pec->w; ec.rpn = t; @@ -1967,7 +2003,8 @@ glwf_widget(glw_view_eval_context_t *ec, struct token *self, memset(&n, 0, sizeof(n)); n.prop = ec->prop; n.prop_parent = ec->prop_parent; - n.prop_view = ec->prop_view; + n.prop_viewx = ec->prop_viewx; + n.prop_clone = ec->prop_clone; n.prop_args = ec->prop_args; n.ei = ec->ei; n.gr = ec->gr; @@ -2014,7 +2051,7 @@ glwf_cloner(glw_view_eval_context_t *ec, struct token *self, if((a = token_resolve_ex(ec, a, GPS_CLONER)) == NULL) return -1; - + if(b->type != TOKEN_IDENTIFIER) return glw_view_seterr(ec->ei, self, "cloner: Invalid second argument, " @@ -2128,6 +2165,7 @@ typedef struct glw_event_map_eval_block { prop_t *prop_parent; prop_t *prop_view; prop_t *prop_args; + prop_t *prop_clone; } glw_event_map_eval_block_t; @@ -2147,7 +2185,8 @@ glw_event_map_eval_block_fire(glw_t *w, glw_event_map_t *gem, event_t *src) memset(&n, 0, sizeof(n)); n.prop = b->prop; n.prop_parent = b->prop_parent; - n.prop_view = b->prop_view; + n.prop_viewx = b->prop_view; + n.prop_clone = b->prop_clone; n.prop_args = b->prop_args; n.gr = w->glw_root; n.w = w; @@ -2183,6 +2222,9 @@ glw_event_map_eval_block_dtor(glw_event_map_t *gem) if(b->prop_args) prop_ref_dec(b->prop_args); + if(b->prop_clone) + prop_ref_dec(b->prop_clone); + free(b); } @@ -2207,10 +2249,14 @@ glw_event_map_eval_block_create(glw_view_eval_context_t *ec, if(b->prop_parent) prop_ref_inc(b->prop_parent); - b->prop_view = ec->prop_view; + b->prop_view = ec->prop_viewx; if(b->prop_view) prop_ref_inc(b->prop_view); + b->prop_clone = ec->prop_clone; + if(b->prop_clone) + prop_ref_inc(b->prop_clone); + b->prop_args = ec->prop_args; if(b->prop_args) prop_ref_inc(b->prop_args); @@ -3290,15 +3336,16 @@ glwf_bind(glw_view_eval_context_t *ec, struct token *self, propname[i++] = rstr_get(t->t_rstring); propname[i] = NULL; - glw_set_i(ec->w, GLW_ATTRIB_BIND_TO_PROPERTY, - ec->prop, propname, ec->prop_view, ec->prop_args, NULL); + glw_set_i(ec->w, GLW_ATTRIB_BIND_TO_PROPERTY5, + ec->prop, propname, ec->prop_viewx, ec->prop_args, + ec->prop_clone, NULL); } else if(a != NULL && a->type == TOKEN_STRING) { glw_set_i(ec->w, GLW_ATTRIB_BIND_TO_ID, rstr_get(a->t_rstring), NULL); } else { - glw_set_i(ec->w, GLW_ATTRIB_BIND_TO_PROPERTY, - NULL, NULL, NULL, NULL, NULL); + glw_set_i(ec->w, GLW_ATTRIB_BIND_TO_PROPERTY5, + NULL, NULL, NULL, NULL, NULL, NULL); } return 0; } @@ -3343,7 +3390,8 @@ glwf_delta(glw_view_eval_context_t *ec, struct token *self, p = prop_get_by_name(propname, 0, PROP_TAG_NAMED_ROOT, ec->prop, "self", PROP_TAG_NAMED_ROOT, ec->prop_parent, "parent", - PROP_TAG_NAMED_ROOT, ec->prop_view, "view", + PROP_TAG_NAMED_ROOT, ec->prop_viewx, "view", + PROP_TAG_NAMED_ROOT, ec->prop_clone, "clone", PROP_TAG_NAMED_ROOT, ec->prop_args, "args", PROP_TAG_ROOT, ec->w->glw_root->gr_uii.uii_prop, NULL); @@ -3622,7 +3670,7 @@ glwf_browse(glw_view_eval_context_t *ec, struct token *self, * */ static void -glwf_setting_ctor(struct token *self) +glwf_null_ctor(struct token *self) { self->t_extra = NULL; } @@ -3941,6 +3989,98 @@ glwf_count(glw_view_eval_context_t *ec, struct token *self, } +/** + * + */ +static void +glwf_propGrouper_dtor(struct token *self) +{ + if(self->t_extra != NULL) + prop_grouper_destroy(self->t_extra); +} + + + +/** + * + */ +static int +glwf_propGrouper(glw_view_eval_context_t *ec, struct token *self, + token_t **argv, unsigned int argc) +{ + token_t *a, *b, *r; + + if((a = resolve_property_name2(ec, argv[0])) == NULL) + return -1; + if((b = token_resolve(ec, argv[1])) == NULL) + return -1; + + if(b->type != TOKEN_STRING) + return glw_view_seterr(ec->ei, a, "propGrouper(): " + "Second argument is not a string"); + + if(self->t_extra != NULL) + prop_grouper_destroy(self->t_extra); + + r = eval_alloc(self, ec, TOKEN_PROPERTY_REF); + r->t_prop = prop_create(NULL, NULL); + prop_ref_inc(r->t_prop); + ec->dynamic_eval |= GLW_VIEW_DYNAMIC_KEEP; + eval_push(ec, r); + + self->t_extra = prop_grouper_create(r->t_prop, a->t_prop, + rstr_get(b->t_rstring), + PROP_GROUPER_TAKE_DST_OWNERSHIP); + return 0; +} + + +/** + * + */ +static void +glwf_propSorter_dtor(struct token *self) +{ + if(self->t_extra != NULL) + prop_nf_release(self->t_extra); +} + + +/** + * + */ +static int +glwf_propSorter(glw_view_eval_context_t *ec, struct token *self, + token_t **argv, unsigned int argc) +{ + token_t *a, *b, *r; + + if((a = resolve_property_name2(ec, argv[0])) == NULL) + return -1; + if((b = token_resolve(ec, argv[1])) == NULL) + return -1; + + if(b->type != TOKEN_STRING) + return glw_view_seterr(ec->ei, a, "propSorter(): " + "Second argument is not a string"); + + if(self->t_extra != NULL) + prop_nf_release(self->t_extra); + + r = eval_alloc(self, ec, TOKEN_PROPERTY_REF); + r->t_prop = prop_create(NULL, NULL); + prop_ref_inc(r->t_prop); + ec->dynamic_eval |= GLW_VIEW_DYNAMIC_KEEP; + eval_push(ec, r); + + self->t_extra = prop_nf_create(r->t_prop, a->t_prop, NULL, + rstr_get(b->t_rstring), + PROP_GROUPER_TAKE_DST_OWNERSHIP); + return 0; +} + + + /** * */ @@ -3980,8 +4120,8 @@ static const token_func_t funcvec[] = { {"select", 3, glwf_select}, {"trace", 2, glwf_trace}, {"browse", 1, glwf_browse, glwf_browse_ctor, glwf_browse_dtor}, - {"settingInt", 8, glw_settingInt, glwf_setting_ctor, glwf_setting_dtor}, - {"settingBool", 4, glw_settingBool, glwf_setting_ctor, glwf_setting_dtor}, + {"settingInt", 8, glw_settingInt, glwf_null_ctor, glwf_setting_dtor}, + {"settingBool", 4, glw_settingBool, glwf_null_ctor, glwf_setting_dtor}, {"isLink", 1, glwf_isLink}, {"sin", 1, glwf_sin}, {"monotime", 0, glwf_monotime}, @@ -3991,6 +4131,8 @@ static const token_func_t funcvec[] = { {"focusDistance", 0, glwf_focusDistance}, {"count", 1, glwf_count}, {"deliverEvent", -1, glwf_deliverEvent}, + {"propGrouper", 2, glwf_propGrouper, glwf_null_ctor, glwf_propGrouper_dtor}, + {"propSorter", 2, glwf_propSorter, glwf_null_ctor, glwf_propSorter_dtor}, };