OSDN Git Service

version bump
[mypaint-anime/master.git] / gui / drawwindow.py
index fc09a92..f5ba5f9 100644 (file)
@@ -13,15 +13,16 @@ This is the main drawing window, containing menu actions.
 Painting is done in tileddrawwidget.py.
 """
 
-MYPAINT_VERSION="0.9.1+git"
+MYPAINT_VERSION="1.0.0"
 
-import os, math, time
+import os, math, time, webbrowser
 from gettext import gettext as _
 
 import gtk, gobject
 from gtk import gdk, keysyms
+import pango
 
-import colorselectionwindow, historypopup, stategroup, colorpicker, windowing, layout
+import colorselectionwindow, historypopup, stategroup, colorpicker, windowing, layout, toolbar
 import dialogs
 from lib import helpers
 import stock
@@ -128,11 +129,10 @@ def button_press_cb_abstraction(drawwindow, win, event, doc):
         return True
 
     # Dispatch regular GTK events.
-    for ag in drawwindow.action_group, doc.action_group:
-        action = ag.get_action(action_name)
-        if action is not None:
-            action.activate()
-            return True
+    action = drawwindow.app.find_action(action_name)
+    if action is not None:
+        action.activate()
+        return True
 
 def button_release_cb_abstraction(win, event, doc):
     #print event.device, event.button
@@ -145,6 +145,10 @@ def button_release_cb_abstraction(win, event, doc):
 
 class Window (windowing.MainWindow, layout.MainWindow):
 
+    MENUISHBAR_RADIO_MENUBAR = 1
+    MENUISHBAR_RADIO_MAIN_TOOLBAR = 2
+    MENUISHBAR_RADIO_BOTH_BARS = 3
+
     def __init__(self, app):
         windowing.MainWindow.__init__(self, app)
         self.app = app
@@ -177,6 +181,14 @@ class Window (windowing.MainWindow, layout.MainWindow):
 
         lm = app.layout_manager
         layout.MainWindow.__init__(self, lm)
+
+        # Park the focus on the main tdw rather than on the toolbar. Default
+        # activation doesn't really mean much for MyPaint's main window, so
+        # it's safe to do this and it looks better.
+        self.main_widget.set_can_default(True)
+        self.main_widget.set_can_focus(True)
+        self.main_widget.grab_focus()
+
         self.main_widget.connect("button-press-event", self.button_press_cb)
         self.main_widget.connect("button-release-event",self.button_release_cb)
         self.main_widget.connect("scroll-event", self.scroll_cb)
@@ -208,8 +220,10 @@ class Window (windowing.MainWindow, layout.MainWindow):
             ('ColorMenu',    None, _('Color')),
             ('ColorPickerPopup',    gtk.STOCK_COLOR_PICKER, _('Pick Color'), 'r', None, self.popup_cb),
             ('ColorHistoryPopup',  None, _('Color History'), 'x', None, self.popup_cb),
-            ('ColorChangerPopup', None, _('Color Changer'), 'v', None, self.popup_cb),
+            ('ColorChangerCrossedBowlPopup', None, _('Color Changer (crossed bowl)'), 'v', None, self.popup_cb),
+            ('ColorChangerWashPopup', None, _('Color Changer (washed)'), 'c', None, self.popup_cb),
             ('ColorRingPopup',  None, _('Color Ring'), None, None, self.popup_cb),
+            ('ColorDetailsDialog', None, _("Color Details"), None, None, self.color_details_dialog_cb),
 
             ('ContextMenu',  None, _('Brushkeys')),
             ('ContextHelp',  gtk.STOCK_HELP, _('Help!'), None, None, self.show_infodialog_cb),
@@ -218,20 +232,22 @@ class Window (windowing.MainWindow, layout.MainWindow):
 
             # Scratchpad menu items
             ('ScratchMenu',    None, _('Scratchpad')),
-            ('ScratchNew',  gtk.STOCK_NEW, _('New scratchpad'), '', None, self.new_scratchpad_cb),
-            ('ScratchLoad',  gtk.STOCK_OPEN, _('Load into scratchpad'), '', None, self.load_scratchpad_cb),
+            ('ScratchNew',  gtk.STOCK_NEW, _('New Scratchpad'), '', None, self.new_scratchpad_cb),
+            ('ScratchLoad',  gtk.STOCK_OPEN, _('Load Scratchpad...'), '', None, self.load_scratchpad_cb),
             ('ScratchSaveNow',  gtk.STOCK_SAVE, _('Save Scratchpad Now'), '', None, self.save_current_scratchpad_cb),
             ('ScratchSaveAs',  gtk.STOCK_SAVE_AS, _('Save Scratchpad As...'), '', None, self.save_as_scratchpad_cb),
-            ('ScratchRevert',  gtk.STOCK_UNDO, _('Revert scratchpad'), '', None, self.revert_current_scratchpad_cb),
-            ('ScratchSaveAsDefault',  None, _('Save Scratchpad As Default'), None, None, self.save_scratchpad_as_default_cb),
+            ('ScratchRevert',  gtk.STOCK_UNDO, _('Revert Scratchpad'), '', None, self.revert_current_scratchpad_cb),
+            ('ScratchSaveAsDefault',  None, _('Save Scratchpad as Default'), None, None, self.save_scratchpad_as_default_cb),
             ('ScratchClearDefault',  None, _('Clear the Default Scratchpad'), None, None, self.clear_default_scratchpad_cb),
-            ('ScratchLoadPalette',  None, _('Draw a palette in the current Scratchpad'), None, None, self.draw_palette_cb),
-            ('ScratchPaletteOptions',    None, _('Draw a palette...')),
-            ('ScratchDrawSatPalette',  None, _('Draw a saturation palette of current color'), None, None, self.draw_sat_spectrum_cb),
-            ('ScratchCopyBackground',  None, _('Match scratchpad bg to canvas bg'), None, None, self.scratchpad_copy_background_cb),
+            ('ScratchPaletteOptions', None, _('Render a Palette')),
+            ('ScratchLoadPalette',  None, _('Load Palette File...'), None, None, self.draw_palette_cb),
+            ('ScratchDrawSatPalette',  None, _('Different Saturations of the current Color'), None, None, self.draw_sat_spectrum_cb),
+            ('ScratchCopyBackground',  None, _('Copy Background to Scratchpad'), None, None, self.scratchpad_copy_background_cb),
 
             ('BrushMenu',    None, _('Brush')),
-            ('ImportBrushPack',       gtk.STOCK_OPEN, _('Import brush package...'), '', None, self.import_brush_pack_cb),
+            ('BrushChooserPopup', stock.TOOL_BRUSH, _("Change Brush..."), 'b', None, self.brush_chooser_popup_cb),
+            ('DownloadBrushPack', gtk.STOCK_OPEN, _('Download more brushes (in web browser)'), '', None, self.download_brush_pack_cb),
+            ('ImportBrushPack', gtk.STOCK_OPEN, _('Import brush package...'), '', None, self.import_brush_pack_cb),
 
             ('HelpMenu',   None, _('Help')),
             ('Docu', gtk.STOCK_INFO, _('Where is the Documentation?'), None, None, self.show_infodialog_cb),
@@ -246,12 +262,15 @@ class Window (windowing.MainWindow, layout.MainWindow):
 
 
             ('ViewMenu', None, _('View')),
+            ('MenuishBarMenu', None, _('Toolbars')),
             ('ShowPopupMenu',    None, _('Popup Menu'), 'Menu', None, self.popupmenu_show_cb),
-            ('Fullscreen',   gtk.STOCK_FULLSCREEN, _('Fullscreen'), 'F11', None, self.fullscreen_cb),
+            ('Fullscreen',   gtk.STOCK_FULLSCREEN, None, 'F11', None, self.fullscreen_cb),
             ('ViewHelp',  gtk.STOCK_HELP, _('Help'), None, None, self.show_infodialog_cb),
             ]
         ag = self.action_group = gtk.ActionGroup('WindowActions')
+        self.app.add_action_group(ag)
         ag.add_actions(actions)
+        self.update_fullscreen_action()
 
         # Toggle actions
         toggle_actions = [
@@ -267,10 +286,12 @@ class Window (windowing.MainWindow, layout.MainWindow):
             ('BackgroundWindow', gtk.STOCK_PAGE_SETUP,
                     _('Background'), None, None, self.toggle_window_cb),
             ('BrushSelectionWindow', stock.TOOL_BRUSH,
-                    None, None, _("Toggle the Brush selector"),
+                    None, None,
+                    _("Edit and reorganise Brush Lists"),
                     self.toggle_window_cb),
             ('BrushSettingsWindow', gtk.STOCK_PROPERTIES,
-                    _('Brush Editor'), '<control>b', None,
+                    _('Brush Settings Editor'), '<control>b',
+                    _("Change Brush Settings in detail"),
                     self.toggle_window_cb),
             ('ColorSelectionWindow', stock.TOOL_COLOR_SELECTOR,
                     None, None, _("Toggle the Colour Triangle"),
@@ -302,37 +323,63 @@ class Window (windowing.MainWindow, layout.MainWindow):
 
         # More toggle actions - ones which don't control windows.
         toggle_actions = [
-            ('ToggleToolbar', None, _('Toolbar'), None,
-                    _("Show toolbar"), self.toggle_toolbar_cb,
-                    self.get_show_toolbar()),
             ('ToggleSubwindows', None, _('Subwindows'), 'Tab',
                     _("Show subwindows"), self.toggle_subwindows_cb,
                     self.get_show_subwindows()),
             ]
         ag.add_toggle_actions(toggle_actions)
 
+        # Radio actions
+        menuishbar_radio_actions = [
+            ('MenuishBarRadioMenubar', None, _('Menubar only'), None,
+                _("Show menu bar"),
+                self.MENUISHBAR_RADIO_MENUBAR),
+            ('MenuishBarRadioMainToolbar', None, _('Toolbar only'), None,
+                _("Show toolbar"),
+                self.MENUISHBAR_RADIO_MAIN_TOOLBAR),
+            ('MenuishBarRadioMenubarAndMainToolbar', None, _('Both'), None,
+                _("Show both the menu bar and the toolbar"),
+                self.MENUISHBAR_RADIO_BOTH_BARS),
+            ]
+        menuishbar_state = 0
+        if self.get_ui_part_enabled("menubar"):
+            menuishbar_state += self.MENUISHBAR_RADIO_MENUBAR
+        if self.get_ui_part_enabled("main_toolbar"):
+            menuishbar_state += self.MENUISHBAR_RADIO_MAIN_TOOLBAR
+        if menuishbar_state == 0:
+            menuishbar_state = self.MENUISHBAR_RADIO_MAIN_TOOLBAR
+        ag.add_radio_actions(menuishbar_radio_actions,
+            menuishbar_state, self.on_menuishbar_radio_change)
+        gobject.idle_add(lambda: self.update_ui_parts())
+
         # Keyboard handling
         for action in self.action_group.list_actions():
             self.app.kbm.takeover_action(action)
-        self.app.ui_manager.insert_action_group(ag, -1)
 
     def init_stategroups(self):
         sg = stategroup.StateGroup()
         p2s = sg.create_popup_state
-        changer = p2s(colorselectionwindow.ColorChangerPopup(self.app))
+        changer_crossed_bowl = p2s(colorselectionwindow.ColorChangerCrossedBowlPopup(self.app))
+        changer_wash = p2s(colorselectionwindow.ColorChangerWashPopup(self.app))
         ring = p2s(colorselectionwindow.ColorRingPopup(self.app))
         hist = p2s(historypopup.HistoryPopup(self.app, self.app.doc.model))
         pick = self.colorpick_state = p2s(colorpicker.ColorPicker(self.app, self.app.doc.model))
 
         self.popup_states = {
-            'ColorChangerPopup': changer,
+            'ColorChangerCrossedBowlPopup': changer_crossed_bowl,
+            'ColorChangerWashPopup': changer_wash,
             'ColorRingPopup': ring,
             'ColorHistoryPopup': hist,
             'ColorPickerPopup': pick,
             }
-        changer.next_state = ring
-        ring.next_state = changer
-        changer.autoleave_timeout = None
+
+        # not sure how useful this is; we can't cycle at the moment
+        changer_crossed_bowl.next_state = ring
+        ring.next_state = changer_wash
+        changer_wash.next_state = ring
+
+        changer_wash.autoleave_timeout = None
+        changer_crossed_bowl.autoleave_timeout = None
         ring.autoleave_timeout = None
 
         pick.max_key_hit_duration = 0.0
@@ -349,19 +396,14 @@ class Window (windowing.MainWindow, layout.MainWindow):
         menupath = os.path.join(self.app.datapath, 'gui/menu.xml')
         menubar_xml = open(menupath).read()
         self.app.ui_manager.add_ui_from_string(menubar_xml)
-        self._init_popupmenu(menubar_xml)
+        self.popupmenu = self._clone_menu(menubar_xml, 'PopupMenu', self.app.doc.tdw)
         self.menubar = self.app.ui_manager.get_widget('/Menubar')
 
     def init_toolbar(self):
-        toolbarpath = os.path.join(self.app.datapath, 'gui/toolbar.xml')
-        toolbarbar_xml = open(toolbarpath).read()
-        self.app.ui_manager.add_ui_from_string(toolbarbar_xml)
-        self.toolbar = self.app.ui_manager.get_widget('/toolbar1')
-        if not self.get_show_toolbar():
-            gobject.idle_add(self.toolbar.hide)
+        self.toolbar_manager = toolbar.ToolbarManager(self)
+        self.toolbar = self.toolbar_manager.toolbar1
 
-
-    def _init_popupmenu(self, xml):
+    def _clone_menu(self, xml, name, owner=None):
         """
         Hopefully temporary hack for converting UIManager XML describing the
         main menubar into a rebindable popup menu. UIManager by itself doesn't
