Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

beebox / crossover   deb

Repository URL to install this package:

Version: 18.5.0-1 

/ opt / cxoffice / lib / python / cxguitools.py

# (c) Copyright 2009-2015. CodeWeavers, Inc.

import ctypes
import os
import sys
import traceback
import urllib

import gobject
import gtk

import cxlog
import cxutils
import distversion

# for localization
from cxutils import cxgettext as _
cxutils.setup_textdomain()


def toplevel_quit():
    """If there are no visible toplevel windows left, call gtk.main_quit().

    This should be called when a toplevel is closed on a process that might have
    multiple toplevels and needs to live as long as at least one is still open."""
    for toplevel in gtk.window_list_toplevels():
        if toplevel.get_property('visible') and toplevel.window and \
            toplevel.window.get_window_type() != gtk.gdk.WINDOW_TEMP:
            break
    else:
        gtk.main_quit()

def get_ui_path(basename):
    try:
        ui_dir = os.environ["CX_GLADEPATH"]
    except KeyError:
        ui_dir = os.path.join(cxutils.CX_ROOT, "lib", "python", "glade")
    return os.path.join(ui_dir, basename + ".ui")


ICONS = dict()
def get_std_icon(basename, sizes=cxutils.S_MEDIUM):
    # Assume scanning the cache is faster than disk accesses
    for size in sizes:
        key = basename + "." + size
        if key in ICONS:
            return ICONS[key]

    root = os.path.join(cxutils.CX_ROOT, 'share', 'icons')
    filename = cxutils.get_icon_path(root, '', basename, sizes)
    if filename:
        try:
            ICONS[key] = gtk.gdk.pixbuf_new_from_file(filename)
            return ICONS[key]
        except gobject.GError:
            cxlog.warn("could not load icon file %s:\n%s" % (cxlog.debug_str(filename), traceback.format_exc()))
    return None


def get_std_icon_list(basename='crossover'):
    icons = []
    root = os.path.join(cxutils.CX_ROOT, 'share', 'icons')
    for filename in cxutils.get_icon_paths(root, '', basename):
        try:
            icons.append(gtk.gdk.pixbuf_new_from_file(filename))
        except gobject.GError:
            cxlog.warn("could not load icon file %s:\n%s" % (cxlog.debug_str(filename), traceback.format_exc()))
    icons.sort(key=lambda x: x.get_property('width'))
    return icons

def set_default_icon(basename='crossover'):
    gtk.window_set_default_icon_list(*get_std_icon_list(basename))

def draw_widget_background(_widget, event, pixbuf):
    event.window.draw_pixbuf(None, pixbuf, 0, 0, 0, 0)
    return False


def set_widget_background(widget, filename):
    filename = os.path.join(cxutils.CX_ROOT, 'share', 'images', filename)
    try:
        pixbuf = gtk.gdk.pixbuf_new_from_file(filename)
    except gobject.GError:
        cxlog.warn("couldn't load icon file %s:\n%s" % (cxlog.debug_str(filename), traceback.format_exc()))
        return

    widget.connect('expose-event', draw_widget_background, pixbuf)


# pylint: disable=C0103
def blend_colors(a, b, a_weight, b_weight):
    total_weight = a_weight + b_weight
    return gtk.gdk.Color(
        (a.red * a_weight + b.red * b_weight) // total_weight,
        (a.green * a_weight + b.green * b_weight) // total_weight,
        (a.blue * a_weight + b.blue * b_weight) // total_weight)

def recolor_pixbuf(pixbuf, color, symbolic=False):
    width = pixbuf.get_property('width')
    height = pixbuf.get_property('height')
    result = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, width, height)
    pixbuf.copy_area(0, 0, width, height, result, 0, 0)

    pixels = ctypes.cast(ctypes.c_void_p(hash(result.get_property('pixels'))), ctypes.POINTER(ctypes.c_ubyte))

    r = color.red // 256
    g = color.green // 256
    b = color.blue // 256

    stride = result.get_property('rowstride')

    for y in range(height):
        i = y * stride
        for _x in range(width):
            if not symbolic or (0xbe == pixels[i] == pixels[i+1] == pixels[i+2]):
                pixels[i] = r
                pixels[i+1] = g
                pixels[i+2] = b
            i += 4

    return result



#####
#
# CXMessageDlg
#
#####

def _messagedlg_handle_response(dialog, response, response_function, close_on_response, user_data):
    if close_on_response:
        dialog.destroy()
    if response_function:
        if user_data is not None:
            result = response_function(response, user_data)
        else:
            result = response_function(response)
        if not close_on_response and result:
            dialog.destroy()

