~ubuntu-branches/ubuntu/natty/shotwell/natty

« back to all changes in this revision

Viewing changes to src/DataCollection.vala

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell
  • Date: 2010-08-16 16:58:02 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20100816165802-j6t0o788sk21djtr
Tags: 0.6.91-0ubuntu1
* New upstream release
* debian/control:
  - Build-depend on valac >= 0.9.5
  - Build-depend on libgexiv2-dev >= 0.1.91

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
// operation is probably best done by a Gee.ArrayList.
23
23
//
24
24
 
 
25
// ComparatorPredicate is used to determine if a re-sort operation is necessary; it has no
 
26
// effect on adding a DataObject to a DataSet in sorted order.
 
27
public delegate bool ComparatorPredicate(DataObject object, Alteration alteration);
 
28
 
25
29
public class DataSet {
26
30
    private SortedList<DataObject> list = new SortedList<DataObject>();
27
31
    private Gee.HashSet<DataObject> hash_set = new Gee.HashSet<DataObject>();
28
32
    private Comparator user_comparator = null;
 
33
    private ComparatorPredicate? comparator_predicate = null;
29
34
    
30
35
    public DataSet() {
31
36
        reset_comparator();
35
40
        return ((DataObject *) a)->internal_get_ordinal() - ((DataObject *) b)->internal_get_ordinal();
36
41
    }
37
42
    
 
43
    private bool order_added_predicate(DataObject object, Alteration alteration) {
 
44
        // ordinals don't change (shouldn't change!) while a part of the DataSet
 
45
        return false;
 
46
    }
 
47
    
38
48
    private int64 comparator_wrapper(void *a, void *b) {
39
49
        if (a == b)
40
50
            return 0;
54
64
        return hash_set.contains(object);
55
65
    }
56
66
    
57
 
