1/* 2* Copyright (c) 2009-2013 Yorba Foundation 3* 2017-2018 elementary, Inc. (https://elementary.io) 4* 5* This program is free software; you can redistribute it and/or 6* modify it under the terms of the GNU Lesser General Public 7* License as published by the Free Software Foundation; either 8* version 2.1 of the License, or (at your option) any later version. 9* 10* This program is distributed in the hope that it will be useful, 11* but WITHOUT ANY WARRANTY; without even the implied warranty of 12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13* General Public License for more details. 14* 15* You should have received a copy of the GNU General Public 16* License along with this program; if not, write to the 17* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18* Boston, MA 02110-1301 USA 19*/ 20 21public class AdjustDateTimeDialog : Granite.Dialog { 22 private const int64 SECONDS_IN_DAY = 60 * 60 * 24; 23 private const int64 SECONDS_IN_HOUR = 60 * 60; 24 private const int64 SECONDS_IN_MINUTE = 60; 25 private bool no_original_time = false; 26 27 private const int CALENDAR_THUMBNAIL_SCALE = 1; 28 29 private int64 original_time; 30 private Gtk.Label original_time_label; 31 private Gtk.Calendar calendar; 32 private Gtk.SpinButton hour; 33 private Gtk.SpinButton minute; 34 private Gtk.SpinButton second; 35 private Gtk.ComboBoxText system; 36 private Gtk.RadioButton relativity_radio_button; 37 private Gtk.RadioButton batch_radio_button; 38 private Gtk.CheckButton modify_originals_check_button; 39 private Gtk.Label notification; 40 private GLib.Settings ui_settings; 41 private GLib.Settings file_settings; 42 43 private enum TimeSystem { 44 AM, 45 PM, 46 24HR; 47 } 48 49 private TimeSystem previous_time_system; 50 51 construct { 52 ui_settings = new GLib.Settings (GSettingsConfigurationEngine.UI_PREFS_SCHEMA_NAME); 53 file_settings = new GLib.Settings (GSettingsConfigurationEngine.FILES_PREFS_SCHEMA_NAME); 54 55 add_buttons ( 56 (_("_Cancel")), Gtk.ResponseType.CANCEL, 57 (_("_Apply")), Gtk.ResponseType.OK 58 ); 59 60 deletable = false; 61 modal = true; 62 resizable = false; 63 title = _(Resources.ADJUST_DATE_TIME_LABEL); 64 transient_for = AppWindow.get_instance (); 65 set_default_response (Gtk.ResponseType.OK); 66 } 67 68 public AdjustDateTimeDialog (Dateable source, int photo_count, bool display_options = true, 69 bool contains_video = false, bool only_video = false) { 70 assert (source != null); 71 72 calendar = new Gtk.Calendar (); 73 calendar.day_selected.connect (on_time_changed); 74 calendar.month_changed.connect (on_time_changed); 75 calendar.next_year.connect (on_time_changed); 76 calendar.prev_year.connect (on_time_changed); 77 78 if (ui_settings.get_boolean ("use-24-hour-time")) 79 hour = new Gtk.SpinButton.with_range (0, 23, 1); 80 else 81 hour = new Gtk.SpinButton.with_range (1, 12, 1); 82 83 hour.output.connect (on_spin_button_output); 84 hour.set_width_chars (2); 85 86 minute = new Gtk.SpinButton.with_range (0, 59, 1); 87 minute.set_width_chars (2); 88 minute.output.connect (on_spin_button_output); 89 90 second = new Gtk.SpinButton.with_range (0, 59, 1); 91 second.set_width_chars (2); 92 second.output.connect (on_spin_button_output); 93 94 system = new Gtk.ComboBoxText (); 95 system.append_text (_ ("AM")); 96 system.append_text (_ ("PM")); 97 system.append_text (_ ("24 Hr")); 98 system.changed.connect (on_time_system_changed); 99 100 relativity_radio_button = new Gtk.RadioButton.with_mnemonic (null, 101 _ ("_Shift photos/videos by the same amount")); 102 relativity_radio_button.set_active (ui_settings.get_boolean ("keep-relativity")); 103 relativity_radio_button.sensitive = display_options && photo_count > 1; 104 105 batch_radio_button = new Gtk.RadioButton.with_mnemonic (relativity_radio_button.get_group (), 106 _ ("Set _all photos/videos to this time")); 107 batch_radio_button.set_active (!ui_settings.get_boolean ("keep-relativity")); 108 batch_radio_button.sensitive = display_options && photo_count > 1; 109 batch_radio_button.toggled.connect (on_time_changed); 110 111 if (contains_video) { 112 modify_originals_check_button = new Gtk.CheckButton.with_mnemonic (ngettext ( 113 "_Modify original photo file", "_Modify original photo files", photo_count)); 114 } else { 115 modify_originals_check_button = new Gtk.CheckButton.with_mnemonic (ngettext ( 116 "_Modify original file", "_Modify original files", photo_count)); 117 } 118 119 modify_originals_check_button.set_active (file_settings.get_boolean ("commit-metadata") && display_options); 120 modify_originals_check_button.sensitive = (!only_video) && 121 (!file_settings.get_boolean ("commit-metadata") && display_options); 122 123 Gdk.Pixbuf preview = null; 124 try { 125 // Instead of calling get_pixbuf () here, we use the thumbnail instead; 126 // this was needed for Videos, since they don't support get_pixbuf (). 127 preview = source.get_thumbnail (CALENDAR_THUMBNAIL_SCALE); 128 } catch (Error err) { 129 warning ("Unable to fetch preview for %s", source.to_string ()); 130 } 131 132 var image = (preview != null) ? new Gtk.Image.from_pixbuf (preview) : new Gtk.Image (); 133 original_time_label = new Gtk.Label (null); 134 135 notification = new Gtk.Label (""); 136 notification.set_line_wrap (true); 137 notification.set_justify (Gtk.Justification.CENTER); 138 notification.set_size_request (-1, -1); 139 notification.set_padding (12, 6); 140 141 var clock_grid = new Gtk.Grid (); 142 clock_grid.column_spacing = 3; 143 clock_grid.add (hour); 144 clock_grid.add (new Gtk.Label (":")); // internationalize? 145 clock_grid.add (minute); 146 clock_grid.add (new Gtk.Label (":")); 147 clock_grid.add (second); 148 clock_grid.add (system); 149 150 var grid = new Gtk.Grid (); 151 grid.column_spacing = 12; 152 grid.row_spacing = 12; 153 grid.margin = 6; 154 grid.attach (image, 0, 0); 155 grid.attach (original_time_label, 0, 1); 156 grid.attach (calendar, 1, 0); 157 grid.attach (clock_grid, 1, 1); 158 grid.attach (notification, 0, 5, 2, 1); 159 160 if (display_options) { 161 grid.attach (relativity_radio_button, 1, 2); 162 grid.attach (batch_radio_button, 1, 3); 163 grid.attach (modify_originals_check_button, 1, 4); 164 } 165 166 get_content_area ().add (grid); 167 168 original_time = source.get_exposure_time (); 169 170 if (original_time == 0) { 171 original_time = time_t (); 172 no_original_time = true; 173 } 174 175 set_time (new DateTime.from_unix_local (original_time)); 176 set_original_time_label (ui_settings.get_boolean ("use-24-hour-time")); 177 } 178 179 private void set_time (DateTime time) { 180 calendar.select_month (time.get_month (), time.get_year ()); 181 calendar.select_day (time.get_day_of_month ()); 182 183 if (ui_settings.get_boolean ("use-24-hour-time")) { 184 hour.set_value (time.get_hour ()); 185 system.set_active (TimeSystem.24HR); 186 } else { 187 int ampm_hour = time.get_hour () % 12; 188 hour.set_value ((ampm_hour == 0) ? 12 : ampm_hour); 189 system.set_active ((time.get_hour () >= 12) ? TimeSystem.PM : TimeSystem.AM); 190 } 191 192 minute.set_value (time.get_minute ()); 193 second.set_value (time.get_second ()); 194 195 previous_time_system = (TimeSystem) system.get_active (); 196 } 197 198 private void set_original_time_label (bool use_24_hr_format) { 199 if (no_original_time) 200 return; 201 202 original_time_label.set_text ( 203 _ ("Original: %s").printf ( 204 (new DateTime.from_unix_local (original_time)).format ( 205 use_24_hr_format ? _ ("%m/%d/%Y, %H:%M:%S") : _ ("%m/%d/%Y, %I:%M:%S %p") 206 ) 207 ) 208 ); 209 } 210 211 private int64 get_time () { 212 // convert to 24 hr 213 int hour = (int) hour.get_value (); 214 hour = (hour == 12 && system.get_active () != TimeSystem.24HR) ? 0 : hour; 215 hour += ((system.get_active () == TimeSystem.PM) ? 12 : 0); 216 217 uint year, month, day; 218 calendar.get_date (out year, out month, out day); 219 var date_time = new DateTime.local ( 220 (int) year, (int) month, (int) day, hour, (int) minute.get_value (), second.get_value () 221 ); 222 return date_time.to_unix (); 223 } 224 225 public bool execute (out int64 time_shift, out bool keep_relativity, 226 out bool modify_originals) { 227 show_all (); 228 229 bool response = false; 230 231 if (run () == Gtk.ResponseType.OK) { 232 if (no_original_time) { 233 time_shift = get_time (); 234 } else { 235 time_shift = get_time () - original_time; 236 } 237 238 keep_relativity = relativity_radio_button.get_active (); 239 240 if (relativity_radio_button.sensitive) 241 ui_settings.set_boolean ("keep-relativity", keep_relativity); 242 243 modify_originals = modify_originals_check_button.get_active (); 244 245 if (modify_originals_check_button.sensitive) 246 ui_settings.set_boolean ("modify-originals", modify_originals); 247 248 response = true; 249 } else { 250 time_shift = 0; 251 keep_relativity = true; 252 modify_originals = false; 253 } 254 255 destroy (); 256 257 return response; 258 } 259 260 private bool on_spin_button_output (Gtk.SpinButton button) { 261 button.set_text ("%02d".printf ((int) button.get_value ())); 262 263 on_time_changed (); 264 265 return true; 266 } 267 268 private void on_time_changed () { 269 int64 time_shift = get_time () - original_time; 270 271 previous_time_system = (TimeSystem) system.get_active (); 272 273 if (time_shift == 0 || no_original_time || (batch_radio_button.get_active () && 274 batch_radio_button.sensitive)) { 275 notification.hide (); 276 } else { 277 bool forward = time_shift > 0; 278 int days, hours, minutes, seconds; 279 280 time_shift = time_shift.abs (); 281 282 days = (int) (time_shift / SECONDS_IN_DAY); 283 time_shift = time_shift % SECONDS_IN_DAY; 284 hours = (int) (time_shift / SECONDS_IN_HOUR); 285 time_shift = time_shift % SECONDS_IN_HOUR; 286 minutes = (int) (time_shift / SECONDS_IN_MINUTE); 287 seconds = (int) (time_shift % SECONDS_IN_MINUTE); 288 289 string shift_status = (forward) ? 290 _ ("Exposure time will be shifted forward by\n%d %s, %d %s, %d %s, and %d %s.") : 291 _ ("Exposure time will be shifted backward by\n%d %s, %d %s, %d %s, and %d %s."); 292 293 notification.set_text (shift_status.printf (days, ngettext ("day", "days", days), 294 hours, ngettext ("hour", "hours", hours), minutes, 295 ngettext ("minute", "minutes", minutes), seconds, 296 ngettext ("second", "seconds", seconds))); 297 298 notification.show (); 299 } 300 } 301 302 private void on_time_system_changed () { 303 if (previous_time_system == system.get_active ()) 304 return; 305 306 ui_settings.set_boolean ("use-24-hour-time", system.get_active () == TimeSystem.24HR); 307 308 if (system.get_active () == TimeSystem.24HR) { 309 int time = (hour.get_value () == 12.0) ? 0 : (int) hour.get_value (); 310 time = time + ((previous_time_system == TimeSystem.PM) ? 12 : 0); 311 312 hour.set_range (0, 23); 313 set_original_time_label (true); 314 315 hour.set_value (time); 316 } else { 317 int ampm_hour = ((int) hour.get_value ()) % 12; 318 319 hour.set_range (1, 12); 320 set_original_time_label (false); 321 322 hour.set_value ((ampm_hour == 0) ? 12 : ampm_hour); 323 } 324 325 on_time_changed (); 326 } 327} 328