@@ -370,27 +412,22 @@ class Window (windowing.MainWindow, layout.MainWindow):
         """
         ui_elt = ET.fromstring(xml)
         rootmenu_elt = ui_elt.find("menubar")
-        rootmenu_elt.attrib["name"] = "PopupMenu"
-        ## XML-style menu jiggling. No need for this really though.
-        #for menu_elt in rootmenu_elt.findall("menu"):
-        #    for item_elt in menu_elt.findall("menuitem"):
-        #        if item_elt.attrib.get("action", "") == "ShowPopupMenu":
-        #            menu_elt.remove(item_elt)
-        ## Maybe shift a small number of frequently-used items to the top?
+        rootmenu_elt.attrib["name"] = name
         xml = ET.tostring(ui_elt)
         self.app.ui_manager.add_ui_from_string(xml)
-        tmp_menubar = self.app.ui_manager.get_widget('/PopupMenu')
-        self.popupmenu = gtk.Menu()
+        tmp_menubar = self.app.ui_manager.get_widget('/' + name)
+        popupmenu = gtk.Menu()
         for item in tmp_menubar.get_children():
             tmp_menubar.remove(item)
-            self.popupmenu.append(item)
-        self.popupmenu.attach_to_widget(self.app.doc.tdw, None)
-        #self.popupmenu.set_title("MyPaint")
-        #self.popupmenu.set_take_focus(True)
-        self.popupmenu.connect("selection-done", self.popupmenu_done_cb)
-        self.popupmenu.connect("deactivate", self.popupmenu_done_cb)
-        self.popupmenu.connect("cancel", self.popupmenu_done_cb)
+            popupmenu.append(item)
+        if owner is not None:
+            popupmenu.attach_to_widget(owner, None)
+        popupmenu.set_title("MyPaint")
+        popupmenu.connect("selection-done", self.popupmenu_done_cb)
+        popupmenu.connect("deactivate", self.popupmenu_done_cb)
+        popupmenu.connect("cancel", self.popupmenu_done_cb)
         self.popupmenu_last_active = None
+        return popupmenu
 
 
     def update_title(self, filename):
@@ -575,33 +612,86 @@ class Window (windowing.MainWindow, layout.MainWindow):
         state.activate(action)
 
 
-    # Show Toolbar
-    # Saved in the user prefs between sessions.
-    # Controlled via its ToggleAction only.
+    def brush_chooser_popup_cb(self, action):
+        # It may be even nicer to do this as a real popup state with
+        # mouse-out to cancel. The Action is named accordingly. For now
+        # though a modal dialog will do as an implementation.
+        dialogs.change_current_brush_quick(self.app)
+
+    def color_details_dialog_cb(self, action):
+        dialogs.change_current_color_detailed(self.app)
+
+
+    # User-toggleable UI pieces: things like toolbars, status bars, menu bars.
+    # Saved between sessions.
+
 
-    def set_show_toolbar(self, show_toolbar):
-        """Programatically set the Show Toolbar option.
+    def get_ui_part_enabled(self, part_name):
+        """Returns whether the named UI part is enabled in the prefs.
         """
