[network-manager-applet/lr/team: 6/6] editor/team-port: add UI controls for team port configuration



commit 13eea21c6f1291f43c26f0b4f5170f9629cb4219
Author: Lubomir Rintel <lkundrak v3 sk>
Date:   Mon Jul 4 14:15:44 2016 +0200

    editor/team-port: add UI controls for team port configuration
    
    Editing raw JSON is still supported and kept in sync.

 src/connection-editor/ce-page-team-port.ui |  889 ++++++++++++++++++++++++++--
 src/connection-editor/page-team-port.c     |  528 ++++++++++++++++-
 2 files changed, 1369 insertions(+), 48 deletions(-)
---
diff --git a/src/connection-editor/ce-page-team-port.ui b/src/connection-editor/ce-page-team-port.ui
index 84d22b4..1f32fcb 100644
--- a/src/connection-editor/ce-page-team-port.ui
+++ b/src/connection-editor/ce-page-team-port.ui
@@ -1,62 +1,879 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
 <interface>
   <requires lib="gtk+" version="3.4"/>
   <object class="GtkGrid" id="TeamPortPage">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
+    <property name="hexpand">True</property>
+    <property name="vexpand">True</property>
     <property name="border_width">12</property>
+    <property name="row_spacing">8</property>
     <property name="column_spacing">12</property>
-    <property name="row_spacing">6</property>
-    <property name="column_homogeneous">True</property>
     <child>
-      <object class="GtkLabel" id="team_port_json_config_label">
+      <object class="GtkButton" id="advanced_button">
         <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="xalign">0</property>
-        <property name="label" translatable="yes">_JSON config:</property>
+        <property name="can_focus">True</property>
+        <property name="receives_default">True</property>
+        <property name="halign">end</property>
+        <property name="valign">end</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
         <property name="use_underline">True</property>
-        <property name="mnemonic_widget">team_port_json_config</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="stock">gtk-preferences</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Ad_vanced...</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
       </object>
       <packing>
         <property name="left_attach">0</property>
         <property name="top_attach">0</property>
+        <property name="width">2</property>
       </packing>
     </child>
-    <child>
-      <object class="GtkScrolledWindow" id="scrolledwindow1">
-        <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="shadow_type">in</property>
-        <property name="min_content_height">100</property>
+  </object>
+  <object class="GtkAdjustment" id="delay_down_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="delay_up_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="init_wait_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="lacp_port_key_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">4294967296</property>
+    <property name="value">-1</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="lacp_port_prio_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">4294967296</property>
+    <property name="value">-1</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkListStore" id="link_watch_model">
+    <columns>
+      <!-- column-name type -->
+      <column type="gchararray"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0">master</col>
+        <col id="1" translatable="yes">Set by master</col>
+      </row>
+      <row>
+        <col id="0">ethtool</col>
+        <col id="1" translatable="yes">Ethernet port state</col>
+      </row>
+      <row>
+        <col id="0">arp_ping</col>
+        <col id="1" translatable="yes">ARP (IPv4)</col>
+      </row>
+      <row>
+        <col id="0">nsna_ping</col>
+        <col id="1" translatable="yes">NDP (IPv6)</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkAdjustment" id="missed_max_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="port_prio_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">4294967296</property>
+    <property name="value">-1</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="queue_id_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">4294967296</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="send_interval_adjustment">
+    <property name="lower">-1</property>
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkDialog" id="advanced_dialog">
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Team Advanced Options</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="advanced_dialog_box">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="advanced_buttons">
+            <property name="can_focus">False</property>
+            <property name="margin_left">6</property>
+            <property name="margin_right">6</property>
+            <property name="margin_top">6</property>
+            <property name="margin_bottom">6</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="advanced_cancel">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="advanced_ok">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
         <child>
