1 // 2 // LayersListWidget.cs 3 // 4 // Author: 5 // Jonathan Pobst <monkey@jpobst.com> 6 // Greg Lowe <greg@vis.net.nz> 7 // 8 // Copyright (c) 2010 Jonathan Pobst 9 // 10 // Permission is hereby granted, free of charge, to any person obtaining a copy 11 // of this software and associated documentation files (the "Software"), to deal 12 // in the Software without restriction, including without limitation the rights 13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 // copies of the Software, and to permit persons to whom the Software is 15 // furnished to do so, subject to the following conditions: 16 // 17 // The above copyright notice and this permission notice shall be included in 18 // all copies or substantial portions of the Software. 19 // 20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 // THE SOFTWARE. 27 28 using System; 29 using System.Collections.Generic; 30 using System.ComponentModel; 31 using System.Linq; 32 using Gtk; 33 using Mono.Unix; 34 using Pinta.Core; 35 36 namespace Pinta.Gui.Widgets 37 { 38 [System.ComponentModel.ToolboxItem (true)] 39 public class LayersListWidget : ScrolledWindow 40 { 41 private TreeView tree; 42 private TreeStore store; 43 44 // For the active layer, we also draw the selection layer on top of it, 45 // so we can't directly use that layer's surface. 46 private Cairo.ImageSurface active_layer_surface; 47 private CanvasRenderer canvas_renderer = new CanvasRenderer (false); 48 49 private const int store_index_thumbnail = 0; 50 private const int store_index_name = 1; 51 private const int store_index_visibility = 2; 52 private const int store_index_layer = 3; 53 54 private const int thumbnail_width = 60; 55 private const int thumbnail_height = 40; 56 private const int thumbnail_column_width = 70; 57 58 private const int name_column_min_width = 100; 59 private const int name_column_max_width = 300; 60 61 private const int visibility_column_width = 30; 62 LayersListWidget()63 public LayersListWidget () 64 { 65 CanFocus = false; 66 SetSizeRequest (200, 200); 67 68 SetPolicy (PolicyType.Automatic, PolicyType.Automatic); 69 70 tree = new TreeView (); 71 72 tree.HeadersVisible = false; 73 tree.FixedHeightMode = true; 74 tree.Reorderable = false; 75 tree.EnableGridLines = TreeViewGridLines.None; 76 tree.EnableTreeLines = false; 77 tree.ShowExpanders = false; 78 tree.CanFocus = false; 79 80 var crs = new CellRendererSurface (thumbnail_width, thumbnail_height); 81 var col = new TreeViewColumn ("Thumbnail", crs, "surface", store_index_thumbnail); 82 col.Sizing = TreeViewColumnSizing.Fixed; 83 col.FixedWidth = thumbnail_column_width; 84 tree.AppendColumn (col); 85 86 var textCell = new CellRendererText (); 87 textCell.Ellipsize = Pango.EllipsizeMode.End; 88 col = new TreeViewColumn ("Name", textCell, "text", store_index_name); 89 col.Sizing = TreeViewColumnSizing.Fixed; 90 col.Expand = true; 91 col.MinWidth = name_column_min_width; 92 col.MaxWidth = name_column_max_width; 93 tree.AppendColumn (col); 94 95 var crt = new CellRendererToggle (); 96 crt.Activatable = true; 97 crt.Toggled += LayerVisibilityToggled; 98 99 col = new TreeViewColumn ("Visible", crt, "active", store_index_visibility); 100 col.Sizing = TreeViewColumnSizing.Fixed; 101 col.FixedWidth = visibility_column_width; 102 tree.AppendColumn (col); 103 104 store = new TreeStore (typeof (Cairo.ImageSurface), typeof (string), typeof (bool), typeof (Layer)); 105 106 tree.Model = store; 107 tree.RowActivated += HandleRowActivated; 108 109 Add (tree); 110 111 PintaCore.Layers.LayerAdded += HandleLayerAddedOrRemoved; 112 PintaCore.Layers.LayerRemoved += HandleLayerAddedOrRemoved; 113 PintaCore.Layers.SelectedLayerChanged += HandleSelectedLayerChanged; 114 PintaCore.Layers.LayerPropertyChanged += HandlePintaCoreLayersLayerPropertyChanged; 115 116 PintaCore.History.HistoryItemAdded += HandleHistoryItemAdded; 117 PintaCore.History.ActionRedone += HandleHistoryItemAdded; 118 PintaCore.History.ActionUndone += HandleHistoryItemAdded; 119 120 tree.CursorChanged += HandleLayerSelected; 121 122 123 ShowAll (); 124 } 125 GetSelectedLayerInTreeView()126 private UserLayer GetSelectedLayerInTreeView() 127 { 128 UserLayer layer = null; 129 TreeIter iter; 130 131 var paths = tree.Selection.GetSelectedRows (); 132 133 if (paths != null && paths.Length > 0 && store.GetIter (out iter, paths[0])) { 134 layer = store.GetValue(iter, store_index_layer) as UserLayer; 135 } 136 137 return layer; 138 } 139 SelectLayerInTreeView(int layerIndex)140 private void SelectLayerInTreeView (int layerIndex) 141 { 142 var path = new TreePath (new int[] { layerIndex }); 143 tree.Selection.SelectPath (path); 144 } 145 HandleLayerSelected(object o, EventArgs e)146 private void HandleLayerSelected (object o, EventArgs e) 147 { 148 var layer = GetSelectedLayerInTreeView (); 149 if (PintaCore.Layers.CurrentLayer != layer) 150 PintaCore.Layers.SetCurrentLayer (GetSelectedLayerInTreeView ()); 151 } 152 LayerVisibilityToggled(object o, ToggledArgs args)153 private void LayerVisibilityToggled (object o, ToggledArgs args) 154 { 155 TreeIter iter; 156 if (store.GetIter (out iter, new TreePath (args.Path))) { 157 bool b = (bool) store.GetValue (iter, store_index_visibility); 158 store.SetValue(iter, store_index_visibility, !b); 159 160 var layer = (UserLayer)store.GetValue(iter, store_index_layer); 161 SetLayerVisibility (layer, !b); 162 } 163 } 164 HandleHistoryItemAdded(object sender, EventArgs e)165 private void HandleHistoryItemAdded (object sender, EventArgs e) 166 { 167 // TODO: Handle this more efficiently. 168 Reset (); 169 } 170 HandleSelectedLayerChanged(object sender, EventArgs e)171 private void HandleSelectedLayerChanged (object sender, EventArgs e) 172 { 173 // TODO: Handle this more efficiently. 174 Reset (); 175 } 176 HandlePintaCoreLayersLayerPropertyChanged(object sender, PropertyChangedEventArgs e)177 void HandlePintaCoreLayersLayerPropertyChanged (object sender, PropertyChangedEventArgs e) 178 { 179 // TODO: Handle this more efficiently. 180 Reset (); 181 } 182 HandleLayerAddedOrRemoved(object sender, EventArgs e)183 private void HandleLayerAddedOrRemoved(object sender, EventArgs e) 184 { 185 // TODO: Handle this more efficiently. 186 Reset (); 187 188 // TODO: this should be handled elsewhere 189 PintaCore.Workspace.Invalidate (); 190 } 191 HandleRowActivated(object o, RowActivatedArgs args)192 private void HandleRowActivated(object o, RowActivatedArgs args) 193 { 194 // The double click to activate will have already selected the layer. 195 PintaCore.Actions.Layers.Properties.Activate (); 196 } 197 Reset()198 public void Reset () 199 { 200 store.Clear (); 201 202 if (active_layer_surface != null) { 203 (active_layer_surface as IDisposable).Dispose (); 204 active_layer_surface = null; 205 } 206 207 if (!PintaCore.Workspace.HasOpenDocuments) 208 return; 209 210 var doc = PintaCore.Workspace.ActiveDocument; 211 212 foreach (var layer in (doc.UserLayers as IEnumerable<Layer>).Reverse ()) { 213 var surf = layer.Surface; 214 215 // Draw the selection layer on top of the active layer. 216 if (layer == doc.CurrentUserLayer && doc.ShowSelectionLayer) { 217 active_layer_surface = CairoExtensions.CreateImageSurface (Cairo.Format.Argb32, thumbnail_width, 218 thumbnail_height); 219 canvas_renderer.Initialize (doc.ImageSize, 220 new Gdk.Size (thumbnail_width, thumbnail_height)); 221 222 var layers = new List<Layer> { layer, doc.SelectionLayer }; 223 canvas_renderer.Render (layers, active_layer_surface, Gdk.Point.Zero); 224 225 surf = active_layer_surface; 226 } 227 228 store.AppendValues (surf, layer.Name, !layer.Hidden, layer); 229 } 230 231 SelectLayerInTreeView (PintaCore.Layers.Count - PintaCore.Layers.CurrentLayerIndex - 1); 232 } 233 SetLayerVisibility(UserLayer layer, bool visibility)234 private void SetLayerVisibility(UserLayer layer, bool visibility) 235 { 236 if (layer != null) 237 layer.Hidden = !visibility; 238 239 var initial = new LayerProperties(layer.Name, visibility, layer.Opacity, layer.BlendMode); 240 var updated = new LayerProperties(layer.Name, !visibility, layer.Opacity, layer.BlendMode); 241 242 var historyItem = new UpdateLayerPropertiesHistoryItem ( 243 "Menu.Layers.LayerProperties.png", 244 (visibility) ? Catalog.GetString ("Layer Shown") : Catalog.GetString ("Layer Hidden"), 245 PintaCore.Layers.IndexOf (layer), 246 initial, 247 updated); 248 249 PintaCore.History.PushNewItem (historyItem); 250 251 //TODO Call this automatically when the layer visibility changes. 252 PintaCore.Workspace.Invalidate (); 253 } 254 } 255 } 256