def __init__(self): TweakModule.__init__(self, 'sourceeditor.ui') self.auto_backup_setting = GSetting( 'com.ubuntu-tweak.tweak.auto-backup') self.textview = SourceView(SOURCES_LIST) self.textview.set_sensitive(False) self.sw1.add(self.textview) self.textview.get_buffer().connect('changed', self.on_buffer_changed) self.list_selection = self.list_view.get_selection() self.list_selection.connect("changed", self.on_selection_changed) self.infobar = Gtk.InfoBar() self.info_label = Gtk.Label(label='Current view the list') self.info_label.set_alignment(0, 0.5) self.infobar.get_content_area().add(self.info_label) self.infobar.connect("response", self.on_infobar_response) self.infobar.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE) self.infobar.hide() self.text_vbox.pack_start(self.infobar, False, False, 0) self.connect('realize', self.on_ui_realize) self.add_start(self.hpaned1)
def __init__(self, feature_name): GObject.GObject.__init__(self, hscrollbar_policy=Gtk.PolicyType.NEVER, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC) self.set_property('shadow-type', Gtk.ShadowType.NONE) self.set_border_width(12) self._feature = feature_name self._setting = GSetting('com.ubuntu-tweak.tweak.%s' % feature_name) self._categories = {} self._boxes = [] self._box = Gtk.VBox(spacing=6) viewport = Gtk.Viewport() viewport.set_property('shadow-type', Gtk.ShadowType.NONE) viewport.add(self._box) self.add(viewport) self.load_modules() self.connect('size-allocate', self.rebuild_boxes) self._setting.connect_notify(self.load_modules) self.show_all()
def __init__(self, parent): GuiBuilder.__init__(self, file_name='preferences.ui') self.preferences_dialog.set_transient_for(parent) self.clips_setting = GSetting('com.ubuntu-tweak.tweak.clips') self.tweaks_setting = GSetting('com.ubuntu-tweak.tweak.tweaks') self.admins_setting = GSetting('com.ubuntu-tweak.tweak.admins') self.janitor_setting = GSetting('com.ubuntu-tweak.janitor.plugins') self.clips_location_setting = GSetting('com.ubuntu-tweak.tweak.last-clip-location')
def _initialize_ui_states(self, widget, splash_window): self.window_size_setting = GSetting('com.ubuntu-tweak.tweak.window-size') width, height = self.window_size_setting.get_value() if width >= 900 and height >= 506: self.mainwindow.set_default_size(width, height) for feature_button in ('overview_button', 'apps_button', 'admins_button', \ 'tweaks_button', 'janitor_button'): button = getattr(self, feature_button) label = button.get_child().get_label() button.get_child().set_markup('<b>%s</b>' % label) button.get_child().set_use_underline(True) splash_window.destroy()
def __init__(self): TweakModule.__init__(self, 'sourceeditor.ui') self.auto_backup_setting = GSetting('com.ubuntu-tweak.tweak.auto-backup') self.textview = SourceView(SOURCES_LIST) self.textview.set_sensitive(False) self.sw1.add(self.textview) self.textview.get_buffer().connect('changed', self.on_buffer_changed) un_lock = PolkitButton(PK_ACTION_SOURCE) un_lock.connect('authenticated', self.on_polkit_action) self._authenticated = False self.hbuttonbox2.pack_end(un_lock, False, False, 0) self.list_selection = self.list_view.get_selection() self.list_selection.connect("changed", self.on_selection_changed) self.infobar = Gtk.InfoBar() self.info_label = Gtk.Label('Current view the list') self.info_label.set_alignment(0, 0.5) self.infobar.get_content_area().add(self.info_label) self.infobar.connect("response", self.on_infobar_response) self.infobar.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE) self.infobar.hide() self.text_vbox.pack_start(self.infobar, False, False, 0) self.connect('realize', self.on_ui_realize) self.add_start(self.hbox1)
def __init__(self, feature_name): GObject.GObject.__init__( self, shadow_type=Gtk.ShadowType.NONE, hscrollbar_policy=Gtk.PolicyType.NEVER, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC, ) self.set_border_width(12) self._feature = feature_name self._setting = GSetting("com.ubuntu-tweak.tweak.%s" % feature_name) self._categories = {} self._boxes = [] self._box = Gtk.VBox(spacing=6) viewport = Gtk.Viewport(shadow_type=Gtk.ShadowType.NONE) viewport.add(self._box) self.add(viewport) self.load_modules() self.connect("draw", self.rebuild_boxes) self._setting.connect_notify(self.load_modules, True) self.show_all()
def _initialize_ui_states(self, widget): # TODO implement the search feature self.window_size_setting = GSetting("com.ubuntu-tweak.tweak.window-size") width, height = self.window_size_setting.get_value() if width >= 800 and height >= 480: self.mainwindow.set_default_size(width, height) self.search_entry.hide()
def __init__(self, feature='', module='', splash_window=None): GuiBuilder.__init__(self, file_name='mainwindow.ui') tweaks_page = FeaturePage('tweaks') admins_page = FeaturePage('admins') self.no_result_box.label = self.result_text self.search_page = SearchPage(self.no_result_box) clip_page = ClipPage() self.apps_page = AppsPage(self.back_button, self.next_button) janitor_page = JanitorPage() self.preferences_dialog = PreferencesDialog(self.mainwindow) self.recently_used_settings = GSetting( 'com.ubuntu-tweak.tweak.recently-used') self.feature_dict['overview'] = self.notebook.append_page( clip_page, Gtk.Label('overview')) self.feature_dict['apps'] = self.notebook.append_page( self.apps_page, Gtk.Label()) self.feature_dict['tweaks'] = self.notebook.append_page( tweaks_page, Gtk.Label('tweaks')) self.feature_dict['admins'] = self.notebook.append_page( admins_page, Gtk.Label('admins')) self.feature_dict['janitor'] = self.notebook.append_page( janitor_page, Gtk.Label('janitor')) self.feature_dict['wait'] = self.notebook.append_page( self._crete_wait_page(), Gtk.Label('wait')) self.feature_dict['search'] = self.notebook.append_page( self.search_page, Gtk.Label('search')) # Always show welcome page at first self.mainwindow.connect('realize', self._initialize_ui_states, splash_window) self.back_button.connect('clicked', self.on_back_button_clicked) self.next_button.connect('clicked', self.on_next_button_clicked) tweaks_page.connect('module_selected', self.on_module_selected) self.search_page.connect('module_selected', self.on_module_selected) admins_page.connect('module_selected', self.on_module_selected) self.apps_page.connect('loaded', self.show_apps_page) clip_page.connect('load_module', lambda widget, name: self.do_load_module(name)) clip_page.connect( 'load_feature', lambda widget, name: self.select_target_feature(name)) self.mainwindow.show() if module: self.do_load_module(module) elif feature: self.select_target_feature(feature) accel_group = Gtk.AccelGroup() self.search_entry.add_accelerator('activate', accel_group, Gdk.KEY_f, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE) self.mainwindow.add_accel_group(accel_group) thread.start_new_thread(self.preload_proxy_cache, ())
def __init__(self, parent): GuiBuilder.__init__(self, file_name='preferences.ui') self.preferences_dialog.set_transient_for(parent) self.clips_setting = GSetting('com.ubuntu-tweak.tweak.clips') self.tweaks_setting = GSetting('com.ubuntu-tweak.tweak.tweaks') self.admins_setting = GSetting('com.ubuntu-tweak.tweak.admins') self.janitor_setting = GSetting('com.ubuntu-tweak.janitor.plugins') self.clips_location_setting = GSetting('com.ubuntu-tweak.tweak.last-clip-location') auto_scan_label, auto_scan_switch = WidgetFactory.create("Switch", label=_("Auto scan:"), key='com.ubuntu-tweak.janitor.auto-scan', backend="gsettings") pack = GridPack((auto_scan_label, auto_scan_switch)) self.generic_alignment.add(pack) self.generic_alignment.show_all()
def __init__(self): GObject.GObject.__init__(self) self.scan_tasks = [] self.clean_tasks = [] self.set_border_width(6) GuiBuilder.__init__(self, 'janitorpage.ui') self.autoscan_setting = GSetting('com.ubuntu-tweak.tweak.auto-scan') self.janitor_setting = GSetting('com.ubuntu-tweak.tweak.janitor') self.pack_start(self.vbox1, True, True, 0) self.connect('realize', self.setup_ui_tasks) self.janitor_view.get_selection().connect('changed', self.on_janitor_selection_changed) self.janitor_setting.connect_notify(self.update_model, True) self.show()
def _initialize_ui_states(self, widget, splash_window): self.window_size_setting = GSetting('com.ubuntu-tweak.tweak.window-size') width, height = self.window_size_setting.get_value() if width >= 800 and height >= 480: self.mainwindow.set_default_size(width, height) for feature_button in ('overview_button', 'admins_button', \ 'tweaks_button', 'janitor_button'): button = getattr(self, feature_button) label = button.get_child().get_label() button.get_child().set_markup('<b>%s</b>' % label) button.get_child().set_use_underline(True) splash_window.destroy()
def __init__(self, feature_name): GObject.GObject.__init__(self, hscrollbar_policy=Gtk.PolicyType.NEVER, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC) self.set_property('shadow-type', Gtk.ShadowType.NONE) self.set_border_width(12) self._feature = feature_name self._setting = GSetting('com.ubuntu-tweak.tweak.%s' % feature_name) self._categories = {} self._boxes = [] self._box = Gtk.VBox(spacing=6) viewport = Gtk.Viewport() viewport.set_property('shadow-type', Gtk.ShadowType.NONE) viewport.add(self._box) self.add(viewport) self.load_modules() # TODO this will cause Bug #880663 randomly, as current there's no user extension for features, just disable it # self._setting.connect_notify(self.load_modules) self.show_all()
def __init__(self, feature='', module=''): GuiBuilder.__init__(self, file_name='mainwindow.ui') self.window_size_setting = GSetting('com.ubuntu-tweak.tweak.window-size') width, height = self.window_size_setting.get_value() if width >= 800 and height >= 480: self.mainwindow.set_default_size(width, height) Gtk.rc_parse(os.path.join(DATA_DIR, 'theme/ubuntu-tweak.rc')) tweaks_page = FeaturePage('tweaks') admins_page = FeaturePage('admins') clip_page = ClipPage() # apps_page = AppsPage() janitor_page = JanitorPage() self.preferences_dialog = PreferencesDialog(self.mainwindow) self.rencently_used_settings = GSetting('com.ubuntu-tweak.tweak.rencently-used') self.feature_dict['overview'] = self.notebook.append_page(clip_page, Gtk.Label()) # self.feature_dict['apps'] = self.notebook.append_page(apps_page, Gtk.Label()) self.feature_dict['tweaks'] = self.notebook.append_page(tweaks_page, Gtk.Label()) self.feature_dict['admins'] = self.notebook.append_page(admins_page, Gtk.Label()) self.feature_dict['janitor'] = self.notebook.append_page(janitor_page, Gtk.Label()) self.feature_dict['wait'] = self.notebook.append_page(self._crete_wait_page(), Gtk.Label()) # Always show welcome page at first self.mainwindow.connect('realize', self._initialize_ui_states) tweaks_page.connect('module_selected', self.on_module_selected) admins_page.connect('module_selected', self.on_module_selected) clip_page.connect('load_module', lambda widget, name: self.load_module(name)) clip_page.connect('load_feature', lambda widget, name: self.select_target_feature(name)) self.mainwindow.show() self.link_button.hide() if module: self.load_module(module) elif feature: self.select_target_feature(feature)
def __init__(self, parent): GuiBuilder.__init__(self, file_name='preferences.ui') self.preferences_dialog.set_transient_for(parent) self.clips_setting = GSetting('com.ubuntu-tweak.tweak.clips') self.tweaks_setting = GSetting('com.ubuntu-tweak.tweak.tweaks') self.admins_setting = GSetting('com.ubuntu-tweak.tweak.admins') self.janitor_setting = GSetting('com.ubuntu-tweak.janitor.plugins') self.clips_location_setting = GSetting( 'com.ubuntu-tweak.tweak.last-clip-location') auto_scan_label, auto_scan_switch = WidgetFactory.create( "Switch", label=_("Auto scan:"), key='com.ubuntu-tweak.janitor.auto-scan', backend="gsettings") pack = GridPack((auto_scan_label, auto_scan_switch)) self.generic_alignment.add(pack) self.generic_alignment.show_all()
def __init__(self): GObject.GObject.__init__(self) self.scan_tasks = [] self.clean_tasks = [] self._total_count = 0 self.set_border_width(6) GuiBuilder.__init__(self, 'janitorpage.ui') self.autoscan_setting = GSetting('com.ubuntu-tweak.janitor.auto-scan') self.autoscan_setting.connect_notify(self.on_autoscan_button_toggled) self.plugins_setting = GSetting('com.ubuntu-tweak.janitor.plugins') self.view_width_setting = GSetting( 'com.ubuntu-tweak.janitor.janitor-view-width') self.pack_start(self.vbox1, True, True, 0) self.connect('realize', self.setup_ui_tasks) self.janitor_view.get_selection().connect( 'changed', self.on_janitor_selection_changed) self.plugins_setting.connect_notify(self.update_model, True) self.show()
class UbuntuTweakWindow(GuiBuilder): current_feature = 'overview' feature_dict = {} navigation_dict = {'tweaks': [None, None]} # the module name and page index: 'Compiz': 2 loaded_modules = {} # reversed dict: 2: 'CompizClass' modules_index = {} def __init__(self, feature='', module='', splash_window=None): GuiBuilder.__init__(self, file_name='mainwindow.ui') tweaks_page = FeaturePage('tweaks') admins_page = FeaturePage('admins') self.no_result_box.label = self.result_text self.search_page = SearchPage(self.no_result_box) clip_page = ClipPage() self.apps_page = AppsPage(self.back_button, self.next_button) janitor_page = JanitorPage() self.preferences_dialog = PreferencesDialog(self.mainwindow) self.recently_used_settings = GSetting('com.ubuntu-tweak.tweak.recently-used') self.feature_dict['overview'] = self.notebook.append_page(clip_page, Gtk.Label('overview')) self.feature_dict['apps'] = self.notebook.append_page(self.apps_page, Gtk.Label()) self.feature_dict['tweaks'] = self.notebook.append_page(tweaks_page, Gtk.Label('tweaks')) self.feature_dict['admins'] = self.notebook.append_page(admins_page, Gtk.Label('admins')) self.feature_dict['janitor'] = self.notebook.append_page(janitor_page, Gtk.Label('janitor')) self.feature_dict['wait'] = self.notebook.append_page(self._crete_wait_page(), Gtk.Label('wait')) self.feature_dict['search'] = self.notebook.append_page(self.search_page, Gtk.Label('search')) # Always show welcome page at first self.mainwindow.connect('realize', self._initialize_ui_states, splash_window) self.back_button.connect('clicked', self.on_back_button_clicked) self.next_button.connect('clicked', self.on_next_button_clicked) tweaks_page.connect('module_selected', self.on_module_selected) self.search_page.connect('module_selected', self.on_module_selected) admins_page.connect('module_selected', self.on_module_selected) clip_page.connect('load_module', lambda widget, name: self.do_load_module(name)) clip_page.connect('load_feature', lambda widget, name: self.select_target_feature(name)) self.mainwindow.show() if module: self.do_load_module(module) elif feature: self.select_target_feature(feature) accel_group = Gtk.AccelGroup() self.search_entry.add_accelerator('activate', accel_group, Gdk.KEY_f, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE) self.mainwindow.add_accel_group(accel_group) thread.start_new_thread(self.preload_proxy_cache, ()) @log_func(log) def preload_proxy_cache(self): #This function just called to make sure the cache is loaded as soon as possible proxy.is_package_installed('ubuntu-tweak') @log_func(log) def on_search_entry_activate(self, widget): widget.grab_focus() self.on_search_entry_changed(widget) @log_func(log) def on_search_entry_changed(self, widget): text = widget.get_text() self.set_current_module(None, None) if text: self.notebook.set_current_page(self.feature_dict['search']) self.search_page.search(text) self.search_entry.set_property('secondary-icon-name', 'edit-clear') else: self.on_feature_button_clicked(getattr(self, '%s_button' % self.current_feature), self.current_feature) self.search_page.clean() self.search_entry.set_property('secondary-icon-name', 'edit-find') def on_search_entry_icon_press(self, widget, icon_pos, event): widget.set_text('') def get_module_and_index(self, name): index = self.loaded_modules[name] return self.modules_index[index], index def select_target_feature(self, text): toggle_button = getattr(self, '%s_button' % text, None) log.info("select_target_feature: %s" % text) if toggle_button: self.current_feature = text toggle_button.set_active(True) def _initialize_ui_states(self, widget, splash_window): self.window_size_setting = GSetting('com.ubuntu-tweak.tweak.window-size') width, height = self.window_size_setting.get_value() if width >= 800 and height >= 480: self.mainwindow.set_default_size(width, height) for feature_button in ('overview_button', 'apps_button', 'admins_button', \ 'tweaks_button', 'janitor_button'): button = getattr(self, feature_button) if feature_button == 'apps_button' and getpass.getuser() != 'tualatrix': button.hide() label = button.get_child().get_label() button.get_child().set_markup('<b>%s</b>' % label) button.get_child().set_use_underline(True) splash_window.destroy() def _crete_wait_page(self): vbox = Gtk.VBox() label = Gtk.Label() label.set_markup("<span size=\"xx-large\">%s</span>" % \ _('Please wait a moment...')) label.set_justify(Gtk.Justification.FILL) vbox.pack_start(label, False, False, 50) hbox = Gtk.HBox() vbox.pack_start(hbox, False, False, 0) vbox.show_all() return vbox def on_mainwindow_destroy(self, widget=None): allocation = widget.get_allocation() self.window_size_setting.set_value((allocation.width, allocation.height)) Gtk.main_quit() try: proxy.exit() except Exception, e: log.error(e)
class FeaturePage(Gtk.ScrolledWindow): __gsignals__ = { 'module_selected': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_STRING,)) } _categories = None _boxes = [] def __str__(self): return '<FeaturePage: %s>' % self._feature def __init__(self, feature_name): GObject.GObject.__init__(self, hscrollbar_policy=Gtk.PolicyType.NEVER, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC) self.set_property('shadow-type', Gtk.ShadowType.NONE) self.set_border_width(12) self._feature = feature_name self._setting = GSetting('com.ubuntu-tweak.tweak.%s' % feature_name) self._categories = {} self._boxes = [] self._box = Gtk.VBox(spacing=6) viewport = Gtk.Viewport() viewport.set_property('shadow-type', Gtk.ShadowType.NONE) viewport.add(self._box) self.add(viewport) self.load_modules() self.connect('size-allocate', self.rebuild_boxes) self._setting.connect_notify(self.load_modules) self.show_all() def load_modules(self, *args, **kwargs): log.debug("Loading modules...") loader = ModuleLoader(self._feature) self._boxes = [] for child in self._box.get_children(): self._box.remove(child) for category, category_name in loader.get_categories(): modules = loader.get_modules_by_category(category) if modules: module_to_loads = self._setting.get_value() for module in modules: if module.is_user_extension() and module.get_name() not in module_to_loads: modules.remove(module) category_box = CategoryBox(modules=modules, category_name=category_name) self._connect_signals(category_box) self._boxes.append(category_box) self._box.pack_start(category_box, False, False, 0) self.rebuild_boxes() def _connect_signals(self, category_box): for button in category_box.get_buttons(): button.connect('clicked', self.on_button_clicked) def on_button_clicked(self, widget): log.info('Button clicked') module = widget.get_module() self.emit('module_selected', module.get_name()) @log_func(log) def rebuild_boxes(self, widget=None, event=None): request = self.get_allocation() ncols = request.width / 164 # 32 + 120 + 6 + 4 width = ncols * (164 + 2 * 4) + 40 if width > request.width: ncols -= 1 pos = 0 children = self._box.get_children() for box in self._boxes: modules = box.get_modules() if len (modules) == 0: if box in children: self._box.remove(box) else: if box not in children: self._box.pack_start(box, False, False, 0) self._box.reorder_child(box, pos) box.rebuild_table(ncols) pos += 1
class PreferencesDialog(GuiBuilder): (CLIP_CHECK, CLIP_ICON, CLIP_NAME) = range(3) (TWEAKS_CHECK, TWEAKS_ICON, TWEAKS_NAME) = range(3) (JANITOR_CHECK, JANITOR_NAME) = range(2) page_dict = {'overview': 0, 'tweaks': 1, 'admins': 2, 'janitor': 3} def __init__(self, parent): GuiBuilder.__init__(self, file_name='preferences.ui') self.preferences_dialog.set_transient_for(parent) self.clips_setting = GSetting('com.ubuntu-tweak.tweak.clips') self.tweaks_setting = GSetting('com.ubuntu-tweak.tweak.tweaks') self.admins_setting = GSetting('com.ubuntu-tweak.tweak.admins') self.janitor_setting = GSetting('com.ubuntu-tweak.janitor.plugins') self.clips_location_setting = GSetting( 'com.ubuntu-tweak.tweak.last-clip-location') auto_scan_label, auto_scan_switch = WidgetFactory.create( "Switch", label=_("Auto scan:"), key='com.ubuntu-tweak.janitor.auto-scan', backend="gsettings") pack = GridPack((auto_scan_label, auto_scan_switch)) self.generic_alignment.add(pack) self.generic_alignment.show_all() def on_clip_toggle_render_toggled(self, cell, path): log.debug("on_clip_toggle_render_toggled") self.on_toggle_renderer_toggled(self.clip_model, path, self.CLIP_CHECK, self.CLIP_NAME, self.clips_setting) def on_tweak_toggle_renderer_toggled(self, cell, path): log.debug("on_tweaks_toggle_render_toggled") self.on_toggle_renderer_toggled(self.tweaks_model, path, self.TWEAKS_CHECK, self.TWEAKS_NAME, self.tweaks_setting) def on_admins_toggle_renderer_toggled(self, cell, path): log.debug("on_admins_toggle_render_toggled") self.on_toggle_renderer_toggled(self.admins_model, path, self.TWEAKS_CHECK, self.TWEAKS_NAME, self.admins_setting) def on_janitor_cell_renderer_toggled(self, cell, path): log.debug("on_admins_toggle_render_toggled") self.on_toggle_renderer_toggled(self.janitor_model, path, self.JANITOR_CHECK, self.JANITOR_NAME, self.janitor_setting) def on_toggle_renderer_toggled(self, model, path, check_id, name_id, setting): iter = model.get_iter(path) checked = not model[iter][check_id] model[iter][check_id] = checked self._do_update_model(model, check_id, name_id, setting) def _do_update_model(self, model, check_id, name_id, setting): model_list = [] for row in model: if row[check_id]: model_list.append(row[name_id]) log.debug("on_clip_toggle_render_toggled: %s" % model_list) setting.set_value(model_list) def run(self, feature='overview'): self._update_clip_model() for _feature in ModuleLoader.default_features: self._update_feature_model(_feature) if feature in self.page_dict: self.preference_notebook.set_current_page(self.page_dict[feature]) return self.preferences_dialog.run() def hide(self): return self.preferences_dialog.hide() def on_move_up_button_clicked(self, widget): model, iter = self.clip_view.get_selection().get_selected() if iter: previous_path = str(int(model.get_string_from_iter(iter)) - 1) if int(previous_path) >= 0: previous_iter = model.get_iter_from_string(previous_path) model.move_before(iter, previous_iter) self._do_update_model(self.clip_model, self.CLIP_CHECK, self.CLIP_NAME, self.clips_setting) def on_move_down_button_clicked(self, widget): model, iter = self.clip_view.get_selection().get_selected() if iter: next_iter = model.iter_next(iter) model.move_after(iter, next_iter) self._do_update_model(self.clip_model, self.CLIP_CHECK, self.CLIP_NAME, self.clips_setting) def on_clip_install_button_clicked(self, widget): self.on_install_extension(_('Choose a clip extension'), Clip, 'clips', self.clips_setting, self._update_clip_model, _('"%s" is not a Clip Extension!')) def on_tweaks_install_button_clicked(self, widget): self.on_install_extension(_('Choose a Tweaks Extension'), TweakModule, 'tweaks', self.tweaks_setting, self._update_feature_model, _('"%s" is not a Tweaks Extension!')) def on_admins_install_button_clicked(self, widget): self.on_install_extension(_('Choose a Admins Extension'), TweakModule, 'admins', self.admins_setting, self._update_feature_model, _('"%s" is not a Admins Extension!')) def on_janitor_install_button_clicked(self, widget): self.on_install_extension(_('Choose a Janitor Extension'), JanitorPlugin, 'janitor', self.janitor_setting, self._update_feature_model, _('"%s" is not a Janitor Extension!')) def on_install_extension(self, dialog_label, klass, feature, setting, update_func, error_message): dialog = Gtk.FileChooserDialog( dialog_label, action=Gtk.FileChooserAction.OPEN, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT)) filter = Gtk.FileFilter() filter.set_name(_('Ubuntu Tweak Extension (*.py, *.tar.gz)')) filter.add_pattern('*.py') filter.add_pattern('*.tar.gz') dialog.add_filter(filter) dialog.set_current_folder(self.clips_location_setting.get_value() or GLib.get_home_dir()) filename = '' install_done = False not_extension = False if dialog.run() == Gtk.ResponseType.ACCEPT: filename = dialog.get_filename() dialog.destroy() if filename: self.clips_location_setting.set_value(os.path.dirname(filename)) log.debug("Start to check the class in %s" % filename) if filename.endswith('.tar.gz'): tar_file = TarFile(filename) if tar_file.is_valid(): tar_file.extract(TEMP_ROOT) #TODO if multi-root if tar_file.get_root_name(): temp_dir = os.path.join(TEMP_ROOT, tar_file.get_root_name()) if ModuleLoader.is_target_class(temp_dir, klass): target = os.path.join( ModuleLoader.get_user_extension_dir(feature), os.path.basename(temp_dir)) copy = True if os.path.exists(target): dialog = QuestionDialog(message=_( "Would you like to remove it then install again?"), title=_( '"%s" has already installed' % os.path.basename(target))) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: shutil.rmtree(target) else: copy = False if copy: log.debug("Now copying tree...") shutil.move(temp_dir, target) else: shutil.rmtree(temp_dir) else: not_extension = True else: if ModuleLoader.is_target_class(filename, klass): shutil.copy(filename, ModuleLoader.get_user_extension_dir(feature)) install_done = True else: not_extension = True if install_done: update_func(feature) # To force empty the clips_setting to make load_cips value = setting.get_value() setting.set_value(['']) setting.set_value(value) if not_extension: ErrorDialog(message=error_message % os.path.basename(filename)).launch() def _update_clip_model(self, feature=None): clips = self.clips_setting.get_value() loader = ModuleLoader('clips') self.clip_model.clear() for clip_name in clips: ClipClass = loader.get_module(clip_name) self.clip_model.append( (True, ClipClass.get_pixbuf(), ClipClass.get_name())) for name, ClipClass in loader.module_table.items(): if name not in clips: self.clip_model.append( (False, ClipClass.get_pixbuf(), ClipClass.get_name())) def _update_feature_model(self, feature): module_list = getattr(self, '%s_setting' % feature).get_value() or [] loader = ModuleLoader(feature, user_only=True) model = getattr(self, '%s_model' % feature) model.clear() for name, klass in loader.module_table.items(): if klass.get_pixbuf(): model.append( (name in module_list, klass.get_pixbuf(), klass.get_name())) else: model.append((name in module_list, klass.get_name()))
class UbuntuTweakWindow(GuiBuilder): current_feature = 'overview' feature_dict = {} navigation_dict = {'tweaks': [None, None]} # the module name and page index: 'Compiz': 2 loaded_modules = {} # reversed dict: 2: 'CompizClass' modules_index = {} def __init__(self, feature='', module=''): GuiBuilder.__init__(self, file_name='mainwindow.ui') self.window_size_setting = GSetting('com.ubuntu-tweak.tweak.window-size') width, height = self.window_size_setting.get_value() if width >= 800 and height >= 480: self.mainwindow.set_default_size(width, height) Gtk.rc_parse(os.path.join(DATA_DIR, 'theme/ubuntu-tweak.rc')) tweaks_page = FeaturePage('tweaks') admins_page = FeaturePage('admins') clip_page = ClipPage() # apps_page = AppsPage() janitor_page = JanitorPage() self.preferences_dialog = PreferencesDialog(self.mainwindow) self.rencently_used_settings = GSetting('com.ubuntu-tweak.tweak.rencently-used') self.feature_dict['overview'] = self.notebook.append_page(clip_page, Gtk.Label()) # self.feature_dict['apps'] = self.notebook.append_page(apps_page, Gtk.Label()) self.feature_dict['tweaks'] = self.notebook.append_page(tweaks_page, Gtk.Label()) self.feature_dict['admins'] = self.notebook.append_page(admins_page, Gtk.Label()) self.feature_dict['janitor'] = self.notebook.append_page(janitor_page, Gtk.Label()) self.feature_dict['wait'] = self.notebook.append_page(self._crete_wait_page(), Gtk.Label()) # Always show welcome page at first self.mainwindow.connect('realize', self._initialize_ui_states) tweaks_page.connect('module_selected', self.on_module_selected) admins_page.connect('module_selected', self.on_module_selected) clip_page.connect('load_module', lambda widget, name: self.load_module(name)) clip_page.connect('load_feature', lambda widget, name: self.select_target_feature(name)) self.mainwindow.show() self.link_button.hide() if module: self.load_module(module) elif feature: self.select_target_feature(feature) def on_header_button_press_event(self, widget, event): self.mainwindow.begin_move_drag(event.button, event.x_root, event.y_root, event.time) def get_module_and_index(self, name): index = self.loaded_modules[name] return self.modules_index[index], index def select_target_feature(self, text): toggle_button = getattr(self, '%s_button' % text, None) log.info("select_target_feature: %s" % text) if toggle_button: self.current_feature = text toggle_button.set_active(True) def _initialize_ui_states(self, widget): #TODO implement the search feature self.search_entry.hide() def _crete_wait_page(self): vbox = Gtk.VBox() label = Gtk.Label() label.set_markup("<span size=\"xx-large\">%s</span>" % \ _('Please wait a moment...')) label.set_justify(Gtk.Justification.FILL) vbox.pack_start(label, False, False, 50) hbox = Gtk.HBox() vbox.pack_start(hbox, False, False, 0) vbox.show_all() return vbox def on_mainwindow_destroy(self, widget): allocation = widget.get_allocation() self.window_size_setting.set_value((allocation.width, allocation.height)) Gtk.main_quit() try: proxy.exit() except Exception, e: log.error(e)
class SourceEditor(TweakModule): __title__ = _('Source Editor') __desc__ = _('Manually edit your software sources to suit your needs.') __icon__ = 'system-software-update' __policykit__ = PK_ACTION_SOURCE __category__ = 'system' _authenticated = False def __init__(self): TweakModule.__init__(self, 'sourceeditor.ui') self.auto_backup_setting = GSetting( 'com.ubuntu-tweak.tweak.auto-backup') self.textview = SourceView(SOURCES_LIST) self.textview.set_sensitive(False) self.sw1.add(self.textview) self.textview.get_buffer().connect('changed', self.on_buffer_changed) self.list_selection = self.list_view.get_selection() self.list_selection.connect("changed", self.on_selection_changed) self.infobar = Gtk.InfoBar() self.info_label = Gtk.Label(label='Current view the list') self.info_label.set_alignment(0, 0.5) self.infobar.get_content_area().add(self.info_label) self.infobar.connect("response", self.on_infobar_response) self.infobar.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE) self.infobar.hide() self.text_vbox.pack_start(self.infobar, False, False, 0) self.connect('realize', self.on_ui_realize) self.add_start(self.hpaned1) def on_ui_realize(self, widget): self.infobar.hide() self.update_source_model() self.list_selection.select_iter(self.list_model.get_iter_first()) self.auto_backup_button.set_active( self.auto_backup_setting.get_value()) self.auto_backup_button.connect('toggled', self.on_auto_backup_button_toggled) def set_infobar_backup_info(self, name, list_name): self.info_label.set_markup( _('You\'re viewing the backup "<b>%(backup_name)s</b>" for ' '"<b>%(list_name)s</b>"') % { 'backup_name': name, 'list_name': list_name }) def on_auto_backup_button_toggled(self, widget): self.auto_backup_setting.set_value(widget.get_active()) def on_infobar_response(self, widget, response_id): model, iter = self.list_selection.get_selected() if iter: list_path = model[iter][0] self.textview.set_path(list_path) self.textview.update_content() self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) self.infobar.hide() def on_selection_changed(self, selection): model, iter = selection.get_selected() if iter: self.textview.set_path(model[iter][0]) self.update_sourceslist() self.update_backup_model() def update_source_model(self): model = self.list_model model.clear() model.append(('/etc/apt/sources.list', 'sources.list')) SOURCE_LIST_D = '/etc/apt/sources.list.d' if not os.path.exists(SOURCE_LIST_D): self.source_combo.set_active(0) return files = glob.glob(SOURCE_LIST_D + '/*.list') files.sort() for file in files: if os.path.isdir(file): continue model.append((file, os.path.basename(file))) def update_backup_model(self): def file_cmp(f1, f2): return cmp(os.stat(f1).st_ctime, os.stat(f2).st_ctime) model, iter = self.list_selection.get_selected() if iter: source_list = model[iter][0] self.backup_model.clear() files = glob.glob(source_list + '.*') files.sort(cmp=file_cmp, reverse=True) for path in files: if os.path.isdir(path): continue self.backup_model.append((path, os.path.basename(path).split( '.list.')[-1].split('.save')[0])) if not files: self.backup_model.append((None, _('No backup yet'))) self.backup_edit_button.set_sensitive(False) self.backup_delete_button.set_sensitive(False) self.recover_button.set_sensitive(False) self.backup_view_button.set_sensitive(False) self.infobar.hide() elif self._authenticated == True: self.backup_edit_button.set_sensitive(True) self.backup_delete_button.set_sensitive(True) self.recover_button.set_sensitive(True) self.backup_view_button.set_sensitive(True) self.backup_combobox.set_active(0) def on_source_combo_changed(self, widget): model = widget.get_model() iter = widget.get_active_iter() if self.has_backup_value(iter): self.textview.set_path(model.get_value(iter, 0)) self.update_sourceslist() def on_update_button_clicked(self, widget): self.set_busy() daemon = AptWorker(widget.get_toplevel(), lambda t, s, d: self.unset_busy()) daemon.update_cache() def update_sourceslist(self): self.textview.update_content() self.redo_button.set_sensitive(False) self.save_button.set_sensitive(False) def on_save_button_clicked(self, widget): text = self.textview.get_text().strip() if self.auto_backup_setting.get_value(): proxy.backup_source(self.textview.get_path(), self.get_time_stamp()) self.update_backup_model() if proxy.edit_source(self.textview.get_path(), text) == 'error': ErrorDialog(message=_('Please check the permission of the ' 'sources.list file'), title=_('Save failed!')).launch() else: self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) def on_recover_button_clicked(self, widget): model, iter = self.list_selection.get_selected() if iter: list_path = model[iter][0] list_name = model[iter][1] backup_iter = self.backup_combobox.get_active_iter() if backup_iter: backup_path = self.backup_model[backup_iter][0] backup_name = self.backup_model[backup_iter][1] dialog = QuestionDialog(message=_('Would you like to recover the ' 'backup "<b>%(backup_name)s</b>" for "<b>%(list_name)s</b>"?') % \ {'backup_name': backup_name, 'list_name': list_name}) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: if proxy.restore_source(backup_path, list_path): self.infobar.response(Gtk.ResponseType.CLOSE) else: ErrorDialog(title=_('Recovery Failed!'), message=_( 'You may need to check the permission ' 'of source list.')).launch() def on_backup_view_button_clicked(self, widget=None): model, iter = self.list_selection.get_selected() if iter: list_name = model[iter][1] iter = self.backup_combobox.get_active_iter() if self.has_backup_value(iter): name = self.backup_model[iter][1] self.set_infobar_backup_info(name, list_name) self.textview.set_path(self.backup_model[iter][0]) self.textview.update_content() self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) self.infobar.show() def on_backup_combobox_changed(self, widget): if self.infobar.get_visible(): self.on_backup_view_button_clicked() def on_backup_button_clicked(self, widget): model, iter = self.list_selection.get_selected() if iter: path = model[iter][0] dialog = GetTextDialog( message=_('Please enter the name for your backup:'), text=self.get_time_stamp()) response = dialog.run() dialog.destroy() backup_name = dialog.get_text() if response == Gtk.ResponseType.YES and backup_name: if self.is_valid_backup_name(backup_name): if proxy.backup_source(path, backup_name): self.update_backup_model() else: ErrorDialog(message=_('Backup Failed!')).launch() else: ErrorDialog(message=_( 'Please only use alphanumeric characters' ' and "_" and "-".'), title=_('Backup name is invalid')).launch() def on_backup_delete_button_clicked(self, widget): iter = self.backup_combobox.get_active_iter() path = self.backup_model[iter][0] dialog = QuestionDialog( message=_('Would you like to delete the backup ' '"<b>%s</b>"?') % os.path.basename(path)) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: proxy.delete_source(path) self.update_backup_model() def on_backup_edit_button_clicked(self, widget): iter = self.backup_combobox.get_active_iter() path = self.backup_model[iter][0] name = self.backup_model[iter][1] dialog = GetTextDialog( message=_('Please enter a new name for your backup:'), text=name) response = dialog.run() dialog.destroy() new_name = dialog.get_text() if response == Gtk.ResponseType.YES and new_name and name != new_name: if self.is_valid_backup_name(new_name): proxy.rename_backup(path, name, new_name) self.update_backup_model() else: ErrorDialog(message=_('Please only use alphanumeric characters' ' and "_" and "-".'), title=_('Backup name is invalid')).launch() def on_redo_button_clicked(self, widget): dialog = QuestionDialog(message=_( 'The current content will be lost after reloading!\nDo you wish to continue?' )) if dialog.run() == Gtk.ResponseType.YES: self.textview.update_content() self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) dialog.destroy() def on_buffer_changed(self, buffer): if buffer.get_modified(): self.save_button.set_sensitive(True) self.redo_button.set_sensitive(True) else: self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) def on_delete_button_clicked(self, widget): if self.textview.get_path() == SOURCES_LIST: ErrorDialog(_('You can\'t delete sources.list!')).launch() else: dialog = QuestionDialog(message=_( 'The "%s" will be deleted!\nDo you wish to continue?') % self.textview.get_path()) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: model, iter = self.list_selection.get_selected() if iter: list_path = model[iter][0] proxy.delete_source(list_path) self.update_source_model() self.update_backup_model() def on_polkit_action(self, widget): self._authenticated = True self.textview.set_sensitive(True) self.delete_button.set_sensitive(True) self.recover_button.set_sensitive(True) self.backup_button.set_sensitive(True) self.backup_edit_button.set_sensitive(True) self.backup_delete_button.set_sensitive(True) self.backup_view_button.set_sensitive(True) def is_valid_backup_name(self, name): pattern = re.compile('[\w\-]+') match = pattern.search(name) return match and name == match.group() def has_backup_value(self, iter): return iter and self.backup_model[iter][0] def get_time_stamp(self): return time.strftime('%Y-%m-%d-%H-%M', time.localtime(time.time()))
class UbuntuTweakWindow(GuiBuilder): current_feature = "overview" feature_dict = {} navigation_dict = {"tweaks": [None, None]} # the module name and page index: 'Compiz': 2 loaded_modules = {} # reversed dict: 2: 'CompizClass' modules_index = {} def __init__(self, feature="", module=""): GuiBuilder.__init__(self, file_name="mainwindow.ui") tweaks_page = FeaturePage("tweaks") admins_page = FeaturePage("admins") clip_page = ClipPage() apps_page = AppsPage() janitor_page = JanitorPage() self.preferences_dialog = PreferencesDialog(self.mainwindow) self.rencently_used_settings = GSetting("com.ubuntu-tweak.tweak.rencently-used") self.feature_dict["overview"] = self.notebook.append_page(clip_page, Gtk.Label()) self.feature_dict["apps"] = self.notebook.append_page(apps_page, Gtk.Label()) self.feature_dict["tweaks"] = self.notebook.append_page(tweaks_page, Gtk.Label()) self.feature_dict["admins"] = self.notebook.append_page(admins_page, Gtk.Label()) self.feature_dict["janitor"] = self.notebook.append_page(janitor_page, Gtk.Label()) self.feature_dict["wait"] = self.notebook.append_page(self._crete_wait_page(), Gtk.Label()) # Always show welcome page at first self.mainwindow.connect("realize", self._initialize_ui_states) tweaks_page.connect("module_selected", self.on_module_selected) admins_page.connect("module_selected", self.on_module_selected) clip_page.connect("load_module", lambda widget, name: self.do_load_module(name)) clip_page.connect("load_feature", lambda widget, name: self.select_target_feature(name)) self.mainwindow.show() self.link_button.hide() if module: self.do_load_module(module) elif feature: self.select_target_feature(feature) # TODO remove when natty is deprecated def on_header_button_press_event(self, widget, event): self.mainwindow.begin_move_drag(event.button, event.x_root, event.y_root, event.time) def get_module_and_index(self, name): index = self.loaded_modules[name] return self.modules_index[index], index def select_target_feature(self, text): toggle_button = getattr(self, "%s_button" % text, None) log.info("select_target_feature: %s" % text) if toggle_button: self.current_feature = text toggle_button.set_active(True) def _initialize_ui_states(self, widget): # TODO implement the search feature self.window_size_setting = GSetting("com.ubuntu-tweak.tweak.window-size") width, height = self.window_size_setting.get_value() if width >= 800 and height >= 480: self.mainwindow.set_default_size(width, height) self.search_entry.hide() def _crete_wait_page(self): vbox = Gtk.VBox() label = Gtk.Label() label.set_markup('<span size="xx-large">%s</span>' % _("Please wait a moment...")) label.set_justify(Gtk.Justification.FILL) vbox.pack_start(label, False, False, 50) hbox = Gtk.HBox() vbox.pack_start(hbox, False, False, 0) vbox.show_all() return vbox def on_mainwindow_destroy(self, widget): allocation = widget.get_allocation() self.window_size_setting.set_value((allocation.width, allocation.height)) Gtk.main_quit() try: proxy.exit() except Exception, e: log.error(e)
class SourceEditor(TweakModule): __title__ = _('Source Editor') __desc__ = _('Manually edit your software sources to suit your needs.') __icon__ = 'system-software-update' __category__ = 'system' def __init__(self): TweakModule.__init__(self, 'sourceeditor.ui') self.auto_backup_setting = GSetting('com.ubuntu-tweak.tweak.auto-backup') self.textview = SourceView(SOURCES_LIST) self.textview.set_sensitive(False) self.sw1.add(self.textview) self.textview.get_buffer().connect('changed', self.on_buffer_changed) un_lock = PolkitButton(PK_ACTION_SOURCE) un_lock.connect('authenticated', self.on_polkit_action) self._authenticated = False self.hbuttonbox2.pack_end(un_lock, False, False, 0) self.list_selection = self.list_view.get_selection() self.list_selection.connect("changed", self.on_selection_changed) self.infobar = Gtk.InfoBar() self.info_label = Gtk.Label('Current view the list') self.info_label.set_alignment(0, 0.5) self.infobar.get_content_area().add(self.info_label) self.infobar.connect("response", self.on_infobar_response) self.infobar.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE) self.infobar.hide() self.text_vbox.pack_start(self.infobar, False, False, 0) self.connect('realize', self.on_ui_realize) self.add_start(self.hbox1) def on_ui_realize(self, widget): self.infobar.hide() self.update_source_model() self.list_selection.select_iter(self.list_model.get_iter_first()) self.auto_backup_button.set_active(self.auto_backup_setting.get_value()) self.auto_backup_button.connect('toggled', self.on_auto_backup_button_toggled) def set_infobar_backup_info(self, name, list_name): self.info_label.set_markup(_('You\'re viewing the backup "<b>%s</b>" for' '"<b>%s</b>"') % (name, list_name)) def on_auto_backup_button_toggled(self, widget): self.auto_backup_setting.set_value(widget.get_active()) def on_infobar_response(self, widget, response_id): model, iter = self.list_selection.get_selected() if iter: list_path = model[iter][0] self.textview.set_path(list_path) self.textview.update_content() self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) self.infobar.hide() def on_selection_changed(self, selection): model, iter = selection.get_selected() if iter: self.textview.set_path(model[iter][0]) self.update_sourceslist() self.update_backup_model() def update_source_model(self): model = self.list_model model.clear() model.append(('/etc/apt/sources.list', 'sources.list')) SOURCE_LIST_D = '/etc/apt/sources.list.d' if not os.path.exists(SOURCE_LIST_D): self.source_combo.set_active(0) return files = glob.glob(SOURCE_LIST_D + '/*.list') files.sort() for file in files: if os.path.isdir(file): continue model.append((file, os.path.basename(file))) def update_backup_model(self): def file_cmp(f1, f2): return cmp(os.stat(f1).st_ctime, os.stat(f2).st_ctime) model, iter = self.list_selection.get_selected() if iter: source_list = model[iter][0] self.backup_model.clear() files = glob.glob(source_list + '.*') files.sort(cmp=file_cmp, reverse=True) for path in files: if os.path.isdir(path): continue self.backup_model.append((path, os.path.basename(path).split('.list.')[-1].split('.save')[0])) if not files: self.backup_model.append((None, _('No backup yet'))) self.backup_edit_button.set_sensitive(False) self.backup_delete_button.set_sensitive(False) self.recover_button.set_sensitive(False) self.backup_view_button.set_sensitive(False) self.infobar.hide() elif self._authenticated == True: self.backup_edit_button.set_sensitive(True) self.backup_delete_button.set_sensitive(True) self.recover_button.set_sensitive(True) self.backup_view_button.set_sensitive(True) self.backup_combobox.set_active(0) def on_source_combo_changed(self, widget): model = widget.get_model() iter = widget.get_active_iter() if self.has_backup_value(iter): self.textview.set_path(model.get_value(iter, 0)) self.update_sourceslist() def on_update_button_clicked(self, widget): self.set_busy() daemon = AptWorker(widget.get_toplevel(), lambda t, s, d: self.unset_busy()) daemon.update_cache() def update_sourceslist(self): self.textview.update_content() self.redo_button.set_sensitive(False) self.save_button.set_sensitive(False) def on_save_button_clicked(self, widget): text = self.textview.get_text().strip() if self.auto_backup_setting.get_value(): proxy.backup_source(self.textview.get_path(), self.get_time_stamp()) self.update_backup_model() if proxy.edit_source(self.textview.get_path(), text) == 'error': ErrorDialog(message=_('Please check the permission of the ' 'sources.list file'), title=_('Save failed!')).launch() else: self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) def on_recover_button_clicked(self, widget): model, iter = self.list_selection.get_selected() if iter: list_path = model[iter][0] list_name = model[iter][1] backup_iter = self.backup_combobox.get_active_iter() if backup_iter: backup_path = self.backup_model[backup_iter][0] backup_name = self.backup_model[backup_iter][1] dialog = QuestionDialog(message=_('Would you like to recover the ' 'backup "<b>%s</b>" for "<b>%s</b>"?') % ( backup_name, list_name)) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: if proxy.restore_source(backup_path, list_path): self.infobar.response(Gtk.ResponseType.CLOSE) else: ErrorDialog(title=_('Recovery Failed!'), message=_('You may need to check the permission ' 'of source list.')).launch() def on_backup_view_button_clicked(self, widget=None): model, iter = self.list_selection.get_selected() if iter: list_name = model[iter][1] iter = self.backup_combobox.get_active_iter() if self.has_backup_value(iter): name = self.backup_model[iter][1] self.set_infobar_backup_info(name, list_name) self.textview.set_path(self.backup_model[iter][0]) self.textview.update_content() self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) self.infobar.show() def on_backup_combobox_changed(self, widget): if self.infobar.get_visible(): self.on_backup_view_button_clicked() def on_backup_button_clicked(self, widget): model, iter = self.list_selection.get_selected() if iter: path = model[iter][0] dialog = GetTextDialog(message=_('Please enter the name for your backup:'), text=self.get_time_stamp()) response = dialog.run() dialog.destroy() backup_name = dialog.get_text() if response == Gtk.ResponseType.YES and backup_name: if self.is_valid_backup_name(backup_name): if proxy.backup_source(path, backup_name): self.update_backup_model() else: ErrorDialog(message=_('Backup Failed!')).launch() else: ErrorDialog(message=_('Please only use alphanumeric characters' ' and "_" and "-".'), title=_('Backup name is invalid')).launch() def on_backup_delete_button_clicked(self, widget): iter = self.backup_combobox.get_active_iter() path = self.backup_model[iter][0] dialog = QuestionDialog(message=_('Would you like to delete the backup:' '<b>%s</b>?') % os.path.basename(path)) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: proxy.delete_source(path) self.update_backup_model() def on_backup_edit_button_clicked(self, widget): iter = self.backup_combobox.get_active_iter() path = self.backup_model[iter][0] name = self.backup_model[iter][1] dialog = GetTextDialog(message=_('Please enter a new name for your backup:'), text=name) response = dialog.run() dialog.destroy() new_name = dialog.get_text() if response == Gtk.ResponseType.YES and new_name and name != new_name: if self.is_valid_backup_name(new_name): proxy.rename_backup(path, name, new_name) self.update_backup_model() else: ErrorDialog(message=_('Please only use alphanumeric characters' ' and "_" and "-".'), title=_('Backup name is invalid')).launch() def on_redo_button_clicked(self, widget): dialog = QuestionDialog(message=_('The current content will be lost after reloading!\nDo you wish to continue?')) if dialog.run() == Gtk.ResponseType.YES: self.textview.update_content() self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) dialog.destroy() def on_buffer_changed(self, buffer): if buffer.get_modified(): self.save_button.set_sensitive(True) self.redo_button.set_sensitive(True) else: self.save_button.set_sensitive(False) self.redo_button.set_sensitive(False) def on_delete_button_clicked(self, widget): if self.textview.get_path() == SOURCES_LIST: ErrorDialog(_('You can\'t delete sources.list!')).launch() else: dialog = QuestionDialog(message=_('The "%s" will be deleted!\nDo you wish to continue?') % self.textview.get_path()) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: model, iter = self.list_selection.get_selected() if iter: list_path = model[iter][0] proxy.delete_source(list_path) self.update_source_model() self.update_backup_model() def on_polkit_action(self, widget): self._authenticated = True self.textview.set_sensitive(True) self.delete_button.set_sensitive(True) self.recover_button.set_sensitive(True) self.backup_button.set_sensitive(True) self.backup_edit_button.set_sensitive(True) self.backup_delete_button.set_sensitive(True) self.backup_view_button.set_sensitive(True) def is_valid_backup_name(self, name): pattern = re.compile('[\w\-]+') match = pattern.search(name) return match and name == match.group() def has_backup_value(self, iter): return iter and self.backup_model[iter][0] def get_time_stamp(self): return time.strftime('%Y-%m-%d-%H-%M', time.localtime(time.time()))
class JanitorPage(Gtk.VBox, GuiBuilder): (JANITOR_CHECK, JANITOR_ICON, JANITOR_NAME, JANITOR_DISPLAY, JANITOR_PLUGIN, JANITOR_SPINNER_ACTIVE, JANITOR_SPINNER_PULSE) = range(7) (RESULT_CHECK, RESULT_ICON, RESULT_NAME, RESULT_DISPLAY, RESULT_DESC, RESULT_PLUGIN, RESULT_CRUFT) = range(7) max_janitor_view_width = 0 def __init__(self): GObject.GObject.__init__(self) self.scan_tasks = [] self.clean_tasks = [] self._total_count = 0 self.set_border_width(6) GuiBuilder.__init__(self, 'janitorpage.ui') self.autoscan_setting = GSetting('com.ubuntu-tweak.janitor.auto-scan') self.autoscan_setting.connect_notify(self.on_autoscan_button_toggled) self.plugins_setting = GSetting('com.ubuntu-tweak.janitor.plugins') self.view_width_setting = GSetting( 'com.ubuntu-tweak.janitor.janitor-view-width') self.pack_start(self.vbox1, True, True, 0) self.connect('realize', self.setup_ui_tasks) self.janitor_view.get_selection().connect( 'changed', self.on_janitor_selection_changed) self.plugins_setting.connect_notify(self.update_model, True) self.show() def on_move_handle(self, widget, gproperty): log.debug("on_move_handle: %d", widget.get_property('position')) self.view_width_setting.set_value(widget.get_property('position')) # cancel the size request, or it will fail to resize # TODO why the first scan will make it fail? self.janitor_view.set_size_request(self.max_janitor_view_width, -1) def is_auto_scan(self): return self.autoscan_setting.get_value() @log_func(log) def on_result_view_row_activated(self, treeview, path, column): iter = self.result_model.get_iter(path) cruft = self.result_model[iter][self.RESULT_CRUFT] display = self.result_model[iter][self.RESULT_DISPLAY] if 'red' in display: plugin = self.result_model[iter][self.RESULT_PLUGIN] error = plugin.get_property('error') self.result_model[iter][ self. RESULT_DISPLAY] = '<span color="red"><b>%s</b></span>' % error elif hasattr(cruft, 'get_path'): path = cruft.get_path() if not os.path.isdir(path): path = os.path.dirname(path) os.system("xdg-open '%s' &" % path) def setup_ui_tasks(self, widget): self.janitor_model.set_sort_column_id(self.JANITOR_NAME, Gtk.SortType.ASCENDING) #add janitor columns janitor_column = Gtk.TreeViewColumn() renderer = Gtk.CellRendererToggle() renderer.connect('toggled', self.on_janitor_check_button_toggled) janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'active', self.JANITOR_CHECK) self.janitor_view.append_column(janitor_column) janitor_column = Gtk.TreeViewColumn() renderer = Gtk.CellRendererPixbuf() janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'pixbuf', self.JANITOR_ICON) janitor_column.set_cell_data_func(renderer, self.icon_column_view_func, self.JANITOR_ICON) renderer = Gtk.CellRendererText() renderer.set_property('ellipsize', Pango.EllipsizeMode.END) janitor_column.pack_start(renderer, True) janitor_column.add_attribute(renderer, 'markup', self.JANITOR_DISPLAY) renderer = Gtk.CellRendererSpinner() janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'active', self.JANITOR_SPINNER_ACTIVE) janitor_column.add_attribute(renderer, 'pulse', self.JANITOR_SPINNER_PULSE) self.janitor_view.append_column(janitor_column) #end janitor columns #new result columns result_display_renderer = self.builder.get_object( 'result_display_renderer') result_display_renderer.set_property('ellipsize', Pango.EllipsizeMode.END) result_icon_renderer = self.builder.get_object('result_icon_renderer') self.result_column.set_cell_data_func(result_icon_renderer, self.icon_column_view_func, self.RESULT_ICON) #end new result columns auto_scan = self.autoscan_setting.get_value() log.info("Auto scan status: %s", auto_scan) self.scan_button.set_visible(not auto_scan) self.update_model() self._expand_janitor_view() self.hpaned1.connect('notify::position', self.on_move_handle) def _expand_janitor_view(self): self.janitor_view.expand_all() left_view_width = self.view_width_setting.get_value() log.debug("left_view_width is: %d, max_janitor_view_width is: %d" % (left_view_width, self.max_janitor_view_width)) if left_view_width: self.janitor_view.set_size_request(left_view_width, -1) elif self.max_janitor_view_width: self.janitor_view.set_size_request(self.max_janitor_view_width, -1) def set_busy(self): self.get_parent_window().set_cursor( Gdk.Cursor.new(Gdk.CursorType.WATCH)) def unset_busy(self): self.get_parent_window().set_cursor(None) def on_janitor_selection_changed(self, selection): model, iter = selection.get_selected() if iter: if self.janitor_model.iter_has_child(iter): iter = self.janitor_model.iter_children(iter) plugin = model[iter][self.JANITOR_PLUGIN] for row in self.result_model: if row[self.RESULT_PLUGIN] == plugin: self.result_view.get_selection().select_path(row.path) log.debug("scroll_to_cell: %s" % row.path) self.result_view.scroll_to_cell(row.path) def _is_scanning_or_cleaning(self): for row in self.janitor_model: for child_row in row.iterchildren(): if child_row[self.JANITOR_SPINNER_ACTIVE]: return True else: return False def on_janitor_check_button_toggled(self, cell, path): self.result_view.show() self.happy_box.hide() iter = self.janitor_model.get_iter(path) if self._is_scanning_or_cleaning(): return checked = not self.janitor_model[iter][self.JANITOR_CHECK] if self.janitor_model.iter_has_child(iter): child_iter = self.janitor_model.iter_children(iter) while child_iter: self.janitor_model[child_iter][self.JANITOR_CHECK] = checked child_iter = self.janitor_model.iter_next(child_iter) self.janitor_model[iter][self.JANITOR_CHECK] = checked self._check_child_is_all_the_same(self.janitor_model, iter, self.JANITOR_CHECK, checked) if self.is_auto_scan(): self._auto_scan_cruft(iter, checked) def _update_clean_button_sensitive(self): self.clean_button.set_sensitive(False) for row in self.result_model: for child_row in row.iterchildren(): if child_row[self.RESULT_CHECK]: self.clean_button.set_sensitive(True) break def on_result_check_renderer_toggled(self, cell, path): iter = self.result_model.get_iter(path) checked = self.result_model[iter][self.RESULT_CHECK] if self._is_scanning_or_cleaning(): return if self.result_model.iter_has_child(iter): child_iter = self.result_model.iter_children(iter) while child_iter: self.result_model[child_iter][self.RESULT_CHECK] = not checked child_iter = self.result_model.iter_next(child_iter) self.result_model[iter][self.RESULT_CHECK] = not checked self._check_child_is_all_the_same(self.result_model, iter, self.RESULT_CHECK, not checked) self._update_clean_button_sensitive() def _check_child_is_all_the_same(self, model, iter, column_id, status): iter = model.iter_parent(iter) if iter: child_iter = model.iter_children(iter) while child_iter: if status != model[child_iter][column_id]: model[iter][column_id] = False break child_iter = model.iter_next(child_iter) else: model[iter][column_id] = status def on_scan_button_clicked(self, widget=None): self.result_model.clear() self.clean_button.set_sensitive(False) scan_dict = OrderedDict() for row in self.janitor_model: for child_row in row.iterchildren(): checked = child_row[self.JANITOR_CHECK] scan_dict[child_row.iter] = checked self.scan_tasks = list(scan_dict.items()) self._total_count = 0 self.result_view.show() self.happy_box.hide() self.set_busy() self.do_scan_task() def _auto_scan_cruft(self, iter, checked): self.set_busy() scan_dict = OrderedDict() if self.janitor_model.iter_has_child(iter): log.info('Scan cruft for all plugins') #Scan cruft for children child_iter = self.janitor_model.iter_children(iter) while child_iter: scan_dict[child_iter] = checked child_iter = self.janitor_model.iter_next(child_iter) else: scan_dict[iter] = checked self.scan_tasks = list(scan_dict.items()) for plugin_iter, checked in self.scan_tasks: plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] for row in self.result_model: if row[self.RESULT_PLUGIN] == plugin: self.result_model.remove(row.iter) self.do_scan_task() def do_scan_task(self): plugin_iter, checked = self.scan_tasks.pop(0) plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] plugin.set_property('scan_finished', False) log.debug("do_scan_task for %s for status: %s" % (plugin, checked)) if checked: log.info('Scan cruft for plugin: %s' % plugin.get_name()) iter = self.result_model.append( None, (None, None, plugin.get_title(), '<b>%s</b>' % _('Scanning cruft for "%s"...') % plugin.get_title(), None, plugin, None)) self.janitor_model[plugin_iter][self.JANITOR_SPINNER_ACTIVE] = True self.janitor_model[plugin_iter][self.JANITOR_SPINNER_PULSE] = 0 self.janitor_view.scroll_to_cell( self.janitor_model.get_path(plugin_iter)) self._find_handler = plugin.connect('find_object', self.on_find_object, (plugin_iter, iter)) self._scan_handler = plugin.connect('scan_finished', self.on_scan_finished, (plugin_iter, iter)) self._error_handler = plugin.connect('scan_error', self.on_scan_error, (plugin_iter, iter)) t = threading.Thread(target=plugin.get_cruft) GObject.timeout_add(50, self._on_spinner_timeout, plugin_iter, t) t.start() else: # Update the janitor title for row in self.janitor_model: for child_row in row.iterchildren(): if child_row[self.JANITOR_PLUGIN] == plugin: child_row[self.JANITOR_DISPLAY] = plugin.get_title() if self.scan_tasks: self.do_scan_task() else: if self._total_count == 0: self.result_view.hide() self.happy_box.show() else: self.result_view.show() self.happy_box.hide() self.unset_busy() def _on_spinner_timeout(self, plugin_iter, thread): plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] finished = plugin.get_property('scan_finished') self.janitor_model[plugin_iter][self.JANITOR_SPINNER_PULSE] += 1 if finished: for handler in (self._find_handler, self._scan_handler, self._error_handler): if plugin.handler_is_connected(handler): log.debug( "Disconnect the cleaned signal, or it will clean many times: %s" % plugin) plugin.disconnect(handler) self.janitor_model[plugin_iter][ self.JANITOR_SPINNER_ACTIVE] = False thread.join() if len(self.scan_tasks) != 0: log.debug("Pending scan tasks: %d" % len(self.scan_tasks)) self.do_scan_task() else: log.debug("total_count is: %d" % self._total_count) if self._total_count == 0: self.result_view.hide() self.happy_box.show() else: self.result_view.show() self.happy_box.hide() self.unset_busy() return not finished @post_ui def on_find_object(self, plugin, cruft, count, iters): while Gtk.events_pending(): Gtk.main_iteration() plugin_iter, result_iter = iters self.result_model.append( result_iter, (False, cruft.get_icon(), cruft.get_name(), cruft.get_name(), cruft.get_size_display(), plugin, cruft)) self.result_view.expand_row(self.result_model.get_path(result_iter), True) # Update the janitor title if count: self.janitor_model[plugin_iter][ self.JANITOR_DISPLAY] = "<b>[%d] %s</b>" % (count, plugin.get_title()) else: self.janitor_model[plugin_iter][ self.JANITOR_DISPLAY] = "[0] %s" % plugin.get_title() @post_ui def on_scan_finished(self, plugin, result, count, size, iters): plugin.disconnect(self._find_handler) plugin.disconnect(self._scan_handler) plugin.set_property('scan_finished', True) plugin_iter, result_iter = iters if count == 0: self.result_model.remove(result_iter) else: self.result_model[result_iter][ self.RESULT_DISPLAY] = "<b>%s</b>" % plugin.get_summary(count) if size != 0: self.result_model[result_iter][ self.RESULT_DESC] = "<b>%s</b>" % filesizeformat(size) # Update the janitor title self._total_count += count if count: self.janitor_model[plugin_iter][ self.JANITOR_DISPLAY] = "<b>[%d] %s</b>" % (count, plugin.get_title()) self.result_view.collapse_row( self.result_model.get_path(result_iter)) else: self.janitor_model[plugin_iter][ self.JANITOR_DISPLAY] = "[0] %s" % plugin.get_title() @post_ui def on_scan_error(self, plugin, error, iters): plugin_iter, result_iter = iters self.janitor_model[plugin_iter][ self.JANITOR_ICON] = icon.get_from_name('error', size=16) self.result_model[result_iter][ self.RESULT_DISPLAY] = '<span color="red"><b>%s</b></span>' % _( 'Scan error for "%s", double-click to see details' ) % plugin.get_title() plugin.set_property('scan_finished', True) plugin.set_property('error', error) @inline_callbacks def on_clean_button_clicked(self, widget): '''plugin_dict: {plugin: {cruft: iter}}''' try: yield PolkitAction(PK_ACTION_CLEAN).do_authenticate() except Exception, e: log.debug(e) return self.plugin_to_run = 0 self.set_busy() self.clean_button.set_sensitive(False) plugin_dict = OrderedDict() for row in self.result_model: plugin = row[self.RESULT_PLUGIN] cruft_dict = OrderedDict() for child_row in row.iterchildren(): checked = child_row[self.RESULT_CHECK] if checked: cruft_dict[child_row[self.RESULT_CRUFT]] = child_row.iter if cruft_dict: plugin_dict[plugin] = cruft_dict self.clean_tasks = list(plugin_dict.items()) self.do_real_clean_task() log.debug("All finished!")
class UbuntuTweakWindow(GuiBuilder): current_feature = 'overview' feature_dict = {} navigation_dict = {'tweaks': [None, None]} # the module name and page index: 'Compiz': 2 loaded_modules = {} # reversed dict: 2: 'CompizClass' modules_index = {} def __init__(self, feature='', module='', splash_window=None): GuiBuilder.__init__(self, file_name='mainwindow.ui') tweaks_page = FeaturePage('tweaks') admins_page = FeaturePage('admins') self.no_result_box.label = self.result_text self.search_page = SearchPage(self.no_result_box) clip_page = ClipPage() self.apps_page = AppsPage(self.back_button, self.next_button) janitor_page = JanitorPage() self.preferences_dialog = PreferencesDialog(self.mainwindow) self.recently_used_settings = GSetting('com.ubuntu-tweak.tweak.recently-used') self.feature_dict['overview'] = self.notebook.append_page(clip_page, Gtk.Label('overview')) self.feature_dict['apps'] = self.notebook.append_page(self.apps_page, Gtk.Label()) self.feature_dict['tweaks'] = self.notebook.append_page(tweaks_page, Gtk.Label('tweaks')) self.feature_dict['admins'] = self.notebook.append_page(admins_page, Gtk.Label('admins')) self.feature_dict['janitor'] = self.notebook.append_page(janitor_page, Gtk.Label('janitor')) self.feature_dict['wait'] = self.notebook.append_page(self._crete_wait_page(), Gtk.Label('wait')) self.feature_dict['search'] = self.notebook.append_page(self.search_page, Gtk.Label('search')) # Always show welcome page at first self.mainwindow.connect('realize', self._initialize_ui_states, splash_window) self.back_button.connect('clicked', self.on_back_button_clicked) self.next_button.connect('clicked', self.on_next_button_clicked) tweaks_page.connect('module_selected', self.on_module_selected) self.search_page.connect('module_selected', self.on_module_selected) admins_page.connect('module_selected', self.on_module_selected) self.apps_page.connect('loaded', self.show_apps_page) clip_page.connect('load_module', lambda widget, name: self.do_load_module(name)) clip_page.connect('load_feature', lambda widget, name: self.select_target_feature(name)) self.mainwindow.show() if module: self.do_load_module(module) elif feature: self.select_target_feature(feature) accel_group = Gtk.AccelGroup() self.search_entry.add_accelerator('activate', accel_group, Gdk.KEY_f, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE) self.mainwindow.add_accel_group(accel_group) thread.start_new_thread(self.preload_proxy_cache, ()) def show_apps_page(self, widget): self.notebook.set_current_page(self.feature_dict['apps']) def preload_proxy_cache(self): #This function just called to make sure the cache is loaded as soon as possible proxy.is_package_installed('ubuntu-tweak') def on_search_entry_activate(self, widget): widget.grab_focus() self.on_search_entry_changed(widget) def on_search_entry_changed(self, widget): text = widget.get_text() self.set_current_module(None, None) if text: self.notebook.set_current_page(self.feature_dict['search']) self.search_page.search(text) self.search_entry.set_property('secondary-icon-name', 'edit-clear') else: self.on_feature_button_clicked(getattr(self, '%s_button' % self.current_feature), self.current_feature) self.search_page.clean() self.search_entry.set_property('secondary-icon-name', 'edit-find') def on_search_entry_icon_press(self, widget, icon_pos, event): widget.set_text('') def get_module_and_index(self, name): index = self.loaded_modules[name] return self.modules_index[index], index def select_target_feature(self, text): toggle_button = getattr(self, '%s_button' % text, None) log.info("select_target_feature: %s" % text) if toggle_button: self.current_feature = text toggle_button.set_active(True) def _initialize_ui_states(self, widget, splash_window): self.window_size_setting = GSetting('com.ubuntu-tweak.tweak.window-size') width, height = self.window_size_setting.get_value() if width >= 900 and height >= 506: self.mainwindow.set_default_size(width, height) for feature_button in ('overview_button', 'apps_button', 'admins_button', \ 'tweaks_button', 'janitor_button'): button = getattr(self, feature_button) label = button.get_child().get_label() button.get_child().set_markup('<b>%s</b>' % label) button.get_child().set_use_underline(True) splash_window.destroy() def _crete_wait_page(self): vbox = Gtk.VBox() label = Gtk.Label() label.set_markup("<span size=\"xx-large\">%s</span>" % \ _('Please wait a moment...')) label.set_justify(Gtk.Justification.FILL) vbox.pack_start(label, False, False, 50) hbox = Gtk.HBox() vbox.pack_start(hbox, False, False, 0) vbox.show_all() return vbox def on_mainwindow_destroy(self, widget=None): allocation = widget.get_allocation() self.window_size_setting.set_value((allocation.width, allocation.height)) Gtk.main_quit() try: proxy.exit() except Exception, e: log.error(e)
class FeaturePage(Gtk.ScrolledWindow): __gsignals__ = { 'module_selected': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_STRING,)) } _categories = None _boxes = [] def __str__(self): return '<FeaturePage: %s>' % self._feature def __init__(self, feature_name): GObject.GObject.__init__(self, hscrollbar_policy=Gtk.PolicyType.NEVER, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC) self.set_property('shadow-type', Gtk.ShadowType.NONE) self.set_border_width(12) self._feature = feature_name self._setting = GSetting('com.ubuntu-tweak.tweak.%s' % feature_name) self._categories = {} self._boxes = [] self._box = Gtk.VBox(spacing=6) viewport = Gtk.Viewport() viewport.set_property('shadow-type', Gtk.ShadowType.NONE) viewport.add(self._box) self.add(viewport) self.load_modules() # TODO this will cause Bug #880663 randomly, as current there's no user extension for features, just disable it # self._setting.connect_notify(self.load_modules) self.show_all() def load_modules(self, *args, **kwargs): log.debug("Loading modules...") loader = ModuleLoader(self._feature) self._boxes = [] for child in self._box.get_children(): self._box.remove(child) for category, category_name in loader.get_categories(): modules = loader.get_modules_by_category(category) if modules: module_to_loads = self._setting.get_value() for module in modules: if module.is_user_extension() and module.get_name() not in module_to_loads: modules.remove(module) category_box = CategoryBox(modules=modules, category_name=category_name) self._connect_signals(category_box) self._boxes.append(category_box) self._box.pack_start(category_box, False, False, 0) self.rebuild_boxes() def _connect_signals(self, category_box): for button in category_box.get_buttons(): button.connect('clicked', self.on_button_clicked) def on_button_clicked(self, widget): log.info('Button clicked') module = widget.get_module() self.emit('module_selected', module.get_name()) def rebuild_boxes(self, widget=None, event=None): request = self.get_allocation() ncols = request.width / 164 # 32 + 120 + 6 + 4 width = ncols * (164 + 2 * 4) + 40 if width > request.width: ncols -= 1 pos = 0 children = self._box.get_children() for box in self._boxes: modules = box.get_modules() if len (modules) == 0: if box in children: self._box.remove(box) else: if box not in children: self._box.pack_start(box, False, False, 0) self._box.reorder_child(box, pos) box.rebuild_table(ncols) pos += 1
class JanitorPage(Gtk.VBox, GuiBuilder): (JANITOR_CHECK, JANITOR_ICON, JANITOR_NAME, JANITOR_DISPLAY, JANITOR_PLUGIN, JANITOR_SPINNER_ACTIVE, JANITOR_SPINNER_PULSE) = range(7) (RESULT_CHECK, RESULT_ICON, RESULT_NAME, RESULT_DISPLAY, RESULT_DESC, RESULT_PLUGIN, RESULT_CRUFT) = range(7) max_janitor_view_width = 0 def __init__(self): GObject.GObject.__init__(self) self.scan_tasks = [] self.clean_tasks = [] self.set_border_width(6) GuiBuilder.__init__(self, 'janitorpage.ui') self.autoscan_setting = GSetting('com.ubuntu-tweak.tweak.auto-scan') self.janitor_setting = GSetting('com.ubuntu-tweak.tweak.janitor') self.pack_start(self.vbox1, True, True, 0) self.connect('realize', self.setup_ui_tasks) self.janitor_view.get_selection().connect('changed', self.on_janitor_selection_changed) self.janitor_setting.connect_notify(self.update_model, True) self.show() def is_auto_scan(self): return self.autoscan_button.get_active() def setup_ui_tasks(self, widget): self.janitor_model.set_sort_column_id(self.JANITOR_NAME, Gtk.SortType.ASCENDING) #add janitor columns janitor_column = Gtk.TreeViewColumn() renderer = Gtk.CellRendererToggle() renderer.connect('toggled', self.on_janitor_check_button_toggled) janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'active', self.JANITOR_CHECK) self.janitor_view.append_column(janitor_column) janitor_column = Gtk.TreeViewColumn() renderer = Gtk.CellRendererPixbuf() janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'pixbuf', self.JANITOR_ICON) janitor_column.set_cell_data_func(renderer, self.icon_column_view_func, self.JANITOR_ICON) renderer = Gtk.CellRendererText() renderer.set_property('ellipsize', Pango.EllipsizeMode.MIDDLE) janitor_column.pack_start(renderer, True) janitor_column.add_attribute(renderer, 'markup', self.JANITOR_DISPLAY) renderer = Gtk.CellRendererSpinner() janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'active', self.JANITOR_SPINNER_ACTIVE) janitor_column.add_attribute(renderer, 'pulse', self.JANITOR_SPINNER_PULSE) self.janitor_view.append_column(janitor_column) #end janitor columns #add result columns result_column = Gtk.TreeViewColumn() renderer = Gtk.CellRendererToggle() renderer.connect('toggled', self.on_result_check_renderer_toggled) result_column.pack_start(renderer, False) result_column.add_attribute(renderer, 'active', self.RESULT_CHECK) renderer = Gtk.CellRendererPixbuf() result_column.pack_start(renderer, False) result_column.add_attribute(renderer, 'pixbuf', self.RESULT_ICON) result_column.set_cell_data_func(renderer, self.icon_column_view_func, self.RESULT_ICON) renderer = Gtk.CellRendererText() renderer.set_property('ellipsize', Pango.EllipsizeMode.END) result_column.pack_start(renderer, True) result_column.add_attribute(renderer, 'markup', self.RESULT_DISPLAY) renderer = Gtk.CellRendererText() result_column.pack_start(renderer, False) result_column.add_attribute(renderer, 'text', self.RESULT_DESC) self.result_view.append_column(result_column) #end result columns auto_scan = self.autoscan_setting.get_value() log.info("Auto scan status: %s", auto_scan) self.scan_button.set_visible(not auto_scan) self.autoscan_button.set_active(auto_scan) self.update_model() self._expand_janitor_view() def _expand_janitor_view(self): self.janitor_view.expand_all() if self.max_janitor_view_width: self.janitor_view.set_size_request(self.max_janitor_view_width, -1) def set_busy(self): self.get_parent_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) def unset_busy(self): self.get_parent_window().set_cursor(None) def on_janitor_selection_changed(self, selection): model, iter = selection.get_selected() if iter: if self.janitor_model.iter_has_child(iter): iter = self.janitor_model.iter_children(iter) plugin = model[iter][self.JANITOR_PLUGIN] for row in self.result_model: if row[self.RESULT_PLUGIN] == plugin: self.result_view.get_selection().select_path(row.path) self.result_view.scroll_to_cell(row.path) def on_janitor_check_button_toggled(self, cell, path): iter = self.janitor_model.get_iter(path) for row in self.janitor_model: for child_row in row.iterchildren(): if child_row[self.JANITOR_SPINNER_ACTIVE]: return checked = not self.janitor_model[iter][self.JANITOR_CHECK] if self.janitor_model.iter_has_child(iter): child_iter = self.janitor_model.iter_children(iter) while child_iter: self.janitor_model[child_iter][self.JANITOR_CHECK] = checked child_iter = self.janitor_model.iter_next(child_iter) self.janitor_model[iter][self.JANITOR_CHECK] = checked self._check_child_is_all_the_same(self.janitor_model, iter, self.JANITOR_CHECK, checked) if self.is_auto_scan(): self._auto_scan_cruft(iter, checked) def _update_clean_button_sensitive(self): self.clean_button.set_sensitive(False) for row in self.result_model: for child_row in row.iterchildren(): if child_row[self.RESULT_CHECK]: self.clean_button.set_sensitive(True) break def on_result_check_renderer_toggled(self, cell, path): iter = self.result_model.get_iter(path) checked = self.result_model[iter][self.RESULT_CHECK] if self.result_model.iter_has_child(iter): child_iter = self.result_model.iter_children(iter) while child_iter: self.result_model[child_iter][self.RESULT_CHECK] = not checked child_iter = self.result_model.iter_next(child_iter) self.result_model[iter][self.RESULT_CHECK] = not checked self._check_child_is_all_the_same(self.result_model, iter, self.RESULT_CHECK, not checked) self._update_clean_button_sensitive() def _check_child_is_all_the_same(self, model, iter, column_id, status): iter = model.iter_parent(iter) if iter: child_iter = model.iter_children(iter) while child_iter: if status != model[child_iter][column_id]: model[iter][column_id] = False break child_iter = model.iter_next(child_iter) else: model[iter][column_id] = status def on_scan_button_clicked(self, widget=None): self.result_model.clear() self.clean_button.set_sensitive(False) scan_dict = OrderedDict() for row in self.janitor_model: for child_row in row.iterchildren(): checked = child_row[self.JANITOR_CHECK] scan_dict[child_row.iter] = checked self.set_busy() self.scan_tasks = list(scan_dict.items()) self.do_scan_task() def _auto_scan_cruft(self, iter, checked): self.set_busy() scan_dict = OrderedDict() if self.janitor_model.iter_has_child(iter): log.info('Scan cruft for all plugins') #Scan cruft for children child_iter = self.janitor_model.iter_children(iter) while child_iter: scan_dict[child_iter] = checked child_iter = self.janitor_model.iter_next(child_iter) else: scan_dict[iter] = checked self.scan_tasks = list(scan_dict.items()) for plugin_iter, checked in self.scan_tasks: plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] for row in self.result_model: if row[self.RESULT_PLUGIN] == plugin: self.result_model.remove(row.iter) self.do_scan_task() def do_scan_task(self): plugin_iter, checked = self.scan_tasks.pop(0) plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] plugin.set_data('scan_finished', False) log.debug("do_scan_task for %s for status: %s" % (plugin, checked)) if checked: log.info('Scan cruft for plugin: %s' % plugin.get_name()) iter = self.result_model.append(None, (None, None, plugin.get_title(), "<b>%s</b>" % plugin.get_title(), None, plugin, None)) self.janitor_model[plugin_iter][self.JANITOR_SPINNER_ACTIVE] = True self.janitor_model[plugin_iter][self.JANITOR_SPINNER_PULSE] = 0 self._find_handler = plugin.connect('find_object', self.on_find_object, iter) self._scan_handler = plugin.connect('scan_finished', self.on_scan_finished, iter) self._error_handler = plugin.connect('error', self.on_scan_error, plugin_iter) t = threading.Thread(target=plugin.get_cruft) GObject.timeout_add(50, self._on_spinner_timeout, plugin_iter, t) t.start() else: # Update the janitor title for row in self.janitor_model: for child_row in row.iterchildren(): if child_row[self.JANITOR_PLUGIN] == plugin: child_row[self.JANITOR_DISPLAY] = plugin.get_title() if self.scan_tasks: self.do_scan_task() else: self.unset_busy() def _on_spinner_timeout(self, plugin_iter, thread): plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] finished = plugin.get_data('scan_finished') self.janitor_model[plugin_iter][self.JANITOR_SPINNER_PULSE] += 1 if finished: for handler in (self._find_handler, self._scan_handler): if plugin.handler_is_connected(handler): log.debug("Disconnect the cleaned signal, or it will clean many times") plugin.disconnect(handler) self.janitor_model[plugin_iter][self.JANITOR_SPINNER_ACTIVE] = False for view in (self.janitor_view, self.result_view): view.hide() view.show() thread.join() if len(self.scan_tasks) != 0: self.do_scan_task() else: self.unset_busy() return not finished def on_find_object(self, plugin, cruft, result_iter): Gdk.threads_enter() while Gtk.events_pending(): Gtk.main_iteration() self.result_model.append(result_iter, (False, cruft.get_icon(), cruft.get_name(), cruft.get_name(), cruft.get_size_display(), plugin, cruft)) self.result_view.expand_row(self.result_model.get_path(result_iter), True) Gdk.threads_leave() def on_scan_finished(self, plugin, result, count, size, result_iter): Gdk.threads_enter() plugin.disconnect(self._find_handler) plugin.disconnect(self._scan_handler) plugin.set_data('scan_finished', True) if count == 0: self.result_model.remove(result_iter) else: self.result_model[result_iter][self.RESULT_DISPLAY] = "<b>%s</b>" % plugin.get_summary(count, size) # Update the janitor title for row in self.janitor_model: for child_row in row.iterchildren(): if child_row[self.JANITOR_PLUGIN] == plugin: if count: child_row[self.JANITOR_DISPLAY] = "<b>%s (%d) </b>" % (plugin.get_title(), count) else: child_row[self.JANITOR_DISPLAY] = "%s (%d)" % (plugin.get_title(), count) Gdk.threads_leave() def on_scan_error(self, plugin, error, plugin_iter): Gdk.threads_enter() #TODO deal with the error self.janitor_model[plugin_iter][self.JANITOR_ICON] = icon.get_from_name('error', size=16) plugin.set_data('scan_finished', True) self.scan_tasks = [] Gdk.threads_leave() def on_clean_button_clicked(self, widget): self.plugin_to_run = 0 plugin_dict = OrderedDict() for row in self.result_model: plugin = row[self.RESULT_PLUGIN] cruft_list = [] for child_row in row.iterchildren(): checked = child_row[self.RESULT_CHECK] if checked: cruft = child_row[self.RESULT_CRUFT] cruft_list.append(cruft) if cruft_list: plugin_dict[plugin] = cruft_list self.clean_tasks = list(plugin_dict.items()) self.do_real_clean_task() log.debug("All finished!") def do_real_clean_task(self): plugin, cruft_list = self.clean_tasks.pop(0) log.debug("Call %s to clean cruft" % plugin) self._plugin_handler = plugin.connect('cleaned', self.on_plugin_cleaned) self._error_handler = plugin.connect('error', self.on_clean_error) plugin.clean_cruft(self.get_toplevel(), cruft_list) def on_plugin_cleaned(self, plugin, cleaned): for handler in (self._plugin_handler, self._error_handler): if plugin.handler_is_connected(handler): log.debug("Disconnect the cleaned signal, or it will clean many times") plugin.disconnect(handler) if len(self.clean_tasks) == 0: self.on_scan_button_clicked() else: GObject.timeout_add(300, self.do_real_clean_task) def on_clean_error(self, plugin, error): self.clean_tasks = [] def on_autoscan_button_toggled(self, widget): if widget.get_active(): self.autoscan_setting.set_value(True) self.scan_button.hide() else: self.autoscan_setting.set_value(False) self.scan_button.show() def icon_column_view_func(self, cell_layout, renderer, model, iter, id): if model[iter][id] == None: renderer.set_property("visible", False) else: renderer.set_property("visible", True) def update_model(self, a=None, b=None, expand=False): self.janitor_model.clear() self.result_model.clear() size_list = [] loader = ModuleLoader('janitor') plugin_to_load = self.janitor_setting.get_value() system_text = _('System') iter = self.janitor_model.append(None, (None, icon.get_from_name('ubuntu-logo'), system_text, "<b><big>%s</big></b>" % system_text, None, None, None)) for plugin in loader.get_modules_by_category('system'): if plugin.is_user_extension() and plugin.get_name() not in plugin_to_load: log.debug("User extension: %s not in setting to load" % plugin.get_name()) continue size_list.append(Gtk.Label(label=plugin.get_title()).get_layout().get_pixel_size()[0]) self.janitor_model.append(iter, (False, None, plugin.get_title(), plugin.get_title(), plugin(), None, None)) personal_text = _('Personal') iter = self.janitor_model.append(None, (None, icon.get_from_name('system-users'), personal_text, "<b><big>%s</big></b>" % personal_text, None, None, None)) for plugin in loader.get_modules_by_category('personal'): if plugin.is_user_extension() and plugin.get_name() not in plugin_to_load: log.debug("User extension: %s not in setting to load" % plugin.get_name()) continue size_list.append(Gtk.Label(label=plugin.get_title()).get_layout().get_pixel_size()[0]) self.janitor_model.append(iter, (False, None, plugin.get_title(), plugin.get_title(), plugin(), None, None)) if size_list: self.max_janitor_view_width = max(size_list) + 80 if expand: self._expand_janitor_view()
class PreferencesDialog(GuiBuilder): (CLIP_CHECK, CLIP_ICON, CLIP_NAME) = range(3) (TWEAKS_CHECK, TWEAKS_ICON, TWEAKS_NAME) = range(3) (JANITOR_CHECK, JANITOR_NAME) = range(2) page_dict = {'overview': 0, 'tweaks': 1, 'admins': 2, 'janitor': 3} def __init__(self, parent): GuiBuilder.__init__(self, file_name='preferences.ui') self.preferences_dialog.set_transient_for(parent) self.clips_setting = GSetting('com.ubuntu-tweak.tweak.clips') self.tweaks_setting = GSetting('com.ubuntu-tweak.tweak.tweaks') self.admins_setting = GSetting('com.ubuntu-tweak.tweak.admins') self.janitor_setting = GSetting('com.ubuntu-tweak.janitor.plugins') self.clips_location_setting = GSetting('com.ubuntu-tweak.tweak.last-clip-location') auto_scan_label, auto_scan_switch = WidgetFactory.create("Switch", label=_("Auto scan:"), key='com.ubuntu-tweak.janitor.auto-scan', backend="gsettings") pack = GridPack((auto_scan_label, auto_scan_switch)) self.generic_alignment.add(pack) self.generic_alignment.show_all() def on_clip_toggle_render_toggled(self, cell, path): log.debug("on_clip_toggle_render_toggled") self.on_toggle_renderer_toggled(self.clip_model, path, self.CLIP_CHECK, self.CLIP_NAME, self.clips_setting) def on_tweak_toggle_renderer_toggled(self, cell, path): log.debug("on_tweaks_toggle_render_toggled") self.on_toggle_renderer_toggled(self.tweaks_model, path, self.TWEAKS_CHECK, self.TWEAKS_NAME, self.tweaks_setting) def on_admins_toggle_renderer_toggled(self, cell, path): log.debug("on_admins_toggle_render_toggled") self.on_toggle_renderer_toggled(self.admins_model, path, self.TWEAKS_CHECK, self.TWEAKS_NAME, self.admins_setting) def on_janitor_cell_renderer_toggled(self, cell, path): log.debug("on_admins_toggle_render_toggled") self.on_toggle_renderer_toggled(self.janitor_model, path, self.JANITOR_CHECK, self.JANITOR_NAME, self.janitor_setting) def on_toggle_renderer_toggled(self, model, path, check_id, name_id, setting): iter = model.get_iter(path) checked = not model[iter][check_id] model[iter][check_id] = checked self._do_update_model(model, check_id, name_id, setting) def _do_update_model(self, model, check_id, name_id, setting): model_list = [] for row in model: if row[check_id]: model_list.append(row[name_id]) log.debug("on_clip_toggle_render_toggled: %s" % model_list) setting.set_value(model_list) def run(self, feature='overview'): self._update_clip_model() for _feature in ModuleLoader.default_features: self._update_feature_model(_feature) if feature in self.page_dict: self.preference_notebook.set_current_page(self.page_dict[feature]) return self.preferences_dialog.run() def hide(self): return self.preferences_dialog.hide() def on_move_up_button_clicked(self, widget): model, iter = self.clip_view.get_selection().get_selected() if iter: previous_path = str(int(model.get_string_from_iter(iter)) - 1) if int(previous_path) >= 0: previous_iter = model.get_iter_from_string(previous_path) model.move_before(iter, previous_iter) self._do_update_model(self.clip_model, self.CLIP_CHECK, self.CLIP_NAME, self.clips_setting) def on_move_down_button_clicked(self, widget): model, iter = self.clip_view.get_selection().get_selected() if iter: next_iter = model.iter_next(iter) model.move_after(iter, next_iter) self._do_update_model(self.clip_model, self.CLIP_CHECK, self.CLIP_NAME, self.clips_setting) def on_clip_install_button_clicked(self, widget): self.on_install_extension(_('Choose a clip extension'), Clip, 'clips', self.clips_setting, self._update_clip_model, _('"%s" is not a Clip Extension!')) def on_tweaks_install_button_clicked(self, widget): self.on_install_extension(_('Choose a Tweaks Extension'), TweakModule, 'tweaks', self.tweaks_setting, self._update_feature_model, _('"%s" is not a Tweaks Extension!')) def on_admins_install_button_clicked(self, widget): self.on_install_extension(_('Choose a Admins Extension'), TweakModule, 'admins', self.admins_setting, self._update_feature_model, _('"%s" is not a Admins Extension!')) def on_janitor_install_button_clicked(self, widget): self.on_install_extension(_('Choose a Janitor Extension'), JanitorPlugin, 'janitor', self.janitor_setting, self._update_feature_model, _('"%s" is not a Janitor Extension!')) def on_install_extension(self, dialog_label, klass, feature, setting, update_func, error_message): dialog = Gtk.FileChooserDialog(dialog_label, action=Gtk.FileChooserAction.OPEN, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT)) filter = Gtk.FileFilter() filter.set_name(_('Ubuntu Tweak Extension (*.py, *.tar.gz)')) filter.add_pattern('*.py') filter.add_pattern('*.tar.gz') dialog.add_filter(filter) dialog.set_current_folder(self.clips_location_setting.get_value() or GLib.get_home_dir()) filename = '' install_done = False not_extension = False if dialog.run() == Gtk.ResponseType.ACCEPT: filename = dialog.get_filename() dialog.destroy() if filename: self.clips_location_setting.set_value(os.path.dirname(filename)) log.debug("Start to check the class in %s" % filename) if filename.endswith('.tar.gz'): tar_file = TarFile(filename) if tar_file.is_valid(): tar_file.extract(TEMP_ROOT) #TODO if multi-root if tar_file.get_root_name(): temp_dir = os.path.join(TEMP_ROOT, tar_file.get_root_name()) if ModuleLoader.is_target_class(temp_dir, klass): target = os.path.join(ModuleLoader.get_user_extension_dir(feature), os.path.basename(temp_dir)) copy = True if os.path.exists(target): dialog = QuestionDialog(message=_("Would you like to remove it then install again?"), title=_('"%s" has already installed' % os.path.basename(target))) response = dialog.run() dialog.destroy() if response == Gtk.ResponseType.YES: shutil.rmtree(target) else: copy = False if copy: log.debug("Now copying tree...") shutil.move(temp_dir, target) else: shutil.rmtree(temp_dir) else: not_extension = True else: if ModuleLoader.is_target_class(filename, klass): shutil.copy(filename, ModuleLoader.get_user_extension_dir(feature)) install_done = True else: not_extension = True if install_done: update_func(feature) # To force empty the clips_setting to make load_cips value = setting.get_value() setting.set_value(['']) setting.set_value(value) if not_extension: ErrorDialog(message=error_message % os.path.basename(filename)).launch() def _update_clip_model(self, feature=None): clips = self.clips_setting.get_value() loader = ModuleLoader('clips') self.clip_model.clear() for clip_name in clips: ClipClass = loader.get_module(clip_name) self.clip_model.append((True, ClipClass.get_pixbuf(), ClipClass.get_name())) for name, ClipClass in loader.module_table.items(): if name not in clips: self.clip_model.append((False, ClipClass.get_pixbuf(), ClipClass.get_name())) def _update_feature_model(self, feature): module_list = getattr(self, '%s_setting' % feature).get_value() or [] loader = ModuleLoader(feature, user_only=True) model = getattr(self, '%s_model' % feature) model.clear() for name, klass in loader.module_table.items(): if klass.get_pixbuf(): model.append((name in module_list, klass.get_pixbuf(), klass.get_name())) else: model.append((name in module_list, klass.get_name()))
class JanitorPage(Gtk.VBox, GuiBuilder): (JANITOR_CHECK, JANITOR_ICON, JANITOR_NAME, JANITOR_DISPLAY, JANITOR_PLUGIN, JANITOR_SPINNER_ACTIVE, JANITOR_SPINNER_PULSE) = range(7) (RESULT_CHECK, RESULT_ICON, RESULT_NAME, RESULT_DISPLAY, RESULT_DESC, RESULT_PLUGIN, RESULT_CRUFT) = range(7) max_janitor_view_width = 0 def __init__(self): GObject.GObject.__init__(self) self.scan_tasks = [] self.clean_tasks = [] self._total_count = 0 self.set_border_width(6) GuiBuilder.__init__(self, 'janitorpage.ui') self.autoscan_setting = GSetting('com.ubuntu-tweak.janitor.auto-scan') self.autoscan_setting.connect_notify(self.on_autoscan_button_toggled) self.plugins_setting = GSetting('com.ubuntu-tweak.janitor.plugins') self.view_width_setting = GSetting('com.ubuntu-tweak.janitor.janitor-view-width') self.pack_start(self.vbox1, True, True, 0) self.connect('realize', self.setup_ui_tasks) self.janitor_view.get_selection().connect('changed', self.on_janitor_selection_changed) self.plugins_setting.connect_notify(self.update_model, True) self.show() def on_move_handle(self, widget, gproperty): log.debug("on_move_handle: %d", widget.get_property('position')) self.view_width_setting.set_value(widget.get_property('position')) # cancel the size request, or it will fail to resize # TODO why the first scan will make it fail? self.janitor_view.set_size_request(self.max_janitor_view_width, -1) def is_auto_scan(self): return self.autoscan_setting.get_value() @log_func(log) def on_result_view_row_activated(self, treeview, path, column): iter = self.result_model.get_iter(path) cruft = self.result_model[iter][self.RESULT_CRUFT] display = self.result_model[iter][self.RESULT_DISPLAY] if 'red' in display: plugin = self.result_model[iter][self.RESULT_PLUGIN] error = plugin.get_property('error') self.result_model[iter][self.RESULT_DISPLAY] = '<span color="red"><b>%s</b></span>' % error elif hasattr(cruft, 'get_path'): path = cruft.get_path() if not os.path.isdir(path): path = os.path.dirname(path) os.system("xdg-open '%s' &" % path) def setup_ui_tasks(self, widget): self.janitor_model.set_sort_column_id(self.JANITOR_NAME, Gtk.SortType.ASCENDING) #add janitor columns janitor_column = Gtk.TreeViewColumn() renderer = Gtk.CellRendererToggle() renderer.connect('toggled', self.on_janitor_check_button_toggled) janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'active', self.JANITOR_CHECK) self.janitor_view.append_column(janitor_column) janitor_column = Gtk.TreeViewColumn() renderer = Gtk.CellRendererPixbuf() janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'pixbuf', self.JANITOR_ICON) janitor_column.set_cell_data_func(renderer, self.icon_column_view_func, self.JANITOR_ICON) renderer = Gtk.CellRendererText() renderer.set_property('ellipsize', Pango.EllipsizeMode.END) janitor_column.pack_start(renderer, True) janitor_column.add_attribute(renderer, 'markup', self.JANITOR_DISPLAY) renderer = Gtk.CellRendererSpinner() janitor_column.pack_start(renderer, False) janitor_column.add_attribute(renderer, 'active', self.JANITOR_SPINNER_ACTIVE) janitor_column.add_attribute(renderer, 'pulse', self.JANITOR_SPINNER_PULSE) self.janitor_view.append_column(janitor_column) #end janitor columns #new result columns result_display_renderer = self.builder.get_object('result_display_renderer') result_display_renderer.set_property('ellipsize', Pango.EllipsizeMode.END) result_icon_renderer= self.builder.get_object('result_icon_renderer') self.result_column.set_cell_data_func(result_icon_renderer, self.icon_column_view_func, self.RESULT_ICON) #end new result columns auto_scan = self.autoscan_setting.get_value() log.info("Auto scan status: %s", auto_scan) self.scan_button.set_visible(not auto_scan) self.update_model() self._expand_janitor_view() self.hpaned1.connect('notify::position', self.on_move_handle) def _expand_janitor_view(self): self.janitor_view.expand_all() left_view_width = self.view_width_setting.get_value() log.debug("left_view_width is: %d, max_janitor_view_width is: %d" % (left_view_width, self.max_janitor_view_width)) if left_view_width: self.janitor_view.set_size_request(left_view_width, -1) elif self.max_janitor_view_width: self.janitor_view.set_size_request(self.max_janitor_view_width, -1) def set_busy(self): self.get_parent_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) def unset_busy(self): self.get_parent_window().set_cursor(None) def on_janitor_selection_changed(self, selection): model, iter = selection.get_selected() if iter: if self.janitor_model.iter_has_child(iter): iter = self.janitor_model.iter_children(iter) plugin = model[iter][self.JANITOR_PLUGIN] for row in self.result_model: if row[self.RESULT_PLUGIN] == plugin: self.result_view.get_selection().select_path(row.path) log.debug("scroll_to_cell: %s" % row.path) self.result_view.scroll_to_cell(row.path) def _is_scanning_or_cleaning(self): for row in self.janitor_model: for child_row in row.iterchildren(): if child_row[self.JANITOR_SPINNER_ACTIVE]: return True else: return False def on_janitor_check_button_toggled(self, cell, path): self.result_view.show() self.happy_box.hide() iter = self.janitor_model.get_iter(path) if self._is_scanning_or_cleaning(): return checked = not self.janitor_model[iter][self.JANITOR_CHECK] if self.janitor_model.iter_has_child(iter): child_iter = self.janitor_model.iter_children(iter) while child_iter: self.janitor_model[child_iter][self.JANITOR_CHECK] = checked child_iter = self.janitor_model.iter_next(child_iter) self.janitor_model[iter][self.JANITOR_CHECK] = checked self._check_child_is_all_the_same(self.janitor_model, iter, self.JANITOR_CHECK, checked) if self.is_auto_scan(): self._auto_scan_cruft(iter, checked) def _update_clean_button_sensitive(self): self.clean_button.set_sensitive(False) for row in self.result_model: for child_row in row.iterchildren(): if child_row[self.RESULT_CHECK]: self.clean_button.set_sensitive(True) break def on_result_check_renderer_toggled(self, cell, path): iter = self.result_model.get_iter(path) checked = self.result_model[iter][self.RESULT_CHECK] if self._is_scanning_or_cleaning(): return if self.result_model.iter_has_child(iter): child_iter = self.result_model.iter_children(iter) while child_iter: self.result_model[child_iter][self.RESULT_CHECK] = not checked child_iter = self.result_model.iter_next(child_iter) self.result_model[iter][self.RESULT_CHECK] = not checked self._check_child_is_all_the_same(self.result_model, iter, self.RESULT_CHECK, not checked) self._update_clean_button_sensitive() def _check_child_is_all_the_same(self, model, iter, column_id, status): iter = model.iter_parent(iter) if iter: child_iter = model.iter_children(iter) while child_iter: if status != model[child_iter][column_id]: model[iter][column_id] = False break child_iter = model.iter_next(child_iter) else: model[iter][column_id] = status def on_scan_button_clicked(self, widget=None): self.result_model.clear() self.clean_button.set_sensitive(False) scan_dict = OrderedDict() for row in self.janitor_model: for child_row in row.iterchildren(): checked = child_row[self.JANITOR_CHECK] scan_dict[child_row.iter] = checked self.scan_tasks = list(scan_dict.items()) self._total_count = 0 self.result_view.show() self.happy_box.hide() self.set_busy() self.do_scan_task() def _auto_scan_cruft(self, iter, checked): self.set_busy() scan_dict = OrderedDict() if self.janitor_model.iter_has_child(iter): log.info('Scan cruft for all plugins') #Scan cruft for children child_iter = self.janitor_model.iter_children(iter) while child_iter: scan_dict[child_iter] = checked child_iter = self.janitor_model.iter_next(child_iter) else: scan_dict[iter] = checked self.scan_tasks = list(scan_dict.items()) for plugin_iter, checked in self.scan_tasks: plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] for row in self.result_model: if row[self.RESULT_PLUGIN] == plugin: self.result_model.remove(row.iter) self.do_scan_task() def do_scan_task(self): plugin_iter, checked = self.scan_tasks.pop(0) plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] plugin.set_property('scan_finished', False) log.debug("do_scan_task for %s for status: %s" % (plugin, checked)) if checked: log.info('Scan cruft for plugin: %s' % plugin.get_name()) iter = self.result_model.append(None, (None, None, plugin.get_title(), '<b>%s</b>' % _('Scanning cruft for "%s"...') % plugin.get_title(), None, plugin, None)) self.janitor_model[plugin_iter][self.JANITOR_SPINNER_ACTIVE] = True self.janitor_model[plugin_iter][self.JANITOR_SPINNER_PULSE] = 0 self.janitor_view.scroll_to_cell(self.janitor_model.get_path(plugin_iter)) self._find_handler = plugin.connect('find_object', self.on_find_object, (plugin_iter, iter)) self._scan_handler = plugin.connect('scan_finished', self.on_scan_finished, (plugin_iter, iter)) self._error_handler = plugin.connect('scan_error', self.on_scan_error, (plugin_iter, iter)) t = threading.Thread(target=plugin.get_cruft) GObject.timeout_add(50, self._on_spinner_timeout, plugin_iter, t) t.start() else: # Update the janitor title for row in self.janitor_model: for child_row in row.iterchildren(): if child_row[self.JANITOR_PLUGIN] == plugin: child_row[self.JANITOR_DISPLAY] = plugin.get_title() if self.scan_tasks: self.do_scan_task() else: if self._total_count == 0: self.result_view.hide() self.happy_box.show() else: self.result_view.show() self.happy_box.hide() self.unset_busy() def _on_spinner_timeout(self, plugin_iter, thread): plugin = self.janitor_model[plugin_iter][self.JANITOR_PLUGIN] finished = plugin.get_property('scan_finished') self.janitor_model[plugin_iter][self.JANITOR_SPINNER_PULSE] += 1 if finished: for handler in (self._find_handler, self._scan_handler, self._error_handler): if plugin.handler_is_connected(handler): log.debug("Disconnect the cleaned signal, or it will clean many times: %s" % plugin) plugin.disconnect(handler) self.janitor_model[plugin_iter][self.JANITOR_SPINNER_ACTIVE] = False thread.join() if len(self.scan_tasks) != 0: log.debug("Pending scan tasks: %d" % len(self.scan_tasks)) self.do_scan_task() else: log.debug("total_count is: %d" % self._total_count) if self._total_count == 0: self.result_view.hide() self.happy_box.show() else: self.result_view.show() self.happy_box.hide() self.unset_busy() return not finished @post_ui def on_find_object(self, plugin, cruft, count, iters): while Gtk.events_pending(): Gtk.main_iteration() plugin_iter, result_iter = iters self.result_model.append(result_iter, (False, cruft.get_icon(), cruft.get_name(), cruft.get_name(), cruft.get_size_display(), plugin, cruft)) self.result_view.expand_row(self.result_model.get_path(result_iter), True) # Update the janitor title if count: self.janitor_model[plugin_iter][self.JANITOR_DISPLAY] = "<b>[%d] %s</b>" % (count, plugin.get_title()) else: self.janitor_model[plugin_iter][self.JANITOR_DISPLAY] = "[0] %s" % plugin.get_title() @post_ui def on_scan_finished(self, plugin, result, count, size, iters): plugin.disconnect(self._find_handler) plugin.disconnect(self._scan_handler) plugin.set_property('scan_finished', True) plugin_iter, result_iter = iters if count == 0: self.result_model.remove(result_iter) else: self.result_model[result_iter][self.RESULT_DISPLAY] = "<b>%s</b>" % plugin.get_summary(count) if size != 0: self.result_model[result_iter][self.RESULT_DESC] = "<b>%s</b>" % filesizeformat(size) # Update the janitor title self._total_count += count if count: self.janitor_model[plugin_iter][self.JANITOR_DISPLAY] = "<b>[%d] %s</b>" % (count, plugin.get_title()) self.result_view.collapse_row(self.result_model.get_path(result_iter)) else: self.janitor_model[plugin_iter][self.JANITOR_DISPLAY] = "[0] %s" % plugin.get_title() @post_ui def on_scan_error(self, plugin, error, iters): plugin_iter, result_iter = iters self.janitor_model[plugin_iter][self.JANITOR_ICON] = icon.get_from_name('error', size=16) self.result_model[result_iter][self.RESULT_DISPLAY] = '<span color="red"><b>%s</b></span>' % _('Scan error for "%s", double-click to see details') % plugin.get_title() plugin.set_property('scan_finished', True) plugin.set_property('error', error) @inline_callbacks def on_clean_button_clicked(self, widget): '''plugin_dict: {plugin: {cruft: iter}}''' try: yield PolkitAction(PK_ACTION_CLEAN).do_authenticate() except Exception, e: log.debug(e) return self.plugin_to_run = 0 self.set_busy() self.clean_button.set_sensitive(False) plugin_dict = OrderedDict() for row in self.result_model: plugin = row[self.RESULT_PLUGIN] cruft_dict = OrderedDict() for child_row in row.iterchildren(): checked = child_row[self.RESULT_CHECK] if checked: cruft_dict[child_row[self.RESULT_CRUFT]] = child_row.iter if cruft_dict: plugin_dict[plugin] = cruft_dict self.clean_tasks = list(plugin_dict.items()) self.do_real_clean_task() log.debug("All finished!")
class FeaturePage(Gtk.ScrolledWindow): __gsignals__ = {"module_selected": (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_STRING,))} _categories = None _boxes = [] def __init__(self, feature_name): GObject.GObject.__init__( self, shadow_type=Gtk.ShadowType.NONE, hscrollbar_policy=Gtk.PolicyType.NEVER, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC, ) self.set_border_width(12) self._feature = feature_name self._setting = GSetting("com.ubuntu-tweak.tweak.%s" % feature_name) self._categories = {} self._boxes = [] self._box = Gtk.VBox(spacing=6) viewport = Gtk.Viewport(shadow_type=Gtk.ShadowType.NONE) viewport.add(self._box) self.add(viewport) self.load_modules() self.connect("draw", self.rebuild_boxes) self._setting.connect_notify(self.load_modules, True) self.show_all() def load_modules(self, a=None, b=None, remove=False): log.debug("Load modules, remove: %s" % remove) loader = ModuleLoader(self._feature) if remove: self._boxes = [] for child in self._box.get_children(): self._box.remove(child) for category, category_name in loader.get_categories(): modules = loader.get_modules_by_category(category) if modules: module_to_loads = self._setting.get_value() for module in modules: if module.is_user_extension() and module.get_name() not in module_to_loads: modules.remove(module) category_box = CategoryBox(modules=modules, category_name=category_name) self._connect_signals(category_box) self._boxes.append(category_box) self._box.pack_start(category_box, False, False, 0) if remove: self.rebuild_boxes() def _connect_signals(self, category_box): for button in category_box.get_buttons(): button.connect("clicked", self.on_button_clicked) def on_button_clicked(self, widget): log.info("Button clicked") module = widget.get_module() self.emit("module_selected", module.get_name()) def rebuild_boxes(self, widget=None, event=None): request = self.get_allocation() ncols = request.width / 164 # 32 + 120 + 6 + 4 width = ncols * (164 + 2 * 4) + 40 if width > request.width: ncols -= 1 pos = 0 last_box = None children = self._box.get_children() for box in self._boxes: modules = box.get_modules() if len(modules) == 0: if box in children: self._box.remove(box) else: if box not in children: self._box.pack_start(box, False, False, 0) self._box.reorder_child(box, pos) box.rebuild_table(ncols) pos += 1 last_box = box