From 7eb07d443366ddc5487995dc77b0afc09db39828 Mon Sep 17 00:00:00 2001 From: Jonathan Wilkes <jancsika@yahoo.com> Date: Mon, 20 Apr 2015 01:34:03 -0400 Subject: [PATCH] first try at preferences dialog --- pd/nw/dialog_prefs.html | 432 ++++++++++++++++++++++++++++++ pd/nw/locales/en/translation.json | 17 ++ pd/nw/pd_canvas.html | 4 +- pd/nw/pdgui.js | 32 +++ pd/src/s_audio.c | 87 ++++-- 5 files changed, 551 insertions(+), 21 deletions(-) create mode 100644 pd/nw/dialog_prefs.html diff --git a/pd/nw/dialog_prefs.html b/pd/nw/dialog_prefs.html new file mode 100644 index 000000000..164b8c5f0 --- /dev/null +++ b/pd/nw/dialog_prefs.html @@ -0,0 +1,432 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="dialog_prefs.css"> + </head> + <body> + <div class="container noselect"> + <div class="menu"> + <span data-i18n="[title]prefs.heading.audio_tt"> + <a onclick="display_pref('audio');" + data-i18n="prefs.heading.audio"> + </a> + </span> + <span data-i18n="[title]prefs.heading.midi_tt"> + <a onclick="display_pref('midi');" + data-i18n="prefs.heading.midi"></a> + </span> + <span data-i18n="[title]prefs.heading.gui_tt"> + <a onclick="display_pref('gui');" + data-i18n="prefs.heading.gui"></a> + </span> + </div> + + <fieldset id="audio"> + <legend data-i18n="prefs.heading.audio"></legend> + Audio stuff + </fieldset> + + <fieldset id="midi"> + <legend data-i18n="prefs.heading.midi"></legend> + midi stuff + </fieldset> + + <fieldset id="gui"> + <legend data-i18n="prefs.heading.gui"></legend> + + <label data-i18n="[title]prefs.gui.presets.gui_preset_tt"> + <span data-i18n="prefs.gui.presets.gui_preset"></span> + </label> + <select id="gui_preset"> + <option data-i18n="prefs.gui.presets.vanilla" value="0"> + </option> + <option data-i18n="prefs.gui.presets.extended" value="1"> + </option> + <option data-i18n="prefs.gui.presets.l2ork" value="2"> + </option> + </select> + + </fieldset> + + </div> + <script> + 'use strict'; + var nw = require('nw.gui'); + var pdgui = require('./pdgui.js'); + + // For translations + var l = pdgui.get_local_string; + + console.log("my working dire is " + pdgui.get_pwd()); + + var pd_object_callback; + + // nested arrays of attributes for each garray + // in this canvas + var pd_garray_attrs; + + function ok() { + apply(); + cancel(); + } + +// function toggler(evt) { +// evt.value = evt.checked ? 1 : 0; +// } + + function display_pref(type) { + pdgui.gui_post("here i am with " + type); + var all, i, this_elem; + all = document.getElementsByTagName('fieldset'); + this_elem = document.getElementById(type); + for (i = 0; i < all.length; i++) { + all[i].style.setProperty('display', 'none'); + } + this_elem.style.setProperty('display', 'inline'); + } + + function flag_change(elem) { + var attr, arrays_select, name, value, flag; + arrays_select = document.getElementById('arrays_select'); + attr = pd_garray_attrs[arrays_select.value]; + name = elem.name; +// pdgui.gui_post("name is " + name); + // get value from radio group, checked from checkboxes + if (name === 'array_style') { + value = document.querySelector('input[name="array_style"]:checked').value; + pdgui.gui_post("array style found: " + value); + } else { + // '+' for casting boolean to number + value = +elem.checked; + } +// pdgui.gui_post("value is " + value); + flag = attr[attr.indexOf('array_flags') + 1]; + pdgui.gui_post("flag before is " + flag); + switch (name) { + case "array_save": + flag &= ~1; // clear the save bit + flag |= value; // set it + break; + case "array_style": + flag &= ~(1 << 2); // clear style bit 2... + flag &= ~(1 << 1); // ... and 1 ... + flag += (2 * value); // set them + break; + case "array_jump": + flag &= ~(1 << 3); + flag += (16 * value); + break; + } + attr[attr.indexOf('array_flags') + 1] = flag; + pdgui.gui_post("array is " + attr); + } + + function flag2_change(elem) { + var attr, arrays_select, name, value, flag; + arrays_select = document.getElementById('arrays_select'); + attr = pd_garray_attrs[arrays_select.value]; + name = elem.name; +// pdgui.gui_post("name is " + name); + // get value from radio group, checked from checkboxes + // '+' for casting boolean to number + value = +elem.checked; +// pdgui.gui_post("value is " + value); + attr[attr.indexOf(name) + 1] = value; + pdgui.gui_post("array is " + attr); + } + + function attr_change(elem) { + var array_index, attr, arrays_select, name; + arrays_select = document.getElementById('arrays_select'); + attr = pd_garray_attrs[arrays_select.value]; + name = elem.name; + attr[attr.indexOf(name) + 1] = elem.value; + pdgui.gui_post("name is " + elem.name); + pdgui.gui_post("value is " + elem.value); + } + + function array_choose(array_index) { + var i, name, value, elem, style_index, style_opts, + array_attr = pd_garray_attrs[array_index]; + for (i = 0; i < array_attr.length; i+=2) { + name = array_attr[i]; + value = array_attr[i+1]; + switch (name) { + case "array_gfxstub": break; + case "array_flags": + // save contents + elem = document.getElementsByName('array_save')[0]; + elem.checked = (value & 1) != 0; + // jump on click + elem = document.getElementsByName('array_jump')[0]; + elem.checked = (value & 16) != 0; + // draw style + style_opts = document.getElementsByName('array_style'); + style_index = (value & 6) >> 1; + elem = style_opts[style_index]; + elem.checked = true; + break; + default: + // name, size, fill, and outline + pdgui.gui_post("name is " + name); + elem = document.getElementsByName(name)[0]; + elem.value = value; + break; + } + } + } + + function update_gop_form(opts, state) { + var elem, i; + for(i = 0; i < opts.length; i++) { + elem = opts[i]; + if (elem.type === 'checkbox' || + elem.type === 'text') { + elem.disabled = state ? false : true; + } + if (state) { + elem.classList.remove('disabled'); + } else { + elem.classList.add('disabled'); + } + } + } + + var gop_click_count = 0; + + function show_sane_defaults() { + var w, h, xoff, yoff; + w = document.getElementsByName('x-pix')[0]; + h = document.getElementsByName('y-pix')[0]; + xoff = document.getElementsByName('x-margin')[0]; + yoff = document.getElementsByName('y-margin')[0]; + if (w.value === '0' && h.value === '0') { + w.value = 85; + h.value = 60; + xoff.value = 100; + yoff.value = 100; + } + gop_click_count++; + } + + function set_gop(state) { + var gop_opts, no_gop_opts; + if (state === true && gop_click_count === 0) { + show_sane_defaults(); + } + gop_opts = document.getElementsByClassName('gop_opt'); + no_gop_opts = document.getElementsByClassName('no_gop_opt'); + update_gop_form(gop_opts, state); + update_gop_form(no_gop_opts, state === false); + } + + + function substitute_space(arg) { + var fake_space = String.fromCharCode(11); + return arg.split(' ').join(fake_space); + } + + function strip_problem_chars(arg) { + var problem_chars = [';', ',', '{', '}', '\\']; + var ret = arg; + for(var i = 0; i < problem_chars.length; i++) { + ret = ret.split(';').join(''); + } + return ret; + } + + function get_input(name) { + var val = document.getElementsByName(name)[0].value; + return val === 0 ? '0' : val; + } + + // get a value from the garray attr array + function get_array_value(name, attrs) { + return attrs[attrs.indexOf(name) + 1]; + } + + + function apply() { + var i, attrs, gop, hide_name; + pdgui.gui_post("we're applying shits!"); + + // If this is a dialog to create a new array, we + // skip the canvas dialog callback + if (pd_garray_attrs.length < 1 || pd_garray_attrs[0][0] !== 'array_gfxstub') + { + // Note: the "+" casts Boolean to Number + gop = +document.getElementsByName('gop')[0].checked; + hide_name = +document.getElementsByName('hide-name')[0].checked; + + pdgui.pdsend([pd_object_callback, 'donecanvasdialog', + get_input('x-scale'), + get_input('y-scale'), + (gop + 2 * hide_name), + get_input('x1'), + get_input('y1'), + get_input('x2'), + get_input('y2'), + get_input('x-pix'), + get_input('y-pix'), + get_input('x-margin'), + get_input('y-margin'), + ].join(' ')); + } + + // Now send the array properties, in a separate + // message for each array + for (i = 0; i < pd_garray_attrs.length; i++) { + attrs = pd_garray_attrs[i]; + name = get_array_value('array_name', attrs); + if (name.slice(0, 1) === '$') { + name = '#' + name.slice(1); + } + pdgui.pdsend([ + get_array_value('array_gfxstub', attrs), + 'arraydialog', + name, + get_array_value('array_size', attrs), + get_array_value('array_flags', attrs), + 0, // create an array in a new graph -- we don't + // need this in a prexisting graph + 0, // xdraw-- not sure if this is still used + 0, // ydraw-- not sure if this is still used + get_array_value('array_fill', attrs), + get_array_value('array_outline', attrs), + ].join(' ')); + + } + } + + function cancel() { + var i, attrs, gfxstub; + pdgui.gui_post("closing the window at this point"); +// window.close(true); + pdgui.pdsend(pd_object_callback + " cancel"); + for (i = 0; i < pd_garray_attrs.length; i++) { + attrs = pd_garray_attrs[i]; + gfxstub = attrs[attrs.indexOf("array_gfxstub") + 1]; +pdgui.gui_post("guistub is " + gfxstub); + pdgui.pdsend(gfxstub + " cancel"); + } + } + + function audio_prefs_callback(attrs) { + pdgui.gui_post("attrs are " + attrs); + pdgui.gui_post("attrs length " + attrs.length); + } + + // This gets called from the nw_create_window function in index.html + // It provides us with our window id from the C side. Once we have it + // we can create the menu and register event callbacks + function register_canvas_id(gfxstub, attr_arrays) { + pd_object_callback = gfxstub; + add_events(gfxstub); + translate_form(); + + // default to audio preference panel + display_pref('audio'); + // We don't turn on rendering of the "container" div until + // We've finished displaying all the spans and populating the + // labels and form elements. That makes it more efficient and + // snappier, at least on older machines. However, we still have + // to asynchronously request the form values from Pd for audio + // and MIDI... + + pdgui.pdsend("pd audio-properties"); // request audio pref attrs + pdgui.pdsend("pd midi-properties"); // request midi pref attrs + document.getElementsByClassName('container')[0].style.setProperty('display', 'inline'); + } + +function tr_text(id) { + var elem = document.getElementById('iem.prop.' + id); + elem.textContent = l('iem.prop.' + id); +} + +// Stop-gap translator +function translate_form() { + var i + var elements = document.querySelectorAll('[data-i18n]'); + for (i = 0; i < elements.length; i++) { + var data = elements[i].dataset.i18n; + if (data.slice(0,7) === '[title]') { + elements[i].title = l(data.slice(7)); + } else { + elements[i].textContent = l(data); + } + } +} + +function populate_form(attr_array) { + + // First, let's put the translated text for the form labels: + +// tr_text('heading.size'); +// tr_text('heading.messages'); +// tr_text('heading.label'); +// tr_text('heading.colors'); +// tr_prop('width'); +// tr_tooltip('width'); + +// var headings = ["size", "messages", "label", "colors"]; +// for (var i = 0; i < headings.length; i++) { +// var str = "iem.prop.heading." + headings[i]; +// var heading = document.getElementById(str); +// heading.textContent = l(str); +// } + + for(var i = 0; i < attr_array.length; i+=2) { + // Unhide the span with the class with the same name as the id + var prop_group = document.getElementsByClassName(attr_array[i])[0]; + if (prop_group !== undefined) { + console.log("the thing here is " + attr_array[i]); + prop_group.classList.remove('hidden'); + } else { + pdgui.gui_post("Error: couldn't find iemgui prop group for " + attr_array[i]); + } + + if (attr_array[i] === 'display-flags') { + // protip: '!!' forces Boolean, '+' forces Number type + var flag = +attr_array[i+1]; + document.getElementsByName('gop')[0].checked = !!flag; + document.getElementsByName('hide-name')[0].checked = !!(flag & 2); + // Set the gop-related parts of the form to be enabled/disabled based on state + set_gop(!!flag); + } + + var elem = document.getElementsByName(attr_array[i]); + if (elem.length > 0) { + if(attr_array[i].slice(-5) === 'color') { + var hex_string = Number(attr_array[i+1]).toString(16); + var color_string = "#" + (hex_string === '0' ? '000000' : hex_string); + pdgui.gui_post("color is " + color_string); + elem[0].value = color_string; + } else if (elem[0].type === 'checkbox') { + // The attr here is a string, so we need to + // force it to number, hence the "+" below + gui_post("found a CHECKED ITEM!!!"); + elem[0].checked = +attr_array[i+1]; + } else { + elem[0].value = attr_array[i+1]; + } + } + } +} + +function add_events(name) { + // let's handle some events for this window... + + // closing the Window + nw.Window.get().on("close", function() { + // this needs to do whatever the "cancel" button does +// pdgui.pdsend(name + " menuclose 0"); +// cancel(); + pdgui.remove_dialogwin(pd_object_callback); + this.close(true); + }); + +} + + </script> + </body> +</html> diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json index ca1c96031..955ef278d 100644 --- a/pd/nw/locales/en/translation.json +++ b/pd/nw/locales/en/translation.json @@ -271,5 +271,22 @@ "array_in_existing_graph": "put in last graph", "array_in_existing_graph_tt": "draw the array inside the last graph that was created. This is a way to have multiple arrays drawn in the same graph." } + }, + "prefs": { + "heading": { + "gui": "GUI", + "gui_tt": "settings for the user interface", + "audio": "audio", + "midi": "MIDI" + }, + "gui": { + "presets": { + "gui_preset": "GUI preset", + "gui_preset_tt": "Collection of patch colors and styles", + "vanilla": "Vanilla", + "extended": "Pd-Extended", + "l2ork": "Pd-L2ork" + } + } } } diff --git a/pd/nw/pd_canvas.html b/pd/nw/pd_canvas.html index 8ba9e8159..71e0272ea 100644 --- a/pd/nw/pd_canvas.html +++ b/pd/nw/pd_canvas.html @@ -640,8 +640,8 @@ function nw_create_patch_window_menus (name) { editMenu.append(new nw.MenuItem({ label: l('menu.preferences'), - click: menu_generic, -// key: 'a', + click: pdgui.open_prefs, + key: 'p', modifiers: "ctrl", tooltip: l('menu.preferences_tt') })); diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index a7a5e0755..c348907b2 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -3270,3 +3270,35 @@ function gui_pd_dsp(state) { pd_window.document.getElementById('dsp_control').checked = !!state; } } + +function open_prefs() { + dialogwin['prefs'] = nw_create_window('prefs', 'prefs', 265, 540, 20, 20, 0, + 0, 1, 'white', 'Properties', '', 0, null, null); +} + +exports.open_prefs = open_prefs; + +function gui_audio_properties(gfxstub, sys_indevs, sys_outdevs, + pd_indevs, pd_inchans, pd_outdevs, pd_outchans, audio_attrs) { + + var attrs = audio_attrs.concat([ + "sys_indevs", sys_indevs, + "sys_outdevs", sys_outdevs, + "pd_indevs", pd_indevs, + "pd_inchans", pd_inchans, + "pd_outdevs", pd_outdevs, + "pd_outchans", pd_outchans + ]); + + gui_post("got back some audio props..."); + for (var i = 0; i < arguments.length; i++) { + gui_post("arg " + i + " is " + arguments[i]); + } + + if (dialogwin['prefs'] !== null) { + dialogwin['prefs'].eval(null, + 'audio_prefs_callback(' + + JSON.stringify(attrs) + ');' + ); + } +} diff --git a/pd/src/s_audio.c b/pd/src/s_audio.c index 33586c2b2..2e88da04f 100644 --- a/pd/src/s_audio.c +++ b/pd/src/s_audio.c @@ -712,15 +712,30 @@ void glob_audio_properties(t_pd *dummy, t_floatarg flongform) audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE); - sys_gui("global audio_indevlist; set audio_indevlist {}\n"); + + gui_start_vmess("gui_audio_properties", "s", + gfxstub_new2(&glob_pdobject, (void *)glob_audio_properties)); + + //sys_gui("global audio_indevlist; set audio_indevlist {}\n"); + + gui_start_array(); // input devices for (i = 0; i < nindevs; i++) - sys_vgui("lappend audio_indevlist {%s}\n", - indevlist + i * DEVDESCSIZE); + { + //sys_vgui("lappend audio_indevlist {%s}\n", + // indevlist + i * DEVDESCSIZE); + gui_s(indevlist + i * DEVDESCSIZE); + } + gui_end_array(); - sys_gui("global audio_outdevlist; set audio_outdevlist {}\n"); +// sys_gui("global audio_outdevlist; set audio_outdevlist {}\n"); + gui_start_array(); // output devices for (i = 0; i < noutdevs; i++) - sys_vgui("lappend audio_outdevlist {%s}\n", - outdevlist + i * DEVDESCSIZE); + { + //sys_vgui("lappend audio_outdevlist {%s}\n", + // outdevlist + i * DEVDESCSIZE); + gui_s(outdevlist + i * DEVDESCSIZE); + } + gui_end_array(); sys_get_audio_params(&naudioindev, audioindev, chindev, &naudiooutdev, audiooutdev, choutdev, &rate, &advance, &callback, @@ -754,19 +769,53 @@ void glob_audio_properties(t_pd *dummy, t_floatarg flongform) audiooutchan2 = (naudiooutdev > 1 ? choutdev[1] : 0); audiooutchan3 = (naudiooutdev > 2 ? choutdev[2] : 0); audiooutchan4 = (naudiooutdev > 3 ? choutdev[3] : 0); - sprintf(buf, -"pdtk_audio_dialog %%s \ -%d %d %d %d %d %d %d %d \ -%d %d %d %d %d %d %d %d \ -%d %d %d %d %d %d\n", - audioindev1, audioindev2, audioindev3, audioindev4, - audioinchan1, audioinchan2, audioinchan3, audioinchan4, - audiooutdev1, audiooutdev2, audiooutdev3, audiooutdev4, - audiooutchan1, audiooutchan2, audiooutchan3, audiooutchan4, - rate, advance, canmulti, (cancallback ? callback : -1), - (flongform != 0), blocksize); - gfxstub_deleteforkey(0); - gfxstub_new(&glob_pdobject, (void *)glob_audio_properties, buf); + +// sprintf(buf, +//"pdtk_audio_dialog %%s \ +//%d %d %d %d %d %d %d %d \ +//%d %d %d %d %d %d %d %d \ +//%d %d %d %d %d %d\n", +// audioindev1, audioindev2, audioindev3, audioindev4, +// audioinchan1, audioinchan2, audioinchan3, audioinchan4, +// audiooutdev1, audiooutdev2, audiooutdev3, audiooutdev4, +// audiooutchan1, audiooutchan2, audiooutchan3, audiooutchan4, +// rate, advance, canmulti, (cancallback ? callback : -1), +// (flongform != 0), blocksize); + + gui_start_array(); // audio input devices + gui_i(audioindev1); gui_i(audioindev2); + gui_i(audioindev3); gui_i(audioindev4); + gui_end_array(); + + gui_start_array(); // audio input channels + gui_i(audioinchan1); gui_i(audioinchan2); + gui_i(audioinchan3); gui_i(audioinchan4); + gui_end_array(); + + gui_start_array(); // audio output devices + gui_i(audiooutdev1); gui_i(audiooutdev2); + gui_i(audiooutdev3); gui_i(audiooutdev4); + gui_end_array(); + + gui_start_array(); // audio output channels + gui_i(audiooutchan1); gui_i(audiooutchan2); + gui_i(audiooutchan3); gui_i(audiooutchan4); + gui_end_array(); + + gui_start_array(); + gui_s("rate"); gui_i(rate); + gui_s("advance"); gui_i(advance); + gui_s("canmulti"); gui_i(canmulti); + gui_s("cancallback"); gui_i(cancallback ? callback : -1); + gui_s("flongform"); gui_i(flongform != 0); + gui_s("blocksize"); gui_i(blocksize); + gui_end_array(); + + gui_end_vmess(); + + // not sure why we were deleting the key 0 here... +// gfxstub_deleteforkey(0); +// gfxstub_new(&glob_pdobject, (void *)glob_audio_properties, buf); } extern int pa_foo; -- GitLab