-        action = self.action_group.get_action("ToggleToolbar")
-        if show_toolbar:
-            if not action.get_active():
-                action.set_active(True)
-            self.app.preferences["ui.toolbar"] = True
-        else:
-            if action.get_active():
-                action.set_active(False)
-            self.app.preferences["ui.toolbar"] = False
+        parts = self.app.preferences["ui.parts"]
+        return bool(parts.get(part_name, False))
 
-    def get_show_toolbar(self):
-        return self.app.preferences.get("ui.toolbar", True)
 
-    def toggle_toolbar_cb(self, action):
-        active = action.get_active()
-        if active:
+    def update_ui_parts(self, **updates):
+        """Updates the UI part prefs, then hide/show widgets to match.
+
+        Called without arguments, this updates the UI to match the
+        boolean-valued hash ``ui.parts`` in the app preferences. With keyword
+        arguments, the prefs are updated first, then changes are reflected in
+        the set of visible widgets. Current known parts:
+
+            :``main_toolbar``:
+                The primary toolbar and its menu button.
+            :``menubar``:
+                A conventional menu bar.
+
+        Currently the user cannot turn off both the main toolbar and the
+        menubar: the toolbar will be forced on if an attempt is made.
+        """
+        new_state = self.app.preferences["ui.parts"].copy()
+        new_state.update(updates)
+        # Menu bar
+        if new_state.get("menubar", False):
+            self.menubar.show_all()
+        else:
+            self.menubar.hide()
+            if not new_state.get("main_toolbar", False):
+                new_state["main_toolbar"] = True
+        # Toolbar
+        if new_state.get("main_toolbar", False):
             self.toolbar.show_all()
         else:
             self.toolbar.hide()
