Exemple #1
0
    def __init__(self, filenames, app_datapath, app_extradatapath,
                 user_datapath, user_confpath, version, fullscreen=False):
        """Construct, but do not run.

        :params filenames: The list of files to load.
          Note: only the first is used.
        :param app_datapath: App-specific read-only data area.
          Path used for UI definition XML, and the default sets of backgrounds,
          palettes, and brush defintions. Often $PREFIX/share/.
        :param app_extradatapath: Extra search path for themeable UI icons.
          This will be used in addition to $XDG_DATA_DIRS for the purposes of
          icon lookup. Normally it's $PREFIX/share, to support unusual
          installations outside the usual locations. It should contain an
          icons/ subdirectory.
        :param user_datapath: Location of the user's app-specific data.
          For MyPaint, this means the user's brushes, backgrounds, and
          scratchpads. Commonly $XDG_DATA_HOME/mypaint, i.e.
          ~/.local/share/mypaint
        :param user_confpath: Location of the user's app-specific config area.
          This is where MyPaint will save user preferences data and the
          keyboard accelerator map. Commonly $XDG_CONFIG_HOME/mypaint, i.e.
          ~/.config/mypaint
        :param version: Version string for the about dialog.
        :param fullscreen: Go fullscreen after starting.

        """
        assert Application._INSTANCE is None
        super(Application, self).__init__()
        Application._INSTANCE = self

        self.user_confpath = user_confpath #: User configs (see __init__)
        self.user_datapath = user_datapath #: User data (see __init__)

        self.datapath = app_datapath

        self.version = version  #: version string for the app.

        # create config directory, and subdirs where the user might drop files
        for basedir in [self.user_confpath, self.user_datapath]:
            if not os.path.isdir(basedir):
                os.mkdir(basedir)
                logger.info('Created basedir %r', basedir)
        for datasubdir in ['backgrounds', 'brushes', 'scratchpads']:
            datadir = os.path.join(self.user_datapath, datasubdir)
            if not os.path.isdir(datadir):
                os.mkdir(datadir)
                logger.info('Created data subdir %r', datadir)


        # Default location for our icons. The user's theme can override these.
        icon_theme = gtk.icon_theme_get_default()
        icon_theme.append_search_path(join(app_extradatapath, "icons"))

        # Icon sanity check
        if not icon_theme.has_icon('mypaint') \
                or not icon_theme.has_icon('mypaint-tool-brush'):
            logger.error('Error: Where have my icons gone?')
            logger.error('Icon search path: %r', icon_theme.get_search_path())
            logger.error("Mypaint can't run sensibly without its icons; "
                         "please check your installation. See "
                         "https://gna.org/bugs/?18460 for possible solutions")
            sys.exit(1)

        gtk.Window.set_default_icon_name('mypaint')

        # Stock items, core actions, and menu structure
        resources_xml = join(self.datapath, "gui", "resources.xml")
        self.builder = gtk.Builder()
        self.builder.set_translation_domain("mypaint")
        self.builder.add_from_file(resources_xml)
        factory = self.builder.get_object("stock_icon_factory")
        factory.add_default()

        self.ui_manager = self.builder.get_object("app_ui_manager")
        signal_callback_objs = []

        gdk.set_program_class('MyPaint')

        self.pixmaps = PixbufDirectory(join(self.datapath, 'pixmaps'))
        self.cursor_color_picker = gdk.Cursor(
                  gtk2compat.gdk.display_get_default(),
                  self.pixmaps.cursor_color_picker,
                  1, 30)
        self.cursors = CursorCache(self)

        # unmanaged main brush; always the same instance (we can attach settings_observers)
        # this brush is where temporary changes (color, size...) happen
        self.brush = brush.BrushInfo()
        self.brush.load_defaults()

        # Global pressure mapping function, ignored unless set
        self.pressure_mapping = None

        self.preferences = {}
        self.load_settings()

        # Keyboard manager
        self.kbm = keyboard.KeyboardManager(self)

        # File I/O
        self.filehandler = filehandling.FileHandler(self)

        # Load the main interface
        mypaint_main_xml = join(self.datapath, "gui", "mypaint.glade")
        self.builder.add_from_file(mypaint_main_xml)

        # Main drawing window
        self.drawWindow = self.builder.get_object("drawwindow")
        signal_callback_objs.append(self.drawWindow)

        # Workspace widget. Manages layout of toolwindows, and autohide in
        # fullscreen.
        workspace = self.builder.get_object("app_workspace")
        workspace.build_from_layout(self.preferences["workspace.layout"])
        workspace.floating_window_created += self._floating_window_created_cb
        fs_autohide_action = self.builder.get_object("FullscreenAutohide")
        fs_autohide_action.set_active(workspace.autohide_enabled)
        self.workspace = workspace

        # Working document: viewer widget
        app_canvas = self.builder.get_object("app_canvas")

        # Working document: model and controller
        model = lib.document.Document(self.brush)
        self.doc = document.Document(self, app_canvas, model)
        app_canvas.set_model(model)

        signal_callback_objs.append(self.doc)
        signal_callback_objs.append(self.doc.modes)

        self.scratchpad_filename = ""
        scratchpad_model = lib.document.Document(self.brush)
        scratchpad_tdw = tileddrawwidget.TiledDrawWidget()
        scratchpad_tdw.set_model(scratchpad_model)
        self.scratchpad_doc = document.Document(self, scratchpad_tdw,
                                                scratchpad_model,
                                                leader=self.doc)
        self.brushmanager = brushmanager.BrushManager(
                join(app_datapath, 'brushes'),
                join(user_datapath, 'brushes'),
                self)
        signal_callback_objs.append(self.filehandler)
        self.brushmodifier = brushmodifier.BrushModifier(self)
        self.line_mode_settings = linemode.LineModeSettings(self)

        # Button press mapping
        self.button_mapping = ButtonMapping()

        # Monitors changes of input device & saves device-specific brushes
        self.device_monitor = DeviceUseMonitor(self)

        if not self.preferences.get("scratchpad.last_opened_scratchpad", None):
            self.preferences["scratchpad.last_opened_scratchpad"] = self.filehandler.get_scratchpad_autosave()
        self.scratchpad_filename = self.preferences["scratchpad.last_opened_scratchpad"]

        self.brush_color_manager = BrushColorManager(self)
        self.brush_color_manager.set_picker_cursor(self.cursor_color_picker)
        self.brush_color_manager.set_data_path(self.datapath)

        #: Mapping of setting cname to a GtkAdjustment which controls the base
        #: value of that setting for the app's current brush.
        self.brush_adjustment = {}
        self.init_brush_adjustments()

        # Connect signals defined in mypaint.xml
        callback_finder = CallbackFinder(signal_callback_objs)
        self.builder.connect_signals(callback_finder)

        self.kbm.start_listening()
        self.filehandler.doc = self.doc
        self.filehandler.filename = None
        gtk2compat.gtk.accel_map_load(join(self.user_confpath,
                                            'accelmap.conf'))

        # Load the default background
        for datapath in [self.user_datapath, self.datapath]:
            bg_path = join(datapath, backgroundwindow.BACKGROUNDS_SUBDIR,
                           backgroundwindow.DEFAULT_BACKGROUND)
            if not os.path.exists(bg_path):
                continue
            bg, errors = backgroundwindow.load_background(bg_path)
            if bg:
                self.doc.model.set_background(bg, make_default=True)
                break
            else:
                logger.warning("Failed to load default background image %r",
                               bg_path)
                if errors:
                    for error in errors:
                        logger.warning("warning: %r", error)

        # Non-dockable subwindows
        # Loading is deferred as late as possible
        self._subwindow_classes = {
            # action-name: action-class
            "BackgroundWindow": backgroundwindow.BackgroundWindow,
            "BrushEditorWindow": brusheditor.BrushEditorWindow,
            "PreferencesWindow": preferenceswindow.PreferencesWindow,
            "InputTestWindow": inputtestwindow.InputTestWindow,
            "BrushIconEditorWindow": brushiconeditor.BrushIconEditorWindow,
            }
        self._subwindows = {}

        # Show main UI.
        self.drawWindow.show_all()
        gobject.idle_add(self._at_application_start, filenames, fullscreen)
    def __init__(self, filenames, app_datapath, app_extradatapath,
                 user_datapath, user_confpath, version, fullscreen=False):
        """Construct, but do not run.

        :params filenames: The list of files to load.
          Note: only the first is used.
        :param app_datapath: App-specific read-only data area.
          Path used for UI definition XML, and the default sets of backgrounds,
          palettes, and brush defintions. Often $PREFIX/share/.
        :param app_extradatapath: Extra search path for themeable UI icons.
          This will be used in addition to $XDG_DATA_DIRS for the purposes of
          icon lookup. Normally it's $PREFIX/share, to support unusual
          installations outside the usual locations. It should contain an
          icons/ subdirectory.
        :param user_datapath: Location of the user's app-specific data.
          For MyPaint, this means the user's brushes, backgrounds, and
          scratchpads. Commonly $XDG_DATA_HOME/mypaint, i.e.
          ~/.local/share/mypaint
        :param user_confpath: Location of the user's app-specific config area.
          This is where MyPaint will save user preferences data and the
          keyboard accelerator map. Commonly $XDG_CONFIG_HOME/mypaint, i.e.
          ~/.config/mypaint
        :param version: Version string for the about dialog.
        :param fullscreen: Go fullscreen after starting.

        """
        assert Application._INSTANCE is None
        super(Application, self).__init__()
        Application._INSTANCE = self

        self.user_confpath = user_confpath #: User configs (see __init__)
        self.user_datapath = user_datapath #: User data (see __init__)

        self.datapath = app_datapath

        self.version = version  #: version string for the app.

        # create config directory, and subdirs where the user might drop files
        for basedir in [self.user_confpath, self.user_datapath]:
            if not os.path.isdir(basedir):
                os.mkdir(basedir)
                print 'Created basedir', basedir
        for datasubdir in ['backgrounds', 'brushes', 'scratchpads']:
            datadir = os.path.join(self.user_datapath, datasubdir)
            if not os.path.isdir(datadir):
                os.mkdir(datadir)
                print 'Created data subdir', datadir

        # Default location for our icons. The user's theme can override these.
        icon_theme = gtk.icon_theme_get_default()
        icon_theme.append_search_path(join(app_extradatapath, "icons"))

        # Icon sanity check
        if not icon_theme.has_icon('mypaint') \
                or not icon_theme.has_icon('mypaint-tool-brush'):
            print 'Error: Where have my icons gone?'
            print 'Icon search path:', icon_theme.get_search_path()
            print "Mypaint can't run sensibly without its icons; " \
                + "please check your installation."
            print 'see https://gna.org/bugs/?18460 for possible solutions'
            sys.exit(1)

        if pygtkcompat.USE_GTK3:
            gtk.Window.set_default_icon_name('mypaint')
        else:
            gtk.window_set_default_icon_name('mypaint')

        # Stock items, core actions, and menu structure
        builder_xml = join(self.datapath, "gui", "mypaint.xml")
        self.builder = gtk.Builder()
        self.builder.set_translation_domain("mypaint")
        self.builder.add_from_file(builder_xml)
        factory = self.builder.get_object("stock_icon_factory")
        factory.add_default()

        self.ui_manager = self.builder.get_object("app_ui_manager")
        signal_callback_objs = []

        gdk.set_program_class('MyPaint')

        self.pixmaps = PixbufDirectory(join(self.datapath, 'pixmaps'))
        self.cursor_color_picker = gdk.Cursor(
                  pygtkcompat.gdk.display_get_default(),
                  self.pixmaps.cursor_color_picker,
                  1, 30)
        self.cursors = CursorCache(self)

        # unmanaged main brush; always the same instance (we can attach settings_observers)
        # this brush is where temporary changes (color, size...) happen
        self.brush = brush.BrushInfo()
        self.brush.load_defaults()

        # Global pressure mapping function, ignored unless set
        self.pressure_mapping = None

        self.preferences = {}
        self.load_settings()

        self.scratchpad_filename = ""
        self.kbm = keyboard.KeyboardManager(self)
        self.doc = document.Document(self)
        signal_callback_objs.append(self.doc)
        signal_callback_objs.append(self.doc.modes)
        self.scratchpad_doc = document.Document(self, leader=self.doc)
        self.brushmanager = brushmanager.BrushManager(
                join(app_datapath, 'brushes'),
                join(user_datapath, 'brushes'),
                self)
        self.filehandler = filehandling.FileHandler(self)
        signal_callback_objs.append(self.filehandler)
        self.brushmodifier = brushmodifier.BrushModifier(self)
        self.line_mode_settings = linemode.LineModeSettings(self)

        # Button press mapping
        self.button_mapping = ButtonMapping()

        # Monitors changes of input device & saves device-specific brushes
        self.device_monitor = DeviceUseMonitor(self)

        if not self.preferences.get("scratchpad.last_opened_scratchpad", None):
            self.preferences["scratchpad.last_opened_scratchpad"] = self.filehandler.get_scratchpad_autosave()
        self.scratchpad_filename = self.preferences["scratchpad.last_opened_scratchpad"]

        self.brush_color_manager = BrushColorManager(self)
        self.brush_color_manager.set_picker_cursor(self.cursor_color_picker)
        self.brush_color_manager.set_data_path(self.datapath)

        self.init_brush_adjustments()

        self.layout_manager = layout.LayoutManager(
            prefs=self.preferences["layout.window_positions"],
            factory=windowing.window_factory,
            factory_opts=[self]  )
        self.drawWindow = self.layout_manager.get_widget_by_role("main-window")
        self.layout_manager.show_all()

        signal_callback_objs.append(self.drawWindow)

        # Connect signals defined in mypaint.xml
        callback_finder = CallbackFinder(signal_callback_objs)
        self.builder.connect_signals(callback_finder)

        self.kbm.start_listening()
        self.filehandler.doc = self.doc
        self.filehandler.filename = None
        pygtkcompat.gtk.accel_map_load(join(self.user_confpath,
                                            'accelmap.conf'))

        # Load the background settings window.
        # FIXME: this line shouldn't be needed, but we need to load this up
        # front to get any non-default background that the user has configured
        # from the preferences.
        self.layout_manager.get_subwindow_by_role("backgroundWindow")

        # And the brush settings window, or things like eraser mode will break.
        # FIXME: brush_adjustments should not be dependent on this
        self.layout_manager.get_subwindow_by_role("brushSettingsWindow")

        def at_application_start(*junk):
            col = self.brush_color_manager.get_color()
            self.brushmanager.select_initial_brush()
            self.brush_color_manager.set_color(col)
            if filenames:
                # Open only the first file, no matter how many has been specified
                # If the file does not exist just set it as the file to save to
                fn = filenames[0].replace('file:///', '/')
                # ^ some filebrowsers do this (should only happen with outdated
                #   mypaint.desktop)
                if not os.path.exists(fn):
                    self.filehandler.filename = fn
                else:
                    self.filehandler.open_file(fn)

            # Load last scratchpad
            if not self.preferences["scratchpad.last_opened_scratchpad"]:
                self.preferences["scratchpad.last_opened_scratchpad"] \
                         = self.filehandler.get_scratchpad_autosave()
                self.scratchpad_filename \
                         = self.preferences["scratchpad.last_opened_scratchpad"]
            if os.path.isfile(self.scratchpad_filename):
                try:
                    self.filehandler.open_scratchpad(self.scratchpad_filename)
                except AttributeError, e:
                    print "Scratchpad widget isn't initialised yet, so cannot centre"

            self.apply_settings()
            if not self.pressure_devices:
                print 'No pressure sensitive devices found.'
            self.drawWindow.present()

            # Handle fullscreen command line option
            if fullscreen:
                self.drawWindow.fullscreen_cb()