-          <object class="GtkTextView" id="team_port_json_config">
+          <object class="GtkNotebook" id="advanced_notebook">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="hexpand">True</property>
+            <property name="margin_left">6</property>
+            <property name="margin_right">6</property>
+            <property name="margin_top">6</property>
+            <property name="margin_bottom">6</property>
+            <child>
+              <object class="GtkGrid" id="general_grid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="valign">start</property>
+                <property name="border_width">12</property>
+                <property name="row_spacing">6</property>
+                <property name="column_spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="queue_id_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">_Queue ID:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">queue_id</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="queue_id">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip_text" translatable="yes">Number of bursts of unsolicited NAs and 
gratuitous ARP packets sent after port is enabled or disabled.</property>
+                    <property name="hexpand">True</property>
+                    <property name="secondary_icon_tooltip_text" translatable="yes">ID of queue which this 
port should be mapped to.</property>
+                    <property name="adjustment">queue_id_adjustment</property>
+                    <property name="value">-1</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">etched-out</property>
+                    <child>
+                      <object class="GtkGrid">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="border_width">6</property>
+                        <property name="row_spacing">6</property>
+                        <property name="column_spacing">12</property>
+                        <child>
+                          <object class="GtkLabel" id="port_prio_label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="halign">start</property>
+                            <property name="hexpand">True</property>
+                            <property name="label" translatable="yes">_Port priority:</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="port_sticky">
+                            <property name="label" translatable="yes">Port _sticky</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="tooltip_text" translatable="yes">Validate received ARP packets 
on active ports. If this is not checked, all incoming ARP packets will be considered as a good 
reply.</property>
+                            <property name="halign">start</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">1</property>
+                            <property name="width">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="port_prio">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="tooltip_text" translatable="yes">Value is positive number in 
milliseconds. Specifies an interval between bursts of notify-peer packets.</property>
+                            <property name="hexpand">True</property>
+                            <property name="secondary_icon_tooltip_text" translatable="yes">Port priority. 
The higher number means higher priority.</property>
+                            <property name="adjustment">port_prio_adjustment</property>
+                            <property name="value">-1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Active-Backup runner options</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                    <property name="width">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">etched-out</property>
+                    <child>
+                      <object class="GtkGrid">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="border_width">6</property>
+                        <property name="row_spacing">6</property>
+                        <property name="column_spacing">12</property>
+                        <child>
+                          <object class="GtkLabel" id="lacp_port_prio_label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="halign">start</property>
+                            <property name="hexpand">True</property>
+                            <property name="label" translatable="yes">_LACP port priority:</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="lacp_port_key_label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="halign">start</property>
+                            <property name="hexpand">True</property>
+                            <property name="label" translatable="yes">LACP port _key:</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="lacp_port_prio">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="tooltip_text" translatable="yes">Number of bursts of multicast 
group rejoin requests sent after port is enabled or disabled.</property>
+                            <property name="hexpand">True</property>
+                            <property name="secondary_icon_tooltip_text" translatable="yes">Port priority 
according to LACP standard. The lower number means higher priority.</property>
+                            <property name="adjustment">lacp_port_prio_adjustment</property>
+                            <property name="value">-1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="lacp_port_key">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="tooltip_text" translatable="yes">Value is positive number in 
milliseconds. Specifies an interval between bursts of multicast group rejoin requests.</property>
+                            <property name="hexpand">True</property>
+                            <property name="secondary_icon_tooltip_text" translatable="yes">Port key 
according to LACP standard. It is only possible to aggregate ports with the same key.</property>
+                            <property name="adjustment">lacp_port_key_adjustment</property>
+                            <property name="value">-1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">LACP runner options</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                    <property name="width">2</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="general_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">General</property>
+              </object>
+              <packing>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid" id="link_watch_grid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="valign">start</property>
+                <property name="border_width">12</property>
+                <property name="row_spacing">6</property>
+                <property name="column_spacing">12</property>
+                <child>
+                  <object class="GtkLabel" id="link_watcher_name_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">_Link watcher:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">link_watcher_name</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="delay_up_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">_Up delay:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">delay_up</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="delay_down_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">_Down delay:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">delay_down</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="send_interval_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">Send _interval:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">send_interval</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="init_wait_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">Delay _before first send:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">init_wait</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="missed_max_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">_Maximum missed replies:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">missed_max</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">5</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="source_host_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">_Source host:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">source_host</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">6</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="target_host_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">False</property>
+                    <property name="label" translatable="yes">_Target host:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">target_host</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">7</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="missed_max">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip_text" translatable="yes">Maximum number of missed replies. If 
this number is exceeded, link is reported as down.</property>
+                    <property name="hexpand">True</property>
+                    <property name="adjustment">missed_max_adjustment</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">5</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="source_host">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip_text" translatable="yes">Hostname to be converted to IP address 
which will be filled into ARP request as source address.</property>
+                    <property name="hexpand">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">6</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="target_host">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip_text" translatable="yes">Hostname to be converted to IP address 
which will be filled into request as destination address.</property>
+                    <property name="hexpand">False</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">7</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="validate_active">
+                    <property name="label" translatable="yes">Ignore invalid packets from _active 
ports</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">Validate received ARP packets on active 
ports. If this is not checked, all incoming ARP packets will be considered as a good reply.</property>
+                    <property name="halign">start</property>
+                    <property name="use_underline">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">8</property>
+                    <property name="width">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="validate_inactive">
+                    <property name="label" translatable="yes">Ignore invalid packets from i_nactive 
ports</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">Validate received ARP packets on 
inactive ports. If this is not checked, all incoming ARP packets will be considered as a good 
reply.</property>
+                    <property name="halign">start</property>
+                    <property name="use_underline">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">9</property>
+                    <property name="width">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="send_all">
+                    <property name="label" translatable="yes">S_end on inactive ports</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">By default, ARP requests are sent on 
active ports only. This option allows sending even on inactive ports.</property>
+                    <property name="halign">start</property>
+                    <property name="use_underline">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">10</property>
+                    <property name="width">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="delay_up_ms">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="label">ms</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="delay_down_ms">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="label">ms</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="send_interval_ms">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="label">ms</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="init_wait_ms">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="label">ms</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="delay_up">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip_text" translatable="yes">The delay between the link coming up 
and the runner being notified about it.</property>
+                    <property name="hexpand">True</property>
+                    <property name="adjustment">delay_up_adjustment</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="delay_down">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip_text" translatable="yes">The delay between the link going down 
and the runner being notified about it.</property>
+                    <property name="hexpand">True</property>
+                    <property name="adjustment">delay_down_adjustment</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="send_interval">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip_text" translatable="yes">The interval between requests being 
sent.</property>
+                    <property name="hexpand">True</property>
+                    <property name="adjustment">send_interval_adjustment</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="init_wait">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="tooltip_text" translatable="yes">The delay between link watch 
initialization and the first request being sent.</property>
+                    <property name="hexpand">False</property>
+                    <property name="adjustment">init_wait_adjustment</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBox" id="link_watcher_name">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="tooltip_text" translatable="yes">The link watcher to be used.</property>
+                    <property name="hexpand">True</property>
+                    <property name="model">link_watch_model</property>
+                    <property name="active">0</property>
+                    <property name="id_column">0</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="link_watch_renderer"/>
+                      <attributes>
+                        <attribute name="text">1</attribute>
+                      </attributes>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="link_watch_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Link Watcher</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid" id="json_grid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="vexpand">True</property>
+                <property name="border_width">12</property>
+                <property name="row_spacing">8</property>
+                <property name="column_spacing">12</property>
+                <child>
+                  <object class="GtkButton" id="import_config_button">
+                    <property name="label" translatable="yes">Im_port team configuration from a 
file...</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="shadow_type">in</property>
+                    <property name="min_content_height">100</property>
+                    <child>
+                      <object class="GtkTextView" id="team_port_json_config">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hexpand">True</property>
+                        <property name="vexpand">True</property>
+                        <property name="monospace">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="team_port_json_config_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Edit _JSON configuration:</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">team_port_json_config</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="json_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Raw Configuration</property>
+              </object>
+              <packing>
+                <property name="position">2</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
           </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
         </child>
       </object>
