PageRenderTime 118ms CodeModel.GetById 71ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 1ms

/flux/node.go

https://code.google.com/p/gordon-go/
Go | 471 lines | 423 code | 40 blank | 8 comment | 100 complexity | c652b68120fe185c166b4f287b1a5471 MD5 | raw file
  1// Copyright 2014 Gordon Klaus. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5package main
  6
  7import (
  8	"code.google.com/p/gordon-go/go/types"
  9	. "code.google.com/p/gordon-go/gui"
 10	"go/token"
 11	"math"
 12	"strconv"
 13	"strings"
 14	"time"
 15)
 16
 17type node interface {
 18	View
 19	Mouser
 20	block() *block
 21	setBlock(b *block)
 22	inputs() []*port
 23	outputs() []*port
 24	inConns() []*connection
 25	outConns() []*connection
 26}
 27
 28type nodeBase struct {
 29	*ViewBase
 30	self node
 31	AggregateMouser
 32
 33	blk  *block
 34	pkg  *pkgText
 35	text *Text
 36	ins  []*port
 37	outs []*port
 38
 39	godefer     string
 40	godeferText *Text
 41	typ         *typeView
 42
 43	focused bool
 44	gap     float64
 45}
 46
 47func newNodeBase(self node) *nodeBase {
 48	return newGoDeferNodeBase(self, "")
 49}
 50
 51func newGoDeferNodeBase(self node, godefer string) *nodeBase {
 52	n := &nodeBase{self: self, godefer: godefer}
 53	n.ViewBase = NewView(n)
 54	n.AggregateMouser = AggregateMouser{NewClickFocuser(self), NewMover(self)}
 55	n.pkg = newPkgText()
 56	n.Add(n.pkg)
 57	n.text = NewText("")
 58	n.text.SetBackgroundColor(noColor)
 59	n.text.TextChanged = func(string) { n.reform() }
 60	n.Add(n.text)
 61	n.godeferText = NewText(godefer)
 62	n.godeferText.SetBackgroundColor(noColor)
 63	n.godeferText.SetTextColor(color(special{}, true, false))
 64	n.Add(n.godeferText)
 65	n.ViewBase.Self = self
 66	return n
 67}
 68
 69func (n *nodeBase) newInput(v *types.Var) *port {
 70	p := newInput(n.self, v)
 71	n.Add(p)
 72	n.ins = append(n.ins, p)
 73	n.reform()
 74	return p
 75}
 76
 77func (n *nodeBase) newOutput(v *types.Var) *port {
 78	p := newOutput(n.self, v)
 79	if n.godefer == "" || v.Type == seqType {
 80		n.Add(p)
 81		n.outs = append(n.outs, p)
 82		n.reform()
 83	}
 84	return p
 85}
 86
 87func (n *nodeBase) addSeqPorts() {
 88	n.newInput(newVar("seq", seqType))
 89	n.newOutput(newVar("seq", seqType))
 90	n.reform()
 91}
 92
 93func (n *nodeBase) removePortBase(p *port) { // intentionally named to not implement interface{removePort(*port)}
 94	for _, c := range p.conns {
 95		c.blk.removeConn(c)
 96	}
 97	ports := &n.ins
 98	if p.out {
 99		ports = &n.outs
100	}
101	for i, p2 := range *ports {
102		if p2 == p {
103			*ports = append((*ports)[:i], (*ports)[i+1:]...)
104			n.Remove(p)
105			n.reform()
106
107			if i > 0 && (*ports)[i-1].obj.Type != seqType { // assumes sequencing port, if present, is at index 0
108				i--
109			}
110			if i < len(*ports) {
111				SetKeyFocus((*ports)[i])
112			} else {
113				SetKeyFocus(n.self)
114			}
115			break
116		}
117	}
118}
119
120func (n *nodeBase) reform() {
121	if n.godefer != "" {
122		n.godeferText.SetText(n.godefer)
123	}
124	x := -(Width(n.godeferText) + Width(n.pkg) + Width(n.text)) / 2
125	if n.typ != nil {
126		x -= Width(n.typ) / 2
127	}
128	n.godeferText.Move(Pt(x, -Height(n.godeferText)/2))
129	x += Width(n.godeferText)
130	n.pkg.Move(Pt(x, -Height(n.pkg)/2))
131	x += Width(n.pkg)
132	n.text.Move(Pt(x, -Height(n.text)/2))
133	if n.typ != nil {
134		x += Width(n.text)
135		n.typ.Move(Pt(x, -Height(n.typ)/2))
136	}
137	if n.text.Text() != "" {
138		n.gap = math.Max(math.Max(Height(n.godeferText), Height(n.pkg)), Height(n.text)) / 2
139	}
140	if n.typ != nil {
141		n.gap = math.Max(n.gap, Height(n.typ)/2)
142	}
143
144	ins, outs := ins(n), outs(n)
145	numIn := float64(len(ins))
146	numOut := float64(len(outs))
147	rx, ry := (math.Max(numIn, numOut)+1)*portSize/2, 1.0*portSize
148	rect := ZR
149	for i, p := range ins {
150		x := portSize * (float64(i) - (numIn-1)/2)
151		y := n.gap + ry*math.Sqrt(rx*rx-x*x)/rx
152		if numIn > 1 {
153			y += 8
154		}
155		MoveCenter(p, Pt(x, y))
156		rect = rect.Union(RectInParent(p))
157	}
158	for i, p := range outs {
159		x := portSize * (float64(i) - (numOut-1)/2)
160		y := -n.gap - ry*math.Sqrt(rx*rx-x*x)/rx
161		if numOut > 1 {
162			y -= 8
163		}
164		MoveCenter(p, Pt(x, y))
165		rect = rect.Union(RectInParent(p))
166	}
167	if p := seqIn(n); p != nil {
168		MoveCenter(p, Pt(0, n.gap))
169	}
170	if p := seqOut(n); p != nil {
171		MoveCenter(p, Pt(0, -n.gap))
172	}
173	if _, ok := n.self.(*portsNode); ok {
174		// portsNode rect must have an edge at y==0 or arrangement will diverge
175		n.SetRect(rect)
176	} else {
177		ResizeToFit(n, 0)
178	}
179	rearrange(n.blk)
180}
181
182func (n nodeBase) block() *block      { return n.blk }
183func (n *nodeBase) setBlock(b *block) { n.blk = b }
184func (n nodeBase) inputs() []*port    { return n.ins }
185func (n nodeBase) outputs() []*port   { return n.outs }
186
187func (n nodeBase) inConns() (conns []*connection) {
188	for _, p := range n.inputs() {
189		for _, c := range p.conns {
190			conns = append(conns, c)
191		}
192	}
193	return
194}
195
196func (n nodeBase) outConns() (conns []*connection) {
197	for _, p := range n.outputs() {
198		for _, c := range p.conns {
199			conns = append(conns, c)
200		}
201	}
202	return
203}
204
205func (n *nodeBase) Move(p Point) {
206	n.ViewBase.Move(p)
207	nodeMoved(n.self)
208}
209
210func nodeMoved(n node) {
211	for _, c := range append(n.inConns(), n.outConns()...) {
212		c.reform()
213	}
214	if KeyFocus(n) == n {
215		// TODO: not ZP for if and select
216		panTo(n, ZP)
217	}
218}
219
220func (n *nodeBase) TookKeyFocus() {
221	n.focused = true
222	panTo(n, ZP)
223}
224
225func (n *nodeBase) LostKeyFocus() {
226	n.focused = false
227}
228
229func (n *nodeBase) Paint() {
230	SetColor(lineColor)
231	SetLineWidth(3)
232	for _, p := range append(ins(n), outs(n)...) {
233		pt := CenterInParent(p)
234		dy := n.gap
235		if p.out {
236			dy = -dy
237		}
238		y := (pt.Y-dy)/2 + dy
239		DrawBezier(Pt(0, dy), Pt(0, y), Pt(pt.X, y), pt)
240	}
241	if n.focused && (n.text.Text() != "" || n.typ != nil) {
242		r := RectInParent(n.godeferText).Union(RectInParent(n.pkg)).Union(RectInParent(n.text))
243		if n.typ != nil {
244			r = r.Union(RectInParent(n.typ))
245		}
246		DrawRect(r)
247	}
248}
249
250type pkgText struct {
251	*ViewBase
252	text  *Text
253	pkg   *types.Package
254	leave chan bool
255}
256
257func newPkgText() *pkgText {
258	t := &pkgText{}
259	t.ViewBase = NewView(t)
260	t.text = NewText("")
261	t.text.SetTextColor(color(&pkgObject{}, true, false))
262	t.text.SetBackgroundColor(noColor)
263	t.Add(t.text)
264	t.leave = make(chan bool, 1)
265	return t
266}
267
268func (t *pkgText) setPkg(p *types.Package) {
269	t.pkg = p
270	t.setText(p.Name)
271}
272
273func (t *pkgText) setText(s string) {
274	t.text.SetText(s + ".")
275	dx := Width(t) - Width(t.text)
276	t.Move(Pos(t).Add(Pt(dx, 0)))
277	Resize(t, Size(t.text))
278}
279
280func (t *pkgText) Mouse(m MouseEvent) {
281	if m.Enter {
282		go func() {
283			select {
284			case <-time.After(time.Second):
285				Do(t, func() { t.setText(t.pkg.Path) })
286				<-t.leave
287				Do(t, func() { t.setText(t.pkg.Name) })
288			case <-t.leave:
289			}
290		}()
291	} else if m.Leave {
292		t.leave <- true
293	} else {
294		MouseParent(t, m)
295	}
296}
297
298var seqType = struct{ types.Type }{}
299
300func seqIn(n node) *port {
301	for _, in := range n.inputs() {
302		if in.obj.Type == seqType {
303			return in
304		}
305	}
306	return nil
307}
308
309func seqOut(n node) *port {
310	for _, out := range n.outputs() {
311		if out.obj.Type == seqType {
312			return out
313		}
314	}
315	return nil
316}
317
318func ins(n node) (p []*port) {
319	for _, in := range n.inputs() {
320		if in.obj.Type != seqType {
321			p = append(p, in)
322		}
323	}
324	return
325}
326
327func outs(n node) (p []*port) {
328	for _, out := range n.outputs() {
329		if out.obj.Type != seqType {
330			p = append(p, out)
331		}
332	}
333	return
334}
335
336type basicLiteralNode struct {
337	*nodeBase
338	kind token.Token
339}
340
341func newBasicLiteralNode(kind token.Token) *basicLiteralNode {
342	n := &basicLiteralNode{kind: kind}
343	n.nodeBase = newNodeBase(n)
344	out := n.newOutput(nil)
345	n.addSeqPorts()
346	switch kind {
347	case token.INT, token.FLOAT:
348		if kind == token.INT {
349			out.setType(types.Typ[types.UntypedInt])
350		} else {
351			out.setType(types.Typ[types.UntypedFloat])
352		}
353		n.text.Validate = func(s *string) bool {
354			*s = strings.TrimLeft(*s, "0")
355			if *s == "" || *s == "-" {
356				*s = "0"
357			}
358			if (*s)[0] == '.' {
359				*s = "0" + *s
360			}
361			if l := len(*s); (*s)[l-1] == '-' {
362				if (*s)[0] == '-' {
363					*s = (*s)[1 : l-1]
364				} else {
365					*s = "-" + (*s)[:l-1]
366				}
367			}
368			if _, err := strconv.ParseInt(*s, 10, 64); err == nil {
369				n.kind = token.INT
370				out.setType(types.Typ[types.UntypedInt])
371			} else {
372				if _, err := strconv.ParseFloat(*s, 4096); err == nil {
373					n.kind = token.FLOAT
374					out.setType(types.Typ[types.UntypedFloat])
375				} else {
376					return false
377				}
378			}
379			return true
380		}
381	case token.IMAG:
382		// TODO
383	case token.STRING:
384		out.setType(types.Typ[types.UntypedString])
385	case token.CHAR:
386		out.setType(types.Typ[types.UntypedRune])
387		n.text.Validate = func(s *string) bool {
388			if *s == "" {
389				return false
390			}
391			*s = (*s)[len(*s)-1:]
392			return true
393		}
394	}
395	n.text.Accept = func(string) { SetKeyFocus(n) }
396	return n
397}
398
399func (n *basicLiteralNode) KeyPress(k KeyEvent) {
400	switch k.Key {
401	case KeyEnter:
402		s := n.text.Text()
403		t := n.outs[0].obj.Type
404		n.text.Reject = func() {
405			n.text.SetText(s)
406			n.outs[0].setType(t)
407			SetKeyFocus(n)
408		}
409		SetKeyFocus(n.text)
410	default:
411		n.nodeBase.KeyPress(k)
412	}
413}
414
415type compositeLiteralNode struct {
416	*nodeBase
417}
418
419func newCompositeLiteralNode(currentPkg *types.Package) *compositeLiteralNode {
420	n := &compositeLiteralNode{}
421	n.nodeBase = newNodeBase(n)
422	out := n.newOutput(nil)
423	n.addSeqPorts()
424	n.typ = newTypeView(&out.obj.Type, currentPkg)
425	n.typ.mode = compositeOrPtrType
426	n.Add(n.typ)
427	return n
428}
429func (n *compositeLiteralNode) editType() {
430	n.typ.editType(func() {
431		if t := *n.typ.typ; t != nil {
432			n.setType(t)
433		} else {
434			n.blk.removeNode(n)
435			SetKeyFocus(n.blk)
436		}
437	})
438}
439func (n *compositeLiteralNode) setType(t types.Type) {
440	n.typ.setType(t)
441	n.outs[0].setType(t)
442	n.blk.func_().addPkgRef(t)
443	t, _ = indirect(t)
444	local := true
445	if nt, ok := t.(*types.Named); ok {
446		t = nt.UnderlyingT
447		local = nt.Obj.Pkg == n.blk.func_().pkg()
448	}
449	switch t := t.(type) {
450	case *types.Struct:
451		for _, f := range t.Fields {
452			if local || f.IsExported() {
453				n.newInput(f)
454			}
455		}
456	case *types.Slice:
457		// TODO: variable number of inputs? (same can be achieved using append.)  variable number of index/value input pairs?
458	case *types.Map:
459		// TODO: variable number of key/value input pairs?
460	}
461	MoveCenter(n.typ, ZP)
462	n.gap = Height(n.typ) / 2
463	n.reform()
464	SetKeyFocus(n)
465}
466
467func (n *compositeLiteralNode) removePort(p *port) {
468	if p.bad {
469		n.removePortBase(p)
470	}
471}