#!/usr/bin/env python3 # # by Kendall Weaver # for Peppermint OS # modified by Mark Greaves (PCNetSpec) # with much appreciated code contributions by rhein # internationalization (i18n)/gettext support by Kiyohito AOKI # # Ice is a simple Site Specific Browser (SSB) manager for Chromium and # Chrome specifically intended to integrate with the LXDE menu system. # Unlike the built-in functions in the browsers, Ice boasts the ability # to remove SSBs, validate addresses, and prevent overwriting existing # SSBs. Special thanks to Matt Phillips for the # excellent pyfav library that is integrated into this application. # ADDENDUM: Added support for Firefox (via "ice-firefox") and Vivaldi. import os import sys import requests import urllib.request import urllib.parse import urllib.error import os.path import string import gi ## added by MG ## gi.require_version('Gtk','3.0') ## added by MG ## from gi.repository import Gtk from gi.repository.GdkPixbuf import Pixbuf from urllib.parse import urlparse, urlunparse from bs4 import BeautifulSoup import gettext import locale _HOME = os.getenv("HOME") _ICE_DIR = "{0}/.local/share/ice".format(_HOME) _APPS_DIR = "{0}/.local/share/applications".format(_HOME) _PROFILES_DIR = "{0}/profiles".format(_ICE_DIR) _FF_PROFILES_DIR = "{0}/firefox".format(_ICE_DIR) _ICE_ICON = "/usr/share/pixmaps/ice.png" _CHROME_BIN = "/usr/bin/google-chrome" _CHROMIUM_BIN = "/usr/bin/chromium-browser" _VIVALDI_BIN = "/usr/bin/vivaldi" _FIREFOX_BIN = "/usr/bin/firefox" gettext.bindtextdomain('messages', os.path.dirname(__file__)+ '/../share/ice/locale/') gettext.textdomain('messages') _ = gettext.gettext # Requisite dirs for directory in [ _ICE_DIR, _APPS_DIR, _PROFILES_DIR, _FF_PROFILES_DIR ]: if not os.path.exists(directory): os.system("mkdir -p {0}".format(directory)) headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0', #'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36' } def get_details(app): a = open(app, 'r', errors='ignore') nameline = "" iconline = "" profile = "" is_ice = False is_firefox = False is_isolated = False for line in a: if "Name=" in line: array = line.replace("=", " ").split() array.pop(0) for word in array: nameline = nameline + word + " " elif "Icon=" in line: array = line.replace("=", " ").split() array.pop(0) for word in array: iconline = iconline + word try: pixbuf = Pixbuf.new_from_file_at_size(iconline, 16, 16) except: pixbuf = Pixbuf.new_from_file_at_size(_ICE_ICON, 16, 16) elif "StartupWMClass=Chromium" in line: # for legacy apps is_ice = True elif "StartupWMClass=ICE-SSB" in line: is_ice = True elif "IceFirefox=" in line: is_firefox = True profile = str.replace(line, 'IceFirefox=', '').strip() elif "X-ICE-SSB-Profile=" in line: is_isolated = True profile = str.replace(line, 'X-ICE-SSB-Profile=', '').strip() if nameline != "" and iconline != "" and is_ice is True: details = { 'nameline' : nameline, 'profile' : profile, 'is_firefox' : is_firefox, 'is_isolated' : is_isolated, 'pixbuf': pixbuf } return details return None def normalize(url): (scheme, netloc, path, _, _, frag) = urlparse(url, "http") if not netloc and path: return urlunparse((scheme, path, "", "", "", "")) return urlunparse((scheme, netloc, path, "", "", "")) def errortest(url): try: urllib.request.urlopen(url) return True except (urllib.request.HTTPError, urllib.request.URLError): return False def download_favicon(url, file_prefix='', target_dir='/tmp'): parsed_site_uri = urlparse(url) if not parsed_site_uri.scheme: url = 'http://{0}'.format(url) parsed_site_uri = urlparse(url) if not parsed_site_uri.scheme or not parsed_site_uri.netloc: raise Exception(_("Unable to parse URL, {0}").format(url)) favicon_url = get_favicon_url(url) if not favicon_url: raise Exception(_("Unable to find favicon for, {0}").format(url)) response = requests.get(favicon_url, headers=headers) if response.status_code == requests.codes.ok: parsed_uri = urlparse(favicon_url) favicon_filepath = parsed_uri.path favicon_path, favicon_filename = os.path.split(favicon_filepath) valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits) sanitized_filename = "".join([x if valid_chars else "" for x in favicon_filename]) sanitized_filename = os.path.join(target_dir, file_prefix + sanitized_filename) with open(sanitized_filename, 'wb') as f: for chunk in response.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks f.write(chunk) f.flush() return sanitized_filename def parse_markup_for_favicon(markup, parsed_site_uri): soup = BeautifulSoup(markup, "lxml") icon_link = soup.find('link', rel='icon') if icon_link and icon_link.has_attr('href'): favicon_url = icon_link['href'] if favicon_url.startswith('//'): parsed_uri = urlparse(url) favicon_url = "{0}:{1}".format(parsed_uri.scheme, favicon_url) elif favicon_url.startswith('/'): favicon_url = "{0}://{1}{2}".format(parsed_site_uri.scheme, parsed_site_uri.netloc, favicon_url) elif not favicon_url.startswith('http'): path, filename = os.path.split(parsed_site_uri.path) favicon_url = "{0}://{1}/{2}".format(parsed_site_uri.scheme, parsed_site_uri.netloc, os.path.join(path, favicon_url)) return favicon_url return None def get_favicon_url(url): parsed_site_uri = urlparse(url) try: response = requests.get(url, headers=headers) except: raise Exception(_("Unable to find URL. Is it valid? {0}").format(url)) if response.status_code == requests.codes.ok: favicon_url = parse_markup_for_favicon(response.content, parsed_site_uri) if favicon_url: return favicon_url favicon_url = '{uri.scheme}://{uri.netloc}/favicon.ico'.format(uri=parsed_site_uri) response = requests.get(favicon_url, headers=headers) if response.status_code == requests.codes.ok: return favicon_url return None def applicate(): title = name.get_text() address = normalize(url.get_text()) semiformatted = "" array = filter(str.isalpha, title) for obj in array: semiformatted = semiformatted + obj formatted = semiformatted.lower() loc = where.get_active_text() if loc == _("Accessories"): location = "Utility;" elif loc == _("Games"): location = "Game;" elif loc == _("Graphics"): location = "Graphics;" elif loc == _("Internet"): location = "Network;" elif loc == _("Office"): location = "Office;" elif loc == _("Programming"): location = "Development;" elif loc == _("Multimedia"): location = "AudioVideo;" elif loc == _("System"): location = "System;" global iconpath iconname = iconpath.replace("/", " ").split()[-1] iconext = iconname.replace(".", " ").split()[-1] if os.path.exists("{0}/{1}.desktop".format(_APPS_DIR, formatted)): DuplicateError(title, formatted, address, iconext, location) elif len(title) == 0: EmptyNameError() else: writefile(title, formatted, address, iconext, location) def writefile(title, formatted, address, iconext, location): global iconpath os.system("cp --force {0} {1}/{2}.{3}".format(iconpath, _ICE_DIR, formatted, iconext)) appfile = os.path.expanduser("{0}/{1}.desktop".format(_APPS_DIR, formatted)) os.system("touch {0}".format(appfile)) if chrome.get_active() == True: browser = "google-chrome" elif chromium.get_active() == True: browser = "chromium-browser" elif vivaldi.get_active() == True: browser = "vivaldi" elif firefox.get_active() == True: browser = "firefox" else: print(_("ERROR: An unknown browser selection error has occurred.")) sys.exit(1) with open(appfile, 'w') as appfile1: global isolate_profile appfile1.truncate() appfile1.write("[Desktop Entry]\n") appfile1.write("Version=1.0\n") appfile1.write("Name={0}\n".format(title)) appfile1.write("Comment={0} (Ice SSB)\n".format(title)) if (browser == "firefox"): firefox_profile_path = "{0}/{1}".format(_FF_PROFILES_DIR, formatted) appfile1.write("Exec={0} --class ICE-SSB-{2} --profile {3} --no-remote {1}\n".format(browser, address, formatted, firefox_profile_path)) appfile1.write("IceFirefox={0}\n".format(formatted)) init_firefox_profile(firefox_profile_path) else: if isolate_profile == True: profile_path = "{0}/{1}".format(_PROFILES_DIR, formatted) appfile1.write("Exec={0} --app={1} --class=ICE-SSB-{2} --user-data-dir={3}\n".format(browser, address, formatted, profile_path)) appfile1.write("X-ICE-SSB-Profile={0}\n".format(formatted)) else: appfile1.write("Exec={0} --app={1} --class=ICE-SSB-{2}\n".format(browser, address, formatted)) appfile1.write("Terminal=false\n") appfile1.write("X-MultipleArgs=false\n") appfile1.write("Type=Application\n") appfile1.write("Icon={0}/{1}.{2}\n".format(_ICE_DIR, formatted, iconext)) appfile1.write("Categories=GTK;{0}\n".format(location)) appfile1.write("MimeType=text/html;text/xml;application/xhtml_xml;\n") appfile1.write("StartupWMClass=ICE-SSB-{0}\n".format(formatted)) appfile1.write("StartupNotify=true\n") name.set_text("") url.set_text("") iconpath = _ICE_ICON new_icon = Pixbuf.new_from_file_at_size(iconpath, 32, 32) icon.set_from_pixbuf(new_icon) details = get_details(appfile) if details is not None: liststore.prepend([ details['pixbuf'], details['nameline'] ]) def init_firefox_profile(path): chromepath = "{0}/chrome".format(path) settingsfile = "{0}/user.js".format(path) cssfile = "{0}/userChrome.css".format(chromepath) os.system('mkdir -p ' + chromepath) os.system('cp -n /usr/lib/peppermint/ice/search.json.mozlz4 ' + path + '/search.json.mozlz4') os.system('cp -n /usr/lib/peppermint/ice/places.sqlite ' + path + '/places.sqlite') os.system("touch {0}".format(cssfile)) with open(cssfile, 'w') as cfile: cfile.write("#nav-bar, #identity-box, #tabbrowser-tabs, #TabsToolbar { visibility: collapse !important; }") os.system("touch {0}".format(settingsfile)) with open(settingsfile, 'w') as sfile: sfile.write('user_pref("browser.cache.disk.enable", false);') sfile.write('user_pref("browser.cache.disk.capacity", 0);') sfile.write('user_pref("browser.cache.disk.filesystem_reported", 1);') sfile.write('user_pref("browser.cache.disk.smart_size.enabled", false);') sfile.write('user_pref("browser.cache.disk.smart_size.first_run", false);') sfile.write('user_pref("browser.cache.disk.smart_size.use_old_max", false);') sfile.write('user_pref("browser.ctrlTab.previews", true);') sfile.write('user_pref("browser.tabs.warnOnClose", false);') sfile.write('user_pref("plugin.state.flash", 2);') sfile.write('user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);') # rhein: do we really need this? #os.system('rm -rf ' + chromepath + '/cache2') def delete(button): a = iconview.get_selected_items() b = liststore.get_iter(a[0]) c = liststore.get_value(b, 1) liststore.remove(b) semiformatted = "" array = filter(str.isalpha, c) for obj in array: semiformatted = semiformatted + obj formatted = semiformatted.lower() appfile = "{0}/{1}.desktop".format(_APPS_DIR, formatted) appfileopen = open(appfile, 'r') appfilelines = appfileopen.readlines() appfileopen.close() for line in appfilelines: if "IceFirefox=" in line: profile = str.replace(line, 'IceFirefox=', '') os.system("rm -rf {0}/{1}".format(_FF_PROFILES_DIR, profile)) if "X-ICE-SSB-Profile=" in line: profile = str.replace(line, 'X-ICE-SSB-Profile=', '') os.system("rm -rf {0}/{1}".format(_PROFILES_DIR, profile)) os.system("rm {0}".format(appfile)) def clean_orphaned_profiles(known_apps): known_profiles = [] for app in known_apps: a = "{0}/{1}".format(_FF_PROFILES_DIR, app['profile']) if app['profile'] != "": # make sure firefox apps have profiles available if app['is_firefox'] is True and not os.path.isdir(a): init_firefox_profile(a) known_profiles.append(app['profile']) for p_type in ['profiles','firefox']: for fl in os.listdir("{0}/{1}/".format(_ICE_DIR, p_type)): a = "{0}/{1}/{2}".format(_ICE_DIR, p_type, fl) if not os.path.isdir(a) or fl not in known_profiles: os.system("rm -rf {0}".format(a)) class IconSel(Gtk.FileChooserDialog): def __init__(self): def update_image(dialog): filename = dialog.get_preview_filename() try: pixbuf = Pixbuf.new_from_file(filename) preview.set_from_pixbuf(pixbuf) valid_preview = True except: valid_preview = False dialog.set_preview_widget_active(valid_preview) filew = Gtk.FileChooserDialog( _("Please choose an icon."), None, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) filew.set_filename(_ICE_ICON) filter1 = Gtk.FileFilter() filter1.set_name("Icons") filter1.add_mime_type("image/png") filter1.add_mime_type("image/jpeg") filter1.add_mime_type("image/gif") filter1.add_pattern("*.png") filter1.add_pattern("*.jpg") filter1.add_pattern("*.gif") filter1.add_pattern("*.xpm") filter1.add_pattern("*.svg") filew.add_filter(filter1) preview = Gtk.Image() filew.set_preview_widget(preview) filew.connect("update-preview", update_image) response = filew.run() if response == Gtk.ResponseType.OK: global iconpath iconpath = filew.get_filename() new_icon = Pixbuf.new_from_file_at_size(iconpath, 32, 32) icon.set_from_pixbuf(new_icon) filew.destroy() elif response == Gtk.ResponseType.CANCEL: filew.destroy() class NoBrowserError(Gtk.Window): def destroy(self, button): self.close() def __init__(self): Gtk.Window.__init__(self, title=_("Browser Error")) self.set_size_request(250, 130) self.set_icon_from_file(_ICE_ICON) print(_("test")) main_lab = Gtk.Label() main_lab.set_markup(_("Warning: No Suitable Browser Detected")) text_lab = Gtk.Label(_("The name of the SSB will cause an existing SSB to\nbe overwritten. To prevent this, change a letter in\nthe name. Continue anyway?")) text_lab = Gtk.Label(_("Ice requires a system installation of either Google\nChrome or Chromium in order to function. Please\ninstall at least one in order to create SSBs.")) close = Gtk.Button(label=_("Close")) close.connect("clicked", self.destroy) void = Gtk.Label() box = Gtk.HBox() box.pack_start(void, True, True, 0) box.pack_start(close, False, False, 0) main_vbox = Gtk.VBox() main_vbox.pack_start(main_lab, False, False, 10) main_vbox.pack_start(text_lab, False, False, 0) main_vbox.pack_start(box, False, False, 10) main_hbox = Gtk.HBox() main_hbox.pack_start(main_vbox, True, True, 10) self.add(main_hbox) self.show_all() class DuplicateError(Gtk.Window): def destroy(self, button): self.close() def okay_clicked(self, button, title, formatted, address, iconext, location): for item in liststore: itemiter = item.iter semiformatted = "" array = filter(str.isalpha, item[1]) for obj in array: semiformatted = semiformatted + obj forma = semiformatted.lower() if forma == formatted: liststore.remove(itemiter) writefile(title, formatted, address, iconext, location) self.close() def __init__(self, title, formatted, address, iconext, location): Gtk.Window.__init__(self, title=_("Duplication Error")) self.set_size_request(250, 130) self.set_icon_from_file(_ICE_ICON) main_lab = Gtk.Label() main_lab.set_markup(_("Warning: File Duplication Error")) text_lab = Gtk.Label(_("The name of the SSB will cause an existing SSB to\nbe overwritten. To prevent this, change a letter in\nthe name. Continue anyway?")) okay = Gtk.Button(label=_("OK")) okay.connect("clicked", self.okay_clicked, title, formatted, address, iconext, location) cancel = Gtk.Button(label=_("Cancel")) cancel.connect("clicked", self.destroy) void = Gtk.Label() box = Gtk.HBox() box.pack_start(void, True, True, 0) box.pack_start(okay, False, False, 10) box.pack_start(cancel, False, False, 0) main_vbox = Gtk.VBox() main_vbox.pack_start(main_lab, False, False, 10) main_vbox.pack_start(text_lab, False, False, 0) main_vbox.pack_start(box, False, False, 10) main_hbox = Gtk.HBox() main_hbox.pack_start(main_vbox, True, True, 10) self.add(main_hbox) self.show_all() class AddressError(Gtk.Window): def destroy(self, button): self.close() def okay_clicked(self, button): applicate() self.close() def __init__(self): Gtk.Window.__init__(self, title=_("Address Error")) self.set_size_request(250, 130) self.set_icon_from_file(_ICE_ICON) main_lab = Gtk.Label() main_lab.set_markup(_("Warning: HTTP or URL Error")) text_lab = Gtk.Label(_("An error with the web address has been detected.\nThis is possibly the site being down or unavailable\nright now. Continue anyway?")) okay = Gtk.Button(label=_("OK")) okay.connect("clicked", self.okay_clicked) cancel = Gtk.Button(label=_("Cancel")) cancel.connect("clicked", self.destroy) void = Gtk.Label() box = Gtk.HBox() box.pack_start(void, True, True, 0) box.pack_start(okay, False, False, 10) box.pack_start(cancel, False, False, 0) main_vbox = Gtk.VBox() main_vbox.pack_start(main_lab, False, False, 10) main_vbox.pack_start(text_lab, False, False, 0) main_vbox.pack_start(box, False, False, 10) main_hbox = Gtk.HBox() main_hbox.pack_start(main_vbox, True, True, 10) self.add(main_hbox) self.show_all() class EmptyNameError(Gtk.Window): def destroy(self, button): self.close() def okay_clicked(self, button): applicate() self.close() def __init__(self): Gtk.Window.__init__(self, title=_("Name Error")) self.set_size_request(250, 130) self.set_icon_from_file(_ICE_ICON) main_lab = Gtk.Label() main_lab.set_markup(_("Error: No Application Name Entered.")) text_lab = Gtk.Label(_("Please enter an application name to continue.")) close = Gtk.Button(label=_("Close")) close.connect("clicked", self.destroy) void = Gtk.Label() box = Gtk.HBox() box.pack_start(void, True, True, 0) box.pack_start(close, False, False, 0) main_vbox = Gtk.VBox() main_vbox.pack_start(main_lab, False, False, 10) main_vbox.pack_start(text_lab, False, False, 0) main_vbox.pack_start(box, False, False, 10) main_hbox = Gtk.HBox() main_hbox.pack_start(main_vbox, True, True, 10) self.add(main_hbox) self.show_all() class Ice(Gtk.Window): def destroy(self, button): Gtk.main_quit() def icon_select(self, button): IconSel() def apply_clicked(self, button): if errortest(normalize(url.get_text())) is True: applicate() elif errortest(normalize(url.get_text())) is False: AddressError() elif errortest(normalize(url.get_text())) is None: print(_("ERROR: An address error has occurred.")) sys.exit(1) else: print(_("ERROR: An unknown error has occurred.")) sys.exit(1) def icon_download(self, button): appurl = normalize(url.get_text()) global iconpath try: download_favicon(appurl) addr0 = get_favicon_url(appurl) addr1 = addr0.replace('/', ' ') addr2 = addr1.split()[-1] iconpath = "/tmp/{0}".format(addr2) new_icon = Pixbuf.new_from_file_at_size(iconpath, 32, 32) icon.set_from_pixbuf(new_icon) except: iconpath = _ICE_ICON new_icon = Pixbuf.new_from_file_at_size(iconpath, 32, 32) icon.set_from_pixbuf(new_icon) def isolate_clicked(self, button): global isolate_profile isolate_profile=False if button.get_active() == True: isolate_profile = True def __init__(self): Gtk.Window.__init__(self, title="Ice") self.current_directory = os.path.realpath(_APPS_DIR) self.set_size_request(460, 350) self.set_icon_from_file(_ICE_ICON) ###################### # 'Create' page. # ###################### welcome = Gtk.Label() welcome.set_markup(_("Welcome to Ice, a simple SSB manager.")) global name name = Gtk.Entry() name.set_placeholder_text(_("Name the application")) global url url = Gtk.Entry() url.set_placeholder_text(_("Enter web address")) where_store = [_("Accessories"), _("Games"), _("Graphics"), _("Internet"), _("Office"), _("Programming"), _("Multimedia"), _("System")] where_lab = Gtk.Label(label=_("Where in the menu?")) global where where = Gtk.ComboBoxText() where.set_entry_text_column(0) for entry in where_store: where.append_text(entry) where.set_active(3) where_box = Gtk.HBox() where_void = Gtk.Label() where_box.pack_start(where_lab, False, False, 0) where_box.pack_start(where_void, False, False, 10) where_box.pack_start(where, True, True, 0) global iconpath iconpath = _ICE_ICON icon_pixbuf = Pixbuf.new_from_file_at_size(iconpath, 32, 32) global icon icon = Gtk.Image() icon.set_from_pixbuf(icon_pixbuf) icon_void = Gtk.Label() icon_box = Gtk.HBox() icon_box.pack_start(icon, False, False, 10) icon_box.pack_start(icon_void, False, False, 10) choose_icon = Gtk.Button(label=_("Select an icon")) choose_icon.connect("clicked", self.icon_select) download_icon = Gtk.Button(label=_("Use site favicon")) download_icon.connect("clicked", self.icon_download) icon_vbox = Gtk.VBox() icon_vbox.pack_start(choose_icon, True, True, 5) icon_vbox.pack_start(download_icon, True, True, 5) icon_hbox = Gtk.HBox() icon_hbox.pack_start(icon_box, False, False, 10) icon_hbox.pack_start(icon_vbox, True, True, 0) global firefox firefox = Gtk.RadioButton.new_with_label_from_widget(None, " Firefox") if not os.path.exists(_FIREFOX_BIN): firefox.set_sensitive(False) if not os.path.exists(_CHROMIUM_BIN) and not \ os.path.exists(_CHROME_BIN) and not \ os.path.exists(_VIVALDI_BIN) and \ os.path.exists(_FIREFOX_BIN): firefox.set_active(True) global chrome chrome = Gtk.RadioButton.new_from_widget(firefox) chrome.set_label(" Chrome") if not os.path.exists(_CHROME_BIN): chrome.set_sensitive(False) if not os.path.exists(_CHROMIUM_BIN) and not \ os.path.exists(_FIREFOX_BIN) and not \ os.path.exists(_VIVALDI_BIN) and \ os.path.exists(_CHROME_BIN): chrome.set_active(True) global vivaldi vivaldi = Gtk.RadioButton.new_from_widget(chrome) vivaldi.set_label(" Vivaldi") if not os.path.exists(_VIVALDI_BIN): vivaldi.set_sensitive(False) if not os.path.exists(_CHROMIUM_BIN) and not \ os.path.exists(_FIREFOX_BIN) and not \ os.path.exists(_CHROME_BIN) and \ os.path.exists(_VIVALDI_BIN): vivaldi.set_active(True) global chromium chromium = Gtk.RadioButton.new_from_widget(chrome) chromium.set_label(" Chromium") if not os.path.exists(_CHROMIUM_BIN): chromium.set_sensitive(False) if not os.path.exists(_CHROME_BIN) and not \ os.path.exists(_FIREFOX_BIN) and not \ os.path.exists(_VIVALDI_BIN) and \ os.path.exists(_CHROMIUM_BIN): chromium.set_active(True) global isolate_profile isolate_profile=False isolate_box = Gtk.VBox() isolate_button = Gtk.CheckButton(label=_(" Create the SSB with an isolated browser profile (Note: Firefox SSB's are always isolated)")) isolate_button.connect("toggled", self.isolate_clicked) isolate_box.add(isolate_button) apply_button = Gtk.Button(label=_("Apply")) apply_button.connect("clicked", self.apply_clicked) close_button = Gtk.Button(label=_("Close")) close_button.connect("clicked", self.destroy) button_void = Gtk.Label() button_box = Gtk.HBox() button_box.pack_start(chrome, False, False, 10) button_box.pack_start(chromium, False, False, 0) button_box.pack_start(vivaldi, False, False, 10) button_box.pack_start(firefox, False, False, 0) button_box.pack_start(button_void, True, True, 0) button_box.pack_start(close_button, False, False, 20) button_box.pack_start(apply_button, False, False, 0) create_vbox = Gtk.VBox() create_vbox.pack_start(welcome, False, False, 15) create_vbox.pack_start(name, False, False, 0) create_vbox.pack_start(url, False, False, 10) create_vbox.pack_start(where_box, False, False, 10) create_vbox.pack_start(icon_hbox, False, False, 10) create_vbox.pack_start(isolate_box, False, False, 10) create_vbox.pack_start(button_box, False, False, 0) create_hbox = Gtk.HBox() create_hbox.pack_start(create_vbox, True, True, 20) create_lab = Gtk.Label(label=_("Create")) ###################### # 'Remove' page. # ###################### global liststore known_profiles = [] liststore = Gtk.ListStore(Pixbuf, str) for fl in os.listdir(_APPS_DIR): a = "{0}/{1}".format(_APPS_DIR, fl) if not os.path.isdir(a): details = get_details(a) if details is not None: liststore.append([ details['pixbuf'], details['nameline'] ]) known_profiles.append(details) clean_orphaned_profiles(known_profiles) global iconview iconview = Gtk.IconView() iconview.set_model(liststore) iconview.set_pixbuf_column(0) iconview.set_text_column(1) iconview.set_selection_mode(1) scroll = Gtk.ScrolledWindow() scroll.add(iconview) remove = Gtk.Button(label=_("Remove")) remove.connect("clicked", delete) close = Gtk.Button(label=_("Close")) close.connect("clicked", self.destroy) void = Gtk.Label() buttons = Gtk.HBox() buttons.pack_start(void, True, True, 0) buttons.pack_start(close, False, False, 20) buttons.pack_start(remove, False, False, 0) remove_vbox = Gtk.VBox() remove_vbox.pack_start(scroll, True, True, 10) remove_vbox.pack_start(buttons, False, False, 0) remove_hbox = Gtk.HBox() remove_hbox.pack_start(remove_vbox, True, True, 20) remove_lab = Gtk.Label(label=_("Remove")) ########################## # Main window stuff. # ########################## notebook = Gtk.Notebook() notebook.append_page(create_hbox, create_lab) notebook.append_page(remove_hbox, remove_lab) main_vbox = Gtk.VBox() main_vbox.pack_start(notebook, True, True, 10) main_hbox = Gtk.HBox() main_hbox.pack_start(main_vbox, True, True, 10) self.add(main_hbox) self.show_all() if not os.path.exists(_CHROME_BIN) and not \ os.path.exists(_CHROMIUM_BIN) and not \ os.path.exists(_VIVALDI_BIN) and not \ os.path.exists(_FIREFOX_BIN): apply_button.set_sensitive(False) NoBrowserError() if __name__ == '__main__': window = Ice() window.connect("delete-event", Gtk.main_quit) Gtk.main()