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

Repository URL to install this package:

Details    
crossover / opt / cxoffice / lib / python / cxrunui.py
Size: Mime:
# (c) Copyright 2009-2013, 2015. CodeWeavers, Inc.

import os
import subprocess
import traceback

import gobject
gobject.threads_init()
import gtk

import cxutils
import bottlequery
import cxobservable

import bottlecollection
import createshortcutdlg
import cxguilog
import cxguitools
import pyop

# for localization
from cxutils import cxgettext as _

# These are the observable events
DIALOG_CLOSED = 'dialog_closed'


#
# Dialog instance
#

class RunCommandController(cxobservable.Object):

    observable_events = frozenset((DIALOG_CLOSED,))

    def __init__(self, inBottleName):
        cxobservable.Object.__init__(self)

        #  Setup the GUI
        self.xml = gtk.Builder()
        self.xml.set_translation_domain("crossover")
        self.xml.add_from_file(cxguitools.get_ui_path("cxrun"))
        self.xml.connect_signals(self)


        # Add indents to debug channel menu items
        for item in self.xml.get_object("DebugChannelMenu").get_children()[3:]:
            item.set_label('  '+item.get_label())


        #----------------------------------------------------------------------------
        #  Set up progress bar
        #----------------------------------------------------------------------------
        progbar = self.xml.get_object("ProgBar")
        progbar.set_pulse_step(.05)
        progbar.hide()


        #----------------------------------------------------------------------------
        #  Fill Bottle popup list
        #----------------------------------------------------------------------------
        bottlePopupWidget = self.xml.get_object("BottlePopup")
        self.bottleListStore = gtk.ListStore(str, object) #display name, internal name
        bottlePopupWidget.set_model(self.bottleListStore)
        selectIter = 0
        collection = bottlecollection.sharedCollection()
        if len(collection.bottleList()) > 0:
            for bottleName in sorted(collection.bottleList()):
                newrow = self.bottleListStore.append()
                self.bottleListStore.set_value(newrow, 0, bottleName)
                self.bottleListStore.set_value(newrow, 1, bottleName)
                if bottleName == inBottleName:
                    selectIter = newrow
        else:
            newrow = self.bottleListStore.append()
            self.bottleListStore.set_value(newrow, 0, _("(No bottles)"))
            self.bottleListStore.set_value(newrow, 1, None)

        if selectIter:
            bottlePopupWidget.set_active_iter(selectIter)
        else:
            bottlePopupWidget.set_active_iter(self.bottleListStore.get_iter_first())

        self.add_menu_open = False
        self.commandOp = None
        self.animateEvent = None
        self.set_widget_sensitivities(self)

    def set_command(self, command):
        commandEntry = self.xml.get_object("CommandEntry")
        commandEntry.set_text(command)

    def cancel_clicked(self, caller):
        self.xml.get_object("RunCommandDialog").destroy()
        self.quit_requested(caller)

    def quit_requested(self, _caller):
        self.emit_event(DIALOG_CLOSED)
        cxguitools.toplevel_quit()

    def progbar_pulse(self):
        progBar = self.xml.get_object("ProgBar")
        progBar.pulse()
        return True

    def set_widget_sensitivities(self, _caller):
        progBar = self.xml.get_object("ProgBar")

        collection = bottlecollection.sharedCollection()
        if self.commandOp != None or len(collection.bottleList()) == 0:
            self.xml.get_object("RunCommandButton").set_sensitive(False)
            self.xml.get_object("CommandEntry").set_sensitive(False)
            self.xml.get_object("BottlePopup").set_sensitive(False)
            self.xml.get_object("DebugOptionsFrame").set_sensitive(False)
            self.xml.get_object("CommandBrowseButton").set_sensitive(False)
            progBar.show()
        else:
            if self.xml.get_object("CommandEntry").get_text():
                self.xml.get_object("RunCommandButton").set_sensitive(True)
            else:
                self.xml.get_object("RunCommandButton").set_sensitive(False)

            self.xml.get_object("CommandEntry").set_sensitive(True)
            self.xml.get_object("BottlePopup").set_sensitive(True)
            self.xml.get_object("DebugOptionsFrame").set_sensitive(True)
            self.xml.get_object("CommandBrowseButton").set_sensitive(True)
            progBar.hide()

        if self.xml.get_object("CommandEntry").get_text():
            self.xml.get_object("CreateIconButton").set_sensitive(True)
        else:
            self.xml.get_object("CreateIconButton").set_sensitive(False)

        if len(collection.bottleList()) == 0:
            self.xml.get_object("ProgBar").hide()

    def present(self):
        self.xml.get_object("RunCommandDialog").present()


    def run_command(self, _caller):

        logFileName = ""
        loggingChannels = ""
        if self.xml.get_object("LogFileCheckbox").get_active():
            fileSaver = gtk.FileChooserDialog(_("Specify where to save the log file"), self.xml.get_object("RunCommandDialog"), gtk.FILE_CHOOSER_ACTION_SAVE)
            fileFilter = gtk.FileFilter()
            fileFilter.add_pattern("*.cxlog")
            fileFilter.set_name(_("CrossOver Log Files"))
            fileSaver.add_filter(fileFilter)

            fileSaver.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
            fileSaver.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_OK)
            fileSaver.set_do_overwrite_confirmation(True)

            cxguitools.set_default_extension(fileSaver, 'cxlog')

            if fileSaver.run() == gtk.RESPONSE_OK:
                logFileName = fileSaver.get_filename()
                fileSaver.destroy()
            else:
                fileSaver.destroy()
                return

            loggingChannels = self.xml.get_object("DbgChannelEntry").get_text()

        self.animateEvent = gobject.timeout_add(100, self.progbar_pulse)

        command_line = self.xml.get_object("CommandEntry").get_text()

        if logFileName:
            log_file = cxguilog.create_log_file(logFileName, "Running command: %s\nBottle: %s\nDebug channels: %s\n\n" % (command_line, self.get_selected_bottle().name, loggingChannels))
        else:
            log_file = None

        self.commandOp = RunWineCommandOperation(self, command_line, self.get_selected_bottle().name, log_file, loggingChannels)
        self.set_widget_sensitivities(self)
        pyop.sharedOperationQueue.enqueue(self.commandOp)


    def toggle_logfile(self, widget):
        self.xml.get_object("ExtraLoggingChannelsFrame").set_sensitive(
            widget.get_active())


    def run_command_finished(self, _runCommandOp):
        self.commandOp = None
        self.set_widget_sensitivities(self)

        gobject.source_remove(self.animateEvent)


    def get_selected_bottle(self):
        collection = bottlecollection.sharedCollection()
        bottlePopup = self.xml.get_object("BottlePopup")
        activeIndex = bottlePopup.get_active_iter()
        selectedBottleName = self.bottleListStore.get_value(activeIndex, 1)
        if selectedBottleName:
            bottleObj = collection.bottleObject(selectedBottleName)
            return bottleObj


    def browse_for_command(self, _caller):
        selectedBottle = self.get_selected_bottle()
        filePicker = gtk.FileChooserDialog(_("Choose a File to Run"), self.xml.get_object("RunCommandDialog"), gtk.FILE_CHOOSER_ACTION_OPEN)
        cxguitools.add_filters(filePicker, cxguitools.FILTERS_RUNNABLE | cxguitools.FILTERS_ALLFILES)

        environ = bottlequery.get_win_environ(selectedBottle.name, ("SystemDrive",))
        drive = bottlequery.expand_win_string(environ, "%SystemDrive%")
        drive = bottlequery.get_native_path(selectedBottle.name, drive)
        filePicker.set_current_folder(drive)

        filePicker.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        filePicker.add_button(gtk.STOCK_OPEN, gtk.RESPONSE_OK)

        if filePicker.run() == gtk.RESPONSE_OK:
            commandEntry = self.xml.get_object("CommandEntry")
            filename = cxutils.argvtocmdline((filePicker.get_filename(),))
            commandEntry.set_text(filename)

        filePicker.destroy()

    def create_icon(self, _widget):
        bottlename = self.get_selected_bottle().name

        command = self.xml.get_object("CommandEntry").get_text()

        createshortcutdlg.CreateShortcutDialog(bottlename=bottlename, command=command)


    def add_button_pos(self, menu):
        _menu_width, menu_height = menu.size_request()

        button = self.xml.get_object("DbgChannelButton")

        button_x, button_y = button.window.get_origin()
        button_allocation = button.get_allocation()
        button_x += button_allocation.x
        button_y += button_allocation.y
        button_height = button_allocation.height
        button_screen = button.window.get_screen()
        monitor_frame = button_screen.get_monitor_geometry(button_screen.get_monitor_at_window(button.window))

        fits_below = (button_y + button_height + menu_height <= monitor_frame.y + monitor_frame.height)
        fits_above = (button_y - menu_height >= monitor_frame.y)

        if fits_below:
            return button_x, button_y + button_height, True
        elif fits_above:
            return button_x, button_y - menu_height, True
        else:
            return button_x, button_y + button_height, False

    def on_DbgChannelButton_button_press_event(self, widget, event):
        if self.add_menu_open or event.button != 1:
            return

        menu = self.xml.get_object("DebugChannelMenu")
        menu.popup(None, None, self.add_button_pos, event.button, event.time)

        self.add_menu_open = True

        widget.set_active(True)

        return True

    def on_DbgChannelButton_clicked(self, widget):
        if self.add_menu_open or not widget.get_active():
            return

        menu = self.xml.get_object("DebugChannelMenu")
        menu.popup(None, None, self.add_button_pos, 0, 0)

        self.add_menu_open = True

    def on_DebugChannelMenu_deactivate(self, _menushell):
        self.add_menu_open = False
        button = self.xml.get_object("DbgChannelButton")
        button.set_active(False)

    def on_DbgClearMenu_activate(self, _menu):
        self.xml.get_object("DbgChannelEntry").set_text('')

    def add_debug_channels(self, menu):
        channels = self.xml.get_object("DbgChannelEntry").get_text().split(',')
        for channel in menu.get_tooltip_text().split(','):
            if channel not in channels:
                channels.append(channel)
        self.xml.get_object("DbgChannelEntry").set_text(','.join(x for x in channels if x))