-        self.app.preferences["ui.toolbar"] = active
+        self.app.preferences["ui.parts"] = new_state
+        self.update_menu_button()
+
+
+    def update_menu_button(self):
+        """Updates the menu button to match toolbar and menubar visibility.
+
+        The menu button is visible when the menu bar is hidden. Since the user
+        must have either a toolbar or a menu or both, this ensures that a menu
+        is on-screen at all times in non-fullscreen mode.
+        """
+        toolbar_visible = self.toolbar.get_property("visible")
+        menubar_visible = self.menubar.get_property("visible")
+        if toolbar_visible and menubar_visible:
+            self.toolbar_manager.menu_button.hide()
+        else:
+            self.toolbar_manager.menu_button.show_all()
+
+
+    def on_menuishbar_radio_change(self, radioaction, current):
+        """Respond to a change of the 'menu bar/toolbar' radio menu items.
+        """
+        value = radioaction.get_current_value()
+        if value == self.MENUISHBAR_RADIO_MENUBAR:
+            self.update_ui_parts(main_toolbar=False, menubar=True)
+        elif value == self.MENUISHBAR_RADIO_MAIN_TOOLBAR:
+            self.update_ui_parts(main_toolbar=True, menubar=False)
+        else:
+            self.update_ui_parts(main_toolbar=True, menubar=True)
 
 
     # Show Subwindows
@@ -667,21 +757,36 @@ class Window (windowing.MainWindow, layout.MainWindow):
             while gtk.events_pending():
                 gtk.main_iteration()
             if getattr(self, "_restore_menubar_on_unfullscreen", False):
-                self.menubar.show()
+                if self.get_ui_part_enabled("menubar"):
+                    self.menubar.show()
                 del self._restore_menubar_on_unfullscreen
             if getattr(self, "_restore_toolbar_on_unfullscreen", False):
-                if self.get_show_toolbar():
+                if self.get_ui_part_enabled("main_toolbar"):
                     self.toolbar.show()
                 del self._restore_toolbar_on_unfullscreen
             if getattr(self, "_restore_subwindows_on_unfullscreen", False):
                 self.set_show_subwindows(True)
                 del self._restore_subwindows_on_unfullscreen
+        self.update_menu_button()
+        self.update_fullscreen_action()
+
+    def update_fullscreen_action(self):
+        action = self.action_group.get_action("Fullscreen")
+        if self.is_fullscreen:
+            action.set_stock_id(gtk.STOCK_LEAVE_FULLSCREEN)
+            action.set_tooltip(_("Leave Fullscreen Mode"))
+            action.set_label(_("UnFullscreen"))
+        else:
+            action.set_stock_id(gtk.STOCK_FULLSCREEN)
+            action.set_tooltip(_("Enter Fullscreen Mode"))
+            action.set_label(_("Fullscreen"))
 
     def popupmenu_show_cb(self, action):
         self.show_popupmenu()
 
     def show_popupmenu(self, event=None):
         self.menubar.set_sensitive(False)   # excessive feedback?