    public int get_count() {
58
 
        return list.size;
 
67
    public inline int get_count() {
 
68
        return list.get_count();
59
69
    }
60
70
    
61
71
    public void reset_comparator() {
62
72
        user_comparator = null;
 
73
        comparator_predicate = order_added_predicate;
63
74
        list.resort(order_added_comparator);
64
75
    }
65
76
    
66
 
    public void set_comparator(Comparator user_comparator) {
 
77
    public void set_comparator(Comparator user_comparator, ComparatorPredicate? comparator_predicate) {
67
78
        this.user_comparator = user_comparator;
 
79
        this.comparator_predicate = comparator_predicate;
68
80
        list.resort(comparator_wrapper);
69
81
    }
70
82
    
71
83
    public Gee.Collection<DataObject> get_all() {
72
 
        // TODO: Returning a copy because of code elsewhere that removes items during an iteration.
73
 
        // This needs to be fixed.
74
 
        return list.copy();
 
84
        return list.read_only_view;
75
85
    }
76
86
    
77
87
    public DataSet copy() {
90
100
        return list.locate(object);
91
101
    }
92
102
    
 
103
    // DataObject's ordinal should be set before adding.
93
104
    public bool add(DataObject object) {
94
105
        if (!list.add(object))
95
106
            return false;
104
115
        return true;
105
116
    }
106
117
    
107
 
    public bool add_many(Gee.List<DataObject> objects) {
 
118
    // DataObjects' ordinals should be set before adding.
 
119
    public bool add_many(Gee.Collection<DataObject> objects) {
108
120
        int count = objects.size;
109
121
        if (count == 0)
110
122
            return true;
147
159
    }
148
160
    
149
161
    // Returns true if the item has moved.
150
 
    public bool resort_object(DataObject object) {
 
162
    public bool resort_object(DataObject object, Alteration? alteration) {
 
163
        if (comparator_predicate != null && alteration != null
 
164
            && !comparator_predicate(object, alteration)) {
 
165
            return false;
 
166
        }
 
167
        
151
168
        return list.resort_item(object);
152
169
    }
153
170
}
451
468
    }
452
469
    
453
470
    // This signal fires whenever any (or multiple) items in the collection signal they've been
454
 
    // altered.  This is more useful than item_altered() because it isn't blocked when notifications
455
 
    // are frozen and is called when they are thawed.
 
471
    // altered.  This is more efficient than being called for each altered item.
456
472
    public virtual signal void items_altered(Gee.Map<DataObject, Alteration> items) {
457
473
    }
458
474
 
470
486
    public virtual signal void property_cleared(string name) {
471
487
    }
472
488
    
 
489
    // Fired when "altered" signal (and possibly other related signals, depending on the subclass)
 
490
    // is frozen.
 
491
    public virtual signal void frozen() {
 
492
    }
 
493
    
 
494
    // Fired when "altered" signal (and other related signals, depending on the subclass) is
 
495
    // restored (thawed).
 
496
    public virtual signal void thawed() {
 
497
    }
 
498
    
473
499
    public DataCollection(string name) {
474
500
        this.name = name;
475
501
    }
539
565
        return true;
540
566
    }
541
567
    
542
 
    public virtual void set_comparator(Comparator comparator) {
543
 
        dataset.set_comparator(comparator);
 
568
    public virtual void set_comparator(Comparator comparator, ComparatorPredicate? predicate) {
 
569
        dataset.set_comparator(comparator, predicate);
544
570
        notify_ordering_changed();
545
571
    }
546
572
    
618
644
    }
619
645
    
620
646
    // Returns false if item is already part of the collection.
621
 
    public bool add(DataObject object) {
 
647
    public virtual bool add(DataObject object) {
622
648
        if (internal_contains(object)) {
623
649
            debug("%s cannot add %s: already present", to_string(), object.to_string());
624
650
            
638
664
        return true;
639
665
    }
640
666
    
641
 
    // Returns number of items added to collection.
642
 
    public int add_many(Gee.Collection<DataObject> objects, ProgressMonitor? monitor = null) {
 
667
    // Returns the items added to the collection.
 
668
    public virtual Gee.Collection<DataObject> add_many(Gee.Collection<DataObject> objects, 
 
669
        ProgressMonitor? monitor = null) {
643
670
        Gee.ArrayList<DataObject> added = new Gee.ArrayList<DataObject>();
644
671
        foreach (DataObject object in objects) {
645
672
            if (internal_contains(object)) {
653
680
        
654
681
        int count = added.size;
655
682
        if (count == 0)
656
 
            return 0;
 
683
            return added;
657
684
        
658
685
        internal_add_many(added, monitor);
659
686
        
665
692
        for (int ctr = 0; ctr < count; ctr++)
666
693
            added.get(ctr).notify_membership_changed(this);
667
694
        
668
 
        return count;
 
695
        return added;
669
696
    }
670
697
    
671
698
    // Obtain a marker to build a list of objects to perform an action upon.
800
827
    public void internal_notify_altered(DataObject object, Alteration alteration) {
801
828
        assert(internal_contains(object));
802
829
        
803
 
        bool resort_occurred = dataset.resort_object(object);
 
830
        bool resort_occurred = dataset.resort_object(object, alteration);
804
831
        
805
832
        if (are_notifications_frozen()) {
806
833
            if (frozen_items_altered == null)
877
904
    // fired at once.
878
905
    //
879
906
    // DataObject/DataSource/DataView should also "eat" their signals as well, to prevent observers
880
 
    // from being notified while their collection is frozen.
 
907
    // from being notified while their collection is frozen, and only fire them when
 
908
    // internal_collection_thawed is called.
881
909
    //
882
910
    // For DataCollection, the signals affected are item_altered, item_metadata_altered, and
883
911
    // ordering_changed (and their corresponding signals in DataObject).
884
 
    //
885
 
    // WARNING: In current implementation, this drops incoming signals from the DataObjects, relying
886
 
    // on aggregate signals (items_altered rather than item_altered, etc.) to notify observers.
887
 
    // This should be used selectively, and with caution.
888
912
    public void freeze_notifications() {
889
913
        if (notifies_frozen++ == 0)
890
 
            frozen();
 
914
            notify_frozen();
891
915
    }
892
916
    
893
917
    public void thaw_notifications() {
895
919
            return;
896
920
        
897
921
        if (--notifies_frozen == 0)
898
 
            thawed();
 
922
            notify_thawed();
899
923
    }
900
924
    
901
925
    public bool are_notifications_frozen() {
904
928
    
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() {
 
932
        frozen();
908
933
    }
909
934
    
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;
 
942
            
 
943
            foreach (DataObject object in copy.keys)
 
944
                notify_item_altered(object, copy.get(object));
 
945
            notify_items_altered(copy);
918
946
        }
919
947
        
920
948
        if (fire_ordering_changed) {
921
949
            fire_ordering_changed = false;
922
950
            notify_ordering_changed();
923
951
        }
 
952
        
 
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();
 
957
        
 
958
        thawed();
924
959
    }
925
960
}
926
961
 
1206
1241
        Gee.Collection<DataSource> added) {
1207
1242
        // if source is in holding tank, remove it now and relink to collection
1208
1243
        if (holding_tank.contains(container)) {
1209
 
            debug("Adding %s from holding tank in %s", container.to_string(), to_string());
1210
 
            
1211
1244
            bool removed = holding_tank.remove(container);
1212
1245
            assert(removed);
1213
1246
            
1254
1287
    }
1255
1288
    
1256
1289
    private void on_contained_sources_unlinking(Gee.Collection<DataSource> unlinking) {
 
1290
        Gee.HashMultiMap<ContainerSource, DataSource> map =
 
1291
            new Gee.HashMultiMap<ContainerSource, DataSource>();
 
1292
        
1257
1293
        foreach (DataSource source in unlinking) {
1258
1294
            Gee.Collection<ContainerSource>? containers = get_containers_holding_source(source);
1259
1295
            if (containers == null || containers.size == 0)
1260
1296
                continue;
1261
1297
            
1262
 
            foreach (ContainerSource container in containers)
 
1298
            foreach (ContainerSource container in containers) {
 
1299
                map.set(container, source);
1263
1300
                source.set_backlink(container.get_backlink());
1264
 
            
1265
 
            foreach (ContainerSource container in containers)
1266
 
                container.break_link(source);
 
1301
            }
1267
1302
        }
 
1303
        
 
1304
        foreach (ContainerSource container in map.get_keys())
 
1305
            container.break_link_many(map.get(container));
1268
1306
    }
1269
1307
    
1270
1308
    private void on_contained_sources_relinked(Gee.Collection<DataSource> relinked) {
 
1309
        Gee.HashMultiMap<ContainerSource, DataSource> map =
 
1310
            new Gee.HashMultiMap<ContainerSource, DataSource>();
 
1311
        
1271
1312
        foreach (DataSource source in relinked) {
1272
1313
            Gee.List<SourceBacklink>? backlinks = source.get_backlinks(backlink_name);
1273
1314
            if (backlinks == null || backlinks.size == 0)
1275
1316
            
1276
1317
            foreach (SourceBacklink backlink in backlinks) {
1277
1318
                ContainerSource? container = convert_backlink_to_container(backlink);
1278
 
                if (container != null)
1279
 
                    container.establish_link(source);
1280
 
                else
 
1319
                if (container != null) {
 
1320
                    map.set(container, source);
 
1321
                } else {
1281
1322
                    warning("Unable to relink %s to container backlink %s", source.to_string(),
1282
1323
                        backlink.to_string());
 
1324
                }
1283
1325
            }
1284
1326
        }
 
1327
        
 
1328
        foreach (ContainerSource container in map.get_keys())
 
1329
            container.establish_link_many(map.get(container));
1285
1330
    }
1286
1331
    
1287
1332
    private void on_contained_source_destroyed(DataSource source) {
1289
1334
        while (iter.next()) {
1290
1335
            ContainerSource container = iter.get();
1291
1336
            if (!container.has_links()) {
1292
 
                debug("Destroying %s in %s holding tank: no more backlinks", container.to_string(),
1293
 
                    to_string());
1294
 
                
1295
1337
                iter.remove();
1296
1338
                container.destroy_orphan(true);
1297
1339
            }
1310
1352
    // destroyed.
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());
1314
 
            
1315
1355
            unlink_marked(mark(container));
1316
1356
            bool added = holding_tank.add(container);
1317
1357
            assert(added);
1318
1358
        } else {
1319
 
            debug("Destroying %s in %s", container.to_string(), to_string());
1320
 
            
1321
1359
            destroy_marked(mark(container), true);
1322
1360
        }
1323
1361
    }
1324
1362
}
1325
1363
 
 
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.
 
1368
//
 
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).
 
1374
//
 
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.
 
1380
 
 
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);
 
1384
    
 
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;
 
1391
    
 
1392
    public virtual signal void contents_altered(Gee.Collection<DataSource>? added,
 
1393
        Gee.Collection<DataSource>? removed) {
 
1394
    }
 
1395
    
 
1396
    public SourceHoldingTank(SourceCollection sources, CheckToKeep check_to_keep) {
 
1397
        this.sources = sources;
 
1398
        this.check_to_keep = check_to_keep;
 
1399
        
 
1400
        this.sources.item_destroyed.connect(on_source_destroyed);
 
1401
        this.sources.thawed.connect(on_source_collection_thawed);
 
1402
    }
 
1403
    
 
1404
    ~SourceHoldingTank() {
 
1405
        sources.item_destroyed.disconnect(on_source_destroyed);
 
1406
        sources.thawed.disconnect(on_source_collection_thawed);
 
1407
        
 
1408
        foreach (DataObject object in tank.get_all())
 
1409
            ((DataSource) object).altered.disconnect(on_held_source_altered);
 
1410
    }
 
1411
    
 
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);
 
1417
        }
 
1418
        
 
1419
        if (removed != null) {
 
1420
            foreach (DataSource source in removed)
 
1421
                source.altered.disconnect(on_held_source_altered);
 
1422
        }
 
1423
        
 
1424
        contents_altered(added, removed);
 
1425
    }
 
