CoverArt Browser
v2.0
Browse your cover-art albums in Rhythmbox
|
00001 # -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- 00002 # 00003 # IMPORTANT - WHILST THIS MODULE IS USED BY SEVERAL OTHER PLUGINS 00004 # THE MASTER AND MOST UP-TO-DATE IS FOUND IN THE COVERART BROWSER 00005 # PLUGIN - https://github.com/fossfreedom/coverart-browser 00006 # PLEASE SUBMIT CHANGES BACK TO HELP EXPAND THIS API 00007 # 00008 # Copyright (C) 2012 - fossfreedom 00009 # Copyright (C) 2012 - Agustin Carrasco 00010 # 00011 # This program is free software; you can redistribute it and/or modify 00012 # it under the terms of the GNU General Public License as published by 00013 # the Free Software Foundation; either version 2, or (at your option) 00014 # any later version. 00015 # 00016 # This program is distributed in the hope that it will be useful, 00017 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 # GNU General Public License for more details. 00020 # 00021 # You should have received a copy of the GNU General Public License 00022 # along with this program; if not, write to the Free Software 00023 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 00024 00025 import sys 00026 import xml.etree.ElementTree as ET 00027 00028 from gi.repository import Gtk 00029 from gi.repository import Gio 00030 from gi.repository import GLib 00031 from gi.repository import GObject 00032 from gi.repository import RB 00033 00034 import rb 00035 00036 00037 def pygobject_version(): 00038 ''' 00039 returns float of the major and minor parts of a pygobject version 00040 e.g. version (3, 9, 5) return float(3.9) 00041 ''' 00042 to_number = lambda t: ".".join(str(v) for v in t) 00043 00044 str_version = to_number(GObject.pygobject_version) 00045 00046 return float(str_version.rsplit('.', 1)[0]) 00047 00048 00049 def compare_pygobject_version(version): 00050 ''' 00051 return True if version is less than pygobject_version 00052 i.e. 3.9 < 3.11 00053 ''' 00054 to_number = lambda t: ".".join(str(v) for v in t) 00055 00056 str_version = to_number(GObject.pygobject_version) 00057 00058 split = str_version.rsplit('.', 2) 00059 split_compare = version.rsplit('.', 2) 00060 00061 if int(split_compare[0]) < int(split[0]): 00062 return True 00063 00064 if int(split_compare[1]) < int(split[1]): 00065 return True 00066 00067 return False 00068 00069 00070 PYVER = sys.version_info[0] 00071 00072 if PYVER >= 3: 00073 import urllib.request, urllib.parse, urllib.error 00074 else: 00075 import urllib 00076 from urlparse import urlparse as rb2urlparse 00077 00078 if PYVER >= 3: 00079 import http.client 00080 else: 00081 import httplib 00082 00083 00084 def responses(): 00085 if PYVER >= 3: 00086 return http.client.responses 00087 else: 00088 return httplib.responses 00089 00090 00091 def unicodestr(param, charset): 00092 if PYVER >= 3: 00093 return param #str(param, charset) 00094 else: 00095 return unicode(param, charset) 00096 00097 00098 def unicodeencode(param, charset): 00099 if PYVER >= 3: 00100 return param #str(param).encode(charset) 00101 else: 00102 return unicode(param).encode(charset) 00103 00104 00105 def unicodedecode(param, charset): 00106 if PYVER >= 3: 00107 return param 00108 else: 00109 return param.decode(charset) 00110 00111 00112 def urlparse(uri): 00113 if PYVER >= 3: 00114 return urllib.parse.urlparse(uri) 00115 else: 00116 return rb2urlparse(uri) 00117 00118 00119 def url2pathname(url): 00120 if PYVER >= 3: 00121 return urllib.request.url2pathname(url) 00122 else: 00123 return urllib.url2pathname(url) 00124 00125 00126 def urlopen(filename): 00127 if PYVER >= 3: 00128 return urllib.request.urlopen(filename) 00129 else: 00130 return urllib.urlopen(filename) 00131 00132 00133 def pathname2url(filename): 00134 if PYVER >= 3: 00135 return urllib.request.pathname2url(filename) 00136 else: 00137 return urllib.pathname2url(filename) 00138 00139 00140 def unquote(uri): 00141 if PYVER >= 3: 00142 return urllib.parse.unquote(uri) 00143 else: 00144 return urllib.unquote(uri) 00145 00146 00147 def quote(uri, safe=None): 00148 if PYVER >= 3: 00149 if safe: 00150 return urllib.parse.quote(uri, safe=safe) 00151 else: 00152 return urllib.parse.quote(uri) 00153 else: 00154 if safe: 00155 return urllib.quote(uri, safe=safe) 00156 else: 00157 return urllib.quote(uri) 00158 00159 00160 def quote_plus(uri): 00161 if PYVER >= 3: 00162 return urllib.parse.quote_plus(uri) 00163 else: 00164 return urllib.quote_plus(uri) 00165 00166 00167 def is_rb3(*args): 00168 if hasattr(RB.Shell.props, 'ui_manager'): 00169 return False 00170 else: 00171 return True 00172 00173 00174 class Menu(GObject.Object): 00175 ''' 00176 Menu object used to create window popup menus 00177 ''' 00178 __gsignals__ = { 00179 'pre-popup': (GObject.SIGNAL_RUN_LAST, None, ()) 00180 } 00181 00182 def __init__(self, plugin, shell): 00183 ''' 00184 Initializes the menu. 00185 ''' 00186 super(Menu, self).__init__() 00187 self.plugin = plugin 00188 self.shell = shell 00189 self._unique_num = 0 00190 00191 self._rbmenu_items = {} 00192 self._rbmenu_objects = {} 00193 00194 def add_menu_item(self, menubar, section_name, action): 00195 ''' 00196 add a new menu item to the popup 00197 :param menubar: `str` is the name GtkMenu (or ignored for RB2.99+) 00198 :param section_name: `str` is the name of the section to add the item to (RB2.99+) 00199 :param action: `Action` to associate with the menu item 00200 ''' 00201 return self.insert_menu_item(menubar, section_name, -1, action) 00202 00203 def insert_menu_item(self, menubar, section_name, position, action): 00204 ''' 00205 add a new menu item to the popup 00206 :param menubar: `str` is the name GtkMenu (or ignored for RB2.99+) 00207 :param section_name: `str` is the name of the section to add the item to (RB2.99+) 00208 :param position: `int` position to add to GtkMenu (ignored for RB2.99+) 00209 :param action: `Action` to associate with the menu item 00210 ''' 00211 label = action.label 00212 00213 if is_rb3(self.shell): 00214 app = self.shell.props.application 00215 item = Gio.MenuItem() 00216 action.associate_menuitem(item) 00217 item.set_label(label) 00218 00219 if not section_name in self._rbmenu_items: 00220 self._rbmenu_items[section_name] = [] 00221 self._rbmenu_items[section_name].append(label) 00222 00223 app.add_plugin_menu_item(section_name, label, item) 00224 else: 00225 item = Gtk.MenuItem(label=label) 00226 action.associate_menuitem(item) 00227 self._rbmenu_items[label] = item 00228 bar = self.get_menu_object(menubar) 00229 00230 if position == -1: 00231 bar.append(item) 00232 else: 00233 bar.insert(item, position) 00234 bar.show_all() 00235 uim = self.shell.props.ui_manager 00236 uim.ensure_update() 00237 00238 return item 00239 00240 def insert_separator(self, menubar, at_position): 00241 ''' 00242 add a separator to the popup (only required for RB2.98 and earlier) 00243 :param menubar: `str` is the name GtkMenu (or ignored for RB2.99+) 00244 :param position: `int` position to add to GtkMenu (ignored for RB2.99+) 00245 ''' 00246 if not is_rb3(self.shell): 00247 menu_item = Gtk.SeparatorMenuItem().new() 00248 menu_item.set_visible(True) 00249 self._rbmenu_items['separator' + str(self._unique_num)] = menu_item 00250 self._unique_num = self._unique_num + 1 00251 bar = self.get_menu_object(menubar) 00252 bar.insert(menu_item, at_position) 00253 bar.show_all() 00254 uim = self.shell.props.ui_manager 00255 uim.ensure_update() 00256 00257 def remove_menu_items(self, menubar, section_name): 00258 ''' 00259 utility function to remove all menuitems associated with the menu section 00260 :param menubar: `str` is the name of the GtkMenu containing the menu items (ignored for RB2.99+) 00261 :param section_name: `str` is the name of the section containing the menu items (for RB2.99+ only) 00262 ''' 00263 if is_rb3(self.shell): 00264 if not section_name in self._rbmenu_items: 00265 return 00266 00267 app = self.shell.props.application 00268 00269 for menu_item in self._rbmenu_items[section_name]: 00270 app.remove_plugin_menu_item(section_name, menu_item) 00271 00272 if self._rbmenu_items[section_name]: 00273 del self._rbmenu_items[section_name][:] 00274 00275 else: 00276 00277 if not self._rbmenu_items: 00278 return 00279 00280 uim = self.shell.props.ui_manager 00281 bar = self.get_menu_object(menubar) 00282 00283 for menu_item in self._rbmenu_items: 00284 bar.remove(self._rbmenu_items[menu_item]) 00285 00286 bar.show_all() 00287 uim.ensure_update() 00288 00289 def load_from_file(self, rb2_ui_filename, rb3_ui_filename): 00290 ''' 00291 utility function to load the menu structure 00292 :param rb2_ui_filename: `str` RB2.98 and below UI file 00293 :param rb3_ui_filename: `str` RB2.99 and higher UI file 00294 ''' 00295 self.builder = Gtk.Builder() 00296 try: 00297 from coverart_browser_prefs import CoverLocale 00298 00299 cl = CoverLocale() 00300 00301 self.builder.set_translation_domain(cl.Locale.LOCALE_DOMAIN) 00302 except: 00303 pass 00304 00305 if is_rb3(self.shell): 00306 ui_filename = rb3_ui_filename 00307 else: 00308 ui_filename = rb2_ui_filename 00309 00310 self.ui_filename = ui_filename 00311 00312 self.builder.add_from_file(rb.find_plugin_file(self.plugin, 00313 ui_filename)) 00314 00315 def _connect_rb3_signals(self, signals): 00316 def _menu_connect(action_name, func): 00317 action = Gio.SimpleAction(name=action_name) 00318 action.connect('activate', func) 00319 action.set_enabled(True) 00320 self.shell.props.window.add_action(action) 00321 00322 for key, value in signals.items(): 00323 _menu_connect(key, value) 00324 00325 def _connect_rb2_signals(self, signals): 00326 def _menu_connect(menu_item_name, func): 00327 menu_item = self.get_menu_object(menu_item_name) 00328 menu_item.connect('activate', func) 00329 00330 for key, value in signals.items(): 00331 _menu_connect(key, value) 00332 00333 def connect_signals(self, signals): 00334 ''' 00335 connect all signal handlers with their menuitem counterparts 00336 :param signals: `dict` key is the name of the menuitem 00337 and value is the function callback when the menu is activated 00338 ''' 00339 if is_rb3(self.shell): 00340 self._connect_rb3_signals(signals) 00341 else: 00342 self._connect_rb2_signals(signals) 00343 00344 def get_gtkmenu(self, source, popup_name): 00345 ''' 00346 utility function to obtain the GtkMenu from the menu UI file 00347 :param popup_name: `str` is the name menu-id in the UI file 00348 ''' 00349 if popup_name in self._rbmenu_objects: 00350 return self._rbmenu_objects[popup_name] 00351 item = self.builder.get_object(popup_name) 00352 00353 if is_rb3(self.shell): 00354 app = self.shell.props.application 00355 app.link_shared_menus(item) 00356 popup_menu = Gtk.Menu.new_from_model(item) 00357 popup_menu.attach_to_widget(source, None) 00358 else: 00359 popup_menu = item 00360 00361 self._rbmenu_objects[popup_name] = popup_menu 00362 00363 return popup_menu 00364 00365 def get_menu_object(self, menu_name_or_link): 00366 ''' 00367 utility function returns the GtkMenuItem/Gio.MenuItem 00368 :param menu_name_or_link: `str` to search for in the UI file 00369 ''' 00370 if menu_name_or_link in self._rbmenu_objects: 00371 return self._rbmenu_objects[menu_name_or_link] 00372 item = self.builder.get_object(menu_name_or_link) 00373 if is_rb3(self.shell): 00374 if item: 00375 popup_menu = item 00376 else: 00377 app = self.shell.props.application 00378 popup_menu = app.get_plugin_menu(menu_name_or_link) 00379 else: 00380 popup_menu = item 00381 print(menu_name_or_link) 00382 self._rbmenu_objects[menu_name_or_link] = popup_menu 00383 00384 return popup_menu 00385 00386 def set_sensitive(self, menu_or_action_item, enable): 00387 ''' 00388 utility function to enable/disable a menu-item 00389 :param menu_or_action_item: `GtkMenuItem` or `Gio.SimpleAction` 00390 that is to be enabled/disabled 00391 :param enable: `bool` value to enable/disable 00392 ''' 00393 00394 if is_rb3(self.shell): 00395 item = self.shell.props.window.lookup_action(menu_or_action_item) 00396 item.set_enabled(enable) 00397 else: 00398 item = self.get_menu_object(menu_or_action_item) 00399 item.set_sensitive(enable) 00400 00401 def popup(self, source, menu_name, button, time): 00402 ''' 00403 utility function to show the popup menu 00404 ''' 00405 self.emit('pre-popup') 00406 menu = self.get_gtkmenu(source, menu_name) 00407 menu.popup(None, None, None, None, button, time) 00408 00409 00410 class ActionGroup(object): 00411 ''' 00412 container for all Actions used to associate with menu items 00413 ''' 00414 00415 # action_state 00416 STANDARD = 0 00417 TOGGLE = 1 00418 00419 def __init__(self, shell, group_name): 00420 ''' 00421 constructor 00422 :param shell: `RBShell` 00423 :param group_name: `str` unique name for the object to create 00424 ''' 00425 self.group_name = group_name 00426 self.shell = shell 00427 00428 self._actions = {} 00429 00430 if is_rb3(self.shell): 00431 self.actiongroup = Gio.SimpleActionGroup() 00432 else: 00433 self.actiongroup = Gtk.ActionGroup(group_name) 00434 uim = self.shell.props.ui_manager 00435 uim.insert_action_group(self.actiongroup) 00436 00437 @property 00438 def name(self): 00439 return self.group_name 00440 00441 def remove_actions(self): 00442 ''' 00443 utility function to remove all actions associated with the ActionGroup 00444 ''' 00445 for action in self.actiongroup.list_actions(): 00446 self.actiongroup.remove_action(action) 00447 00448 def get_action(self, action_name): 00449 ''' 00450 utility function to obtain the Action from the ActionGroup 00451 00452 :param action_name: `str` is the Action unique name 00453 ''' 00454 return self._actions[action_name] 00455 00456 def add_action_with_accel(self, func, action_name, accel, **args): 00457 ''' 00458 Creates an Action with an accelerator and adds it to the ActionGroup 00459 00460 :param func: function callback used when user activates the action 00461 :param action_name: `str` unique name to associate with an action 00462 :param accel: `str` accelerator 00463 :param args: dict of arguments - this is passed to the function callback 00464 00465 Notes: 00466 see notes for add_action 00467 ''' 00468 args['accel'] = accel 00469 return self.add_action(func, action_name, **args) 00470 00471 def add_action(self, func, action_name, **args): 00472 ''' 00473 Creates an Action and adds it to the ActionGroup 00474 00475 :param func: function callback used when user activates the action 00476 :param action_name: `str` unique name to associate with an action 00477 :param args: dict of arguments - this is passed to the function callback 00478 00479 Notes: 00480 key value of "label" is the visual menu label to display 00481 key value of "action_type" is the RB2.99 Gio.Action type ("win" or "app") 00482 by default it assumes all actions are "win" type 00483 key value of "action_state" determines what action state to create 00484 ''' 00485 if 'label' in args: 00486 label = args['label'] 00487 else: 00488 label = action_name 00489 00490 if 'accel' in args: 00491 accel = args['accel'] 00492 else: 00493 accel = None 00494 00495 state = ActionGroup.STANDARD 00496 if 'action_state' in args: 00497 state = args['action_state'] 00498 00499 if is_rb3(self.shell): 00500 if state == ActionGroup.TOGGLE: 00501 action = Gio.SimpleAction.new_stateful(action_name, None, 00502 GLib.Variant('b', False)) 00503 else: 00504 action = Gio.SimpleAction.new(action_name, None) 00505 00506 action_type = 'win' 00507 if 'action_type' in args: 00508 if args['action_type'] == 'app': 00509 action_type = 'app' 00510 00511 app = Gio.Application.get_default() 00512 00513 if action_type == 'app': 00514 app.add_action(action) 00515 else: 00516 self.shell.props.window.add_action(action) 00517 self.actiongroup.add_action(action) 00518 00519 if accel: 00520 app.add_accelerator(accel, action_type + "." + action_name, None) 00521 else: 00522 if 'stock_id' in args: 00523 stock_id = args['stock_id'] 00524 else: 00525 stock_id = Gtk.STOCK_CLEAR 00526 00527 if state == ActionGroup.TOGGLE: 00528 action = Gtk.ToggleAction(label=label, 00529 name=action_name, 00530 tooltip='', stock_id=stock_id) 00531 else: 00532 action = Gtk.Action(label=label, 00533 name=action_name, 00534 tooltip='', stock_id=stock_id) 00535 00536 if accel: 00537 self.actiongroup.add_action_with_accel(action, accel) 00538 else: 00539 self.actiongroup.add_action(action) 00540 00541 act = Action(self.shell, action) 00542 act.connect('activate', func, args) 00543 00544 act.label = label 00545 act.accel = accel 00546 00547 self._actions[action_name] = act 00548 00549 return act 00550 00551 00552 class ApplicationShell(object): 00553 ''' 00554 Unique class that mirrors RB.Application & RB.Shell menu functionality 00555 ''' 00556 # storage for the instance reference 00557 __instance = None 00558 00559 class __impl: 00560 """ Implementation of the singleton interface """ 00561 00562 def __init__(self, shell): 00563 self.shell = shell 00564 00565 if is_rb3(self.shell): 00566 self._uids = {} 00567 else: 00568 self._uids = [] 00569 00570 self._action_groups = {} 00571 00572 def insert_action_group(self, action_group): 00573 ''' 00574 Adds an ActionGroup to the ApplicationShell 00575 00576 :param action_group: `ActionGroup` to add 00577 ''' 00578 self._action_groups[action_group.name] = action_group 00579 00580 def lookup_action(self, action_group_name, action_name, action_type='app'): 00581 ''' 00582 looks up (finds) an action created by another plugin. If found returns 00583 an Action or None if no matching Action. 00584 00585 :param action_group_name: `str` is the Gtk.ActionGroup name (ignored for RB2.99+) 00586 :param action_name: `str` unique name for the action to look for 00587 :param action_type: `str` RB2.99+ action type ("win" or "app") 00588 ''' 00589 00590 if is_rb3(self.shell): 00591 if action_type == "app": 00592 action = self.shell.props.application.lookup_action(action_name) 00593 else: 00594 action = self.shell.props.window.lookup_action(action_name) 00595 else: 00596 uim = self.shell.props.ui_manager 00597 ui_actiongroups = uim.get_action_groups() 00598 00599 actiongroup = None 00600 for actiongroup in ui_actiongroups: 00601 if actiongroup.get_name() == action_group_name: 00602 break 00603 00604 action = None 00605 if actiongroup: 00606 action = actiongroup.get_action(action_name) 00607 00608 if action: 00609 return Action(self.shell, action) 00610 else: 00611 return None 00612 00613 def add_app_menuitems(self, ui_string, group_name, menu='tools'): 00614 ''' 00615 utility function to add application menu items. 00616 00617 For RB2.99 all application menu items are added to the "tools" section of the 00618 application menu. All Actions are assumed to be of action_type "app". 00619 00620 For RB2.98 or less, it is added however the UI_MANAGER string 00621 is defined. 00622 00623 :param ui_string: `str` is the Gtk UI definition. There is not an 00624 equivalent UI definition in RB2.99 but we can parse out menu items since 00625 this string is in XML format 00626 00627 :param group_name: `str` unique name of the ActionGroup to add menu items to 00628 :param menu: `str` RB2.99 menu section to add to - nominally either 00629 'tools' or 'view' 00630 ''' 00631 if is_rb3(self.shell): 00632 root = ET.fromstring(ui_string) 00633 for elem in root.findall(".//menuitem"): 00634 action_name = elem.attrib['action'] 00635 item_name = elem.attrib['name'] 00636 00637 group = self._action_groups[group_name] 00638 act = group.get_action(action_name) 00639 00640 item = Gio.MenuItem() 00641 item.set_detailed_action('app.' + action_name) 00642 item.set_label(act.label) 00643 item.set_attribute_value("accel", GLib.Variant("s", act.accel)) 00644 app = Gio.Application.get_default() 00645 index = menu + action_name 00646 app.add_plugin_menu_item(menu, 00647 index, item) 00648 self._uids[index] = menu 00649 else: 00650 uim = self.shell.props.ui_manager 00651 self._uids.append(uim.add_ui_from_string(ui_string)) 00652 uim.ensure_update() 00653 00654 def add_browser_menuitems(self, ui_string, group_name): 00655 ''' 00656 utility function to add popup menu items to existing browser popups 00657 00658 For RB2.99 all menu items are are assumed to be of action_type "win". 00659 00660 For RB2.98 or less, it is added however the UI_MANAGER string 00661 is defined. 00662 00663 :param ui_string: `str` is the Gtk UI definition. There is not an 00664 equivalent UI definition in RB2.99 but we can parse out menu items since 00665 this string is in XML format 00666 00667 :param group_name: `str` unique name of the ActionGroup to add menu items to 00668 ''' 00669 if is_rb3(self.shell): 00670 root = ET.fromstring(ui_string) 00671 for elem in root.findall("./popup"): 00672 popup_name = elem.attrib['name'] 00673 00674 menuelem = elem.find('.//menuitem') 00675 action_name = menuelem.attrib['action'] 00676 item_name = menuelem.attrib['name'] 00677 00678 group = self._action_groups[group_name] 00679 act = group.get_action(action_name) 00680 00681 item = Gio.MenuItem() 00682 item.set_detailed_action('win.' + action_name) 00683 item.set_label(act.label) 00684 app = Gio.Application.get_default() 00685 00686 if popup_name == 'QueuePlaylistViewPopup': 00687 plugin_type = 'queue-popup' 00688 elif popup_name == 'BrowserSourceViewPopup': 00689 plugin_type = 'browser-popup' 00690 elif popup_name == 'PlaylistViewPopup': 00691 plugin_type = 'playlist-popup' 00692 elif popup_name == 'PodcastViewPopup': 00693 plugin_type = 'podcast-episode-popup' 00694 else: 00695 print("unknown type %s" % plugin_type) 00696 00697 index = plugin_type + action_name 00698 app.add_plugin_menu_item(plugin_type, index, item) 00699 self._uids[index] = plugin_type 00700 else: 00701 uim = self.shell.props.ui_manager 00702 self._uids.append(uim.add_ui_from_string(ui_string)) 00703 uim.ensure_update() 00704 00705 def cleanup(self): 00706 ''' 00707 utility remove any menuitems created. 00708 ''' 00709 if is_rb3(self.shell): 00710 for uid in self._uids: 00711 Gio.Application.get_default().remove_plugin_menu_item(self._uids[uid], 00712 uid) 00713 else: 00714 uim = self.shell.props.ui_manager 00715 for uid in self._uids: 00716 uim.remove_ui(uid) 00717 uim.ensure_update(); 00718 00719 def __init__(self, shell): 00720 """ Create singleton instance """ 00721 # Check whether we already have an instance 00722 if ApplicationShell.__instance is None: 00723 # Create and remember instance 00724 ApplicationShell.__instance = ApplicationShell.__impl(shell) 00725 00726 # Store instance reference as the only member in the handle 00727 self.__dict__['_ApplicationShell__instance'] = ApplicationShell.__instance 00728 00729 def __getattr__(self, attr): 00730 """ Delegate access to implementation """ 00731 return getattr(self.__instance, attr) 00732 00733 def __setattr__(self, attr, value): 00734 """ Delegate access to implementation """ 00735 return setattr(self.__instance, attr, value) 00736 00737 00738 class Action(object): 00739 ''' 00740 class that wraps around either a Gio.Action or a Gtk.Action 00741 ''' 00742 00743 def __init__(self, shell, action): 00744 ''' 00745 constructor. 00746 00747 :param shell: `RBShell` 00748 :param action: `Gio.Action` or `Gtk.Action` 00749 ''' 00750 self.shell = shell 00751 self.action = action 00752 00753 self._label = '' 00754 self._accel = '' 00755 self._current_state = False 00756 self._do_update_state = True 00757 00758 def connect(self, address, func, args): 00759 self._connect_func = func 00760 self._connect_args = args 00761 00762 if address == 'activate': 00763 func = self._activate 00764 00765 if is_rb3(self.shell): 00766 self.action.connect(address, func, args) 00767 else: 00768 self.action.connect(address, func, None, args) 00769 00770 def _activate(self, action, *args): 00771 if self._do_update_state: 00772 self._current_state = not self._current_state 00773 self.set_state(self._current_state) 00774 00775 self._connect_func(action, None, self._connect_args) 00776 00777 @property 00778 def label(self): 00779 ''' 00780 get the menu label associated with the Action 00781 00782 for RB2.99+ actions dont have menu labels so this is managed 00783 manually 00784 ''' 00785 if not is_rb3(self.shell): 00786 return self.action.get_label() 00787 else: 00788 return self._label 00789 00790 @label.setter 00791 def label(self, new_label): 00792 if not is_rb3(self.shell): 00793 self.action.set_label(new_label) 00794 00795 self._label = new_label 00796 00797 @property 00798 def accel(self): 00799 ''' 00800 get the accelerator associated with the Action 00801 ''' 00802 return self._accel 00803 00804 @accel.setter 00805 def accel(self, new_accelerator): 00806 if new_accelerator: 00807 self._accel = new_accelerator 00808 else: 00809 self._accel = '' 00810 00811 def get_sensitive(self): 00812 ''' 00813 get the sensitivity (enabled/disabled) state of the Action 00814 00815 returns boolean 00816 ''' 00817 if is_rb3(self.shell): 00818 return self.action.get_enabled() 00819 else: 00820 return self.action.get_sensitive() 00821 00822 def set_state(self, value): 00823 ''' 00824 set the state of a stateful action - this is applicable only 00825 to RB2.99+ 00826 ''' 00827 if is_rb3(self.shell) and self.action.props.state_type: 00828 self.action.change_state(GLib.Variant('b', value)) 00829 00830 def activate(self): 00831 ''' 00832 invokes the activate signal for the action 00833 ''' 00834 if is_rb3(self.shell): 00835 self.action.activate(None) 00836 else: 00837 self.action.activate() 00838 00839 def set_active(self, value): 00840 ''' 00841 activate or deactivate a stateful action signal 00842 For consistency with earlier RB versions, this will fire the 00843 activate signal for the action 00844 00845 :param value: `boolean` state value 00846 ''' 00847 00848 if is_rb3(self.shell): 00849 self.action.change_state(GLib.Variant('b', value)) 00850 self._current_state = value 00851 self._do_update_state = False 00852 self.activate() 00853 self._do_update_state = True 00854 else: 00855 self.action.set_active(value) 00856 00857 def get_active(self): 00858 ''' 00859 get the state of the action 00860 00861 returns `boolean` state value 00862 ''' 00863 if is_rb3(self.shell): 00864 returnval = self._current_state 00865 else: 00866 returnval = self.action.get_active() 00867 00868 return returnval 00869 00870 def associate_menuitem(self, menuitem): 00871 ''' 00872 links a menu with the action 00873 00874 ''' 00875 if is_rb3(self.shell): 00876 menuitem.set_detailed_action('win.' + self.action.get_name()) 00877 else: 00878 menuitem.set_related_action(self.action) 00879 00880