[PATCH] Add GtkNotebook support to the acclerators check.

David Shea dshea at redhat.com
Fri Sep 20 19:50:00 UTC 2013


This removes the need for most of the exception comments. The only
exceptions left are one in installation source where elements are shown
and hidden based on a radio button selection, and one in the iscsi login
where elements are shown and hidden based on a dropdown.
---
 pyanaconda/ui/gui/spokes/advstorage/iscsi.glade |  11 +-
 pyanaconda/ui/gui/spokes/custom.glade           |   2 -
 pyanaconda/ui/gui/spokes/filter.glade           |   7 --
 pyanaconda/ui/gui/spokes/network.glade          |   4 -
 tests/accelerators/check_accelerators.py        | 150 ++++++++++++++++++------
 5 files changed, 117 insertions(+), 57 deletions(-)

diff --git a/pyanaconda/ui/gui/spokes/advstorage/iscsi.glade b/pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
index 98fb46f..208122a 100644
--- a/pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
+++ b/pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
@@ -397,7 +397,6 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <property name="xalign">0</property>
-                                <!-- check_accelerators: not visible at same time as label2 -->
                                 <property name="label" translatable="yes">CHAP _Username:</property>
                                 <property name="use_underline">True</property>
                                 <property name="mnemonic_widget">rchapUsernameEntry</property>
@@ -414,7 +413,6 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <property name="xalign">0</property>
-                                <!-- check_accelerators: not visible at same time as label3 -->
                                 <property name="label" translatable="yes">CHAP _Password:</property>
                                 <property name="use_underline">True</property>
                                 <property name="mnemonic_widget">rchapPasswordEntry</property>
@@ -764,7 +762,6 @@
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="xalign">0</property>
-                            <!-- check_accelerators: not visible at same time as label11 -->
                             <property name="label" translatable="yes">_Node login authentication type:</property>
                             <property name="use_underline">True</property>
                             <property name="mnemonic_widget">loginAuthTypeCombo</property>
@@ -823,7 +820,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="xalign">0</property>
-                                    <!-- check_accelerators: not visible at same time as label2 -->
                                     <property name="label" translatable="yes">CHAP _Username:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">loginChapUsernameEntry</property>
@@ -840,7 +836,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="xalign">0</property>
-                                    <!-- check_accelerators: not visible at same time as label3 -->
                                     <property name="label" translatable="yes">CHAP _Password:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">loginChapPasswordEntry</property>
@@ -967,7 +962,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="xalign">0</property>
-                                    <!-- check_accelerators: not visible at same time as label2 -->
                                     <property name="label" translatable="yes">CHAP _Username:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">loginRchapUsernameEntry</property>
@@ -984,7 +978,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="xalign">0</property>
-                                    <!-- check_accelerators: not visible at same time as label3 -->
                                     <property name="label" translatable="yes">CHAP _Password:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">loginRchapPasswordEntry</property>
@@ -1001,7 +994,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="xalign">0</property>
-                                    <!-- check_accelerators: not visible at same time as retryButton -->
                                     <property name="label" translatable="yes">_Reverse CHAP Username:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">loginRchapReverseUsername</property>
@@ -1018,7 +1010,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="xalign">0</property>
-                                    <!-- check_accelerators: not visible at same time as label12 -->
                                     <property name="label" translatable="yes">Reverse CHAP Pass_word:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">loginRchapReversePassword</property>
@@ -1170,7 +1161,7 @@
                                 </child>
                                 <child>
                                   <object class="GtkButton" id="retryLoginButton">
-                                    <!-- check_accelerators: not visible at same time as retryButton -->
+                                    <!-- check_accelerators: not visible at same time as label19 -->
                                     <property name="label" translatable="yes">_Retry Log In</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
diff --git a/pyanaconda/ui/gui/spokes/custom.glade b/pyanaconda/ui/gui/spokes/custom.glade
index 1b5de13..2c45c5f 100644
--- a/pyanaconda/ui/gui/spokes/custom.glade
+++ b/pyanaconda/ui/gui/spokes/custom.glade
@@ -1364,7 +1364,6 @@ until you click on the main menu's 'Begin Installation' button.</property>
                                   <object class="GtkLabel" id="passphraseLabel">
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
-                                    <!-- check_accelerators: not visible at same time as mountPointLabel -->
                                     <property name="label" translatable="yes">_Passphrase:</property>
                                     <property name="use_underline">True</property>
                                     <attributes>
