2015-03-23 11:36:12 +00:00
|
|
|
# Abstract base classes for GUI classes
|
|
|
|
#
|
|
|
|
# Copyright (C) 2014 Red Hat, Inc.
|
|
|
|
#
|
|
|
|
# This copyrighted material is made available to anyone wishing to use,
|
|
|
|
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
|
|
# the GNU General Public License v.2, or (at your option) any later version.
|
|
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
|
|
# Public License for more details. You should have received a copy of the
|
|
|
|
# GNU General Public License along with this program; if not, write to the
|
|
|
|
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
|
|
# source code or documentation are not subject to the GNU General Public
|
|
|
|
# License and may only be used or replicated with the express permission of
|
|
|
|
# Red Hat, Inc.
|
|
|
|
#
|
|
|
|
# Red Hat Author(s): David Shea <dshea@redhat.com>
|
|
|
|
#
|
|
|
|
|
|
|
|
# This file contains abstract base classes that are specific to GUI
|
|
|
|
# functionality. See also pyanaconda.ui.helpers.
|
|
|
|
|
|
|
|
from abc import ABCMeta, abstractproperty, abstractmethod
|
2016-04-10 04:00:00 +00:00
|
|
|
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "3.0")
|
|
|
|
|
2015-03-23 11:36:12 +00:00
|
|
|
from gi.repository import Gtk
|
|
|
|
|
|
|
|
from pyanaconda.ui.helpers import InputCheck, InputCheckHandler
|
2016-04-10 04:00:00 +00:00
|
|
|
from pyanaconda.ui.gui.utils import timed_action
|
|
|
|
|
|
|
|
class GUIInputCheck(InputCheck):
|
|
|
|
""" Add timer awareness to an InputCheck.
|
|
|
|
|
|
|
|
Add a delay before running the validation function so that the
|
|
|
|
function is not run for every keystroke. Run any pending actions
|
|
|
|
before returning a status.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, parent, input_obj, run_check, data=None):
|
|
|
|
InputCheck.__init__(self, parent, input_obj, run_check, data)
|
|
|
|
|
|
|
|
# Add the timer here instead of decorating a method so that a new
|
|
|
|
# TimedAction is created for every instance
|
|
|
|
self.update_check_status = timed_action(busy_cursor=False)(self.update_check_status)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def check_status(self):
|
|
|
|
if self.update_check_status.timer_active:
|
|
|
|
# The timer is hooked up to update_check_status, which takes no arguments.
|
|
|
|
# Since the timed_action wrapper was made around the bound method of a
|
|
|
|
# GUIInputCheck instance and not the function of a GUIInputCheck class,
|
|
|
|
# self is already applied and update_check_status is just a regular TimedAction
|
|
|
|
# object, not a curried function around the object.
|
|
|
|
self.update_check_status.run_now()
|
|
|
|
|
|
|
|
return super(GUIInputCheck, self).check_status
|
2015-03-23 11:36:12 +00:00
|
|
|
|
|
|
|
# Inherit abstract methods from InputCheckHandler
|
|
|
|
# pylint: disable=abstract-method
|
2016-04-10 04:00:00 +00:00
|
|
|
class GUIInputCheckHandler(InputCheckHandler, metaclass=ABCMeta):
|
2015-03-23 11:36:12 +00:00
|
|
|
"""Provide InputCheckHandler functionality for Gtk input screens.
|
|
|
|
|
|
|
|
This class assumes that all input objects are of type GtkEditable and
|
|
|
|
attaches InputCheck.update_check_status to the changed signal.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def _update_check_status(self, editable, inputcheck):
|
|
|
|
inputcheck.update_check_status()
|
|
|
|
|
|
|
|
def get_input(self, input_obj):
|
|
|
|
return input_obj.get_text()
|
|
|
|
|
|
|
|
def add_check(self, input_obj, run_check, data=None):
|
2016-04-10 04:00:00 +00:00
|
|
|
# Use a GUIInputCheck to run the validation in a GLib timer
|
|
|
|
checkRef = GUIInputCheck(self, input_obj, run_check, data)
|
|
|
|
|
|
|
|
# Start a new timer on each keystroke
|
2015-03-23 11:36:12 +00:00
|
|
|
input_obj.connect_after("changed", self._update_check_status, checkRef)
|
2016-04-10 04:00:00 +00:00
|
|
|
|
|
|
|
# Add the InputCheck to the parent class's list of checks
|
|
|
|
self._check_list.append(checkRef)
|
|
|
|
|
2015-03-23 11:36:12 +00:00
|
|
|
return checkRef
|
|
|
|
|
2016-04-10 04:00:00 +00:00
|
|
|
class GUIDialogInputCheckHandler(GUIInputCheckHandler, metaclass=ABCMeta):
|
2015-03-23 11:36:12 +00:00
|
|
|
"""Provide InputCheckHandler functionality for Gtk dialogs.
|
|
|
|
|
2016-04-10 04:00:00 +00:00
|
|
|
If an OK button is provided in the constructor, this class will
|
|
|
|
handle setting the sensitivity of the button to match the input
|
|
|
|
check result. A method on_ok_clicked is provided to determine whether
|
|
|
|
the dialog can be exited, similar to on_back_clicked for spokes.
|
|
|
|
|
|
|
|
It's not possible (or at least not easy) to prent a GtkDialog from
|
|
|
|
returning a response, so the caller of gtk_dialog_run needs to check
|
|
|
|
whether the input is valid and decide based on that whether to destroy
|
|
|
|
the dialog or call gtk_dialog_run again.
|
2015-03-23 11:36:12 +00:00
|
|
|
"""
|
|
|
|
|
2016-04-10 04:00:00 +00:00
|
|
|
def __init__(self, ok_button=None):
|
|
|
|
GUIInputCheckHandler.__init__(self)
|
|
|
|
self._ok_button = ok_button
|
|
|
|
|
|
|
|
def _update_check_status(self, editable, inputcheck):
|
|
|
|
# If an OK button was provided, set it to sensitive on any change in
|
|
|
|
# input. This way if a user changes invalid input to valid, they can
|
|
|
|
# immediately leave the dialog. This also means that there will be a
|
|
|
|
# period in which the user is not prented from leaving with empty input,
|
|
|
|
# and this condition needs to be checked.
|
|
|
|
if self._ok_button:
|
|
|
|
self._ok_button.set_sensitive(True)
|
|
|
|
|
|
|
|
return super(GUIDialogInputCheckHandler, self)._update_check_status(editable, inputcheck)
|
2015-03-23 11:36:12 +00:00
|
|
|
|
|
|
|
def set_status(self, inputcheck):
|
|
|
|
if inputcheck.check_status in (InputCheck.CHECK_OK, InputCheck.CHECK_SILENT):
|
|
|
|
inputcheck.input_obj.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
|
|
|
inputcheck.input_obj.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, "")
|
|
|
|
else:
|
|
|
|
inputcheck.input_obj.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
|
|
|
"dialog-error")
|
|
|
|
inputcheck.input_obj.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY,
|
|
|
|
inputcheck.check_status)
|
|
|
|
|
2016-04-10 04:00:00 +00:00
|
|
|
# Update the ok button sensitivity based on the check status.
|
|
|
|
# If the result is CHECK_OK, set_sensitive(True) still needs to be
|
|
|
|
# called, even though the changed handler above also makes the button
|
|
|
|
# sensitive. A direct call to update_check_status may have bypassed the
|
|
|
|
# changed signal.
|
|
|
|
if self._ok_button:
|
|
|
|
self._ok_button.set_sensitive(inputcheck.check_status == InputCheck.CHECK_OK)
|
|
|
|
|
|
|
|
def on_ok_clicked(self):
|
|
|
|
"""Return whether the input validation checks allow the dialog to be exited.
|
|
|
|
|
|
|
|
Unlike GUISpokeInputCheckHandler.on_back_clicked, it is not expected that
|
|
|
|
subclasses will implement this method.
|
|
|
|
"""
|
|
|
|
failed_check = next(self.failed_checks, None)
|
|
|
|
|
|
|
|
if failed_check:
|
|
|
|
failed_check.input_obj.grab_focus()
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
|
|
|
class GUISpokeInputCheckHandler(GUIInputCheckHandler, metaclass=ABCMeta):
|
2015-03-23 11:36:12 +00:00
|
|
|
"""Provide InputCheckHandler functionality for graphical spokes.
|
|
|
|
|
|
|
|
This class implements set_status to set a message in the warning area of
|
|
|
|
the spoke window and provides an implementation of on_back_clicked to
|
|
|
|
prevent the user from exiting a spoke with bad input.
|
|
|
|
"""
|
|
|
|
|
2016-04-10 04:00:00 +00:00
|
|
|
def __init__(self):
|
|
|
|
GUIInputCheckHandler.__init__(self)
|
|
|
|
|
|
|
|
# Store the previous status to avoid setting the info bar to the same
|
|
|
|
# message multiple times
|
|
|
|
self._prev_status = None
|
2015-03-23 11:36:12 +00:00
|
|
|
|
|
|
|
def set_status(self, inputcheck):
|
|
|
|
"""Update the warning with the input validation error from the first
|
|
|
|
error message.
|
|
|
|
"""
|
|
|
|
failed_check = next(self.failed_checks_with_message, None)
|
|
|
|
|
2016-04-10 04:00:00 +00:00
|
|
|
if not failed_check:
|
|
|
|
self.clear_info()
|
|
|
|
self._prev_status = None
|
|
|
|
elif failed_check.check_status != self._prev_status:
|
|
|
|
self._prev_status = failed_check.check_status
|
|
|
|
self.clear_info()
|
2015-03-23 11:36:12 +00:00
|
|
|
self.set_warning(failed_check.check_status)
|
|
|
|
|
|
|
|
# Implemented by GUIObject
|
|
|
|
@abstractmethod
|
|
|
|
def clear_info(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Implemented by GUIObject
|
|
|
|
@abstractmethod
|
|
|
|
def set_warning(self, msg):
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Implemented by GUIObject
|
|
|
|
@abstractproperty
|
|
|
|
def window(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def on_back_clicked(self, window):
|
|
|
|
"""Check whether the input validation checks allow the spoke to be exited.
|
|
|
|
|
|
|
|
Unlike NormalSpoke.on_back_clicked, this function returns a boolean value.
|
|
|
|
Classes implementing this class should run GUISpokeInputCheckHandler.on_back_clicked,
|
|
|
|
and if it succeeded, run NormalSpoke.on_back_clicked.
|
|
|
|
"""
|
|
|
|
failed_check = next(self.failed_checks, None)
|
|
|
|
|
|
|
|
if failed_check:
|
|
|
|
failed_check.input_obj.grab_focus()
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|