diff options
Diffstat (limited to 'src/Dialogs.vala')
-rw-r--r-- | src/Dialogs.vala | 1784 |
1 files changed, 0 insertions, 1784 deletions
diff --git a/src/Dialogs.vala b/src/Dialogs.vala index d2f2cb0..2ca2678 100644 --- a/src/Dialogs.vala +++ b/src/Dialogs.vala @@ -134,345 +134,6 @@ public Gtk.ResponseType export_error_dialog(File dest, bool photos_remaining) { } -public class ExportDialog : Gtk.Dialog { - public const int DEFAULT_SCALE = 1200; - - // "Unmodified" and "Current," though they appear in the "Format:" popup menu, really - // aren't formats so much as they are operating modes that determine specific formats. - // Hereafter we'll refer to these as "special formats." - public const int NUM_SPECIAL_FORMATS = 2; - public const string UNMODIFIED_FORMAT_LABEL = _("Unmodified"); - public const string CURRENT_FORMAT_LABEL = _("Current"); - - public const ScaleConstraint[] CONSTRAINT_ARRAY = { ScaleConstraint.ORIGINAL, - ScaleConstraint.DIMENSIONS, ScaleConstraint.WIDTH, ScaleConstraint.HEIGHT }; - - public const Jpeg.Quality[] QUALITY_ARRAY = { Jpeg.Quality.LOW, Jpeg.Quality.MEDIUM, - Jpeg.Quality.HIGH, Jpeg.Quality.MAXIMUM }; - - private static ScaleConstraint current_constraint = ScaleConstraint.ORIGINAL; - private static ExportFormatParameters current_parameters = ExportFormatParameters.current(); - private static int current_scale = DEFAULT_SCALE; - - private Gtk.Grid table = new Gtk.Grid(); - private Gtk.ComboBoxText quality_combo; - private Gtk.ComboBoxText constraint_combo; - private Gtk.ComboBoxText format_combo; - private Gtk.Switch export_metadata; - private Gee.ArrayList<string> format_options = new Gee.ArrayList<string>(); - private Gtk.Entry pixels_entry; - private Gtk.Widget ok_button; - private bool in_insert = false; - - public ExportDialog(string title) { - bool use_header; - Gtk.Settings.get_default ().get ("gtk-dialogs-use-header", out use_header); - Object (use_header_bar: use_header ? 1 : 0); - - this.title = title; - resizable = false; - - //get information about the export settings out of our config backend - Config.Facade config = Config.Facade.get_instance(); - current_parameters.mode = config.get_export_export_format_mode(); //ExportFormatMode - current_parameters.specified_format = config.get_export_photo_file_format(); //PhotoFileFormat - current_parameters.quality = config.get_export_quality(); //quality - current_parameters.export_metadata = config.get_export_export_metadata(); //export metadata - current_constraint = config.get_export_constraint(); //constraint - current_scale = config.get_export_scale(); //scale - - quality_combo = new Gtk.ComboBoxText(); - int ctr = 0; - foreach (Jpeg.Quality quality in QUALITY_ARRAY) { - quality_combo.append_text(quality.to_string()); - if (quality == current_parameters.quality) - quality_combo.set_active(ctr); - ctr++; - } - - constraint_combo = new Gtk.ComboBoxText(); - ctr = 0; - foreach (ScaleConstraint constraint in CONSTRAINT_ARRAY) { - constraint_combo.append_text(constraint.to_string()); - if (constraint == current_constraint) - constraint_combo.set_active(ctr); - ctr++; - } - - format_combo = new Gtk.ComboBoxText(); - format_add_option(UNMODIFIED_FORMAT_LABEL); - format_add_option(CURRENT_FORMAT_LABEL); - foreach (PhotoFileFormat format in PhotoFileFormat.get_writeable()) { - format_add_option(format.get_properties().get_user_visible_name()); - } - - pixels_entry = new Gtk.Entry(); - pixels_entry.set_max_length(6); - pixels_entry.set_text("%d".printf(current_scale)); - - // register after preparation to avoid signals during init - constraint_combo.changed.connect(on_constraint_changed); - format_combo.changed.connect(on_format_changed); - pixels_entry.changed.connect(on_pixels_changed); - pixels_entry.insert_text.connect(on_pixels_insert_text); - pixels_entry.activate.connect(on_activate); - - // layout controls - add_label(_("_Format:"), 0, 0, format_combo); - add_control(format_combo, 1, 0); - - add_label(_("_Quality:"), 0, 1, quality_combo); - add_control(quality_combo, 1, 1); - - add_label(_("_Scaling constraint:"), 0, 2, constraint_combo); - add_control(constraint_combo, 1, 2); - - add_label(_("_Pixels:"), 0, 3, pixels_entry); - add_control(pixels_entry, 1, 3); - - export_metadata = new Gtk.Switch (); - add_label(_("Export _metadata:"), 0, 4, export_metadata); - add_control(export_metadata, 1, 4); - export_metadata.active = true; - export_metadata.halign = Gtk.Align.START; - - table.set_row_spacing(6); - table.set_column_spacing(12); - table.set_border_width(18); - - ((Gtk.Box) get_content_area()).add(table); - - // add buttons to action area - add_button(Resources.CANCEL_LABEL, Gtk.ResponseType.CANCEL); - ok_button = add_button(Resources.OK_LABEL, Gtk.ResponseType.OK); - set_default_response(Gtk.ResponseType.OK); - - ok_button.set_can_default(true); - ok_button.has_default = true; - set_default(ok_button); - - if (current_constraint == ScaleConstraint.ORIGINAL) { - pixels_entry.sensitive = false; - quality_combo.sensitive = false; - } - - ok_button.grab_focus(); - } - - private void format_add_option(string format_name) { - format_options.add(format_name); - format_combo.append_text(format_name); - } - - private void format_set_active_text(string text) { - int selection_ticker = 0; - - foreach (string current_text in format_options) { - if (current_text == text) { - format_combo.set_active(selection_ticker); - return; - } - selection_ticker++; - } - - error("format_set_active_text: text '%s' isn't in combo box", text); - } - - private PhotoFileFormat get_specified_format() { - int index = format_combo.get_active(); - if (index < NUM_SPECIAL_FORMATS) - index = NUM_SPECIAL_FORMATS; - - index -= NUM_SPECIAL_FORMATS; - PhotoFileFormat[] writeable_formats = PhotoFileFormat.get_writeable(); - return writeable_formats[index]; - } - - private string get_label_for_parameters(ExportFormatParameters params) { - switch(params.mode) { - case ExportFormatMode.UNMODIFIED: - return UNMODIFIED_FORMAT_LABEL; - - case ExportFormatMode.CURRENT: - return CURRENT_FORMAT_LABEL; - - case ExportFormatMode.SPECIFIED: - return params.specified_format.get_properties().get_user_visible_name(); - - default: - error("get_label_for_parameters: unrecognized export format mode"); - } - } - - // unlike other parameters, which should be persisted across dialog executions, the - // format parameters must be set each time the dialog is executed -- this is why - // it's passed qualified as ref and not as out - public bool execute(out int scale, out ScaleConstraint constraint, - ref ExportFormatParameters parameters) { - show_all(); - - // if the export format mode isn't set to last (i.e., don't use the persisted settings), - // reset the scale constraint to original size - if (parameters.mode != ExportFormatMode.LAST) { - current_constraint = constraint = ScaleConstraint.ORIGINAL; - constraint_combo.set_active(0); - } - - if (parameters.mode == ExportFormatMode.LAST) - parameters = current_parameters; - else if (parameters.mode == ExportFormatMode.SPECIFIED && !parameters.specified_format.can_write()) - parameters.specified_format = PhotoFileFormat.get_system_default_format(); - - format_set_active_text(get_label_for_parameters(parameters)); - on_format_changed(); - - bool ok = (run() == Gtk.ResponseType.OK); - if (ok) { - int index = constraint_combo.get_active(); - assert(index >= 0); - constraint = CONSTRAINT_ARRAY[index]; - current_constraint = constraint; - - scale = int.parse(pixels_entry.get_text()); - if (constraint != ScaleConstraint.ORIGINAL) - assert(scale > 0); - current_scale = scale; - - parameters.export_metadata = export_metadata.sensitive ? export_metadata.active : false; - - if (format_combo.get_active_text() == UNMODIFIED_FORMAT_LABEL) { - parameters.mode = current_parameters.mode = ExportFormatMode.UNMODIFIED; - } else if (format_combo.get_active_text() == CURRENT_FORMAT_LABEL) { - parameters.mode = current_parameters.mode = ExportFormatMode.CURRENT; - } else { - parameters.mode = current_parameters.mode = ExportFormatMode.SPECIFIED; - parameters.specified_format = current_parameters.specified_format = get_specified_format(); - if (current_parameters.specified_format == PhotoFileFormat.JFIF) - parameters.quality = current_parameters.quality = QUALITY_ARRAY[quality_combo.get_active()]; - } - - //save current settings in config backend for reusing later - Config.Facade config = Config.Facade.get_instance(); - config.set_export_export_format_mode(current_parameters.mode); //ExportFormatMode - config.set_export_photo_file_format(current_parameters.specified_format); //PhotoFileFormat - config.set_export_quality(current_parameters.quality); //quality - config.set_export_export_metadata(current_parameters.export_metadata); //export metadata - config.set_export_constraint(current_constraint); //constraint - config.set_export_scale(current_scale); //scale - } else { - scale = 0; - constraint = ScaleConstraint.ORIGINAL; - } - - destroy(); - - return ok; - } - - private void add_label(string text, int x, int y, Gtk.Widget? widget = null) { - Gtk.Label new_label = new Gtk.Label.with_mnemonic(text); - new_label.halign = Gtk.Align.END; - new_label.valign = Gtk.Align.CENTER; - new_label.set_use_underline(true); - - if (widget != null) - new_label.set_mnemonic_widget(widget); - - table.attach(new_label, x, y, 1, 1); - } - - private void add_control(Gtk.Widget widget, int x, int y) { - widget.halign = Gtk.Align.FILL; - widget.valign = Gtk.Align.CENTER; - widget.hexpand = true; - widget.vexpand = true; - - table.attach(widget, x, y, 1, 1); - } - - private void on_constraint_changed() { - bool original = CONSTRAINT_ARRAY[constraint_combo.get_active()] == ScaleConstraint.ORIGINAL; - bool jpeg = format_combo.get_active_text() == - PhotoFileFormat.JFIF.get_properties().get_user_visible_name(); - pixels_entry.sensitive = !original; - quality_combo.sensitive = !original && jpeg; - if (original) - ok_button.sensitive = true; - else - on_pixels_changed(); - } - - private void on_format_changed() { - bool original = CONSTRAINT_ARRAY[constraint_combo.get_active()] == ScaleConstraint.ORIGINAL; - - if (format_combo.get_active_text() == UNMODIFIED_FORMAT_LABEL) { - // if the user wishes to export the media unmodified, then we just copy the original - // files, so parameterizing size, quality, etc. is impossible -- these are all - // just as they are in the original file. In this case, we set the scale constraint to - // original and lock out all the controls - constraint_combo.set_active(0); /* 0 == original size */ - constraint_combo.set_sensitive(false); - quality_combo.set_sensitive(false); - pixels_entry.sensitive = false; - export_metadata.active = false; - export_metadata.sensitive = false; - } else if (format_combo.get_active_text() == CURRENT_FORMAT_LABEL) { - // if the user wishes to export the media in its current format, we allow sizing but - // not JPEG quality customization, because in a batch of many photos, it's not - // guaranteed that all of them will be JPEGs or RAWs that get converted to JPEGs. Some - // could be PNGs, and PNG has no notion of quality. So lock out the quality control. - // If the user wants to set JPEG quality, he or she can explicitly specify the JPEG - // format. - constraint_combo.set_sensitive(true); - quality_combo.set_sensitive(false); - pixels_entry.sensitive = !original; - export_metadata.sensitive = true; - } else { - // if the user has chosen a specific format, then allow JPEG quality customization if - // the format is JPEG and the user is re-sizing the image, otherwise, disallow JPEG - // quality customization; always allow scaling. - constraint_combo.set_sensitive(true); - bool jpeg = get_specified_format() == PhotoFileFormat.JFIF; - quality_combo.sensitive = !original && jpeg; - export_metadata.sensitive = true; - } - } - - private void on_activate() { - response(Gtk.ResponseType.OK); - } - - private void on_pixels_changed() { - ok_button.sensitive = (pixels_entry.get_text_length() > 0) && (int.parse(pixels_entry.get_text()) > 0); - } - - private void on_pixels_insert_text(string text, int length, ref int position) { - // This is necessary because SignalHandler.block_by_func() is not properly bound - if (in_insert) - return; - - in_insert = true; - - if (length == -1) - length = (int) text.length; - - // only permit numeric text - string new_text = ""; - for (int ctr = 0; ctr < length; ctr++) { - if (text[ctr].isdigit()) { - new_text += ((char) text[ctr]).to_string(); - } - } - - if (new_text.length > 0) - pixels_entry.insert_text(new_text, (int) new_text.length, ref position); - - Signal.stop_emission_by_name(pixels_entry, "insert-text"); - - in_insert = false; - } -} - namespace ImportUI { private const int REPORT_FAILURE_COUNT = 4; internal const string SAVE_RESULTS_BUTTON_NAME = _("Save Details…"); @@ -939,10 +600,7 @@ public abstract class TextEntryDialogMediator { public TextEntryDialogMediator(string title, string label, string? initial_text = null, Gee.Collection<string>? completion_list = null, string? completion_delimiter = null) { - Gtk.Builder builder = AppWindow.create_builder(); dialog = new TextEntryDialog(); - dialog.get_content_area().add((Gtk.Box) builder.get_object("dialog-vbox2")); - dialog.set_builder(builder); dialog.setup(on_modify_validate, title, label, initial_text, completion_list, completion_delimiter); } @@ -959,10 +617,7 @@ public abstract class MultiTextEntryDialogMediator { private MultiTextEntryDialog dialog; public MultiTextEntryDialogMediator(string title, string label, string? initial_text = null) { - Gtk.Builder builder = AppWindow.create_builder(); dialog = new MultiTextEntryDialog(); - dialog.get_content_area().add((Gtk.Box) builder.get_object("dialog-vbox4")); - dialog.set_builder(builder); dialog.setup(on_modify_validate, title, label, initial_text); } @@ -989,328 +644,6 @@ public string build_alert_body_text(string? primary_text, string? secondary_text guarded_markup_escape_text(primary_text), secondary_text); } -// Entry completion for values separated by separators (e.g. comma in the case of tags) -// Partly inspired by the class of the same name in gtkmm-utils by Marko Anastasov -public class EntryMultiCompletion : Gtk.EntryCompletion { - private string delimiter; - - public EntryMultiCompletion(Gee.Collection<string> completion_list, string? delimiter) { - assert(delimiter == null || delimiter.length == 1); - this.delimiter = delimiter; - - set_model(create_completion_store(completion_list)); - set_text_column(0); - set_match_func(match_func); - } - - private static Gtk.ListStore create_completion_store(Gee.Collection<string> completion_list) { - Gtk.ListStore completion_store = new Gtk.ListStore(1, typeof(string)); - Gtk.TreeIter store_iter; - Gee.Iterator<string> completion_iter = completion_list.iterator(); - while (completion_iter.next()) { - completion_store.append(out store_iter); - completion_store.set(store_iter, 0, completion_iter.get(), -1); - } - - return completion_store; - } - - private bool match_func(Gtk.EntryCompletion completion, string key, Gtk.TreeIter iter) { - Gtk.TreeModel model = completion.get_model(); - string possible_match; - model.get(iter, 0, out possible_match); - - // Normalize key and possible matches to allow comparison of non-ASCII characters. - // Use a "COMPOSE" normalization to allow comparison to the position value returned by - // Gtk.Entry, i.e. one character=one position. Using the default normalization a character - // like "é" or "ö" would have a length of two. - possible_match = possible_match.casefold().normalize(-1, NormalizeMode.ALL_COMPOSE); - string normed_key = key.normalize(-1, NormalizeMode.ALL_COMPOSE); - - if (delimiter == null) { - return possible_match.has_prefix(normed_key.strip()); - } else { - if (normed_key.contains(delimiter)) { - // check whether cursor is before last delimiter - int offset = normed_key.char_count(normed_key.last_index_of_char(delimiter[0])); - int position = ((Gtk.Entry) get_entry()).get_position(); - if (position <= offset) - return false; // TODO: Autocompletion for tags not last in list - } - - string last_part = get_last_part(normed_key.strip(), delimiter); - - if (last_part.length == 0) - return false; // need at least one character to show matches - - return possible_match.has_prefix(last_part.strip()); - } - } - - public override bool match_selected(Gtk.TreeModel model, Gtk.TreeIter iter) { - string match; - model.get(iter, 0, out match); - - Gtk.Entry entry = (Gtk.Entry)get_entry(); - - string old_text = entry.get_text().normalize(-1, NormalizeMode.ALL_COMPOSE); - if (old_text.length > 0) { - if (old_text.contains(delimiter)) { - old_text = old_text.substring(0, old_text.last_index_of_char(delimiter[0]) + 1) + (delimiter != " " ? " " : ""); - } else - old_text = ""; - } - - string new_text = old_text + match + delimiter + (delimiter != " " ? " " : ""); - entry.set_text(new_text); - entry.set_position((int) new_text.length); - - return true; - } - - // Find last string after any delimiter - private static string get_last_part(string s, string delimiter) { - string[] split = s.split(delimiter); - - if((split != null) && (split[0] != null)) { - return split[split.length - 1]; - } else { - return ""; - } - } -} - -[GtkTemplate (ui = "/org/gnome/Shotwell/ui/set_background_dialog.ui")] -public class SetBackgroundPhotoDialog : Gtk.Dialog { - [GtkChild] - private Gtk.CheckButton desktop_background_checkbox; - [GtkChild] - private Gtk.CheckButton screensaver_checkbox; - - public SetBackgroundPhotoDialog() { - bool use_header; - Gtk.Settings.get_default ().get ("gtk-dialogs-use-header", out use_header); - Object(use_header_bar: use_header ? 1 : 0); - this.set_transient_for (AppWindow.get_instance()); - } - - [GtkCallback] - private void on_checkbox_clicked() { - set_response_sensitive (Gtk.ResponseType.OK, - desktop_background_checkbox.active || - screensaver_checkbox.active); - } - - public bool execute(out bool desktop_background, out bool screensaver) { - this.show_all(); - var result = this.run() == Gtk.ResponseType.OK; - this.hide (); - - desktop_background = desktop_background_checkbox.active; - screensaver = screensaver_checkbox.active; - - this.destroy(); - return result; - } -} - -[GtkTemplate (ui = "/org/gnome/Shotwell/ui/set_background_slideshow_dialog.ui")] -public class SetBackgroundSlideshowDialog : Gtk.Dialog { - [GtkChild] - private Gtk.CheckButton desktop_background_checkbox; - [GtkChild] - private Gtk.CheckButton screensaver_checkbox; - [GtkChild] - private Gtk.Scale delay_scale; - [GtkChild] - private Gtk.Label delay_value_label; - - private int delay_value = 0; - - public SetBackgroundSlideshowDialog() { - bool use_header; - Gtk.Settings.get_default ().get ("gtk-dialogs-use-header", out use_header); - Object(use_header_bar: use_header ? 1 : 0); - this.set_transient_for (AppWindow.get_instance()); - } - - public override void constructed () { - on_delay_scale_value_changed (); - } - - [GtkCallback] - private void on_checkbox_clicked() { - set_response_sensitive (Gtk.ResponseType.OK, - desktop_background_checkbox.active || - screensaver_checkbox.active); - } - - [GtkCallback] - private void on_delay_scale_value_changed() { - double value = delay_scale.adjustment.value; - - // f(x)=x^5 allows to have fine-grained values (seconds) to the left - // and very coarse-grained values (hours) to the right of the slider. - // We limit maximum value to 1 day and minimum to 5 seconds. - delay_value = (int) (Math.pow(value, 5) / Math.pow(90, 5) * 60 * 60 * 24 + 5); - - // convert to text and remove fractions from values > 1 minute - string text; - if (delay_value < 60) { - text = ngettext("%d second", "%d seconds", delay_value).printf(delay_value); - } else if (delay_value < 60 * 60) { - int minutes = delay_value / 60; - text = ngettext("%d minute", "%d minutes", minutes).printf(minutes); - delay_value = minutes * 60; - } else if (delay_value < 60 * 60 * 24) { - int hours = delay_value / (60 * 60); - text = ngettext("%d hour", "%d hours", hours).printf(hours); - delay_value = hours * (60 * 60); - } else { - text = _("1 day"); - delay_value = 60 * 60 * 24; - } - - delay_value_label.label = text; - } - - public bool execute(out int delay_value, out bool desktop_background, out bool screensaver) { - this.show_all(); - var result = this.run() == Gtk.ResponseType.OK; - this.hide (); - - delay_value = this.delay_value; - desktop_background = desktop_background_checkbox.active; - screensaver = screensaver_checkbox.active; - - this.destroy(); - return result; - } -} - - -public class TextEntryDialog : Gtk.Dialog { - public delegate bool OnModifyValidateType(string text); - - private unowned OnModifyValidateType on_modify_validate; - private Gtk.Entry entry; - private Gtk.Builder builder; - private Gtk.Button button1; - private Gtk.Button button2; - - public TextEntryDialog() { - bool use_header; - Gtk.Settings.get_default ().get ("gtk-dialogs-use-header", out use_header); - Object (use_header_bar: use_header ? 1 : 0); - } - - public void set_builder(Gtk.Builder builder) { - this.builder = builder; - } - - public void setup(OnModifyValidateType? modify_validate, string title, string label, - string? initial_text, Gee.Collection<string>? completion_list, string? completion_delimiter) { - set_title(title); - set_resizable(true); - set_parent_window(AppWindow.get_instance().get_parent_window()); - set_transient_for(AppWindow.get_instance()); - on_modify_validate = modify_validate; - - Gtk.Label name_label = builder.get_object("label") as Gtk.Label; - name_label.set_text(label); - - entry = builder.get_object("entry") as Gtk.Entry; - entry.set_text(initial_text != null ? initial_text : ""); - entry.grab_focus(); - entry.changed.connect(on_entry_changed); - - button1 = (Gtk.Button) add_button(Resources.CANCEL_LABEL, Gtk.ResponseType.CANCEL); - button2 = (Gtk.Button) add_button(Resources.SAVE_LABEL, Gtk.ResponseType.OK); - set_default_response(Gtk.ResponseType.OK); - - if (completion_list != null) { // Textfield with autocompletion - EntryMultiCompletion completion = new EntryMultiCompletion(completion_list, - completion_delimiter); - entry.set_completion(completion); - } - - set_default_response(Gtk.ResponseType.OK); - } - - public string? execute() { - string? text = null; - - // validate entry to start with - set_response_sensitive(Gtk.ResponseType.OK, on_modify_validate(entry.get_text())); - - show_all(); - - if (run() == Gtk.ResponseType.OK) - text = entry.get_text(); - - entry.changed.disconnect(on_entry_changed); - destroy(); - - return text; - } - - public void on_entry_changed() { - set_response_sensitive(Gtk.ResponseType.OK, on_modify_validate(entry.get_text())); - } -} - -public class MultiTextEntryDialog : Gtk.Dialog { - public delegate bool OnModifyValidateType(string text); - - private unowned OnModifyValidateType on_modify_validate; - private Gtk.TextView entry; - private Gtk.Builder builder; - private Gtk.Button button1; - private Gtk.Button button2; - - public MultiTextEntryDialog() { - bool use_header; - Gtk.Settings.get_default ().get ("gtk-dialogs-use-header", out use_header); - Object (use_header_bar: use_header ? 1 : 0); - } - - public void set_builder(Gtk.Builder builder) { - this.builder = builder; - } - - public void setup(OnModifyValidateType? modify_validate, string title, string label, string? initial_text) { - set_title(title); - set_resizable(true); - set_default_size(500,300); - set_parent_window(AppWindow.get_instance().get_parent_window()); - set_transient_for(AppWindow.get_instance()); - on_modify_validate = modify_validate; - - entry = builder.get_object("textview1") as Gtk.TextView; - entry.set_wrap_mode (Gtk.WrapMode.WORD); - entry.buffer = new Gtk.TextBuffer(null); - entry.buffer.text = (initial_text != null ? initial_text : ""); - - entry.grab_focus(); - - button1 = (Gtk.Button) add_button(Resources.CANCEL_LABEL, Gtk.ResponseType.CANCEL); - button2 = (Gtk.Button) add_button(Resources.SAVE_LABEL, Gtk.ResponseType.OK); - set_default_response(Gtk.ResponseType.OK); - } - - public string? execute() { - string? text = null; - - show_all(); - - if (run() == Gtk.ResponseType.OK) - text = entry.buffer.text; - - destroy(); - - return text; - } -} public class EventRenameDialog : TextEntryDialogMediator { public EventRenameDialog(string? event_name) { @@ -1451,499 +784,6 @@ public bool remove_offline_dialog(Gtk.Window owner, int count) { return result == Gtk.ResponseType.OK; } -public class ProgressDialog : Gtk.Window { - private Gtk.ProgressBar progress_bar = new Gtk.ProgressBar(); - private Gtk.Button cancel_button = null; - private Cancellable cancellable; - private uint64 last_count = uint64.MAX; - private int update_every = 1; - private int minimum_on_screen_time_msec = 500; - private ulong time_started; -#if UNITY_SUPPORT - UnityProgressBar uniprobar = UnityProgressBar.get_instance(); -#endif - - public ProgressDialog(Gtk.Window? owner, string text, Cancellable? cancellable = null) { - this.cancellable = cancellable; - - set_title(text); - set_resizable(false); - if (owner != null) - set_transient_for(owner); - set_modal(true); - set_type_hint(Gdk.WindowTypeHint.DIALOG); - - progress_bar.set_size_request(300, -1); - progress_bar.set_show_text(true); - - Gtk.Box vbox_bar = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); - vbox_bar.pack_start(progress_bar, true, false, 0); - - if (cancellable != null) { - cancel_button = new Gtk.Button.with_mnemonic(Resources.CANCEL_LABEL); - cancel_button.clicked.connect(on_cancel); - delete_event.connect(on_window_closed); - } - - Gtk.Box hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 8); - hbox.pack_start(vbox_bar, true, false, 0); - if (cancel_button != null) - hbox.pack_end(cancel_button, false, false, 0); - - Gtk.Label primary_text_label = new Gtk.Label(""); - primary_text_label.set_markup("<span weight=\"bold\">%s</span>".printf(text)); - primary_text_label.xalign = 0.0f; - primary_text_label.yalign = 0.5f; - - Gtk.Box vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 12); - vbox.pack_start(primary_text_label, false, false, 0); - vbox.pack_start(hbox, true, false, 0); - vbox.halign = Gtk.Align.CENTER; - vbox.valign = Gtk.Align.CENTER; - vbox.hexpand = true; - vbox.vexpand = true; - vbox.margin_start = 12; - vbox.margin_end = 12; - vbox.margin_top = 12; - vbox.margin_bottom = 12; - - add(vbox); - - time_started = now_ms(); - } - - public override void realize() { - base.realize(); - - // if unable to cancel the progress bar, remove the close button - if (cancellable == null) - get_window().set_functions(Gdk.WMFunction.MOVE); - } - - public void update_display_every(int update_every) { - assert(update_every >= 1); - - this.update_every = update_every; - } - - public void set_minimum_on_screen_time_msec(int minimum_on_screen_time_msec) { - this.minimum_on_screen_time_msec = minimum_on_screen_time_msec; - } - - public void set_fraction(int current, int total) { - set_percentage((double) current / (double) total); - } - - public void set_percentage(double pct) { - pct = pct.clamp(0.0, 1.0); - - maybe_show_all(pct); - - progress_bar.set_fraction(pct); - progress_bar.set_text(_("%d%%").printf((int) (pct * 100.0))); - -#if UNITY_SUPPORT - //UnityProgressBar: set progress - uniprobar.set_progress(pct); -#endif - } - - public void set_status(string text) { - progress_bar.set_text(text); - -#if UNITY_SUPPORT - //UnityProgressBar: try to draw progress bar - uniprobar.set_visible(true); -#endif - show_all(); - } - - // This can be used as a ProgressMonitor delegate. - public bool monitor(uint64 count, uint64 total, bool do_event_loop = true) { - if ((last_count == uint64.MAX) || (count - last_count) >= update_every) { - set_percentage((double) count / (double) total); - last_count = count; - } - - bool keep_going = (cancellable != null) ? !cancellable.is_cancelled() : true; - - // TODO: get rid of this. non-trivial, as some progress-monitor operations are blocking - // and need to allow the event loop to spin - // - // Important: Since it's possible the progress dialog might be destroyed inside this call, - // avoid referring to "this" afterwards at all costs (in case all refs have been dropped) - - if (do_event_loop) - spin_event_loop(); - - return keep_going; - } - - public new void close() { -#if UNITY_SUPPORT - //UnityProgressBar: reset - uniprobar.reset(); -#endif - hide(); - destroy(); - } - - private bool on_window_closed() { - on_cancel(); - return false; // return false so that the system handler will remove the window from - // the screen - } - - private void on_cancel() { - if (cancellable != null) - cancellable.cancel(); - - cancel_button.sensitive = false; - } - - private void maybe_show_all(double pct) { - // Appear only after a while because some jobs may take only a - // fraction of second to complete so there's no point in showing progress. - if (!this.visible && now_ms() - time_started > minimum_on_screen_time_msec) { - // calculate percents completed in one ms - double pps = pct * 100.0 / minimum_on_screen_time_msec; - // calculate [very rough] estimate of time to complete in ms - double ttc = 100.0 / pps; - // If there is still more work to do for at least MINIMUM_ON_SCREEN_TIME_MSEC, - // finally display the dialog. - if (ttc > minimum_on_screen_time_msec) { -#if UNITY_SUPPORT - //UnityProgressBar: try to draw progress bar - uniprobar.set_visible(true); -#endif - show_all(); - spin_event_loop(); - } - } - } -} - -public class AdjustDateTimeDialog : Gtk.Dialog { - private const int64 SECONDS_IN_DAY = 60 * 60 * 24; - private const int64 SECONDS_IN_HOUR = 60 * 60; - private const int64 SECONDS_IN_MINUTE = 60; - private const int YEAR_OFFSET = 1900; - private bool no_original_time = false; - - private const int CALENDAR_THUMBNAIL_SCALE = 1; - - time_t original_time; - Gtk.Label original_time_label; - Gtk.Calendar calendar; - Gtk.SpinButton hour; - Gtk.SpinButton minute; - Gtk.SpinButton second; - Gtk.ComboBoxText system; - Gtk.RadioButton relativity_radio_button; - Gtk.RadioButton batch_radio_button; - Gtk.CheckButton modify_originals_check_button; - Gtk.Label notification; - - private enum TimeSystem { - AM, - PM, - 24HR; - } - - TimeSystem previous_time_system; - - public AdjustDateTimeDialog(Dateable source, int photo_count, bool display_options = true, - bool contains_video = false, bool only_video = false) { - assert(source != null); - - bool use_header; - Gtk.Settings.get_default ().get ("gtk-dialogs-use-header", out use_header); - Object(use_header_bar: use_header ? 1 : 0); - - set_modal(true); - set_resizable(false); - set_transient_for(AppWindow.get_instance()); - - add_buttons(Resources.CANCEL_LABEL, Gtk.ResponseType.CANCEL, - Resources.OK_LABEL, Gtk.ResponseType.OK); - set_title(Resources.ADJUST_DATE_TIME_LABEL); - - calendar = new Gtk.Calendar(); - calendar.day_selected.connect(on_time_changed); - calendar.month_changed.connect(on_time_changed); - calendar.next_year.connect(on_time_changed); - calendar.prev_year.connect(on_time_changed); - - if (Config.Facade.get_instance().get_use_24_hour_time()) - hour = new Gtk.SpinButton.with_range(0, 23, 1); - else - hour = new Gtk.SpinButton.with_range(1, 12, 1); - - hour.output.connect(on_spin_button_output); - hour.set_width_chars(2); - hour.set_max_width_chars(2); - - minute = new Gtk.SpinButton.with_range(0, 59, 1); - minute.set_width_chars(2); - minute.set_max_width_chars(2); - minute.output.connect(on_spin_button_output); - - second = new Gtk.SpinButton.with_range(0, 59, 1); - second.set_width_chars(2); - second.set_max_width_chars(2); - second.output.connect(on_spin_button_output); - - system = new Gtk.ComboBoxText(); - system.append_text(_("AM")); - system.append_text(_("PM")); - system.append_text(_("24 Hr")); - system.changed.connect(on_time_system_changed); - - Gtk.Box clock = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 3); - - clock.pack_start(hour, false, false, 0); - clock.pack_start(new Gtk.Label(":"), false, false, 0); // internationalize? - clock.pack_start(minute, false, false, 0); - clock.pack_start(new Gtk.Label(":"), false, false, 0); - clock.pack_start(second, false, false, 0); - clock.pack_start(system, false, false, 0); - - set_default_response(Gtk.ResponseType.OK); - - relativity_radio_button = new Gtk.RadioButton.with_mnemonic(null, - _("_Shift photos/videos by the same amount")); - relativity_radio_button.set_active(Config.Facade.get_instance().get_keep_relativity()); - relativity_radio_button.sensitive = display_options && photo_count > 1; - - batch_radio_button = new Gtk.RadioButton.with_mnemonic(relativity_radio_button.get_group(), - _("Set _all photos/videos to this time")); - batch_radio_button.set_active(!Config.Facade.get_instance().get_keep_relativity()); - batch_radio_button.sensitive = display_options && photo_count > 1; - batch_radio_button.toggled.connect(on_time_changed); - - if (contains_video) { - var text = ngettext ("_Modify original photo file", "_Modify original photo files", - photo_count); - modify_originals_check_button = new Gtk.CheckButton.with_mnemonic(text); - } else { - var text = ngettext ("_Modify original file", "_Modify original files", photo_count); - modify_originals_check_button = new Gtk.CheckButton.with_mnemonic(text); - } - - modify_originals_check_button.set_active(Config.Facade.get_instance().get_commit_metadata_to_masters() && - display_options); - modify_originals_check_button.sensitive = (!only_video) && - (!Config.Facade.get_instance().get_commit_metadata_to_masters() && display_options); - - Gtk.Box time_content = new Gtk.Box(Gtk.Orientation.VERTICAL, 5); - - time_content.pack_start(calendar, true, false, 0); - time_content.pack_start(clock, true, false, 0); - - if (display_options) { - time_content.pack_start(relativity_radio_button, true, false, 0); - time_content.pack_start(batch_radio_button, true, false, 0); - time_content.pack_start(modify_originals_check_button, true, false, 0); - } - - Gdk.Pixbuf preview = null; - try { - // Instead of calling get_pixbuf() here, we use the thumbnail instead; - // this was needed for Videos, since they don't support get_pixbuf(). - preview = source.get_thumbnail(CALENDAR_THUMBNAIL_SCALE); - } catch (Error err) { - warning("Unable to fetch preview for %s", source.to_string()); - } - - Gtk.Box image_content = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); - image_content.set_valign(Gtk.Align.START); - image_content.set_homogeneous(true); - Gtk.Image image = (preview != null) ? new Gtk.Image.from_pixbuf(preview) : new Gtk.Image(); - original_time_label = new Gtk.Label(null); - image_content.pack_start(image, true, false, 0); - image_content.pack_start(original_time_label, true, false, 0); - - Gtk.Box hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 10); - hbox.set_border_width(3); - hbox.pack_start(image_content, true, false, 0); - hbox.pack_start(time_content, true, false, 0); - hbox.halign = Gtk.Align.CENTER; - hbox.valign = Gtk.Align.CENTER; - hbox.hexpand = false; - hbox.vexpand = false; - - ((Gtk.Box) get_content_area()).pack_start(hbox, true, false, 0); - - notification = new Gtk.Label(""); - notification.set_line_wrap(true); - notification.set_justify(Gtk.Justification.CENTER); - - ((Gtk.Box) get_content_area()).pack_start(notification, true, true, 0); - - original_time = source.get_exposure_time(); - - if (original_time == 0) { - original_time = time_t(); - no_original_time = true; - } - - set_time(Time.local(original_time)); - set_original_time_label(Config.Facade.get_instance().get_use_24_hour_time()); - } - - private void set_time(Time time) { - calendar.select_month(time.month, time.year + YEAR_OFFSET); - calendar.select_day(time.day); - - if (Config.Facade.get_instance().get_use_24_hour_time()) { - system.set_active(TimeSystem.24HR); - hour.set_value(time.hour); - } else { - int AMPM_hour = time.hour % 12; - hour.set_value((AMPM_hour == 0) ? 12 : AMPM_hour); - system.set_active((time.hour >= 12) ? TimeSystem.PM : TimeSystem.AM); - } - - minute.set_value(time.minute); - second.set_value(time.second); - - previous_time_system = (TimeSystem) system.get_active(); - } - - private void set_original_time_label(bool use_24_hr_format) { - if (no_original_time) - return; - - original_time_label.set_text(_("Original: ") + - Time.local(original_time).format(use_24_hr_format ? _("%m/%d/%Y, %H:%M:%S") : - _("%m/%d/%Y, %I:%M:%S %p"))); - } - - private time_t get_time() { - Time time = Time(); - - time.second = (int) second.get_value(); - time.minute = (int) minute.get_value(); - - // convert to 24 hr - int hour = (int) hour.get_value(); - time.hour = (hour == 12 && system.get_active() != TimeSystem.24HR) ? 0 : hour; - time.hour += ((system.get_active() == TimeSystem.PM) ? 12 : 0); - - uint year, month, day; - calendar.get_date(out year, out month, out day); - time.year = ((int) year) - YEAR_OFFSET; - time.month = (int) month; - time.day = (int) day; - - time.isdst = -1; - - return time.mktime(); - } - - public bool execute(out int64 time_shift, out bool keep_relativity, - out bool modify_originals) { - show_all(); - - bool response = false; - - if (run() == Gtk.ResponseType.OK) { - if (no_original_time) - time_shift = (int64) get_time(); - else - time_shift = (int64) (get_time() - original_time); - - keep_relativity = relativity_radio_button.get_active(); - - if (relativity_radio_button.sensitive) - Config.Facade.get_instance().set_keep_relativity(keep_relativity); - - modify_originals = modify_originals_check_button.get_active(); - - if (modify_originals_check_button.sensitive) - Config.Facade.get_instance().set_modify_originals(modify_originals); - - response = true; - } else { - time_shift = 0; - keep_relativity = true; - modify_originals = false; - } - - destroy(); - - return response; - } - - private bool on_spin_button_output(Gtk.SpinButton button) { - button.set_text("%02d".printf((int) button.get_value())); - - on_time_changed(); - - return true; - } - - private void on_time_changed() { - int64 time_shift = ((int64) get_time() - (int64) original_time); - - previous_time_system = (TimeSystem) system.get_active(); - - if (time_shift == 0 || no_original_time || (batch_radio_button.get_active() && - batch_radio_button.sensitive)) { - notification.hide(); - } else { - bool forward = time_shift > 0; - int days, hours, minutes, seconds; - - time_shift = time_shift.abs(); - - days = (int) (time_shift / SECONDS_IN_DAY); - time_shift = time_shift % SECONDS_IN_DAY; - hours = (int) (time_shift / SECONDS_IN_HOUR); - time_shift = time_shift % SECONDS_IN_HOUR; - minutes = (int) (time_shift / SECONDS_IN_MINUTE); - seconds = (int) (time_shift % SECONDS_IN_MINUTE); - - string shift_status = (forward) ? - _("Exposure time will be shifted forward by\n%d %s, %d %s, %d %s, and %d %s.") : - _("Exposure time will be shifted backward by\n%d %s, %d %s, %d %s, and %d %s."); - - notification.set_text(shift_status.printf(days, ngettext("day", "days", days), - hours, ngettext("hour", "hours", hours), minutes, - ngettext("minute", "minutes", minutes), seconds, - ngettext("second", "seconds", seconds))); - - notification.show(); - } - } - - private void on_time_system_changed() { - if (previous_time_system == system.get_active()) - return; - - Config.Facade.get_instance().set_use_24_hour_time(system.get_active() == TimeSystem.24HR); - - if (system.get_active() == TimeSystem.24HR) { - int time = (hour.get_value() == 12.0) ? 0 : (int) hour.get_value(); - time = time + ((previous_time_system == TimeSystem.PM) ? 12 : 0); - - hour.set_range(0, 23); - set_original_time_label(true); - - hour.set_value(time); - } else { - int AMPM_hour = ((int) hour.get_value()) % 12; - - hour.set_range(1, 12); - set_original_time_label(false); - - hour.set_value((AMPM_hour == 0) ? 12 : AMPM_hour); - } - - on_time_changed(); - } -} - public const int MAX_OBJECTS_DISPLAYED = 3; public void multiple_object_error_dialog(Gee.ArrayList<DataObject> objects, string message, string title) { @@ -2062,630 +902,6 @@ public class ModifyTagsDialog : TagsDialog { } -public interface WelcomeServiceEntry : GLib.Object { - public abstract string get_service_name(); - - public abstract void execute(); -} - -public class WelcomeDialog : Gtk.Dialog { - Gtk.CheckButton hide_button; - Gtk.CheckButton? system_pictures_import_check = null; - Gtk.CheckButton[] external_import_checks = new Gtk.CheckButton[0]; - WelcomeServiceEntry[] external_import_entries = new WelcomeServiceEntry[0]; - Gtk.Label secondary_text; - Gtk.Label instruction_header; - Gtk.Box import_content; - Gtk.Box import_action_checkbox_packer; - Gtk.Box external_import_action_checkbox_packer; - Spit.DataImports.WelcomeImportMetaHost import_meta_host; - bool import_content_already_installed = false; - bool ok_clicked = false; - - public WelcomeDialog(Gtk.Window owner) { - import_meta_host = new Spit.DataImports.WelcomeImportMetaHost(this); - bool show_system_pictures_import = is_system_pictures_import_possible(); - Gtk.Widget ok_button = add_button(Resources.OK_LABEL, Gtk.ResponseType.OK); - set_title(_("Welcome!")); - set_resizable(false); - set_type_hint(Gdk.WindowTypeHint.DIALOG); - set_transient_for(owner); - - Gtk.Label primary_text = new Gtk.Label(""); - primary_text.set_markup( - "<span size=\"large\" weight=\"bold\">%s</span>".printf(_("Welcome to Shotwell!"))); - primary_text.xalign = 0.0f; - primary_text.yalign = 0.5f; - secondary_text = new Gtk.Label(""); - secondary_text.set_markup("<span weight=\"normal\">%s</span>".printf( - _("To get started, import photos in any of these ways:"))); - secondary_text.xalign = 0.0f; - secondary_text.yalign = 0.5f; - var image = new Gtk.Image.from_icon_name ("shotwell", Gtk.IconSize.DIALOG); - - Gtk.Box header_text = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); - header_text.pack_start(primary_text, false, false, 5); - header_text.pack_start(secondary_text, false, false, 0); - - Gtk.Box header_content = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 12); - header_content.pack_start(image, false, false, 0); - header_content.pack_start(header_text, false, false, 0); - - Gtk.Label instructions = new Gtk.Label(""); - string indent_prefix = " "; // we can't tell what the indent prefix is going to be so assume we need one - - string arrow_glyph = (get_direction() == Gtk.TextDirection.RTL) ? "◂" : "▸"; - - instructions.set_markup(((indent_prefix + "• %s\n") + (indent_prefix + "• %s\n") - + (indent_prefix + "• %s")).printf( - _("Choose <span weight=\"bold\">File %s Import From Folder</span>").printf(arrow_glyph), - _("Drag and drop photos onto the Shotwell window"), - _("Connect a camera to your computer and import"))); - instructions.xalign = 0.0f; - instructions.yalign = 0.5f; - - import_action_checkbox_packer = new Gtk.Box(Gtk.Orientation.VERTICAL, 2); - - external_import_action_checkbox_packer = new Gtk.Box(Gtk.Orientation.VERTICAL, 2); - import_action_checkbox_packer.add(external_import_action_checkbox_packer); - - if (show_system_pictures_import) { - system_pictures_import_check = new Gtk.CheckButton.with_mnemonic( - _("_Import photos from your %s folder").printf( - get_display_pathname(AppDirs.get_import_dir()))); - import_action_checkbox_packer.add(system_pictures_import_check); - system_pictures_import_check.set_active(true); - } - - instruction_header = new Gtk.Label( - _("You can also import photos in any of these ways:")); - instruction_header.xalign = 0.0f; - instruction_header.yalign = 0.5f; - instruction_header.set_margin_top(20); - - Gtk.Box content = new Gtk.Box(Gtk.Orientation.VERTICAL, 16); - content.pack_start(header_content, true, true, 0); - import_content = new Gtk.Box(Gtk.Orientation.VERTICAL, 2); - content.add(import_content); - content.pack_start(instructions, false, false, 0); - - hide_button = new Gtk.CheckButton.with_mnemonic(_("_Don’t show this message again")); - hide_button.set_active(true); - content.pack_start(hide_button, false, false, 6); - content.halign = Gtk.Align.FILL; - content.valign = Gtk.Align.FILL; - content.hexpand = false; - content.vexpand = false; - content.margin_top = 12; - content.margin_bottom = 0; - content.margin_start = 12; - content.margin_end = 12; - - ((Gtk.Box) get_content_area()).pack_start(content, false, false, 0); - - ok_button.grab_focus(); - - install_import_content(); - - import_meta_host.start(); - } - - private void install_import_content() { - if ( - (external_import_checks.length > 0 || system_pictures_import_check != null) && - (import_content_already_installed == false) - ) { - secondary_text.set_markup(""); - import_content.add(import_action_checkbox_packer); - import_content.add(instruction_header); - import_content_already_installed = true; - } - } - - public void install_service_entry(WelcomeServiceEntry entry) { - debug("WelcomeDialog: Installing service entry for %s".printf(entry.get_service_name())); - external_import_entries += entry; - Gtk.CheckButton entry_check = new Gtk.CheckButton.with_label( - _("Import photos from your %s library").printf(entry.get_service_name())); - external_import_checks += entry_check; - entry_check.set_active(true); - external_import_action_checkbox_packer.add(entry_check); - install_import_content(); - } - - /** - * Connected to the 'response' signal. This is part of a workaround - * for the fact that run()-ning this dialog can interfere with displaying - * images from a camera; please see #4997 for details. - */ - private void on_dismiss(int resp) { - if (resp == Gtk.ResponseType.OK) { - ok_clicked = true; - } - hide(); - Gtk.main_quit(); - } - - public bool execute(out WelcomeServiceEntry[] selected_import_entries, out bool do_system_pictures_import) { - // it's unsafe to call run() here - it interferes with displaying - // images from a camera - so we process the dialog ourselves. - response.connect(on_dismiss); - show_all(); - show(); - - // this will block the thread we're in until a matching call - // to main_quit() is encountered; this happens when either the window - // is closed or OK is clicked. - Gtk.main(); - - // at this point, the inner main loop will have been exited. - // we've got the response, so we don't need this signal anymore. - response.disconnect(on_dismiss); - - bool ok = ok_clicked; - bool show_dialog = true; - - if (ok) - show_dialog = !hide_button.get_active(); - - // Use a temporary variable as += cannot be used on parameters - WelcomeServiceEntry[] result = new WelcomeServiceEntry[0]; - for (int i = 0; i < external_import_entries.length; i++) { - if (external_import_checks[i].get_active() == true) - result += external_import_entries[i]; - } - selected_import_entries = result; - do_system_pictures_import = - (system_pictures_import_check != null) ? system_pictures_import_check.get_active() : false; - - destroy(); - - return show_dialog; - } - - private static bool is_system_pictures_import_possible() { - File system_pictures = AppDirs.get_import_dir(); - if (!system_pictures.query_exists(null)) - return false; - - if (!(system_pictures.query_file_type(FileQueryInfoFlags.NONE, null) == FileType.DIRECTORY)) - return false; - - try { - FileEnumerator syspics_child_enum = system_pictures.enumerate_children("standard::*", - FileQueryInfoFlags.NONE, null); - return (syspics_child_enum.next_file(null) != null); - } catch (Error e) { - return false; - } - } -} - -public class PreferencesDialog { - private class PathFormat { - public PathFormat(string name, string? pattern) { - this.name = name; - this.pattern = pattern; - } - public string name; - public string? pattern; - } - - private static PreferencesDialog preferences_dialog; - - private Gtk.Dialog dialog; - private Gtk.Builder builder; - private Gtk.Adjustment bg_color_adjustment; - private Gtk.Scale bg_color_slider; - private Gtk.ComboBox photo_editor_combo; - private Gtk.ComboBox raw_editor_combo; - private SortedList<AppInfo> external_raw_apps; - private SortedList<AppInfo> external_photo_apps; - private Gtk.FileChooserButton library_dir_button; - private Gtk.ComboBoxText dir_pattern_combo; - private Gtk.Entry dir_pattern_entry; - private Gtk.Label dir_pattern_example; - private bool allow_closing = false; - private string? lib_dir = null; - private Gee.ArrayList<PathFormat> path_formats = new Gee.ArrayList<PathFormat>(); - private GLib.DateTime example_date = new GLib.DateTime.local(2009, 3, 10, 18, 16, 11); - private Gtk.CheckButton lowercase; - private Plugins.ManifestWidgetMediator plugins_mediator = new Plugins.ManifestWidgetMediator(); - private Gtk.ComboBoxText default_raw_developer_combo; - - private PreferencesDialog() { - builder = AppWindow.create_builder(); - - dialog = builder.get_object("preferences_dialog") as Gtk.Dialog; - bool use_header; - Gtk.Settings.get_default ().get ("gtk-dialogs-use-header", out use_header); - if (!use_header) { - Gtk.Widget null_titlebar = null; - dialog.set_titlebar (null_titlebar); - } - dialog.set_parent_window(AppWindow.get_instance().get_parent_window()); - dialog.set_transient_for(AppWindow.get_instance()); - dialog.delete_event.connect(on_delete); - dialog.response.connect(on_close); - - bg_color_adjustment = builder.get_object("bg_color_adjustment") as Gtk.Adjustment; - bg_color_adjustment.set_value(bg_color_adjustment.get_upper() - - (Config.Facade.get_instance().get_bg_color().red * 65535.0)); - bg_color_adjustment.value_changed.connect(on_value_changed); - - bg_color_slider = builder.get_object("bg_color_slider") as Gtk.Scale; - bg_color_slider.button_press_event.connect(on_bg_color_reset); - - library_dir_button = builder.get_object("library_dir_button") as Gtk.FileChooserButton; - - photo_editor_combo = builder.get_object("external_photo_editor_combo") as Gtk.ComboBox; - raw_editor_combo = builder.get_object("external_raw_editor_combo") as Gtk.ComboBox; - - Gtk.Label pattern_help = builder.get_object("pattern_help") as Gtk.Label; - - // Ticket #3162 - Move dir pattern blurb into Gnome help. - // Because specifying a particular snippet of the help requires - // us to know where its located, we can't hardcode a URL anymore; - // instead, we ask for the help path, and if we find it, we tell - // yelp to read from there, otherwise, we read from system-wide. - string help_path = Resources.get_help_path(); - - if (help_path == null) { - // We're installed system-wide, so use the system help. - pattern_help.set_markup("<a href=\"" + Resources.DIR_PATTERN_URI_SYSWIDE + "\">" + _("(Help)") + "</a>"); - } else { - // We're being run from the build directory; we'll have to handle clicks to this - // link manually ourselves, due to a limitation of help: URIs. - pattern_help.set_markup("<a href=\"dummy:\">" + _("(Help)") + "</a>"); - pattern_help.activate_link.connect(on_local_pattern_help); - } - - dir_pattern_combo = builder.get_object("dir choser") as Gtk.ComboBoxText; - dir_pattern_entry = builder.get_object("dir_pattern_entry") as Gtk.Entry; - dir_pattern_example = builder.get_object("dynamic example") as Gtk.Label; - add_to_dir_formats(_("Year%sMonth%sDay").printf(Path.DIR_SEPARATOR_S, Path.DIR_SEPARATOR_S), - "%Y" + Path.DIR_SEPARATOR_S + "%m" + Path.DIR_SEPARATOR_S + "%d"); - add_to_dir_formats(_("Year%sMonth").printf(Path.DIR_SEPARATOR_S), "%Y" + - Path.DIR_SEPARATOR_S + "%m"); - add_to_dir_formats(_("Year%sMonth-Day").printf(Path.DIR_SEPARATOR_S), - "%Y" + Path.DIR_SEPARATOR_S + "%m-%d"); - add_to_dir_formats(_("Year-Month-Day"), "%Y-%m-%d"); - add_to_dir_formats(_("Custom"), null); // Custom must always be last. - dir_pattern_combo.changed.connect(on_dir_pattern_combo_changed); - dir_pattern_entry.changed.connect(on_dir_pattern_entry_changed); - - (builder.get_object("dir_structure_label") as Gtk.Label).set_mnemonic_widget(dir_pattern_combo); - - lowercase = builder.get_object("lowercase") as Gtk.CheckButton; - lowercase.toggled.connect(on_lowercase_toggled); - - var notebook = builder.get_object("preferences-notebook") as Gtk.Notebook; - (notebook.get_nth_page (2) as Gtk.Container).add (plugins_mediator.widget); - - populate_preference_options(); - - photo_editor_combo.changed.connect(on_photo_editor_changed); - raw_editor_combo.changed.connect(on_raw_editor_changed); - - Gtk.CheckButton auto_import_button = builder.get_object("autoimport") as Gtk.CheckButton; - auto_import_button.set_active(Config.Facade.get_instance().get_auto_import_from_library()); - - Gtk.CheckButton commit_metadata_button = builder.get_object("write_metadata") as Gtk.CheckButton; - commit_metadata_button.set_active(Config.Facade.get_instance().get_commit_metadata_to_masters()); - - default_raw_developer_combo = builder.get_object("default_raw_developer") as Gtk.ComboBoxText; - default_raw_developer_combo.append_text(RawDeveloper.CAMERA.get_label()); - default_raw_developer_combo.append_text(RawDeveloper.SHOTWELL.get_label()); - set_raw_developer_combo(Config.Facade.get_instance().get_default_raw_developer()); - default_raw_developer_combo.changed.connect(on_default_raw_developer_changed); - - dialog.map_event.connect(map_event); - } - - public void populate_preference_options() { - populate_app_combo_box(photo_editor_combo, PhotoFileFormat.get_editable_mime_types(), - Config.Facade.get_instance().get_external_photo_app(), out external_photo_apps); - - populate_app_combo_box(raw_editor_combo, PhotoFileFormat.RAW.get_mime_types(), - Config.Facade.get_instance().get_external_raw_app(), out external_raw_apps); - - setup_dir_pattern(dir_pattern_combo, dir_pattern_entry); - - lowercase.set_active(Config.Facade.get_instance().get_use_lowercase_filenames()); - } - - // Ticket #3162, part II - if we're not yet installed, then we have to manually launch - // the help viewer and specify the full path to the subsection we want... - private bool on_local_pattern_help(string ignore) { - try { - Resources.launch_help(AppWindow.get_instance().get_screen(), "other-files.page"); - } catch (Error e) { - message("Unable to launch help: %s", e.message); - } - return true; - } - - private void populate_app_combo_box(Gtk.ComboBox combo_box, string[] mime_types, - string current_app_executable, out SortedList<AppInfo> external_apps) { - // get list of all applications for the given mime types - assert(mime_types.length != 0); - external_apps = DesktopIntegration.get_apps_for_mime_types(mime_types); - - if (external_apps.size == 0) - return; - - // populate application ComboBox with app names and icons - Gtk.CellRendererPixbuf pixbuf_renderer = new Gtk.CellRendererPixbuf(); - Gtk.CellRendererText text_renderer = new Gtk.CellRendererText(); - combo_box.clear(); - combo_box.pack_start(pixbuf_renderer, false); - combo_box.pack_start(text_renderer, false); - combo_box.add_attribute(pixbuf_renderer, "pixbuf", 0); - combo_box.add_attribute(text_renderer, "text", 1); - - // TODO: need more space between icons and text - Gtk.ListStore combo_store = new Gtk.ListStore(2, typeof(Gdk.Pixbuf), typeof(string)); - Gtk.TreeIter iter; - - int current_app = -1; - - foreach (AppInfo app in external_apps) { - combo_store.append(out iter); - - Icon app_icon = app.get_icon(); - try { - if (app_icon is FileIcon) { - combo_store.set_value(iter, 0, scale_pixbuf(new Gdk.Pixbuf.from_file( - ((FileIcon) app_icon).get_file().get_path()), Resources.DEFAULT_ICON_SCALE, - Gdk.InterpType.BILINEAR, false)); - } else if (app_icon is ThemedIcon) { - Gdk.Pixbuf icon_pixbuf = - Gtk.IconTheme.get_default().load_icon(((ThemedIcon) app_icon).get_names()[0], - Resources.DEFAULT_ICON_SCALE, Gtk.IconLookupFlags.FORCE_SIZE); - - combo_store.set_value(iter, 0, icon_pixbuf); - } - } catch (GLib.Error error) { - warning("Error loading icon pixbuf: " + error.message); - } - - combo_store.set_value(iter, 1, app.get_name()); - - if (app.get_commandline() == current_app_executable) - current_app = external_apps.index_of(app); - } - - // TODO: allow users to choose unlisted applications like Nautilus's "Open with -> Other Application..." - - combo_box.set_model(combo_store); - - if (current_app != -1) - combo_box.set_active(current_app); - } - - private void setup_dir_pattern(Gtk.ComboBox combo_box, Gtk.Entry entry) { - string? pattern = Config.Facade.get_instance().get_directory_pattern(); - bool found = false; - if (null != pattern) { - // Locate pre-built text. - int i = 0; - foreach (PathFormat pf in path_formats) { - if (pf.pattern == pattern) { - combo_box.set_active(i); - found = true; - break; - } - i++; - } - } else { - // Custom path. - string? s = Config.Facade.get_instance().get_directory_pattern_custom(); - if (!is_string_empty(s)) { - combo_box.set_active(path_formats.size - 1); // Assume "custom" is last. - found = true; - } - } - - if (!found) { - combo_box.set_active(0); - } - - on_dir_pattern_combo_changed(); - } - - public static void show() { - if (preferences_dialog == null) - preferences_dialog = new PreferencesDialog(); - - preferences_dialog.populate_preference_options(); - preferences_dialog.dialog.show_all(); - preferences_dialog.library_dir_button.set_current_folder(AppDirs.get_import_dir().get_path()); - - // Ticket #3001: Cause the dialog to become active if the user chooses 'Preferences' - // from the menus a second time. - preferences_dialog.dialog.present(); - } - - // For items that should only be committed when the dialog is closed, not as soon as the change - // is made. - private void commit_on_close() { - Config.Facade.get_instance().commit_bg_color(); - - Gtk.CheckButton? autoimport = builder.get_object("autoimport") as Gtk.CheckButton; - if (autoimport != null) - Config.Facade.get_instance().set_auto_import_from_library(autoimport.active); - - Gtk.CheckButton? commit_metadata = builder.get_object("write_metadata") as Gtk.CheckButton; - if (commit_metadata != null) - Config.Facade.get_instance().set_commit_metadata_to_masters(commit_metadata.active); - - if (lib_dir != null) - AppDirs.set_import_dir(lib_dir); - - PathFormat pf = path_formats.get(dir_pattern_combo.get_active()); - if (null == pf.pattern) { - Config.Facade.get_instance().set_directory_pattern_custom(dir_pattern_entry.text); - Config.Facade.get_instance().set_directory_pattern(null); - } else { - Config.Facade.get_instance().set_directory_pattern(pf.pattern); - } - } - - private bool on_delete() { - if (!get_allow_closing()) - return true; - - commit_on_close(); - return dialog.hide_on_delete(); //prevent widgets from getting destroyed - } - - private void on_close() { - if (!get_allow_closing()) - return; - - dialog.hide(); - commit_on_close(); - } - - private void on_value_changed() { - set_background_color((double)(bg_color_adjustment.get_upper() - - bg_color_adjustment.get_value()) / 65535.0); - } - - private bool on_bg_color_reset(Gdk.EventButton event) { - if (event.button == 1 && event.type == Gdk.EventType.BUTTON_PRESS - && has_only_key_modifier(event.state, Gdk.ModifierType.CONTROL_MASK)) { - // Left Mouse Button and CTRL pressed - bg_color_slider.set_value(bg_color_adjustment.get_upper() - - (parse_color(Config.Facade.DEFAULT_BG_COLOR).red * 65536.0f)); - on_value_changed(); - - return true; - } - - return false; - } - - private void on_dir_pattern_combo_changed() { - PathFormat pf = path_formats.get(dir_pattern_combo.get_active()); - if (null == pf.pattern) { - // Custom format. - string? dir_pattern = Config.Facade.get_instance().get_directory_pattern_custom(); - if (is_string_empty(dir_pattern)) - dir_pattern = ""; - dir_pattern_entry.set_text(dir_pattern); - dir_pattern_entry.editable = true; - dir_pattern_entry.sensitive = true; - } else { - dir_pattern_entry.set_text(pf.pattern); - dir_pattern_entry.editable = false; - dir_pattern_entry.sensitive = false; - } - } - - private void on_dir_pattern_entry_changed() { - string example = example_date.format(dir_pattern_entry.text); - if (is_string_empty(example) && !is_string_empty(dir_pattern_entry.text)) { - // Invalid pattern. - dir_pattern_example.set_text(_("Invalid pattern")); - dir_pattern_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "dialog-error"); - dir_pattern_entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, false); - set_allow_closing(false); - } else { - // Valid pattern. - dir_pattern_example.set_text(example); - dir_pattern_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, null); - set_allow_closing(true); - } - } - - private void set_allow_closing(bool allow) { - dialog.set_deletable(allow); - allow_closing = allow; - } - - private bool get_allow_closing() { - return allow_closing; - } - - private void set_background_color(double bg_color_value) { - Config.Facade.get_instance().set_bg_color(to_grayscale(bg_color_value)); - } - - private Gdk.RGBA to_grayscale(double color_value) { - Gdk.RGBA color = Gdk.RGBA(); - - color.red = color_value; - color.green = color_value; - color.blue = color_value; - color.alpha = 1.0; - - return color; - } - - private void on_photo_editor_changed() { - int photo_app_choice_index = (photo_editor_combo.get_active() < external_photo_apps.size) ? - photo_editor_combo.get_active() : external_photo_apps.size; - - AppInfo app = external_photo_apps.get_at(photo_app_choice_index); - - Config.Facade.get_instance().set_external_photo_app(DesktopIntegration.get_app_open_command(app)); - - debug("setting external photo editor to: %s", DesktopIntegration.get_app_open_command(app)); - } - - private void on_raw_editor_changed() { - int raw_app_choice_index = (raw_editor_combo.get_active() < external_raw_apps.size) ? - raw_editor_combo.get_active() : external_raw_apps.size; - - AppInfo app = external_raw_apps.get_at(raw_app_choice_index); - - Config.Facade.get_instance().set_external_raw_app(app.get_commandline()); - - debug("setting external raw editor to: %s", app.get_commandline()); - } - - private RawDeveloper raw_developer_from_combo() { - if (default_raw_developer_combo.get_active() == 0) - return RawDeveloper.CAMERA; - return RawDeveloper.SHOTWELL; - } - - private void set_raw_developer_combo(RawDeveloper d) { - if (d == RawDeveloper.CAMERA) - default_raw_developer_combo.set_active(0); - else - default_raw_developer_combo.set_active(1); - } - - private void on_default_raw_developer_changed() { - Config.Facade.get_instance().set_default_raw_developer(raw_developer_from_combo()); - } - - private void on_current_folder_changed() { - lib_dir = library_dir_button.get_filename(); - } - - private bool map_event() { - // Set the signal for the lib dir button after the dialog is displayed, - // because the FileChooserButton has a nasty habit of selecting a - // different folder when displayed if the provided path doesn't exist. - // See ticket #3000 for more info. - library_dir_button.current_folder_changed.connect(on_current_folder_changed); - return true; - } - - private void add_to_dir_formats(string name, string? pattern) { - PathFormat pf = new PathFormat(name, pattern); - path_formats.add(pf); - dir_pattern_combo.append_text(name); - } - - private void on_lowercase_toggled() { - Config.Facade.get_instance().set_use_lowercase_filenames(lowercase.get_active()); - } -} - // This function is used to determine whether or not files should be copied or linked when imported. // Returns ACCEPT for copy, REJECT for link, and CANCEL for (drum-roll) cancel. public Gtk.ResponseType copy_files_dialog() { |