1# This file is part of MyPaint. 2# -*- coding: utf-8 -*- 3# Copyright (C) 2017 by the MyPaint Development Team 4# 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 2 of the License, or 8# (at your option) any later version. 9 10"""Bulk management of layer visibility: presentation layer and view.""" 11 12# Imports: 13 14from __future__ import print_function, division 15 16import gui.mvp 17import logging 18import gui.dialogs 19import gui.application 20import lib.layervis 21from lib.gettext import C_ 22from lib.xml import escape 23 24from lib.gibindings import Gtk 25 26 27# Module vars: 28 29logger = logging.getLogger(__name__) 30 31 32# Class definitions: 33 34class LayerViewUI (gui.mvp.BuiltUIPresenter, object): 35 """Presents the document's named layer views in a row of controls.""" 36 37 def __init__(self, docmodel): 38 super(LayerViewUI, self).__init__() 39 self._docmodel = docmodel 40 self._lvm = docmodel.layer_view_manager 41 self._app = gui.application.get_app() 42 43 def _init_model(self): 44 lvm = self._lvm 45 lvm.current_view_changed += self._current_view_changed_cb 46 lvm.view_names_changed += self._view_names_changed_cb 47 48 def init_view(self): 49 self._init_model() 50 store = Gtk.ListStore(str, str) # columns: <our_id, display_markup> 51 markup = C_( 52 "view controls: dropdown: item markup", 53 u"<i>{builtin_view_name}</i>", 54 ).format( 55 builtin_view_name=escape(lib.layervis.UNSAVED_VIEW_DISPLAY_NAME), 56 ) 57 # None has a special meaning for GTK combo ID columns, 58 # so we substitute the empty string. Corrolory: you can't name 59 # views to the empty string. 60 store.append([u"", markup]) 61 store.set_sort_column_id(0, Gtk.SortType.ASCENDING) 62 self._store = store 63 64 combo = self.view.current_view_combo 65 combo.set_model(store) 66 combo.set_id_column(0) 67 68 cell = self.view.layer_text_cell 69 combo.add_attribute(cell, "markup", 1) 70 71 self._refresh_ui() 72 73 # MVP implementation: 74 75 @property 76 def widget(self): 77 return self.view.layout_grid 78 79 # View updater callbacks: 80 81 @gui.mvp.view_updater 82 def _current_view_changed_cb(self, lvm): 83 self._update_current_view_name() 84 self._update_buttons() 85 86 @gui.mvp.view_updater 87 def _view_names_changed_cb(self, lvm): 88 self._update_view_combo_names() 89 90 @gui.mvp.view_updater 91 def _refresh_ui(self): 92 self._update_view_combo_names() 93 self._update_current_view_name() 94 self._update_buttons() 95 96 @gui.mvp.view_updater 97 def _update_view_combo_names(self): 98 """Update the combo box to reflect a changed list names.""" 99 model_names = set(self._lvm.view_names) 100 store = self._store 101 102 kept_names = set() 103 it = store.get_iter_first() 104 while it is not None: 105 name, = store.get(it, 0) 106 if isinstance(name, bytes): 107 name = name.decode("utf-8") 108 # Skip over our representation of the builtin 109 if name == u"": 110 it = store.iter_next(it) 111 continue 112 # Drop or keep 113 if name not in model_names: 114 it_still_valid = store.remove(it) # advances it 115 if not it_still_valid: 116 it = None 117 else: 118 it = store.iter_next(it) 119 kept_names.add(name) 120 121 for name in model_names: 122 if name not in kept_names: 123 markup = C_( 124 "view controls: dropdown: item markup", 125 u"{user_chosen_view_name}", 126 ).format( 127 user_chosen_view_name=escape(name), 128 ) 129 store.append([name, markup]) 130 131 @gui.mvp.view_updater 132 def _update_buttons(self): 133 current = self._lvm.current_view_name 134 op_buttons = [ 135 self.view.rename_button, 136 self.view.remove_button, 137 self.view.locked_button, 138 ] 139 for b in op_buttons: 140 b.set_sensitive(current is not None) 141 142 icon_name = "mypaint-object-unlocked-symbolic" 143 active = self._lvm.current_view_locked 144 if self._lvm.current_view_locked: 145 icon_name = "mypaint-object-locked-symbolic" 146 self.view.locked_image.set_from_icon_name( 147 icon_name, 148 Gtk.IconSize.BUTTON, 149 ) 150 btn = self.view.locked_button 151 if bool(active) != bool(btn.get_active()): 152 btn.set_active(active) 153 154 @gui.mvp.view_updater 155 def _update_current_view_name(self): 156 combo = self.view.current_view_combo 157 id_ = self._lvm.current_view_name 158 if id_ is None: 159 id_ = "" 160 combo.set_active_id(id_) 161 162 # Model updaters: 163 164 @gui.mvp.model_updater 165 def _add_button_clicked_cb(self, button): 166 doc = self._docmodel 167 name = self._lvm.current_view_name 168 if name is None: 169 name = lib.layervis.NEW_VIEW_IDENT 170 cmd = lib.layervis.AddLayerView(doc, name=name) 171 doc.do(cmd) 172 173 @gui.mvp.model_updater 174 def _remove_button_clicked_cb(self, button): 175 doc = self._docmodel 176 cmd = lib.layervis.RemoveActiveLayerView(doc) 177 doc.do(cmd) 178 179 @gui.mvp.model_updater 180 def _rename_button_clicked_cb(self, button): 181 old_name = self._lvm.current_view_name 182 if old_name is None: 183 return 184 new_name = gui.dialogs.ask_for_name( 185 title=C_( 186 "view controls: rename view dialog", 187 u"Rename View", 188 ), 189 widget=self._app.drawWindow, 190 default=old_name, 191 ) 192 if new_name is None: 193 return 194 new_name = new_name.strip() 195 if new_name == old_name: 196 return 197 doc = self._docmodel 198 cmd = lib.layervis.RenameActiveLayerView(doc, new_name) 199 doc.do(cmd) 200 201 @gui.mvp.model_updater 202 def _locked_button_toggled_cb(self, button): 203 if self._lvm.current_view_name is None: 204 return 205 new_locked = button.get_active() 206 doc = self._docmodel 207 cmd = lib.layervis.SetActiveLayerViewLocked(doc, locked=new_locked) 208 doc.do(cmd) 209 210 @gui.mvp.model_updater 211 def _current_view_combo_changed_cb(self, combo): 212 name = combo.get_active_id() 213 if name is None: 214 return # specifically means "nothing active" in GTK 215 if name == "": 216 name = None 217 if name == self._lvm.current_view_name: 218 return # nothing to do 219 doc = self._docmodel 220 cmd = lib.layervis.ActivateLayerView(doc, name) 221 doc.do(cmd) 222