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()
def __init__(self, datapath, extradata, confpath, filenames): """Construct, but do not run. :`datapath`: Usually ``$PREFIX/share/mypaint``. Where MyPaint should find its app-specific read-only data, e.g. UI definition XML, backgrounds and brush defintions. :`extradata`: Where to find the defaults for MyPaint's themeable UI icons. This will be effectively 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. :`confpath`: Where the user's configuration is stored. ``$HOME/.mypaint`` is typical on Unix-like OSes. """ self.confpath = confpath self.datapath = datapath # create config directory, and subdirs where the user might drop files # TODO make scratchpad dir something pulled from preferences #PALETTE1 for d in ['', 'backgrounds', 'brushes', 'scratchpads']: d = os.path.join(self.confpath, d) if not os.path.isdir(d): os.mkdir(d) print 'Created', d # 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(extradata, "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(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(datapath, 'brushes'), join(confpath, '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(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.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()