Example #1
0
class ControlPanel(Gtk.Window):
    __gtype_name__ = 'SugarControlPanel'

    def __init__(self):
        Gtk.Window.__init__(self)

        self._calculate_max_columns()
        self.set_border_width(style.LINE_WIDTH)
        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
        self.set_decorated(False)
        self.set_resizable(False)
        self.set_modal(True)

        self.set_can_focus(True)
        self.connect('key-press-event', self.__key_press_event_cb)

        self._toolbar = None
        self._canvas = None
        self._table = None
        self._scrolledwindow = None
        self._separator = None
        self._section_view = None
        self._section_toolbar = None
        self._main_toolbar = None

        self._vbox = Gtk.VBox()
        self._hbox = Gtk.HBox()
        self._vbox.pack_start(self._hbox, True, True, 0)
        self._hbox.show()

        self._main_view = Gtk.EventBox()
        self._hbox.pack_start(self._main_view, True, True, 0)
        self._main_view.modify_bg(Gtk.StateType.NORMAL,
                                  style.COLOR_BLACK.get_gdk_color())
        self._main_view.show()

        self.add(self._vbox)
        self._vbox.show()

        self.connect('realize', self.__realize_cb)

        self._options = self._get_options()
        self._current_option = None
        self._setup_main()
        self._setup_section()
        self._show_main_view()
        Gdk.Screen.get_default().connect(
            'size-changed', self.__size_changed_cb)

    def __realize_cb(self, widget):
        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
        self.get_window().set_accept_focus(True)
        # the modal windows counter is updated to disable hot keys - SL#4601
        shell.get_model().push_modal()

    def __size_changed_cb(self, event):
        self._calculate_max_columns()

    def grab_focus(self):
        # overwrite grab focus in order to grab focus on the view
        self._main_view.get_child().grab_focus()

    def _calculate_max_columns(self):
        self._max_columns = int(0.285 * (float(Gdk.Screen.width()) /
                                         style.GRID_CELL_SIZE - 3))
        offset = style.GRID_CELL_SIZE
        width = Gdk.Screen.width() - offset * 2
        height = Gdk.Screen.height() - offset * 2
        self.set_size_request(width, height)
        if hasattr(self, '_table'):
            for child in self._table.get_children():
                child.destroy()
            self._setup_options()

    def _set_canvas(self, canvas):
        if self._canvas:
            self._main_view.remove(self._canvas)
        if canvas:
            self._main_view.add(canvas)
        self._canvas = canvas

    def _set_toolbar(self, toolbar):
        if self._toolbar:
            self._vbox.remove(self._toolbar)
        self._vbox.pack_start(toolbar, False, False, 0)
        self._vbox.reorder_child(toolbar, 0)
        self._toolbar = toolbar
        if not self._separator:
            self._separator = Gtk.HSeparator()
            self._vbox.pack_start(self._separator, False, False, 0)
            self._vbox.reorder_child(self._separator, 1)
            self._separator.show()

    def _setup_main(self):
        self._main_toolbar = MainToolbar()

        self._table = Gtk.Table()
        self._table.set_col_spacings(style.GRID_CELL_SIZE)
        self._table.set_row_spacings(style.GRID_CELL_SIZE)
        self._table.set_border_width(style.GRID_CELL_SIZE)

        self._scrolledwindow = Gtk.ScrolledWindow()
        self._scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC,
                                        Gtk.PolicyType.AUTOMATIC)
        self._scrolledwindow.add_with_viewport(self._table)
        child = self._scrolledwindow.get_child()
        child.modify_bg(
            Gtk.StateType.NORMAL, style.COLOR_BLACK.get_gdk_color())

        self._setup_options()
        self._main_toolbar.connect('stop-clicked',
                                   self.__stop_clicked_cb)
        self._main_toolbar.connect('search-changed',
                                   self.__search_changed_cb)

    def _setup_options(self):
        # If the screen width only supports two columns, start
        # placing from the second row.
        if self._max_columns == 2:
            row = 1
            column = 0
        else:
            # About Me and About my computer are hardcoded below to use the
            # first two slots so we need to leave them free.
            row = 0
            column = 2

        options = self._options.keys()
        options.sort()

        for option in options:
            sectionicon = _SectionIcon(icon_name=self._options[option]['icon'],
                                       title=self._options[option]['title'],
                                       xo_color=self._options[option]['color'],
                                       pixel_size=style.GRID_CELL_SIZE)
            sectionicon.connect('button_press_event',
                                self.__select_option_cb, option)
            sectionicon.show()

            if option == 'aboutme':
                self._table.attach(sectionicon, 0, 1, 0, 1)
            elif option == 'aboutcomputer':
                self._table.attach(sectionicon, 1, 2, 0, 1)
            else:
                self._table.attach(sectionicon,
                                   column, column + 1,
                                   row, row + 1)
                column += 1
                if column == self._max_columns:
                    column = 0
                    row += 1

            self._options[option]['button'] = sectionicon

    def _show_main_view(self):
        self._set_toolbar(self._main_toolbar)
        self._main_toolbar.show()
        self._set_canvas(self._scrolledwindow)
        self._main_view.modify_bg(Gtk.StateType.NORMAL,
                                  style.COLOR_BLACK.get_gdk_color())
        self._table.show()
        self._scrolledwindow.show()
        entry = self._main_toolbar.get_entry()
        entry.set_text('')
        entry.connect('icon-press', self.__clear_icon_pressed_cb)
        self.grab_focus()

    def __key_press_event_cb(self, window, event):
        entry = self._main_toolbar.get_entry()
        if not entry.has_focus():
            entry.grab_focus()
        return False

    def __clear_icon_pressed_cb(self, entry, icon_pos, event):
        self.grab_focus()

    def _update(self, query):
        for option in self._options:
            found = False
            for key in self._options[option]['keywords']:
                if query.lower() in key.lower():
                    self._options[option]['button'].set_sensitive(True)
                    found = True
                    break
            if not found:
                self._options[option]['button'].set_sensitive(False)

    def _setup_section(self):
        self._section_toolbar = SectionToolbar()
        self._section_toolbar.connect('cancel-clicked',
                                      self.__cancel_clicked_cb)
        self._section_toolbar.connect('accept-clicked',
                                      self.__accept_clicked_cb)

    def show_section_view(self, option):
        self._set_toolbar(self._section_toolbar)

        icon = self._section_toolbar.get_icon()
        icon.set_from_icon_name(self._options[option]['icon'],
                                Gtk.IconSize.LARGE_TOOLBAR)
        icon.props.xo_color = self._options[option]['color']
        title = self._section_toolbar.get_title()
        title.set_text(self._options[option]['title'])
        self._section_toolbar.show()

        self._current_option = option

        mod = __import__('.'.join(('cpsection', option, 'view')),
                         globals(), locals(), ['view'])
        view_class = getattr(mod, self._options[option]['view'], None)

        mod = __import__('.'.join(('cpsection', option, 'model')),
                         globals(), locals(), ['model'])
        model = ModelWrapper(mod)

        try:
            self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
            self._section_view = view_class(model,
                                            self._options[option]['alerts'])

            self._set_canvas(self._section_view)
            self._section_view.show()
        finally:
            self.get_window().set_cursor(None)

        self._section_view.connect('notify::is-valid',
                                   self.__valid_section_cb)
        self._section_view.connect('request-close',
                                   self.__close_request_cb)
        self._main_view.modify_bg(Gtk.StateType.NORMAL,
                                  style.COLOR_WHITE.get_gdk_color())

    def set_section_view_auto_close(self):
        """Automatically close the control panel if there is "nothing to do"
        """
        self._section_view.auto_close = True

    def _get_options(self):
        """Get the available option information from the extensions
        """
        options = {}

        path = os.path.join(config.ext_path, 'cpsection')
        folder = os.listdir(path)

        for item in folder:
            if os.path.isdir(os.path.join(path, item)) and \
                    os.path.exists(os.path.join(path, item, '__init__.py')):
                try:
                    mod = __import__('.'.join(('cpsection', item)),
                                     globals(), locals(), [item])
                    view_class = getattr(mod, 'CLASS', None)
                    if view_class is not None:
                        options[item] = {}
                        options[item]['alerts'] = []
                        options[item]['view'] = view_class
                        options[item]['icon'] = getattr(mod, 'ICON', item)
                        options[item]['title'] = getattr(mod, 'TITLE', item)
                        options[item]['color'] = getattr(mod, 'COLOR', None)
                        keywords = getattr(mod, 'KEYWORDS', [])
                        keywords.append(options[item]['title'].lower())
                        if item not in keywords:
                            keywords.append(item)
                        options[item]['keywords'] = keywords
                    else:
                        _logger.error('no CLASS attribute in %r', item)
                except Exception:
                    logging.exception('Exception while loading extension:')

        return options

    def __cancel_clicked_cb(self, widget):
        self._section_view.undo()
        self._options[self._current_option]['alerts'] = []
        self._section_toolbar.accept_button.set_sensitive(True)
        self._show_main_view()

    def __accept_clicked_cb(self, widget):
        if self._section_view.needs_restart:
            self._section_toolbar.accept_button.set_sensitive(False)
            self._section_toolbar.cancel_button.set_sensitive(False)
            alert = Alert()
            alert.props.title = _('Warning')
            alert.props.msg = _('Changes require restart')

            icon = Icon(icon_name='dialog-cancel')
            alert.add_button(Gtk.ResponseType.CANCEL,
                             _('Cancel changes'), icon)
            icon.show()

            if self._current_option != 'aboutme':
                icon = Icon(icon_name='dialog-ok')
                alert.add_button(Gtk.ResponseType.ACCEPT, _('Later'), icon)
                icon.show()

            icon = Icon(icon_name='system-restart')
            alert.add_button(Gtk.ResponseType.APPLY, _('Restart now'), icon)
            icon.show()

            self._vbox.pack_start(alert, False, False, 0)
            self._vbox.reorder_child(alert, 2)
            alert.connect('response', self.__response_cb)
            alert.show()
        else:
            self._show_main_view()

    def __response_cb(self, alert, response_id):
        self._vbox.remove(alert)
        self._section_toolbar.accept_button.set_sensitive(True)
        self._section_toolbar.cancel_button.set_sensitive(True)
        if response_id is Gtk.ResponseType.CANCEL:
            self._section_view.undo()
            self._section_view.setup()
            self._options[self._current_option]['alerts'] = []
        elif response_id is Gtk.ResponseType.ACCEPT:
            self._options[self._current_option]['alerts'] = \
                self._section_view.restart_alerts
            self._show_main_view()
        elif response_id is Gtk.ResponseType.APPLY:
            session_manager = get_session_manager()
            session_manager.logout()

    def __select_option_cb(self, button, event, option):
        self.show_section_view(option)

    def __search_changed_cb(self, maintoolbar, query):
        self._update(query)

    def __stop_clicked_cb(self, widget):
        shell.get_model().pop_modal()
        self.destroy()

    def __close_request_cb(self, widget, event=None):
        self.destroy()

    def __valid_section_cb(self, section_view, pspec):
        section_is_valid = section_view.props.is_valid
        self._section_toolbar.accept_button.set_sensitive(section_is_valid)
