diff --git a/src/api/api_idnode.c b/src/api/api_idnode.c index b7d56d51..997b8742 100644 --- a/src/api/api_idnode.c +++ b/src/api/api_idnode.c @@ -57,7 +57,7 @@ static void api_idnode_grid_conf ( htsmsg_t *args, api_idnode_grid_conf_t *conf ) { - htsmsg_field_t *f; + htsmsg_field_t *f, *f2; htsmsg_t *filter, *e; const char *str; @@ -84,11 +84,20 @@ api_idnode_grid_conf if ((v = htsmsg_get_str(e, "value"))) idnode_filter_add_str(&conf->filter, k, v, IC_RE); } else if (!strcmp(t, "numeric")) { - uint32_t v; - if (!htsmsg_get_u32(e, "value", &v)) { - int t = str2val(htsmsg_get_str(e, "comparison") ?: "", - filtcmptab); - idnode_filter_add_num(&conf->filter, k, v, t == -1 ? IC_EQ : t); + f2 = htsmsg_field_find(e, "value"); + if (f2) { + int t = str2val(htsmsg_get_str(e, "comparison") ?: "", filtcmptab); + if (f2->hmf_type == HMF_DBL) { + double dbl; + if (!htsmsg_field_get_dbl(f2, &dbl)) + idnode_filter_add_dbl(&conf->filter, k, dbl, t == -1 ? IC_EQ : t); + } else { + int64_t v; + int64_t intsplit = 0; + htsmsg_get_s64(e, "intsplit", &intsplit); + if (!htsmsg_field_get_s64(f2, &v)) + idnode_filter_add_num(&conf->filter, k, v, t == -1 ? IC_EQ : t, intsplit); + } } } else if (!strcmp(t, "boolean")) { uint32_t v; diff --git a/src/idnode.c b/src/idnode.c index 3f20e096..a1d6f0cf 100644 --- a/src/idnode.c +++ b/src/idnode.c @@ -407,8 +407,54 @@ idnode_get_s64 case PT_S64: *s64 = *(int64_t*)ptr; return 0; + case PT_DBL: + *s64 = *(double*)ptr; + return 0; case PT_TIME: *s64 = *(time_t*)ptr; + return 0; + default: + break; + } + } + return 1; +} + +/* + * Get field as double + */ +int +idnode_get_dbl + ( idnode_t *self, const char *key, double *dbl ) +{ + const property_t *p = idnode_find_prop(self, key); + if (p->islist) return 1; + if (p) { + const void *ptr; + if (p->get) + ptr = p->get(self); + else + ptr = ((void*)self) + p->off; + switch (p->type) { + case PT_INT: + case PT_BOOL: + *dbl = *(int*)ptr; + return 0; + case PT_U16: + *dbl = *(uint16_t*)ptr; + return 0; + case PT_U32: + *dbl = *(uint32_t*)ptr; + return 0; + case PT_S64: + *dbl = *(int64_t*)ptr; + return 0; + case PT_DBL: + *dbl = *(double *)ptr; + return 0; + case PT_TIME: + *dbl = *(time_t*)ptr; + return 0; default: break; } @@ -597,7 +643,16 @@ idnode_cmp_sort } break; case PT_DBL: - // TODO + { + double dbla = 0, dblb = 0; + idnode_get_dbl(ina, sort->key, &dbla); + idnode_get_dbl(inb, sort->key, &dblb); + if (sort->dir == IS_ASC) + return dbla - dblb; + else + return dblb - dbla; + } + break; case PT_TIME: { time_t ta = 0, tb = 0; @@ -617,6 +672,31 @@ idnode_cmp_sort return 0; } +static void +idnode_filter_init + ( idnode_t *in, idnode_filter_t *filter ) +{ + idnode_filter_ele_t *f; + const property_t *p; + + LIST_FOREACH(f, filter, link) { + if (f->type == IF_NUM) { + p = idnode_find_prop(in, f->key); + if (p) { + if (p->type == PT_U32 || p->type == PT_S64 || + p->type == PT_TIME) { + int64_t v = f->u.n.n; + if (p->intsplit != f->u.n.intsplit) { + v = (v / (f->u.n.intsplit <= 0 ? 1 : 0)) * p->intsplit; + f->u.n.n = v; + } + } + } + } + f->checked = 1; + } +} + int idnode_filter ( idnode_t *in, idnode_filter_t *filter ) @@ -624,6 +704,8 @@ idnode_filter idnode_filter_ele_t *f; LIST_FOREACH(f, filter, link) { + if (!f->checked) + idnode_filter_init(in, filter); if (f->type == IF_STR) { const char *str; str = idnode_get_display(in, idnode_find_prop(in, f->key)); @@ -655,7 +737,29 @@ idnode_filter int64_t a, b; if (idnode_get_s64(in, f->key, &a)) return 1; - b = (f->type == IF_NUM) ? f->u.n : f->u.b; + b = (f->type == IF_NUM) ? f->u.n.n : f->u.b; + switch (f->comp) { + case IC_IN: + case IC_RE: + break; // Note: invalid + case IC_EQ: + if (a != b) + return 1; + break; + case IC_LT: + if (a > b) + return 1; + break; + case IC_GT: + if (a < b) + return 1; + break; + } + } else if (f->type == IF_DBL) { + double a, b; + if (idnode_get_dbl(in, f->key, &a)) + return 1; + b = f->u.dbl; switch (f->comp) { case IC_IN: case IC_RE: @@ -699,13 +803,26 @@ idnode_filter_add_str void idnode_filter_add_num - ( idnode_filter_t *filt, const char *key, int64_t val, int comp ) + ( idnode_filter_t *filt, const char *key, int64_t val, int comp, int64_t intsplit ) { idnode_filter_ele_t *ele = calloc(1, sizeof(idnode_filter_ele_t)); ele->key = strdup(key); ele->type = IF_NUM; ele->comp = comp; - ele->u.n = val; + ele->u.n.n = val; + ele->u.n.intsplit = intsplit; + LIST_INSERT_HEAD(filt, ele, link); +} + +void +idnode_filter_add_dbl + ( idnode_filter_t *filt, const char *key, double dbl, int comp ) +{ + idnode_filter_ele_t *ele = calloc(1, sizeof(idnode_filter_ele_t)); + ele->key = strdup(key); + ele->type = IF_DBL; + ele->comp = comp; + ele->u.dbl = dbl; LIST_INSERT_HEAD(filt, ele, link); } diff --git a/src/idnode.h b/src/idnode.h index 82c70245..f3d03387 100644 --- a/src/idnode.h +++ b/src/idnode.h @@ -100,16 +100,22 @@ typedef struct idnode_filter_ele { LIST_ENTRY(idnode_filter_ele) link; ///< List link + int checked; char *key; ///< Filter key enum { IF_STR, IF_NUM, + IF_DBL, IF_BOOL } type; ///< Filter type union { int b; char *s; - int64_t n; + struct { + int64_t n; + int64_t intsplit; + } n; + double dbl; regex_t re; } u; ///< Filter data enum { @@ -175,13 +181,16 @@ idnode_perm(idnode_t *self, struct access *a, htsmsg_t *msg_to_write) const char *idnode_get_str (idnode_t *self, const char *key ); int idnode_get_u32 (idnode_t *self, const char *key, uint32_t *u32); int idnode_get_s64 (idnode_t *self, const char *key, int64_t *s64); +int idnode_get_dbl (idnode_t *self, const char *key, double *dbl); int idnode_get_bool(idnode_t *self, const char *key, int *b); int idnode_get_time(idnode_t *self, const char *key, time_t *tm); void idnode_filter_add_str (idnode_filter_t *f, const char *k, const char *v, int t); void idnode_filter_add_num - (idnode_filter_t *f, const char *k, int64_t s64, int t); + (idnode_filter_t *f, const char *k, int64_t s64, int t, int64_t intsplit); +void idnode_filter_add_dbl + (idnode_filter_t *f, const char *k, double dbl, int t); void idnode_filter_add_bool (idnode_filter_t *f, const char *k, int b, int t); void idnode_filter_clear diff --git a/src/webui/static/app/idnode.js b/src/webui/static/app/idnode.js index 18eabf5f..9608274f 100644 --- a/src/webui/static/app/idnode.js +++ b/src/webui/static/app/idnode.js @@ -107,6 +107,44 @@ tvheadend.idnode_enum_store = function(f) return store; }; +Ext.ux.grid.filter.IntsplitFilter = Ext.extend(Ext.ux.grid.filter.NumericFilter, { + + fieldCls : Ext.form.TextField, + + constructor: function(conf) { + this.intsplit = conf.intsplit; + if (!conf.fields) + conf.fields = { + gt: { maskRe: /[0-9\.]/ }, + lt: { maskRe: /[0-9\.]/ }, + eq: { maskRe: /[0-9\.]/ } + }; + Ext.ux.grid.filter.IntsplitFilter.superclass.constructor.call(this, conf); + }, + + getSerialArgs: function () { + var key, + args = [], + values = this.menu.getValue(); + for (key in values) { + var s = values[key].toString().split('.'); + var v = 0; + if (s.length > 0) + v = parseInt(s[0]) * this.intsplit; + if (s.length > 1) + v += parseInt(s[1]); + args.push({ + type: 'numeric', + comparison: key, + value: v, + intsplit: this.intsplit + }); + } + return args; + } + +}); + tvheadend.IdNodeField = function(conf) { /* @@ -151,7 +189,10 @@ tvheadend.IdNodeField = function(conf) var cfg = conf && this.id in conf ? conf[this.id] : {}; var w = 300; var ftype = 'string'; - if (this.type === 'int' || this.type === 'u32' || + if (this.intsplit) { + ftype = 'intsplit'; + w = 80; + } else if (this.type === 'int' || this.type === 'u32' || this.type === 'u16' || this.type === 's64' || this.type === 'dbl') { ftype = 'numeric'; @@ -180,7 +221,8 @@ tvheadend.IdNodeField = function(conf) hidden: this.hidden, filter: { type: ftype, - dataIndex: this.id + dataIndex: this.id, + intsplit: this.intsplit } }; @@ -307,7 +349,11 @@ tvheadend.IdNodeField = function(conf) case 's32': case 'dbl': case 'time': - cons = Ext.form.NumberField; + if (this.intsplit) { + c['maskRe'] = /[0-9\.]/; + cons = Ext.form.TextField; + } else + cons = Ext.form.NumberField; break; /* 'str' and 'perm' */