1426
    
 
1427
    public int get_count() {
 
1428
        return tank.get_count();
 
1429
    }
 
1430
    
 
1431
    public Gee.Collection<DataSource> get_all() {
 
1432
        return (Gee.Collection<DataSource>) tank.get_all();
 
1433
    }
 
1434
    
 
1435
    public bool contains(DataSource source) {
 
1436
        return tank.contains(source) || unlinking.contains(source);
 
1437
    }
 
1438
    
 
1439
    // Only use for DataSources that have not been installed in their SourceCollection.
 
1440
    public void add_many(Gee.Collection<DataSource> many) {
 
1441
        if (many.size == 0)
 
1442
            return;
 
1443
        
 
1444
        foreach (DataSource source in many)
 
1445
            source.internal_set_ordinal(ordinal++);
 
1446
        
 
1447
        bool added = tank.add_many(many);
 
1448
        assert(added);
 
1449
        
 
1450
        notify_contents_altered(many, null);
 
1451
    }
 
1452
    
 
1453
    // Do not pass in DataSources which have already been unlinked, including into this holding
 
1454
    // tank.
 
1455
    public void unlink_and_hold(Gee.Collection<DataSource> unlink) {
 
1456
        if (unlink.size == 0)
 
1457
            return;
 
1458
        
 
1459
        // store in the unlinking collection to guard against reentrancy
 
1460
        unlinking.add_all(unlink);
 
1461
        
 
1462
        sources.unlink_marked(sources.mark_many(unlink));
 
1463
        
 
1464
        foreach (DataSource source in unlink)
 
1465
            source.internal_set_ordinal(ordinal++);
 
1466
        
 
1467
        bool added = tank.add_many(unlink);
 
1468
        assert(added);
 
1469
        
 
1470
        // remove from the unlinking pool, as they're now unlinked
 
1471
        unlinking.remove_all(unlink);
 
1472
        
 
1473
        notify_contents_altered(unlink, null);
 
1474
    }
 
