public override void OnInspectorGUI()
        {
            Rect   rect;
            string currentControlName;

            // Check for key down before drawing any fields because they might consume the event.
            bool enterKeyDown = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return;

            // Load serialized object
            serializedObject.Update();

            // Port field
            EditorGUI.BeginChangeCheck();
            GUI.SetNextControlName(portControlName);
            int newPort = EditorGUILayout.IntField(_portLabel, _oscIn.port);

            if (EditorGUI.EndChangeCheck())
            {
                _portProp.intValue = newPort;
                if (_oscIn.isOpen)
                {
                    _oscIn.Close();                                 // Close UDPReceiver while editing
                }
            }
            currentControlName = GUI.GetNameOfFocusedControl();
            bool enterKeyDownPort = enterKeyDown && currentControlName == portControlName;

            if (enterKeyDownPort)
            {
                UnfocusAndUpdateUI();
            }
            bool deselect = _prevControlName == portControlName && currentControlName != portControlName;

            if ((deselect || enterKeyDownPort) && !_oscIn.isOpen)
            {
                if (_oscIn.Open(_portProp.intValue))
                {
                    _tempPort = _portProp.intValue;
                }
                else
                {
                    _portProp.intValue = _tempPort;                     // undo
                    _oscIn.Open(_portProp.intValue);
                }
            }

            // Mode field
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(_modeProp, _modeLabel);
            if (EditorGUI.EndChangeCheck())
            {
                switch ((OscReceiveMode)_modeProp.enumValueIndex)
                {
                case OscReceiveMode.UnicastBroadcast:
                    _oscIn.Open(_oscIn.port, string.Empty);
                    _multicastAddressProp.stringValue = string.Empty;
                    break;

                case OscReceiveMode.UnicastBroadcastMulticast:
                    _oscIn.Open(_oscIn.port, OscConst.multicastAddressDefault);
                    _multicastAddressProp.stringValue = OscConst.multicastAddressDefault;
                    break;
                }
            }

            // Multicast field
            if (_oscIn.mode == OscReceiveMode.UnicastBroadcastMulticast)
            {
                EditorGUI.BeginChangeCheck();
                GUI.SetNextControlName(multicastAddressControlName);
                EditorGUILayout.PropertyField(_multicastAddressProp, _multicastIpAddressLabel);
                if (EditorGUI.EndChangeCheck())
                {
                    if (_oscIn.isOpen)
                    {
                        _oscIn.Close();                                     // Close socket while editing
                    }
                }
                currentControlName = GUI.GetNameOfFocusedControl();
                bool enterKeyDownMulticastIpAddress = enterKeyDown && currentControlName == multicastAddressControlName;
                if (enterKeyDownMulticastIpAddress)
                {
                    UnfocusAndUpdateUI();
                }
                deselect = _prevControlName == multicastAddressControlName && currentControlName != multicastAddressControlName;
                if ((deselect || enterKeyDownMulticastIpAddress) && !_oscIn.isOpen)
                {
                    if (_oscIn.Open(_portProp.intValue, _multicastAddressProp.stringValue))
                    {
                        _tempMulticastAddress = _multicastAddressProp.stringValue;
                    }
                    else
                    {
                        _multicastAddressProp.stringValue = _tempMulticastAddress;                         // undo
                        _oscIn.Open(_portProp.intValue, _multicastAddressProp.stringValue);
                    }
                }
            }

            // IP Address field.
            EditorGUILayout.BeginVertical();
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel(_localIpAddressLabel);
            EditorGUILayout.LabelField(" ");
            rect = GUILayoutUtility.GetLastRect();             // UI voodoo to position the selectable label perfectly
            EditorGUI.SelectableLabel(rect, OscIn.localIpAddress);
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();

            // Alternative IP Addresses field.
            if (OscIn.localIpAddressAlternatives.Count > 0)
            {
                int i = 0;
                foreach (string ip in OscIn.localIpAddressAlternatives)
                {
                    EditorGUILayout.BeginVertical();
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.PrefixLabel(new GUIContent(_localIpAddressAlternativesLabel.text + "[" + i + "]", _localIpAddressAlternativesLabel.tooltip));
                    EditorGUILayout.LabelField(" ");
                    rect = GUILayoutUtility.GetLastRect();                     // UI voodoo to position the selectable label perfectly
                    EditorGUI.SelectableLabel(rect, ip);
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.EndVertical();
                    i++;
                }
            }

            // Is Open field
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.Toggle(_isOpenLabel, _oscIn.isOpen);
            EditorGUI.EndDisabledGroup();

            // Open On Awake field
            EditorGUI.BeginDisabledGroup(Application.isPlaying);
            EditorGUILayout.PropertyField(_openOnAwakeProp, _openOnAwakeLabel);
            EditorGUI.EndDisabledGroup();


            // Settings ...
            _settingsFoldoutProp.boolValue = EditorGUILayout.Foldout(_settingsFoldoutProp.boolValue, _settingsFoldLabel, true);

            if (_settingsFoldoutProp.boolValue)
            {
                EditorGUI.indentLevel++;

                // Udp Buffer Size field.
                EditorGUI.BeginChangeCheck();
                int newBufferSize = EditorGUILayout.IntField(_udpBufferSizeLabel, _udpBufferSizeProp.intValue);
                if (EditorGUI.EndChangeCheck())
                {
                    if (newBufferSize >= OscConst.udpBufferSizeMin)
                    {
                        _udpBufferSizeProp.intValue = newBufferSize;
                        _oscIn.udpBufferSize        = newBufferSize;                  // This will reopen OscIn
                    }
                }

                // Filter Duplicates field
                BoolSettingsField(_filterDuplicatesProp, _filterDuplicatesLabel);

                // Add Time Tags To Bundled Messages field.
                BoolSettingsField(_addTimeTagsToBundledMessagesProp, _addTimeTagsToBundledMessagesLabel);

                EditorGUI.indentLevel--;
            }

            // Mappings ...
            string mappingsFoldLabel = "Mappings (" + _mappingsProp.arraySize + ")";

            _mappingsFoldoutProp.boolValue = EditorGUILayout.Foldout(_mappingsFoldoutProp.boolValue, mappingsFoldLabel, true);
            if (_mappingsFoldoutProp.boolValue)
            {
                EditorGUI.indentLevel++;
                EditorGUI.BeginDisabledGroup(Application.isPlaying);

                // Mapping elements ..
                int removeIndexRequsted = -1;

                for (int m = 0; m < _mappingsProp.arraySize; m++)
                {
                    SerializedProperty mappingProp = _mappingsProp.GetArrayElementAtIndex(m);

                    // Mapping field (using custom property drawer)
                    EditorGUI.BeginChangeCheck();
                    EditorGUILayout.PropertyField(mappingProp);
                    if (EditorGUI.EndChangeCheck())
                    {
                        SerializedProperty addressProp = mappingProp.FindPropertyRelative(mappingAddressFieldName);
                        addressProp.stringValue = GetSanitizedAndUniqueAddress(_mappingsProp, m, addressProp.stringValue);
                    }

                    // Remove mapping button
                    rect        = GUILayoutUtility.GetLastRect();
                    rect        = EditorGUI.IndentedRect(GUILayoutUtility.GetLastRect());
                    rect.x      = rect.x + rect.width - OscMappingDrawer.removeButtonWidth - 7;
                    rect.y     += 6;
                    rect.width  = OscMappingDrawer.removeButtonWidth;
                    rect.height = OscMappingDrawer.removeButtonHeight;
                    if (GUI.Button(rect, _removeMappingButtonLabel))
                    {
                        removeIndexRequsted = m;
                    }
                }

                // Handle mapping removal ..
                if (removeIndexRequsted != -1)
                {
                    _mappingsProp.DeleteArrayElementAtIndex(removeIndexRequsted);
                }

                // Add mapping button
                rect = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 30));
                if (GUI.Button(rect, _addMappingButtonLabel))
                {
                    string            newAddress   = GetSanitizedAndUniqueAddress(_mappingsProp, -1, "/");
                    FieldInfo         meppingsInfo = serializedObject.targetObject.GetType().GetField(_mappingsProp.propertyPath, BindingFlags.Instance | BindingFlags.NonPublic);
                    List <OscMapping> mappings     = (List <OscMapping>)meppingsInfo.GetValue(serializedObject.targetObject);
                    OscMapping        mapping      = new OscMapping(newAddress, OscMessageType.OscMessage);

                    mapping.AddEmptyEntry();
                    mappings.Add(mapping);
                }
                EditorGUILayout.Space();

                EditorGUI.EndDisabledGroup();
                EditorGUI.indentLevel--;
            }

            // Messages ...
            _sb.Clear();
            _sb.Append("Messages (").Append(_oscIn.messageCountReceivedLastFrame).Append(",").Append(OscDebug.GetPrettyByteSize(_oscIn.byteCountReceivedLastFrame)).Append(")");
            GUIContent messagesFoldContent = new GUIContent(_sb.ToString(), "Messages received last update");

            _messagesFoldoutProp.boolValue = EditorGUILayout.Foldout(_messagesFoldoutProp.boolValue, messagesFoldContent, true);
            if (_messagesFoldoutProp.boolValue)
            {
                EditorGUI.indentLevel++;
                if (_messageMonitorText.Length > 0)
                {
                    _messageMonitorText.Remove(_messageMonitorText.Length - 1, 1);                                                  // Trim last new line temporarily.
                }
                EditorGUILayout.HelpBox(_messageMonitorText.ToString(), MessageType.None);
                if (_messageMonitorText.Length > 0)
                {
                    _messageMonitorText.Append('\n');
                }
                EditorGUI.indentLevel--;
            }

            // Apply
            serializedObject.ApplyModifiedProperties();

            // Request OnInspectorGUI to be called every frame as long as inspector is active
            EditorUtility.SetDirty(target);

            // Store name of focused control to detect unfocus events
            _prevControlName = GUI.GetNameOfFocusedControl();
        }
        public override void OnGUI(Rect areaRect, SerializedProperty entryProp, GUIContent label)
        {
            // Begin.
            EditorGUI.BeginProperty(areaRect, label, entryProp);

            // Find property path to OscEvent in which this entry lives.
            String mappingPropPath = entryProp.propertyPath.Substring(0, entryProp.propertyPath.IndexOf("_entries") - 1);

            // Get properties.
            SerializedProperty mappingProp                       = entryProp.serializedObject.FindProperty(mappingPropPath);
            SerializedProperty mappingTypeProp                   = mappingProp.FindPropertyRelative("_type");
            SerializedProperty targetGameObjectProp              = entryProp.FindPropertyRelative("targetGameObject");
            SerializedProperty serializedTargetObjectProp        = entryProp.FindPropertyRelative("serializedTargetObject");
            SerializedProperty nonSerializedTargetObjectNameProp = entryProp.FindPropertyRelative("nonSerializedTargetObjectName");
            SerializedProperty targetMethodNameProp              = entryProp.FindPropertyRelative("targetMethodName");
            SerializedProperty targetParamAssemblyNameProp       = entryProp.FindPropertyRelative("targetParamAssemblyQualifiedName");

            //Debug.Log("oscEventParamAssemblyNameProp: " + oscEventParamAssemblyNameProp.name + " " + oscEventParamAssemblyNameProp.propertyPath + "  " + oscEventParamAssemblyNameProp.stringValue );

            // Get the parameter type.
            Type mappingParamType = OscMapping.GetParamType((OscMessageType)mappingTypeProp.enumValueIndex);

            // Prepare positioning.
            Rect rect = areaRect;

            rect.height = EditorGUIUtility.singleLineHeight;
            rect.width  = (areaRect.width + 16 + 26) / 3f;
            rect.y     += verticalPadding;
            rect.x     -= 16;

            // Target GameObject.
            bool displayGameObjectField = (Application.isPlaying && targetGameObjectProp.objectReferenceValue != null) || !Application.isPlaying;

            if (displayGameObjectField)
            {
                targetGameObjectProp.objectReferenceValue = EditorGUI.ObjectField(rect, targetGameObjectProp.objectReferenceValue, typeof(GameObject), true);
                if (targetGameObjectProp.objectReferenceValue is GameObject && !(serializedTargetObjectProp.objectReferenceValue is Component))
                {
                    serializedTargetObjectProp.objectReferenceValue = null;
                }
            }
            else
            {
                EditorGUI.LabelField(rect, string.Empty);
            }
            rect.x += rect.width - 13;

            // Target Object.
            rect.y += 1; // The default object selection GUI is taller than drop down.
            bool isAnonymous = targetMethodNameProp.stringValue.IndexOfAny(anonymousChars) > -1;

            if (isAnonymous)
            {
                EditorGUI.LabelField(rect, string.Empty);
            }
            else
            {
                bool displayObjectDropdown = targetGameObjectProp.objectReferenceValue != null;
                if (displayObjectDropdown)
                {
                    if (targetGameObjectProp.objectReferenceValue != null)
                    {
                        GameObject   go                     = targetGameObjectProp.objectReferenceValue as GameObject;
                        Component[]  components             = go.GetComponents <Component>();
                        int          selectedComponentIndex = Array.IndexOf(components, serializedTargetObjectProp.objectReferenceValue as Component);
                        GUIContent[] componentOptions       = new GUIContent[components.Length];
                        for (int i = 0; i < components.Length; i++)
                        {
                            componentOptions[i] = new GUIContent(components[i].GetType().Name);
                        }
                        int newSelectedComponentIndex = EditorGUI.Popup(rect, selectedComponentIndex, componentOptions);
                        if (newSelectedComponentIndex != selectedComponentIndex)
                        {
                            serializedTargetObjectProp.objectReferenceValue = components[newSelectedComponentIndex];
                            targetMethodNameProp.stringValue = string.Empty;
                        }
                    }
                    else if (serializedTargetObjectProp.objectReferenceValue != null)
                    {
                        EditorGUI.BeginDisabledGroup(true);
                        serializedTargetObjectProp.objectReferenceValue = EditorGUI.ObjectField(rect, serializedTargetObjectProp.objectReferenceValue, typeof(Object), true);
                        EditorGUI.EndDisabledGroup();
                    }
                }
                else if (!string.IsNullOrEmpty(nonSerializedTargetObjectNameProp.stringValue))
                {
                    // Non serialized object.
                    EditorGUI.LabelField(rect, nonSerializedTargetObjectNameProp.stringValue);
                }
                else
                {
                    EditorGUI.LabelField(rect, string.Empty);
                }
            }
            rect.x += rect.width - 13;

            // Method.
            if (isAnonymous)
            {
                EditorGUI.LabelField(rect, "Anonymous");
            }
            else
            {
                bool displayMethodDropdown = serializedTargetObjectProp.objectReferenceValue != null;
                if (displayMethodDropdown)
                {
                    bool           isMappingParamNull = mappingParamType == null;
                    CandidateLists candidateLists;
                    GetMethodOptions(serializedTargetObjectProp.objectReferenceValue.GetType(), mappingParamType, out candidateLists);
                    int  selectedMethodNameIndex = Array.IndexOf(candidateLists.methodNames, targetMethodNameProp.stringValue);
                    bool isPrivateMethod         = selectedMethodNameIndex == -1 && !string.IsNullOrEmpty(targetMethodNameProp.stringValue) && Application.isPlaying;
                    if (isPrivateMethod)
                    {
                        EditorGUI.LabelField(rect, targetMethodNameProp.stringValue);
                    }
                    else
                    {
                        int newSelectedMethodNameIndex = EditorGUI.Popup(rect, selectedMethodNameIndex, candidateLists.methodOptions);
                        if (newSelectedMethodNameIndex != selectedMethodNameIndex)
                        {
                            selectedMethodNameIndex          = newSelectedMethodNameIndex;
                            targetMethodNameProp.stringValue = candidateLists.methodNames[selectedMethodNameIndex];
                            if (!isMappingParamNull)
                            {
                                targetParamAssemblyNameProp.stringValue = candidateLists.paramTypes[selectedMethodNameIndex].AssemblyQualifiedName;
                            }
                        }
                    }
                }
                else
                {
                    EditorGUI.LabelField(rect, targetMethodNameProp.stringValue);
                }
            }

            // End.
            EditorGUI.EndProperty();
        }