-      <packing>
-        <property name="left_attach">0</property>
-        <property name="top_attach">1</property>
-        <property name="width">2</property>
-        <property name="height">1</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkButton" id="import_config_button">
-        <property name="label" translatable="yes">_Import team configuration from a file...</property>
-        <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="use_underline">True</property>
-        <property name="hexpand">True</property>
-      </object>
-      <packing>
-        <property name="left_attach">0</property>
-        <property name="top_attach">2</property>
-        <property name="width">2</property>
-        <property name="height">1</property>
-      </packing>
     </child>
+    <action-widgets>
+      <action-widget response="-6">advanced_cancel</action-widget>
+      <action-widget response="-5">advanced_ok</action-widget>
+    </action-widgets>
   </object>
 </interface>
diff --git a/src/connection-editor/page-team-port.c b/src/connection-editor/page-team-port.c
index e033ccf..a89d4ce 100644
--- a/src/connection-editor/page-team-port.c
+++ b/src/connection-editor/page-team-port.c
@@ -15,11 +15,15 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright 2013 Jiri Pirko <jiri resnulli us>
- * Copyright 2013 - 2014  Red Hat, Inc.
+ * Copyright 2013 - 2016  Red Hat, Inc.
  */
 
 #include "nm-default.h"
 
+#if WITH_JANSSON
+#include <jansson.h>
+#endif
+
 #include <string.h>
 
 #include "page-team-port.h"
@@ -33,8 +37,97 @@ typedef struct {
 
        GtkTextView *json_config_widget;
        GtkWidget *import_config_button;
+
+       GtkButton *advanced_button;
+       GtkDialog *advanced_dialog;
+       GtkNotebook *advanced_notebook;
+
+       /* General */
+       GtkSpinButton *queue_id;
+       GtkSpinButton *port_prio;
+       GtkToggleButton *port_sticky;
+       GtkSpinButton *lacp_port_prio;
+       GtkSpinButton *lacp_port_key;
+
+       /* Link Watch */
+       GtkComboBox *link_watcher_name;
+       GtkSpinButton *delay_up;
+       GtkLabel *delay_up_label;
+       GtkLabel *delay_up_ms;
+       GtkSpinButton *delay_down;
+       GtkLabel *delay_down_label;
+       GtkLabel *delay_down_ms;
+       GtkSpinButton *send_interval;
+       GtkLabel *send_interval_label;
+       GtkLabel *send_interval_ms;
+       GtkSpinButton *init_wait;
+       GtkLabel *init_wait_label;
+       GtkLabel *init_wait_ms;
+       GtkSpinButton *missed_max;
+       GtkLabel *missed_max_label;
+       GtkEntry *source_host;
+       GtkLabel *source_host_label;
+       GtkEntry *target_host;
+       GtkLabel *target_host_label;
+       GtkToggleButton *validate_active;
+       GtkToggleButton *validate_inactive;
+       GtkToggleButton *send_all;
 } CEPageTeamPortPrivate;
 
