/*
* SPL - The SPL Programming Language
* Copyright (C) 2004, 2005 Clifford Wolf
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* mod_wsf_dialog.spl: WSF Dialog Component
*/
/**
* A module which implements a "Qt Designer"-like component for creating WSF
* dialogs in a web browser.
*/
load "wsf";
load "format_xml";
load "encode_xml";
/**
* A WSF component for creating simple WSF components in a web browser.
*
* A Dialog is simply a collection of form elements with small code snippets,
* which are automatically executed when the content of the element has been
* modified (or the button been pushed).
*
* Such a dialog can be dumped as XML and later loaded again.
*
* If the "save" button should be functional, you eighter need to derive your
* own object from this one or overwrite the method name after instanciating
* this object.
*
* Derived from [[wsf:WsfComponent]].
*/
object WsfDialog WsfComponent
{
/**
* The configuration of the dialog, in the format described in
* [[xml:format_xml_parse()]].
*
* All changes are done directly in this data structrue. So there is
* no need of calling any kind of convertation function in order to
* keep this structure up to date.
*
* Simply call [[xml:format_xml_dump()]] on this variable to create an
* XML file for this dialog (usually this is done in an overloaded
* [[.save()]] method.
*/
var xmltree;
/**
* The current values of the elements. If there is an input box
* "foobar", "values.foobar" can be used to read and modify the value
* of that input box.
*/
var values;
/**
* When this variable is set to '1', the dialog is currently in edit
* mode. Usually applications only allow privelidged users to switch
* to edit mode.
*
* The dirty variable ([[wsf:WsfComponent.dirty]]) must be set when
* this variable is changed. Usually the method [[.set_edit_mode()]] is
* used to set this variable.
*/
var edit_mode = 0;
/**
* The name of the element which is open in the editor right now. If
* this is set to "", the screen with the generic dialog options is
* displayed.
*
* Usually this is only set internally by [[.main()]] when the user
* changes the current element in the web frontend.
*/
var edit_current = "";
/**
* The frontend allows displaying the current XML tree so it can e.g.
* be copy&pasted to a file. This variable is set to '1' when displaying
* of the XML tree is currently active.
*/
var edit_show_xml = 0;
/**
* Set this to '1' if you want this component to add an 'edit' button
* to the component. With this button, the user can switch to the editor
* view at any time. This should be used with care since someone with
* access to the editor may access any SPL code! Only privelidged
* users show have access to this feature.
*/
var show_edit_button = 0;
/**
* Call this function if you want to switch this component to edit mode
* (parameter = '1') or back to executing the dialog (parameter = '0').
*/
method set_edit_mode(newmode)
{
if ( newmode != edit_mode ) {
edit_mode = newmode;
edit_current = "";
edit_show_xml = 0;
dirty = 1;
}
}
method __WsfDialog_get_html_element(element)
{
var onchange = << EOT:
:document.${id}_f.el.value = '${element.["A:name"]}';
:submit(); return true;
EOT;
if ( element.["A:type"] ~== "text" ) {
return << EOT:
:
EOT;
}
else
if ( element.["A:type"] ~== "textarea" ) {
return << EOT:
:
EOT;
}
else
if ( element.["A:type"] ~== "button" ) {
return << EOT:
:
EOT;
}
else
if ( element.["A:type"] ~== "label" ) {
return values.[element.["A:name"]];
}
return "";
}
method __WsfDialog_get_html_editor()
{
var d = xmltree.["E0:wsfdialog"];
var html = "";
html ~= '\n';
html ~= '\n';
if ( edit_current ~== "" )
{
html ~= << EOT:
:
:
New Element:
:
:
:
:
:
Width:
cells
:
Height:
cells
:
Step:
pixel/cell
:
:
Init code:
:
:
:
:
EOT;
}
else
{
var e = d.[edit_current];
var set_type = '';
html ~= << EOT:
:
:
Name:
text
:
Pos. X:
cells
:
Pos. Y:
cells
:
Width:
cells
:
Height:
cells
:
Type:
$set_type
type
:
Value:
text
:
:
Action code:
:
:
:
:
EOT;
}
return html;
}
/**
* Overloaded [[wsf:WsfComponent.get_html()]].
*/
method get_html()
{
var html = "";
var d = xmltree.["E0:wsfdialog"];
html ~= << EOT:
:
EOT;
return html;
}
/**
* This method is called when the user clicks on the "save" button in
* the dialog editor. It does nothing in the default implementation. So
* you need to overload it in a derived object to add this
* functionality.
*/
method save()
{
/* To be overloaded */
return;
}
/**
* Overloaded [[wsf:WsfComponent.main()]].
*/
method main()
{
while (1)
{
task_co_return();
var d = xmltree.["E0:wsfdialog"];
if ( edit_mode )
{
edit_current = cgi.param.new_current;
if ( declared cgi.param.show_xml )
edit_show_xml = not edit_show_xml;
if ( cgi.param.current ~== "" )
{
d.["A:width"] = cgi.param.set_width;
d.["A:height"] = cgi.param.set_height;
d.["A:step"] = cgi.param.set_step;
if ( cgi.param.set_code =~ /\S/ )
d.["E0:code"].["C0"] = cgi.param.set_code;
else
delete d.["E0:code"];
if ( cgi.param.new_element =~ /\S/ )
{
var id = 0;
while ( declared d.["E${id}:field"] ) id++;
edit_current = "E${id}:field";
d.["E${id}:field"].["A:name"] = cgi.param.new_element;
d.["E${id}:field"].["A:width"] = 1;
d.["E${id}:field"].["A:height"] = 1;
d.["E${id}:field"].["A:posx"] = 0;
d.["E${id}:field"].["A:posy"] = 0;
d.["E${id}:field"].["A:type"] = "text";
}
if ( declared cgi.param.save )
save();
if ( declared cgi.param.run_dialog ) {
set_edit_mode(0);
show_edit_button = 1;
reset_values();
}
}
else
if ( declared cgi.param.delete )
{
delete d.[cgi.param.current];
edit_current = "";
}
else
{
var e = d.[cgi.param.current];
e.["A:name"] = cgi.param.set_name;
e.["A:width"] = cgi.param.set_width;
e.["A:height"] = cgi.param.set_height;
e.["A:posx"] = cgi.param.set_posx;
e.["A:posy"] = cgi.param.set_posy;
e.["A:type"] = cgi.param.set_type;
if ( cgi.param.set_code =~ /\S/ )
e.["E0:code"].["C0"] = cgi.param.set_code;
else
delete e.["E0:code"];
if ( cgi.param.set_value =~ /\S/ )
e.["E0:value"].["C0"] = cgi.param.set_value;
else
delete e.["E0:value"];
if ( declared cgi.param.update )
edit_current = "";
}
}
else
if ( show_edit_button && declared cgi.param.switch_edit_mode ) {
show_edit_button = 0;
set_edit_mode(1);
}
else
{
foreach f (d) {
var fn = d.[f].["A:name"];
if ( declared cgi.param.["${id}_${fn}"] )
values.[fn] = cgi.param.["${id}_${fn}"];
}
foreach f (d) {
var fn = d.[f].["A:name"];
if ( f =~ /^E\d+:field/ &&
fn ~== cgi.param.el &&
declared d.[f].["E0:code"].["C0"])
task_eval(d.[f].["E0:code"].["C0"], this);
}
}
dirty = 1;
}
}
/**
* Reset the dialog elements to their default values. This can also be
* used in the code snippets assigned to the dialog elements. To reset
* the dialog.
*/
method reset_values()
{
var d = xmltree.["E0:wsfdialog"];
foreach f (d) {
if ( f =~ /^E\d+:field/ ) {
values.[d.[f].["A:name"]] =
declared d.[f].["E0:value"] ?
d.[f].["E0:value"].["C0"] : undef;
}
}
dirty = 1;
}
/**
* Overwrite xml data. The argument is the XML text. This might also be
* called by one of the code snippets attached to dialog elements to
* switch to another dialog. (E.g. when clicking on a "Next" button.)
*/
method import_xml(xmldata)
{
if (not defined xmldata)
xmldata = '';
xmltree = format_xml_parse(xmldata);
reset_values();
}
/**
* The constructor.
*
* The parameter is the XML text containing the dialog configuration.
* If it is undef (or not specified at all), an empty dialog will be
* created.
*/
method init(xmldata)
{
*WsfComponent.init();
import_xml(xmldata);
return this;
}
}