#
# Global dialog management
#

DIALOG = None

def _dialog_closed(_event=None, _data=None, *_args):
    # pylint: disable=W0603
    global DIALOG
    DIALOG = None

def open_or_show(*args, **kwargs):
    # pylint: disable=W0603
    global DIALOG
    if DIALOG is None:
        DIALOG = RunCommandController(*args, **kwargs)
        DIALOG.add_observer(DIALOG_CLOSED, _dialog_closed)
    else:
        DIALOG.present()
    return DIALOG


#
# Operations
#

class RunWineCommandOperation(pyop.PythonOperation):

    def __init__(self, inCommandController, inCommand, inBottleName, inLogFile=None, inDebugChannels=""):
        pyop.PythonOperation.__init__(self)
        self.bottlename = inBottleName
        self.commandController = inCommandController
        self.argArray = [os.path.join(cxutils.CX_ROOT, "bin", "cxstart")]
        self.argArray.extend(["--bottle", inBottleName, '--new-console'])
        self.log_file = inLogFile
        if inDebugChannels != "":
            self.argArray.append("--debugmsg")
            self.argArray.append(inDebugChannels)
        cmdline_array = cxutils.cmdlinetoargv(inCommand)
        dirname = cxutils.dirname(cmdline_array[0])
        if dirname:
            self.argArray.append('--workdir')
            self.argArray.append(dirname)
        self.argArray.append("--")
        self.argArray.extend(cxutils.cmdlinetoargv(inCommand))

    def __unicode__(self):
        return "RunWineCommandOperation for " + self.bottlename


    def main(self):
        try:
            if self.log_file:
                env = os.environ.copy()
                env['CX_LOG'] = '-'
                stderr = self.log_file
            else:
                env = None
                stderr = subprocess.PIPE
            subp = subprocess.Popen(self.argArray, stdout=subprocess.PIPE, stderr=stderr, env=env, universal_newlines=True, close_fds=True)
            if self.log_file:
                self.log_file.close()
            subp.communicate()
        except:
            traceback.print_exc()


    def finish(self):
        self.commandController.run_command_finished(self)
        pyop.PythonOperation.finish(self)