def CXMessageDlg(message=None, primary=None, secondary=None, markup=None, buttons=gtk.BUTTONS_OK, response_function=None, close_on_response=True, parent=None, user_data=None, button_array=None, message_type=None):
    if not markup:
        if message is None:
            text = []
            if primary is not None:
                text.append(u'<span weight="bold" size="larger">%s</span>' % cxutils.html_escape(primary))
            if secondary is not None:
                text.append(cxutils.html_escape(secondary))
            markup = '\n\n'.join(text)
        else:
            markup = cxutils.html_escape(message)
    else:
        # &nbsp; is an important HTML typographic entity (e.g. in French) but
        # it is not supported by GTK+. So manually convert it to the
        # corresponding Unicode character
        markup = markup.replace(u"&nbsp;", u"\u00a0")
    if message_type is None:
        cxlog.warn("Callers to cxguitools.CXMessageDlg should always set an alert type.")
        message_type = gtk.MESSAGE_INFO
    if button_array:
        dialog = gtk.MessageDialog(buttons=gtk.BUTTONS_NONE, flags=gtk.DIALOG_MODAL, parent=parent, type=message_type)
        for button in button_array:
            dialog.add_button(button[0], button[1])
    else:
        dialog = gtk.MessageDialog(buttons=buttons, flags=gtk.DIALOG_MODAL, parent=parent, type=message_type)
    dialog.set_markup(markup)
    dialog.connect('response', _messagedlg_handle_response, response_function, close_on_response, user_data)
    dialog.show()


def _malware_response(response, user_data):
    c4pfile, stop_loop = user_data
    if response == 0:
        # More Info
        url = 'http://www.codeweavers.com/support/wiki/malware/' + urllib.quote_plus(c4pfile.malware_appid)
        cxutils.launch_url(url)
        return False
    else:
        # Close
        if stop_loop:
            gtk.main_quit()
        return True

def show_malware_dialog(c4pfile, parent=None, stop_loop=False):
    primary = _("The file '%(filename)s' contains malware") % {'filename': c4pfile.filename}
    secondary = _("This file cannot be used because it would install malicious software or otherwise harm your computer.")
    CXMessageDlg(secondary=secondary, primary=primary,
                 button_array=[[_(u"More Info\u2026"), 0],
                               [gtk.STOCK_CLOSE, 1]],
                 user_data=(c4pfile, stop_loop),
                 response_function=_malware_response,
                 close_on_response=False,
                 parent=parent,
                 message_type=gtk.MESSAGE_ERROR)


#####
#
# Standard warning dialog if running as root
#
#####

def return_from_root_check(response):
    if response:
        sys.exit()
    # dismiss this dialog
    gtk.main_quit()

# This should be called before loading the main dialog to make sure the code
# this protects is not run at all
def warn_if_root():
    if os.getuid() == 0:
        # We probably shouldn't be running as root.
        warning = _("Running this tool as root is very dangerous, and will only allow you to configure CrossOver for the root user.")
        if distversion.HAS_MULTI_USER:
            warning = _("%s\n\nIf you wish to create a bottle that all the users of this computer can access, log in as a user and use the 'Publish Bottle' feature.") % warning
        CXMessageDlg(warning, buttons=None, button_array=[[_("Exit"), 1], [_("Configure CrossOver for Root"), 0]], response_function=return_from_root_check, message_type=gtk.MESSAGE_WARNING)
        gtk.main()


#####
#
# Standard File Picker selectors
#
#####

FILTERS_RUNNABLE = set(('exe', 'lnk'))
FILTERS_INSTALLABLE = set(('install',))
FILTERS_CXARCHIVES = set(('archives', 'cxarchives'))
FILTERS_ALLFILES = set(('all',))

def add_ipattern(file_picker, pattern):
    """Adds a case-insensitive pattern based on the given case-sensitive one.
    """
    insensitive = []
    for char in pattern:
        if char.isalpha():
            insensitive.append("[%s%s]" % (char.lower(), char.upper()))
        else:
            insensitive.append(char)
    file_picker.add_pattern("".join(insensitive))

