PageRenderTime 104ms CodeModel.GetById 2ms app.highlight 93ms RepoModel.GetById 1ms app.codeStats 0ms

/src/xf/nucled/Graph.d

https://bitbucket.org/h3r3tic/boxen
D | 1478 lines | 1094 code | 287 blank | 97 comment | 203 complexity | c2a232ce94c2c9177abfbcd9820bc40d MD5 | raw file
   1module xf.nucled.Graph;
   2
   3private {
   4	import xf.Common;
   5
   6	import xf.nucleus.Param;
   7	import xf.nucleus.TypeSystem;
   8	import xf.nucleus.kdef.model.IKDefUtilParser;
   9	import xf.nucleus.kdef.model.KDefInvalidation;
  10	import xf.nucleus.kdef.model.IKDefRegistry;
  11	import xf.nucleus.kdef.Common : KDefGraph = GraphDef, KDefGraphNode = GraphDefNode, ParamListValue, GraphDefValue, KernelDefValue, KernelDef, KernelImpl;
  12	import xf.nucleus.Value;
  13	import xf.nucleus.Function;
  14	import xf.nucleus.Nucleus;
  15	import xf.nucled.DataProvider;
  16	
  17	import xf.hybrid.Hybrid;
  18	import xf.hybrid.Common;
  19	import xf.hybrid.CustomWidget;
  20	import xf.omg.core.LinearAlgebra;
  21	import xf.nucled.Widgets;
  22	import xf.nucled.Misc;
  23	import xf.nucled.Settings;
  24	import xf.nucled.Log : log = nucledLog;
  25	import xf.nucled.DynamicGridInput;
  26	import xf.utils.Array : arrayRemove = remove;
  27
  28	import xf.mem.ChunkQueue;
  29	import xf.mem.Gather;
  30
  31	static import xf.utils.Array;
  32	
  33	//import xf.utils.OldCfg : Config = Array;
  34	import xf.core.Registry;
  35	import tango.text.convert.Format;
  36	import TextUtil = tango.text.Util;
  37	import tango.io.stream.Format;
  38	import tango.io.vfs.model.Vfs : VfsFolder, VfsFile;
  39	import tango.io.device.File : FileConduit = File;
  40	import tango.io.stream.Lines : Lines;
  41	
  42	import tango.io.Stdout;
  43}
  44
  45
  46
  47class Graph {
  48	this(GraphMngr mngr) {
  49		this._mngr = mngr;
  50		mngr.register(this);
  51	}
  52	
  53	
  54	void addNode(GraphNode node) {
  55		_nodes ~= node;
  56		node._mngr = this._mngr;
  57	}
  58	
  59	
  60	void clearNodes() {
  61		// TODO: clean up the nodes?
  62		_nodes.length = 0;
  63	}
  64	
  65	
  66	int iterAllConnections(int delegate(ref Connection) dg) {
  67		foreach (n; _nodes) {
  68			foreach (o; n.outgoing) {
  69				if (auto r = dg(o)) {
  70					return r;
  71				}
  72			}
  73		}
  74		
  75		return 0;
  76	}
  77	
  78	
  79	GraphNode[] nodes() {
  80		return _nodes;
  81	}
  82	
  83
  84	void doGUI() {
  85		GraphNode nodeToDelete = null;
  86		foreach (n; _nodes) {
  87			if (!n.doGUI) {
  88				nodeToDelete = n;
  89			}
  90		}
  91		
  92		if (nodeToDelete !is null) {
  93			_nodes.arrayRemove(nodeToDelete);
  94			nodeToDelete.unlink;
  95		}
  96	}
  97	
  98	
  99	void dump(KDefGraph kdef, IKDefRegistry reg) {
 100		kdef._name = null;	// TODO
 101		kdef._nodes = kdef.mem.allocArrayNoInit!(KDefGraphNode)(this.nodes.length);
 102		kdef._nodeNames = kdef.mem.allocArrayNoInit!(cstring)(this.nodes.length);
 103
 104		foreach (ni, n; this.nodes) {
 105			VarDef[1] vars;
 106			vars[0] = VarDef("type", kdef.mem._new!(IdentifierValue)(n.typeName()));
 107
 108			kdef._nodes[ni] = kdef.mem._new!(KDefGraphNode)(
 109				vars[],
 110				kdef.mem._allocator
 111			);
 112			n.dump(kdef._nodes[ni], reg);
 113			kdef._nodeNames[ni] = kdef.mem.dupString(n.label);
 114		}
 115
 116		{
 117			alias GraphDef.NodeConnection NC;
 118
 119			gatherArrays!(NC)(kdef.mem,
 120			(void delegate(lazy NC) gen) {
 121				foreach (n; nodes) {
 122					foreach (con; n.outgoing) {
 123						foreach (flow; con.flow) {
 124							if (depOutputConnectorName == flow.from) {
 125								gen(NC(
 126									kdef._nodes[xf.utils.Array.indexOf(nodes, con.from)],
 127									kdef._nodes[xf.utils.Array.indexOf(nodes, con.to)]
 128								));
 129							}
 130						}
 131					}
 132				}
 133			},
 134			(NC[] nodeCons) {
 135				kdef.nodeConnections = nodeCons;
 136			});
 137		}
 138
 139		{
 140			alias GraphDef.NodeFieldConnection NFC;
 141
 142			gatherArrays!(NFC)(kdef.mem,
 143			(void delegate(lazy NFC) gen) {
 144				foreach (n; nodes) {
 145					foreach (con; n.outgoing) {
 146						foreach (flow; con.flow) {
 147							if (depOutputConnectorName != flow.from) {
 148								gen(NFC(
 149									kdef._nodes[xf.utils.Array.indexOf(nodes, con.from)],
 150									kdef._nodes[xf.utils.Array.indexOf(nodes, con.to)],
 151
 152									// duplicated later --->
 153									flow.from,
 154									flow.to
 155								));
 156							}
 157						}
 158					}
 159				}
 160			},
 161			(NFC[] nodeCons) {
 162				foreach (ref c; nodeCons) {
 163					// <---
 164					c.from = kdef.mem.dupString(c.from);
 165					c.to = kdef.mem.dupString(c.to);
 166				}
 167				kdef.nodeFieldConnections = nodeCons;
 168			});
 169		}
 170
 171		{
 172			alias GraphDef.NoAutoFlow NAF;
 173
 174			gatherArrays!(NAF)(kdef.mem,
 175			(void delegate(lazy NAF) gen) {
 176				foreach (ni, n; nodes) {
 177					foreach (i; n.inputs) {
 178						if (i.noAutoFlow) {
 179							gen(NAF(
 180								kdef._nodes[ni],
 181								kdef.mem.dupString(i.name)
 182							));
 183						}
 184					}
 185				}
 186			},
 187			(NAF[] noAutoFlow) {
 188				kdef.noAutoFlow = noAutoFlow;
 189			});
 190		}
 191	}
 192	
 193	
 194	void load(KDefGraph source) {
 195		Stdout.formatln("Graph.load() called");
 196		
 197		GraphNode[void*] def2node;
 198		
 199		foreach (nname, kdefNode; source.nodes) {
 200			auto node = new GraphNode(kdefNode);
 201			def2node[cast(void*)kdefNode] = node;
 202			node.label = nname.dup;
 203			addNode(node);
 204			Stdout.formatln("loading a graph node");
 205		}
 206		
 207		foreach (ncon; source.nodeConnections) {
 208			new Connection(
 209				def2node[cast(void*)ncon.from],
 210				def2node[cast(void*)ncon.to],
 211				DataFlow(depOutputConnectorName, depInputConnectorName)
 212			);
 213		}
 214		
 215		foreach (nfcon; source.nodeFieldConnections) {
 216			new Connection(
 217				def2node[cast(void*)nfcon.fromNode],
 218				def2node[cast(void*)nfcon.toNode],
 219				DataFlow(nfcon.from.dup, nfcon.to.dup)
 220			);
 221		}
 222
 223		foreach (naf; source.noAutoFlow) {
 224			final node = def2node[cast(void*)naf.toNode];
 225			foreach (ref input; node.inputs) {
 226				if (naf.to == input.name) {
 227					input.noAutoFlow = true;
 228				}
 229			}
 230		}
 231		
 232		foreach (lo; loadObservers) {
 233			lo(this);
 234		}
 235	}
 236
 237
 238	protected {
 239		GraphMngr	_mngr;
 240		GraphNode[]	_nodes;
 241	}
 242	
 243	public {
 244		void delegate(Graph)[]	loadObservers;
 245	}
 246}
 247
 248
 249
 250
 251class ConnectorInfo {
 252	char[]		name;
 253	GraphNode	node;
 254	vec2		windowPos = vec2.zero;
 255	bool		noAutoFlow = false;
 256	
 257	
 258	this(Param param, GraphNode node) {
 259		this.name = param.name.dup;
 260		this.node = node;
 261	}
 262}
 263
 264
 265
 266
 267interface NodeContents {
 268	void	doGUI();
 269	void	refresh();
 270}
 271
 272
 273
 274struct DataFlow {
 275	char[]	from;
 276	char[]	to;
 277}
 278
 279
 280
 281class Connection {
 282	this(GraphNode from, GraphNode to, DataFlow[] flow ...) {
 283		this.from = from;
 284		this.to = to;
 285		this.flow = flow.dup;
 286		
 287		from.addOutgoingConnection(this);
 288		to.addIncomingConnection(this);
 289	}
 290	
 291
 292	void unlink() {
 293		void remFrom(ref Connection[] cl) {
 294			foreach (i, c; cl) {
 295				if (this is c) {
 296					cl[i] = cl[$-1];
 297					cl = cl[0..$-1];
 298					return;
 299				}
 300			}
 301			assert (false, `not found`);
 302		}
 303		
 304		assert (from !is null);
 305		assert (to !is null);
 306		remFrom(from.outgoing);
 307		remFrom(to.incoming);
 308	}
 309
 310	
 311	GraphNode	from;
 312	GraphNode	to;
 313	DataFlow[]	flow;
 314}
 315
 316
 317ConnectorInfo find(ConnectorInfo[] arr, char[] name) {
 318	foreach (ref x; arr) {
 319		if (x.name == name) {
 320			return x;
 321		}
 322	}
 323	return null;
 324}
 325
 326
 327
 328class GraphNode {
 329	enum Type {
 330		Calc,
 331		Data,
 332		GPUWrap,
 333		Demux,
 334		Query,
 335		Input,
 336		Output
 337	}
 338
 339	
 340	this(Type t) {
 341		_mem.initialize();
 342		this._id = g_nextId++;
 343		this.data = new DataCommons;
 344		this.data.params._allocator = &_mem.pushBack;
 345		this.type = t;
 346		
 347		if (type != Type.Input && type != Type.Data) {
 348			Param meh;
 349			// works because the arg is a const string
 350			meh.unsafeOverrideName(depInputConnectorName);
 351			this.inputs ~= new ConnectorInfo(meh, this);
 352		}
 353		
 354		if (type != Type.Output) {
 355			Param meh;
 356			// works because the arg is a const string
 357			meh.unsafeOverrideName(depOutputConnectorName);
 358			this.outputs ~= new ConnectorInfo(meh, this);
 359		}
 360	}
 361	
 362
 363	this(Type t, char[] kernelName) {
 364		this(t);
 365		this._kernelName = kernelName;
 366		if (isKernelBased) {
 367			createKernelNodeInputs();
 368		}
 369	}
 370
 371
 372	private void createKernelNodeInputs() {
 373		auto impl = getKernel();
 374		
 375		if (KernelImpl.Type.Kernel == impl.type) {
 376			final kernel = impl.kernel;
 377			final func = kernel.func;
 378
 379			foreach (param; func.params) {
 380				Stdout.formatln(`Creating a param '{}'`, param.name);
 381				if (param.isInput) {
 382					inputs ~= new ConnectorInfo(param, this);
 383				} else {
 384					outputs ~= new ConnectorInfo(param, this);
 385				}
 386			}
 387		} else {
 388			final graph = cast(GraphDef)impl.graph;
 389			foreach (name, n; graph.nodes) {
 390				if ("input" == n.type) {
 391					foreach (param; n.params) {
 392						inputs ~= new ConnectorInfo(param, this);
 393					}
 394				} else if ("output" == n.type) {
 395					foreach (param; n.params) {
 396						outputs ~= new ConnectorInfo(param, this);
 397					}
 398				}
 399			}
 400		}
 401	}
 402	
 403	
 404	this (KDefGraphNode cfg) {
 405		//_mem.initialize();
 406
 407		char[] identVal(char[] name) {
 408			return cfg.getVar(name).as!(IdentifierValue).value;
 409		}
 410
 411		this(typeFromString(identVal("type")));
 412		
 413		if (auto sp = cfg.getVar("center")) {
 414			this.spawnPosition = vec2.from(sp.as!(Vector2Value).value);
 415		}
 416		
 417		// there's also the size, but we'll ignore it for now
 418		
 419		if (this.isKernelBased) {
 420			final val = cfg.getVar("kernel");
 421			if (auto kd = cast(KernelDefValue)val) {
 422				_kernelName = "inline";
 423				_isInline = true;
 424				_inlineKernel = kd.kernelDef;
 425				//assert (false, "TODO: inline kernels");
 426			} else if (auto gd = cast(GraphDefValue)val) {
 427				_kernelName = "inline";
 428				_isInline = true;
 429				_inlineGraph = gd.graphDef;
 430				//assert (false, "TODO: inline graphs");
 431			} else {
 432				_kernelName = identVal("kernel").dup;
 433			}
 434
 435			createKernelNodeInputs();
 436			// TODO: ParamValueInfo
 437		} else {
 438			foreach (param; cfg.params) {
 439				this.data.params.add(param);
 440				paramValueInfo ~= ParamValueInfo();
 441				
 442				if (Type.Output == this.type) {
 443					inputs ~= new ConnectorInfo(param, this);
 444				} else {
 445					outputs ~= new ConnectorInfo(param, this);
 446				}
 447			}
 448		}
 449	}
 450
 451
 452	// TODO: mem
 453	void addInput(Param p) {
 454		inputs ~= new ConnectorInfo(p, this);
 455		if (!this.isKernelBased) {
 456			this.data.params.add(p).dir = ParamDirection.In;
 457		}
 458		paramValueInfo ~= ParamValueInfo();
 459	}
 460		
 461
 462	// TODO: mem
 463	void addOutput(Param p) {
 464		outputs ~= new ConnectorInfo(p, this);
 465		if (!this.isKernelBased) {
 466			this.data.params.add(p).dir = ParamDirection.Out;
 467		}
 468		paramValueInfo ~= ParamValueInfo();
 469	}
 470
 471	
 472	uint id() {
 473		return this._id;
 474	}
 475	
 476	
 477	int iterInputs(int delegate(ref int i, ref ConnectorInfo) dg) {
 478		int i;
 479		foreach (ref x; inputs) {
 480			if (auto r = dg(i, x)) {
 481				return r;
 482			}
 483			++i;
 484		}
 485		return 0;
 486	}
 487	
 488
 489	int iterOutputs(int delegate(ref int i, ref ConnectorInfo) dg) {
 490		int i;
 491		foreach (ref x; outputs) {
 492			if (auto r = dg(i, x)) {
 493				return r;
 494			}
 495			++i;
 496		}
 497		return 0;
 498	}
 499	
 500	
 501	/+char[] title() {
 502		return Format("{} ({}) : {}", _funcName == "main" ? _kernelName : _kernelName ~ ":" ~ _funcName, this.id, primLevelStr);
 503	}+/
 504	
 505	void getTitle(Cb)(Cb res) {
 506		char[256] buf;
 507		uint bufPtr = 0;
 508		auto sink = (char[] s) {
 509			uint to = bufPtr+s.length;
 510			if (to > buf.length) {
 511				to = buf.length;
 512			}
 513			buf[bufPtr..to] = s;
 514			bufPtr = to;
 515			return bufPtr;
 516		};
 517		
 518		char[] label = _kernelName;
 519		switch (type) {
 520			case Type.Data: label = "Data"; break;
 521			case Type.Input: label = "Input"; break;
 522			case Type.Output: label = "Output"; break;
 523			default: break;
 524		}
 525		Format.convert(sink, "{} ({})", label, this.id);
 526		
 527		res(buf[0..bufPtr]);
 528	}
 529	
 530	
 531	/+char[] primLevelStr() {
 532		return CPU ? "cpu" : (primLevel == PrimLevel.Vertex ? "vtx" : (primLevel == PrimLevel.Fragment ? "frag" : "wtf"));
 533	}
 534	
 535	
 536	void setPrimLevel(char[] str) {
 537		switch (str) {
 538			case "cpu":
 539				CPU = true;
 540				break;
 541			case "vtx":
 542				CPU = false;
 543				primLevel = PrimLevel.Vertex;
 544				break;
 545			case "frag":
 546				CPU = false;
 547				primLevel = PrimLevel.Fragment;
 548				break;
 549			default: assert (false, str);
 550		}
 551	}+/
 552	
 553	
 554	void dump(KDefGraphNode kdef, IKDefRegistry reg) {
 555		if (this.isKernelBased) {
 556			kdef.kernelImpl = getKernel();
 557		} else {
 558			kdef.params = data.params;
 559		}
 560	}
 561	
 562	
 563	char[] typeName() {
 564		switch (this.type) {
 565			case Type.Calc:		return "kernel";
 566			case Type.Data:		return "data";
 567			case Type.Input:	return "input";
 568			case Type.Output:	return "output";
 569			default: assert (false);
 570		}
 571	}
 572	
 573	
 574	static Type typeFromString(char[] type) {
 575		switch (type) {
 576			case "kernel":		return Type.Calc;
 577			case "data":		return Type.Data;
 578			case "input":		return Type.Input;
 579			case "output":		return Type.Output;
 580			default: assert (false, type);
 581		}
 582	}
 583	
 584	
 585	bool doGUI() {
 586		auto box = _widget = GraphNodeBox(this.id);
 587		getTitle((char[] t) { box.label = t; });
 588				
 589		this.currentCenter = box.globalOffset + box.size * 0.5f;
 590		this.currentSize = box.size;
 591		
 592		if (!box.initialized) {
 593			box.parentOffset = spawnPosition;
 594
 595			switch (this.type) {
 596				case Type.Calc:
 597					break;
 598				default:
 599					box.enableStyle(this.typeName);
 600					break;
 601			}
 602			
 603			box.addHandler(&clickHandler);
 604		} else {
 605			this.spawnPosition = box.parentOffset;
 606		}
 607		
 608		int inputToRemove = -1;
 609		
 610		box.open(`inputs`);
 611		foreach (i, ref data; &iterInputs) {
 612			if (Type.Demux == this.type) {
 613				if (i >= inputs.length / 2) {
 614					break;
 615				}
 616			}
 617			
 618			HBox(i) [{
 619				DataConnector con;
 620				if (!data.noAutoFlow) {
 621					con = DataConnector();
 622					con.layoutAttribs("vexpand");
 623				}
 624				
 625				if (showDataNames) {
 626					final label = Label().text(data.name).fontSize(10).valign(1).layoutAttribs("vexpand vfill");
 627					if (con) {
 628						label.style.color.value = vec4(1, 1, 1, 1);
 629					} else {
 630						label.style.color.value = vec4(1, 1, 1, 0.2);
 631					}
 632					auto brk = ConnectionBreaker();
 633					brk.layoutAttribs("vexpand");
 634					if (brk.clicked) {
 635						inputToRemove = i;
 636					}
 637				}
 638
 639				if (con) {
 640					con.input = true;
 641					con.ci = data;
 642					con.mouseButtonHandler = &_mngr.handleMouseButton;
 643					data.windowPos = con.globalOffset + con.size * 0.5f;
 644				}
 645			}].layoutAttribs = "hexpand hfill";
 646		}
 647		gui.close;
 648		
 649		if (showContents && contents !is null) {
 650			box.open(`contents`);
 651			contents.doGUI();
 652			gui.close;
 653		}
 654		
 655		int outputToRemove = -1;
 656		
 657		box.open(`outputs`);
 658			foreach (i, ref data; &iterOutputs) {
 659			HBox(i) [{
 660				Dummy().layoutAttribs("hexpand hfill");
 661				if (showDataNames) {
 662					auto brk = ConnectionBreaker();
 663					brk.layoutAttribs("vexpand");
 664					if (brk.clicked) {
 665						outputToRemove = i;
 666					}
 667					Label().text(data.name).fontSize(10).valign(1).layoutAttribs("vexpand vfill");
 668				}
 669
 670				auto con = DataConnector();
 671				con.layoutAttribs("vexpand");
 672				con.input = false;
 673				con.ci = data;
 674				con.mouseButtonHandler = &_mngr.handleMouseButton;
 675				data.windowPos = con.globalOffset + con.size * 0.5f;
 676			}].layoutAttribs = "hexpand hfill";
 677		}
 678		gui.close;
 679		
 680		if (inputToRemove != -1) {
 681			bool removed = removeIncomingConnectionsTo(inputs[inputToRemove].name);
 682			if (!removed) {
 683				inputs[inputToRemove].noAutoFlow ^= true;
 684			}
 685		}
 686		if (outputToRemove != -1) {
 687			removeOutgoingConnectionsFrom(outputs[outputToRemove].name);
 688		}
 689		
 690		/+bool allowGPU = this.type != Type.GPUWrap;
 691		if (allowGPU) {
 692			box.open(`bottom`); {
 693				bool allowCPU = this.type != Type.Demux && this.type != Type.Query;
 694				
 695				XorSelector grp;
 696				
 697				int cpuIndex = -1;
 698				XCheck cpu;
 699				if (allowCPU) {
 700					cpu			= XCheck().text("c").group(grp);
 701					cpuIndex	= 0;
 702				}
 703				auto vertex		= XCheck().text("v").group(grp);
 704				auto fragment	= XCheck().text("f").group(grp);
 705
 706				if (this.CPU) {
 707					assert (cpu !is null);
 708					DefaultOption = cpu;
 709				} else {
 710					if (PrimLevel.Fragment == this.primLevel) {
 711						DefaultOption = fragment;
 712					} else {
 713						DefaultOption = vertex;
 714					}
 715				}
 716				
 717				this.CPU = cpuIndex == grp.index;
 718				
 719				if (cpuIndex+1 == grp.index) {
 720					this.primLevel = PrimLevel.Vertex;
 721				}
 722				else if (cpuIndex+2 == grp.index) {
 723					this.primLevel = PrimLevel.Fragment;
 724				}
 725			}
 726			gui.close;
 727		} else {
 728			assert (this.CPU);
 729		}+/
 730
 731		bool wasEditingProps = _editingProps;
 732		if (box.doubleClicked) {
 733			_editingProps = true;
 734		}
 735		
 736		if (_editingProps) {
 737			auto frame = FloatingWindow(this.id);
 738			frame [{
 739				doEditorGUI(!wasEditingProps, frame.wantsToClose);
 740			}];
 741			//frame.text = this.title;
 742			getTitle((char[] t) { frame.text = t; });
 743			if (frame.wantsToClose) {
 744				_editingProps = false;
 745			}
 746			
 747			if (!wasEditingProps) {
 748				frame.parentOffset = box.parentOffset + vec2(5, box.size.y+5);
 749			}
 750		}
 751		
 752		return !box.deleteClicked;
 753	}
 754	
 755	
 756	void addOutgoingConnection(Connection con) {
 757		outgoing ~= con;
 758	}
 759
 760
 761	void addIncomingConnection(Connection con) {
 762		incoming ~= con;
 763	}
 764	
 765	
 766	bool isKernelBased() {
 767		switch (this.type) {
 768			case Type.Calc:
 769			case Type.GPUWrap:
 770			case Type.Demux:
 771			case Type.Query:
 772				return true;
 773				
 774			case Type.Data:
 775			case Type.Input:
 776			case Type.Output:
 777				return false;
 778
 779			default: assert (false);
 780		}
 781	}
 782
 783
 784	KernelImpl getKernel() {
 785		if (_isInline) {
 786			return _inlineGraph
 787				? KernelImpl(_inlineGraph)
 788				: KernelImpl(_inlineKernel);
 789		} else {
 790			return kdefRegistry.getKernel(kernelName);
 791		}
 792	}
 793	
 794	
 795	bool		_choosingImpl;
 796	//QuarkDef	_quarkBeingEdited;
 797	
 798	void doEditorGUI(bool justOpened, bool closing) {
 799		if (isKernelBased) {
 800			if (!_inlineGraph) {
 801				doCodeEditorGUI(justOpened, closing);
 802			} else {
 803				// TODO: graph editor
 804			}
 805		} else {
 806			doDataEditorGUI(justOpened, closing);
 807		}
 808	}
 809	
 810	
 811	char[] safeName(char[] prev, char[] name) {
 812		if (!data.params.get(name)) {
 813			return name;
 814		} else {
 815			if (prev.length > 0) {
 816				return prev;
 817			}
 818			
 819			for (int i = 1; i < 10000; ++i) {
 820				auto tmp = Format("{}{}", name, i);
 821				if (!data.params.get(tmp)) {
 822					return tmp;
 823				}
 824			}
 825		}
 826		assert (false);
 827	}
 828	
 829	
 830	static class QuarkSciEditor : SciEditor {
 831		// in the loaded source
 832		uint		firstByte;
 833		uint		lastByte;
 834		KDefModule	kdefMod;
 835
 836		
 837		void editorSaveHandler() {
 838			if (kdefMod is null) {
 839				return;
 840			}
 841			
 842			auto sourceVfsFile = _outer.getVfsFile(kdefMod);
 843
 844			char[] text; {
 845				auto sourceFile = sourceVfsFile.input;
 846				scope (exit) sourceFile.close;
 847				text = cast(char[])sourceFile.load();
 848			}
 849
 850			// TODO: check for potential external modifications to the file
 851			
 852			try {
 853				if (sourceVfsFile.exists) {
 854					sourceVfsFile.remove;
 855				}
 856			} catch {}		// we don't want failz here. better try to write the contents anyway when shit hits the fan
 857
 858			try {
 859				sourceVfsFile.create;
 860			} catch {}		// ditto
 861
 862			auto dstFile = sourceVfsFile.output;
 863			scope (exit) dstFile.flush.close;
 864			dstFile.write(text[0..firstByte] ~ this.text ~ text[lastByte..$]);
 865		}
 866		
 867
 868		override protected EventHandling handleKey(KeyboardEvent e) {
 869			if (KeySym.s == e.keySym && (e.modifiers & e.modifiers.CTRL) != 0) {
 870				if (e.sinking && e.down) {
 871					this.editorSaveHandler();
 872				}
 873				return EventHandling.Stop;
 874			} else {
 875				return super.handleKey(e);
 876			}
 877		}
 878		
 879		GraphNode _outer;
 880		mixin MWidget;
 881	}
 882	
 883	
 884	VfsFile getVfsFile(KDefModule mod) {
 885		assert (mod !is null);
 886		final vfs = kdefRegistry.kdefFileParser.getVFS();
 887		assert (vfs !is null);
 888		
 889		if (auto sourceVfsFile = vfs.file(mod.filePath)) {
 890			log.trace("mod file: {} [ {} ]", sourceVfsFile.name, sourceVfsFile.toString);
 891			return sourceVfsFile;
 892		} else {
 893			log.error("Could not get load the source for module: '{}'.", mod.filePath);
 894			return null;
 895		}
 896	}
 897	
 898	
 899	void doCodeEditorGUI(bool justOpened, bool closing) {
 900		//assert (quark !is null);
 901		
 902		auto sci = QuarkSciEditor();
 903		sci._outer = this;
 904		
 905		if (justOpened) {
 906			KernelDef kernel = _isInline
 907				? _inlineKernel
 908				: kdefRegistry.getKernel(_kernelName).kernel;
 909			
 910			if (kernel) {
 911				if (auto func = cast(Function)kernel.func) {
 912					if (func.code._lengthBytes > 0) {
 913						if (auto mod = cast(KDefModule)func.code._module) {
 914							if (auto sourceVfsFile = getVfsFile(mod)) {
 915								auto sourceFile = sourceVfsFile.input;
 916								scope (exit) sourceFile.close;
 917								
 918								char[] text = cast(char[])sourceFile.load();
 919
 920								uint first = func.code._firstByte;
 921								uint last = first + func.code._lengthBytes;
 922
 923								// expand the range to include all the spaces and
 924								// tabs preceding the first token in the code
 925								char c;
 926								while (
 927									first > 0 &&
 928									((c = text[first-1]) == ' ' || c == '\t')
 929								) --first;
 930								
 931								sci.text = text[first..last];
 932
 933								sci.firstByte = first;
 934								sci.lastByte = last;
 935								sci.kdefMod = mod;
 936							} else {
 937								log.warn("Failed to load code for the kernel '{}': unable to load file.", _kernelName);
 938							}
 939						} else {
 940							log.warn("Failed to load code for the kernel '{}': _module is null.", _kernelName);
 941						}
 942					}
 943				} else {
 944					log.info("Can't edit kernel '{}': it is abstract.", _kernelName);
 945				}
 946			} else {
 947				log.warn("Failed to get the kernel '{}' for a code editor.", _kernelName);
 948			}
 949
 950			sci.grabKeyboardFocus();
 951		}
 952		
 953		sci.userSize = vec2(300, 80);
 954	}
 955	
 956	
 957	void doDataEditorGUI(bool justOpened, bool closing) {
 958		auto grid = DynamicGridInput();
 959		
 960		struct UserData {
 961			char[][int] semanticsEdited;
 962		}
 963		
 964		UserData* userData = justOpened ? null : cast(UserData*)grid.userData;
 965		if (userData is null) {
 966			grid.userData = userData = new UserData;
 967		}
 968		
 969		if (justOpened) {
 970			grid.popupMsg = null;
 971		}
 972		
 973		DynamicGridInputModel model;
 974		model.onAddRow = {
 975			char[] newName = safeName(null, "noname");
 976			if (Type.Output == this.type) {
 977				auto p = data.params.add(ParamDirection.In, newName);
 978				p.hasPlainSemantic = true;
 979				p.type = "void";
 980				inputs ~= new ConnectorInfo(*p, this);
 981			} else {
 982				auto p = data.params.add(ParamDirection.Out, newName);
 983				p.hasPlainSemantic = true;
 984				p.type = "void";
 985				outputs ~= new ConnectorInfo(*p, this);
 986			}
 987			paramValueInfo ~= ParamValueInfo();
 988		};
 989		model.onRemoveRow = (int i) {
 990			grid.popupMsg = null;
 991			if (i < data.params.length) {
 992				onDeleteParam(data.params[i].name);
 993			}
 994		};
 995		model.onCellChanged = (int row, int column, char[] val) {
 996			Param* p = data.params[row];
 997			grid.popupMsg = null;
 998			
 999			bool semanticChanged = false;
1000
1001			// TODO: paramValueInfo
1002			
1003			switch (column) {
1004				case 0:
1005					char[] newName = safeName(p.name, val.dup);
1006					onRenameParam(p.name, newName);
1007					p.name = newName;
1008					break;
1009				case 1:
1010					auto str = val.dup;
1011					userData.semanticsEdited[row] = str;
1012					str = TextUtil.trim(str);
1013
1014					if (str.length > 0) {
1015						auto parser = create!(IKDefUtilParser)();
1016						parser.parse_ParamSemantic(str, (Semantic res) {
1017							p.semantic.clearTraits();
1018							
1019							// HACK: this is ugly. manage allocators
1020							// in some other way
1021							*p.semantic() = res.dup(&_mem.pushBack);
1022						});
1023						delete parser;
1024					} else {
1025						p.semantic.clearTraits();
1026					}
1027					
1028					semanticChanged = true;
1029					break;
1030				default: assert (false);
1031			}
1032			
1033			if (semanticChanged) switch (column) {
1034				case 1:
1035					grid.popupCol = 1;
1036					grid.popupRow = row;
1037					grid.popupMsg = p.semantic.toString.dup;
1038				default:
1039					break;
1040			}
1041		};
1042		model.getNumRows = delegate int(){
1043			return data.params.length;
1044		};
1045		model.getNumColumns = {
1046			return 2;
1047		};
1048		model.getCellValue = (int row, int column) {
1049			if (row < data.params.length) {
1050				Param* p = data.params[row];
1051				switch (column) {
1052					case 0:
1053						return p.name;
1054					case 1:
1055						if (auto s = row in userData.semanticsEdited) {
1056							return *s;
1057						} else {
1058							return p.semantic.toString();
1059						}
1060					default: assert (false);
1061				}
1062			} else {
1063				return cast(char[])null;
1064			}
1065		};
1066		
1067		grid.doGUI(justOpened, model);
1068	}
1069	
1070	
1071	void onRenameParam(char[] from, char[] to) {
1072		foreach (con; outgoing) {
1073			foreach (ref fl; con.flow) {
1074				if (fl.from == from) {
1075					fl.from = to;
1076				}
1077			}
1078		}
1079
1080		foreach (con; incoming) {
1081			foreach (ref fl; con.flow) {
1082				if (fl.to == from) {
1083					fl.to = to;
1084				}
1085			}
1086		}
1087		
1088		foreach (ref con; inputs) {
1089			if (con.name == from) {
1090				con.name = to;
1091			}
1092		}
1093
1094		foreach (ref con; outputs) {
1095			if (con.name == from) {
1096				con.name = to;
1097			}
1098		}
1099	}
1100	
1101	
1102	void removeOutgoingConnectionsFrom(char[] name) {
1103		for (int i = 0; i < outgoing.length;) {
1104			auto con = outgoing[i];
1105			for (int j = 0; j < con.flow.length;) {
1106				auto fl = &con.flow[j];
1107				if (fl.from == name) {
1108					*fl = con.flow[$-1];
1109					con.flow = con.flow[0..$-1];
1110				} else {
1111					++j;
1112				}
1113			}
1114			if (0 == con.flow.length) {
1115				con.unlink;
1116			} else {
1117				++i;
1118			}
1119		}
1120	}
1121	
1122	
1123	bool removeIncomingConnectionsTo(char[] name) {
1124		bool res = false;
1125		
1126		for (int i = 0; i < incoming.length;) {
1127			auto con = incoming[i];
1128			for (int j = 0; j < con.flow.length;) {
1129				auto fl = &con.flow[j];
1130				if (fl.to == name) {
1131					*fl = con.flow[$-1];
1132					con.flow = con.flow[0..$-1];
1133					res = true;
1134				} else {
1135					++j;
1136				}
1137			}
1138			if (0 == con.flow.length) {
1139				con.unlink;
1140				res = true;
1141			} else {
1142				++i;
1143			}
1144		}
1145
1146		return res;
1147	}
1148	
1149	
1150	void onDeleteParam(char[] name) {
1151		final i = data.params.indexOf(name);
1152		xf.utils.Array.removeKeepOrder(paramValueInfo, i);
1153		
1154		data.params.remove(name);
1155		removeOutgoingConnectionsFrom(name);
1156		removeIncomingConnectionsTo(name);
1157		
1158		foreach (ref con; inputs) {
1159			if (con.name == name) {
1160				con = inputs[$-1];
1161				inputs = inputs[0..$-1];
1162				break;
1163			}
1164		}
1165		foreach (ref con; outputs) {
1166			if (con.name == name) {
1167				con = outputs[$-1];
1168				outputs = outputs[0..$-1];
1169				break;
1170			}
1171		}
1172		
1173		foreach (con; outgoing) {
1174			foreach (fl; con.flow) {
1175				auto src = con.from.outputs.find(fl.from);
1176				assert (src !is null);
1177			}
1178		}
1179	}
1180	
1181	
1182	void unlink() {
1183		while (incoming.length) {
1184			incoming[0].unlink;
1185		}
1186		while (outgoing.length) {
1187			outgoing[0].unlink;
1188		}
1189		// TODO: other cleanup (connectors)
1190	}
1191	
1192	
1193	char[] kernelName() {
1194		return _kernelName;
1195	}
1196
1197	
1198	class DataCommons {
1199		ParamList params;
1200	}
1201	
1202
1203	EventHandling clickHandler(ClickEvent e) {
1204		if (/+MouseButton.Left == e.button && +/e.bubbling && !e.handled) {
1205			_mngr.onNodeSelected(this);
1206		}
1207		
1208		return EventHandling.Continue;
1209	}
1210	
1211	
1212	// returns the first one
1213	Connection hasConnectionToInput(char[] name, char[]* fromOutput = null) {
1214		foreach (input; incoming) {
1215			foreach (fl; input.flow) {
1216				if (fl.to == name) {
1217					if (fromOutput !is null) {
1218						*fromOutput = fl.from;
1219					}
1220					return input;
1221				}
1222			}
1223		}
1224		
1225		return null;
1226	}
1227
1228
1229	public {
1230		bool			showContents = true;
1231		bool			showDataNames = true;
1232		NodeContents	contents;
1233		DataCommons		data;
1234
1235		ParamValueInfo[]	paramValueInfo;
1236		
1237		Connection[]	incoming;
1238		Connection[]	outgoing;
1239		
1240		ConnectorInfo[]	inputs;
1241		ConnectorInfo[]	outputs;
1242		
1243		vec2			spawnPosition = vec2.zero;
1244		vec2			currentCenter = vec2.zero;
1245		vec2			currentSize = vec2.zero;
1246
1247		bool			_isInline;
1248
1249		// TODO: reload these on kdef refresh
1250		static assert (false);
1251		KernelDef		_inlineKernel;
1252		GraphDef		_inlineGraph;
1253		
1254		Type			type;
1255		char[]			label;
1256	}
1257	
1258	private {
1259		uint			_id;
1260		char[]			_kernelName;
1261		bool			_editingProps;
1262		GraphMngr		_mngr;
1263		GraphNodeBox	_widget;
1264
1265		ScratchFIFO		_mem;
1266
1267		static uint		g_nextId = 0;
1268	}
1269}
1270
1271
1272class DataConnector : CustomWidget {
1273	//bool		_dragHandlerRegistered = false;
1274	mixin	MWidget;
1275	
1276	EventHandling delegate(bool input, ConnectorInfo ci, MouseButtonEvent e)
1277					mouseButtonHandler;
1278	ConnectorInfo	ci;
1279	bool			input;
1280	
1281	EventHandling handleMouseButton(MouseButtonEvent e) {
1282		assert (mouseButtonHandler !is null);
1283		return mouseButtonHandler(input, ci, e);
1284	}
1285	
1286	
1287	this() {
1288		addHandler(&handleMouseButton);
1289	}
1290}
1291
1292
1293
1294class GraphMngr {
1295	/+this(INucleus core) {
1296		this._core = core;
1297		this._vfs = core.root;
1298	}+/
1299	
1300	
1301	void register(Graph graph) {
1302		_graphs ~= graph;
1303	}
1304	
1305
1306	private {
1307		GraphNode _prevClicked;
1308	}
1309	
1310	
1311	GraphNode selected() {
1312		return _prevClicked;
1313	}
1314	
1315	
1316	void onNodeSelected(GraphNode node) {
1317		if (_prevClicked) {
1318			_prevClicked._widget.disableStyle("selected");
1319		}
1320		
1321		if (node) {
1322			node._widget.enableStyle("selected");
1323		}
1324
1325		_prevClicked = node;
1326	}
1327	
1328	
1329	public EventHandling handleMouseButton(bool input, ConnectorInfo ci, MouseButtonEvent e) {
1330		if (MouseButton.Left == e.button && e.down && (e.bubbling && !e.handled)) {
1331			Stdout.formatln("drag start");
1332			_connecting = true;
1333			_connectingFrom = ci;
1334			_connectingFromInput = input;
1335			
1336			gui.addGlobalHandler(&this.globalHandleMouseButton);
1337			
1338			if (e.bubbling) {
1339				return EventHandling.Stop;
1340			}
1341		}
1342		
1343		return EventHandling.Continue;
1344	}
1345
1346
1347	protected bool globalHandleMouseButton(MouseButtonEvent e) {
1348		if (MouseButton.Left == e.button && !e.down) {
1349			Stdout.formatln("drag end");
1350			tryCreateConnection();
1351			_connecting = false;
1352			return true;
1353		}
1354		
1355		return false;
1356	}
1357	
1358	
1359	ConnectorInfo findConnector(vec2 pos, bool inputs, bool outputs, bool autoFlow, GraphNode skipNode, float radius = 20.f) {
1360		if (autoFlow) {
1361			radius = 300.f;
1362		}
1363		
1364		float					bestDist = float.max;
1365		ConnectorInfo	bestCon;
1366		float					radSq = radius * radius;
1367		
1368		foreach (graph; _graphs) {
1369			foreach (node; graph.nodes) {
1370				if (node is skipNode) {
1371					continue;
1372				}
1373				
1374				void process(ConnectorInfo con) {
1375					float distSq = (con.windowPos - pos).sqLength;
1376					if (distSq < radSq && distSq < bestDist) {
1377						bestDist = distSq;
1378						bestCon = con;
1379					}
1380				}
1381				
1382				if (inputs) foreach (ref x; node.inputs) {
1383					if (!autoFlow || x.name == depInputConnectorName) {
1384						process(x);
1385					}
1386				}
1387				if (outputs) foreach (ref x; node.outputs) {
1388					if (!autoFlow || x.name == depOutputConnectorName) {
1389						process(x);
1390					}
1391				}
1392			}
1393		}
1394		
1395		return bestCon;
1396	}
1397	
1398	
1399	void tryCreateConnection() {
1400		assert (_connecting);
1401		
1402		bool tryingAutoFlow =		(_connectingFromInput && depInputConnectorName == _connectingFrom.name) ||
1403											(!_connectingFromInput && depOutputConnectorName == _connectingFrom.name);
1404		
1405		auto droppedOn = findConnector(mousePos, !_connectingFromInput, _connectingFromInput, tryingAutoFlow, _connectingFrom.node);
1406		if (!droppedOn) {
1407			return;
1408		}
1409		
1410		if (_connectingFromInput) {
1411			if ((depInputConnectorName == _connectingFrom.name) != (droppedOn.name == depOutputConnectorName)) {
1412				return;
1413			}
1414			
1415			auto flow = DataFlow(droppedOn.name, _connectingFrom.name);
1416			foreach (con; droppedOn.node.outgoing) {
1417				if (con.to is _connectingFrom.node) {
1418					con.flow ~= flow;
1419					return;
1420				}
1421			}
1422			new Connection(droppedOn.node, _connectingFrom.node, flow);
1423		} else {
1424			if ((depOutputConnectorName == _connectingFrom.name) != (droppedOn.name == depInputConnectorName)) {
1425				return;
1426			}
1427
1428			auto flow = DataFlow(_connectingFrom.name, droppedOn.name);
1429			foreach (con; _connectingFrom.node.outgoing) {
1430				if (con.to is droppedOn.node) {
1431					con.flow ~= flow;
1432					return;
1433				}
1434			}
1435			new Connection(_connectingFrom.node, droppedOn.node, flow);
1436		}
1437	}
1438	
1439	
1440	bool connecting() {
1441		return _connecting;
1442	}
1443	
1444	
1445	ConnectorInfo connectingFrom() {
1446		return _connectingFrom;
1447	}
1448	
1449	
1450	bool connectingFromInput() {
1451		return _connectingFromInput;
1452	}
1453	
1454	
1455	vec2 mousePos() {
1456		return gui.mousePos;
1457	}
1458	
1459	
1460	Graph[] graphs() {
1461		return _graphs;
1462	}
1463	
1464	
1465	VfsFolder vfs() {
1466		return _vfs;
1467	}
1468	
1469	
1470	private {
1471		ConnectorInfo	_connectingFrom;
1472		bool			_connecting;
1473		bool			_connectingFromInput;
1474		Graph[]			_graphs;
1475		VfsFolder		_vfs;
1476		//INucleus			_core;
1477	}
1478}