Exemple #3
0
    def __init__(self,
                 filenames,
                 app_datapath,
                 app_extradatapath,
                 user_datapath,
                 user_confpath,
                 version,
                 fullscreen=False):
        """Construct, but do not run.

        :params filenames: The list of files to load.
          Note: only the first is used.
        :param app_datapath: App-specific read-only data area.
          Path used for UI definition XML, and the default sets of backgrounds,
          palettes, and brush defintions. Often $PREFIX/share/.
        :param app_extradatapath: Extra search path for themeable UI icons.
          This will be used in addition to $XDG_DATA_DIRS for the purposes of
          icon lookup. Normally it's $PREFIX/share, to support unusual
          installations outside the usual locations. It should contain an
          icons/ subdirectory.
        :param user_datapath: Location of the user's app-specific data.
          For MyPaint, this means the user's brushes, backgrounds, and
          scratchpads. Commonly $XDG_DATA_HOME/mypaint, i.e.
          ~/.local/share/mypaint
        :param user_confpath: Location of the user's app-specific config area.
          This is where MyPaint will save user preferences data and the
          keyboard accelerator map. Commonly $XDG_CONFIG_HOME/mypaint, i.e.
          ~/.config/mypaint
        :param version: Version string for the about dialog.
        :param fullscreen: Go fullscreen after starting.

        """
        assert Application._INSTANCE is None
        super(Application, self).__init__()
        Application._INSTANCE = self

        self.user_confpath = user_confpath  #: User configs (see __init__)
        self.user_datapath = user_datapath  #: User data (see __init__)

        self.datapath = app_datapath

        self.version = version  #: version string for the app.

        # create config directory, and subdirs where the user might drop files
        for basedir in [self.user_confpath, self.user_datapath]:
            if not os.path.isdir(basedir):
                os.mkdir(basedir)
                logger.info('Created basedir %r', basedir)
        for datasubdir in ['backgrounds', 'brushes', 'scratchpads']:
            datadir = os.path.join(self.user_datapath, datasubdir)
            if not os.path.isdir(datadir):
                os.mkdir(datadir)
                logger.info('Created data subdir %r', datadir)

        # Default location for our icons. The user's theme can override these.
        icon_theme = gtk.icon_theme_get_default()
        icon_theme.append_search_path(join(app_extradatapath, "icons"))

        # Icon sanity check
        if not icon_theme.has_icon('mypaint') \
                or not icon_theme.has_icon('mypaint-tool-brush'):
            logger.error('Error: Where have my icons gone?')
            logger.error('Icon search path: %r', icon_theme.get_search_path())
            logger.error("Mypaint can't run sensibly without its icons; "
                         "please check your installation. See "
                         "https://gna.org/bugs/?18460 for possible solutions")
            sys.exit(1)

        gtk.Window.set_default_icon_name('mypaint')

        # Stock items, core actions, and menu structure
        resources_xml = join(self.datapath, "gui", "resources.xml")
        self.builder = gtk.Builder()
        self.builder.set_translation_domain("mypaint")
        self.builder.add_from_file(resources_xml)
        factory = self.builder.get_object("stock_icon_factory")
        factory.add_default()

        self.ui_manager = self.builder.get_object("app_ui_manager")
        signal_callback_objs = []

        gdk.set_program_class('MyPaint')

        self.pixmaps = PixbufDirectory(join(self.datapath, 'pixmaps'))
        self.cursor_color_picker = gdk.Cursor(
            gtk2compat.gdk.display_get_default(),
            self.pixmaps.cursor_color_picker, 1, 30)
        self.cursors = CursorCache(self)

        # unmanaged main brush; always the same instance (we can attach settings_observers)
        # this brush is where temporary changes (color, size...) happen
        self.brush = brush.BrushInfo()
        self.brush.load_defaults()

        # Global pressure mapping function, ignored unless set
        self.pressure_mapping = None

        self.preferences = {}
        self.load_settings()

        # Keyboard manager
        self.kbm = keyboard.KeyboardManager(self)

        # File I/O
        self.filehandler = filehandling.FileHandler(self)

        # Load the main interface
        mypaint_main_xml = join(self.datapath, "gui", "mypaint.glade")
        self.builder.add_from_file(mypaint_main_xml)

        # Main drawing window
        self.drawWindow = self.builder.get_object("drawwindow")
        signal_callback_objs.append(self.drawWindow)

        # Workspace widget. Manages layout of toolwindows, and autohide in
        # fullscreen.
        workspace = self.builder.get_object("app_workspace")
        workspace.build_from_layout(self.preferences["workspace.layout"])
        workspace.floating_window_created += self._floating_window_created_cb
        fs_autohide_action = self.builder.get_object("FullscreenAutohide")
        fs_autohide_action.set_active(workspace.autohide_enabled)
        self.workspace = workspace

        # Working document: viewer widget
        app_canvas = self.builder.get_object("app_canvas")

        # Working document: model and controller
        model = lib.document.Document(self.brush)
        self.doc = document.Document(self, app_canvas, model)
        app_canvas.set_model(model)

        signal_callback_objs.append(self.doc)
        signal_callback_objs.append(self.doc.modes)

        self.scratchpad_filename = ""
        scratchpad_model = lib.document.Document(self.brush)
        scratchpad_tdw = tileddrawwidget.TiledDrawWidget()
        scratchpad_tdw.set_model(scratchpad_model)
        self.scratchpad_doc = document.Document(self,
                                                scratchpad_tdw,
                                                scratchpad_model,
                                                leader=self.doc)
        self.brushmanager = brushmanager.BrushManager(
            join(app_datapath, 'brushes'), join(user_datapath, 'brushes'),
            self)
        signal_callback_objs.append(self.filehandler)
        self.brushmodifier = brushmodifier.BrushModifier(self)
        self.line_mode_settings = linemode.LineModeSettings(self)

        # Button press mapping
        self.button_mapping = ButtonMapping()

        # Monitors changes of input device & saves device-specific brushes
        self.device_monitor = DeviceUseMonitor(self)

        if not self.preferences.get("scratchpad.last_opened_scratchpad", None):
            self.preferences[
                "scratchpad.last_opened_scratchpad"] = self.filehandler.get_scratchpad_autosave(
                )
        self.scratchpad_filename = self.preferences[
            "scratchpad.last_opened_scratchpad"]

        self.brush_color_manager = BrushColorManager(self)
        self.brush_color_manager.set_picker_cursor(self.cursor_color_picker)
        self.brush_color_manager.set_data_path(self.datapath)

        #: Mapping of setting cname to a GtkAdjustment which controls the base
        #: value of that setting for the app's current brush.
        self.brush_adjustment = {}
        self.init_brush_adjustments()

        # Connect signals defined in mypaint.xml
        callback_finder = CallbackFinder(signal_callback_objs)
        self.builder.connect_signals(callback_finder)

        self.kbm.start_listening()
        self.filehandler.doc = self.doc
        self.filehandler.filename = None
        gtk2compat.gtk.accel_map_load(join(self.user_confpath,
                                           'accelmap.conf'))

        # Load the default background
        for datapath in [self.user_datapath, self.datapath]:
            bg_path = join(datapath, backgroundwindow.BACKGROUNDS_SUBDIR,
                           backgroundwindow.DEFAULT_BACKGROUND)
            if not os.path.exists(bg_path):
                continue
            bg, errors = backgroundwindow.load_background(bg_path)
            if bg:
                self.doc.model.set_background(bg, make_default=True)
                break
            else:
                logger.warning("Failed to load default background image %r",
                               bg_path)
                if errors:
                    for error in errors:
                        logger.warning("warning: %r", error)

        # Non-dockable subwindows
        # Loading is deferred as late as possible
        self._subwindow_classes = {
            # action-name: action-class
            "BackgroundWindow": backgroundwindow.BackgroundWindow,
            "BrushEditorWindow": brusheditor.BrushEditorWindow,
            "PreferencesWindow": preferenceswindow.PreferencesWindow,
            "InputTestWindow": inputtestwindow.InputTestWindow,
            "BrushIconEditorWindow": brushiconeditor.BrushIconEditorWindow,
        }
        self._subwindows = {}

        # Show main UI.
        self.drawWindow.show_all()
        gobject.idle_add(self._at_application_start, filenames, fullscreen)