Commit a4462cb8 authored by Jim Nelson's avatar Jim Nelson

#2308: Now marking photos missing on the fly (when they double-click on it).

parent 5e3edca7
......@@ -285,6 +285,9 @@ public interface Marker : Object {
// Returns the number of marked items, or the number of items when the marker was frozen
// and used.
public abstract int get_count();
// Returns a copy of the collection of marked items.
public abstract Gee.Collection<DataObject> get_all();
}
// MarkedAction is a callback to perform an action on the marked DataObject. Return false to
......@@ -416,6 +419,13 @@ public class DataCollection {
return (marked != null) ? marked.size : freeze_count;
}
public Gee.Collection<DataObject> get_all() {
Gee.ArrayList<DataObject> copy = new Gee.ArrayList<DataObject>();
copy.add_all(marked);
return copy;
}
private void on_items_removed(Gee.Iterable<DataObject> removed) {
foreach (DataObject object in removed)
marked.remove(object);
......@@ -754,7 +764,7 @@ public class DataCollection {
// Remove marked items from collection. This two-step process allows for iterating in a foreach
// loop and removing without creating a separate list. The marker is invalid after this call.
public void remove_marked(Marker m) {
public virtual void remove_marked(Marker m) {
MarkerImpl marker = (MarkerImpl) m;
assert(marker.is_valid(this));
......@@ -763,17 +773,26 @@ public class DataCollection {
marker.freeze();
// remove everything in the marked list
Gee.ArrayList<DataObject> skipped = null;
foreach (DataObject object in marker.marked) {
// although marker should track items already removed, catch it here as well
if (!internal_contains(object)) {
warning("remove_marked: marker holding ref to unknown %s", object.to_string());
if (skipped == null)
skipped = new Gee.ArrayList<DataObject>();
skipped.add(object);
continue;
}
internal_remove(object);
}
if (skipped != null)
marker.marked.remove_all(skipped);
// signal after removing
if (marker.marked.size > 0) {
notify_items_removed(marker.marked);
......@@ -1624,6 +1643,11 @@ public delegate bool ViewFilter(DataView view);
// objects, which are withheld entirely from the collection until they're made visible. Currently
// the only way to hide objects is with a ViewFilter.
//
// A ViewCollection may also be locked. When locked, it will not (a) remove hidden items from the
// collection and (b) remove DataViews representing unlinked DataSources. This allows for the
// ViewCollection to be "frozen" while manipulating items within it. When the collection is
// unlocked, all changes are applied at once.
//
// The default implementation provides a browser which orders the view in the order they're
// stored in DataCollection, which is not specified.
public class ViewCollection : DataCollection {
......@@ -1641,9 +1665,10 @@ public class ViewCollection : DataCollection {
private DataSet visible = null;
private Gee.HashSet<DataView> frozen_views_altered = null;
private Gee.HashSet<DataView> frozen_geometries_altered = null;
private int view_filter_lock_count = 0;
private int view_lock_count = 0;
// if true the items needs to be shown, if false, it needs to be hidden
private Gee.HashMap<DataView, bool> locked_filter_items = null;
private Gee.HashMap<DataView, bool> locked_filter_items = new Gee.HashMap<DataView, bool>();
private Gee.HashSet<DataView> locked_unlinked_items = new Gee.HashSet<DataView>();
// TODO: source-to-view mapping ... for now, only one view is allowed for each source.
// This may need to change in the future.
......@@ -1697,8 +1722,6 @@ public class ViewCollection : DataCollection {
public ViewCollection(string name) {
base (name);
locked_filter_items = new Gee.HashMap<DataView, bool>();
}
protected virtual void notify_items_selected(Gee.Iterable<DataView> views) {
......@@ -1759,7 +1782,8 @@ public class ViewCollection : DataCollection {
base.clear();
locked_filter_items.clear();
view_filter_lock_count = 0;
locked_unlinked_items.clear();
view_lock_count = 0;
}
public override void close() {
......@@ -2120,7 +2144,7 @@ public class ViewCollection : DataCollection {
}
}
if (view_filter_lock_count > 0) {
if (view_lock_count > 0) {
if (to_show != null) {
foreach (DataView view in to_show) {
locked_filter_items.set(view, true);
......@@ -2148,22 +2172,31 @@ public class ViewCollection : DataCollection {
base.items_altered(map);
}
public bool is_view_filter_locked() {
return view_filter_lock_count > 0;
public bool is_view_locked() {
return view_lock_count > 0;
}
public void lock_view_filter() {
view_filter_lock_count++;
public void lock_view() {
view_lock_count++;
}
public void unlock_view_filter() {
view_filter_lock_count--;
if (view_filter_lock_count > 0)
public void unlock_view() {
assert(view_lock_count > 0);
if (--view_lock_count != 0)
return;
assert(view_filter_lock_count == 0); // watch out for negative numbers
// remove everything unlinked while locked, being sure to remove them from the locked
// visible lists as well
foreach (DataView view in locked_unlinked_items)
locked_filter_items.unset(view);
// Because verify_remove will be called inside remove_marked, want to have the
// locked_unlinked_items collection cleared at that point
Marker marker = mark_many(locked_unlinked_items);
locked_unlinked_items.clear();
remove_marked(marker);
// everything remaining on the visible list update as well
Gee.ArrayList<DataView> to_show = new Gee.ArrayList<DataView>();
Gee.ArrayList<DataView> to_hide = new Gee.ArrayList<DataView>();
......@@ -2176,12 +2209,25 @@ public class ViewCollection : DataCollection {
if (to_show.size > 0)
show_items(to_show);
if (to_hide.size > 0)
hide_items(to_hide);
locked_filter_items.clear();
}
public override void remove_marked(Marker marker) {
// if locked, block removal of unlinked DataViews
if (locked_unlinked_items.size > 0) {
foreach (DataObject object in marker.get_all()) {
if (locked_unlinked_items.contains((DataView) object))
marker.unmark(object);
}
}
base.remove_marked(marker);
}
public override void set_comparator(Comparator comparator, ComparatorPredicate? predicate) {
selected.set_comparator(comparator, predicate);
if (visible != null)
......@@ -2586,6 +2632,22 @@ public class ViewCollection : DataCollection {
}
}
// This is only called by DataView.
public void internal_notify_unlinking(DataView view, SourceCollection sources) {
if (view_lock_count > 0) {
bool added = locked_unlinked_items.add(view);
assert(added);
}
}
// This is only called by DataView.
public void internal_notify_relinked(DataView view, SourceCollection sources) {
if (view_lock_count > 0) {
bool removed = locked_unlinked_items.remove(view);
assert(removed);
}
}
protected override void notify_thawed() {
if (frozen_views_altered != null) {
foreach (DataView view in frozen_views_altered)
......
......@@ -568,6 +568,8 @@ public abstract class DataSource : DataObject {
unlinked_from_collection = collection;
backlinks = new Gee.HashMap<string, Gee.List<string>>();
contact_subscribers(subscriber_unlinking);
}
// This method is called by DataSource. It should not be called otherwise.
......@@ -581,6 +583,10 @@ public abstract class DataSource : DataObject {
commit_backlinks(unlinked_from_collection, dehydrate_backlinks());
}
private void subscriber_unlinking(DataView view) {
view.notify_source_unlinking(unlinked_from_collection);
}
// This method is called by SourceCollection. It should not be called otherwise.
public virtual void notify_relinking(SourceCollection collection) {
assert(backlinks != null && unlinked_from_collection == collection);
......@@ -594,11 +600,16 @@ public abstract class DataSource : DataObject {
backlinks = null;
unlinked_from_collection = null;
relinked(relinked_to);
contact_subscribers(subscriber_relinked);
// have the DataSource delete any persisted link state
commit_backlinks(null, null);
}
private void subscriber_relinked(DataView view) {
view.notify_source_relinked((SourceCollection) get_membership());
}
public bool has_backlink(SourceBacklink backlink) {
if (backlinks == null)
return false;
......@@ -1218,6 +1229,20 @@ public class DataView : DataObject {
public virtual void notify_unsubscribed(DataSource source) {
unsubscribed(source);
}
// This is only called by DataSource
public virtual void notify_source_unlinking(SourceCollection sources) {
ViewCollection? membership = (ViewCollection?) get_membership();
if (membership != null)
membership.internal_notify_unlinking(this, sources);
}
// This is only called by DataSource
public virtual void notify_source_relinked(SourceCollection sources) {
ViewCollection? membership = (ViewCollection?) get_membership();
if (membership != null)
membership.internal_notify_relinked(this, sources);
}
}
public class ThumbnailView : DataView {
......
......@@ -396,7 +396,10 @@ public abstract class EditingHostPage : SinglePhotoPage {
private bool is_pan_in_progress = false;
private double saved_slider_val = 0.0;
private ZoomBuffer? zoom_buffer = null;
public virtual signal void photo_backing_missing(Photo photo, bool missing) {
}
public EditingHostPage(SourceCollection sources, string name) {
base(name, false);
......@@ -928,15 +931,19 @@ public abstract class EditingHostPage : SinglePhotoPage {
replace_photo(controller, photo);
}
protected virtual void set_missing_photo_sensitivities(bool sensitivity) {
protected virtual void notify_photo_backing_missing(Photo photo, bool missing) {
bool sensitivity = !missing;
rotate_button.sensitive = sensitivity;
crop_button.sensitive = sensitivity;
redeye_button.sensitive = sensitivity;
adjust_button.sensitive = sensitivity;
enhance_button.sensitive = sensitivity;
zoom_slider.sensitive = sensitivity;
deactivate_tool();
photo_backing_missing(photo, missing);
}
private void draw_message(string message) {
......@@ -955,22 +962,25 @@ public abstract class EditingHostPage : SinglePhotoPage {
}
protected void set_photo_missing(bool missing) {
if (photo_missing == missing) {
if (photo_missing == missing)
return;
}
photo_missing = missing;
set_missing_photo_sensitivities(!photo_missing);
Photo? photo = get_photo();
if (photo == null)
return;
notify_photo_backing_missing(photo, missing);
if (photo_missing) {
try {
Gdk.Pixbuf pixbuf = get_photo().get_preview_pixbuf(get_canvas_scaling());
Gdk.Pixbuf pixbuf = photo.get_preview_pixbuf(get_canvas_scaling());
pixbuf = pixbuf.composite_color_simple(pixbuf.get_width(), pixbuf.get_height(),
Gdk.InterpType.NEAREST, 100, 2, 0, 0);
set_pixbuf(pixbuf, get_photo().get_dimensions());
set_pixbuf(pixbuf, photo.get_dimensions());
} catch (GLib.Error err) {
warning("%s", err.message);
}
......@@ -1893,7 +1903,7 @@ public abstract class EditingHostPage : SinglePhotoPage {
// we need both show & present so we get keyboard focus in metacity, but due to a bug in
// compiz, we only want to show the window.
// ticket #2141 prompted this: http://trac.yorba.org/ticket/2141
// ticket #2141 prompted this: http://trac.yorba.org/ticket/2141
tool_window.show();
if (!get_window_manager().contains("compiz"))
tool_window.present();
......@@ -1984,8 +1994,7 @@ public class LibraryPhotoPage : EditingHostPage {
get_view().contents_altered.connect(on_contents_altered);
get_view().items_altered.connect(on_photos_altered);
// watch for photos being destroyed or removed or altered, either here or in other pages
LibraryPhoto.global.items_removed.connect(on_photos_removed);
// watch for photos being destroyed or altered, either here or in other pages
LibraryPhoto.global.item_destroyed.connect(on_photo_destroyed);
LibraryPhoto.global.item_altered.connect(on_metadata_altered);
......@@ -2358,16 +2367,16 @@ public class LibraryPhotoPage : EditingHostPage {
update_zoom_menu_item_sensitivity();
update_rating_menu_item_sensitivity();
set_display_ratings(Config.get_instance().get_display_photo_ratings());
get_controller().lock_view_filter();
get_controller().lock_view();
}
public override void switching_from() {
base.switching_from();
get_controller().unlock_view_filter();
get_controller().unlock_view();
}
protected override void paint(Gdk.GC gc, Gdk.Drawable drawable) {
......@@ -2438,7 +2447,9 @@ public class LibraryPhotoPage : EditingHostPage {
}
}
protected override void set_missing_photo_sensitivities(bool sensitivity) {
protected override void notify_photo_backing_missing(Photo photo, bool missing) {
bool sensitivity = !missing;
set_item_sensitive("/PhotoMenuBar/FileMenu/PublishPlaceholder/Publish", sensitivity);
set_item_sensitive("/PhotoMenuBar/FileMenu/PrintPlaceholder/Print", sensitivity);
set_item_sensitive("/PhotoMenuBar/FileMenu/JumpToFile", sensitivity);
......@@ -2465,12 +2476,11 @@ public class LibraryPhotoPage : EditingHostPage {
set_item_sensitive("/PhotoMenuBar/PhotoMenu/AdjustDateTime", sensitivity);
set_item_sensitive("/PhotoMenuBar/PhotoMenu/ExternalEdit", sensitivity);
set_item_sensitive("/PhotoMenuBar/PhotoMenu/ExternalEditRAW", sensitivity);
set_item_sensitive("/PhotoMenuBar/PhotoMenu/Revert", sensitivity);
set_item_sensitive("/PhotoMenuBar/PhotoMenu/Revert", sensitivity);
set_item_sensitive("/PhotoMenuBar/PhotoMenu/Rate", sensitivity);
set_item_sensitive("/PhotoMenuBar/TagsMenu/AddTags", sensitivity);
set_item_sensitive("/PhotoMenuBar/TagsMenu/ModifyTags", sensitivity);
//AppWindow.get_instance().set_common_action_sensitive("CommonFullscreen", sensitivity);
set_item_sensitive("/PhotoContextMenu/ContextEnhance", sensitivity);
set_item_sensitive("/PhotoContextMenu/ContextRevert", sensitivity);
......@@ -2487,7 +2497,12 @@ public class LibraryPhotoPage : EditingHostPage {
set_action_sensitive("SetBackground", sensitivity);
#endif
base.set_missing_photo_sensitivities(sensitivity);
if (missing)
((LibraryPhoto) photo).mark_offline();
else
((LibraryPhoto) photo).mark_online();
base.notify_photo_backing_missing(photo, missing);
}
private override bool key_press_event(Gdk.EventKey event) {
......@@ -2508,12 +2523,12 @@ public class LibraryPhotoPage : EditingHostPage {
break;
case "period":
case "greater":
case "greater":
on_increase_rating();
break;
case "comma":
case "less":
case "less":
on_decrease_rating();
break;
......@@ -3387,7 +3402,9 @@ public class DirectPhotoPage : EditingHostPage {
update_zoom_menu_item_sensitivity();
}
protected override void set_missing_photo_sensitivities(bool sensitivity) {
protected override void notify_photo_backing_missing(Photo photo, bool missing) {
bool sensitivity = !missing;
set_item_sensitive("/DirectMenuBar/FileMenu/Save", sensitivity);
set_item_sensitive("/DirectMenuBar/FileMenu/SaveAs", sensitivity);
set_item_sensitive("/DirectMenuBar/FileMenu/PublishPlaceholder/Publish", sensitivity);
......@@ -3420,7 +3437,7 @@ public class DirectPhotoPage : EditingHostPage {
set_action_sensitive("SetBackground", has_photo() && !get_photo_missing());
#endif
base.set_missing_photo_sensitivities(sensitivity);
base.notify_photo_backing_missing(photo, missing);
}
protected override void init_actions(int selected_count, int count) {
......
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