]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
wizard: initial (demo) work
authorJaroslav Kysela <perex@perex.cz>
Thu, 3 Dec 2015 18:16:49 +0000 (19:16 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 3 Dec 2015 18:18:15 +0000 (19:18 +0100)
16 files changed:
Makefile
Makefile.webui
src/api.c
src/api.h
src/api/api_wizard.c [new file with mode: 0644]
src/config.c
src/config.h
src/webui/comet.c
src/webui/static/app/ext.css
src/webui/static/app/idnode.js
src/webui/static/app/tvheadend.js
src/webui/static/app/wizard.js [new file with mode: 0644]
src/webui/static/icons/arrow_left.png [new symlink]
src/webui/static/icons/arrow_right.png [new symlink]
src/wizard.c [new file with mode: 0644]
src/wizard.h [new file with mode: 0644]

index 802ab071257e33d86dff272a156ad9c0967d080f..36596545af21aac99c8c46d1ffa8d0299701409b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -207,7 +207,8 @@ SRCS-1 = \
        src/intlconv.c \
        src/profile.c \
        src/bouquet.c \
-       src/lock.c
+       src/lock.c \
+       src/wizard.c
 SRCS = $(SRCS-1)
 I18N-C = $(SRCS-1)
 
@@ -249,7 +250,8 @@ SRCS-2 = \
        src/api/api_bouquet.c \
        src/api/api_language.c \
        src/api/api_satip.c \
-       src/api/api_timeshift.c
+       src/api/api_timeshift.c \
+       src/api/api_wizard.c
 
 SRCS-2 += \
        src/parsers/parsers.c \
index af06a4bccd30711e579e0d113a9c841b6f2d0572..5db29ec9647061a6408b5d90012b28cbe2ce1976 100644 (file)
@@ -125,6 +125,7 @@ JAVASCRIPT += $(ROOTPATH)/app/epggrab.js
 JAVASCRIPT += $(ROOTPATH)/app/config.js
 JAVASCRIPT += $(ROOTPATH)/app/tvhlog.js
 JAVASCRIPT += $(ROOTPATH)/app/status.js
+JAVASCRIPT += $(ROOTPATH)/app/wizard.js
 JAVASCRIPT += $(ROOTPATH)/tv.js
 JAVASCRIPT += $(ROOTPATH)/app/servicemapper.js
 
index 6db216a2448f0bc997505bfaa94d766cd3ff66fe..41b4ba07b745429287e0491b51e69f997836fece 100644 (file)
--- a/src/api.c
+++ b/src/api.c
@@ -140,6 +140,7 @@ void api_init ( void )
   api_language_init();
   api_satip_server_init();
   api_timeshift_init();
+  api_wizard_init();
 }
 
 void api_done ( void )
index 3ab69921bf6065a67c8d04f8cb29e27571839811..b5365e0e437f1cf6e473fbe7029d69a3214eed21 100644 (file)
--- a/src/api.h
+++ b/src/api.h
@@ -80,6 +80,7 @@ void api_profile_init       ( void );
 void api_language_init      ( void );
 void api_satip_server_init  ( void );
 void api_timeshift_init     ( void );
