protected void OnGUI()
    {
        if (_system == null)
        {
            return;
        }

        // Not sure why the magic numbers on size are needed to make the scrollbars appear in the correct place....
        _scrollPosition = GUI.BeginScrollView(new Rect(0, 0, Screen.width - 4, Screen.height - 21), _scrollPosition, new Rect(0, 0, 5000, 5000), true, true);

        Event evt = Event.current;

        if (evt.type == EventType.MouseDrag)
        {
            Repaint();
        }

        _connections.Clear();

        foreach (MonoBehaviour component in _system.gameObject.GetComponents <MonoBehaviour>())
        {
            Type componentType = component.GetType();

            GraphNodeAttribute[] nodeAttributes = componentType.GetCustomAttributes(typeof(GraphNodeAttribute), false) as GraphNodeAttribute[];
            if (nodeAttributes != null)
            {
                bool hasSystem = false;
                foreach (GraphNodeAttribute attribute in nodeAttributes)
                {
                    if (attribute.System == _systemName)
                    {
                        hasSystem = true;
                        break;
                    }
                }

                if (!hasSystem)
                {
                    continue;
                }

                GraphNodeData nodeData = _graphData.GetNodeData(component);

                _areaRect = new Rect(nodeData.Position.x, nodeData.Position.y, nodeData.Width, 1000);
                GUILayout.BeginArea(_areaRect);

                GUILayout.Box(ObjectNames.NicifyVariableName(componentType.Name), GUILayout.ExpandWidth(true));

                if (evt.type == EventType.ContextClick && GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
                {
                    GenericMenu menu = new GenericMenu();
                    menu.AddItem(new GUIContent("Delete"), false, (object data) => { DestroyImmediate(data as MonoBehaviour, false); },
                                 component);
                    menu.ShowAsContext();
                    evt.Use();
                }

                if (evt.type == EventType.MouseDown && GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) &&
                    _dragItem == null)
                {
                    _dragItem = component;
                }

                if (evt.type == EventType.MouseDrag && _dragItem == component)
                {
                    nodeData.Position += Event.current.delta;
                    Event.current.Use();
                }

                GUILayout.Box("", GUILayout.ExpandWidth(true));

                if (evt.type == EventType.MouseUp && GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) &&
                    _dragConnectionComponent != null)
                {
                    if (EditorHelpers.IsSubclassOfRawGeneric(_dragConnectionField.FieldType, component.GetType()))
                    {
                        _dragConnectionField.SetValue(_dragConnectionComponent, component);
                    }
                }

                string description = component.ToString();
                // ) is the last character on the default ToString... there's probably a better way of doing this, without requiring a custom method on every node
                if (!string.IsNullOrEmpty(description) && description[description.Length - 1] != ')')
                {
                    GUI.skin.label.wordWrap = true;
                    GUILayout.Label(description, GUILayout.ExpandWidth(true));
                }

                List <FieldInfo> componentFields = new List <FieldInfo>();
                EditorHelpers.FindFields(componentFields, componentType);

                foreach (FieldInfo field in componentFields)
                {
                    GraphConnectionAttribute[] connectionAttributes = field.GetCustomAttributes(typeof(GraphConnectionAttribute), false) as GraphConnectionAttribute[];
                    if (connectionAttributes != null && connectionAttributes.Length > 0)
                    {
                        GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));

                        if (connectionAttributes[0].Direction == GraphConnectionAttribute.DirectionType.In)
                        {
                            DoConnectionBox(component, field);
                        }
                        else
                        {
                            GUILayout.Space(24);
                        }

                        GUILayout.FlexibleSpace();
                        GUILayout.Label(ObjectNames.NicifyVariableName(field.Name), GUILayout.ExpandWidth(true));
                        GUILayout.FlexibleSpace();

                        if (connectionAttributes[0].Direction == GraphConnectionAttribute.DirectionType.Out)
                        {
                            DoConnectionBox(component, field);
                        }
                        else
                        {
                            GUILayout.Space(24);
                        }

                        GUILayout.EndHorizontal();
                    }
                }

                nodeData.ShowInspector = EditorGUILayout.Foldout(nodeData.ShowInspector, "Inspector");
                if (nodeData.ShowInspector)
                {
                    var editor = Editor.CreateEditor(component);
                    editor.OnInspectorGUI();
                }

                GUILayout.EndArea();
            }
        }

        foreach (var connection in _connections)
        {
            GraphNodeData graphData = _graphData.GetNodeData(connection.Value);

            if (graphData != null)
            {
                if (connection.Key.x > graphData.Position.x)
                {
                    DrawLine(connection.Key, graphData.Position + new Vector2(graphData.Width, 32));
                }
                else
                {
                    DrawLine(connection.Key, graphData.Position + new Vector2(0, 32));
                }
            }
        }

        if (_dragConnectionComponent != null)
        {
            DrawLine(_dragConnectionPosition, Event.current.mousePosition);
        }

        if (evt.type == EventType.MouseUp)
        {
            _dragItem = null;
            _dragConnectionComponent = null;
            _dragConnectionField     = null;
            Repaint();
        }

        if (evt.type == EventType.ContextClick)
        {
            _mousePosition = evt.mousePosition;

            GenericMenu menu = new GenericMenu();

            menu.AddSeparator("Add New Item");

            Assembly assembly = typeof(Realtime).Assembly;
            foreach (Type type in assembly.GetTypes())
            {
                object[] attributes = type.GetCustomAttributes(false);

                foreach (Attribute attribute in attributes)
                {
                    GraphNodeAttribute graphNode = attribute as GraphNodeAttribute;
                    if (graphNode != null && graphNode.System == _systemName)
                    {
                        menu.AddItem(new GUIContent(graphNode.Type + "/" + graphNode.Name), false, OnMenuItemClicked, type);
                        break;
                    }
                }
            }

            menu.ShowAsContext();
            evt.Use();
        }

        GUI.EndScrollView();

        _graphData.Clean();

        EditorUtility.SetDirty(_system.gameObject);
    }