1475
    
 
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))
 
1480
                return true;
 
1481
        }
 
1482
        
 
1483
        return false;
 
1484
    }
 
1485
    
 
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);
 
1490
    }
 
1491
    
 
1492
    public void destroy_orphans(Gee.List<DataSource> destroy, bool delete_backing,
 
1493
        ProgressMonitor? monitor = null) {
 
1494
        if (destroy.size == 0)
 
1495
            return;
 
1496
        
 
1497
        bool removed = tank.remove_many(destroy);
 
1498
        assert(removed);
 
1499
        
 
1500
        notify_contents_altered(null, destroy);
 
1501
        
 
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);
 
1507
        }
 
1508
    }
 
1509
    
 
1510
    private void on_source_destroyed(DataSource source) {
 
1511
        if (!tank.contains(source))
 
1512
            return;
 
1513
        
 
1514
        bool removed = tank.remove(source);
 
1515
        assert(removed);
 
1516
        
 
1517
        notify_contents_altered(null, new SingletonCollection<DataSource>(source));
 
1518
    }
 
1519
    
 
1520
    private void on_held_source_altered(DataObject object, Alteration alteration) {
 
1521
        DataSource source = (DataSource) object;
 
1522
        
 
1523
        assert(tank.contains(source));
 
1524
        
 
1525
        // see if it should stay put
 
1526
        if (check_to_keep(source, alteration))
 
1527
            return;
 
1528
        
 
1529
        bool removed = tank.remove(source);
 
1530
        assert(removed);
 
1531
        
 
1532
        if (sources.are_notifications_frozen()) {
 
1533
            relinks.add(source);
 
1534
            
 
1535
            return;
 
1536
        }
 
1537
        
 
1538
        notify_contents_altered(null, new SingletonCollection<DataSource>(source));
 
1539
        
 
1540
        sources.relink(source);
 
1541
    }
 