+        self.toolbar_manager.menu_button.set_sensitive(False)
         button = 1
         time = 0
         if event is not None:
@@ -699,16 +804,17 @@ class Window (windowing.MainWindow, layout.MainWindow):
 
     def popupmenu_done_cb(self, *a, **kw):
         # Not sure if we need to bother with this level of feedback,
-        # but it actaully looks quite nice to see one menu taking over
+        # but it actually looks quite nice to see one menu taking over
         # the other. Makes it clear that the popups are the same thing as
         # the full menu, maybe.
         self.menubar.set_sensitive(True)
+        self.toolbar_manager.menu_button.set_sensitive(True)
         self.popupmenu_last_active = self.popupmenu.get_active()
 
     # BEGIN -- Scratchpad menu options
     def save_scratchpad_as_default_cb(self, action):
         self.app.filehandler.save_scratchpad(self.app.filehandler.get_scratchpad_default(), export = True)
-    
+
     def clear_default_scratchpad_cb(self, action):
         self.app.filehandler.delete_default_scratchpad()
 
@@ -725,7 +831,7 @@ class Window (windowing.MainWindow, layout.MainWindow):
             bg = self.app.doc.model.background
             if self.app.scratchpad_doc:
                 self.app.scratchpad_doc.model.set_background(bg)
-            
+
         self.app.scratchpad_filename = self.app.preferences['scratchpad.last_opened'] = self.app.filehandler.get_scratchpad_autosave()
 
     def load_scratchpad_cb(self, action):
@@ -744,12 +850,12 @@ class Window (windowing.MainWindow, layout.MainWindow):
         self.app.filehandler.save_scratchpad_as_dialog()
 
     def revert_current_scratchpad_cb(self, action):
-        if os.path.isfile(self.app.scratchpad_filename):                                                             
+        if os.path.isfile(self.app.scratchpad_filename):
             self.app.filehandler.open_scratchpad(self.app.scratchpad_filename)
             print "Reverted to %s" % self.app.scratchpad_filename
         else:
             print "No file to revert to yet."
