1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- 2### BEGIN LICENSE 3# Copyright (c) 2012, Peter Levi <peterlevi@peterlevi.com> 4# This program is free software: you can redistribute it and/or modify it 5# under the terms of the GNU General Public License version 3, as published 6# by the Free Software Foundation. 7# 8# This program is distributed in the hope that it will be useful, but 9# WITHOUT ANY WARRANTY; without even the implied warranties of 10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 11# PURPOSE. See the GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License along 14# with this program. If not, see <http://www.gnu.org/licenses/>. 15### END LICENSE 16 17"""Code to add AppIndicator.""" 18 19import logging 20import os 21 22from gi.repository import Gtk # pylint: disable=E0611 23 24from variety.Util import Util, _ 25from variety_lib import varietyconfig 26 27THEME_ICON_NAME = "variety-indicator" 28THEME_ICON_NAME_DARK = "variety-indicator-dark" 29THEME_ICON_NAME_NUM = "variety-indicator-num{}" 30 31try: 32 import gi 33 34 try: 35 gi.require_version("AyatanaAppIndicator3", "0.1") 36 from gi.repository import AyatanaAppIndicator3 as AppIndicator3 # pylint: disable=E0611 37 38 _indicator_backend = "AyatanaAppIndicator3" # Just a dummy value we use for logging 39 except (ValueError, ImportError): 40 gi.require_version("AppIndicator3", "0.1") 41 from gi.repository import AppIndicator3 # pylint: disable=E0611 42 43 _indicator_backend = "AppIndicator3" 44 use_appindicator = True 45except (ValueError, ImportError): 46 _indicator_backend = "fallback tray" 47 use_appindicator = False 48 49 50logger = logging.getLogger("variety") 51 52 53class Indicator: 54 def __init__(self, window): 55 self.parent = window 56 self.create_menu(window) 57 self.create_indicator(window) 58 59 def create_menu(self, window): 60 self.menu = Gtk.Menu() 61 62 if not Util.is_unity(): 63 self.next_main = Gtk.MenuItem(_("_Next")) 64 self.next_main.set_use_underline(True) 65 self.next_main.connect("activate", window.next_wallpaper) 66 self.menu.append(self.next_main) 67 68 self.prev_main = Gtk.MenuItem(_("_Previous")) 69 self.prev_main.set_use_underline(True) 70 self.prev_main.connect("activate", window.prev_wallpaper) 71 self.menu.append(self.prev_main) 72 73 self.menu.append(Gtk.SeparatorMenuItem.new()) 74 75 self.file_label = Gtk.MenuItem(_("Current desktop wallpaper")) 76 self.file_label.connect("activate", window.open_file) 77 self.menu.append(self.file_label) 78 79 self.show_origin = Gtk.MenuItem(_("Show origin")) 80 self.show_origin.connect("activate", window.on_show_origin) 81 self.show_origin.set_sensitive(False) 82 self.menu.append(self.show_origin) 83 84 self.show_author = Gtk.MenuItem("Show author") 85 self.show_author.connect("activate", window.on_show_author) 86 self.show_author.set_sensitive(False) 87 self.show_author.set_visible(False) 88 self.menu.append(self.show_author) 89 90 self.copy_to_favorites = Gtk.MenuItem(_("Copy to _Favorites")) 91 self.copy_to_favorites.set_use_underline(True) 92 self.copy_to_favorites.connect("activate", window.copy_to_favorites) 93 self.menu.append(self.copy_to_favorites) 94 95 self.move_to_favorites = Gtk.MenuItem(_("Move to Favorites")) 96 self.move_to_favorites.set_use_underline(True) 97 self.move_to_favorites.connect("activate", window.move_to_favorites) 98 self.move_to_favorites.set_visible(False) 99 self.menu.append(self.move_to_favorites) 100 101 self.trash = Gtk.MenuItem(_("Delete to _Trash")) 102 self.trash.set_use_underline(True) 103 self.trash.connect("activate", window.move_to_trash) 104 self.menu.append(self.trash) 105 106 self.menu.append(Gtk.SeparatorMenuItem.new()) 107 108 self.image_menu = Gtk.Menu() 109 110 self.next = Gtk.MenuItem(_("_Next")) 111 self.next.set_use_underline(True) 112 self.next.connect("activate", window.next_wallpaper) 113 self.image_menu.append(self.next) 114 115 self.prev = Gtk.MenuItem(_("_Previous")) 116 self.prev.set_use_underline(True) 117 self.prev.connect("activate", window.prev_wallpaper) 118 self.image_menu.append(self.prev) 119 120 self.fast_forward = Gtk.MenuItem(_("_Next, skipping forward history")) 121 self.fast_forward.set_use_underline(True) 122 123 def _fast_forward(widget): 124 window.next_wallpaper(widget, bypass_history=True) 125 126 self.fast_forward.connect("activate", _fast_forward) 127 self.image_menu.append(self.fast_forward) 128 129 self.image_menu.append(Gtk.SeparatorMenuItem.new()) 130 self.scroll_tip = Gtk.MenuItem(_("Tip: Scroll wheel over icon\nfor Next and Previous")) 131 self.scroll_tip.set_sensitive(False) 132 self.image_menu.append(self.scroll_tip) 133 134 self.image_menu.append(Gtk.SeparatorMenuItem.new()) 135 136 self.pause_resume = Gtk.MenuItem(_("Pause on current")) 137 self.pause_resume.connect("activate", window.on_pause_resume) 138 self.image_menu.append(self.pause_resume) 139 140 self.image_item = Gtk.MenuItem(_("_Image")) 141 self.image_item.set_use_underline(True) 142 self.image_item.set_submenu(self.image_menu) 143 self.menu.append(self.image_item) 144 145 self.image_menu.append(Gtk.SeparatorMenuItem.new()) 146 147 self.focus = Gtk.MenuItem(_("Where is it from?")) 148 self.focus.connect("activate", window.focus_in_preferences) 149 self.image_menu.append(self.focus) 150 151 self.no_effects = Gtk.CheckMenuItem(_("Show without effects")) 152 self.no_effects.set_active(False) 153 self.no_effects.set_use_underline(True) 154 155 def _toggle_no_effects(widget=None): 156 window.toggle_no_effects(self.no_effects.get_active()) 157 158 self.no_effects_handler_id = self.no_effects.connect("toggled", _toggle_no_effects) 159 self.image_menu.append(self.no_effects) 160 161 self.google_image = Gtk.MenuItem(_("Google Image Search")) 162 self.google_image.connect("activate", window.google_image_search) 163 self.image_menu.append(self.google_image) 164 165 self.rating_separator = Gtk.SeparatorMenuItem.new() 166 self.image_menu.append(self.rating_separator) 167 168 self.rating = Gtk.MenuItem(_("Set EXIF Rating")) 169 self.image_menu.append(self.rating) 170 171 # self.image_item = Gtk.MenuItem(_("_Image")) 172 # self.image_item.set_use_underline(True) 173 # self.image_item.set_submenu(self.image_menu) 174 # self.menu.append(self.image_item) 175 # 176 177 self.quotes_menu = Gtk.Menu() 178 179 self.next_quote = Gtk.MenuItem(_("_Next")) 180 self.next_quote.set_use_underline(True) 181 self.next_quote.connect("activate", window.next_quote) 182 self.quotes_menu.append(self.next_quote) 183 184 self.prev_quote = Gtk.MenuItem(_("_Previous")) 185 self.prev_quote.set_use_underline(True) 186 self.prev_quote.connect("activate", window.prev_quote) 187 self.quotes_menu.append(self.prev_quote) 188 189 self.fast_forward_quote = Gtk.MenuItem(_("_Next, skipping forward history")) 190 self.fast_forward_quote.set_use_underline(True) 191 192 def _fast_forward_quote(widget): 193 window.next_quote(widget, bypass_history=True) 194 195 self.fast_forward_quote.connect("activate", _fast_forward_quote) 196 self.quotes_menu.append(self.fast_forward_quote) 197 198 self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) 199 200 self.quotes_pause_resume = Gtk.MenuItem(_("Pause on current")) 201 self.quotes_pause_resume.connect("activate", window.on_quotes_pause_resume) 202 self.quotes_menu.append(self.quotes_pause_resume) 203 204 self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) 205 206 self.quote_favorite = Gtk.MenuItem(_("Save to Favorites")) 207 self.quote_favorite.set_use_underline(True) 208 self.quote_favorite.connect("activate", window.quote_save_to_favorites) 209 self.quotes_menu.append(self.quote_favorite) 210 211 self.quote_view_favs = Gtk.MenuItem(_("View Favorites...")) 212 self.quote_view_favs.set_use_underline(True) 213 self.quote_view_favs.connect("activate", window.quote_view_favorites) 214 self.quotes_menu.append(self.quote_view_favs) 215 216 self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) 217 218 self.quote_clipboard = Gtk.MenuItem(_("Copy to Clipboard")) 219 self.quote_clipboard.set_use_underline(True) 220 self.quote_clipboard.connect("activate", window.quote_copy_to_clipboard) 221 self.quotes_menu.append(self.quote_clipboard) 222 223 self.view_quote = Gtk.MenuItem() 224 self.view_quote.set_use_underline(True) 225 self.view_quote.connect("activate", window.view_quote) 226 self.quotes_menu.append(self.view_quote) 227 228 self.google_quote_text = Gtk.MenuItem(_("Google Quote")) 229 self.google_quote_text.set_use_underline(True) 230 self.google_quote_text.connect("activate", window.google_quote_text) 231 self.quotes_menu.append(self.google_quote_text) 232 233 self.google_quote_author = Gtk.MenuItem(_("Google Author")) 234 self.google_quote_author.set_use_underline(True) 235 self.google_quote_author.connect("activate", window.google_quote_author) 236 self.quotes_menu.append(self.google_quote_author) 237 238 self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) 239 240 self.quotes_preferences = Gtk.MenuItem(_("Preferences...")) 241 self.quotes_preferences.set_use_underline(True) 242 243 def _quotes_prefs(widget=None): 244 window.preferences_dialog.ui.notebook.set_current_page(1) 245 window.on_mnu_preferences_activate() 246 247 self.quotes_preferences.connect("activate", _quotes_prefs) 248 self.quotes_menu.append(self.quotes_preferences) 249 250 self.quotes_disable = Gtk.MenuItem(_("Turn off")) 251 self.quotes_disable.set_use_underline(True) 252 self.quotes_disable.connect("activate", window.disable_quotes) 253 self.quotes_menu.append(self.quotes_disable) 254 255 self.quotes = Gtk.MenuItem(_("_Quote")) 256 self.quotes.set_use_underline(True) 257 self.quotes.set_submenu(self.quotes_menu) 258 self.menu.append(self.quotes) 259 260 self.menu.append(Gtk.SeparatorMenuItem.new()) 261 262 self.history = Gtk.CheckMenuItem(_("_History")) 263 self.history.set_active(False) 264 self.history.set_use_underline(True) 265 self.history_handler_id = self.history.connect("toggled", window.show_hide_history) 266 self.menu.append(self.history) 267 268 self.selector = Gtk.CheckMenuItem(_("_Wallpaper Selector")) 269 self.selector.set_active(False) 270 self.selector.set_use_underline(True) 271 self.selector_handler_id = self.selector.connect( 272 "toggled", window.show_hide_wallpaper_selector 273 ) 274 self.menu.append(self.selector) 275 276 self.downloads = Gtk.CheckMenuItem(_("Recent _Downloads")) 277 self.downloads.set_active(False) 278 self.downloads.set_use_underline(True) 279 self.downloads_handler_id = self.downloads.connect("toggled", window.show_hide_downloads) 280 self.menu.append(self.downloads) 281 282 if Util.check_variety_slideshow_present(): 283 self.menu.append(Gtk.SeparatorMenuItem.new()) 284 self.slideshow = Gtk.MenuItem(_("Start Slideshow")) 285 self.slideshow.connect("activate", window.on_start_slideshow) 286 self.menu.append(self.slideshow) 287 else: 288 logger.warning( 289 "Variety Slideshow is not installed. This is an optional extension " 290 "adding pan-and-zoom slideshows to Variety: see " 291 "https://github.com/peterlevi/variety-slideshow for details" 292 ) 293 294 self.menu.append(Gtk.SeparatorMenuItem.new()) 295 296 self.preferences = Gtk.MenuItem(_("Preferences...")) 297 self.preferences.connect("activate", window.on_mnu_preferences_activate) 298 self.menu.append(self.preferences) 299 300 self.about = Gtk.MenuItem(_("About")) 301 self.about.connect("activate", window.on_mnu_about_activate) 302 self.menu.append(self.about) 303 304 self.donate = Gtk.MenuItem(_("Donate")) 305 self.donate.connect("activate", window.on_mnu_donate_activate) 306 self.menu.append(self.donate) 307 308 self.quit = Gtk.MenuItem(_("Quit")) 309 self.quit.connect("activate", window.on_quit) 310 self.menu.append(self.quit) 311 312 self.menu.show_all() 313 314 def create_indicator(self, window): 315 logger.info("indicator backend: %s", _indicator_backend) 316 self.indicator = None 317 self.status_icon = None 318 self.visible = True 319 320 def right_click_event(icon, button, time): 321 self.menu.popup(None, None, Gtk.StatusIcon.position_menu, self.status_icon, 0, time) 322 323 def left_click_event(data): 324 self.menu.popup( 325 None, 326 None, 327 Gtk.StatusIcon.position_menu, 328 self.status_icon, 329 0, 330 Gtk.get_current_event_time(), 331 ) 332 333 def on_indicator_scroll_status_icon(status_icon, event): 334 window.on_indicator_scroll(None, 1, event.direction) 335 336 icon_path = varietyconfig.get_data_file("media", "variety-indicator.png") 337 if use_appindicator: 338 self.indicator = AppIndicator3.Indicator.new( 339 "variety", "", AppIndicator3.IndicatorCategory.APPLICATION_STATUS 340 ) 341 self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) 342 self.indicator.set_icon(icon_path) 343 self.indicator.connect("scroll-event", window.on_indicator_scroll) 344 self.indicator.set_menu(self.menu) 345 else: 346 self.status_icon = Gtk.StatusIcon.new_from_file(icon_path) 347 self.status_icon.set_visible(True) 348 self.status_icon.connect("activate", left_click_event) 349 self.status_icon.connect("popup-menu", right_click_event) 350 self.status_icon.connect("scroll-event", on_indicator_scroll_status_icon) 351 352 def set_visible(self, visible): 353 self.visible = visible 354 if visible: 355 if self.indicator: 356 logger.info(lambda: "Showing indicator icon") 357 self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) 358 if self.status_icon: 359 logger.info(lambda: "Showing status icon") 360 self.status_icon.set_visible(True) 361 else: 362 if self.indicator: 363 logger.info(lambda: "Hiding indicator icon") 364 self.indicator.set_status(AppIndicator3.IndicatorStatus.PASSIVE) 365 if self.status_icon: 366 logger.info(lambda: "Hiding status icon") 367 self.status_icon.set_visible(False) 368 369 def set_icon(self, icon): 370 def set_from_theme_icon(name): 371 if self.indicator: 372 logger.info(lambda: "Showing indicator icon %s from GTK theme" % name) 373 self.indicator.set_icon(name) 374 if self.status_icon: 375 logger.info(lambda: "Showing status icon %s from GTK theme" % name) 376 self.status_icon.set_from_icon_name(name) 377 378 if icon == "Light": 379 if Gtk.IconTheme.get_default().has_icon(THEME_ICON_NAME): 380 set_from_theme_icon(THEME_ICON_NAME) 381 return 382 else: 383 icon_path = varietyconfig.get_data_file("media", "variety-indicator.png") 384 elif icon == "Dark": 385 if Gtk.IconTheme.get_default().has_icon(THEME_ICON_NAME_DARK): 386 set_from_theme_icon(THEME_ICON_NAME_DARK) 387 return 388 else: 389 icon_path = varietyconfig.get_data_file("media", "variety-indicator-dark.png") 390 elif icon in ["1", "2", "3", "4"]: 391 if Gtk.IconTheme.get_default().has_icon(THEME_ICON_NAME_NUM.format(icon)): 392 set_from_theme_icon(THEME_ICON_NAME_NUM.format(icon)) 393 return 394 else: 395 icon_path = varietyconfig.get_data_file( 396 "media", "variety-indicator-num{}.png".format(icon) 397 ) 398 elif icon and os.access(icon, os.R_OK) and Util.is_image(icon): 399 icon_path = icon 400 else: 401 icon_path = varietyconfig.get_data_file("media", "variety-indicator.png") 402 403 if self.indicator: 404 logger.info(lambda: "Showing indicator icon image: " + icon_path) 405 self.indicator.set_icon(icon_path) 406 if self.status_icon: 407 logger.info(lambda: "Showing status icon image: " + icon_path) 408 self.status_icon.set_from_file(icon_path) 409 410 def get_visible(self): 411 return self.visible 412 413 414def new_application_indicator(window): 415 ind = Indicator(window) 416 return ind, ind.indicator, ind.status_icon 417