Example #2
0
class ControlPanel(gtk.Window):
    __gtype_name__ = 'SugarControlPanel'

    def __init__(self):
        gtk.Window.__init__(self)

        self._max_columns = int(0.285 * (float(gtk.gdk.screen_width()) /
            style.GRID_CELL_SIZE - 3))

        self.set_border_width(style.LINE_WIDTH)
        offset = style.GRID_CELL_SIZE
        width = gtk.gdk.screen_width() - offset * 2
        height = gtk.gdk.screen_height() - offset * 2
        self.set_size_request(width, height)
        self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
        self.set_decorated(False)
        self.set_resizable(False)
        self.set_modal(True)

        self._toolbar = None
        self._canvas = None
        self._table = None
        self._scrolledwindow = None
        self._separator = None
        self._section_view = None
        self._section_toolbar = None
        self._main_toolbar = None

        self._vbox = gtk.VBox()
        self._hbox = gtk.HBox()
        self._vbox.pack_start(self._hbox)
        self._hbox.show()

        self._main_view = gtk.EventBox()
        self._hbox.pack_start(self._main_view)
        self._main_view.modify_bg(gtk.STATE_NORMAL,
                                  style.COLOR_BLACK.get_gdk_color())
        self._main_view.show()

        self.add(self._vbox)
        self._vbox.show()

        self.connect('realize', self.__realize_cb)

        self._options = self._get_options()
        self._current_option = None
        self._setup_main()
        self._setup_section()
        self._show_main_view()

    def __realize_cb(self, widget):
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
        self.window.set_accept_focus(True)

    def _set_canvas(self, canvas):
        if self._canvas:
            self._main_view.remove(self._canvas)
        if canvas:
            self._main_view.add(canvas)
        self._canvas = canvas

    def _set_toolbar(self, toolbar):
        if self._toolbar:
            self._vbox.remove(self._toolbar)
        self._vbox.pack_start(toolbar, False)
        self._vbox.reorder_child(toolbar, 0)
        self._toolbar = toolbar
        if not self._separator:
            self._separator = gtk.HSeparator()
            self._vbox.pack_start(self._separator, False)
            self._vbox.reorder_child(self._separator, 1)
            self._separator.show()

    def _setup_main(self):
        self._main_toolbar = MainToolbar()

        self._table = gtk.Table()
        self._table.set_col_spacings(style.GRID_CELL_SIZE)
        self._table.set_row_spacings(style.GRID_CELL_SIZE)
        self._table.set_border_width(style.GRID_CELL_SIZE)

        self._scrolledwindow = gtk.ScrolledWindow()
        self._scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC,
                                        gtk.POLICY_AUTOMATIC)
        self._scrolledwindow.add_with_viewport(self._table)
        child = self._scrolledwindow.get_child()
        child.modify_bg(gtk.STATE_NORMAL, style.COLOR_BLACK.get_gdk_color())

        self._setup_options()
        self._main_toolbar.connect('stop-clicked',
                                   self.__stop_clicked_cb)
        self._main_toolbar.connect('search-changed',
                                   self.__search_changed_cb)

    def _setup_options(self):
        if not os.access(POWERD_FLAG_DIR, os.W_OK):
            del self._options['power']

        try:
            import xklavier
        except ImportError:
            del self._options['keyboard']

        # If the screen width only supports two columns, start
        # placing from the second row.
        if self._max_columns == 2:
            row = 1
            column = 0
        else:
            # About Me and About my computer are hardcoded below to use the
            # first two slots so we need to leave them free.
            row = 0
            column = 2

        options = self._options.keys()
        options.sort()

        for option in options:
            sectionicon = _SectionIcon(icon_name=self._options[option]['icon'],
                                       title=self._options[option]['title'],
                                       xo_color=self._options[option]['color'],
                                       pixel_size=style.GRID_CELL_SIZE)
            sectionicon.connect('button_press_event',
                               self.__select_option_cb, option)
            sectionicon.show()

            if option == 'aboutme':
                self._table.attach(sectionicon, 0, 1, 0, 1)
            elif option == 'aboutcomputer':
                self._table.attach(sectionicon, 1, 2, 0, 1)
            else:
                self._table.attach(sectionicon,
                                   column, column + 1,
                                   row, row + 1)
                column += 1
                if column == self._max_columns:
                    column = 0
                    row += 1

            self._options[option]['button'] = sectionicon

    def _show_main_view(self):
        self._set_toolbar(self._main_toolbar)
        self._main_toolbar.show()
        self._set_canvas(self._scrolledwindow)
        self._main_view.modify_bg(gtk.STATE_NORMAL,
                                  style.COLOR_BLACK.get_gdk_color())
        self._table.show()
        self._scrolledwindow.show()
        entry = self._main_toolbar.get_entry()
        entry.grab_focus()
        entry.set_text('')

    def _update(self, query):
        for option in self._options:
            found = False
            for key in self._options[option]['keywords']:
                if query.lower() in key.lower():
                    self._options[option]['button'].set_sensitive(True)
                    found = True
                    break
            if not found:
                self._options[option]['button'].set_sensitive(False)

    def _setup_section(self):
        self._section_toolbar = SectionToolbar()
        self._section_toolbar.connect('cancel-clicked',
                                     self.__cancel_clicked_cb)
        self._section_toolbar.connect('accept-clicked',
                                     self.__accept_clicked_cb)

    def show_section_view(self, option):
        self._set_toolbar(self._section_toolbar)

        icon = self._section_toolbar.get_icon()
        icon.set_from_icon_name(self._options[option]['icon'],
                                gtk.ICON_SIZE_LARGE_TOOLBAR)
        icon.props.xo_color = self._options[option]['color']
        title = self._section_toolbar.get_title()
        title.set_text(self._options[option]['title'])
        self._section_toolbar.show()

        self._current_option = option

        mod = __import__('.'.join(('cpsection', option, 'view')),
                         globals(), locals(), ['view'])
        view_class = getattr(mod, self._options[option]['view'], None)

        mod = __import__('.'.join(('cpsection', option, 'model')),
                         globals(), locals(), ['model'])
        model = ModelWrapper(mod)

        try:
            self.get_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
            self._section_view = view_class(model,
                                            self._options[option]['alerts'])

            self._set_canvas(self._section_view)
            self._section_view.show()
        finally:
            self.get_window().set_cursor(None)

        self._section_view.connect('notify::is-valid',
                                   self.__valid_section_cb)
        self._section_view.connect('request-close',
                                   self.__close_request_cb)
        self._main_view.modify_bg(gtk.STATE_NORMAL,
                                  style.COLOR_WHITE.get_gdk_color())

    def set_section_view_auto_close(self):
        """Automatically close the control panel if there is "nothing to do"
        """
        self._section_view.auto_close = True

    def _get_options(self):
        """Get the available option information from the extensions
        """
        options = {}

        path = os.path.join(config.ext_path, 'cpsection')
        folder = os.listdir(path)

        for item in folder:
            if os.path.isdir(os.path.join(path, item)) and \
                    os.path.exists(os.path.join(path, item, '__init__.py')):
                try:
                    mod = __import__('.'.join(('cpsection', item)),
                                     globals(), locals(), [item])
                    view_class = getattr(mod, 'CLASS', None)
                    if view_class is not None:
                        options[item] = {}
                        options[item]['alerts'] = []
                        options[item]['view'] = view_class
                        options[item]['icon'] = getattr(mod, 'ICON', item)
                        options[item]['title'] = getattr(mod, 'TITLE', item)
                        options[item]['color'] = getattr(mod, 'COLOR', None)
                        keywords = getattr(mod, 'KEYWORDS', [])
                        keywords.append(options[item]['title'].lower())
                        if item not in keywords:
                            keywords.append(item)
                        options[item]['keywords'] = keywords
                    else:
                        _logger.error('no CLASS attribute in %r', item)
                except Exception:
                    logging.exception('Exception while loading extension:')

        return options

    def __cancel_clicked_cb(self, widget):
        self._section_view.undo()
        self._options[self._current_option]['alerts'] = []
        self._section_toolbar.accept_button.set_sensitive(True)
        self._show_main_view()

    def __accept_clicked_cb(self, widget):
        if self._section_view.needs_restart:
            self._section_toolbar.accept_button.set_sensitive(False)
            self._section_toolbar.cancel_button.set_sensitive(False)
            alert = Alert()
            alert.props.title = _('Warning')
            alert.props.msg = _('Changes require restart')

            icon = Icon(icon_name='dialog-cancel')
            alert.add_button(gtk.RESPONSE_CANCEL, _('Cancel changes'), icon)
            icon.show()

            if self._current_option != 'aboutme':
                icon = Icon(icon_name='dialog-ok')
                alert.add_button(gtk.RESPONSE_ACCEPT, _('Later'), icon)
                icon.show()

            icon = Icon(icon_name='system-restart')
            alert.add_button(gtk.RESPONSE_APPLY, _('Restart now'), icon)
            icon.show()

            self._vbox.pack_start(alert, False)
            self._vbox.reorder_child(alert, 2)
            alert.connect('response', self.__response_cb)
            alert.show()
        else:
            self._show_main_view()

    def __response_cb(self, alert, response_id):
        self._vbox.remove(alert)
        self._section_toolbar.accept_button.set_sensitive(True)
        self._section_toolbar.cancel_button.set_sensitive(True)
        if response_id is gtk.RESPONSE_CANCEL:
            self._section_view.undo()
            self._section_view.setup()
            self._options[self._current_option]['alerts'] = []
        elif response_id is gtk.RESPONSE_ACCEPT:
            self._options[self._current_option]['alerts'] = \
                self._section_view.restart_alerts
            self._show_main_view()
        elif response_id is gtk.RESPONSE_APPLY:
            session_manager = get_session_manager()
            session_manager.logout()

    def __select_option_cb(self, button, event, option):
        self.show_section_view(option)

    def __search_changed_cb(self, maintoolbar, query):
        self._update(query)

    def __stop_clicked_cb(self, widget):
        self.destroy()

    def __close_request_cb(self, widget, event=None):
        self.destroy()

    def __valid_section_cb(self, section_view, pspec):
        section_is_valid = section_view.props.is_valid
        self._section_toolbar.accept_button.set_sensitive(section_is_valid)