1542
    
 
1543
    private void on_source_collection_thawed() {
 
1544
        if (relinks.size == 0)
 
1545
            return;
 
1546
        
 
1547
        // swap out to protect against reentrancy
 
1548
        Gee.HashSet<DataSource> copy = relinks;
 
1549
        relinks = new Gee.HashSet<DataSource>();
 
1550
        
 
1551
        notify_contents_altered(null, copy);
 
1552
        
 
1553
        sources.relink_many(copy);
 
1554
    }
 
1555
}
 
1556
 
 
1557
public class DatabaseSourceHoldingTank : SourceHoldingTank {
 
1558
    private GetSourceDatabaseKey get_key;
 
1559
    private Gee.HashMap<int64?, DataSource> map = new Gee.HashMap<int64?, DataSource>(int64_hash,
 
1560
        int64_equal);
 
1561
    
 
1562
    public DatabaseSourceHoldingTank(SourceCollection sources,
 
1563
        SourceHoldingTank.CheckToKeep check_to_keep, GetSourceDatabaseKey get_key) {
 
1564
        base (sources, check_to_keep);
 
1565
        
 
1566
        this.get_key = get_key;
 
1567
    }
 
1568
    
 
1569
    public DataSource? get_by_id(int64 id) {
 
1570
        return map.get(id);
 
1571
    }
 
1572
    
 
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);
 
1578
        }
 
1579
        
 
1580
        if (removed != null) {
 
1581
            foreach (DataSource source in removed)
 
1582
                map.unset(get_key(source));
 
1583
        }
 
1584
        
 
1585
        base.notify_contents_altered(added, removed);
 
1586
    }
 
1587
}
 
1588
 
1326
1589
//
1327
1590
// ViewCollection
1328
1591
//
1378
1641
    private DataSet visible = null;
1379
1642
    private Gee.HashSet<DataView> frozen_views_altered = null;
1380
1643
    private Gee.HashSet<DataView> frozen_geometries_altered = null;
 
1644
    private int view_filter_lock_count = 0;
 
1645
    // if true the items needs to be shown, if false, it needs to be hidden
 
1646
    private Gee.HashMap<DataView, bool> locked_filter_items = null;
1381
1647
    
1382
1648
    // TODO: source-to-view mapping ... for now, only one view is allowed for each source.
1383
1649
    // This may need to change in the future.
1404
1670
    }
1405
1671
    
1406
1672
    // Signal aggregator.
1407
 
    public virtual signal void items_shown(Gee.Iterable<DataView> visible) {
1408
 
    }
1409
 
    
1410
 
    // Signal aggregator.
1411
 
    public virtual signal void items_hidden(Gee.Iterable<DataView> hidden) {
1412
 
    }
1413
 
    
1414
 
    // Signal aggregator.
1415
 
    public virtual signal void items_visibility_changed(Gee.Iterable<DataView> changed) {
 
1673
    public virtual signal void items_shown(Gee.Collection<DataView> visible) {
 
1674
    }
 
1675
    
 
1676
    // Signal aggregator.
 
1677
    public virtual signal void items_hidden(Gee.Collection<DataView> hidden) {
 
1678
    }
 
1679
    
 
1680
    // Signal aggregator.
 
1681
    public virtual signal void items_visibility_changed(Gee.Collection<DataView> changed) {
1416
1682
    }
1417
1683
    
1418
1684
    // Signal aggregator.
1431
1697
    
1432
1698
    public ViewCollection(string name) {
1433
1699
        base (name);
 
1700
        
 
1701
        locked_filter_items = new Gee.HashMap<DataView, bool>();
1434
1702
    }
1435
1703
    
1436
1704
    protected virtual void notify_items_selected(Gee.Iterable<DataView> views) {
1467
1735
        geometries_altered(views);
1468
1736
    }
1469
1737
    
 
1738
    public virtual void notify_items_shown(Gee.Collection<DataView> shown) {
 
1739
        items_shown(shown);
 
1740
    }
 
1741
    
 
1742
    public virtual void notify_items_hidden(Gee.Collection<DataView> hidden) {
 
1743
        items_hidden(hidden);
 
1744
    }
 
1745
    
 
1746
    public virtual void notify_items_visibility_changed(Gee.Collection<DataView> changed) {
 
1747
        items_visibility_changed(changed);
 
1748
    }
 
1749
    
1470
1750
    public override void clear() {
1471
1751
        // cannot clear a ViewCollection if it is monitoring a SourceCollection or mirroring a
1472
1752
        // ViewCollection
1477
1757
        }
1478
1758
        
1479
1759
        base.clear();
 
1760
        
 
1761
        locked_filter_items.clear();
 
1762
        view_filter_lock_count = 0;
1480
1763
    }
