1/* 2 * Copyright © 2010 Yuvaraj Pandian T <yuvipanda@yuvi.in> 3 * Copyright © 2010 daniel g. siegel <dgsiegel@gnome.org> 4 * Copyright © 2008 Filippo Argiolas <filippo.argiolas@gmail.com> 5 * 6 * Licensed under the GNU General Public License Version 2 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22using Gtk; 23 24[GtkTemplate (ui = "/org/gnome/Cheese/cheese-prefs.ui")] 25public class Cheese.PreferencesDialog : Gtk.Dialog 26{ 27 private Cheese.Camera camera; 28 29 private GLib.Settings settings; 30 31 [GtkChild] 32 private unowned Gtk.ComboBox photo_resolution_combo; 33 [GtkChild] 34 private unowned Gtk.ComboBox video_resolution_combo; 35 [GtkChild] 36 private unowned Gtk.ComboBox source_combo; 37 38 private Gtk.ListStore camera_model; 39 40 [GtkChild] 41 private unowned Gtk.Adjustment brightness_adjustment; 42 [GtkChild] 43 private unowned Gtk.Adjustment contrast_adjustment; 44 [GtkChild] 45 private unowned Gtk.Adjustment hue_adjustment; 46 [GtkChild] 47 private unowned Gtk.Adjustment saturation_adjustment; 48 49 [GtkChild] 50 private unowned Gtk.SpinButton burst_repeat_spin; 51 [GtkChild] 52 private unowned Gtk.SpinButton burst_delay_spin; 53 54 [GtkChild] 55 private unowned Gtk.CheckButton countdown_check; 56 [GtkChild] 57 private unowned Gtk.CheckButton flash_check; 58 59 private MediaMode current_mode; 60 61public PreferencesDialog (Cheese.Camera camera) 62{ 63 var gtk_settings = Gtk.Settings.get_default (); 64 Object (use_header_bar: gtk_settings.gtk_dialogs_use_header ? 1 : 0); 65 66 this.camera = camera; 67 68 settings = new GLib.Settings ("org.gnome.Cheese"); 69 70 setup_combo_box_models (); 71 initialize_camera_devices (); 72 initialize_values_from_settings (); 73 74 /* 75 * Connect signals only after all the widgets have been setup 76 * Stops a bunch of unnecessary signals from being fired 77 */ 78 camera.notify["num-camera-devices"].connect(this.on_camera_update_num_camera_devices); 79} 80 81 /** 82 * Set up combo box cell renderers. 83 */ 84 private void setup_combo_box_models () 85 { 86 CellRendererText cell = new CellRendererText (); 87 88 photo_resolution_combo.pack_start (cell, false); 89 photo_resolution_combo.set_attributes (cell, "text", 0); 90 91 video_resolution_combo.pack_start (cell, false); 92 video_resolution_combo.set_attributes (cell, "text", 0); 93 94 source_combo.pack_start (cell, false); 95 source_combo.set_attributes (cell, "text", 0); 96 } 97 98 /** 99 * Initialize and populate the camera device combo box model. 100 */ 101 private void initialize_camera_devices () 102 { 103 GLib.GenericArray<unowned Cheese.CameraDevice> devices = camera.get_camera_devices (); 104 camera_model = new Gtk.ListStore (2, typeof (string), typeof (Cheese.CameraDevice)); 105 106 source_combo.model = camera_model; 107 source_combo.sensitive = false; 108 109 devices.foreach(add_camera_device); 110 111 settings.set_string ("camera", camera.get_selected_device ().get_name ()); 112 setup_resolutions_for_device (camera.get_selected_device ()); 113 } 114 115 /** 116 * Initialize and populate the resolution combo box models for the device. 117 * 118 * @param device the Cheese.CameraDevice for which to enumerate resolutions 119 */ 120 private void setup_resolutions_for_device (Cheese.CameraDevice device) 121 { 122 var formats = device.get_format_list (); 123 Gtk.ListStore resolution_model = new Gtk.ListStore (2, typeof (string), 124 typeof (Cheese.VideoFormat)); 125 126 photo_resolution_combo.model = resolution_model; 127 video_resolution_combo.model = resolution_model; 128 129 formats.foreach ((format) => 130 { 131 TreeIter iter; 132 resolution_model.append (out iter); 133 resolution_model.set (iter, 134 0, format.width.to_string () + " × " + format.height.to_string (), 135 1, format); 136 if (camera.get_current_video_format ().width == format.width && 137 camera.get_current_video_format ().height == format.height) 138 { 139 photo_resolution_combo.set_active_iter (iter); 140 settings.set_int ("photo-x-resolution", format.width); 141 settings.set_int ("photo-y-resolution", format.height); 142 } 143 144 if (settings.get_int ("video-x-resolution") == format.width && 145 settings.get_int ("video-y-resolution") == format.height) 146 { 147 video_resolution_combo.set_active_iter (iter); 148 } 149 }); 150 151 /* Video resolution combo shows photo resolution by default if previous 152 * user choice is not found in settings or not supported by current device. 153 * These values are saved to settings. 154 */ 155 if (video_resolution_combo.get_active () == -1) 156 { 157 video_resolution_combo.set_active (photo_resolution_combo.get_active ()); 158 settings.set_int ("video-x-resolution", settings.get_int ("photo-x-resolution")); 159 settings.set_int ("video-y-resolution", settings.get_int ("photo-y-resolution")); 160 } 161 } 162 163 /** 164 * Take the user preferences from GSettings. 165 */ 166 private void initialize_values_from_settings () 167 { 168 brightness_adjustment.value = settings.get_double ("brightness"); 169 contrast_adjustment.value = settings.get_double ("contrast"); 170 hue_adjustment.value = settings.get_double ("hue"); 171 saturation_adjustment.value = settings.get_double ("saturation"); 172 173 settings.bind ("burst-repeat", burst_repeat_spin, "value", 174 SettingsBindFlags.DEFAULT); 175 burst_delay_spin.value = settings.get_int ("burst-delay") / 1000; 176 177 settings.bind ("countdown", countdown_check, "active", 178 SettingsBindFlags.DEFAULT); 179 settings.bind ("flash", flash_check, "active", 180 SettingsBindFlags.DEFAULT); 181 } 182 183 /** 184 * Update the active device to the active iter of the device combo box. 185 * 186 * @param combo the video device combo box 187 */ 188 [GtkCallback] 189 private void on_source_change (Gtk.ComboBox combo) 190 { 191 // TODO: Handle going from 1 to 0 devices, cleanly! 192 return_if_fail (camera.num_camera_devices > 0); 193 194 TreeIter iter; 195 Cheese.CameraDevice dev; 196 197 combo.get_active_iter (out iter); 198 combo.model.get (iter, 1, out dev); 199 camera.set_device (dev); 200 camera.switch_camera_device (); 201 setup_resolutions_for_device (camera.get_selected_device ()); 202 settings.set_string ("camera", dev.get_name ()); 203 } 204 205 /** 206 * Update the current photo capture resolution to the active iter of the 207 * photo resolution combo box. 208 * 209 * @param combo the photo resolution combo box 210 */ 211 [GtkCallback] 212 private void on_photo_resolution_change (Gtk.ComboBox combo) 213 { 214 TreeIter iter; 215 216 Cheese.VideoFormat format; 217 218 combo.get_active_iter (out iter); 219 combo.model.get (iter, 1, out format); 220 221 if (current_mode == MediaMode.PHOTO || current_mode == MediaMode.BURST) 222 camera.set_video_format (format); 223 224 settings.set_int ("photo-x-resolution", format.width); 225 settings.set_int ("photo-y-resolution", format.height); 226 } 227 228 /** 229 * Update the current video capture resolution to the active iter of the 230 * video resolution combo box. 231 * 232 * @param combo the video resolution combo box 233 */ 234 [GtkCallback] 235 private void on_video_resolution_change (Gtk.ComboBox combo) 236 { 237 TreeIter iter; 238 239 Cheese.VideoFormat format; 240 241 combo.get_active_iter (out iter); 242 combo.model.get (iter, 1, out format); 243 244 if (current_mode == MediaMode.VIDEO) 245 camera.set_video_format (format); 246 247 settings.set_int ("video-x-resolution", format.width); 248 settings.set_int ("video-y-resolution", format.height); 249 } 250 251 /** 252 * Hide the dialog when it is closed, rather than deleting it. 253 */ 254 [GtkCallback] 255 private bool on_delete () 256 { 257 return this.hide_on_delete (); 258 } 259 260 /** 261 * Hide the dialog when it is closed, rather than deleting it. 262 */ 263 [GtkCallback] 264 private void on_dialog_close (Gtk.Button button) 265 { 266 this.hide (); 267 } 268 269 /** 270 * Show the help for the preferences dialog. 271 * 272 * @param button the help button 273 */ 274 [GtkCallback] 275 private void on_dialog_help (Gtk.Button button) 276 { 277 try 278 { 279 Gtk.show_uri (this.get_screen (), "help:cheese/index#preferences", 280 Gdk.CURRENT_TIME); 281 } 282 catch 283 { 284 warning ("%s", "Error showing help"); 285 } 286 } 287 288 /** 289 * Change the burst-delay GSetting when changing the spin button. 290 * 291 * The burst delay is the time, in milliseconds, between individual photos in 292 * a burst. 293 * 294 * @param spinbutton the burst-delay spin button 295 */ 296 [GtkCallback] 297 private void on_burst_delay_change (Gtk.SpinButton spinbutton) 298 { 299 settings.set_int ("burst-delay", (int) spinbutton.value * 1000); 300 } 301 302 /** 303 * Change the brightness of the image, and update the GSetting, when changing 304 * the scale. 305 * 306 * @param adjustment the adjustment of the brightness Gtk.Scale 307 */ 308 [GtkCallback] 309 private void on_brightness_change (Gtk.Adjustment adjustment) 310 { 311 this.camera.set_balance_property ("brightness", adjustment.value); 312 settings.set_double ("brightness", adjustment.value); 313 } 314 315 /** 316 * Change the contrast of the image, and update the GSetting, when changing 317 * the scale. 318 * 319 * @param adjustment the adjustment of the contrast Gtk.Scale 320 */ 321 [GtkCallback] 322 private void on_contrast_change (Gtk.Adjustment adjustment) 323 { 324 this.camera.set_balance_property ("contrast", adjustment.value); 325 settings.set_double ("contrast", adjustment.value); 326 } 327 328 /** 329 * Change the hue of the image, and update the GSetting, when changing the 330 * scale. 331 * 332 * @param adjustment the adjustment of the hue Gtk.Scale 333 */ 334 [GtkCallback] 335 private void on_hue_change (Gtk.Adjustment adjustment) 336 { 337 this.camera.set_balance_property ("hue", adjustment.value); 338 settings.set_double ("hue", adjustment.value); 339 } 340 341 /** 342 * Change the saturation of the image, and update the GSetting, when changing 343 * the scale. 344 * 345 * @param adjustment the adjustment of the saturation Gtk.Scale 346 */ 347 [GtkCallback] 348 private void on_saturation_change (Gtk.Adjustment adjustment) 349 { 350 this.camera.set_balance_property ("saturation", adjustment.value); 351 settings.set_double ("saturation", adjustment.value); 352 } 353 354 /** 355 * Update the video device combo box when a camera device was added or 356 * removed. 357 */ 358 private void on_camera_update_num_camera_devices () 359 { 360 GLib.GenericArray<unowned Cheese.CameraDevice> devices = camera.get_camera_devices (); 361 unowned Cheese.CameraDevice dev; 362 363 // Add (if) / Remove (else) a camera device. 364 if (devices.length > camera_model.iter_n_children (null)) 365 { 366 dev = devices.get (devices.length - 1); 367 add_camera_device(dev); 368 } 369 else 370 { 371 // First camera device in the combobox. 372 TreeIter iter; 373 camera_model.get_iter_first (out iter); 374 375 // Combobox active element. 376 TreeIter active_iter; 377 Cheese.CameraDevice active_device; 378 source_combo.get_active_iter (out active_iter); 379 camera_model.get (active_iter, 1, out active_device, -1); 380 381 // Find which device was removed. 382 bool device_removed = false; 383 devices.foreach ((device) => 384 { 385 Cheese.CameraDevice new_device; 386 camera_model.get (iter, 1, out new_device, -1); 387 388 // Found the device that was removed. 389 if (device != new_device) 390 { 391 remove_camera_device (iter, new_device, active_device); 392 device_removed = true; 393 // Remember, this is from the anonymous function! 394 return; 395 } 396 camera_model.iter_next (ref iter); 397 }); 398 399 // Special case: the last device on the list was removed. 400 if (!device_removed) 401 { 402 Cheese.CameraDevice old_device; 403 camera_model.get (iter, 1, out old_device, -1); 404 remove_camera_device (iter, old_device, active_device); 405 } 406 } 407 408 settings.set_string ("camera", camera.get_selected_device ().get_name ()); 409 setup_resolutions_for_device (camera.get_selected_device ()); 410 } 411 412 /** 413 * Add the supplied camera device to the device combo box model. 414 * 415 * This method is intended to be used with the foreach method of GLib 416 * containers. 417 * 418 * @param device a Cheese.CameraDevice to add to the device combo box model 419 */ 420 private void add_camera_device (Cheese.CameraDevice device) 421 { 422 TreeIter iter; 423 424 camera_model.append (out iter); 425 camera_model.set (iter, 426 0, device.get_name (), 427 1, device); 428 429 if (camera.get_selected_device () == device) 430 source_combo.set_active_iter (iter); 431 432 if (camera_model.iter_n_children (null) > 1) 433 source_combo.sensitive = true; 434 } 435 436 /** 437 * Remove the supplied camera device from the device combo box model. 438 * 439 * @param iter the iterator of the device to remove 440 * @param device_node the device to remove from the combo box model 441 * @param active_device_node the currently-active camera device 442 */ 443 private void remove_camera_device (TreeIter iter, Cheese.CameraDevice device_node, 444 Cheese.CameraDevice active_device_node) 445 { 446 GLib.GenericArray<unowned Cheese.CameraDevice> devices = camera.get_camera_devices (); 447 448 // Check if the camera that we want to remove, is the active one 449 if (device_node == active_device_node) 450 { 451 if (devices.length > 0) 452 set_new_available_camera_device (iter); 453 else 454 this.hide (); 455 } 456 457#if VALA_0_36 458 camera_model.remove (ref iter); 459#else 460 camera_model.remove (iter); 461#endif 462 463 if (camera_model.iter_n_children (null) <= 1) 464 source_combo.sensitive = false; 465 } 466 467 /** 468 * Search for an available camera device and activate it in the device combo 469 * box model. 470 * 471 * @param iter a device in the combo box model to search either side of 472 */ 473 private void set_new_available_camera_device (TreeIter iter) 474 { 475 TreeIter new_iter = iter; 476 477 if (!camera_model.iter_next (ref new_iter)) 478 { 479 new_iter = iter; 480 camera_model.iter_previous (ref new_iter); 481 } 482 source_combo.set_active_iter (new_iter); 483 } 484 485 486 /** 487 * Set the current media mode (photo, video or burst). 488 * 489 * The current mode is used to update the video format on the Cheese.Camera 490 * when the resolution for the current mode is changed. 491 * 492 * @param mode the media mode to set 493 */ 494 public void set_current_mode (MediaMode mode) 495 { 496 current_mode = mode; 497 } 498} 499