diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-23 09:06:59 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-07-23 09:06:59 +0200 |
commit | 4ea2cc3bd4a7d9b1c54a9d33e6a1cf82e7c8c21d (patch) | |
tree | d2e54377d14d604356c86862a326f64ae64dadd6 /src/MediaInterfaces.vala |
Imported Upstream version 0.18.1upstream/0.18.1
Diffstat (limited to 'src/MediaInterfaces.vala')
-rw-r--r-- | src/MediaInterfaces.vala | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/src/MediaInterfaces.vala b/src/MediaInterfaces.vala new file mode 100644 index 0000000..96c5d49 --- /dev/null +++ b/src/MediaInterfaces.vala @@ -0,0 +1,215 @@ +/* Copyright 2010-2014 Yorba Foundation + * + * This software is licensed under the GNU LGPL (version 2.1 or later). + * See the COPYING file in this distribution. + */ + +// +// Going forward, Shotwell will use MediaInterfaces, which allow for various operations and features +// to be added only to the MediaSources that support them (or make sense for). For example, adding +// a library-mode photo or video to an Event makes perfect sense, but does not make sense for a +// direct-mode photo. All three are MediaSources, and to make DirectPhoto descend from another +// base class is only inviting chaos and a tremendous amount of replicated code. +// +// A key point to make of all MediaInterfaces is that they require MediaSource as a base class. +// Thus, any code dealing with one of these interfaces knows they are also dealing with a +// MediaSource. +// +// TODO: Make Eventable and Taggable interfaces, which are the only types Event and Tag will deal +// with (rather than MediaSources). +// +// TODO: Make Trashable interface, which are much like Flaggable. +// +// TODO: ContainerSources may also have specific needs in the future; an interface-based system +// may make sense as well when that need arises. +// + +// +// TransactionController +// +// Because many operations in Shotwell need to be performed on collections of objects all at once, +// and that most of these objects are backed by a database, the TransactionController object gives +// a way to generically group a series of operations on one or more similar objects into a single +// transaction. This class is listed here because it's used by the various media interfaces to offer +// multiple operations. +// +// begin() and commit() may be called multiple times in layering fashion. The implementation +// accounts for this. If either throws an exception it should be assumed that the object is in +// a "clean" state; that is, if begin() throws an exception, there is no need to call commit(), +// and if commit() throws an exception, it does not need to be called again to revert the object +// state. +// +// This means that any user who calls begin() *must* match it with a corresponding commit(), even +// if there is an error during the transaction. It is up to the user to back out any undesired +// changes. +// +// Because of the nature of this object, it's assumed that every object type will share one +// between all callers. +// +// The object is thread-safe. There is no guarantee that the underlying persistent store is, +// however. +public abstract class TransactionController { + private int count = 0; + + public TransactionController() { + } + + ~TransactionController() { + lock (count) { + assert(count == 0); + } + } + + public void begin() { + lock (count) { + if (count++ != 0) + return; + + try { + begin_impl(); + } catch (Error err) { + // unwind + count--; + + if (err is DatabaseError) + AppWindow.database_error((DatabaseError) err); + else + AppWindow.panic("%s".printf(err.message)); + } + } + } + + // For thread safety, this method will only be called under the protection of a mutex. + public abstract void begin_impl() throws Error; + + public void commit() { + lock (count) { + assert(count > 0); + if (--count != 0) + return; + + // no need to unwind the count here; it's already unwound. + try { + commit_impl(); + } catch (Error err) { + if (err is DatabaseError) + AppWindow.database_error((DatabaseError) err); + else + AppWindow.panic("%s".printf(err.message)); + } + } + } + + // For thread safety, this method will only be called under the protection of a mutex. + public abstract void commit_impl() throws Error; +} + +// +// Flaggable +// +// Flaggable media can be marked for later use in batch operations. +// +// The mark_flagged() and mark_unflagged() methods should fire "metadata:flags" and "metadata:flagged" +// alterations if the flag has changed. +public interface Flaggable : MediaSource { + public abstract bool is_flagged(); + + public abstract void mark_flagged(); + + public abstract void mark_unflagged(); + + public static void mark_many_flagged_unflagged(Gee.Collection<Flaggable>? flag, + Gee.Collection<Flaggable>? unflag, TransactionController controller) throws Error { + controller.begin(); + + if (flag != null) { + foreach (Flaggable flaggable in flag) + flaggable.mark_flagged(); + } + + if (unflag != null) { + foreach (Flaggable flaggable in unflag) + flaggable.mark_unflagged(); + } + + controller.commit(); + } +} + +// +// Monitorable +// +// Monitorable media can be updated at startup or run-time about changes to their backing file(s). +// +// The mark_online() and mark_offline() methods should fire "metadata:flags" and "metadata:online-state" +// alterations if the flag has changed. +// +// The set_master_file() method should fire "backing:master" alteration and "metadata:name" if +// the name of the file is determined by the filename (which is default behavior). It should also +// call notify_master_file_replaced(). +// +// The set_master_timestamp() method should fire "metadata:master-timestamp" alteration. +public interface Monitorable : MediaSource { + public abstract bool is_offline(); + + public abstract void mark_online(); + + public abstract void mark_offline(); + + public static void mark_many_online_offline(Gee.Collection<Monitorable>? online, + Gee.Collection<Monitorable>? offline, TransactionController controller) throws Error { + controller.begin(); + + if (online != null) { + foreach (Monitorable monitorable in online) + monitorable.mark_online(); + } + + if (offline != null) { + foreach (Monitorable monitorable in offline) + monitorable.mark_offline(); + } + + controller.commit(); + } + + public abstract void set_master_file(File file); + + public static void set_many_master_file(Gee.Map<Monitorable, File> map, + TransactionController controller) throws Error { + controller.begin(); + + Gee.MapIterator<Monitorable, File> map_iter = map.map_iterator(); + while (map_iter.next()) + map_iter.get_key().set_master_file(map_iter.get_value()); + + controller.commit(); + } + + public abstract void set_master_timestamp(FileInfo info); + + public static void set_many_master_timestamp(Gee.Map<Monitorable, FileInfo> map, + TransactionController controller) throws Error { + controller.begin(); + + Gee.MapIterator<Monitorable, FileInfo> map_iter = map.map_iterator(); + while (map_iter.next()) + map_iter.get_key().set_master_timestamp(map_iter.get_value()); + + controller.commit(); + } +} + +// +// Dateable +// +// Dateable media may have their exposure date and time set arbitrarily. +// +// The set_exposure_time() method refactors the existing set_exposure_time() +// from Photo to here in order to add this capability to videos. It should +// fire a "metadata:exposure-time" alteration when called. +public interface Dateable : MediaSource { + public abstract void set_exposure_time(time_t target_time); + + public abstract time_t get_exposure_time(); +} |