1481
1764
    
1482
1765
    public override void close() {
1512
1795
        // those changes in this collection
1513
1796
        sources.items_added.connect(on_sources_added);
1514
1797
        sources.items_removed.connect(on_sources_removed);
1515
 
        sources.item_altered.connect(on_source_altered);
 
1798
        sources.items_altered.connect(on_sources_altered);
1516
1799
    }
1517
1800
    
1518
1801
    public void halt_monitoring() {
1519
1802
        if (sources != null) {
1520
1803
            sources.items_added.disconnect(on_sources_added);
1521
1804
            sources.items_removed.disconnect(on_sources_removed);
1522
 
            sources.item_altered.disconnect(on_source_altered);
 
1805
            sources.items_altered.disconnect(on_sources_altered);
1523
1806
        }
1524
1807
        
1525
1808
        sources = null;
1564
1847
    // This is used when conditions outside of the collection have changed and the entire collection
1565
1848
    // should be re-filtered.
1566
1849
    public void reapply_view_filter() {
1567
 
        if (filter == null)
1568
 
            return;
1569
 
        
1570
 
        // Can't use the marking system because ViewCollection completely overrides DataCollection,
1571
 
        // hence hidden items can't be marked.  Okay to do this manually because we know what we're
1572
 
        // doing here in regards to adding and removing objects from lists.
1573
 
        Gee.ArrayList<DataView> to_show = new Gee.ArrayList<DataView>();
1574
 
        Gee.ArrayList<DataView> to_hide = new Gee.ArrayList<DataView>();
1575
 
        
1576
 
        // iterate through base.all(), otherwise merely iterating the visible items
1577
 
        foreach (DataObject object in base.get_all()) {
1578
 
            DataView view = (DataView) object;
1579
 
            
1580
 
            if (filter(view)) {
1581
 
                if (!view.is_visible())
1582
 
                    to_show.add((DataView) object);
1583
 
            } else {
1584
 
                if (view.is_visible())
1585
 
                    to_hide.add((DataView) object);
1586
 
            }
1587
 
        }
1588
 
        
1589
 
        if (to_show.size > 0)
1590
 
            show_items(to_show);
1591
 
        
1592
 
        if (to_hide.size > 0)
1593
 
            hide_items(to_hide);
 
1850
        filter_altered_items((Gee.Collection<DataView>) base.get_all());
1594
1851
    }
1595
1852
    
1596
1853
    public void reset_view_filter() {
1650
1907
        else if (created_views != null && created_views.size > 0)
1651
1908
            add_many(created_views, monitor);
1652
1909
    }
 
1910
 
 
1911
    public override bool add(DataObject object) {
 
1912
        if (!base.add(object))
 
1913
            return false;
 
1914
        
 
1915
        DataView view = (DataView) object;
 
1916
        Gee.Collection<DataView> views = (Gee.Collection<DataView>) get_singleton(view);
 
1917
        
 
1918
        view.internal_set_visible(true);
 
1919
        add_many_visible(views);
 
1920
        filter_altered_items(views);
 
1921
        
 
1922
        return true;
 
1923
    }
 
1924
 
 
1925
    public override Gee.Collection<DataObject> add_many(Gee.Collection<DataObject> objects, 
 
1926
        ProgressMonitor? monitor = null) {
 
1927
        Gee.Collection<DataObject> return_list = base.add_many(objects, monitor);
 
1928
        
 
1929
        foreach (DataObject object in return_list)
 
1930
            ((DataView) object).internal_set_visible(true);
 
1931
        
 
1932
        add_many_visible((Gee.Collection<DataView>) return_list);
 
1933
        filter_altered_items((Gee.Collection<DataView>) return_list);
 
1934
        
 
1935
        return return_list;
 
1936
    }
1653
1937
    
1654
1938
    private void on_sources_removed(Gee.Iterable<DataSource> removed) {
1655
1939
        // mark all view items associated with the source to be removed
1670
1954
            remove_marked(marker);
1671
1955
    }
1672
1956
    
1673
 
    private void on_source_altered(DataObject object, Alteration alteration) {
1674
 
        DataSource source = (DataSource) object;
1675
 
        
 
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);
1686
 
            
1687
 
            if (selected.contains(view))
1688
 
                selected.resort_object(view);
1689
 
            
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);
 
1966
            
 
1967
            bool include = manager.include_in_view(source);
 