@@ -1393,7 +1392,6 @@ until you click on the main menu's 'Begin Installation' button.</property>
                                 </child>
                                 <child>
                                   <object class="GtkButton" id="unlockButton">
-                                    <!-- check_accelerators: not visible at same time as applyButton -->
                                     <property name="label" translatable="yes">_Unlock</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
diff --git a/pyanaconda/ui/gui/spokes/filter.glade b/pyanaconda/ui/gui/spokes/filter.glade
index e98ed1a..8c50349 100644
--- a/pyanaconda/ui/gui/spokes/filter.glade
+++ b/pyanaconda/ui/gui/spokes/filter.glade
@@ -641,7 +641,6 @@
                                           <object class="GtkLabel" id="label16">
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
-                                            <!-- check_accelerators: not visible at same time as label7 -->
                                             <property name="label" translatable="yes">Show Only _Devices With:</property>
                                             <property name="use_underline">True</property>
                                             <property name="mnemonic_widget">multipathInterconnectCombo</property>
@@ -682,7 +681,6 @@
                                           <object class="GtkLabel" id="label17">
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
-                                            <!-- check_accelerators: not visible at same time as label7 -->
                                             <property name="label" translatable="yes">Show Only _Devices Containing:</property>
                                             <property name="use_underline">True</property>
                                             <property name="mnemonic_widget">multipathWWIDEntry</property>
@@ -901,7 +899,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="xalign">0</property>
-                                    <!-- check_accelerators: not visible at same time as label2 -->
                                     <property name="label" translatable="yes">_Filter By:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">multipathTypeCombo</property>
@@ -960,7 +957,6 @@
                                           <object class="GtkLabel" id="label12">
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
-                                            <!-- check_accelerators: not visible at same time as label7 -->
                                             <property name="label" translatable="yes">Show Only _Devices From:</property>
                                             <property name="use_underline">True</property>
                                             <property name="mnemonic_widget">otherVendorCombo</property>
@@ -1001,7 +997,6 @@
                                           <object class="GtkLabel" id="label20">
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
-                                            <!-- check_accelerators: not visible at same time as label7 -->
                                             <property name="label" translatable="yes">Show Only _Devices With:</property>
                                             <property name="use_underline">True</property>
                                             <property name="mnemonic_widget">otherInterconnectCombo</property>
@@ -1042,7 +1037,6 @@
                                           <object class="GtkLabel" id="label21">
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
-                                            <!-- check_accelerators: not visible at same time as label7 -->
                                             <property name="label" translatable="yes">Show Only _Devices Containing:</property>
                                             <property name="use_underline">True</property>
                                             <property name="mnemonic_widget">otherIDEntry</property>
@@ -1272,7 +1266,6 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="xalign">0</property>
-                                    <!-- check_accelerators: not visible at same time as label2 -->
                                     <property name="label" translatable="yes">_Filter By:</property>
                                     <property name="use_underline">True</property>
                                     <property name="mnemonic_widget">zFilterCombo</property>
diff --git a/pyanaconda/ui/gui/spokes/network.glade b/pyanaconda/ui/gui/spokes/network.glade
index 14ec157..5701e68 100644
--- a/pyanaconda/ui/gui/spokes/network.glade
+++ b/pyanaconda/ui/gui/spokes/network.glade
@@ -1641,7 +1641,6 @@ updates available for you.</property>
                                         <property name="top_padding">12</property>
                                         <child>
                                           <object class="GtkButton" id="button_mobilebb_options">
-                                            <!-- check_accelerators: not in same tab as button_wireless_options -->
                                             <property name="label" translatable="yes">_Configure...</property>
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
@@ -1927,7 +1926,6 @@ updates available for you.</property>
                                         <property name="top_padding">12</property>
                                         <child>
                                           <object class="GtkButton" id="button_vpn_options">
-                                            <!-- check_accelerators: not in same tab as button_wireless_options -->
                                             <property name="label" translatable="yes">_Configure...</property>
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
@@ -2083,7 +2081,6 @@ updates available for you.</property>
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
                                             <property name="xalign">1</property>
-                                            <!-- check_accelerators: not in same tab as button_wireless_options -->
                                             <property name="label" translatable="yes">_Configuration URL</property>
                                             <property name="use_underline">True</property>
                                             <property name="mnemonic_widget">entry_proxy_url</property>
@@ -2162,7 +2159,6 @@ updates available for you.</property>
                                             <property name="visible">True</property>
                                             <property name="can_focus">False</property>
                                             <property name="xalign">1</property>
-                                            <!-- check_accelerators: not in same tab as button_wireless_options -->
                                             <property name="label" translatable="yes">_Socks Host</property>
                                             <property name="use_underline">True</property>
                                             <property name="mnemonic_widget">entry_proxy_socks</property>
diff --git a/tests/accelerators/check_accelerators.py b/tests/accelerators/check_accelerators.py
index dff3c3f..e4a5f56 100755
--- a/tests/accelerators/check_accelerators.py
+++ b/tests/accelerators/check_accelerators.py
@@ -21,6 +21,7 @@ import sys
 import argparse
 import re
 import os.path
+import copy
 
 try:
     from lxml import etree
@@ -30,55 +31,136 @@ except ImportError:
 
 accel_re = re.compile(r'_(?P<accel>.)')
 
+success = True
+def add_check_accel(glade_filename, accels, label):
+    """Check whether an accelerator conflicts with existing accelerators.
+       and add it to the current accelerator context.
+    """
+    global success
+
+    match = accel_re.search(label.text)
+    if match:
+        accel = match.group('accel').lower()
+        if accel in accels:
+            # Check for an exception comment
+            prev = label.getprevious()
+            if (prev is not None) and (prev.tag == etree.Comment) and \
+                    prev.text.strip().startswith('check_accelerators:'):
+                return
+
+            print("Accelerator collision for key %s in %s\n    line %d: %s\n    line %d: %s" %\
+                    (accel, os.path.normpath(glade_filename),
+                        accels[accel].sourceline, accels[accel].text,
+                        label.sourceline, label.text))
+            success = False
+        else:
+            accels[accel] = label
+
+def combine_accels(glade_filename, list_a, list_b):
+    if not list_a:
+        return list_b
+    if not list_b:
+        return list_a
+
+    newlist = []
+    for accels_a in list_a:
+        for accels_b in list_b:
+            new_accels = copy.copy(accels_a)
+            for accel in accels_b.keys():
+                add_check_accel(glade_filename, new_accels, accels_b[accel])
+            newlist.append(new_accels)
+    return newlist
+
+# GtkNotebook widgets define several child widgets, not all of which are active
+# at the same time. To further complicate things, an object can have more than
+# one GtkNotebook child, and a GtkNotebook and have GtkNotebook children.
+#
+# To handle this, GtkNotebook objects are processed separately.
+# process_notebook returns a list of possible accelerator lists, and each of
+# these is compared against the list of accelerators returned for the object's
+# other GtkNotebook children.
+
+def process_object(glade_filename, interface_object):
+    """Process keyboard shortcuts for a given glade object.
+
+       The return value from this function is a list of accelerator
+       dictionaries, with each consiting of accelerator shortcut characters
+       as keys and the corresponding <object> Element objects as values. Each
+       dictionary represents a set of accelerators that could be active at any
+       given time.
+    """
+    # Start with an empty context for things that are always active
+    accels = [{}]
+
+    # Add everything that isn't a child of a GtkNotebook
+    for label in interface_object.xpath(".//property[@name='label' and ../property[@name='use_underline']/text() = 'True' and not(ancestor::object[@class='GtkNotebook'])]"):
+        add_check_accel(glade_filename, accels[0], label)
+
+    # For each GtkNotebook tab that is not a child of another notebook,
+    # add the tab to the top-level context
+    for notebook_label in interface_object.xpath(".//object[@class='GtkNotebook' and not(ancestor::object[@class='GtkNotebook'])]/child[@type='tab']//property[@name='label' and ../property[@name='use_underline']/text() = 'True']"):
+        add_check_accel(glade_filename, accels[0], notebook_label)
+
+    # Now process each non-tab object in each Gtknotebook that is not a child
+    # of another notebook. For each Gtk notebook, each non-tab child represents
+    # a separate potentially-active context. Since each list returned by
+    # process_object for a GtkNotebook child is independent of each other
+    # GtkNotebook child, we can just combine all of them into a single list.
+    # For example, say there's a notebook with two panes. The first pane
+    # contains another notebook with two panes. Let's call the main pane
+    # A, and the panes in the child GtkNotebook A_1 and A_2. A contains an
+    # accelerator for 'a', A_1 contains accelerators for 'b' and 'c', and A_2
+    # contains accelerators for 'b' and 'c'. The list returned would look like:
+    #   [{'a': label1, 'b': label2, 'c': label3},
+    #    {'a': label1, 'b': label4, 'c': label5}]
+    # Then when we process the other pane in the Notebook that contains A
+    # (let's call it B), we find acclerators for 'a' and 'b':
+    #   [{'a': label6, 'b': label7}].
+    # None of these dictionaries are active at the same time. Because
+    # process_object on A combined the accelerators that are in the top-level
+    # of A with each of the accelerators in the Notebook children of A, we can
+    # treat A as if it were actually two panes at the same-level of B and just
+    # create a list of three dictionaries for the whole notebook.
+    #
+    # A deepcopy of each object is taken so that the object can be treated as a
+    # separate XML document so that the ancestor axis stuff works.
+
+    for notebook in interface_object.xpath(".//object[@class='GtkNotebook' and not(ancestor::object[@class='GtkNotebook'])]"):
+        # Create the list of dictionaries for the notebook
+        notebook_list = []
+        for child in notebook.xpath("./child[not(@type='tab')]"):
+            notebook_list.extend(process_object(glade_filename, copy.deepcopy(child)))
+
+        # Now combine this with our list of accelerators
+        accels = combine_accels(glade_filename, accels, notebook_list)
+
+    return accels
+
 def check_glade(glade_filename):