def add_filters(file_picker, filters):
    """Adds the specified set of standard file filters to the file picker."""
    file_filters = {}
    default = ''

    if 'archives' in  filters:
        file_filter = gtk.FileFilter()
        # These are Unix files so keep them case-sensitive
        file_filter.add_pattern("*.cpio.7z")
        file_filter.add_pattern("*.cpio.gz")
        file_filter.add_pattern("*.cpio.bz2")
        file_filter.add_pattern("*.cpio.Z")
        file_filter.add_pattern("*.tar.7z")
        file_filter.add_pattern("*.tar.gz")
        file_filter.add_pattern("*.tar.bz2")
        file_filter.add_pattern("*.tar.Z")
        file_filter.add_pattern("*.tgz")
        file_filters[_("Unix Archives")] = file_filter

    if 'cxarchives' in  filters:
        file_filter = gtk.FileFilter()
        # These are Unix files so keep them case-sensitive
        file_filter.add_pattern("*.cxarchive")
        default = _("CrossOver Bottle Archives")
        file_filters[default] = file_filter

    if 'exe' in filters:
        file_filter = gtk.FileFilter()
        add_ipattern(file_filter, "*.bat")
        add_ipattern(file_filter, "*.cmd")
        add_ipattern(file_filter, "*.com")
        add_ipattern(file_filter, "*.exe")
        add_ipattern(file_filter, "autorun.inf")
        default = _("Windows Programs")
        file_filters[default] = file_filter

    if 'install' in filters:
        file_filter = gtk.FileFilter()
        # executables
        add_ipattern(file_filter, "*.bat")
        add_ipattern(file_filter, "*.cmd")
        add_ipattern(file_filter, "*.com")
        add_ipattern(file_filter, "*.exe")
        add_ipattern(file_filter, "autorun.inf")
        # fonts
        add_ipattern(file_filter, "*.otf")
        add_ipattern(file_filter, "*.ttc")
        add_ipattern(file_filter, "*.ttf")
        # msi
        add_ipattern(file_filter, "*.msi")
        add_ipattern(file_filter, "*.msp")
        # archives
        add_ipattern(file_filter, "*.zip")
        add_ipattern(file_filter, "*.cab")
        add_ipattern(file_filter, "*.tgz")
        add_ipattern(file_filter, "*.tar.gz")
        add_ipattern(file_filter, "*.tar.bz2")
        add_ipattern(file_filter, "*.tar")
        add_ipattern(file_filter, "*.tbz")
        add_ipattern(file_filter, "*.tb2")
        add_ipattern(file_filter, "*.rar")
        add_ipattern(file_filter, "*.7z")
        default = _("Installer Files")
        file_filters[default] = file_filter

    if 'lnk' in filters:
        file_filter = gtk.FileFilter()
        add_ipattern(file_filter, "*.lnk")
        file_filters[_("Windows Shortcuts")] = file_filter

    if 'all' in filters:
        file_filter = gtk.FileFilter()
        add_ipattern(file_filter, "*")
        file_filters[_("All Files")] = file_filter

    names = file_filters.keys()
    names.sort()
    for name in names:
        file_filter = file_filters[name]
        file_filter.set_name(name)
        file_picker.add_filter(file_filter)
        if name == default:
            # Make this filter the default
            file_picker.set_filter(file_filter)

def _skip_confirm_extensionless(file_picker):
    """If we're going to append an extension to a filename,
    skip the overwrite confirm dialog.
    """
    # pylint: disable=R0201
    filename = os.path.basename(file_picker.get_filename())
    extension = os.path.splitext(filename)[1]
    if extension == "":
        return gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME
    return gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM

def _append_extension(file_picker, response, default_extension):
    """Append extension to a filename if it lacks one."""
    # pylint: disable=R0201
    if response == gtk.RESPONSE_OK:
        filename = os.path.basename(file_picker.get_filename())
        basename = os.path.splitext(filename)[0]
        extension = os.path.splitext(filename)[1]

        if extension == "":
            # we have to restart the 'response' event because the overwrite check already happened
            file_picker.stop_emission('response')

            filename = basename + "." + default_extension

            file_picker.set_current_name(filename)
            file_picker.response(gtk.RESPONSE_OK)
    return True

def set_default_extension(file_picker, extension):
    file_picker.connect("response", _append_extension, extension)
    file_picker.connect("confirm-overwrite", _skip_confirm_extensionless)

def show_help(page):
    for lang in cxutils.get_preferred_languages():
        # We must convert the language id to match the naming of the
        # $CX_ROOT/doc folders. Note that on Debian this also relies on the
        # doc symbolic link.
        if lang == "":
            lang = "en"
        else:
            lang = lang.replace('-', '_')
        filename = os.path.join(cxutils.CX_ROOT, 'doc', lang, "html", page)
        if os.path.exists(filename):
            cxutils.launch_url(filename)
            return
    message = _("Sorry, the '%s' documentation page appears to be missing!") % page
    CXMessageDlg(message, buttons=None, button_array=[[_("Close"), 1]], message_type=gtk.MESSAGE_WARNING)