1968
            if (include && !has_view_for_source(source)) {
 
1969
                if (to_add == null)
 
1970
                    to_add = new Gee.ArrayList<DataView>();
 
1971
                
 
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>();
 
1976
                
 
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);
 
1980
                
 
1981
                if (selected.contains(view))
 
1982
                    selected.resort_object(view, alteration);
 
1983
                
 
1984
                if (visible != null && is_visible(view)) {
 
1985
                    if (visible.resort_object(view, alteration))
 
1986
                        ordering_changed = true;
 
1987
                }
1693
1988
            }
1694
1989
        }
 
1990
        
 
1991
        if (to_add != null)
 
1992
            add_many(to_add);
 
1993
        
 
1994
        if (to_remove != null)
 
1995
            remove_marked(mark_many(to_remove));
 
1996
        
 
1997
        if (ordering_changed)
 
1998
            notify_ordering_changed();
1695
1999
    }
1696
2000
    
1697
2001
    private void on_mirror_contents_added(Gee.Iterable<DataObject> added) {
1719
2023
    }
1720
2024
    
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;
1725
2029
        
1726
2030
        foreach (DataObject object in added) {
1727
2031
            DataView view = (DataView) object;
1728
2032
            source_map.set(view.get_source(), view);
1729
2033
            
1730
 
            if (view.is_selected())
 
2034
            if (view.is_selected()) {
 
2035
                if (added_selected == null)
 
2036
                    added_selected = new Gee.ArrayList<DataView>();
 
2037
                
1731
2038
                added_selected.add(view);
 
2039
            }
1732
2040
            
1733
2041
            if (filter != null)
1734
2042
                view.internal_set_visible(filter(view));
1735
2043
            
1736
 
            if (view.is_visible())
 
2044
            if (view.is_visible()) {
 
2045
                if (added_visible == null)
 
2046
                    added_visible = new Gee.ArrayList<DataView>();
 
2047
                
1737
2048
                added_visible.add(view);
1738
 
        }
1739
 
        
1740
 
        bool is_added = add_many_visible(added_visible);
1741
 
        assert(is_added);
1742
 
        is_added = selected.add_many(added_selected);
1743
 
        assert(is_added);
1744
 
        
1745
 
        if (added_selected.size > 0)
 
2049
            }
 
2050
        }
 
2051
        
 
2052
        if (added_visible != null) {
 
2053
            bool is_added = add_many_visible(added_visible);
 
2054
            assert(is_added);
 
2055
        }
 
2056
        
 
2057
        if (added_selected != null) {
 
2058
            bool is_added = selected.add_many(added_selected);
 
2059
            assert(is_added);
 
2060
            
1746
2061
            notify_items_selected(added_selected);
 
2062
        }
1747
2063
        
1748
2064
        base.notify_items_added(added);
1749
2065
    }
1750
2066
    
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);
1778
2094
    }
1779
2095
    
1780
 
    private void filter_altered_item(DataObject object) {
 
2096
    private void filter_altered_items(Gee.Collection<DataView> views) {
1781
2097
        if (filter == null)
1782
2098
            return;
1783
2099
        
1784
 
        DataView view = (DataView) object;
1785
 
        
1786
2100
        // Can't use the marker system because ViewCollection completely overrides DataCollection
1787
2101
        // and hidden items cannot be marked.
1788
 
        if (filter(view)) {
1789
 
            if (!view.is_visible()) {
1790
 
                Gee.ArrayList<DataView> to_show = new Gee.ArrayList<DataView>();
1791
 
                to_show.add(view);
1792
 
                show_items(to_show);
1793
 
            }
1794
 
        } else {
1795
 
            if (view.is_visible()) {
1796
 
                Gee.ArrayList<DataView> to_hide = new Gee.ArrayList<DataView>();
1797
 
                to_hide.add(view);
1798
 
                hide_items(to_hide);
1799
 
            }
1800
 
        }
1801
 
    }
1802
 
    
1803
 
    public override void item_altered(DataObject object, Alteration alteration) {
1804
 
        filter_altered_item(object);
1805
 
 
1806
 
        base.item_altered(object, alteration);
1807
 
    }
1808
 
    
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;
 
2104
        
 
2105
        foreach (DataView view in views) {
 
2106
            if (filter(view)) {
 
2107
                if (locked_filter_items.has_key(view) || !view.is_visible()) {
 
2108
                    if (to_show == null)
 
2109
                        to_show = new Gee.ArrayList<DataView>();
 
2110
                    
 
2111
                    to_show.add(view);
 
2112
                }
 
2113
            } else {
 
2114
                if (locked_filter_items.has_key(view) || view.is_visible()) {
 
2115
                    if (to_hide == null)
 
2116
                        to_hide = new Gee.ArrayList<DataView>();
 
2117
                    
 
2118
                    to_hide.add(view);
 
2119
                }
 
2120
            }
 
2121
        }
 
2122
 
 
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);
 
