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