summaryrefslogtreecommitdiffstats
path: root/pyanaconda/ui
diff options
context:
space:
mode:
authorVratislav Podzimek <vpodzime@redhat.com>2012-08-13 08:47:58 +0200
committerVratislav Podzimek <vpodzime@redhat.com>2013-02-06 11:01:31 +0100
commit6b919e5046fe307b6d93dbb868f21f4bb86d8941 (patch)
tree05d910647581e5dff0f6acf9d104e14d947e210c /pyanaconda/ui
parent1b589b572d71ed49458b0db2738719face25fa18 (diff)
downloadanaconda-6b919e5046fe307b6d93dbb868f21f4bb86d8941.tar.gz
anaconda-6b919e5046fe307b6d93dbb868f21f4bb86d8941.tar.xz
anaconda-6b919e5046fe307b6d93dbb868f21f4bb86d8941.zip
Add entries with completions to the comboboxes (DatetimeSpoke)
Some regions have so many timezones (cities), that the combobox doesn't fit in the screen and is hard to search through. This patch modifies the city and region comboboxes to have text entry, that can be used to choose timezone. There are five ways of choosing timezone: 1) click on the map 2) popup comboboxes and choose region and city 3) type to the region and city comboboxes and choose from the completions 4) type a whole region or city name to the combobox and hit ENTER 5) type a whole region or city name to the combobox and click somewhere else or hit TAB
Diffstat (limited to 'pyanaconda/ui')
-rw-r--r--pyanaconda/ui/gui/spokes/datetime_spoke.glade152
-rw-r--r--pyanaconda/ui/gui/spokes/datetime_spoke.py87
2 files changed, 155 insertions, 84 deletions
diff --git a/pyanaconda/ui/gui/spokes/datetime_spoke.glade b/pyanaconda/ui/gui/spokes/datetime_spoke.glade
index d2b9825f3..393f88661 100644
--- a/pyanaconda/ui/gui/spokes/datetime_spoke.glade
+++ b/pyanaconda/ui/gui/spokes/datetime_spoke.glade
@@ -2,28 +2,36 @@
<interface>
<!-- interface-requires gtk+ 3.0 -->
<!-- interface-requires AnacondaWidgets 1.0 -->
+ <object class="GtkImage" id="addImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">list-add-symbolic</property>
+ </object>
+ <object class="GtkImage" id="configImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">system-run-symbolic</property>
+ </object>
+ <object class="GtkTreeModelFilter" id="daysFilter">
+ <property name="child_model">days</property>
+ </object>
<object class="GtkListStore" id="cities">
<columns>
<!-- column-name name -->
<column type="gchararray"/>
</columns>
</object>
+ <object class="GtkEntryCompletion" id="cityCompletion">
+ <property name="model">citiesSort</property>
+ <property name="text_column">0</property>
+ <signal name="match-selected" handler="on_completion_match_selected" object="cityCombobox" swapped="no"/>
+ </object>
<object class="GtkTreeModelFilter" id="citiesFilter">
<property name="child_model">cities</property>
</object>
<object class="GtkTreeModelSort" id="citiesSort">
<property name="model">citiesFilter</property>
</object>
- <object class="GtkImage" id="configImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">system-run-symbolic</property>
- </object>
- <object class="GtkImage" id="addImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">list-add-symbolic</property>
- </object>
<object class="AnacondaSpokeWindow" id="datetimeWindow">
<property name="startup_id">filler</property>
<property name="can_focus">False</property>
@@ -35,18 +43,9 @@
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
- <child internal-child="nav_box">
- <object class="GtkEventBox" id="AnacondaSpokeWindow-nav_box1">
- <child internal-child="nav_area">
- <object class="GtkGrid" id="AnacondaSpokeWindow-nav_area1">
- <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="n_rows">2</property>
- <property name="n_columns">2</property>
- </object>
- </child>
+ <child internal-child="nav_area">
+ <object class="GtkGrid" id="AnacondaSpokeWindow-nav_area1">
+ <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -100,12 +99,19 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">regions</property>
- <signal name="changed" handler="on_region_changed" swapped="no"/>
- <child>
- <object class="GtkCellRendererText" id="regionsComboRenderer"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
+ <property name="has_entry">True</property>
+ <property name="entry_text_column">0</property>
+ <signal name="changed" handler="on_region_changed" object="regionsEntry" swapped="no"/>
+ <child internal-child="entry">
+ <object class="GtkEntry" id="regionsEntry">
+ <property name="can_focus">True</property>
+ <property name="events">GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="width_chars">20</property>
+ <property name="completion">regionCompletion</property>
+ <signal name="key-release-event" handler="on_city_region_key_released" swapped="no"/>
+ <signal name="activate" handler="on_city_region_text_entry_activated" swapped="no"/>
+ <signal name="focus-out-event" handler="on_entry_left" swapped="no"/>
+ </object>
</child>
</object>
<packing>
@@ -135,12 +141,19 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">citiesSort</property>
- <signal name="changed" handler="on_city_changed" swapped="no"/>
- <child>
- <object class="GtkCellRendererText" id="citiesComboRenderer"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
+ <property name="has_entry">True</property>
+ <property name="entry_text_column">0</property>
+ <signal name="changed" handler="on_city_changed" object="citiesEntry" swapped="no"/>
+ <child internal-child="entry">
+ <object class="GtkEntry" id="citiesEntry">
+ <property name="can_focus">True</property>
+ <property name="events">GDK_KEY_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="width_chars">20</property>
+ <property name="completion">cityCompletion</property>
+ <signal name="key-release-event" handler="on_city_region_key_released" swapped="no"/>
+ <signal name="activate" handler="on_city_region_text_entry_activated" swapped="no"/>
+ <signal name="focus-out-event" handler="on_entry_left" swapped="no"/>
+ </object>
</child>
</object>
<packing>
@@ -182,7 +195,6 @@
<property name="can_focus">True</property>
<property name="halign">end</property>
<property name="valign">start</property>
- <property name="use_action_appearance">False</property>
<signal name="notify::active" handler="on_ntp_switched" swapped="no"/>
</object>
<packing>
@@ -197,7 +209,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="image">configImage</property>
<signal name="clicked" handler="on_ntp_config_clicked" swapped="no"/>
</object>
@@ -309,7 +320,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="image">upImage</property>
<signal name="clicked" handler="on_up_hours_clicked" swapped="no"/>
</object>
@@ -325,7 +335,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="image">downImage</property>
<signal name="clicked" handler="on_down_hours_clicked" swapped="no"/>
</object>
@@ -341,7 +350,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="image">upImage1</property>
<signal name="clicked" handler="on_up_ampm_clicked" swapped="no"/>
</object>
@@ -357,7 +365,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="image">upImage2</property>
<signal name="clicked" handler="on_up_minutes_clicked" swapped="no"/>
</object>
@@ -373,7 +380,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="image">downImage1</property>
<signal name="clicked" handler="on_down_ampm_clicked" swapped="no"/>
</object>
@@ -389,7 +395,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="image">downImage2</property>
<signal name="clicked" handler="on_down_minutes_clicked" swapped="no"/>
</object>
@@ -623,8 +628,37 @@
<column type="guint"/>
</columns>
</object>
- <object class="GtkTreeModelFilter" id="daysFilter">
- <property name="child_model">days</property>
+ <object class="GtkImage" id="downImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-down-symbolic</property>
+ </object>
+ <object class="GtkImage" id="downImage1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-down-symbolic</property>
+ </object>
+ <object class="GtkImage" id="downImage2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-down-symbolic</property>
+ </object>
+ <object class="GtkListStore" id="regions">
+ <columns>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkEntryCompletion" id="regionCompletion">
+ <property name="model">regions</property>
+ <property name="text_column">0</property>
+ <signal name="match-selected" handler="on_completion_match_selected" object="regionCombobox" swapped="no"/>
+ </object>
+ <object class="GtkListStore" id="months">
+ <columns>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
</object>
<object class="GtkDialog" id="ntpConfigDialog">
<property name="can_focus">False</property>
@@ -694,7 +728,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="image">addImage</property>
<signal name="clicked" handler="on_add_clicked" swapped="no"/>
</object>
@@ -815,7 +848,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
@@ -830,7 +862,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
@@ -854,33 +885,6 @@
<action-widget response="1">okButton</action-widget>
</action-widgets>
</object>
- <object class="GtkImage" id="downImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">go-down-symbolic</property>
- </object>
- <object class="GtkImage" id="downImage1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">go-down-symbolic</property>
- </object>
- <object class="GtkImage" id="downImage2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">go-down-symbolic</property>
- </object>
- <object class="GtkListStore" id="months">
- <columns>
- <!-- column-name name -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkListStore" id="regions">
- <columns>
- <!-- column-name name -->
- <column type="gchararray"/>
- </columns>
- </object>
<object class="GtkListStore" id="serversStore">
<columns>
<!-- column-name hostname -->
diff --git a/pyanaconda/ui/gui/spokes/datetime_spoke.py b/pyanaconda/ui/gui/spokes/datetime_spoke.py
index b9b59bb86..5746ce75b 100644
--- a/pyanaconda/ui/gui/spokes/datetime_spoke.py
+++ b/pyanaconda/ui/gui/spokes/datetime_spoke.py
@@ -27,7 +27,7 @@ import logging
log = logging.getLogger("anaconda")
# pylint: disable-msg=E0611
-from gi.repository import AnacondaWidgets, GLib, Gtk
+from gi.repository import AnacondaWidgets, GLib, Gtk, Gdk
from pyanaconda.ui.gui import GUIObject
from pyanaconda.ui.gui.spokes import NormalSpoke
@@ -285,6 +285,7 @@ class DatetimeSpoke(NormalSpoke):
"upImage", "upImage1", "upImage2", "downImage",
"downImage1", "downImage2", "downImage3", "configImage",
"citiesFilter", "daysFilter", "citiesSort", "regionsSort",
+ "cityCompletion", "regionCompletion",
]
mainWidgetName = "datetimeWindow"
@@ -328,6 +329,10 @@ class DatetimeSpoke(NormalSpoke):
for city in self._regions_zones[region]:
self.add_to_store(self._citiesStore, city)
+ # we need to know it the new value is the same as previous or not
+ self._old_region = None
+ self._old_city = None
+
self._regionCombo = self.builder.get_object("regionCombobox")
self._cityCombo = self.builder.get_object("cityCombobox")
self._monthCombo = self.builder.get_object("monthCombobox")
@@ -428,8 +433,8 @@ class DatetimeSpoke(NormalSpoke):
@property
def mandatory(self):
- return True
-
+ return True
+
def refresh(self):
#update the displayed time
self._update_datetime_timer_id = GLib.timeout_add_seconds(1,
@@ -663,6 +668,11 @@ class DatetimeSpoke(NormalSpoke):
return model[itr][0]
+ def _restore_old_city_region(self):
+ """Restore stored "old" (or last valid) values."""
+
+ self._set_combo_selection(self._regionCombo, self._old_region)
+ self._set_combo_selection(self._cityCombo, self._old_city)
def on_up_hours_clicked(self, *args):
self._stop_and_maybe_start_time_updating()
@@ -729,27 +739,49 @@ class DatetimeSpoke(NormalSpoke):
else:
self._amPmLabel.set_text("AM")
- def on_region_changed(self, *args):
- self._citiesFilter.refilter()
+ def on_region_changed(self, combo, *args):
+ """
+ @see: on_city_changed
+
+ """
- # Attempt to set the city to the first one available in this newly
- # selected region.
region = self._get_active_region()
- if not region:
+ if not region or region == self._old_region:
+ # region entry being edited or old_value chosen, no action needed
+ # @see: on_city_changed
return
+ self._citiesFilter.refilter()
+
+ # Set the city to the first one available in this newly selected region.
zone = self._regions_zones[region]
firstCity = sorted(list(zone))[0]
self._set_combo_selection(self._cityCombo, firstCity)
- self._cityCombo.emit("changed")
+ self._old_region = region
+ self._old_city = firstCity
+
+ def on_city_changed(self, combo, *args):
+ """
+ ComboBox emits ::changed signal not only when something is selected, but
+ also when its entry's text is changed. We need to distinguish between
+ those two cases ('London' typed in the entry => no action until ENTER is
+ hit etc.; 'London' chosen in the expanded combobox => update timezone
+ map and do all necessary actions). Fortunately when entry is being
+ edited, self._get_active_city returns None.
+
+ """
- def on_city_changed(self, *args):
timezone = None
region = self._get_active_region()
city = self._get_active_city()
+ if not region or not city or (region == self._old_region and
+ city == self._old_city):
+ # entry being edited or no change, no actions needed
+ return
+
if city and region:
timezone = region + "/" + city
@@ -765,6 +797,41 @@ class DatetimeSpoke(NormalSpoke):
elif timezone and (self._tzmap.get_timezone() != timezone):
self._tzmap.set_timezone(timezone)
+ # update "old" values
+ self._old_city = city
+ print "Storing %s" % city
+
+ def on_entry_left(self, entry, *args):
+ # user clicked somewhere else or hit TAB => finished editing
+ entry.emit("activate")
+
+ def on_city_region_key_released(self, entry, event, *args):
+ if event.type == Gdk.EventType.KEY_RELEASE and \
+ event.keyval == Gdk.KEY_Escape:
+ # editing canceled
+ self._restore_old_city_region()
+
+ def on_completion_match_selected(self, combo, model, itr):
+ item = None
+ if model and itr:
+ item = model[itr][0]
+ if item:
+ self._set_combo_selection(combo, item)
+
+ def on_city_region_text_entry_activated(self, entry):
+ combo = entry.get_parent()
+ model = combo.get_model()
+ itr = model.get_iter_first()
+ entry_text = entry.get_text().lower()
+
+ for row in model:
+ if entry_text == row[0].lower():
+ self._set_combo_selection(combo, row[0])
+ return
+
+ # non-matching value entered, reset to old values
+ self._restore_old_city_region()
+
def on_month_changed(self, *args):
self._stop_and_maybe_start_time_updating(interval=5)
self._daysFilter.refilter()