1 // 2 // NewImageDialog.cs 3 // 4 // Author: 5 // Jonathan Pobst <monkey@jpobst.com> 6 // 7 // Copyright (c) 2015 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.Collections.Generic; 29 using System.Linq; 30 31 using Gtk; 32 using Mono.Unix; 33 using Pinta.Core; 34 35 namespace Pinta 36 { 37 public class NewImageDialog : Dialog 38 { 39 /// <summary> 40 /// Configures and builds a NewImageDialog object. 41 /// </summary> 42 /// <param name="imgWidth">Initial value of the width entry.</param> 43 /// <param name="imgHeight">Initial value of the height entry.</param> 44 /// <param name="isClipboardSize">Indicates if there is an image on the clipboard (and the size parameters represent the clipboard image size).</param> 45 private bool allow_background_color; 46 private bool has_clipboard; 47 private bool suppress_events; 48 49 private Gdk.Size clipboard_size; 50 51 private List<Gdk.Size> preset_sizes; 52 private PreviewArea preview; 53 54 private ComboBox preset_combo; 55 private Entry width_entry; 56 private Entry height_entry; 57 58 private RadioButton portrait_radio; 59 private RadioButton landscape_radio; 60 61 private RadioButton white_bg_radio; 62 private RadioButton secondary_bg_radio; 63 private RadioButton trans_bg_radio; 64 NewImageDialog(int initialWidth, int initialHeight, BackgroundType initial_bg_type, bool isClipboardSize)65 public NewImageDialog (int initialWidth, int initialHeight, BackgroundType initial_bg_type, bool isClipboardSize) 66 : base (string.Empty, PintaCore.Chrome.MainWindow, DialogFlags.Modal, Gtk.Stock.Cancel, Gtk.ResponseType.Cancel, Gtk.Stock.Ok, Gtk.ResponseType.Ok) 67 { 68 Title = Catalog.GetString ("New Image"); 69 WindowPosition = Gtk.WindowPosition.CenterOnParent; 70 71 // We don't show the background color option if it's the same as "White" 72 allow_background_color = PintaCore.Palette.SecondaryColor.ToColorBgra () != ColorBgra.White; 73 74 BorderWidth = 4; 75 VBox.Spacing = 4; 76 77 Resizable = false; 78 DefaultResponse = ResponseType.Ok; 79 80 Icon = PintaCore.Resources.GetIcon (Stock.New, 16); 81 AlternativeButtonOrder = new int[] { (int)ResponseType.Ok, (int)ResponseType.Cancel }; 82 83 has_clipboard = isClipboardSize; 84 clipboard_size = new Gdk.Size (initialWidth, initialHeight); 85 86 InitializePresets (); 87 BuildDialog (); 88 89 if (initial_bg_type == BackgroundType.SecondaryColor && allow_background_color) 90 secondary_bg_radio.Active = true; 91 else if (initial_bg_type == BackgroundType.Transparent) 92 trans_bg_radio.Active = true; 93 else 94 white_bg_radio.Active = true; 95 96 width_entry.Text = initialWidth.ToString (); 97 height_entry.Text = initialHeight.ToString (); 98 99 width_entry.GrabFocus (); 100 width_entry.SelectRegion (0, width_entry.Text.Length); 101 102 WireUpEvents (); 103 104 UpdateOrientation (); 105 UpdatePresetSelection (); 106 preview.Update (NewImageSize, NewImageBackground); 107 } 108 109 public int NewImageWidth { get { return int.Parse (width_entry.Text); } } 110 public int NewImageHeight { get { return int.Parse (height_entry.Text); } } 111 public Gdk.Size NewImageSize { get { return new Gdk.Size (NewImageWidth, NewImageHeight); } } 112 113 public enum BackgroundType 114 { 115 White, 116 Transparent, 117 SecondaryColor 118 } 119 120 public BackgroundType NewImageBackgroundType { 121 get { 122 if (white_bg_radio.Active) 123 return BackgroundType.White; 124 else if (trans_bg_radio.Active) 125 return BackgroundType.Transparent; 126 else 127 return BackgroundType.SecondaryColor; 128 } 129 } 130 131 public Cairo.Color NewImageBackground { 132 get { 133 switch (NewImageBackgroundType) { 134 case BackgroundType.White: 135 return new Cairo.Color (1, 1, 1); 136 case BackgroundType.Transparent: 137 return new Cairo.Color (1, 1, 1, 0); 138 case BackgroundType.SecondaryColor: 139 default: 140 return PintaCore.Palette.SecondaryColor; 141 } 142 } 143 } 144 145 146 private bool IsValidSize { 147 get { 148 int width = 0; 149 int height = 0; 150 151 if (!int.TryParse (width_entry.Text, out width)) 152 return false; 153 if (!int.TryParse (height_entry.Text, out height)) 154 return false; 155 156 return width > 0 && height > 0; 157 } 158 } 159 160 private Gdk.Size SelectedPresetSize { 161 get { 162 if (preset_combo.ActiveText == Catalog.GetString ("Clipboard") || preset_combo.ActiveText == Catalog.GetString ("Custom")) 163 return Gdk.Size.Empty; 164 165 var text_parts = preset_combo.ActiveText.Split (' '); 166 var width = int.Parse (text_parts[0]); 167 var height = int.Parse (text_parts[2]); 168 169 return new Gdk.Size (width, height); 170 } 171 } 172 InitializePresets()173 private void InitializePresets () 174 { 175 // Some arbitrary presets 176 preset_sizes = new List<Gdk.Size> (); 177 178 preset_sizes.Add (new Gdk.Size (640, 480)); 179 preset_sizes.Add (new Gdk.Size (800, 600)); 180 preset_sizes.Add (new Gdk.Size (1024, 768)); 181 preset_sizes.Add (new Gdk.Size (1600, 1200)); 182 } 183 BuildDialog()184 private void BuildDialog () 185 { 186 // Layout table for preset, width, and height 187 var layout_table = new Table (3, 2, false); 188 189 layout_table.RowSpacing = 5; 190 layout_table.ColumnSpacing = 6; 191 192 // Preset Combo 193 var size_label = new Label (Catalog.GetString ("Preset:")); 194 size_label.SetAlignment (1f, .5f); 195 196 var preset_entries = new List<string> (); 197 198 if (has_clipboard) 199 preset_entries.Add (Catalog.GetString ("Clipboard")); 200 201 preset_entries.Add (Catalog.GetString ("Custom")); 202 preset_entries.AddRange (preset_sizes.Select (p => string.Format ("{0} x {1}", p.Width, p.Height))); 203 204 preset_combo = new ComboBox (preset_entries.ToArray ()); 205 preset_combo.Active = 0; 206 207 layout_table.Attach (size_label, 0, 1, 0, 1, AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Fill, 0, 0); 208 layout_table.Attach (preset_combo, 1, 2, 0, 1, AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Fill, 0, 0); 209 210 // Width Entry 211 var width_label = new Label (Catalog.GetString ("Width:")); 212 width_label.SetAlignment (1f, .5f); 213 214 width_entry = new Entry (); 215 width_entry.WidthRequest = 50; 216 width_entry.ActivatesDefault = true; 217 218 var width_units = new Label (Catalog.GetString ("pixels")); 219 220 var width_hbox = new HBox (); 221 width_hbox.PackStart (width_entry, false, false, 0); 222 width_hbox.PackStart (width_units, false, false, 5); 223 224 layout_table.Attach (width_label, 0, 1, 1, 2, AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Fill, 0, 0); 225 layout_table.Attach (width_hbox, 1, 2, 1, 2, AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Fill, 0, 0); 226 227 // Height Entry 228 var height_label = new Label (Catalog.GetString ("Height:")); 229 height_label.SetAlignment (1f, .5f); 230 231 height_entry = new Entry (); 232 height_entry.WidthRequest = 50; 233 height_entry.ActivatesDefault = true; 234 235 var height_units = new Label (Catalog.GetString ("pixels")); 236 237 var height_hbox = new HBox (); 238 height_hbox.PackStart (height_entry, false, false, 0); 239 height_hbox.PackStart (height_units, false, false, 5); 240 241 layout_table.Attach (height_label, 0, 1, 2, 3, AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Fill, 0, 0); 242 layout_table.Attach (height_hbox, 1, 2, 2, 3, AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Fill, 0, 0); 243 244 // Orientation Radio options 245 var orientation_label = new Label (Catalog.GetString ("Orientation:")); 246 orientation_label.SetAlignment (0f, .5f); 247 248 portrait_radio = new RadioButton (Catalog.GetString ("Portrait")); 249 var portrait_image = new Image (PintaCore.Resources.GetIcon (Stock.OrientationPortrait, 16)); 250 251 var portrait_hbox = new HBox (); 252 253 portrait_hbox.PackStart (portrait_image, false, false, 7); 254 portrait_hbox.PackStart (portrait_radio, false, false, 0); 255 256 landscape_radio = new RadioButton (portrait_radio, Catalog.GetString ("Landscape")); 257 var landscape_image = new Image (PintaCore.Resources.GetIcon (Stock.OrientationLandscape, 16)); 258 259 var landscape_hbox = new HBox (); 260 261 landscape_hbox.PackStart (landscape_image, false, false, 7); 262 landscape_hbox.PackStart (landscape_radio, false, false, 0); 263 264 // Orientation VBox 265 var orientation_vbox = new VBox (); 266 orientation_vbox.PackStart (orientation_label, false, false, 4); 267 orientation_vbox.PackStart (portrait_hbox, false, false, 0); 268 orientation_vbox.PackStart (landscape_hbox, false, false, 0); 269 270 // Background Color options 271 var background_label = new Label (Catalog.GetString ("Background:")); 272 background_label.SetAlignment (0f, .5f); 273 274 white_bg_radio = new RadioButton (Catalog.GetString ("White")); 275 var image_white = new Image (GdkExtensions.CreateColorSwatch (16, new Gdk.Color (255, 255, 255))); 276 277 var hbox_white = new HBox (); 278 279 hbox_white.PackStart (image_white, false, false, 7); 280 hbox_white.PackStart (white_bg_radio, false, false, 0); 281 282 secondary_bg_radio = new RadioButton (white_bg_radio, Catalog.GetString ("Background Color")); 283 var image_bg = new Image (GdkExtensions.CreateColorSwatch (16, PintaCore.Palette.SecondaryColor.ToGdkColor ())); 284 285 var hbox_bg = new HBox (); 286 287 hbox_bg.PackStart (image_bg, false, false, 7); 288 hbox_bg.PackStart (secondary_bg_radio, false, false, 0); 289 290 trans_bg_radio = new RadioButton (secondary_bg_radio, Catalog.GetString ("Transparent")); 291 var image_trans = new Image (GdkExtensions.CreateTransparentColorSwatch (true)); 292 293 var hbox_trans = new HBox (); 294 295 hbox_trans.PackStart (image_trans, false, false, 7); 296 hbox_trans.PackStart (trans_bg_radio, false, false, 0); 297 298 // Background VBox 299 var background_vbox = new VBox (); 300 background_vbox.PackStart (background_label, false, false, 4); 301 background_vbox.PackStart (hbox_white, false, false, 0); 302 303 if (allow_background_color) 304 background_vbox.PackStart (hbox_bg, false, false, 0); 305 306 background_vbox.PackStart (hbox_trans, false, false, 0); 307 308 // Put all the options together 309 var options_vbox = new VBox (); 310 options_vbox.Spacing = 10; 311 312 options_vbox.PackStart (layout_table, false, false, 3); 313 options_vbox.PackStart (orientation_vbox, false, false, 0); 314 options_vbox.PackStart (background_vbox, false, false, 4); 315 316 // Layout the preview + the options 317 preview = new PreviewArea (); 318 319 var preview_label = new Label (Catalog.GetString ("Preview")); 320 321 var preview_vbox = new VBox (); 322 preview_vbox.PackStart (preview_label, false, false, 0); 323 preview_vbox.PackStart (preview, true, true, 0); 324 325 326 var main_hbox = new HBox (false, 10); 327 328 main_hbox.PackStart (options_vbox, false, false, 0); 329 main_hbox.PackStart (preview_vbox, true, true, 0); 330 331 VBox.Add (main_hbox); 332 333 ShowAll (); 334 } 335 WireUpEvents()336 private void WireUpEvents () 337 { 338 // Handle preset combo changes 339 preset_combo.Changed += (o, e) => { 340 var new_size = IsValidSize ? NewImageSize : Gdk.Size.Empty; 341 342 if (has_clipboard && preset_combo.ActiveText == Catalog.GetString ("Clipboard")) 343 new_size = clipboard_size; 344 else if (preset_combo.ActiveText == Catalog.GetString ("Custom")) 345 return; 346 else 347 new_size = SelectedPresetSize; 348 349 suppress_events = true; 350 width_entry.Text = new_size.Width.ToString (); 351 height_entry.Text = new_size.Height.ToString (); 352 suppress_events = false; 353 354 UpdateOkButton (); 355 if (!IsValidSize) 356 return; 357 358 UpdateOrientation (); 359 preview.Update (NewImageSize); 360 }; 361 362 // Handle width/height entry changes 363 width_entry.Changed += (o, e) => { 364 if (suppress_events) 365 return; 366 367 UpdateOkButton (); 368 369 if (!IsValidSize) 370 return; 371 372 if (NewImageSize != SelectedPresetSize) 373 preset_combo.Active = has_clipboard ? 1 : 0; 374 375 UpdateOrientation (); 376 UpdatePresetSelection (); 377 preview.Update (NewImageSize); 378 }; 379 380 height_entry.Changed += (o, e) => { 381 if (suppress_events) 382 return; 383 384 UpdateOkButton (); 385 386 if (!IsValidSize) 387 return; 388 389 if (NewImageSize != SelectedPresetSize) 390 preset_combo.Active = has_clipboard ? 1 : 0; 391 392 UpdateOrientation (); 393 UpdatePresetSelection (); 394 preview.Update (NewImageSize); 395 }; 396 397 // Handle orientation changes 398 portrait_radio.Toggled += (o, e) => { 399 if (portrait_radio.Active && IsValidSize && NewImageWidth > NewImageHeight) { 400 var temp = NewImageWidth; 401 width_entry.Text = height_entry.Text; 402 height_entry.Text = temp.ToString (); 403 preview.Update (NewImageSize); 404 } 405 }; 406 407 landscape_radio.Toggled += (o, e) => { 408 if (landscape_radio.Active && IsValidSize && NewImageWidth < NewImageHeight) { 409 var temp = NewImageWidth; 410 width_entry.Text = height_entry.Text; 411 height_entry.Text = temp.ToString (); 412 preview.Update (NewImageSize); 413 } 414 }; 415 416 // Handle background color changes 417 white_bg_radio.Toggled += (o, e) => { if (white_bg_radio.Active) preview.Update (new Cairo.Color (1, 1, 1)); }; 418 secondary_bg_radio.Toggled += (o, e) => { if (secondary_bg_radio.Active) preview.Update (PintaCore.Palette.SecondaryColor); }; 419 trans_bg_radio.Toggled += (o, e) => { if (trans_bg_radio.Active ) preview.Update (new Cairo.Color (1, 1, 1, 0)); }; 420 } 421 UpdateOrientation()422 private void UpdateOrientation () 423 { 424 if (NewImageWidth < NewImageHeight && !portrait_radio.Active) 425 portrait_radio.Activate (); 426 else if (NewImageWidth > NewImageHeight && !landscape_radio.Active) 427 landscape_radio.Activate (); 428 429 for (var i = 1; i < preset_combo.GetItemCount (); i++) { 430 var text = preset_combo.GetValueAt<string> (i); 431 432 if (text == Catalog.GetString ("Clipboard") || text == Catalog.GetString ("Custom")) 433 continue; 434 435 var text_parts = text.Split ('x'); 436 var width = int.Parse (text_parts[0].Trim ()); 437 var height = int.Parse (text_parts[1].Trim ()); 438 439 var new_size = new Gdk.Size (NewImageWidth < NewImageHeight ? Math.Min (width, height) : Math.Max (width, height), NewImageWidth < NewImageHeight ? Math.Max (width, height) : Math.Min (width, height)); 440 var new_text = string.Format ("{0} x {1}", new_size.Width, new_size.Height); 441 442 preset_combo.SetValueAt (i, new_text); 443 } 444 } 445 UpdateOkButton()446 private void UpdateOkButton () 447 { 448 foreach (Widget widget in ActionArea.Children) 449 if (widget is Button && (widget as Button).Label == "gtk-ok") 450 widget.Sensitive = IsValidSize; 451 } 452 UpdatePresetSelection()453 private void UpdatePresetSelection () 454 { 455 if (!IsValidSize) 456 return; 457 458 var text = string.Format ("{0} x {1}", NewImageWidth, NewImageHeight); 459 var index = preset_combo.FindValue (text); 460 461 if (index >= 0 && preset_combo.Active != index) 462 preset_combo.Active = index; 463 } 464 465 private class PreviewArea : DrawingArea 466 { 467 private Gdk.Size size; 468 private Cairo.Color color; 469 470 private int max_size = 250; 471 PreviewArea()472 public PreviewArea () 473 { 474 WidthRequest = 300; 475 } 476 Update(Gdk.Size size)477 public void Update (Gdk.Size size) 478 { 479 this.size = size; 480 481 this.QueueDraw (); 482 } 483 Update(Cairo.Color color)484 public void Update (Cairo.Color color) 485 { 486 this.color = color; 487 488 this.QueueDraw (); 489 } 490 Update(Gdk.Size size, Cairo.Color color)491 public void Update (Gdk.Size size, Cairo.Color color) 492 { 493 this.size = size; 494 this.color = color; 495 496 this.QueueDraw (); 497 } 498 OnExposeEvent(Gdk.EventExpose evnt)499 protected override bool OnExposeEvent (Gdk.EventExpose evnt) 500 { 501 base.OnExposeEvent (evnt); 502 503 if (size == Gdk.Size.Empty) 504 return true; 505 506 var preview_size = Gdk.Size.Empty; 507 var widget_size = GdkWindow.GetBounds (); 508 509 // Figure out the dimensions of the preview to draw 510 if (size.Width <= max_size && size.Height <= max_size) 511 preview_size = size; 512 else if (size.Width > size.Height) 513 preview_size = new Gdk.Size (max_size, (int)(max_size / ((float)size.Width / (float)size.Height))); 514 else 515 preview_size = new Gdk.Size ((int)(max_size / ((float)size.Height / (float)size.Width)), max_size); 516 517 using (var g = Gdk.CairoHelper.Create (GdkWindow)) { 518 var r = new Cairo.Rectangle ((widget_size.Width - preview_size.Width) / 2, (widget_size.Height - preview_size.Height) / 2, preview_size.Width, preview_size.Height); 519 520 if (color.A == 0) { 521 // Fill with transparent checkerboard pattern 522 using (var grid = GdkExtensions.CreateTransparentColorSwatch (false)) 523 using (var surf = grid.ToSurface ()) 524 using (var pattern = surf.ToTiledPattern ()) 525 g.FillRectangle (r, pattern); 526 } else { 527 // Fill with selected color 528 g.FillRectangle (r, color); 529 } 530 531 // Draw our canvas drop shadow 532 g.DrawRectangle (new Cairo.Rectangle (r.X - 1, r.Y - 1, r.Width + 2, r.Height + 2), new Cairo.Color (.5, .5, .5), 1); 533 g.DrawRectangle (new Cairo.Rectangle (r.X - 2, r.Y - 2, r.Width + 4, r.Height + 4), new Cairo.Color (.8, .8, .8), 1); 534 g.DrawRectangle (new Cairo.Rectangle (r.X - 3, r.Y - 3, r.Width + 6, r.Height + 6), new Cairo.Color (.9, .9, .9), 1); 535 } 536 537 return true; 538 } 539 } 540 } 541 } 542 543