+/* Get string "type" from a combo box with a ["type", "name"] model. */
+static gchar *
+get_combo_box (GtkComboBox *combo)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gchar *name;
+
+       if (!gtk_combo_box_get_active_iter (combo, &iter))
+               g_return_val_if_reached (NULL);
+
+       model = gtk_combo_box_get_model (combo);
+       gtk_tree_model_get (model, &iter, 0, &name, -1);
+
+       return name;
+}
+
+#if WITH_JANSSON
+
+/* Set active item to "type" in a combo box with a ["type", "name"] model. */
+static gboolean
+set_combo_box (GtkComboBox *combo, const gchar *value, GError **error)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gboolean valid;
+       const gchar *name;
+       int i = 0;
+
+       if (!value || !*value) {
+               gtk_combo_box_set_active (combo, 0);
+               return TRUE;
+       }
+
+       model = gtk_combo_box_get_model (combo);
+       for (valid = gtk_tree_model_get_iter_first (model, &iter);
+            valid;
+            valid = gtk_tree_model_iter_next (model, &iter)) {
+               gtk_tree_model_get (model, &iter, 0, &name, -1);
+               if (strcmp (name, value) == 0) {
+                       gtk_combo_box_set_active (combo, i);
+                       return TRUE;
+               }
+               i++;
+       }
+
+       g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC,
+                    "Value of \"%s\" is not known", value);
+
+       return FALSE;
+}
+
+#endif
+
 static void
 team_port_private_init (CEPageTeamPort *self)
 {
@@ -45,12 +138,51 @@ team_port_private_init (CEPageTeamPort *self)
 
        priv->json_config_widget = GTK_TEXT_VIEW (gtk_builder_get_object (builder, "team_port_json_config"));
        priv->import_config_button = GTK_WIDGET (gtk_builder_get_object (builder, "import_config_button"));
-}
-
-static void
-json_config_changed (GObject *object, CEPageTeamPort *self)
-{
-       ce_page_changed (CE_PAGE (self));
+       priv->advanced_button = GTK_BUTTON (gtk_builder_get_object (builder, "advanced_button"));
+       priv->advanced_dialog = GTK_DIALOG (gtk_builder_get_object (builder, "advanced_dialog"));
+       gtk_window_set_modal (GTK_WINDOW (priv->advanced_dialog), TRUE);
+       priv->advanced_notebook = GTK_NOTEBOOK (gtk_builder_get_object (builder, "advanced_notebook"));
+
+       /* General */
+       priv->queue_id = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "queue_id"));
+       priv->port_prio = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "port_prio"));
+       priv->port_sticky = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "port_sticky"));
+       priv->lacp_port_prio = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "lacp_port_prio"));
+       priv->lacp_port_key = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "lacp_port_key"));
+
+       /* Link Watcher */
+       priv->link_watcher_name = GTK_COMBO_BOX (gtk_builder_get_object (builder, "link_watcher_name"));
+       priv->send_interval = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "send_interval"));
+       priv->send_interval_label = GTK_LABEL (gtk_builder_get_object (builder, "send_interval_label"));
+       priv->send_interval_ms = GTK_LABEL (gtk_builder_get_object (builder, "send_interval_ms"));
+       priv->init_wait = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "init_wait"));
+       priv->init_wait_label = GTK_LABEL (gtk_builder_get_object (builder, "init_wait_label"));
+       priv->init_wait_ms = GTK_LABEL (gtk_builder_get_object (builder, "init_wait_ms"));
+       priv->missed_max = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "missed_max"));
+       priv->missed_max_label = GTK_LABEL (gtk_builder_get_object (builder, "missed_max_label"));
+       priv->source_host = GTK_ENTRY (gtk_builder_get_object (builder, "source_host"));
+       priv->source_host_label = GTK_LABEL (gtk_builder_get_object (builder, "source_host_label"));
+       priv->target_host = GTK_ENTRY (gtk_builder_get_object (builder, "target_host"));
+       priv->target_host_label = GTK_LABEL (gtk_builder_get_object (builder, "target_host_label"));
+       priv->validate_active = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "validate_active"));
+       priv->validate_inactive = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "validate_inactive"));
+       priv->send_all = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "send_all"));
+       priv->delay_up = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "delay_up"));
+       priv->delay_up_label = GTK_LABEL (gtk_builder_get_object (builder, "delay_up_label"));
+       priv->delay_up_ms = GTK_LABEL (gtk_builder_get_object (builder, "delay_up_ms"));
+       priv->delay_down = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "delay_down"));
+       priv->delay_down_label = GTK_LABEL (gtk_builder_get_object (builder, "delay_down_label"));
+       priv->delay_down_ms = GTK_LABEL (gtk_builder_get_object (builder, "delay_down_ms"));
+
+       ce_spin_default_val (priv->queue_id, -1);
+       ce_spin_default_val (priv->port_prio, -1);
+       ce_spin_default_val (priv->lacp_port_prio, -1);
+       ce_spin_default_val (priv->lacp_port_key, -1);
+       ce_spin_default_val (priv->send_interval, -1);
+       ce_spin_default_val (priv->init_wait, -1);
+       ce_spin_default_val (priv->missed_max, -1);
+       ce_spin_default_val (priv->delay_up, -1);
+       ce_spin_default_val (priv->delay_down, -1);
 }
 
 static void
