first stab at file-select dialog. Initial target dir can be set in
/sim/gui/dialogs/file-select/directory, the selected file's full path is returned in /sim/gui/dialogs/file-select/directory. There will be a gui.file_selector class that allows to have more than one of these dialogs open, where each of them reports the result to a callback function. The dialog toggles dotfile display with ctrl-click on the "." entry, and switches to FG_ROOT and FG_HOME with ctrl-click and shift-click on the ".." entry. This dialog is ATM not used anywhere in fgfs, but might be needed by add-ons, much like the xml.nas code. Note that backslashes are converted to slashes, which should ensure that no characters can be sneaked in on UNIX systems. This breaks files containing escaped characters on MS Windows. Fix for that on request. :-) Special characters (like German umlauts) aren't displayed correctly, but properly returned. (Depending on the plib font.)
This commit is contained in:
parent
a41ba9f403
commit
8e4209c5cb
1 changed files with 219 additions and 0 deletions
219
gui/dialogs/file-select.xml
Normal file
219
gui/dialogs/file-select.xml
Normal file
|
@ -0,0 +1,219 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!--
|
||||
Ctrl-click on "." toggles display of hidden Unix filex (dotfiles)
|
||||
Ctrl-click on ".." enters $FG_ROOT
|
||||
Shift-click on ".." enters $FG_HOME
|
||||
-->
|
||||
|
||||
<PropertyList>
|
||||
<name>file-select</name>
|
||||
<dialog-name>file-select</dialog-name>
|
||||
<layout>vbox</layout>
|
||||
<pref-width>450</pref-width>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<empty><whatever/></empty>
|
||||
|
||||
<text>
|
||||
<property>/sim/gui/dialogs/file-select/title</property>
|
||||
<live>1</live>
|
||||
</text>
|
||||
|
||||
<empty><stretch>1</stretch></empty>
|
||||
|
||||
<button>
|
||||
<pref-width>16</pref-width>
|
||||
<pref-height>16</pref-height>
|
||||
<legend></legend>
|
||||
<default>1</default>
|
||||
<keynum>27</keynum>
|
||||
<border>2</border>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>close()</script>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<list>
|
||||
<name>list</name>
|
||||
<halign>fill</halign>
|
||||
<pref-height>350</pref-height>
|
||||
<property>/sim/gui/dialogs/file-select/selection</property>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
<object-name>list</object-name>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>select()</script>
|
||||
</binding>
|
||||
</list>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
|
||||
<input>
|
||||
<name>input</name>
|
||||
<pref-width>280</pref-width>
|
||||
<property>/sim/gui/dialogs/file-select/selection</property>
|
||||
<live>1</live>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
<object-name>input</object-name>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>input()</script>
|
||||
</binding>
|
||||
</input>
|
||||
|
||||
<button>
|
||||
<legend>OK</legend>
|
||||
<live>1</live>
|
||||
<pref-width>150</pref-width>
|
||||
<default>true</default>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>ok()</script>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
|
||||
<nasal>
|
||||
<open>
|
||||
var self = cmdarg();
|
||||
var list = self.getNode("list");
|
||||
|
||||
var dlgname = self.getNode("name").getValue();
|
||||
self.getNode("group/text/property").setValue("/sim/gui/dialogs/" ~ dlgname ~ "/title");
|
||||
self.getNode("list/property").setValue("/sim/gui/dialogs/" ~ dlgname ~ "/selection");
|
||||
self.getNode("group[1]/input/property").setValue("/sim/gui/dialogs/" ~ dlgname ~ "/selection");
|
||||
|
||||
var dlg = props.globals.getNode("/sim/gui/dialogs/" ~ dlgname, 1);
|
||||
var selection = dlg.getNode("selection", 1);
|
||||
var operation = dlg.getNode("operation", 1);
|
||||
var title = dlg.getNode("title", 1);
|
||||
var dir = dlg.getNode("directory", 1);
|
||||
var file = dlg.getNode("file", 1);
|
||||
var path = dlg.getNode("path", 1);
|
||||
var show_hidden = dlg.getNode("show-hidden", 1);
|
||||
show_hidden.setBoolValue(show_hidden.getValue());
|
||||
|
||||
var kbdctrl = props.globals.getNode("/devices/status/keyboard/ctrl", 1);
|
||||
var kbdshift = props.globals.getNode("/devices/status/keyboard/shift", 1);
|
||||
var root = getprop("/sim/fg-root");
|
||||
var current = { dir : "", file : "" };
|
||||
|
||||
var squeeze = func(s, n) {
|
||||
return n >= size(s) ? s : "... " ~ substr(s, size(s) - n);
|
||||
}
|
||||
|
||||
var update = func(d) {
|
||||
dir.setValue(d);
|
||||
title.setValue(squeeze(d, 45));
|
||||
list.removeChildren("value");
|
||||
var dents = directory(current.dir);
|
||||
if (dents == nil)
|
||||
return;
|
||||
var files = [];
|
||||
var dirs = [];
|
||||
var hide = !show_hidden.getValue();
|
||||
foreach (var e; dents) {
|
||||
if (e == "." or e == "..") {
|
||||
if (current.dir != "/")
|
||||
append(dirs, e);
|
||||
continue;
|
||||
}
|
||||
if (hide and e[0] == `.`)
|
||||
continue;
|
||||
|
||||
var stat = io.stat(current.dir ~ "/" ~ e);
|
||||
if (stat == nil) # dead link
|
||||
continue;
|
||||
|
||||
if (io.isdir(stat[2]))
|
||||
append(dirs, e ~ "/");
|
||||
else
|
||||
append(files, e);
|
||||
}
|
||||
var entries = sort(dirs, cmp) ~ sort(files, cmp);
|
||||
forindex (var i; entries)
|
||||
list.getChild("value", i, 1).setValue(entries[i]);
|
||||
}
|
||||
|
||||
var select = func {
|
||||
var e = selection.getValue();
|
||||
current.file = "";
|
||||
var new = nil;
|
||||
if (e == ".") {
|
||||
new = current.dir;
|
||||
if (kbdctrl.getValue())
|
||||
show_hidden.setBoolValue(!show_hidden.getValue());
|
||||
} elsif (e == "..") {
|
||||
if (kbdctrl.getValue())
|
||||
new = root;
|
||||
elsif (kbdshift.getValue())
|
||||
new = getprop("/sim/fg-home");
|
||||
else
|
||||
new = current.dir ~ "/..";
|
||||
} elsif (e[size(e) - 1] == `/`) {
|
||||
new = current.dir ~ "/" ~ e;
|
||||
} else {
|
||||
current.file = e;
|
||||
gui.dialog_update(dlgname, "input");
|
||||
}
|
||||
if (new != nil) {
|
||||
update(current.dir = io.fixpath(new));
|
||||
selection.setValue("");
|
||||
gui.dialog_update(dlgname, "list", "input");
|
||||
}
|
||||
}
|
||||
|
||||
var input = func {
|
||||
current.file = selection.getValue();
|
||||
}
|
||||
|
||||
var close = func {
|
||||
if (contains(globals.gui, "file_selector")
|
||||
and contains(gui.file_selector.instance, dlgname))
|
||||
gui.file_selector.instance[dlgname].close();
|
||||
}
|
||||
|
||||
var ok = func {
|
||||
input();
|
||||
file.setValue(current.file);
|
||||
path.setValue(current.dir ~ "/" ~ current.file);
|
||||
close();
|
||||
}
|
||||
|
||||
var op = operation.getValue();
|
||||
if (op == nil or op == "")
|
||||
op = "OK";
|
||||
self.getNode("group[1]/button/legend").setValue(op);
|
||||
|
||||
current.dir = (var d = dir.getValue()) != nil and d != "" ? d : root;
|
||||
current.file = (var d = file.getValue()) != nil and d != "" ? d : "";
|
||||
gui.dialog_update(dlgname, "input");
|
||||
update(io.fixpath(current.dir));
|
||||
</open>
|
||||
|
||||
<close>
|
||||
operation.setValue("");
|
||||
selection.setValue("");
|
||||
</close>
|
||||
</nasal>
|
||||
</PropertyList>
|
Loading…
Reference in a new issue