+void api_wizard_init        ( void );
 
 /*
  * IDnode
diff --git a/src/api/api_wizard.c b/src/api/api_wizard.c
new file mode 100644 (file)
index 0000000..e77bf74
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  API - Wizard interface
+ *
+ *  Copyright (C) 2015 Jaroslav Kysela
+ *
+ *  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; withm 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "channels.h"
+#include "access.h"
+#include "api.h"
+#include "config.h"
+#include "wizard.h"
+
+static int
+wizard_idnode_load_simple
+  ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+  int r;
+  wizard_build_fcn_t fcn = opaque;
+  wizard_page_t *page = fcn();
+  r = api_idnode_load_simple(perm, &page->idnode, op, args, resp);
+  page->free(page);
+  return r;
+}
+
+static int
+wizard_idnode_save_simple
+  ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+  int r;
+  wizard_build_fcn_t fcn = opaque;
+  wizard_page_t *page = fcn();
+  r = api_idnode_save_simple(perm, &page->idnode, op, args, resp);
+  page->free(page);
+  return r;
+}
+
+static int
+wizard_cancel
+  ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+  pthread_mutex_lock(&global_lock);
+  free(config.wizard);
+  config.wizard = strdup("");
+  config_save();
+  pthread_mutex_unlock(&global_lock);
+  return 0;
+}
+
+void
+api_wizard_init ( void )
+{
+  static api_hook_t ah[] = {
+    { "wizard/hello/load",   ACCESS_ADMIN, wizard_idnode_load_simple, wizard_hello },
+    { "wizard/hello/save",   ACCESS_ADMIN, wizard_idnode_save_simple, wizard_hello },
+    { "wizard/network/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_network },
+    { "wizard/network/save", ACCESS_ADMIN, wizard_idnode_save_simple, wizard_network },
+    { "wizard/input/load",   ACCESS_ADMIN, wizard_idnode_load_simple, wizard_input },
+    { "wizard/input/save",   ACCESS_ADMIN, wizard_idnode_save_simple, wizard_input },
+    { "wizard/status/load",  ACCESS_ADMIN, wizard_idnode_load_simple, wizard_status },
+    { "wizard/status/save",  ACCESS_ADMIN, wizard_idnode_save_simple, wizard_status },
+    { "wizard/mapping/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_mapping },
+    { "wizard/mapping/save", ACCESS_ADMIN, wizard_idnode_save_simple, wizard_mapping },
+    { "wizard/cancel",       ACCESS_ADMIN, wizard_cancel, NULL },
+    { NULL },
+  };
+
+  api_register_all(ah);
+}
index 62065529a35d6ebd4781cb3f7187158b822c8efa..4c42afa6afdf675361c83450a716f2730d92118d 100644 (file)
@@ -1626,6 +1626,7 @@ config_boot ( const char *path, gid_t gid, uid_t uid )
 
   memset(&config, 0, sizeof(config));
   config.idnode.in_class = &config_class;
+  config.wizard = strdup("hello");
   config.info_area = strdup("login,storage,time");
   config.cookie_expires = 7;
   config.dscp = -1;
@@ -1732,6 +1733,7 @@ config_init ( int backup )
 void config_done ( void )
 {
   /* note: tvhlog is inactive !!! */
+  free(config.wizard);
   free(config.full_version);
   free(config.server_name);
   free(config.language);
@@ -2110,6 +2112,13 @@ const idclass_t config_class = {
       .opts   = PO_ADVANCED,
       .group  = 6,
     },
+    {
+      .type   = PT_STR,
+      .id     = "wizard",
+      .name   = "Wizard level", /* untranslated */
+      .off    = offsetof(config_t, wizard),
+      .opts   = PO_NOUI,
+    },
     {}
   }
 };
index 57798bfb6713f4d605bcab25dc7bca26d7b34d85..ec733914b1ba633fb59574f58e423d0a04d9c05e 100644 (file)
@@ -32,6 +32,7 @@ typedef struct config {
   uint32_t version;
   int uilevel;
   int uilevel_nochange;
+  char *wizard;
   char *full_version;
   char *server_name;
   char *language;
index f2dfc5cc1081221850a457964aa489e93759d74b..b35308ef11e16f6b9118760d48ae99bab076a27f 100644 (file)
@@ -150,6 +150,7 @@ comet_access_update(http_connection_t *hc, comet_mailbox_t *cmb)
   const char *username = hc->hc_access ? (hc->hc_access->aa_username ?: "") : "";
   int64_t bfree, btotal;
   int dvr = !http_access_verify(hc, ACCESS_RECORDER);
+  int admin = !http_access_verify(hc, ACCESS_ADMIN);
   const char *s;
 
   htsmsg_add_str(m, "notificationClass", "accessUpdate");
@@ -170,7 +171,7 @@ comet_access_update(http_connection_t *hc, comet_mailbox_t *cmb)
   if (hc->hc_peer_ipstr)
     htsmsg_add_str(m, "address", hc->hc_peer_ipstr);
   htsmsg_add_u32(m, "dvr",      dvr);
-  htsmsg_add_u32(m, "admin",    !http_access_verify(hc, ACCESS_ADMIN));
+  htsmsg_add_u32(m, "admin",    admin);
 
   htsmsg_add_s64(m, "time",     time(NULL));
 
@@ -185,6 +186,9 @@ comet_access_update(http_connection_t *hc, comet_mailbox_t *cmb)
     htsmsg_add_s64(m, "totaldiskspace", btotal);
   }
 