-    
+
     def save_current_scratchpad_cb(self, action):
         self.app.filehandler.save_scratchpad(self.app.scratchpad_filename)
 
@@ -808,6 +914,11 @@ class Window (windowing.MainWindow, layout.MainWindow):
         enabled = self.app.doc.model.frame_enabled
         self.app.doc.model.set_frame_enabled(not enabled)
 
+    def download_brush_pack_cb(self, *junk):
+        url = 'http://wiki.mypaint.info/index.php?title=Brush_Packages/redirect_mypaint_1.0_gui'
+        print 'URL:', url
+        webbrowser.open(url)
+
     def import_brush_pack_cb(self, *junk):
         format_id, filename = dialogs.open_dialog(_("Import brush package..."), self,
                                  [(_("MyPaint brush package (*.zip)"), "*.zip")])
@@ -837,30 +948,36 @@ class Window (windowing.MainWindow, layout.MainWindow):
         d.set_authors([
             # (in order of appearance)
             u"Martin Renold (%s)" % _('programming'),
-            u"Artis Rozentāls (%s)" % _('brushes'),
             u"Yves Combe (%s)" % _('portability'),
-            u"Popolon (%s)" % _('brushes, programming'),
+            u"Popolon (%s)" % _('programming'),
             u"Clement Skau (%s)" % _('programming'),
-            u"Marcelo 'Tanda' Cerviño (%s)" % _('patterns, brushes'),
             u"Jon Nordby (%s)" % _('programming'),
             u"Álinson Santos (%s)" % _('programming'),
             u"Tumagonx (%s)" % _('portability'),
             u"Ilya Portnov (%s)" % _('programming'),
-            u"David Revoy (%s)" % _('brushes'),
-            u"Ramón Miranda (%s)" % _('brushes, patterns'),
-            u"Enrico Guarnieri 'Ico_dY' (%s)" % _('brushes'),
             u"Jonas Wagner (%s)" % _('programming'),
             u"Luka Čehovin (%s)" % _('programming'),
             u"Andrew Chadwick (%s)" % _('programming'),
             u"Till Hartmann (%s)" % _('programming'),
-            u"Nicola Lunghi (%s)" % _('patterns'),
-            u"Toni Kasurinen (%s)" % _('brushes'),
-            u"Сан Саныч (%s)" % _('patterns'),
             u'David Grundberg (%s)' % _('programming'),
             u"Krzysztof Pasek (%s)" % _('programming'),
+            u"Ben O'Steen (%s)" % _('programming'),
+            u"Ferry Jérémie (%s)" % _('programming'),
+            u"しげっち 'sigetch' (%s)" % _('programming'),
             ])
         d.set_artists([
+            u"Artis Rozentāls (%s)" % _('brushes'),
+            u"Popolon (%s)" % _('brushes'),
+            u"Marcelo 'Tanda' Cerviño (%s)" % _('patterns, brushes'),
+            u"David Revoy (%s)" % _('brushes'),
+            u"Ramón Miranda (%s)" % _('brushes, patterns'),
+            u"Enrico Guarnieri 'Ico_dY' (%s)" % _('brushes'),
             u'Sebastian Kraft (%s)' % _('desktop icon'),
+            u"Nicola Lunghi (%s)" % _('patterns'),
+            u"Toni Kasurinen (%s)" % _('brushes'),
+            u"Сан Саныч 'MrMamurk' (%s)" % _('patterns'),
+            u"Andrew Chadwick (%s)" % _('tool icons'),
+            u"Ben O'Steen (%s)" % _('tool icons'),
             ])
         # list all translators, not only those of the current language
         d.set_translator_credits(
@@ -919,3 +1036,5 @@ class Window (windowing.MainWindow, layout.MainWindow):
                  "\n"),
         }
         self.app.message_dialog(text[action.get_name()])
+
+