Example #3
0
class ControlPanel(Gtk.Window):
    __gtype_name__ = 'SugarControlPanel'

    def __init__(self):
        Gtk.Window.__init__(self)

        self._calculate_max_columns()
        self.set_border_width(style.LINE_WIDTH)
        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
        self.set_decorated(False)
        self.set_resizable(False)
        self.set_modal(True)

        self.set_can_focus(True)
        self.connect('key-press-event', self.__key_press_event_cb)

        self._toolbar = None
        self._canvas = None
        self._table = None
        self._scrolledwindow = None
        self._separator = None
        self._section_view = None
        self._section_toolbar = None
        self._main_toolbar = None

        self._vbox = Gtk.VBox()
        self._hbox = Gtk.HBox()
        self._vbox.pack_start(self._hbox, True, True, 0)
        self._hbox.show()

        self._main_view = Gtk.EventBox()
        self._hbox.pack_start(self._main_view, True, True, 0)
        self._main_view.modify_bg(Gtk.StateType.NORMAL,
                                  style.COLOR_BLACK.get_gdk_color())
        self._main_view.show()

        self.add(self._vbox)
        self._vbox.show()

        self.connect('realize', self.__realize_cb)

        self._options = self._get_options()
        self._current_option = None
        self._setup_main()
        self._setup_section()
        self._show_main_view()
        Gdk.Screen.get_default().connect('size-changed',
                                         self.__size_changed_cb)

    def __realize_cb(self, widget):
        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
        self.get_window().set_accept_focus(True)
        # the modal windows counter is updated to disable hot keys - SL#4601
        shell.get_model().push_modal()

    def __size_changed_cb(self, event):
        self._calculate_max_columns()

    def grab_focus(self):
        # overwrite grab focus in order to grab focus on the view
        self._main_view.get_child().grab_focus()

    def _calculate_max_columns(self):
        self._max_columns = int(
            0.285 * (float(Gdk.Screen.width()) / style.GRID_CELL_SIZE - 3))
        offset = style.GRID_CELL_SIZE
        width = Gdk.Screen.width() - offset * 2
        height = Gdk.Screen.height() - offset * 2
        self.set_size_request(width, height)
        if hasattr(self, '_table'):
            for child in self._table.get_children():
                child.destroy()
            self._setup_options()

    def _set_canvas(self, canvas):
        if self._canvas:
            self._main_view.remove(self._canvas)
        if canvas:
            self._main_view.add(canvas)
        self._canvas = canvas

    def _set_toolbar(self, toolbar):
        if self._toolbar:
            self._vbox.remove(self._toolbar)
        self._vbox.pack_start(toolbar, False, False, 0)
        self._vbox.reorder_child(toolbar, 0)
        self._toolbar = toolbar
        if not self._separator:
            self._separator = Gtk.HSeparator()
            self._vbox.pack_start(self._separator, False, False, 0)
            self._vbox.reorder_child(self._separator, 1)
            self._separator.show()

    def _setup_main(self):
        self._main_toolbar = MainToolbar()

        self._table = Gtk.Table()
        self._table.set_col_spacings(style.GRID_CELL_SIZE)
        self._table.set_row_spacings(style.GRID_CELL_SIZE)
        self._table.set_border_width(style.GRID_CELL_SIZE)

        self._scrolledwindow = Gtk.ScrolledWindow()
        self._scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC,
                                        Gtk.PolicyType.AUTOMATIC)
        self._scrolledwindow.add_with_viewport(self._table)
        child = self._scrolledwindow.get_child()
        child.modify_bg(Gtk.StateType.NORMAL,
                        style.COLOR_BLACK.get_gdk_color())

        self._setup_options()
        self._main_toolbar.connect('stop-clicked', self.__stop_clicked_cb)
        self._main_toolbar.connect('search-changed', self.__search_changed_cb)

    def _setup_options(self):
        # If the screen width only supports two columns, start
        # placing from the second row.
        if self._max_columns == 2:
            row = 1
            column = 0
        else:
            # About Me and About my computer are hardcoded below to use the
            # first two slots so we need to leave them free.
            row = 0
            column = 2

        options = self._options.keys()
        options.sort()

        for option in options:
            sectionicon = _SectionIcon(icon_name=self._options[option]['icon'],
                                       title=self._options[option]['title'],
                                       xo_color=self._options[option]['color'],
                                       pixel_size=style.GRID_CELL_SIZE)
            sectionicon.connect('button_press_event', self.__select_option_cb,
                                option)
            sectionicon.show()

            if option == 'aboutme':
                self._table.attach(sectionicon, 0, 1, 0, 1)
            elif option == 'aboutcomputer':
                self._table.attach(sectionicon, 1, 2, 0, 1)
            else:
                self._table.attach(sectionicon, column, column + 1, row,
                                   row + 1)
                column += 1
                if column == self._max_columns:
                    column = 0
                    row += 1

            self._options[option]['button'] = sectionicon

    def _show_main_view(self):
        if self._section_view is not None:
            self._section_view.destroy()
            self._section_view = None

        self._set_toolbar(self._main_toolbar)
        self._main_toolbar.show()
        self._set_canvas(self._scrolledwindow)
        self._main_view.modify_bg(Gtk.StateType.NORMAL,
                                  style.COLOR_BLACK.get_gdk_color())
        self._table.show()
        self._scrolledwindow.show()
        entry = self._main_toolbar.get_entry()
        entry.set_text('')
        entry.connect('icon-press', self.__clear_icon_pressed_cb)
        self.grab_focus()

    def __key_press_event_cb(self, window, event):
        if event.keyval == Gdk.KEY_Escape:
            if self._toolbar == self._main_toolbar:
                self.__stop_clicked_cb(None)
                self.destroy()
            else:
                self.__cancel_clicked_cb(None)
            return True

        # if the user clicked out of the window - fix SL #3188
        if not self.is_active():
            self.present()

        entry = self._main_toolbar.get_entry()
        if not entry.has_focus():
            entry.grab_focus()
        return False

    def __clear_icon_pressed_cb(self, entry, icon_pos, event):
        self.grab_focus()

    def _update(self, query):
        for option in self._options:
            found = False
            for key in self._options[option]['keywords']:
                if query.lower() in key.lower():
                    self._options[option]['button'].set_sensitive(True)
                    found = True
                    break
            if not found:
                self._options[option]['button'].set_sensitive(False)

    def _setup_section(self):
        self._section_toolbar = SectionToolbar()
        self._section_toolbar.connect('cancel-clicked',
                                      self.__cancel_clicked_cb)
        self._section_toolbar.connect('accept-clicked',
                                      self.__accept_clicked_cb)

    def show_section_view(self, option):
        self._set_toolbar(self._section_toolbar)

        icon = self._section_toolbar.get_icon()
        icon.set_from_icon_name(self._options[option]['icon'],
                                Gtk.IconSize.LARGE_TOOLBAR)
        icon.props.xo_color = self._options[option]['color']
        title = self._section_toolbar.get_title()
        title.set_text(self._options[option]['title'])
        self._section_toolbar.show()

        self._current_option = option

        mod = __import__('.'.join(('cpsection', option, 'view')), globals(),
                         locals(), ['view'])
        view_class = getattr(mod, self._options[option]['view'], None)

        mod = __import__('.'.join(('cpsection', option, 'model')), globals(),
                         locals(), ['model'])
        model = ModelWrapper(mod)

        try:
            self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
            self._section_view = view_class(model,
                                            self._options[option]['alerts'])

            self._set_canvas(self._section_view)
            self._section_view.show()
        finally:
            self.get_window().set_cursor(None)

        self._section_view.connect('notify::is-valid', self.__valid_section_cb)
        self._section_view.connect('notify::is-cancellable',
                                   self.__cancellable_section_cb)
        self._section_view.connect('request-close', self.__close_request_cb)
        self._main_view.modify_bg(Gtk.StateType.NORMAL,
                                  style.COLOR_WHITE.get_gdk_color())

    def set_section_view_auto_close(self):
        """Automatically close the control panel if there is "nothing to do"
        """
        self._section_view.auto_close = True

    def _get_options(self):
        """Get the available option information from the extensions
        """
        options = {}

        path = os.path.join(config.ext_path, 'cpsection')
        folder = os.listdir(path)

        for item in folder:
            if os.path.isdir(os.path.join(path, item)) and \
                    os.path.exists(os.path.join(path, item, '__init__.py')):
                try:
                    mod = __import__('.'.join(('cpsection', item)), globals(),
                                     locals(), [item])
                    view_class = getattr(mod, 'CLASS', None)
                    if view_class is not None:
                        options[item] = {}
                        options[item]['alerts'] = []
                        options[item]['view'] = view_class
                        options[item]['icon'] = getattr(mod, 'ICON', item)
                        options[item]['title'] = getattr(mod, 'TITLE', item)
                        options[item]['color'] = getattr(mod, 'COLOR', None)
                        keywords = getattr(mod, 'KEYWORDS', [])
                        keywords.append(options[item]['title'].lower())
                        if item not in keywords:
                            keywords.append(item)
                        options[item]['keywords'] = keywords
                    else:
                        _logger.error('no CLASS attribute in %r', item)
                except Exception:
                    logging.exception('Exception while loading extension:')

        return options

    def __cancel_clicked_cb(self, widget):
        self._section_view.undo()
        self._options[self._current_option]['alerts'] = []
        self._section_toolbar.accept_button.set_sensitive(True)
        self._show_main_view()

    def __accept_clicked_cb(self, widget):
        if hasattr(self._section_view, "apply"):
            self._section_view.apply()

        if self._section_view.needs_restart:
            self._section_toolbar.accept_button.set_sensitive(False)
            self._section_toolbar.cancel_button.set_sensitive(False)
            alert = Alert()
            alert.props.title = _('Warning')
            alert.props.msg = _('Changes require restart')

            if self._section_view.props.is_cancellable:
                icon = Icon(icon_name='dialog-cancel')
                alert.add_button(Gtk.ResponseType.CANCEL, _('Cancel changes'),
                                 icon)
                icon.show()

            if self._current_option not in ('aboutme', 'backup'):
                icon = Icon(icon_name='dialog-ok')
                alert.add_button(Gtk.ResponseType.ACCEPT, _('Later'), icon)
                icon.show()

            icon = Icon(icon_name='system-restart')
            alert.add_button(Gtk.ResponseType.APPLY, _('Restart now'), icon)
            icon.show()

            self._vbox.pack_start(alert, False, False, 0)
            self._vbox.reorder_child(alert, 2)
            alert.connect('response', self.__response_cb)
            alert.show()
        else:
            self._show_main_view()

    def __response_cb(self, alert, response_id):
        self._vbox.remove(alert)
        self._section_toolbar.accept_button.set_sensitive(True)
        self._section_toolbar.cancel_button.set_sensitive(True)
        if response_id is Gtk.ResponseType.CANCEL:
            self._section_view.undo()
            self._section_view.setup()
            self._options[self._current_option]['alerts'] = []
        elif response_id is Gtk.ResponseType.ACCEPT:
            self._options[self._current_option]['alerts'] = \
                self._section_view.restart_alerts
            self._show_main_view()
        elif response_id is Gtk.ResponseType.APPLY:
            session_manager = get_session_manager()
            session_manager.logout()

    def __select_option_cb(self, button, event, option):
        self.show_section_view(option)

    def __search_changed_cb(self, maintoolbar, query):
        self._update(query)

    def __stop_clicked_cb(self, widget):
        shell.get_model().pop_modal()
        self.destroy()

    def __close_request_cb(self, widget, event=None):
        self.destroy()

    def __valid_section_cb(self, section_view, pspec):
        section_is_valid = section_view.props.is_valid
        self._section_toolbar.accept_button.set_sensitive(section_is_valid)

    def __cancellable_section_cb(self, section_view, pspec):
        cancellable = section_view.props.is_cancellable
        self._section_toolbar.cancel_button.set_sensitive(cancellable)
