Commit dadcf262 authored by Jim Nelson's avatar Jim Nelson

Fixes various problems in direct-edit identified by Adam, including Save/Save...

Fixes various problems in direct-edit identified by Adam, including Save/Save As behavior and dialog titles, 
displaying a dirty asterisk and path in the title bar (a la gedit), and handling cases where a file is 
unreadable or undecodable.
parent 096727a8
......@@ -454,7 +454,7 @@ public class CollectionPage : CheckerboardPage {
base.returning_from_fullscreen();
}
protected override void on_selection_changed(int count) {
protected override void selection_changed(int count) {
rotate_button.sensitive = (count > 0);
}
......@@ -601,8 +601,7 @@ public class CollectionPage : CheckerboardPage {
}
private void on_thumbnail_altered(LibraryPhoto photo) {
// TODO: use a different signal: e.g. contents_changed or photo_altered
notify_selection_changed(get_selected_queryable_count());
queryable_altered(photo);
// the thumbnail is only going to reload a low-quality interp, so schedule improval
schedule_thumbnail_improval();
......
......@@ -8,11 +8,12 @@ public class DirectWindow : AppWindow {
public DirectWindow(File file) {
DirectPhotoPage direct_photo_page = new DirectPhotoPage(file);
direct_photo_page.set_container(this);
direct_photo_page.photo_replaced += on_photo_replaced;
direct_photo_page.contents_changed += on_photo_changed;
direct_photo_page.queryable_altered += on_photo_changed;
current_page = direct_photo_page;
update_title(file);
update_title(file, false);
// add accelerators
Gtk.AccelGroup accel_group = current_page.ui.get_accel_group();
......@@ -33,8 +34,9 @@ public class DirectWindow : AppWindow {
return (DirectPhotoPage) current_page;
}
public void update_title(File file) {
title = file.get_basename() + " ~ " + Resources.APP_TITLE;
public void update_title(File file, bool modified) {
title = "%s%s (%s) - %s".printf((modified) ? "*" : "", file.get_basename(),
get_display_pathname(file.get_parent()), Resources.APP_TITLE);
}
public override void on_fullscreen() {
......@@ -51,8 +53,9 @@ public class DirectWindow : AppWindow {
return Resources.APP_DIRECT_ROLE;
}
private void on_photo_replaced(TransformablePhoto? old_photo, TransformablePhoto new_photo) {
update_title(new_photo.get_file());
private void on_photo_changed() {
TransformablePhoto photo = get_direct_page().get_photo();
update_title(photo.get_file(), photo.has_transformations());
}
private override void on_quit() {
......
......@@ -56,15 +56,19 @@ namespace ExportUI {
}
public class ExportDialog : Gtk.Dialog {
public static const ScaleConstraint[] CONSTRAINT_ARRAY = { ScaleConstraint.ORIGINAL,
public const int DEFAULT_SCALE = 1200;
public const ScaleConstraint DEFAULT_CONSTRAINT = ScaleConstraint.DIMENSIONS;
public const Jpeg.Quality DEFAULT_QUALITY = Jpeg.Quality.HIGH;
public const ScaleConstraint[] CONSTRAINT_ARRAY = { ScaleConstraint.ORIGINAL,
ScaleConstraint.DIMENSIONS, ScaleConstraint.WIDTH, ScaleConstraint.HEIGHT };
public static const Jpeg.Quality[] QUALITY_ARRAY = { Jpeg.Quality.LOW, Jpeg.Quality.MEDIUM,
public const Jpeg.Quality[] QUALITY_ARRAY = { Jpeg.Quality.LOW, Jpeg.Quality.MEDIUM,
Jpeg.Quality.HIGH, Jpeg.Quality.MAXIMUM };
private static ScaleConstraint current_constraint = ScaleConstraint.DIMENSIONS;
private static Jpeg.Quality current_quality = Jpeg.Quality.HIGH;
private static int current_scale = 1200;
private static int current_scale = DEFAULT_SCALE;
private Gtk.Table table = new Gtk.Table(0, 0, false);
private Gtk.ComboBox quality_combo;
......@@ -73,11 +77,18 @@ public class ExportDialog : Gtk.Dialog {
private Gtk.Widget ok_button;
private bool in_insert = false;
public ExportDialog(string title) {
public ExportDialog(string title, int default_scale = DEFAULT_SCALE,
ScaleConstraint default_constraint = DEFAULT_CONSTRAINT,
Jpeg.Quality default_quality = DEFAULT_QUALITY) {
this.title = title;
has_separator = false;
allow_grow = false;
// use defaults for controls
current_scale = default_scale;
current_constraint = default_constraint;
current_quality = default_quality;
// prepare controls
quality_combo = new Gtk.ComboBox.text();
int ctr = 0;
......
......@@ -266,7 +266,7 @@ public class ImportPage : CheckerboardPage {
return toolbar;
}
public override void on_selection_changed(int count) {
public override void selection_changed(int count) {
import_selected_button.sensitive = !busy && refreshed && (count > 0);
}
......
......@@ -49,7 +49,14 @@ public abstract class Page : Gtk.ScrolledWindow {
private bool dnd_enabled = false;
private bool in_view = false;
public signal void selection_changed();
public virtual signal void selection_changed(int count) {
}
public virtual signal void contents_changed(int count) {
}
public virtual signal void queryable_altered(Queryable queryable) {
}
public Page(string page_name) {
this.page_name = page_name;
......@@ -57,14 +64,6 @@ public abstract class Page : Gtk.ScrolledWindow {
set_flags(Gtk.WidgetFlags.CAN_FOCUS);
}
protected virtual void on_selection_changed(int count) {
}
protected void notify_selection_changed(int count) {
on_selection_changed(count);
selection_changed();
}
public string get_page_name() {
return page_name;
}
......@@ -534,32 +533,41 @@ public abstract class CheckerboardPage : Page {
public void add_item(LayoutItem item) {
layout.add_item(item);
contents_changed(layout.items.size);
}
public void remove_item(LayoutItem item) {
int count = selected_items.size;
int count = layout.items.size;
int selected_count = selected_items.size;
selected_items.remove(item);
layout.remove_item(item);
if (count != layout.items.size)
contents_changed(layout.items.size);
if (count != selected_items.size) {
notify_selection_changed(selected_items.size);
if (selected_count != selected_items.size) {
selection_changed(selected_items.size);
}
}
public int remove_selected() {
int count = selected_items.size;
int count = layout.items.size;
int selected_count = selected_items.size;
foreach (LayoutItem item in selected_items)
layout.remove_item(item);
selected_items.clear();
if (count != layout.items.size)
contents_changed(layout.items.size);
if (count != selected_items.size) {
notify_selection_changed(selected_items.size);
if (selected_count != selected_items.size) {
selection_changed(selected_items.size);
}
return count;
return selected_count;
}
public int remove_all() {
......@@ -569,8 +577,11 @@ public abstract class CheckerboardPage : Page {
layout.clear();
selected_items.clear();
if (count != layout.items.size)
contents_changed(layout.items.size);
if (selection_count != selected_items.size) {
notify_selection_changed(selected_items.size);
selection_changed(selected_items.size);
}
return count;
......@@ -591,7 +602,7 @@ public abstract class CheckerboardPage : Page {
}
if (changed)
notify_selection_changed(selected_items.size);
selection_changed(selected_items.size);
}
public void unselect_all() {
......@@ -605,7 +616,7 @@ public abstract class CheckerboardPage : Page {
selected_items.clear();
notify_selection_changed(0);
selection_changed(0);
}
public void unselect_all_but(LayoutItem exception) {
......@@ -627,7 +638,7 @@ public abstract class CheckerboardPage : Page {
selected_items.add(exception);
if (changed)
notify_selection_changed(1);
selection_changed(1);
}
public void select(LayoutItem item) {
......@@ -637,7 +648,7 @@ public abstract class CheckerboardPage : Page {
item.select();
selected_items.add(item);
notify_selection_changed(selected_items.size);
selection_changed(selected_items.size);
}
}
......@@ -648,7 +659,7 @@ public abstract class CheckerboardPage : Page {
item.unselect();
selected_items.remove(item);
notify_selection_changed(selected_items.size);
selection_changed(selected_items.size);
}
}
......@@ -661,7 +672,7 @@ public abstract class CheckerboardPage : Page {
selected_items.remove(item);
}
notify_selection_changed(selected_items.size);
selection_changed(selected_items.size);
}
public int get_selected_count() {
......
......@@ -43,9 +43,6 @@ public abstract class EditingHostPage : SinglePhotoPage {
ok = true;
}
public virtual signal void photo_replaced(TransformablePhoto? old_photo, TransformablePhoto new_photo) {
}
public EditingHostPage(string name) {
base(name);
......@@ -163,7 +160,6 @@ public abstract class EditingHostPage : SinglePhotoPage {
if (photo != null)
photo.altered -= on_photo_altered;
TransformablePhoto? old_photo = photo;
photo = new_photo;
photo.altered += on_photo_altered;
......@@ -174,9 +170,8 @@ public abstract class EditingHostPage : SinglePhotoPage {
update_ui();
// signal the photo has been replaced
photo_replaced(old_photo, photo);
notify_selection_changed(1);
contents_changed(1);
selection_changed(1);
}
private void quick_update_pixbuf() {
......@@ -346,8 +341,10 @@ public abstract class EditingHostPage : SinglePhotoPage {
}
private void on_photo_altered(TransformablePhoto p) {
// TODO: use a different signal: e.g. contents_changed or photo_altered
notify_selection_changed(1);
assert(p.equals(photo));
// signal that the photo has been altered
queryable_altered(photo);
quick_update_pixbuf();
......@@ -449,6 +446,8 @@ public abstract class EditingHostPage : SinglePhotoPage {
deactivate_tool();
photo.remove_all_transformations();
queryable_altered(photo);
}
private override bool on_ctrl_pressed(Gdk.EventKey event) {
......@@ -876,7 +875,7 @@ public class DirectPhotoPage : EditingHostPage {
{ "Revert", Gtk.STOCK_REVERT_TO_SAVED, "Re_vert to Original", null, "Revert to the original photo",
on_revert },
{ "ViewMenu", null, "_View", null, null, null },
{ "ViewMenu", null, "_View", null, null, null },
{ "HelpMenu", null, "_Help", null, null, null }
};
......@@ -884,7 +883,8 @@ public class DirectPhotoPage : EditingHostPage {
private Gtk.Menu context_menu;
private File initial_file;
private File current_save_dir;
private bool drop_if_dirty = false;
public DirectPhotoPage(File file) {
base(file.get_basename());
......@@ -922,8 +922,10 @@ public class DirectPhotoPage : EditingHostPage {
base.realize();
DirectPhoto photo = DirectPhoto.fetch(initial_file);
if (photo == null)
return;
if (photo == null) {
// dead in the water
Posix.exit(1);
}
display(new DirectPhotoCollection(initial_file.get_parent()), photo);
initial_file = null;
......@@ -944,10 +946,18 @@ public class DirectPhotoPage : EditingHostPage {
return true;
}
private static bool check_ok_to_close_photo(TransformablePhoto photo) {
private bool check_ok_to_close_photo(TransformablePhoto photo) {
if (!photo.has_transformations())
return true;
if (drop_if_dirty) {
// need to remove transformations, or else they stick around in memory (reappearing
// if the user opens the file again)
photo.remove_all_transformations();
return true;
}
bool ok = AppWindow.yes_no_question("Lose changes to %s?".printf(photo.get_name()));
if (ok)
photo.remove_all_transformations();
......@@ -987,20 +997,13 @@ public class DirectPhotoPage : EditingHostPage {
if (!get_photo().has_transformations())
return;
ExportDialog dialog = new ExportDialog("Save Photo");
int scale;
ScaleConstraint constraint;
Jpeg.Quality quality;
if (!dialog.execute(out scale, out constraint, out quality))
return;
// save right on top of the current file
save(get_photo().get_file(), scale, constraint, quality);
// save full-sized version right on top of the current file
save(get_photo().get_file(), 0, ScaleConstraint.ORIGINAL, Jpeg.Quality.HIGH);
}
private void on_save_as() {
ExportDialog export_dialog = new ExportDialog("Save Photo As...");
ExportDialog export_dialog = new ExportDialog("Save As", ExportDialog.DEFAULT_SCALE,
ScaleConstraint.ORIGINAL, ExportDialog.DEFAULT_QUALITY);
int scale;
ScaleConstraint constraint;
......@@ -1008,7 +1011,7 @@ public class DirectPhotoPage : EditingHostPage {
if (!export_dialog.execute(out scale, out constraint, out quality))
return;
Gtk.FileChooserDialog save_as_dialog = new Gtk.FileChooserDialog("Save Photo As...",
Gtk.FileChooserDialog save_as_dialog = new Gtk.FileChooserDialog("Save As",
AppWindow.get_instance(), Gtk.FileChooserAction.SAVE, Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK);
save_as_dialog.set_select_multiple(false);
......@@ -1018,7 +1021,12 @@ public class DirectPhotoPage : EditingHostPage {
int response = save_as_dialog.run();
if (response == Gtk.ResponseType.OK) {
// flag to prevent asking user about losing changes to the old file (since they'll be
// loaded right into the new one)
drop_if_dirty = true;
save(File.new_for_uri(save_as_dialog.get_uri()), scale, constraint, quality);
drop_if_dirty = false;
current_save_dir = File.new_for_path(save_as_dialog.get_current_folder());
}
......
......@@ -235,3 +235,17 @@ public bool query_is_directory_empty(File dir) throws Error {
return enumerator.next_file(null) == null;
}
public string get_display_pathname(File file) {
// attempt to replace home path with tilde in a user-pleasable way
string path = file.get_parse_name();
string home = Environment.get_home_dir();
if (path == home)
return "~";
if (path.has_prefix(home))
return "~%s".printf(path.substring(home.length));
return path;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment