int search_class_create(prop_t *parent, prop_t **nodesp, prop_t **entriesp, const char *title, const char *icon) { prop_t *p = prop_create_root(NULL); prop_t *m = prop_create(p, "metadata"); prop_t *n, *e; char url[URL_MAX]; backend_prop_make(p, url, sizeof(url)); prop_set_string(prop_create(p, "url"), url); prop_set_string(prop_create(m, "title"), title); if(icon != NULL) prop_set_string(prop_create(m, "icon"), icon); prop_set_string(prop_create(p, "type"), "directory"); n = prop_create(p, "nodes"); e = prop_create(p, "entries"); prop_set_int(e, 0); *nodesp = prop_ref_inc(n); *entriesp = prop_ref_inc(e); if(prop_set_parent(p, parent)) { prop_destroy(p); return 1; } return 0; }
static prop_t * add_news_locked(const char *id, const char *message, const char *location, const char *caption, const char *action) { prop_t *p, *ret = NULL; prop_t *root = prop_create(prop_get_global(), "news"); if(dismissed_news_out != NULL) { if(htsmsg_get_u32_or_default(dismissed_news_in, id, 0)) { dismis_news(id); } else { p = prop_create_root(id); prop_set(p, "message", PROP_SET_STRING, message); prop_set(p, "id", PROP_SET_STRING, id); prop_set(p, "location", PROP_SET_STRING, location); prop_set(p, "caption", PROP_SET_STRING, caption); prop_set(p, "action", PROP_SET_STRING, action); prop_subscribe(PROP_SUB_TRACK_DESTROY, PROP_TAG_CALLBACK, news_sink, prop_ref_inc(p), PROP_TAG_ROOT, prop_create(p, "eventSink"), PROP_TAG_MUTEX, &news_mutex, NULL); ret = prop_ref_inc(p); if(prop_set_parent(p, root)) prop_destroy(p); } } return ret; }
prop_t * add_news(const char *message, const char *location, const char *caption) { prop_t *p, *ret = NULL; prop_t *root = prop_create(prop_get_global(), "news"); hts_mutex_lock(&news_mutex); if(dismissed_news_out != NULL) { if(htsmsg_get_u32_or_default(dismissed_news_in, message, 0)) { dismis_news(message); } else { p = prop_create_root(NULL); prop_set_string(prop_create(p, "message"), message); prop_set_string(prop_create(p, "location"), location); prop_set_string(prop_create(p, "caption"), caption); prop_subscribe(PROP_SUB_TRACK_DESTROY, PROP_TAG_CALLBACK, news_sink, prop_ref_inc(p), PROP_TAG_ROOT, prop_create(p, "eventSink"), PROP_TAG_MUTEX, &news_mutex, NULL); ret = prop_ref_inc(p); if(prop_set_parent(p, root)) prop_destroy(p); } } hts_mutex_unlock(&news_mutex); return ret; }
static setting_t * setting_create_leaf(prop_t *parent, prop_t *title, const char *type, const char *valuename, int flags) { setting_t *s = calloc(1, sizeof(setting_t)); s->s_root = prop_ref_inc(setting_add(parent, title, type, flags)); s->s_val = prop_ref_inc(prop_create(s->s_root, valuename)); return s; }
void * notify_add(prop_t *root, notify_type_t type, const char *icon, int delay, rstr_t *fmt, ...) { char msg[256]; prop_t *p; const char *typestr; int tl; va_list ap, apx; switch(type) { case NOTIFY_INFO: typestr = "info"; tl = TRACE_INFO; break; case NOTIFY_WARNING: typestr = "warning"; tl = TRACE_INFO; break; case NOTIFY_ERROR: typestr = "error"; tl = TRACE_ERROR; break; default: return NULL; } va_start(ap, fmt); va_copy(apx, ap); tracev(0, tl, "notify", rstr_get(fmt), ap); vsnprintf(msg, sizeof(msg), rstr_get(fmt), apx); va_end(ap); va_end(apx); rstr_release(fmt); p = prop_create_root(NULL); prop_set_string(prop_create(p, "text"), msg); prop_set_string(prop_create(p, "type"), typestr); if(icon != NULL) prop_set_string(prop_create(p, "icon"), icon); p = prop_ref_inc(p); if(prop_set_parent(p, root ?: notify_prop_entries)) prop_destroy(p); if(delay != 0) { prop_t *r = NULL; if(delay < 0) { r = prop_ref_inc(p); delay = -delay; } callout_arm(NULL, notify_timeout, p, delay); return r; } return p; }
event_t * event_create_openurl(const char *url, const char *view, prop_t *origin, prop_t *model, const char *how) { event_openurl_t *e = event_create(EVENT_OPENURL, sizeof(event_openurl_t)); e->url = url ? strdup(url) : NULL; e->view = view ? strdup(view) : NULL; e->origin = prop_ref_inc(origin); e->model = prop_ref_inc(model); e->how = how ? strdup(how) : NULL; e->h.e_dtor = event_openurl_dtor; return &e->h; }
event_t * event_create_openurl_args(const event_openurl_args_t *args) { event_openurl_t *e = event_create(EVENT_OPENURL, sizeof(event_openurl_t)); e->h.e_dtor = event_openurl_dtor; e->url = args->url ? strdup(args->url) : NULL; e->view = args->view ? strdup(args->view) : NULL; e->item_model = prop_ref_inc(args->item_model); e->parent_model = prop_ref_inc(args->parent_model); e->how = args->how ? strdup(args->how) : NULL; e->parent_url = args->parent_url ? strdup(args->parent_url) : NULL; return &e->h; }
static void prop_callback(void *opaque, prop_event_t event, ...) { glw_keyintercept_t *ki = opaque; const char *str; prop_t *p; int c; va_list ap; va_start(ap, event); switch(event) { case PROP_SET_VOID: ki->buflen = 0; str = NULL; p = va_arg(ap, prop_t *); break; case PROP_SET_RSTRING: str = rstr_get(va_arg(ap, const rstr_t *)); ki->buflen = 0; while((c = utf8_get(&str)) != 0 && ki->buflen < 64) ki->buf[ki->buflen++] = c; p = va_arg(ap, prop_t *); break; default: return; } prop_ref_dec(ki->prop); ki->prop = prop_ref_inc(p); }
static int be_prop_open(prop_t *page, const char *url) { proppage_t *pp; openpage_t *op; hts_mutex_lock(&pp_mutex); LIST_FOREACH(pp, &proppages, pp_link) if(!strcmp(rstr_get(pp->pp_url), url)) break; if(pp == NULL) { hts_mutex_unlock(&pp_mutex); return 1; } op = calloc(1, sizeof(openpage_t)); LIST_INSERT_HEAD(&pp->pp_pages, op, op_link); op->op_pp = pp; op->op_root = prop_ref_inc(page); op->op_page_sub = prop_subscribe(PROP_SUB_TRACK_DESTROY, PROP_TAG_CALLBACK, op_cb, op, PROP_TAG_MUTEX, &pp_mutex, PROP_TAG_ROOT, page, NULL); prop_link(pp->pp_model, prop_create(page, "model")); hts_mutex_unlock(&pp_mutex); return 0; }
rstr_t * backend_prop_make(prop_t *model, const char *suggest) { proppage_t *pp; rstr_t *r; hts_mutex_lock(&pp_mutex); pp = calloc(1, sizeof(proppage_t)); if(suggest == NULL) { char url[50]; pp_tally++; snprintf(url, sizeof(url), "prop:%d", pp_tally); r = rstr_alloc(url); } else { r = rstr_alloc(suggest); } pp->pp_url = rstr_dup(r); pp->pp_model = prop_ref_inc(model); pp->pp_model_sub = prop_subscribe(PROP_SUB_TRACK_DESTROY, PROP_TAG_CALLBACK, pp_cb, pp, PROP_TAG_MUTEX, &pp_mutex, PROP_TAG_ROOT, model, NULL); LIST_INSERT_HEAD(&proppages, pp, pp_link); hts_mutex_unlock(&pp_mutex); return r; }
static void prop_callback(void *opaque, prop_event_t event, ...) { glw_text_bitmap_t *gtb = opaque; const char *caption; prop_t *p; prop_str_type_t type = 0; va_list ap; va_start(ap, event); switch(event) { case PROP_SET_VOID: caption = NULL; p = va_arg(ap, prop_t *); break; case PROP_SET_RSTRING: caption = rstr_get(va_arg(ap, const rstr_t *)); p = va_arg(ap, prop_t *); type = va_arg(ap, prop_str_type_t); break; default: return; } prop_ref_dec(gtb->gtb_p); gtb->gtb_p = prop_ref_inc(p); caption_set_internal(gtb, caption, type); }
void backend_prop_make(prop_t *model, char *url, size_t urllen) { proppage_t *pp; hts_mutex_lock(&pp_mutex); pp = malloc(sizeof(proppage_t)); pp_tally++; snprintf(url, urllen, "prop:%d", pp_tally); pp->pp_url = strdup(url); pp->pp_model = prop_ref_inc(model); pp->pp_model_sub = prop_subscribe(PROP_SUB_TRACK_DESTROY, PROP_TAG_CALLBACK, pp_cb, pp, PROP_TAG_MUTEX, &pp_mutex, PROP_TAG_ROOT, model, NULL); LIST_INSERT_HEAD(&proppages, pp, pp_link); hts_mutex_unlock(&pp_mutex); }
static void notifications_update(void *opaque, prop_event_t event, ...) { statusbar_t *sb = opaque; prop_t *p, *txt; statusbar_entry_t *sbe; char *buf; rstr_t *msg; int i, l; va_list ap; va_start(ap, event); switch(event) { case PROP_ADD_CHILD: p = va_arg(ap, prop_t *); txt = prop_get_by_name(PNVEC("self", "text"), 1, PROP_TAG_NAMED_ROOT, p, "self", NULL); if(txt != NULL) { msg = prop_get_string(txt); if(msg != NULL) { buf = mystrdupa(rstr_get(msg)); l = strlen(buf); for(i = 0; i < l; i++) if(buf[i] < ' ') buf[i] = ' '; sbe = calloc(1, sizeof(statusbar_entry_t)); sbe->p = prop_ref_inc(p); sbe->id = gtk_statusbar_push(GTK_STATUSBAR(sb->bar), sb->ctxid, buf); LIST_INSERT_HEAD(&sb->entries, sbe, link); rstr_release(msg); } prop_ref_dec(txt); } break; case PROP_DEL_CHILD: p = va_arg(ap, prop_t *); LIST_FOREACH(sbe, &sb->entries, link) if(sbe->p == p) break; if(sbe == NULL) break; prop_ref_dec(sbe->p); gtk_statusbar_remove(GTK_STATUSBAR(sb->bar), sb->ctxid, sbe->id); LIST_REMOVE(sbe, link); free(sbe); break; default: break; } }
JSObject * js_object_from_prop(JSContext *cx, prop_t *p) { JSObject *obj = JS_NewObjectWithGivenProto(cx, &prop_bridge_class, NULL, NULL); JS_SetPrivate(cx, obj, prop_ref_inc(p)); return obj; }
event_t * event_create_prop(event_type_t type, prop_t *p) { event_prop_t *e = event_create(type, sizeof(event_prop_t)); e->p = prop_ref_inc(p); e->h.e_dtor = event_prop_dtor; return &e->h; }
static setting_t * setting_create_leaf(prop_t *parent, prop_t *title, const char *type, const char *valuename) { setting_t *s = calloc(1, sizeof(setting_t)); s->s_root = setting_add(prop_create(parent, "model"), title, type); s->s_model = prop_create(s->s_root, "model"); s->s_val = prop_ref_inc(prop_create(s->s_model, valuename)); return s; }
event_t * event_create_prop_action(prop_t *p, rstr_t *action) { event_prop_action_t *e = event_create(EVENT_PROP_ACTION, sizeof(event_prop_action_t)); e->p = prop_ref_inc(p); e->action = rstr_dup(action); e->h.e_dtor = event_prop_action_dtor; return &e->h; }
int message_popup(const char *message, int flags, const char **extra) { prop_t *p; int rval; p = prop_ref_inc(prop_create_root(NULL)); TRACE(TRACE_DEBUG, "Notification", "%s", message); prop_set_string(prop_create(p, "type"), "message"); prop_set_string_ex(prop_create(p, "message"), NULL, message, flags & MESSAGE_POPUP_RICH_TEXT ? PROP_STR_RICH : PROP_STR_UTF8); if(extra) { int cnt = 1; prop_t *btns = prop_create(p, "buttons"); while(*extra) { prop_t *b = prop_create_root(NULL); prop_set_string(prop_create(b, "title"), *extra); char action[10]; snprintf(action, sizeof(action), "btn%d", cnt); prop_set_string(prop_create(b, "action"), action); if(prop_set_parent(b, btns)) abort(); cnt++; extra++; } } if(flags & MESSAGE_POPUP_CANCEL) prop_set_int(prop_create(p, "cancel"), 1); if(flags & MESSAGE_POPUP_OK) prop_set_int(prop_create(p, "ok"), 1); event_t *e = popup_display(p); prop_destroy(p); prop_ref_dec(p); const event_payload_t *ep = (const event_payload_t *)e; if(event_is_action(e, ACTION_OK)) rval = MESSAGE_POPUP_OK; else if(event_is_action(e, ACTION_CANCEL)) rval = MESSAGE_POPUP_CANCEL; else if(event_is_type(e, EVENT_DYNAMIC_ACTION) && !strncmp(ep->payload, "btn", 3)) rval = atoi(ep->payload + 3); else rval = 0; event_release(e); return rval; }
static JSBool js_appendItem0(JSContext *cx, js_model_t *model, prop_t *parent, const char *url, const char *type, JSObject *metaobj, jsval *data, jsval *rval, int enabled, const char *metabind) { prop_t *item = prop_create_root(NULL); if(url != NULL) prop_set_string(prop_create(item, "url"), url); if(data != NULL) js_prop_set_from_jsval(cx, prop_create(item, "data"), *data); *rval = JSVAL_VOID; if(metabind != NULL) metadb_bind_url_to_prop(NULL, metabind, item); if(type != NULL) { prop_set_string(prop_create(item, "type"), type); if(metaobj) js_prop_from_object(cx, metaobj, prop_create(item, "metadata")); } else if(url != NULL) { if(backend_resolve_item(url, item)) { prop_destroy(item); return JS_TRUE; } } prop_set_int(prop_create(item, "enabled"), enabled); prop_t *p = prop_ref_inc(item); if(prop_set_parent(item, parent)) { prop_destroy(item); prop_ref_dec(p); } else { JSObject *robj = JS_NewObjectWithGivenProto(cx, &item_class, JSVAL_TO_OBJECT(model->jm_item_proto), NULL); *rval = OBJECT_TO_JSVAL(robj); js_item_t *ji = calloc(1, sizeof(js_item_t)); ji->ji_model = model; ji->ji_root = p; LIST_INSERT_HEAD(&model->jm_items, ji, ji_link); JS_SetPrivate(cx, robj, ji); ji->ji_enable_set_property = 1; } return JS_TRUE; }
prop_vec_t *prop_vec_append(prop_vec_t *pv, prop_t *p) { assert(atomic_get(&pv->pv_refcount) == 1); if(pv->pv_length == pv->pv_capacity) { pv->pv_capacity++; pv = realloc(pv, sizeof(prop_vec_t) + sizeof(prop_t *) * pv->pv_capacity); } assert(pv->pv_length < pv->pv_capacity); pv->pv_vec[pv->pv_length] = prop_ref_inc(p); pv->pv_length++; return pv; }
event_t * event_create_playurl_args(const event_playurl_args_t *args) { event_playurl_t *ep = event_create(EVENT_PLAY_URL, sizeof(event_playurl_t)); ep->h.e_dtor = event_playurl_dtor; ep->url = strdup(args->url); ep->how = args->how ? strdup(args->how) : NULL; ep->item_model = prop_xref_addref(args->item_model); ep->parent_model = prop_ref_inc(args->parent_model); ep->primary = args->primary; ep->priority = args->priority; ep->no_audio = args->no_audio; ep->parent_url = args->parent_url ? strdup(args->parent_url) : NULL; return &ep->h; }
static void * video_player_idle(void *aux) { video_playback_t *vp = aux; int run = 1; event_t *e = NULL, *next; media_pipe_t *mp = vp->vp_mp; char errbuf[256]; prop_t *errprop = prop_ref_inc(prop_create(mp->mp_prop_root, "error")); while(run) { if(e == NULL) e = mp_dequeue_event(mp); if(event_is_type(e, EVENT_PLAY_URL)) { prop_set_void(errprop); event_playurl_t *ep = (event_playurl_t *)e; int flags = 0; if(ep->primary) flags |= BACKEND_VIDEO_PRIMARY; if(ep->no_audio) flags |= BACKEND_VIDEO_NO_AUDIO; next = backend_play_video(ep->url, mp, flags, ep->priority, errbuf, sizeof(errbuf)); if(next == NULL) { notify_add(NOTIFY_ERROR, NULL, 5, "URL: %s\nError: %s", ep->url, errbuf); prop_set_string(errprop, errbuf); } event_release(e); e = next; continue; } else if(event_is_type(e, EVENT_EXIT)) { event_release(e); break; } event_release(e); e = NULL; } prop_ref_dec(errprop); return NULL; }
event_t * event_create_playurl(const char *url, int primary, int priority, int no_audio, prop_t *model, const char *how, prop_t *origin, const char *parent_url) { event_playurl_t *ep = event_create(EVENT_PLAY_URL, sizeof(event_playurl_t)); ep->url = strdup(url); ep->how = how ? strdup(how) : NULL; ep->model = prop_xref_addref(model); ep->origin = prop_ref_inc(origin); ep->primary = primary; ep->priority = priority; ep->no_audio = no_audio; ep->parent_url = parent_url ? strdup(parent_url) : NULL; ep->h.e_dtor = event_playurl_dtor; return &ep->h; }
JSBool js_createPageOptions(JSContext *cx, JSObject *page, const char *url, prop_t *options) { js_setting_group_t *jsg = calloc(1, sizeof(js_setting_group_t)); JSObject *robj; jsg->jsg_refcount = 1; jsg->jsg_frozen = 1; jsg->jsg_kv_url = strdup(url); jsg->jsg_root = prop_ref_inc(options); jsg->jsg_root_owner = 0; jsg->jsg_settings_flags = SETTINGS_RAW_NODES; robj = JS_DefineObject(cx, page, "options", &page_options_class, NULL, 0); JS_SetPrivate(cx, robj, jsg); JS_DefineFunctions(cx, robj, setting_functions); jsg->jsg_frozen = 0; return JS_TRUE; }
static void dyn_menu_item_add(gtk_ui_t *gu, dyn_menu_t *dm, prop_t *p, dyn_menu_item_t *dmi, dyn_menu_item_t *before, int position) { dmi->dmi_dm = dm; dmi->dmi_prop = prop_ref_inc(p); dmi->dmi_type_sub = prop_subscribe(0, PROP_TAG_NAME("node", "type"), PROP_TAG_CALLBACK_STRING, dmi_set_type, dmi, PROP_TAG_NAMED_ROOT, p, "node", PROP_TAG_COURIER, gu->gu_pc, NULL); dmi->dmi_title_sub = prop_subscribe(0, PROP_TAG_NAME("node", "metadata", "title"), PROP_TAG_CALLBACK_STRING, dmi_set_title, dmi, PROP_TAG_NAMED_ROOT, p, "node", PROP_TAG_COURIER, gu->gu_pc, NULL); dmi->dmi_enabled_sub = prop_subscribe(0, PROP_TAG_NAME("node", "enabled"), PROP_TAG_CALLBACK_INT, dmi_set_enabled, dmi, PROP_TAG_NAMED_ROOT, p, "node", PROP_TAG_COURIER, gu->gu_pc, NULL); dmi->dmi_value_sub = prop_subscribe(0, PROP_TAG_NAME("node", "value"), PROP_TAG_CALLBACK_INT, dmi_set_value, dmi, PROP_TAG_NAMED_ROOT, p, "node", PROP_TAG_COURIER, gu->gu_pc, NULL); }
static void locatedb_search(prop_t *model, const char *query) { if (!locatedb_enabled) return; fa_search_t *fas = calloc(1, sizeof(*fas)); char *s; /* Convery query to lower-case to provide case-insensitive search. */ fas->fas_query = s = strdup(query); do { *s = tolower((int)*s); } while (*++s); fas->fas_run = 1; fas->fas_nodes = prop_ref_inc(prop_create(model, "nodes")); hts_thread_create_detached("fa search", fa_searcher, fas, THREAD_PRIO_NORMAL); }
JSBool js_createSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { const char *title; const char *icon = NULL; const char *desc = NULL; char spath[URL_MAX]; if(!JS_ConvertArguments(cx, argc, argv, "s/ss", &title, &icon, &desc)) return JS_FALSE; js_plugin_t *jsp = JS_GetPrivate(cx, obj); snprintf(spath, sizeof(spath), "plugins/%s", jsp->jsp_id); js_setting_group_t *jsg = calloc(1, sizeof(js_setting_group_t)); JSObject *robj; jsg->jsg_refcount = 2; LIST_INSERT_HEAD(&jsp->jsp_setting_groups, jsg, jsg_link); jsg->jsg_frozen = 1; jsg->jsg_spath = strdup(spath); jsg->jsg_store = htsmsg_store_load(spath) ?: htsmsg_create_map(); jsg->jsg_root_owner = 1; jsg->jsg_root = prop_ref_inc(settings_add_dir_cstr(gconf.settings_apps, title, NULL, icon, desc, NULL)); robj = JS_NewObjectWithGivenProto(cx, &setting_group_class, NULL, obj); jsg->jsg_val = *rval = OBJECT_TO_JSVAL(robj); JS_AddNamedRoot(cx, &jsg->jsg_val, "jsg"); JS_SetPrivate(cx, robj, jsg); JS_DefineFunctions(cx, robj, setting_functions); jsval val = OBJECT_TO_JSVAL(js_object_from_prop(cx, jsg->jsg_root)); JS_SetProperty(cx, robj, "properties", &val); jsg->jsg_frozen = 0; return JS_TRUE; }
int text_dialog(const char *message, char **answer, int flags) { rstr_t *r; *answer = NULL; prop_t *p = prop_ref_inc(prop_create_root(NULL)); prop_set_string(prop_create(p, "type"), "textDialog"); prop_set_string_ex(prop_create(p, "message"), NULL, message, flags & MESSAGE_POPUP_RICH_TEXT ? PROP_STR_RICH : PROP_STR_UTF8); prop_t *string = prop_create(p, "input"); if(flags & MESSAGE_POPUP_CANCEL) prop_set_int(prop_create(p, "cancel"), 1); if(flags & MESSAGE_POPUP_OK) prop_set_int(prop_create(p, "ok"), 1); event_t *e = popup_display(p); if(event_is_action(e, ACTION_OK)) { r = prop_get_string(string, NULL); if(r) *answer = strdup(rstr_get(r)); rstr_release(r); } prop_destroy(p); prop_ref_dec(p); if(event_is_action(e, ACTION_CANCEL)) { event_release(e); return -1; } event_release(e); return 0; }
static gu_nav_page_t * gu_nav_page_create(gu_tab_t *gt, prop_t *p) { gu_nav_page_t *gnp = calloc(1, sizeof(gu_nav_page_t)); gtk_ui_t *gu = gt->gt_gw->gw_gu; gnp->gnp_gt = gt; gnp->gnp_prop = prop_ref_inc(p); gnp->gnp_pagebin = gtk_vbox_new(FALSE, 0); gtk_widget_show(gnp->gnp_pagebin); gtk_container_set_border_width(GTK_CONTAINER(gnp->gnp_pagebin), 0); gtk_notebook_append_page(GTK_NOTEBOOK(gt->gt_notebook), gnp->gnp_pagebin, NULL); LIST_INSERT_HEAD(>->gt_pages, gnp, gnp_link); gnp->gnp_sub_type = prop_subscribe(0, PROP_TAG_NAME("self", "model", "type"), PROP_TAG_CALLBACK_STRING, gu_nav_page_set_type, gnp, PROP_TAG_COURIER, gu->gu_pc, PROP_TAG_NAMED_ROOT, p, "self", NULL); gnp->gnp_sub_url = prop_subscribe(0, PROP_TAG_NAME("self", "url"), PROP_TAG_CALLBACK_STRING, gu_nav_page_set_url, gnp, PROP_TAG_COURIER, gu->gu_pc, PROP_TAG_NAMED_ROOT, p, "self", NULL); g_signal_connect(gnp->gnp_pagebin, "destroy", G_CALLBACK(gnp_dtor), gnp); return gnp; }
static JSBool js_appendItem0(JSContext *cx, js_model_t *model, prop_t *parent, const char *url, const char *type, JSObject *metaobj, jsval *data, jsval *rval, int enabled, const char *metabind) { install_nodesub(model); prop_t *item = prop_create_root(NULL); rstr_t *rurl = url ? rstr_alloc(url) : NULL; if(url != NULL) prop_set(item, "url", PROP_SET_RSTRING, rurl); if(data != NULL) js_prop_set_from_jsval(cx, prop_create(item, "data"), *data); *rval = JSVAL_VOID; if(metabind != NULL) playinfo_bind_url_to_prop(metabind, item); if(type != NULL) { prop_set_string(prop_create(item, "type"), type); if(metaobj) js_prop_from_object(cx, metaobj, prop_create(item, "metadata")); } else if(url != NULL) { if(backend_resolve_item(url, item)) { prop_destroy(item); rstr_release(rurl); return JS_TRUE; } } prop_set_int(prop_create(item, "enabled"), enabled); prop_t *p = prop_ref_inc(item); if(prop_set_parent(item, parent)) { prop_destroy(item); prop_ref_dec(p); } else { JSObject *robj = JS_NewObjectWithGivenProto(cx, &item_class, JSVAL_TO_OBJECT(model->jm_item_proto), NULL); *rval = OBJECT_TO_JSVAL(robj); js_item_t *ji = calloc(1, sizeof(js_item_t)); atomic_add(&model->jm_refcount, 1); ji->ji_url = rstr_dup(rurl); ji->ji_model = model; ji->ji_root = p; TAILQ_INSERT_TAIL(&model->jm_items, ji, ji_link); JS_SetPrivate(cx, robj, ji); ji->ji_enable_set_property = 1; ji->ji_eventsub = prop_subscribe(PROP_SUB_TRACK_DESTROY, PROP_TAG_CALLBACK, js_item_eventsub, ji, PROP_TAG_ROOT, ji->ji_root, PROP_TAG_COURIER, model->jm_pc, NULL); model->jm_subs++; ji->ji_this = OBJECT_TO_JSVAL(robj); JS_AddNamedRoot(cx, &ji->ji_this, "item_this"); prop_tag_set(ji->ji_root, model, ji); } rstr_release(rurl); return JS_TRUE; }