905
929
// This is called when notifications have frozen. Child collections should halt notifications
906
930
// until thawed() is called.
907
protected virtual void frozen() {
931
protected virtual void notify_frozen() {
910
935
// This is called when enough thaw_notifications() calls have been made. Child collections
911
936
// should issue caught notifications.
912
protected virtual void thawed() {
937
protected virtual void notify_thawed() {
913
938
if (frozen_items_altered != null) {
914
foreach (DataObject object in frozen_items_altered.keys)
915
notify_item_altered(object, frozen_items_altered.get(object));
916
notify_items_altered(frozen_items_altered);
939
// refs are swapped around due to reentrancy
940
Gee.Map<DataObject, Alteration> copy = frozen_items_altered;
917
941
frozen_items_altered = null;
943
foreach (DataObject object in copy.keys)
944
notify_item_altered(object, copy.get(object));
945
notify_items_altered(copy);
920
948
if (fire_ordering_changed) {
921
949
fire_ordering_changed = false;
922
950
notify_ordering_changed();
953
// notify all members as well
954
int count = get_count();
955
for (int ctr = 0; ctr < count; ctr++)
956
get_at(ctr).internal_collection_thawed();
1311
1353
public void evaporate(ContainerSource container) {
1312
1354
if (contained_sources.has_backlink(container.get_backlink())) {
1313
debug("Unlinking %s to %s holding tank", container.to_string(), to_string());
1315
1355
unlink_marked(mark(container));
1316
1356
bool added = holding_tank.add(container);
1319
debug("Destroying %s in %s", container.to_string(), to_string());
1321
1359
destroy_marked(mark(container), true);
1364
// A SourceHoldingTank is similar to the holding tank used by ContainerSourceCollection, but for
1365
// non-ContainerSources to be held offline from their natural SourceCollection (i.e. PhotoSources
1366
// being held in a trashcan, for example). It is *not* a DataCollection (important!), but rather
1367
// a signalled collection that moves DataSources to and from their SourceCollection.
1369
// DataSources can be shuttled from their SourceCollection to the SourceHoldingTank manually
1370
// (via unlink_and_hold) or can be automatically moved by installing a HoldingPredicate.
1371
// Only one HoldingConditional may be installed. Because of assertions in the methods, it's unwise
1372
// to use more than one method. add() and add_many() should ONLY be used for DataSources not
1373
// first installed in their SourceCollection (i.e. they're born in the SourceHoldingTank).
1375
// NOTE: DataSources should never be in more than one SourceHoldingTank. No tests are performed
1376
// here to verify this. This is why a filter/predicate method (which could automatically move
1377
// them in as they're altered) is not offered; there's no easy way to keep DataSources from being
1378
// moved into more than one holding tank, or which should have preference. The CheckToRemove
1379
// predicate is offered only to know when to release them.
1381
public class SourceHoldingTank {
1382
// Return true if the DataSource should remain in the SourceHoldingTank, false otherwise.
1383
public delegate bool CheckToKeep(DataSource source, Alteration alteration);
1385
private SourceCollection sources;
1386
private CheckToKeep check_to_keep;
1387
private DataSet tank = new DataSet();
1388
private Gee.HashSet<DataSource> relinks = new Gee.HashSet<DataSource>();
1389
private Gee.HashSet<DataSource> unlinking = new Gee.HashSet<DataSource>();
1390
private int64 ordinal = 0;
1392
public virtual signal void contents_altered(Gee.Collection<DataSource>? added,
1393
Gee.Collection<DataSource>? removed) {
1396
public SourceHoldingTank(SourceCollection sources, CheckToKeep check_to_keep) {
1397
this.sources = sources;
1398
this.check_to_keep = check_to_keep;
1400
this.sources.item_destroyed.connect(on_source_destroyed);
1401
this.sources.thawed.connect(on_source_collection_thawed);
1404
~SourceHoldingTank() {
1405
sources.item_destroyed.disconnect(on_source_destroyed);
1406
sources.thawed.disconnect(on_source_collection_thawed);
1408
foreach (DataObject object in tank.get_all())
1409
((DataSource) object).altered.disconnect(on_held_source_altered);
1412
protected virtual void notify_contents_altered(Gee.Collection<DataSource>? added,
1413
Gee.Collection<DataSource>? removed) {
1414
if (added != null) {
1415
foreach (DataSource source in added)
1416
source.altered.connect(on_held_source_altered);
1419
if (removed != null) {
1420
foreach (DataSource source in removed)
1421
source.altered.disconnect(on_held_source_altered);
1424
contents_altered(added, removed);
1427
public int get_count() {
1428
return tank.get_count();
1431
public Gee.Collection<DataSource> get_all() {
1432
return (Gee.Collection<DataSource>) tank.get_all();
1435
public bool contains(DataSource source) {
1436
return tank.contains(source) || unlinking.contains(source);
1439
// Only use for DataSources that have not been installed in their SourceCollection.
1440
public void add_many(Gee.Collection<DataSource> many) {
1444
foreach (DataSource source in many)
1445
source.internal_set_ordinal(ordinal++);
1447
bool added = tank.add_many(many);
1450
notify_contents_altered(many, null);
1453
// Do not pass in DataSources which have already been unlinked, including into this holding
1455
public void unlink_and_hold(Gee.Collection<DataSource> unlink) {
1456
if (unlink.size == 0)
1459
// store in the unlinking collection to guard against reentrancy
1460
unlinking.add_all(unlink);
1462
sources.unlink_marked(sources.mark_many(unlink));
1464
foreach (DataSource source in unlink)
1465
source.internal_set_ordinal(ordinal++);
1467
bool added = tank.add_many(unlink);
1470
// remove from the unlinking pool, as they're now unlinked
1471
unlinking.remove_all(unlink);
1473
notify_contents_altered(unlink, null);
1476
public bool has_backlink(SourceBacklink backlink) {
1477
int count = tank.get_count();
1478
for (int ctr = 0; ctr < count; ctr++) {
1479
if (((DataSource) tank.get_at(ctr)).has_backlink(backlink))
1486
public void remove_backlink(SourceBacklink backlink) {
1487
int count = tank.get_count();
1488
for (int ctr = 0; ctr < count; ctr++)
1489
((DataSource) tank.get_at(ctr)).remove_backlink(backlink);
1492
public void destroy_orphans(Gee.List<DataSource> destroy, bool delete_backing,
1493
ProgressMonitor? monitor = null) {
1494
if (destroy.size == 0)
1497
bool removed = tank.remove_many(destroy);
1500
notify_contents_altered(null, destroy);
1502
int count = destroy.size;
1503
for (int ctr = 0; ctr < count; ctr++) {
1504
destroy.get(ctr).destroy_orphan(delete_backing);
1505
if (monitor != null)
1506
monitor(ctr + 1, count);
1510
private void on_source_destroyed(DataSource source) {
1511
if (!tank.contains(source))
1514
bool removed = tank.remove(source);
1517
notify_contents_altered(null, new SingletonCollection<DataSource>(source));
1520
private void on_held_source_altered(DataObject object, Alteration alteration) {
1521
DataSource source = (DataSource) object;
1523
assert(tank.contains(source));
1525
// see if it should stay put
1526
if (check_to_keep(source, alteration))
1529
bool removed = tank.remove(source);
1532
if (sources.are_notifications_frozen()) {
1533
relinks.add(source);
1538
notify_contents_altered(null, new SingletonCollection<DataSource>(source));
1540
sources.relink(source);
1543
private void on_source_collection_thawed() {
1544
if (relinks.size == 0)
1547
// swap out to protect against reentrancy
1548
Gee.HashSet<DataSource> copy = relinks;
1549
relinks = new Gee.HashSet<DataSource>();
1551
notify_contents_altered(null, copy);
1553
sources.relink_many(copy);
1557
public class DatabaseSourceHoldingTank : SourceHoldingTank {
1558
private GetSourceDatabaseKey get_key;
1559
private Gee.HashMap<int64?, DataSource> map = new Gee.HashMap<int64?, DataSource>(int64_hash,
1562
public DatabaseSourceHoldingTank(SourceCollection sources,
1563
SourceHoldingTank.CheckToKeep check_to_keep, GetSourceDatabaseKey get_key) {
1564
base (sources, check_to_keep);
1566
this.get_key = get_key;
1569
public DataSource? get_by_id(int64 id) {
1573
protected override void notify_contents_altered(Gee.Collection<DataSource>? added,
1574
Gee.Collection<DataSource>? removed) {
1575
if (added != null) {
1576
foreach (DataSource source in added)
1577
map.set(get_key(source), source);
1580
if (removed != null) {
1581
foreach (DataSource source in removed)
1582
map.unset(get_key(source));
1585
base.notify_contents_altered(added, removed);
1327
1590
// ViewCollection
1670
1954
remove_marked(marker);
1673
private void on_source_altered(DataObject object, Alteration alteration) {
1674
DataSource source = (DataSource) object;
1957
private void on_sources_altered(Gee.Map<DataObject, Alteration> items) {
1676
1958
// let ViewManager decide whether or not to keep, but only add if not already present
1677
1959
// and only remove if already present
1678
bool include = manager.include_in_view(source);
1679
if (include && !has_view_for_source(source)) {
1680
add(manager.create_view(source));
1681
} else if (!include && has_view_for_source(source)) {
1682
Marker marker = mark(get_view_for_source(source));
1683
remove_marked(marker);
1684
} else if (include && has_view_for_source(source)) {
1685
DataView view = get_view_for_source(source);
1687
if (selected.contains(view))
1688
selected.resort_object(view);
1690
if (visible != null && is_visible(view)) {
1691
if (visible.resort_object(view))
1692
notify_ordering_changed();
1960
Gee.ArrayList<DataView> to_add = null;
1961
Gee.ArrayList<DataView> to_remove = null;
1962
bool ordering_changed = false;
1963
foreach (DataObject object in items.keys) {
1964
DataSource source = (DataSource) object;
1965
Alteration alteration = items.get(object);
1967
bool include = manager.include_in_view(source);
1968
if (include && !has_view_for_source(source)) {
1970
to_add = new Gee.ArrayList<DataView>();
1972
to_add.add(manager.create_view(source));
1973
} else if (!include && has_view_for_source(source)) {
1974
if (to_remove == null)
1975
to_remove = new Gee.ArrayList<DataView>();
1977
to_remove.add(get_view_for_source(source));
1978
} else if (include && has_view_for_source(source)) {
1979
DataView view = get_view_for_source(source);
1981
if (selected.contains(view))
1982
selected.resort_object(view, alteration);
1984
if (visible != null && is_visible(view)) {
1985
if (visible.resort_object(view, alteration))
1986
ordering_changed = true;
1994
if (to_remove != null)
1995
remove_marked(mark_many(to_remove));
1997
if (ordering_changed)
1998
notify_ordering_changed();
1697
2001
private void on_mirror_contents_added(Gee.Iterable<DataObject> added) {
1721
2025
// Keep the source map and state tables synchronized
1722
public override void notify_items_added(Gee.Iterable<DataObject> added) {
1723
Gee.ArrayList<DataView> added_visible = new Gee.ArrayList<DataView>();
1724
Gee.ArrayList<DataView> added_selected = new Gee.ArrayList<DataView>();
2026
protected override void notify_items_added(Gee.Iterable<DataObject> added) {
2027
Gee.ArrayList<DataView> added_visible = null;
2028
Gee.ArrayList<DataView> added_selected = null;
1726
2030
foreach (DataObject object in added) {
1727
2031
DataView view = (DataView) object;
1728
2032
source_map.set(view.get_source(), view);
1730
if (view.is_selected())
2034
if (view.is_selected()) {
2035
if (added_selected == null)
2036
added_selected = new Gee.ArrayList<DataView>();
1731
2038
added_selected.add(view);
1733
2041
if (filter != null)
1734
2042
view.internal_set_visible(filter(view));
1736
if (view.is_visible())
2044
if (view.is_visible()) {
2045
if (added_visible == null)
2046
added_visible = new Gee.ArrayList<DataView>();
1737
2048
added_visible.add(view);
1740
bool is_added = add_many_visible(added_visible);
1742
is_added = selected.add_many(added_selected);
1745
if (added_selected.size > 0)
2052
if (added_visible != null) {
2053
bool is_added = add_many_visible(added_visible);
2057
if (added_selected != null) {
2058
bool is_added = selected.add_many(added_selected);
1746
2061
notify_items_selected(added_selected);
1748
2064
base.notify_items_added(added);
1751
2067
// Keep the source map and state tables synchronized
1752
public override void notify_items_removed(Gee.Iterable<DataObject> removed) {
2068
protected override void notify_items_removed(Gee.Iterable<DataObject> removed) {
1753
2069
bool selected_removed = false;
1754
2070
foreach (DataObject object in removed) {
1755
2071
DataView view = (DataView) object;
1777
2093
base.notify_items_removed(removed);
1780
private void filter_altered_item(DataObject object) {
2096
private void filter_altered_items(Gee.Collection<DataView> views) {
1781
2097
if (filter == null)
1784
DataView view = (DataView) object;
1786
2100
// Can't use the marker system because ViewCollection completely overrides DataCollection
1787
2101
// and hidden items cannot be marked.
1789
if (!view.is_visible()) {
1790
Gee.ArrayList<DataView> to_show = new Gee.ArrayList<DataView>();
1792
show_items(to_show);
1795
if (view.is_visible()) {
1796
Gee.ArrayList<DataView> to_hide = new Gee.ArrayList<DataView>();
1798
hide_items(to_hide);
1803
public override void item_altered(DataObject object, Alteration alteration) {
1804
filter_altered_item(object);
1806
base.item_altered(object, alteration);
1809
public override void set_comparator(Comparator comparator) {
1810
selected.set_comparator(comparator);
2102
Gee.ArrayList<DataView> to_show = null;
2103
Gee.ArrayList<DataView> to_hide = null;
2105
foreach (DataView view in views) {
2107
if (locked_filter_items.has_key(view) || !view.is_visible()) {
2108
if (to_show == null)
2109
to_show = new Gee.ArrayList<DataView>();
2114
if (locked_filter_items.has_key(view) || view.is_visible()) {
2115
if (to_hide == null)
2116
to_hide = new Gee.ArrayList<DataView>();
2123
if (view_filter_lock_count > 0) {
2124
if (to_show != null) {
2125
foreach (DataView view in to_show) {
2126
locked_filter_items.set(view, true);
2130
if (to_hide != null) {
2131
foreach (DataView view in to_hide) {
2132
locked_filter_items.set(view, false);
2138
if (to_show != null)
2139
show_items(to_show);
2141
if (to_hide != null)
2142
hide_items(to_hide);
2145
public override void items_altered(Gee.Map<DataObject, Alteration> map) {
2146
filter_altered_items(map.keys);
2148
base.items_altered(map);
2151
public bool is_view_filter_locked() {
2152
return view_filter_lock_count > 0;
2155
public void lock_view_filter() {
2156
view_filter_lock_count++;
2159
public void unlock_view_filter() {
2160
view_filter_lock_count--;
2162
if (view_filter_lock_count > 0)
2165
assert(view_filter_lock_count == 0); // watch out for negative numbers
2167
Gee.ArrayList<DataView> to_show = new Gee.ArrayList<DataView>();
2168
Gee.ArrayList<DataView> to_hide = new Gee.ArrayList<DataView>();
2170
foreach (DataView key in locked_filter_items.keys) {
2171
if (locked_filter_items[key] && !key.is_visible())
2173
else if (!locked_filter_items[key] && key.is_visible())
2177
if (to_show.size > 0)
2178
show_items(to_show);
2179
if (to_hide.size > 0)
2180
hide_items(to_hide);
2182
locked_filter_items.clear();
2185
public override void set_comparator(Comparator comparator, ComparatorPredicate? predicate) {
2186
selected.set_comparator(comparator, predicate);
1811
2187
if (visible != null)
1812
visible.set_comparator(comparator);
2188
visible.set_comparator(comparator, predicate);
1814
base.set_comparator(comparator);
2190
base.set_comparator(comparator, predicate);
1817
2193
public override void reset_comparator() {