[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