1 // 2 // ViewActions.cs 3 // 4 // Author: 5 // Jonathan Pobst <monkey@jpobst.com> 6 // 7 // Copyright (c) 2010 Jonathan Pobst 8 // 9 // Permission is hereby granted, free of charge, to any person obtaining a copy 10 // of this software and associated documentation files (the "Software"), to deal 11 // in the Software without restriction, including without limitation the rights 12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 // copies of the Software, and to permit persons to whom the Software is 14 // furnished to do so, subject to the following conditions: 15 // 16 // The above copyright notice and this permission notice shall be included in 17 // all copies or substantial portions of the Software. 18 // 19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 // THE SOFTWARE. 26 27 using System; 28 using System.Globalization; 29 using Mono.Unix; 30 using Gtk; 31 32 namespace Pinta.Core 33 { 34 public class ViewActions 35 { 36 public Gtk.Action ZoomIn { get; private set; } 37 public Gtk.Action ZoomOut { get; private set; } 38 public Gtk.Action ZoomToWindow { get; private set; } 39 public Gtk.Action ZoomToSelection { get; private set; } 40 public Gtk.Action ActualSize { get; private set; } 41 public Gtk.ToggleAction ToolBar { get; private set; } 42 public Gtk.ToggleAction ImageTabs { get; private set; } 43 public Gtk.ToggleAction PixelGrid { get; private set; } 44 public Gtk.ToggleAction Rulers { get; private set; } 45 public Gtk.RadioAction Pixels { get; private set; } 46 public Gtk.RadioAction Inches { get; private set; } 47 public Gtk.RadioAction Centimeters { get; private set; } 48 public Gtk.Action Fullscreen { get; private set; } 49 50 public ToolBarComboBox ZoomComboBox { get; private set; } 51 public string[] ZoomCollection { get; private set; } 52 53 private string old_zoom_text = ""; 54 private bool zoom_to_window_activated = false; 55 56 public bool ZoomToWindowActivated { 57 get { return zoom_to_window_activated; } 58 set 59 { 60 zoom_to_window_activated = value; 61 old_zoom_text = ZoomComboBox.ComboBox.ActiveText; 62 } 63 } 64 ViewActions()65 public ViewActions () 66 { 67 Gtk.IconFactory fact = new Gtk.IconFactory (); 68 fact.Add ("Menu.View.ActualSize.png", new Gtk.IconSet (PintaCore.Resources.GetIcon ("Menu.View.ActualSize.png"))); 69 fact.Add ("Menu.View.Grid.png", new Gtk.IconSet (PintaCore.Resources.GetIcon ("Menu.View.Grid.png"))); 70 fact.Add ("Menu.View.Rulers.png", new Gtk.IconSet (PintaCore.Resources.GetIcon ("Menu.View.Rulers.png"))); 71 fact.Add ("Menu.View.ZoomIn.png", new Gtk.IconSet (PintaCore.Resources.GetIcon ("Menu.View.ZoomIn.png"))); 72 fact.Add ("Menu.View.ZoomOut.png", new Gtk.IconSet (PintaCore.Resources.GetIcon ("Menu.View.ZoomOut.png"))); 73 fact.Add ("Menu.View.ZoomToSelection.png", new Gtk.IconSet (PintaCore.Resources.GetIcon ("Menu.View.ZoomToSelection.png"))); 74 fact.Add ("Menu.View.ZoomToWindow.png", new Gtk.IconSet (PintaCore.Resources.GetIcon ("Menu.View.ZoomToWindow.png"))); 75 fact.AddDefault (); 76 77 ZoomIn = new Gtk.Action ("ZoomIn", Catalog.GetString ("Zoom In"), null, Stock.ZoomIn); 78 ZoomOut = new Gtk.Action ("ZoomOut", Catalog.GetString ("Zoom Out"), null, Stock.ZoomOut); 79 ZoomToWindow = new Gtk.Action ("ZoomToWindow", Catalog.GetString ("Best Fit"), null, Stock.ZoomFit); 80 ZoomToSelection = new Gtk.Action ("ZoomToSelection", Catalog.GetString ("Zoom to Selection"), null, "Menu.View.ZoomToSelection.png"); 81 ActualSize = new Gtk.Action ("ActualSize", Catalog.GetString ("Normal Size"), null, Stock.Zoom100); 82 ToolBar = new Gtk.ToggleAction ("Toolbar", Catalog.GetString ("Toolbar"), null, null); 83 ImageTabs = new Gtk.ToggleAction ("ImageTabs", Catalog.GetString ("Image Tabs"), null, null); 84 PixelGrid = new Gtk.ToggleAction ("PixelGrid", Catalog.GetString ("Pixel Grid"), null, "Menu.View.Grid.png"); 85 Rulers = new Gtk.ToggleAction ("Rulers", Catalog.GetString ("Rulers"), null, "Menu.View.Rulers.png"); 86 Pixels = new Gtk.RadioAction ("Pixels", Catalog.GetString ("Pixels"), null, null, 0); 87 Inches = new Gtk.RadioAction ("Inches", Catalog.GetString ("Inches"), null, null, 1); 88 Centimeters = new Gtk.RadioAction ("Centimeters", Catalog.GetString ("Centimeters"), null, null, 2); 89 Fullscreen = new Gtk.Action ("Fullscreen", Catalog.GetString ("Fullscreen"), null, Stock.Fullscreen); 90 91 ZoomCollection = new string[] { 92 ToPercent (36), 93 ToPercent (24), 94 ToPercent (16), 95 ToPercent (12), 96 ToPercent (8), 97 ToPercent (7), 98 ToPercent (6), 99 ToPercent (5), 100 ToPercent (4), 101 ToPercent (3), 102 ToPercent (2), 103 ToPercent (1.75), 104 ToPercent (1.5), 105 ToPercent (1.25), 106 ToPercent (1), 107 ToPercent (0.66), 108 ToPercent (0.5), 109 ToPercent (0.33), 110 ToPercent (0.25), 111 ToPercent (0.16), 112 ToPercent (0.12), 113 ToPercent (0.08), 114 ToPercent (0.05), 115 Catalog.GetString ("Window") 116 }; 117 ZoomComboBox = new ToolBarComboBox (90, DefaultZoomIndex(), true, ZoomCollection); 118 119 // Make sure these are the same group so only one will be selected at a time 120 Inches.Group = Pixels.Group; 121 Centimeters.Group = Pixels.Group; 122 123 // The toolbar is shown by default. 124 ToolBar.Active = true; 125 ImageTabs.Active = true; 126 } 127 128 #region Initialization CreateMainMenu(Gtk.Menu menu)129 public void CreateMainMenu (Gtk.Menu menu) 130 { 131 MenuItem show_pad = (MenuItem)menu.Children[0]; 132 menu.Remove (show_pad); 133 134 menu.Append (ToolBar.CreateMenuItem ()); 135 menu.Append (PixelGrid.CreateMenuItem ()); 136 menu.Append (Rulers.CreateMenuItem ()); 137 menu.Append (ImageTabs.CreateMenuItem ()); 138 menu.AppendSeparator (); 139 140 ImageMenuItem zoomin = ZoomIn.CreateAcceleratedMenuItem (Gdk.Key.plus, Gdk.ModifierType.ControlMask); 141 zoomin.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.equal, Gdk.ModifierType.ControlMask, AccelFlags.Visible)); 142 zoomin.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.equal, 0, AccelFlags.Visible)); 143 zoomin.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.KP_Add, Gdk.ModifierType.ControlMask, AccelFlags.Visible)); 144 zoomin.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.KP_Add, 0, AccelFlags.Visible)); 145 menu.Append (zoomin); 146 147 ImageMenuItem zoomout = ZoomOut.CreateAcceleratedMenuItem (Gdk.Key.minus, Gdk.ModifierType.ControlMask); 148 zoomout.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.minus, Gdk.ModifierType.ControlMask, AccelFlags.Visible)); 149 zoomout.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.minus, 0, AccelFlags.Visible)); 150 zoomout.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.KP_Subtract, Gdk.ModifierType.ControlMask, AccelFlags.Visible)); 151 zoomout.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.KP_Subtract, 0, AccelFlags.Visible)); 152 menu.Append (zoomout); 153 154 ImageMenuItem actualsize = ActualSize.CreateAcceleratedMenuItem (Gdk.Key.Key_0, Gdk.ModifierType.ControlMask); 155 actualsize.AddAccelerator ("activate", PintaCore.Actions.AccelGroup, new AccelKey (Gdk.Key.A, Gdk.ModifierType.ControlMask | Gdk.ModifierType.ShiftMask, AccelFlags.Visible)); 156 menu.Append (actualsize); 157 menu.Append (ZoomToWindow.CreateAcceleratedMenuItem (Gdk.Key.B, Gdk.ModifierType.ControlMask)); 158 //menu.Append (ZoomToSelection.CreateAcceleratedMenuItem (Gdk.Key.B, Gdk.ModifierType.ControlMask | Gdk.ModifierType.ShiftMask)); 159 menu.Append (Fullscreen.CreateAcceleratedMenuItem (Gdk.Key.F11, Gdk.ModifierType.None)); 160 161 menu.AppendSeparator (); 162 163 Gtk.Action unit_action = new Gtk.Action ("RulerUnits", Mono.Unix.Catalog.GetString ("Ruler Units"), null, null); 164 Menu unit_menu = (Menu)menu.AppendItem (unit_action.CreateSubMenuItem ()).Submenu; 165 unit_menu.Append (Pixels.CreateMenuItem ()); 166 unit_menu.Append (Inches.CreateMenuItem ()); 167 unit_menu.Append (Centimeters.CreateMenuItem ()); 168 169 menu.AppendSeparator (); 170 menu.Append (show_pad); 171 } 172 CreateToolBar(Gtk.Toolbar toolbar)173 public void CreateToolBar (Gtk.Toolbar toolbar) 174 { 175 toolbar.AppendItem (new Gtk.SeparatorToolItem ()); 176 toolbar.AppendItem (ZoomOut.CreateToolBarItem ()); 177 toolbar.AppendItem (ZoomComboBox); 178 toolbar.AppendItem (ZoomIn.CreateToolBarItem ()); 179 } 180 RegisterHandlers()181 public void RegisterHandlers () 182 { 183 ZoomIn.Activated += HandlePintaCoreActionsViewZoomInActivated; 184 ZoomOut.Activated += HandlePintaCoreActionsViewZoomOutActivated; 185 ZoomComboBox.ComboBox.Changed += HandlePintaCoreActionsViewZoomComboBoxComboBoxChanged; 186 (ZoomComboBox.ComboBox as Gtk.ComboBoxEntry).Entry.FocusOutEvent += new Gtk.FocusOutEventHandler (ComboBox_FocusOutEvent); 187 (ZoomComboBox.ComboBox as Gtk.ComboBoxEntry).Entry.FocusInEvent += new Gtk.FocusInEventHandler (Entry_FocusInEvent); 188 ActualSize.Activated += HandlePintaCoreActionsViewActualSizeActivated; 189 190 PixelGrid.Toggled += delegate (object sender, EventArgs e) { 191 PintaCore.Workspace.Invalidate (); 192 }; 193 194 var isFullscreen = false; 195 196 Fullscreen.Activated += (foo, bar) => { 197 if (!isFullscreen) 198 PintaCore.Chrome.MainWindow.Fullscreen (); 199 else 200 PintaCore.Chrome.MainWindow.Unfullscreen (); 201 202 isFullscreen = !isFullscreen; 203 }; 204 } 205 206 private string temp_zoom; 207 private bool suspend_zoom_change; 208 Entry_FocusInEvent(object o, Gtk.FocusInEventArgs args)209 private void Entry_FocusInEvent (object o, Gtk.FocusInEventArgs args) 210 { 211 temp_zoom = PintaCore.Actions.View.ZoomComboBox.ComboBox.ActiveText; 212 } 213 ComboBox_FocusOutEvent(object o, Gtk.FocusOutEventArgs args)214 private void ComboBox_FocusOutEvent (object o, Gtk.FocusOutEventArgs args) 215 { 216 string text = PintaCore.Actions.View.ZoomComboBox.ComboBox.ActiveText; 217 double percent; 218 219 if (!TryParsePercent (text, out percent)) { 220 (PintaCore.Actions.View.ZoomComboBox.ComboBox as Gtk.ComboBoxEntry).Entry.Text = temp_zoom; 221 return; 222 } 223 224 if (percent > 3600) 225 PintaCore.Actions.View.ZoomComboBox.ComboBox.Active = 0; 226 } 227 #endregion 228 229 /// <summary> 230 /// Converts the string representation of a percent (with or without a '%' sign) to a numeric value 231 /// </summary> TryParsePercent(string text, out double percent)232 public static bool TryParsePercent (string text, out double percent) 233 { 234 var culture = CultureInfo.CurrentCulture; 235 var format = culture.NumberFormat; 236 237 // In order to use double.TryParse, we must: 238 // - replace the decimal separator for percents with the regular separator. 239 // - remove the percent sign. We remove both the locale's percent sign and 240 // the standard one (U+0025). When running under mono, the 'fr' locale 241 // uses U+066A but the translation string uses U+0025, so there may be a bug in Mono. 242 // - remove the group separators, since they might be different from the regular 243 // group separator, and the group sizes could also be different. 244 text = text.Replace (format.PercentGroupSeparator, string.Empty); 245 text = text.Replace (format.PercentSymbol, string.Empty); 246 text = text.Replace ("%", string.Empty); 247 text = text.Replace (format.PercentDecimalSeparator, format.NumberDecimalSeparator); 248 text = text.Trim(); 249 250 return double.TryParse (text, 251 NumberStyles.AllowDecimalPoint | 252 NumberStyles.AllowLeadingWhite | 253 NumberStyles.AllowTrailingWhite, 254 culture, out percent); 255 } 256 257 /// <summary> 258 /// Convert the given number to a percentage string using the current locale. 259 /// </summary> ToPercent(double n)260 public static string ToPercent (double n) 261 { 262 var percent = (n * 100).ToString ("N0", CultureInfo.CurrentCulture); 263 // Translators: This specifies the format of the zoom percentage choices 264 // in the toolbar. 265 return string.Format (Catalog.GetString ("{0}%"), percent); 266 } 267 SuspendZoomUpdate()268 public void SuspendZoomUpdate () 269 { 270 suspend_zoom_change = true; 271 } 272 ResumeZoomUpdate()273 public void ResumeZoomUpdate () 274 { 275 suspend_zoom_change = false; 276 } 277 UpdateCanvasScale()278 public void UpdateCanvasScale () 279 { 280 string text = PintaCore.Actions.View.ZoomComboBox.ComboBox.ActiveText; 281 282 // stay in "Zoom to Window" mode if this function was called without the zoom level being changed by the user (e.g. if the 283 // image was rotated or cropped) and "Zoom to Window" mode is active 284 if (text == Catalog.GetString ("Window") || (ZoomToWindowActivated && old_zoom_text == text)) 285 { 286 PintaCore.Actions.View.ZoomToWindow.Activate (); 287 ZoomToWindowActivated = true; 288 return; 289 } 290 else 291 { 292 ZoomToWindowActivated = false; 293 } 294 295 double percent; 296 297 if (!TryParsePercent (text, out percent)) 298 return; 299 300 percent = Math.Min (percent, 3600); 301 percent = percent / 100.0; 302 303 PintaCore.Workspace.Scale = percent; 304 } 305 306 #region Action Handlers HandlePintaCoreActionsViewActualSizeActivated(object sender, EventArgs e)307 private void HandlePintaCoreActionsViewActualSizeActivated (object sender, EventArgs e) 308 { 309 int default_zoom = DefaultZoomIndex (); 310 if (ZoomComboBox.ComboBox.Active != default_zoom) 311 { 312 ZoomComboBox.ComboBox.Active = default_zoom; 313 UpdateCanvasScale (); 314 } 315 } 316 HandlePintaCoreActionsViewZoomComboBoxComboBoxChanged(object sender, EventArgs e)317 private void HandlePintaCoreActionsViewZoomComboBoxComboBoxChanged (object sender, EventArgs e) 318 { 319 if (suspend_zoom_change) 320 return; 321 322 PintaCore.Workspace.ActiveDocument.Workspace.ZoomManually (); 323 } 324 HandlePintaCoreActionsViewZoomOutActivated(object sender, EventArgs e)325 private void HandlePintaCoreActionsViewZoomOutActivated (object sender, EventArgs e) 326 { 327 PintaCore.Workspace.ActiveDocument.Workspace.ZoomOut (); 328 } 329 HandlePintaCoreActionsViewZoomInActivated(object sender, EventArgs e)330 private void HandlePintaCoreActionsViewZoomInActivated (object sender, EventArgs e) 331 { 332 PintaCore.Workspace.ActiveDocument.Workspace.ZoomIn (); 333 } 334 #endregion 335 336 /// <summary> 337 /// Returns the index in the ZoomCollection of the default zoom level 338 /// </summary> DefaultZoomIndex()339 private int DefaultZoomIndex() 340 { 341 return Array.IndexOf (ZoomCollection, ToPercent (1)); 342 } 343 } 344 } 345