@@ -58,18 +190,18 @@ import_button_clicked_cb (GtkWidget *widget, CEPageTeamPort *self)
 {
        CEPageTeamPortPrivate *priv = CE_PAGE_TEAM_PORT_GET_PRIVATE (self);
        GtkWidget *dialog;
-       GtkWindow *toplevel;
        GtkTextBuffer *buffer;
        char *filename;
        char *buf = NULL;
        gsize buf_len;
+       GtkWidget *toplevel;
 
-       toplevel = GTK_WINDOW (gtk_widget_get_toplevel (widget));
-       if (!gtk_widget_is_toplevel (GTK_WIDGET (toplevel)))
-               toplevel = NULL;
+       toplevel = gtk_widget_get_toplevel (CE_PAGE (self)->page);
+       g_return_if_fail (toplevel);
+       g_return_if_fail (gtk_widget_is_toplevel (toplevel));
 
        dialog = gtk_file_chooser_dialog_new (_("Select file to import"),
-                                             toplevel,
+                                             GTK_WINDOW (toplevel),
                                              GTK_FILE_CHOOSER_ACTION_OPEN,
                                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                              GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
@@ -103,6 +235,376 @@ out:
 }
 
 static void
+link_watcher_changed (GtkComboBox *combo, gpointer user_data)
+{
+       CEPageTeamPortPrivate *priv = CE_PAGE_TEAM_PORT_GET_PRIVATE (user_data);
+       gchar *name;
+
+       /* First hide everything and then show just what we need. */
+       gtk_widget_hide (GTK_WIDGET (priv->delay_up));
+       gtk_widget_hide (GTK_WIDGET (priv->delay_up_label));
+       gtk_widget_hide (GTK_WIDGET (priv->delay_up_ms));
+       gtk_widget_hide (GTK_WIDGET (priv->delay_down));
+       gtk_widget_hide (GTK_WIDGET (priv->delay_down_label));
+       gtk_widget_hide (GTK_WIDGET (priv->delay_down_ms));
+       gtk_widget_hide (GTK_WIDGET (priv->send_interval));
+       gtk_widget_hide (GTK_WIDGET (priv->send_interval_label));
+       gtk_widget_hide (GTK_WIDGET (priv->send_interval_ms));
+       gtk_widget_hide (GTK_WIDGET (priv->init_wait));
+       gtk_widget_hide (GTK_WIDGET (priv->init_wait_label));
+       gtk_widget_hide (GTK_WIDGET (priv->init_wait_ms));
+       gtk_widget_hide (GTK_WIDGET (priv->missed_max));
+       gtk_widget_hide (GTK_WIDGET (priv->missed_max_label));
+       gtk_widget_hide (GTK_WIDGET (priv->source_host));
+       gtk_widget_hide (GTK_WIDGET (priv->source_host_label));
+       gtk_widget_hide (GTK_WIDGET (priv->target_host));
+       gtk_widget_hide (GTK_WIDGET (priv->target_host_label));
+       gtk_widget_hide (GTK_WIDGET (priv->validate_active));
+       gtk_widget_hide (GTK_WIDGET (priv->validate_inactive));
+       gtk_widget_hide (GTK_WIDGET (priv->send_all));
+
+       name = get_combo_box (combo);
+       if (g_strcmp0 (name, "master") == 0) {
+               /* Leave everything disabled. */
+       } else if (g_strcmp0 (name, "ethtool") == 0) {
+               gtk_widget_show (GTK_WIDGET (priv->delay_up));
+               gtk_widget_show (GTK_WIDGET (priv->delay_up_label));
+               gtk_widget_show (GTK_WIDGET (priv->delay_up_ms));
+               gtk_widget_show (GTK_WIDGET (priv->delay_down));
+               gtk_widget_show (GTK_WIDGET (priv->delay_down_label));
+               gtk_widget_show (GTK_WIDGET (priv->delay_down_ms));
+       } else if (g_strcmp0 (name, "arp_ping") == 0) {
+               gtk_widget_show (GTK_WIDGET (priv->send_interval));
+               gtk_widget_show (GTK_WIDGET (priv->send_interval_label));
+               gtk_widget_show (GTK_WIDGET (priv->send_interval_ms));
+               gtk_widget_show (GTK_WIDGET (priv->init_wait));
+               gtk_widget_show (GTK_WIDGET (priv->init_wait_label));
+               gtk_widget_show (GTK_WIDGET (priv->init_wait_ms));
+               gtk_widget_show (GTK_WIDGET (priv->missed_max));
+               gtk_widget_show (GTK_WIDGET (priv->missed_max_label));
+               gtk_widget_show (GTK_WIDGET (priv->source_host));
+               gtk_widget_show (GTK_WIDGET (priv->source_host_label));
+               gtk_widget_show (GTK_WIDGET (priv->target_host));
+               gtk_widget_show (GTK_WIDGET (priv->target_host_label));
+               gtk_widget_show (GTK_WIDGET (priv->validate_active));
+               gtk_widget_show (GTK_WIDGET (priv->validate_inactive));
+               gtk_widget_show (GTK_WIDGET (priv->send_all));
+       } else if (g_strcmp0 (name, "nsna_ping") == 0) {
+               gtk_widget_show (GTK_WIDGET (priv->send_interval));
+               gtk_widget_show (GTK_WIDGET (priv->send_interval_label));
+               gtk_widget_show (GTK_WIDGET (priv->send_interval_ms));
+               gtk_widget_show (GTK_WIDGET (priv->init_wait));
+               gtk_widget_show (GTK_WIDGET (priv->init_wait_label));
+               gtk_widget_show (GTK_WIDGET (priv->init_wait_ms));
+               gtk_widget_show (GTK_WIDGET (priv->missed_max));
+               gtk_widget_show (GTK_WIDGET (priv->missed_max_label));
+               gtk_widget_show (GTK_WIDGET (priv->target_host));
+               gtk_widget_show (GTK_WIDGET (priv->target_host_label));
+       } else {
+               g_return_if_reached ();
+       }
+       g_free (name);
+}
+
+#if WITH_JANSSON
+
+static gboolean
+json_to_dialog (CEPageTeamPort *self)
+{
+       CEPageTeamPortPrivate *priv = CE_PAGE_TEAM_PORT_GET_PRIVATE (self);
+       char *json_config;
+       json_t *json;
+       json_error_t json_error;
+       int ret;
+       gboolean success = TRUE;
+       GtkTextIter start, end;
+       GtkTextBuffer *buffer;
+       GError *error = NULL;
+       /* General */
+       int queue_id = -1;
+       int port_prio = -1;
+       int port_sticky = FALSE;
+       int lacp_port_prio = -1;
+       int lacp_port_key = -1;
+       /* Link Watch */
+       const char *link_watch_name = "";
+       int link_watch_delay_up = -1;
+       int link_watch_delay_down = -1;
+       int link_watch_interval = -1;
+       int link_watch_init_wait = -1;
+       int link_watch_missed_max = -1;
+       const char *link_watch_source_host = "";
+       const char *link_watch_target_host = "";
+       int link_watch_validate_active = FALSE;
+       int link_watch_validate_inactive = FALSE;
+       int link_watch_send_always = FALSE;
+
+       buffer = gtk_text_view_get_buffer (priv->json_config_widget);
+       gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
+       gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
+       json_config = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+       if (strcmp (json_config, "") == 0) {
+               /* Initial empty configuration */
+               json = json_object ();
+       } else {
+               json = json_loads (json_config, 0, &json_error);
+       }
+
+       if (!json) {
+               g_message ("Failed to parse JSON: %s on line %d", json_error.text, json_error.line);
+               success = FALSE;
+       }
+
+       /* For simplicity, we proceed with json==NULL. The attempt to
+        * unpack will produce an error which we'll ignore. */
+       g_free (json_config);
+       ret = json_unpack_ex (json, &json_error, 0,
+                             "{"
+                             " s?:i,"
+                             " s?:i,"
+                             " s?:b,"
+                             " s?:i,"
+                             " s?:i,"
+                             " s?:{s?:s, s?:i, s?:i, s?:i, s?:i, s?:i, s?:s, s?:s, s?:b, s?:b, s?:b !}"
+                             "!}",
+                             "queue_id", &queue_id,
+                             "prio", &port_prio,
+                             "sticky", &port_sticky,
+                             "lacp_prio", &lacp_port_prio,
+                             "lacp_key", &lacp_port_key,
+                             "link_watch",
+                                 "name", &link_watch_name,
+                                 "delay_up", &link_watch_delay_up,
+                                 "delay_down", &link_watch_delay_down,
+                                 "interval", &link_watch_interval,
+                                 "init_wait", &link_watch_init_wait,
+                                 "missed_max", &link_watch_missed_max,
+                                 "source_host", &link_watch_source_host,
+                                 "target_host", &link_watch_target_host,
+                                 "validate_active", &link_watch_validate_active,
+                                 "validate_inactive", &link_watch_validate_inactive,
+                                 "send_always", &link_watch_send_always);
+
+       if (success == TRUE && ret == -1) {
+               g_message ("Failed to parse JSON: %s on line %d", json_error.text, json_error.line);
+               success = FALSE;
+       }
+
+       /* We proceed setting the form fields even in case we couldn't unpack.
+        * That way we'll get at least sensible default values. Editing will
+        * be disabled anyway. */
+       gtk_spin_button_set_value (priv->queue_id, queue_id);
+       gtk_spin_button_set_value (priv->port_prio, port_prio);
+       gtk_toggle_button_set_active (priv->port_sticky, port_sticky);
+       gtk_spin_button_set_value (priv->lacp_port_prio, lacp_port_prio);
+       gtk_spin_button_set_value (priv->lacp_port_key, lacp_port_key);
+       if (!set_combo_box (priv->link_watcher_name, link_watch_name, &error)) {
+               g_message ("Cannot read link watcher name: %s", error->message);
+               g_clear_error (&error);
+               success = FALSE;
+       }
+       gtk_spin_button_set_value (priv->delay_up, link_watch_delay_up);
+       gtk_spin_button_set_value (priv->delay_down, link_watch_delay_down);
+       gtk_spin_button_set_value (priv->send_interval, link_watch_interval);
+       gtk_spin_button_set_value (priv->init_wait, link_watch_init_wait);
+       gtk_spin_button_set_value (priv->missed_max, link_watch_missed_max);
+       gtk_entry_set_text (priv->source_host, link_watch_source_host);
+       gtk_entry_set_text (priv->target_host, link_watch_target_host);
+       gtk_toggle_button_set_active (priv->validate_active, link_watch_validate_active);
+       gtk_toggle_button_set_active (priv->validate_inactive, link_watch_validate_inactive);
+       gtk_toggle_button_set_active (priv->send_all, link_watch_send_always);
+
+       if (success) {
+               /* Enable editing. */
+               gtk_widget_set_sensitive (gtk_notebook_get_nth_page (priv->advanced_notebook, 0), 1);
+               gtk_widget_set_sensitive (gtk_notebook_get_nth_page (priv->advanced_notebook, 1), 1);
+       }
+
+       json_decref (json);
+       return success;
+}
+
+static void
+maybe_set_str (json_t *json, const char *key, const char *str)
+{
+       if (str && *str)
+               json_object_set_new(json, key, json_string (str));
+}
+
+static void
+maybe_set_int (json_t *json, const char *key, int num)
+{
+       if (num != -1)
+               json_object_set_new(json, key, json_integer (num));
+}
+
+static void
+maybe_set_json (json_t *json, const char *key, json_t *val)
+{
+       if (   (json_is_object (val) && json_object_size (val))
+           || (json_is_array (val) && json_array_size (val))) {
+               json_object_set_new (json, key, val);
+       } else {
+               json_decref (val);
+       }
+}
+
+static void
+maybe_set_true (json_t *json, const char *key, int set)
+{
+       if (set)
+               json_object_set_new(json, key, json_true ());
+       else
+               json_object_set_new(json, key, json_false ());
+}
+
+static void
+dialog_to_json (CEPageTeamPort *self)
+{
+       CEPageTeamPortPrivate *priv = CE_PAGE_TEAM_PORT_GET_PRIVATE (self);
+       json_t *json;
+       json_t *obj;
+       gchar *tmp;
+       char *json_config;
+       GtkTextBuffer *buffer;
+
+       /* If the JSON is being edited, don't overwrite it. */
+       if (!gtk_widget_get_sensitive (gtk_notebook_get_nth_page (priv->advanced_notebook, 0)))
+               return;
+
+       /* Disable editing via form, until converted back from JSON. */
+       gtk_widget_set_sensitive (gtk_notebook_get_nth_page (priv->advanced_notebook, 0), 0);
+       gtk_widget_set_sensitive (gtk_notebook_get_nth_page (priv->advanced_notebook, 1), 0);
+
+       json = json_object ();
+       maybe_set_int (json, "queue_id", gtk_spin_button_get_value_as_int (priv->queue_id));
+       maybe_set_int (json, "prio", gtk_spin_button_get_value_as_int (priv->port_prio));
+       if (gtk_toggle_button_get_active (priv->port_sticky))
+               json_object_set_new(json, "sticky", json_true ());
+       maybe_set_int (json, "lacp_prio", gtk_spin_button_get_value_as_int (priv->lacp_port_prio));
+       maybe_set_int (json, "lacp_key", gtk_spin_button_get_value_as_int (priv->lacp_port_key));
+
+       obj = json_object ();
+       tmp = get_combo_box (priv->link_watcher_name);
+       if (g_strcmp0 (tmp, "master") != 0) {
+               maybe_set_str (obj, "name", tmp);
+               if (g_strcmp0 (tmp, "ethtool") == 0) {
+                       maybe_set_int (obj, "delay_up", gtk_spin_button_get_value (priv->delay_up));
+                       maybe_set_int (obj, "delay_down", gtk_spin_button_get_value (priv->delay_down));
+               } else if (g_strcmp0 (tmp, "arp_ping") == 0) {
+                       maybe_set_int (obj, "interval", gtk_spin_button_get_value (priv->send_interval));
+                       maybe_set_int (obj, "init_wait", gtk_spin_button_get_value (priv->init_wait));
+                       maybe_set_int (obj, "missed_max", gtk_spin_button_get_value (priv->missed_max));
+                       maybe_set_str (obj, "source_host", gtk_entry_get_text (priv->source_host));
+                       maybe_set_str (obj, "target_host", gtk_entry_get_text (priv->target_host));
+                       maybe_set_true (obj, "validate_active", gtk_toggle_button_get_active 
(priv->validate_active));
+                       maybe_set_true (obj, "validate_inactive", gtk_toggle_button_get_active 
(priv->validate_inactive));
+                       maybe_set_true (obj, "send_always", gtk_toggle_button_get_active (priv->send_all));
+               } else if (g_strcmp0 (tmp, "nsna_ping") == 0) {
+                       maybe_set_int (obj, "interval", gtk_spin_button_get_value (priv->send_interval));
+                       maybe_set_int (obj, "init_wait", gtk_spin_button_get_value (priv->init_wait));
+                       maybe_set_int (obj, "missed_max", gtk_spin_button_get_value (priv->missed_max));
+                       maybe_set_str (obj, "target_host", gtk_entry_get_text (priv->target_host));
+               } else {
+                       g_return_if_reached ();
+               }
+       }
+       maybe_set_json (json, "link_watch", obj);
+       g_free (tmp);
+
+       json_config = json_dumps (json, JSON_INDENT (4));
+       json_decref (json);
+
+       buffer = gtk_text_view_get_buffer (priv->json_config_widget);
+       gtk_text_buffer_set_text (buffer, json_config, -1);
+       free (json_config);
+}
+
+#else /* WITH_JANSSON */
+
+static gboolean
+json_to_dialog (CEPageTeamPort *self)
+{
+       return FALSE;
+}
+
+static void
+dialog_to_json (CEPageTeamPort *self)
+{
+}
+
+#endif /* WITH_JANSSON */
+
+static void
+advanced_button_clicked_cb (GtkWidget *button, gpointer user_data)
+{
+       CEPageTeamPort *self = CE_PAGE_TEAM_PORT (user_data);
+       CEPageTeamPortPrivate *priv = CE_PAGE_TEAM_PORT_GET_PRIVATE (self);
+       NMSettingTeamPort *s_port = priv->setting;
+       GtkWidget *toplevel;
+       GtkTextBuffer *buffer;
+       GtkTextIter start, end;
+       char *json_config = NULL;
+
+       toplevel = gtk_widget_get_toplevel (CE_PAGE (self)->page);
+       g_return_if_fail (toplevel);
+       gtk_window_set_transient_for (GTK_WINDOW (priv->advanced_dialog), GTK_WINDOW (toplevel));
+       g_return_if_fail (gtk_widget_is_toplevel (toplevel));
+
+       /* Load in the JSON from settings to dialog. */
+       buffer = gtk_text_view_get_buffer (priv->json_config_widget);
+       gtk_text_buffer_set_text (buffer, nm_setting_team_port_get_config (s_port) ?: "", -1);
+
+       /* Fill in the form fields. */
+       if (json_to_dialog (self)) {
+               gtk_notebook_set_current_page (priv->advanced_notebook, 0);
+       } else {
+               /* First disable the pages, so that potentially
+                * inconsistent changes are not propageated to JSON. */
+               gtk_widget_set_sensitive (gtk_notebook_get_nth_page (priv->advanced_notebook, 0), 0);
+               gtk_widget_set_sensitive (gtk_notebook_get_nth_page (priv->advanced_notebook, 1), 0);
+               gtk_notebook_set_current_page (priv->advanced_notebook, 2);
+       }
+
+       link_watcher_changed (priv->link_watcher_name, self);
+
+       if (gtk_dialog_run (priv->advanced_dialog) == GTK_RESPONSE_OK) {
+               dialog_to_json (self);
+
+               /* Set the JSON from the dialog to setting. */
+               gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
+               gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
+               json_config = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+               g_object_set (priv->setting,
+                             NM_SETTING_TEAM_CONFIG,
+                             g_strcmp0 (json_config, "") == 0 ? NULL : json_config,
+                             NULL);
+               g_free (json_config);
+               ce_page_changed (CE_PAGE (self));
+       }
+       gtk_widget_hide (GTK_WIDGET (priv->advanced_dialog));
+}
+
+static gboolean
+switch_page (GtkNotebook *notebook,
+             GtkWidget   *page,
+             guint        page_num,
+             gpointer     user_data)
+{
+       CEPageTeamPort *self = CE_PAGE_TEAM_PORT (user_data);
+
+       /* Keep the JSON and the form in sync if possible. */
+       if (gtk_notebook_get_current_page (notebook) == 2)
+               json_to_dialog (self);
+       else if (page_num == 2)
+               dialog_to_json (self);
+
+       return TRUE;
+}
+
+static void
 populate_ui (CEPageTeamPort *self)
 {
        CEPageTeamPortPrivate *priv = CE_PAGE_TEAM_PORT_GET_PRIVATE (self);
@@ -114,8 +616,10 @@ populate_ui (CEPageTeamPort *self)
        json_config = nm_setting_team_port_get_config (s_port);
        gtk_text_buffer_set_text (buffer, json_config ? json_config : "", -1);
 
-       g_signal_connect (buffer, "changed", G_CALLBACK (json_config_changed), self);
        g_signal_connect (priv->import_config_button, "clicked", G_CALLBACK (import_button_clicked_cb), self);
+       g_signal_connect (priv->link_watcher_name, "changed", G_CALLBACK (link_watcher_changed), self);
+       g_signal_connect (priv->advanced_button, "clicked", G_CALLBACK (advanced_button_clicked_cb), self);
+       g_signal_connect (priv->advanced_notebook, "switch-page", G_CALLBACK (switch_page), self);
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]