+  if (admin && config.wizard)
+    htsmsg_add_str(m, "wizard", config.wizard);
+
   if(cmb->cmb_messages == NULL)
     cmb->cmb_messages = htsmsg_create_list();
   htsmsg_add_msg(cmb->cmb_messages, NULL, m);
index 4a078cfce5349707cab04c59f03faca53c78511b..0de9fa4f741ad7fd221ba7f83fe4b6e8f9393aef 100644 (file)
     background-image: url(../icons/find.png) !important;
 }
 
+.wizard {
+    background-image: url(../icons/wand.png) !important;
+}
+
 .uilevel {
     background-image: url(../icons/application_form.png) !important;
 }
     background-image: url(../icons/fetch_images.png) !important;
 }
 
+.previous {
+    background-image: url(../icons/arrow_left.png) !important;
+}
+
+.next {
+    background-image: url(../icons/arrow_right.png) !important;
+}
+
+
 .x-linked {
     display: inline-block;
     background-image: url(../icons/linked.gif) !important;
index 07c8d76b0d70aa73bd05fcc906744a0111eac12f..444cd0c24edd3b6021edd5cba7f2f8f36faaca19 100644 (file)
@@ -831,7 +831,7 @@ tvheadend.idnode_editor_form = function(uilevel, d, meta, panel, conf)
         var p = d[i];
         if (conf.uuids && p.rdonly)
             continue;
-        if (conf.noui)
+        if (p.noui)
             continue;
         var f = tvheadend.idnode_editor_field(p, conf);
         if (!f)
@@ -1025,20 +1025,22 @@ tvheadend.idnode_editor = function(_uilevel, item, conf)
             var cancelBtn = new Ext.Button({
                 text: _('Cancel'),
                 iconCls: 'cancel',
-                handler: conf.cancel
+                handler: function() {
+                    conf.cancel(conf);
+                }
             });
             buttons.push(cancelBtn);
         }
 
         var saveBtn = new Ext.Button({
-            text: _('Save'),
-            iconCls: 'save',
+            text: conf.saveText || _('Save'),
+            iconCls: conf.saveIconCls || 'save',
             handler: function() {
                 if (panel.getForm().isDirty()) {
                     var node = panel.getForm().getFieldValues();
                     node.uuid = conf.uuids ? conf.uuids : item.uuid;
                     tvheadend.Ajax({
-                        url: 'api/idnode/save',
+                        url: conf.saveURL || 'api/idnode/save',
                         params: {
                             node: Ext.encode(node)
                         },
@@ -1051,6 +1053,8 @@ tvheadend.idnode_editor = function(_uilevel, item, conf)
                     if (conf.win)
                         conf.win.close();
                 }
+                if (conf.postsave)
+                    conf.postsave(conf);
             }
         });
         buttons.push(saveBtn);
@@ -1064,7 +1068,7 @@ tvheadend.idnode_editor = function(_uilevel, item, conf)
                         var node = panel.getForm().getFieldValues();
                         node.uuid = conf.uuids ? conf.uuids : item.uuid;
                         tvheadend.Ajax({
-                            url: 'api/idnode/save',
+                            url: conf.saveURL || 'api/idnode/save',
                             params: {
                                 node: Ext.encode(node)
                             },
@@ -1078,8 +1082,11 @@ tvheadend.idnode_editor = function(_uilevel, item, conf)
             buttons.push(applyBtn);
         }
 
+
         var uilevelBtn = null;
-        if (!tvheadend.uilevel_nochange && (!conf.uilevel || conf.uilevel !== 'expert')) {
+        if (!tvheadend.uilevel_nochange &&
+            (!conf.uilevel || conf.uilevel !== 'expert') &&
+            !conf.noUIlevel) {
             uilevelBtn = tvheadend.idnode_uilevel_menu(uilevel, function(l) {
                 uilevel = l;
                 var values = panel.getForm().getFieldValues();
@@ -1100,6 +1107,9 @@ tvheadend.idnode_editor = function(_uilevel, item, conf)
             buttons.push(uilevelBtn ? '-' : '->');
             buttons.push(helpBtn);
         }
+
+        if (conf.buildbtn)
+            conf.buildbtn(conf, buttons);
     }
 
     panel = new Ext.form.FormPanel({
@@ -1122,6 +1132,29 @@ tvheadend.idnode_editor = function(_uilevel, item, conf)
     return panel;
 };
 
+/*
+ *
+ */
+tvheadend.idnode_editor_win = function(_uilevel, item, conf)
+{
+   var p = tvheadend.idnode_editor(_uilevel, item, conf);
+   var width = p.fixedWidth;
+   var w = new Ext.ux.Window({
+       title: conf.winTitle,
+       iconCls: 'edit',
+       layout: 'fit',
+       autoWidth: width ? false : true,
+       autoHeight: true,
+       autoScroll: true,
+       plain: true,
+       items: p
+   });
+   conf.win = w;
+   if (width)
+       w.setWidth(width);
+   w.show();
+   return w;
+}
 
 /*
  * IDnode creation dialog
@@ -1669,28 +1702,13 @@ tvheadend.idnode_grid = function(panel, conf)
                                         }
                                     };
                                     if (uuids.length > 1) {
-                                        var title = String.format(_('Edit {0} ({1} entries)'),
-                                                                  conf.titleS, uuids.length);
+                                        c.winTitle = String.format(_('Edit {0} ({1} entries)'),
+                                                                   conf.titleS, uuids.length);
                                         c.uuids = uuids;
                                     } else {
-                                        var title = String.format(_('Edit {0}'), conf.titleS);
+                                        c.winTitle = String.format(_('Edit {0}'), conf.titleS);
                                     }
-                                    var p = tvheadend.idnode_editor(uilevel, d[0], c);
-                                    var width = p.fixedWidth;
-                                    w = new Ext.ux.Window({
-                                        title: title,
-                                        iconCls: 'edit',
-                                        layout: 'fit',
-                                        autoWidth: width ? false : true,
-                                        autoHeight: true,
-                                        autoScroll: true,
-                                        plain: true,
-                                        items: p
-                                    });
-                                    if (width)
-                                        w.setWidth(width);
-                                    c.win = w;
-                                    w.show();
+                                    tvheadend.idnode_editor_win(uilevel, d[0], c);
                                 }
                             });
                         }
index a9e45c750afefb3dfbfaaa5938748a4fe759d140..92427793a4eabfb5c1e750bce8785001ff76cf47 100644 (file)
@@ -5,6 +5,7 @@ tvheadend.admin = false;
 tvheadend.dialog = null;
 tvheadend.uilevel = 'expert';
 tvheadend.uilevel_nochange = false;
+tvheadend.wizard = null;
 
 tvheadend.cookieProvider = new Ext.state.CookieProvider({
   // 7 days from now
@@ -586,6 +587,9 @@ function accessUpdate(o) {
     }
 
     tvheadend.rootTabPanel.doLayout();
+
+    if (o.wizard)
+        tvheadend.wizard_start(o.wizard);
 }
 
 /**
diff --git a/src/webui/static/app/wizard.js b/src/webui/static/app/wizard.js
new file mode 100644 (file)
index 0000000..4f663af
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Wizard
+ */
+
+tvheadend.wizard_start = function(page) {
+
+    var w = null;
+
+    function cancel(conf) {
+        tvheadend.Ajax({
+            url: 'api/wizard/cancel'
+        });
+        tvheadend.wizard = null;
+        if (conf.win)
+            conf.win.close();
+    }
+
+    function getparam(data, prefix) {
+        var m = data.params;
+        var l = prefix.length;
+        for (var i = 0; i < m.length; i++) {
+            var id = m[i].id;
+            if (id.substring(0, l) === prefix)
+                return id.substring(l, 32);
+        }
+        return null;
+    }
+
+    function newpage(conf, prefix) {
+        var p = getparam(conf.fullData, prefix);
+        if (p)
+            tvheadend.wizard_start(p);
+        else
+            Ext.MessageBox.alert(String.format(_('Wizard - page "{0}" not found'), prefix));
+    }
+
+    function buildbtn(conf, buttons) {
+        if (!getparam(conf.fullData, 'page_prev_'))
+            return;
+        var prevBtn = new Ext.Button({
+            text: _('Previous'),
+            iconCls: 'previous',
+            handler: function() {
+                newpage(conf, 'page_prev_');
+            },
+        });
+        buttons.splice(0, 0, prevBtn);
+    }
+    
+    function build(d) {
+        d = json_decode(d);
+        var m = d[0];
+        var last = getparam(m, 'page_next_') === null;
+        tvheadend.idnode_editor_win('basic', m, {
+            fullData: m,
+            url: 'api/wizard/' + page,
+            winTitle: m.caption,
+            iconCls: 'wizard',
+            comet: m.events,
+            noApply: true,
+            noUIlevel: true,
+            postsave: function(conf) {
+                if (!last)
+                    newpage(conf, 'page_next_');
+                else
+                    cancel(conf);
+            },
+            buildbtn: buildbtn,
+            labelWidth: 250,
+            saveText: last ? _('Finish') : _('Save & Next'),
+            saveIconCls: last ? 'exit' : 'next',
+            cancel: cancel,
+            uilevel: 'expert',
+            help: function() {
+                new tvheadend.help(_('Wizard - initial configuration and tutorial'), 'config_wizard.html');
+            }
+        });
+    }
+
+    tvheadend.wizard = page;
+    tvheadend.Ajax({
+        url: 'api/wizard/' + page + '/load',
+        params: {
+            meta: 1
+        },
+        success: build,
+        failure: function(response, options) {
+            Ext.MessageBox.alert(_('Unable to obtain wizard page!'), response.statusText);
+        }
+    });
+
+};
diff --git a/src/webui/static/icons/arrow_left.png b/src/webui/static/icons/arrow_left.png
new file mode 120000 (symlink)
index 0000000..494b4cd
--- /dev/null
@@ -0,0 +1 @@
+../../../../vendor/famfamsilk/arrow_left.png
\ No newline at end of file
diff --git a/src/webui/static/icons/arrow_right.png b/src/webui/static/icons/arrow_right.png
new file mode 120000 (symlink)
index 0000000..1a46dc0
--- /dev/null
@@ -0,0 +1 @@
+../../../../vendor/famfamsilk/arrow_right.png
\ No newline at end of file
diff --git a/src/wizard.c b/src/wizard.c
new file mode 100644 (file)
index 0000000..66366dc
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ *  tvheadend, Wizard
+ *  Copyright (C) 2015 Jaroslav Kysela
+ *
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "access.h"
+#include "settings.h"
+#include "wizard.h"
+
+/*
+ *
+ */
+
+static const void *empty_get(void *o)
+{
+  prop_sbuf[0] = '\0';
+  return &prop_sbuf_ptr;
+}
+
+#define SPECIAL_PROP(idval) { \
+  .type = PT_STR, \
+  .id   = idval, \
+  .name = "", \
+  .get  = empty_get, \
+  .opts = PO_RDONLY | PO_NOUI \
+}
+
+#define PREV_BUTTON(page) SPECIAL_PROP("page_prev_" STRINGIFY(page))
+#define NEXT_BUTTON(page) SPECIAL_PROP("page_next_" STRINGIFY(page))
+#define LAST_BUTTON()     SPECIAL_PROP("page_last")
+
+/*
+ *
+ */
+
+static void page_free(wizard_page_t *page)
+{
+  free((char *)page->idnode.in_class);
+  free(page);
+}
+
+static wizard_page_t *page_init(const char *class_name, const char *caption)
+{
+  wizard_page_t *page = calloc(1, sizeof(*page));
+  idclass_t *ic = calloc(1, sizeof(*ic));
+  page->idnode.in_class = ic;
+  ic->ic_caption = caption;
+  ic->ic_class = ic->ic_event = class_name;
+  ic->ic_perm_def = ACCESS_ADMIN;
+  page->free = page_free;
+  return page;
+}
+
+/*
+ * Hello
+ */
+
+static const void *hello_get_network(void *o)
+{
+  strcpy(prop_sbuf, "Test123");
+  return &prop_sbuf_ptr;
+}
+
+static int hello_set_network(void *o, const void *v)
+{
+  return 0;
+}
+
+wizard_page_t *wizard_hello(void)
+{
+  static const property_t props[] = {
+    {
+      .type     = PT_STR,
+      .id       = "network",
+      .name     = N_("Network (like 192.168.1.0/24)"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "username",
+      .name     = N_("Username"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "password",
+      .name     = N_("Password"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    NEXT_BUTTON(network),
+    {}
+  };
+  wizard_page_t *page = page_init("wizard_hello", N_("Welcome - Tvheadend - your TV streaming server and video recorder"));
+  idclass_t *ic = (idclass_t *)page->idnode.in_class;
+  ic->ic_properties = props;
+  return page;
+}
+
+/*
+ * Network settings
+ */
+
+wizard_page_t *wizard_network(void)
+{
+  static const property_t props[] = {
+    {
+      .type     = PT_STR,
+      .id       = "network1",
+      .name     = N_("Network 1"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "network2",
+      .name     = N_("Network 2"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "network3",
+      .name     = N_("Network 3"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    PREV_BUTTON(hello),
+    NEXT_BUTTON(input),
+    {}
+  };
+  wizard_page_t *page = page_init("wizard_network", N_("Network settings"));
+  idclass_t *ic = (idclass_t *)page->idnode.in_class;
+  ic->ic_properties = props;
+  return page;
+}
+
+/*
+ * Input settings
+ */
+
+wizard_page_t *wizard_input(void)
+{
+  static const property_t props[] = {
+    {
+      .type     = PT_STR,
+      .id       = "input1",
+      .name     = N_("Input 1 network"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "input2",
+      .name     = N_("Input 2 network"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "input3",
+      .name     = N_("Input 3 network"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    PREV_BUTTON(network),
+    NEXT_BUTTON(status),
+    {}
+  };
+  wizard_page_t *page = page_init("wizard_input", N_("Input / tuner settings"));
+  idclass_t *ic = (idclass_t *)page->idnode.in_class;
+  ic->ic_properties = props;
+  return page;
+}
+
+/*
+ * Status
+ */
+
+wizard_page_t *wizard_status(void)
+{
+  static const property_t props[] = {
+    {
+      .type     = PT_STR,
+      .id       = "muxes",
+      .name     = N_("Found muxes"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "services",
+      .name     = N_("Found services"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    PREV_BUTTON(input),
+    NEXT_BUTTON(mapping),
+    {}
+  };
+  wizard_page_t *page = page_init("wizard_status", N_("Scan status"));
+  idclass_t *ic = (idclass_t *)page->idnode.in_class;
+  ic->ic_properties = props;
+  return page;
+}
+
+/*
+ * Service Mapping
+ */
+
+wizard_page_t *wizard_mapping(void)
+{
+  static const property_t props[] = {
+    {
+      .type     = PT_STR,
+      .id       = "pnetwork",
+      .name     = N_("Select network"),
+      .get      = hello_get_network,
+      .set      = hello_set_network,
+    },
+    PREV_BUTTON(status),
+    LAST_BUTTON(),
+    {}
+  };
+  wizard_page_t *page = page_init("wizard_service_map", N_("Service mapping"));
+  idclass_t *ic = (idclass_t *)page->idnode.in_class;
+  ic->ic_properties = props;
+  return page;
+}
diff --git a/src/wizard.h b/src/wizard.h
new file mode 100644 (file)
index 0000000..fc83a5e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  tvheadend, Wizard
+ *  Copyright (C) 2015 Jaroslav Kysela
+ *
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TVH_WIZARD_H__
+#define __TVH_WIZARD_H__
+
+#include "tvheadend.h"
+#include "idnode.h"
+
+typedef struct wizard_page {
+  idnode_t idnode;
+  void (*free)(struct wizard_page *);
+} wizard_page_t;
+
+typedef wizard_page_t *(*wizard_build_fcn_t)(void);
+
+wizard_page_t *wizard_hello(void);
+wizard_page_t *wizard_network(void);
+wizard_page_t *wizard_input(void);
+wizard_page_t *wizard_status(void);
+wizard_page_t *wizard_mapping(void);
+
+#endif /* __TVH_WIZARD_H__ */