|
@@ -0,0 +1,450 @@
|
|
|
+package hide.comp;
|
|
|
+import js.jquery.Helper.*;
|
|
|
+
|
|
|
+private typedef Cursor = {
|
|
|
+ s : cdb.Sheet,
|
|
|
+ x : Int,
|
|
|
+ y : Int,
|
|
|
+ ?select : { x : Int, y : Int },
|
|
|
+ ?onchange : Void -> Void,
|
|
|
+}
|
|
|
+
|
|
|
+class SheetEditor extends Component {
|
|
|
+
|
|
|
+ static var UID = 0;
|
|
|
+
|
|
|
+ var base : cdb.Database;
|
|
|
+ var sheet : cdb.Sheet;
|
|
|
+ var cursor : Cursor;
|
|
|
+ var existsCache : Map<String,{ t : Float, r : Bool }> = new Map();
|
|
|
+
|
|
|
+ public function new(root, sheet) {
|
|
|
+ super(root);
|
|
|
+ this.sheet = sheet;
|
|
|
+ base = sheet.base;
|
|
|
+ cursor = {
|
|
|
+ s : null,
|
|
|
+ x : 0,
|
|
|
+ y : 0,
|
|
|
+ };
|
|
|
+ refresh();
|
|
|
+ }
|
|
|
+
|
|
|
+ function refresh() {
|
|
|
+
|
|
|
+ root.html('');
|
|
|
+ root.addClass('hide-sheet');
|
|
|
+
|
|
|
+ if( sheet.columns.length == 0 ) {
|
|
|
+ J("<a>Add a column</a>").appendTo(root).click(function(_) {
|
|
|
+ newColumn(sheet);
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var content = J("<table>");
|
|
|
+ fillTable(content, sheet);
|
|
|
+ content.appendTo(root);
|
|
|
+ }
|
|
|
+
|
|
|
+ function fillTable( content : Element, sheet : cdb.Sheet ) {
|
|
|
+
|
|
|
+ content.attr("sheet", sheet.getPath());
|
|
|
+
|
|
|
+ var cols = J("<tr>").addClass("head");
|
|
|
+ J("<th>").addClass("start").appendTo(cols);
|
|
|
+ var lines = [for( i in 0...sheet.lines.length ) {
|
|
|
+ var l = J("<tr>");
|
|
|
+ l.data("index", i);
|
|
|
+ var head = J("<td>").addClass("start").text("" + i);
|
|
|
+ l.mousedown(function(e) {
|
|
|
+ if( e.which == 3 ) {
|
|
|
+ head.click();
|
|
|
+ //haxe.Timer.delay(popupLine.bind(sheet,i),1);
|
|
|
+ e.preventDefault();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }).click(function(e) {
|
|
|
+ if( e.shiftKey && cursor.s == sheet && cursor.x < 0 ) {
|
|
|
+ cursor.select = { x : -1, y : i };
|
|
|
+ updateCursor();
|
|
|
+ } else
|
|
|
+ setCursor(sheet, -1, i);
|
|
|
+ });
|
|
|
+ head.appendTo(l);
|
|
|
+ l;
|
|
|
+ }];
|
|
|
+
|
|
|
+
|
|
|
+ var colCount = sheet.columns.length;
|
|
|
+ var todo = [];
|
|
|
+ var inTodo = false;
|
|
|
+ var types = [for( t in Type.getEnumConstructs(cdb.Data.ColumnType) ) t.substr(1).toLowerCase()];
|
|
|
+ for( cindex in 0...sheet.columns.length ) {
|
|
|
+ var c = sheet.columns[cindex];
|
|
|
+ var col = J("<th>");
|
|
|
+ col.text(c.name);
|
|
|
+ col.addClass( "t_"+c.type.getName().substr(1).toLowerCase() );
|
|
|
+ if( sheet.props.displayColumn == c.name )
|
|
|
+ col.addClass("display");
|
|
|
+ col.mousedown(function(e) {
|
|
|
+ if( e.which == 3 ) {
|
|
|
+ //haxe.Timer.delay(popupColumn.bind(sheet,c),1);
|
|
|
+ e.preventDefault();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ col.dblclick(function(_) {
|
|
|
+ newColumn(sheet, c);
|
|
|
+ });
|
|
|
+ cols.append(col);
|
|
|
+
|
|
|
+ var ctype = "t_" + types[Type.enumIndex(c.type)];
|
|
|
+ for( index in 0...sheet.lines.length ) {
|
|
|
+ var obj = sheet.lines[index];
|
|
|
+ var val : Dynamic = Reflect.field(obj,c.name);
|
|
|
+ var v = J("<td>").addClass(ctype).addClass("c");
|
|
|
+ var l = lines[index];
|
|
|
+ v.appendTo(l);
|
|
|
+
|
|
|
+ updateClasses(v, c, val);
|
|
|
+
|
|
|
+ var html = valueHtml(c, val, sheet, obj);
|
|
|
+ if( html == " " ) v.text(" ") else if( html.indexOf('<') < 0 && html.indexOf('&') < 0 ) v.text(html) else v.html(html);
|
|
|
+ v.data("index", cindex);
|
|
|
+ v.click(function(e) {
|
|
|
+ if( inTodo ) {
|
|
|
+ // nothing
|
|
|
+ } else if( e.shiftKey && cursor.s == sheet ) {
|
|
|
+ cursor.select = { x : cindex, y : index };
|
|
|
+ updateCursor();
|
|
|
+ e.stopImmediatePropagation();
|
|
|
+ } else
|
|
|
+ setCursor(sheet, cindex, index);
|
|
|
+ e.stopPropagation();
|
|
|
+ });
|
|
|
+
|
|
|
+ function set(val2:Dynamic) {
|
|
|
+ var old = val;
|
|
|
+ val = val2;
|
|
|
+ if( val == null )
|
|
|
+ Reflect.deleteField(obj, c.name);
|
|
|
+ else
|
|
|
+ Reflect.setField(obj, c.name, val);
|
|
|
+ html = valueHtml(c, val, sheet, obj);
|
|
|
+ v.html(html);
|
|
|
+ updateClasses(v, c, val);
|
|
|
+ //this.changed(sheet, c, index, old);
|
|
|
+ trace("CHANGED");
|
|
|
+ }
|
|
|
+
|
|
|
+ /* TODO */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( sheet.lines.length == 0 ) {
|
|
|
+ var l = J('<tr><td colspan="${sheet.columns.length + 1}"><a href="javascript:_.insertLine()">Insert Line</a></td></tr>');
|
|
|
+ l.find("a").click(function(_) setCursor(sheet));
|
|
|
+ lines.push(l);
|
|
|
+ }
|
|
|
+
|
|
|
+ content.empty();
|
|
|
+ content.append(cols);
|
|
|
+
|
|
|
+ var snext = 0;
|
|
|
+ for( i in 0...lines.length ) {
|
|
|
+ while( sheet.separators[snext] == i ) {
|
|
|
+ var sep = J("<tr>").addClass("separator").append('<td colspan="${colCount+1}">').appendTo(content);
|
|
|
+ var content = sep.find("td");
|
|
|
+ var title = if( sheet.props.separatorTitles != null ) sheet.props.separatorTitles[snext] else null;
|
|
|
+ if( title != null ) content.text(title);
|
|
|
+ var pos = snext;
|
|
|
+ sep.dblclick(function(e) {
|
|
|
+ content.empty();
|
|
|
+ J("<input>").appendTo(content).focus().val(title == null ? "" : title).blur(function(_) {
|
|
|
+ /*title = JTHIS.val();
|
|
|
+ JTHIS.remove();
|
|
|
+ content.text(title);
|
|
|
+ var titles = sheet.props.separatorTitles;
|
|
|
+ if( titles == null ) titles = [];
|
|
|
+ while( titles.length < pos )
|
|
|
+ titles.push(null);
|
|
|
+ titles[pos] = title == "" ? null : title;
|
|
|
+ while( titles[titles.length - 1] == null && titles.length > 0 )
|
|
|
+ titles.pop();
|
|
|
+ if( titles.length == 0 ) titles = null;
|
|
|
+ sheet.props.separatorTitles = titles;
|
|
|
+ save();*/
|
|
|
+ }).keypress(function(e) {
|
|
|
+ e.stopPropagation();
|
|
|
+ }).keydown(function(e) {
|
|
|
+ if( e.keyCode == 13 ) { JTHIS.blur(); e.preventDefault(); } else if( e.keyCode == 27 ) content.text(title);
|
|
|
+ e.stopPropagation();
|
|
|
+ });
|
|
|
+ });
|
|
|
+ snext++;
|
|
|
+ }
|
|
|
+ content.append(lines[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ inTodo = true;
|
|
|
+ for( t in todo ) t();
|
|
|
+ inTodo = false;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ function setCursor( ?s, ?x=0, ?y=0, ?sel, update = true ) {
|
|
|
+ cursor.s = s;
|
|
|
+ cursor.x = x;
|
|
|
+ cursor.y = y;
|
|
|
+ cursor.select = sel;
|
|
|
+ var ch = cursor.onchange;
|
|
|
+ if( ch != null ) {
|
|
|
+ cursor.onchange = null;
|
|
|
+ ch();
|
|
|
+ }
|
|
|
+ if( update ) updateCursor();
|
|
|
+ }
|
|
|
+
|
|
|
+ function getLine( sheet : cdb.Sheet, index : Int ) {
|
|
|
+ return J("table[sheet='"+sheet.getPath()+"'] > tbody > tr").not(".head,.separator,.list").eq(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateCursor() {
|
|
|
+ J(".selected").removeClass("selected");
|
|
|
+ J(".cursor").removeClass("cursor");
|
|
|
+ J(".cursorLine").removeClass("cursorLine");
|
|
|
+ if( cursor.s == null )
|
|
|
+ return;
|
|
|
+ if( cursor.y < 0 ) {
|
|
|
+ cursor.y = 0;
|
|
|
+ cursor.select = null;
|
|
|
+ }
|
|
|
+ if( cursor.y >= cursor.s.lines.length ) {
|
|
|
+ cursor.y = cursor.s.lines.length - 1;
|
|
|
+ cursor.select = null;
|
|
|
+ }
|
|
|
+ var max = cursor.s.props.isProps ? 1 : cursor.s.columns.length;
|
|
|
+ if( cursor.x >= max ) {
|
|
|
+ cursor.x = max - 1;
|
|
|
+ cursor.select = null;
|
|
|
+ }
|
|
|
+ var l = getLine(cursor.s, cursor.y);
|
|
|
+ if( cursor.x < 0 ) {
|
|
|
+ l.addClass("selected");
|
|
|
+ if( cursor.select != null ) {
|
|
|
+ var y = cursor.y;
|
|
|
+ while( cursor.select.y != y ) {
|
|
|
+ if( cursor.select.y > y ) y++ else y--;
|
|
|
+ getLine(cursor.s, y).addClass("selected");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ l.find("td.c").eq(cursor.x).addClass("cursor").closest("tr").addClass("cursorLine");
|
|
|
+ if( cursor.select != null ) {
|
|
|
+ var s = getSelection();
|
|
|
+ for( y in s.y1...s.y2 + 1 )
|
|
|
+ getLine(cursor.s, y).find("td.c").slice(s.x1, s.x2+1).addClass("selected");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var e = l[0];
|
|
|
+ if( e != null ) untyped e.scrollIntoViewIfNeeded();
|
|
|
+ }
|
|
|
+
|
|
|
+ public function valueHtml( c : cdb.Data.Column, v : Dynamic, sheet : cdb.Sheet, obj : Dynamic ) : String {
|
|
|
+ if( v == null ) {
|
|
|
+ if( c.opt )
|
|
|
+ return " ";
|
|
|
+ return '<span class="error">#NULL</span>';
|
|
|
+ }
|
|
|
+ return switch( c.type ) {
|
|
|
+ case TInt, TFloat:
|
|
|
+ switch( c.display ) {
|
|
|
+ case Percent:
|
|
|
+ (Math.round(v * 10000)/100) + "%";
|
|
|
+ default:
|
|
|
+ v + "";
|
|
|
+ }
|
|
|
+ case TId:
|
|
|
+ v == "" ? '<span class="error">#MISSING</span>' : (base.getSheet(sheet.name).index.get(v).obj == obj ? v : '<span class="error">#DUP($v)</span>');
|
|
|
+ case TString, TLayer(_):
|
|
|
+ v == "" ? " " : StringTools.htmlEscape(v);
|
|
|
+ case TRef(sname):
|
|
|
+ if( v == "" )
|
|
|
+ '<span class="error">#MISSING</span>';
|
|
|
+ else {
|
|
|
+ var s = base.getSheet(sname);
|
|
|
+ var i = s.index.get(v);
|
|
|
+ i == null ? '<span class="error">#REF($v)</span>' : (i.ico == null ? "" : tileHtml(i.ico,true)+" ") + StringTools.htmlEscape(i.disp);
|
|
|
+ }
|
|
|
+ case TBool:
|
|
|
+ v?"Y":"N";
|
|
|
+ case TEnum(values):
|
|
|
+ values[v];
|
|
|
+ case TImage:
|
|
|
+ ""; // deprecated
|
|
|
+ case TList:
|
|
|
+ var a : Array<Dynamic> = v;
|
|
|
+ var ps = sheet.getSub(c);
|
|
|
+ var out : Array<String> = [];
|
|
|
+ var size = 0;
|
|
|
+ for( v in a ) {
|
|
|
+ var vals = [];
|
|
|
+ for( c in ps.columns )
|
|
|
+ switch( c.type ) {
|
|
|
+ case TList, TProperties:
|
|
|
+ continue;
|
|
|
+ default:
|
|
|
+ vals.push(valueHtml(c, Reflect.field(v, c.name), ps, v));
|
|
|
+ }
|
|
|
+ var v = vals.length == 1 ? vals[0] : ""+vals;
|
|
|
+ if( size > 200 ) {
|
|
|
+ out.push("...");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ var vstr = v;
|
|
|
+ if( v.indexOf("<") >= 0 ) {
|
|
|
+ vstr = ~/<img src="[^"]+" style="display:none"[^>]+>/g.replace(vstr, "");
|
|
|
+ vstr = ~/<img src="[^"]+"\/>/g.replace(vstr, "[I]");
|
|
|
+ vstr = ~/<div id="[^>]+><\/div>/g.replace(vstr, "[D]");
|
|
|
+ }
|
|
|
+ size += vstr.length;
|
|
|
+ out.push(v);
|
|
|
+ }
|
|
|
+ if( out.length == 0 )
|
|
|
+ return "[]";
|
|
|
+ return out.join(", ");
|
|
|
+ case TProperties:
|
|
|
+ var ps = sheet.getSub(c);
|
|
|
+ var out = [];
|
|
|
+ for( c in ps.columns ) {
|
|
|
+ var pval = Reflect.field(v, c.name);
|
|
|
+ if( pval == null && c.opt ) continue;
|
|
|
+ out.push(c.name+" : "+valueHtml(c, pval, ps, v));
|
|
|
+ }
|
|
|
+ return out.join("<br/>");
|
|
|
+ case TCustom(name):
|
|
|
+ var t = base.getCustomType(name);
|
|
|
+ var a : Array<Dynamic> = v;
|
|
|
+ var cas = t.cases[a[0]];
|
|
|
+ var str = cas.name;
|
|
|
+ if( cas.args.length > 0 ) {
|
|
|
+ str += "(";
|
|
|
+ var out = [];
|
|
|
+ var pos = 1;
|
|
|
+ for( i in 1...a.length )
|
|
|
+ out.push(valueHtml(cas.args[i-1], a[i], sheet, this));
|
|
|
+ str += out.join(",");
|
|
|
+ str += ")";
|
|
|
+ }
|
|
|
+ str;
|
|
|
+ case TFlags(values):
|
|
|
+ var v : Int = v;
|
|
|
+ var flags = [];
|
|
|
+ for( i in 0...values.length )
|
|
|
+ if( v & (1 << i) != 0 )
|
|
|
+ flags.push(StringTools.htmlEscape(values[i]));
|
|
|
+ flags.length == 0 ? String.fromCharCode(0x2205) : flags.join("|<wbr>");
|
|
|
+ case TColor:
|
|
|
+ '<div class="color" style="background-color:#${StringTools.hex(v,6)}"></div>';
|
|
|
+ case TFile:
|
|
|
+ var path = ide.getPath(v);
|
|
|
+ var url = "file://" + path;
|
|
|
+ var ext = v.split(".").pop().toLowerCase();
|
|
|
+ var html = v == "" ? '<span class="error">#MISSING</span>' : StringTools.htmlEscape(v);
|
|
|
+ if( v != "" && !quickExists(path) )
|
|
|
+ html = '<span class="error">' + html + '</span>';
|
|
|
+ else if( ext == "png" || ext == "jpg" || ext == "jpeg" || ext == "gif" )
|
|
|
+ html = '<span class="preview">$html<div class="previewContent"><div class="label"></div><img src="$url" onload="$(this).parent().find(\'.label\').text(this.width+\'x\'+this.height)"/></div></span>';
|
|
|
+ if( v != "" )
|
|
|
+ html += ' <input type="submit" value="open" onclick="_.openFile(\'$path\')"/>';
|
|
|
+ html;
|
|
|
+ case TTilePos:
|
|
|
+ return tileHtml(v);
|
|
|
+ case TTileLayer:
|
|
|
+ var v : cdb.Types.TileLayer = v;
|
|
|
+ var path = ide.getPath(v.file);
|
|
|
+ if( !quickExists(path) )
|
|
|
+ '<span class="error">' + v.file + '</span>';
|
|
|
+ else
|
|
|
+ '#DATA';
|
|
|
+ case TDynamic:
|
|
|
+ var str = Std.string(v).split("\n").join(" ").split("\t").join("");
|
|
|
+ if( str.length > 50 ) str = str.substr(0, 47) + "...";
|
|
|
+ str;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function tileHtml( v : cdb.Types.TilePos, ?isInline ) {
|
|
|
+ var path = ide.getPath(v.file);
|
|
|
+ if( !quickExists(path) ) {
|
|
|
+ if( isInline ) return "";
|
|
|
+ return '<span class="error">' + v.file + '</span>';
|
|
|
+ }
|
|
|
+ var id = UID++;
|
|
|
+ var width = v.size * (v.width == null?1:v.width);
|
|
|
+ var height = v.size * (v.height == null?1:v.height);
|
|
|
+ var max = width > height ? width : height;
|
|
|
+ var zoom = max <= 32 ? 2 : 64 / max;
|
|
|
+ var inl = isInline ? 'display:inline-block;' : '';
|
|
|
+ var url = "file://" + path;
|
|
|
+ var html = '<div class="tile" id="_c${id}" style="width : ${Std.int(width * zoom)}px; height : ${Std.int(height * zoom)}px; background : url(\'$url\') -${Std.int(v.size*v.x*zoom)}px -${Std.int(v.size*v.y*zoom)}px; opacity:0; $inl"></div>';
|
|
|
+ html += '<img src="$url" style="display:none" onload="$(\'#_c$id\').css({opacity:1, backgroundSize : ((this.width*$zoom)|0)+\'px \' + ((this.height*$zoom)|0)+\'px\' '+(zoom > 1 ? ", imageRendering : 'pixelated'" : "") +'}); if( this.parentNode != null ) this.parentNode.removeChild(this)"/>';
|
|
|
+ return html;
|
|
|
+ }
|
|
|
+
|
|
|
+ function getSelection() {
|
|
|
+ if( cursor.s == null )
|
|
|
+ return null;
|
|
|
+ var x1 = if( cursor.x < 0 ) 0 else cursor.x;
|
|
|
+ var x2 = if( cursor.x < 0 ) cursor.s.columns.length-1 else if( cursor.select != null ) cursor.select.x else x1;
|
|
|
+ var y1 = cursor.y;
|
|
|
+ var y2 = if( cursor.select != null ) cursor.select.y else y1;
|
|
|
+ if( x2 < x1 ) {
|
|
|
+ var tmp = x2;
|
|
|
+ x2 = x1;
|
|
|
+ x1 = tmp;
|
|
|
+ }
|
|
|
+ if( y2 < y1 ) {
|
|
|
+ var tmp = y2;
|
|
|
+ y2 = y1;
|
|
|
+ y1 = tmp;
|
|
|
+ }
|
|
|
+ return { x1 : x1, x2 : x2, y1 : y1, y2 : y2 };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ function quickExists(path) {
|
|
|
+ var c = existsCache.get(path);
|
|
|
+ if( c == null ) {
|
|
|
+ c = { t : -1e9, r : false };
|
|
|
+ existsCache.set(path, c);
|
|
|
+ }
|
|
|
+ var t = haxe.Timer.stamp();
|
|
|
+ if( c.t < t - 10 ) { // cache result for 10s
|
|
|
+ c.r = sys.FileSystem.exists(path);
|
|
|
+ c.t = t;
|
|
|
+ }
|
|
|
+ return c.r;
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateClasses(v:Element, c:cdb.Data.Column, val:Dynamic) {
|
|
|
+ switch( c.type ) {
|
|
|
+ case TBool :
|
|
|
+ v.removeClass("true, false").addClass( val==true ? "true" : "false" );
|
|
|
+ case TInt, TFloat :
|
|
|
+ v.removeClass("zero");
|
|
|
+ if( val==0 )
|
|
|
+ v.addClass("zero");
|
|
|
+ default:
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function newColumn( sheet : cdb.Sheet, ?after ) {
|
|
|
+ }
|
|
|
+
|
|
|
+ public dynamic function save() {
|
|
|
+ }
|
|
|
+
|
|
|
+}
|