-    success = True
     with open(glade_filename) as glade_file:
         # Parse the XML
         glade_tree = etree.parse(glade_file)
 
-        # Assume that each top-level object is a separate context
+        # Treat each top-level object as a separate context
         for interface_object in glade_tree.xpath("/interface/object"):
-            accels = {}
-
-            # Select the label properties of objects whose use_underline
-            # property is true.
-            # NB: This is why we can't use python's builtin etree
-            for label in interface_object.xpath(".//property[@name='label' and ../property[@name='use_underline']/text() = 'True']"):
-                match = accel_re.search(label.text)
-                if match:
-                    accel = match.group('accel').lower()
-                    if accel in accels:
-                        # Check for an exception comment
-                        prev = label.getprevious()
-                        if (prev is not None) and (prev.tag == etree.Comment) and \
-                                prev.text.strip().startswith('check_accelerators:'):
-                            continue
-
-                        print("Accelerator collision for key %s in %s:%s\n    line %d: %s\n    line %d: %s" %\
-                                (accel, os.path.normpath(glade_filename), interface_object.attrib['id'],
-                                    accels[accel].sourceline, accels[accel].text, 
-                                    label.sourceline, label.text))
-                        success = False
-                    else:
-                        accels[accel] = label
-    return success
-
-def main(argv=sys.argv):
+            process_object(glade_filename, interface_object)
+
+def main(argv=None):
+    if argv is None:
+        argv = sys.argv
+
     parser = argparse.ArgumentParser("Check for duplicated accelerators")
     parser.add_argument("glade_files", nargs="+", metavar="GLADE-FILE",
             help='The glade file to check')
     args = parser.parse_args(args=argv)
 
-    success = True
     for glade_file in args.glade_files:
-        if not check_glade(glade_file):
-            success = False
-    return success
+        check_glade(glade_file)
 
 if __name__ == "__main__":
-    result = main(sys.argv[1:])
+    main(sys.argv[1:])
 
-    if result:
+    if success:
         sys.exit(0)
     else:
         sys.exit(1)
-- 
1.8.3.1



More information about the anaconda-patches mailing list