2127
                }
 
2128
            }
 
2129
            
 
2130
            if (to_hide != null) {
 
2131
                foreach (DataView view in to_hide) {
 
2132
                    locked_filter_items.set(view, false);
 
2133
                }
 
2134
            }
 
2135
            return;
 
2136
        }
 
2137
        
 
2138
        if (to_show != null)
 
2139
            show_items(to_show);
 
2140
        
 
2141
        if (to_hide != null)
 
2142
            hide_items(to_hide);
 
2143
    }
 
2144
    
 
2145
    public override void items_altered(Gee.Map<DataObject, Alteration> map) {
 
2146
        filter_altered_items(map.keys);
 
2147
 
 
2148
        base.items_altered(map);
 
2149
    }
 
2150
    
 
2151
    public bool is_view_filter_locked() {
 
2152
        return view_filter_lock_count > 0;
 
2153
    }
 
2154
    
 
2155
    public void lock_view_filter() {
 
2156
        view_filter_lock_count++;
 
2157
    }
 
2158
    
 
2159
    public void unlock_view_filter() {
 
2160
        view_filter_lock_count--;
 
2161
        
 
2162
        if (view_filter_lock_count > 0)
 
2163
            return;
 
2164
        
 
2165
        assert(view_filter_lock_count == 0); // watch out for negative numbers
 
2166
        
 
2167
        Gee.ArrayList<DataView> to_show = new Gee.ArrayList<DataView>();
 
2168
        Gee.ArrayList<DataView> to_hide = new Gee.ArrayList<DataView>();
 
2169
        
 
2170
        foreach (DataView key in locked_filter_items.keys) {
 
2171
            if (locked_filter_items[key] && !key.is_visible())
 
2172
                to_show.add(key);
 
2173
            else if (!locked_filter_items[key] && key.is_visible())
 
2174
                to_hide.add(key);
 
2175
        }
 
2176
        
 
2177
        if (to_show.size > 0)
 
2178
            show_items(to_show);
 
2179
        if (to_hide.size > 0)
 
2180
            hide_items(to_hide);
 
2181
        
 
2182
        locked_filter_items.clear();
 
2183
    }
 
2184
    
 
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);
1813
2189
        
1814
 
        base.set_comparator(comparator);
 
2190
        base.set_comparator(comparator, predicate);
1815
2191
    }
1816
2192
    
1817
2193
    public override void reset_comparator() {
2085
2461
    private bool is_visible(DataView view) {
2086
2462
        return (visible != null) ? visible.contains(view) : true;
2087
2463
    }
2088
 
    
2089
 
    private bool add_many_visible(Gee.List<DataView> many) {
 
2464
 
 
2465
    private bool add_many_visible(Gee.Collection<DataView> many) {
2090
2466
        if (visible == null)
2091
2467
            return true;
2092
2468
        
2130
2506
            notify_items_unselected(unselected);
2131
2507
        
2132
2508
        if (to_hide.size > 0) {
2133
 
            items_hidden(to_hide);
2134
 
            items_visibility_changed(to_hide);
 
2509
            notify_items_hidden(to_hide);
 
2510
            notify_items_visibility_changed(to_hide);
2135
2511
        }
2136
2512
    }
2137
2513
    
2159
2535
        assert(added);
2160
2536
        
2161
2537
        if (to_show.size > 0) {
2162
 
            items_shown(to_show);
2163
 
            items_visibility_changed(to_show);
 
2538
            notify_items_shown(to_show);
 
2539
            notify_items_visibility_changed(to_show);
2164
2540
        }
2165
2541
    }
2166
2542
    
2210
2586
        }
2211
2587
    }
2212
2588
    
2213
 
    protected override void thawed() {
 
2589
    protected override void notify_thawed() {
2214
2590
        if (frozen_views_altered != null) {
2215
2591
            foreach (DataView view in frozen_views_altered)
2216
2592
                notify_item_view_altered(view);
2225
2601
            frozen_geometries_altered = null;
2226
2602
        }
2227
2603
        
2228
 
        base.thawed();
 
2604
        base.notify_thawed();
2229
2605
    }
2230
2606
}
2231
2607