Adding in loop detection

This patch fixes #1075 by adding in loop detection: analysing whether a just-picked listener or broadcast channel will form a closed data loop in the app's channel graph. Those are fancy words for saying "this prevents an infinite loop in apps", so let's go with that. This almost works, except there are some cosmetic quirks left. A two counter app is the easiest realistic demonstrator.

A broadcast-initiated loop
---
![image](https://cloud.githubusercontent.com/assets/177243/4142741/1e4e6ba0-33be-11e4-8e32-db19a51bf166.png)

pick "A":

![image](https://cloud.githubusercontent.com/assets/177243/4142748/363b1c90-33be-11e4-80fc-a5cc02d08212.png)

Clicking OK will put you in the driving seat of the worst app. The technical term for what this app does is "data incest". I'm not even making that up.

Click cancel will leave you with a safe app again:

![image](https://cloud.githubusercontent.com/assets/177243/4142759/6eeb93e4-33be-11e4-8403-5d61d8e42b01.png)

Notice that the viz line for "A" on the second counter has been removed: the broadcast was undone by the loop detection code. However, there's still a remnant of it left in the channel menu if you click the outbound arrow again:

![image](https://cloud.githubusercontent.com/assets/177243/4142763/8732c1f2-33be-11e4-932b-15b39a7cfe9f.png)

That's not the cleanest thing, but I'm not quite sure (yet) what to do to make sure the menu system is aware of the unbound broadcast.

A listen-initiated loop
---
![image](https://cloud.githubusercontent.com/assets/177243/4142874/2071c29a-33c0-11e4-970b-e4f19ec26a2d.png)

We chose to listen for A:

![image](https://cloud.githubusercontent.com/assets/177243/4142876/2c1fcbbe-33c0-11e4-8080-08bce02b367c.png)

We get the same warning:

![image](https://cloud.githubusercontent.com/assets/177243/4142880/354ca522-33c0-11e4-9307-34a5ade17133.png)

Saying OK will leave us with questionable functionality, canceling unbinds the listener:

![image](https://cloud.githubusercontent.com/assets/177243/4142883/489878fe-33c0-11e4-9113-0cf6e0129598.png)

but again, the channel menu itself still thinks "A" is bound:

![image](https://cloud.githubusercontent.com/assets/177243/4142884/5023125a-33c0-11e4-96b2-f0a73e0db2bf.png)