Example #4
0
class ControlPanel(Gtk.Window):
    __gtype_name__ = "SugarControlPanel"

    def __init__(self, window_xid=0):
        self.parent_window_xid = window_xid
        Gtk.Window.__init__(self)

        self._calculate_max_columns()
        self.set_border_width(style.LINE_WIDTH)
        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
        self.set_decorated(False)
        self.set_resizable(False)
        self.set_modal(True)

        self.set_can_focus(True)
        self.connect("key-press-event", self.__key_press_event_cb)

        self._toolbar = None
        self._canvas = None
        self._table = None
        self._scrolledwindow = None
        self._separator = None
        self._section_view = None
        self._section_toolbar = None
        self._main_toolbar = None

        self._vbox = Gtk.VBox()
        self._hbox = Gtk.HBox()
        self._vbox.pack_start(self._hbox, True, True, 0)
        self._hbox.show()

        self._main_view = Gtk.EventBox()
        self._hbox.pack_start(self._main_view, True, True, 0)
        self._main_view.modify_bg(Gtk.StateType.NORMAL, style.COLOR_BLACK.get_gdk_color())
        self._main_view.show()

        self.add(self._vbox)
        self._vbox.show()

        self.connect("realize", self.__realize_cb)

        self._options = self._get_options()
        self._current_option = None
        self._setup_main()
        self._setup_section()
        self._show_main_view()
        Gdk.Screen.get_default().connect("size-changed", self.__size_changed_cb)

        self._busy_count = 0
        self._selected = []

    def __realize_cb(self, widget):
        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
        window = self.get_window()
        window.set_accept_focus(True)
        if self.parent_window_xid > 0:
            display = Gdk.Display.get_default()
            parent = GdkX11.X11Window.foreign_new_for_display(display, self.parent_window_xid)
            window.set_transient_for(parent)

        # the modal windows counter is updated to disable hot keys - SL#4601
        shell.get_model().push_modal()

    def __size_changed_cb(self, event):
        self._calculate_max_columns()

    def busy(self):
        if self._busy_count == 0:
            self._old_cursor = self.get_window().get_cursor()
            self._set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
        self._busy_count += 1

    def unbusy(self):
        self._busy_count -= 1
        if self._busy_count == 0:
            self._set_cursor(self._old_cursor)

    def _set_cursor(self, cursor):
        self.get_window().set_cursor(cursor)
        Gdk.flush()

    def add_alert(self, alert):
        self._vbox.pack_start(alert, False, False, 0)
        self._vbox.reorder_child(alert, 2)

    def remove_alert(self, alert):
        self._vbox.remove(alert)

    def grab_focus(self):
        # overwrite grab focus in order to grab focus on the view
        self._main_view.get_child().grab_focus()

    def _calculate_max_columns(self):
        self._max_columns = int(0.285 * (float(Gdk.Screen.width()) / style.GRID_CELL_SIZE - 3))
        offset = style.GRID_CELL_SIZE
        width = Gdk.Screen.width() - offset * 2
        height = Gdk.Screen.height() - offset * 2
        self.set_size_request(width, height)
        if hasattr(self, "_table"):
            for child in self._table.get_children():
                child.destroy()
            self._setup_options()

    def _set_canvas(self, canvas):
        if self._canvas in self._main_view:
            self._main_view.remove(self._canvas)
        if canvas:
            self._main_view.add(canvas)
        self._canvas = canvas

    def _set_toolbar(self, toolbar):
        if self._toolbar:
            self._vbox.remove(self._toolbar)
        self._vbox.pack_start(toolbar, False, False, 0)
        self._vbox.reorder_child(toolbar, 0)
        self._toolbar = toolbar
        if not self._separator:
            self._separator = Gtk.HSeparator()
            self._vbox.pack_start(self._separator, False, False, 0)
            self._vbox.reorder_child(self._separator, 1)
            self._separator.show()

    def _setup_main(self):
        self._main_toolbar = MainToolbar()

        self._table = Gtk.Table()
        self._table.set_col_spacings(style.GRID_CELL_SIZE)
        self._table.set_row_spacings(style.GRID_CELL_SIZE)
        self._table.set_border_width(style.GRID_CELL_SIZE)

        self._scrolledwindow = Gtk.ScrolledWindow()
        self._scrolledwindow.set_can_focus(False)
        self._scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self._scrolledwindow.add_with_viewport(self._table)
        child = self._scrolledwindow.get_child()
        child.modify_bg(Gtk.StateType.NORMAL, style.COLOR_BLACK.get_gdk_color())

        self._setup_options()
        self._main_toolbar.connect("stop-clicked", self.__stop_clicked_cb)
        self._main_toolbar.connect("search-changed", self.__search_changed_cb)

    def _setup_options(self):
        # If the screen width only supports two columns, start
        # placing from the second row.
        if self._max_columns == 2:
            row = 1
            column = 0
        else:
            # About Me and About my computer are hardcoded below to use the
            # first two slots so we need to leave them free.
            row = 0
            column = 2

        options = self._options.keys()
        options.sort()

        for option in options:
            sectionicon = _SectionIcon(
                icon_name=self._options[option]["icon"],
                title=self._options[option]["title"],
                xo_color=self._options[option]["color"],
                pixel_size=style.GRID_CELL_SIZE,
            )
            sectionicon.connect("button_press_event", self.__select_option_cb, option)
            sectionicon.show()

            if option == "aboutme":
                self._table.attach(sectionicon, 0, 1, 0, 1)
            elif option == "aboutcomputer":
                self._table.attach(sectionicon, 1, 2, 0, 1)
            else:
                self._table.attach(sectionicon, column, column + 1, row, row + 1)
                column += 1
                if column == self._max_columns:
                    column = 0
                    row += 1

            self._options[option]["button"] = sectionicon

    def _show_main_view(self):
        if self._section_view is not None:
            self._section_view.destroy()
            self._section_view = None

        self._set_toolbar(self._main_toolbar)
        self._main_toolbar.show()
        self._set_canvas(self._scrolledwindow)
        self._main_view.modify_bg(Gtk.StateType.NORMAL, style.COLOR_BLACK.get_gdk_color())
        self._table.show()
        self._scrolledwindow.show()
        entry = self._main_toolbar.get_entry()
        entry.set_text("")
        entry.connect("icon-press", self.__clear_icon_pressed_cb)
        self.grab_focus()

    def __key_press_event_cb(self, window, event):
        if event.keyval == Gdk.KEY_Return:
            if len(self._selected) == 1:
                self.show_section_view(self._selected[0])
                return True

        if event.keyval == Gdk.KEY_Escape:
            if self._toolbar == self._main_toolbar:
                self.__stop_clicked_cb(None)
                self.destroy()
            else:
                self.__cancel_clicked_cb(None)
            return True

        # if the user clicked out of the window - fix SL #3188
        if not self.is_active():
            self.present()

        entry = self._main_toolbar.get_entry()
        if not entry.has_focus():
            entry.grab_focus()
        return False

    def __clear_icon_pressed_cb(self, entry, icon_pos, event):
        self.grab_focus()

    def _update(self, query):
        self._selected = []
        for option in self._options:
            found = False
            for key in self._options[option]["keywords"]:
                if query.lower() in key.lower():
                    self._options[option]["button"].set_sensitive(True)
                    self._selected.append(option)
                    found = True
                    break
            if not found:
                self._options[option]["button"].set_sensitive(False)

    def _setup_section(self):
        self._section_toolbar = SectionToolbar()
        self._section_toolbar.connect("cancel-clicked", self.__cancel_clicked_cb)
        self._section_toolbar.connect("accept-clicked", self.__accept_clicked_cb)

    def show_section_view(self, option):
        self._set_toolbar(self._section_toolbar)

        icon = self._section_toolbar.get_icon()
        icon.set_from_icon_name(self._options[option]["icon"], Gtk.IconSize.LARGE_TOOLBAR)
        icon.props.xo_color = self._options[option]["color"]
        title = self._section_toolbar.get_title()
        title.set_text(self._options[option]["title"])
        self._section_toolbar.show()

        self._current_option = option

        mod = __import__(".".join(("cpsection", option, "view")), globals(), locals(), ["view"])
        view_class = getattr(mod, self._options[option]["view"], None)

        mod = __import__(".".join(("cpsection", option, "model")), globals(), locals(), ["model"])
        model = ModelWrapper(mod)

        try:
            self.busy()
            self._section_view = view_class(model, self._options[option]["alerts"])

            self._set_canvas(self._section_view)
            self._section_view.show()
        finally:
            self.unbusy()

        self._section_view.connect("notify::is-valid", self.__valid_section_cb)
        self._section_view.connect("notify::is-cancellable", self.__cancellable_section_cb)
        self._section_view.connect("request-close", self.__close_request_cb)
        self._section_view.connect("add-alert", self.__create_restart_alert_cb)
        self._section_view.connect("set-toolbar-sensitivity", self.__set_toolbar_sensitivity_cb)
        self._main_view.modify_bg(Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color())

    def set_section_view_auto_close(self):
        """Automatically close the control panel if there is "nothing to do"
        """
        self._section_view.auto_close = True

    def _get_options(self):
        """Get the available option information from the extensions
        """
        options = {}

        path = os.path.join(config.ext_path, "cpsection")
        folder = os.listdir(path)

        for item in folder:
            if os.path.isdir(os.path.join(path, item)) and os.path.exists(os.path.join(path, item, "__init__.py")):
                try:
                    mod = __import__(".".join(("cpsection", item)), globals(), locals(), [item])
                    view_class = getattr(mod, "CLASS", None)
                    if view_class is not None:
                        options[item] = {}
                        options[item]["alerts"] = []
                        options[item]["view"] = view_class
                        options[item]["icon"] = getattr(mod, "ICON", item)
                        options[item]["title"] = getattr(mod, "TITLE", item)
                        options[item]["color"] = getattr(mod, "COLOR", None)
                        keywords = getattr(mod, "KEYWORDS", [])
                        keywords.append(options[item]["title"].lower())
                        if item not in keywords:
                            keywords.append(item)
                        options[item]["keywords"] = keywords
                    else:
                        _logger.debug("no CLASS attribute in %r", item)
                except Exception:
                    logging.exception("Exception while loading extension:")

        return options

    def __cancel_clicked_cb(self, widget):
        self._section_view.undo()
        self._options[self._current_option]["alerts"] = []
        self._section_toolbar.accept_button.set_sensitive(True)
        self._show_main_view()

    def __accept_clicked_cb(self, widget):
        if hasattr(self._section_view, "apply"):
            self._section_view.apply()

        if self._section_view.needs_restart:
            self.__set_toolbar_sensitivity_cb(False)
            if self._section_view.show_restart_alert:
                self.__create_restart_alert_cb()
        else:
            self._show_main_view()

    def __set_toolbar_sensitivity_cb(self, value=True, widget=None, event=None):
        self._section_toolbar.accept_button.set_sensitive(value)
        self._section_toolbar.cancel_button.set_sensitive(value)

    def __create_restart_alert_cb(self, widget=None, event=None):
        alert = Alert()
        alert.props.title = _("Warning")
        alert.props.msg = self._section_view.restart_msg

        if self._section_view.props.is_cancellable:
            icon = Icon(icon_name="dialog-cancel")
            alert.add_button(Gtk.ResponseType.CANCEL, _("Cancel changes"), icon)
            icon.show()

        if self._section_view.props.is_deferrable:
            icon = Icon(icon_name="dialog-ok")
            alert.add_button(Gtk.ResponseType.ACCEPT, _("Later"), icon)
            icon.show()

        icon = Icon(icon_name="system-restart")
        alert.add_button(Gtk.ResponseType.APPLY, _("Restart now"), icon)
        icon.show()

        self.add_alert(alert)
        alert.connect("response", self.__response_cb)
        alert.show()

    def __response_cb(self, alert, response_id):
        self.remove_alert(alert)
        self._section_toolbar.accept_button.set_sensitive(True)
        self._section_toolbar.cancel_button.set_sensitive(True)
        if response_id is Gtk.ResponseType.CANCEL:
            self._section_view.undo()
            self._section_view.setup()
            self._options[self._current_option]["alerts"] = []
        elif response_id is Gtk.ResponseType.ACCEPT:
            self._options[self._current_option]["alerts"] = self._section_view.restart_alerts
            self._show_main_view()
        elif response_id is Gtk.ResponseType.APPLY:
            self.busy()
            self._section_toolbar.accept_button.set_sensitive(False)
            self._section_toolbar.cancel_button.set_sensitive(False)
            get_session_manager().logout()
            GObject.timeout_add_seconds(4, self.__quit_timeout_cb)

    def __quit_timeout_cb(self):
        self.unbusy()
        alert = TimeoutAlert(30)
        alert.props.title = _("An activity is not responding.")
        alert.props.msg = _("You may lose unsaved work if you continue.")
        alert.connect("response", self.__quit_accept_cb)

        self.add_alert(alert)
        alert.show()

    def __quit_accept_cb(self, alert, response_id):
        self.remove_alert(alert)
        if response_id is Gtk.ResponseType.CANCEL:
            get_session_manager().cancel_shutdown()
            self._section_toolbar.accept_button.set_sensitive(True)
            self._section_toolbar.cancel_button.set_sensitive(True)
        else:
            self.busy()
            get_session_manager().shutdown_completed()

    def __select_option_cb(self, button, event, option):
        self.show_section_view(option)

    def __search_changed_cb(self, maintoolbar, query):
        self._update(query)

    def __stop_clicked_cb(self, widget):
        shell.get_model().pop_modal()
        self.destroy()

    def __close_request_cb(self, widget, event=None):
        self.destroy()

    def __valid_section_cb(self, section_view, pspec):
        section_is_valid = section_view.props.is_valid
        self._section_toolbar.accept_button.set_sensitive(section_is_valid)

    def __cancellable_section_cb(self, section_view, pspec):
        cancellable = section_view.props.is_cancellable
        self._section_toolbar.cancel_button.set_sensitive(cancellable)