Commit da9a2034 authored by Jim Nelson's avatar Jim Nelson

#182: Further work on plugins interfaces.

parent b80664e8
......@@ -12,12 +12,13 @@
VALAC := valac
MAKE_FILES := Makefile ../Makefile.plugin.mk ../plugins.mk
HEADER_FILES := $(wildcard ../*.vapi ../*.h ../*.deps)
include ../plugins.mk
all: $(PLUGIN).so
$(PLUGIN).so: $(SRC_FILES) $(MAKE_FILES)
$(PLUGIN).so: $(SRC_FILES) $(MAKE_FILES) $(HEADER_FILES)
$(VALAC) --save-temps --main=dummy_main --vapidir=../ \
$(foreach pkg,$(PKGS),--pkg=$(pkg)) \
-X -I../.. -X --shared -X -fPIC -X -D_VERSION='"$(PLUGINS_VERSION)"' -X -DGETTEXT_PACKAGE='"shotwell"' $(SRC_FILES) -o $@
......
......@@ -13,14 +13,22 @@ public class FacebookService : Object, Spit.Pluggable, Spit.Publishing.Publishin
Spit.Publishing.CURRENT_API_VERSION);
}
public string get_service_id() {
return "org.yorba.shotwell.publishing.core_services.facebook";
public string get_id() {
return "org.yorba.shotwell.publishing.facebook";
}
public string get_service_name() {
public string get_pluggable_name() {
return "Facebook";
}
public void get_info(out Spit.PluggableInfo info) {
info.copyright = _("Copyright 2009-2011 Yorba Foundation");
info.translators = _("translator-credits");
info.version = _VERSION;
info.website_name = _("Visit the Yorba web site");
info.website_url = "http://www.yorba.org";
}
public Spit.Publishing.Publisher create_publisher() {
return new Publishing.Facebook.FacebookPublisher();
}
......
......@@ -7,7 +7,7 @@
extern const string _VERSION;
// "core services" are: Facebook, Flickr, Picasa Web Albums, and YouTube
private class ShotwellPublishingCoreServices : Object, Spit.Wad {
private class ShotwellPublishingCoreServices : Object, Spit.Module {
private Spit.Pluggable[] pluggables = new Spit.Pluggable[0];
public ShotwellPublishingCoreServices() {
......@@ -25,7 +25,7 @@ private class ShotwellPublishingCoreServices : Object, Spit.Wad {
return _VERSION;
}
public string get_wad_name() {
public string get_id() {
return "org.yorba.shotwell.publishing.core_services";
}
......@@ -38,7 +38,7 @@ private ShotwellPublishingCoreServices? core_services = null;
private Spit.EntryPoint? compiler_entry_point = null;
// This entry point is required for all SPIT modules.
public unowned Spit.Wad? spit_entry_point(int host_min_spit_interface, int host_max_spit_interface,
public unowned Spit.Module? spit_entry_point(int host_min_spit_interface, int host_max_spit_interface,
out int module_spit_interface) {
// this is purely for compilation, to verify that the entry point matches SpitEntryPoint's sig;
// it does nothing functionally
......
......@@ -8,16 +8,15 @@
using Spit;
private class CrumbleEffectDescriptor : ShotwellTransitionDescriptor {
public override string get_effect_id() {
public override string get_id() {
return "org.yorba.shotwell.transitions.crumble";
}
public override string get_effect_name() {
// TODO: Need to enable gettext for the plug-ins
return "Crumble";
public override string get_pluggable_name() {
return _("Crumble");
}
public override Transitions.Effect create() {
public override Transitions.Effect create(Spit.HostInterface host) {
return new CrumbleEffect();
}
}
......
......@@ -8,16 +8,15 @@
using Spit;
private class FadeEffectDescriptor : ShotwellTransitionDescriptor {
public override string get_effect_id() {
public override string get_id() {
return "org.yorba.shotwell.transitions.fade";
}
public override string get_effect_name() {
// TODO: Need to enable gettext for the plug-ins
return "Fade";
public override string get_pluggable_name() {
return _("Fade");
}
public override Transitions.Effect create() {
public override Transitions.Effect create(Spit.HostInterface host) {
return new FadeEffect();
}
}
......
......@@ -8,16 +8,15 @@
using Spit;
private class SlideEffectDescriptor : ShotwellTransitionDescriptor {
public override string get_effect_id() {
public override string get_id() {
return "org.yorba.shotwell.transitions.slide";
}
public override string get_effect_name() {
// TODO: Need to enable gettext for the plug-ins
return "Slide";
public override string get_pluggable_name() {
return _("Slide");
}
public override Transitions.Effect create() {
public override Transitions.Effect create(Spit.HostInterface host) {
return new SlideEffect();
}
}
......
......@@ -6,7 +6,7 @@
extern const string _VERSION;
private class ShotwellTransitions : Object, Spit.Wad {
private class ShotwellTransitions : Object, Spit.Module {
private Spit.Pluggable[] pluggables = new Spit.Pluggable[0];
public ShotwellTransitions() {
......@@ -23,7 +23,7 @@ private class ShotwellTransitions : Object, Spit.Wad {
return _VERSION;
}
public string get_wad_name() {
public string get_id() {
return "org.yorba.shotwell.transitions";
}
......@@ -35,7 +35,7 @@ private class ShotwellTransitions : Object, Spit.Wad {
private ShotwellTransitions? spitwad = null;
// This entry point is required for all SPIT modules.
public unowned Spit.Wad? spit_entry_point(int host_min_spit_interface, int host_max_spit_interface,
public unowned Spit.Module? spit_entry_point(int host_min_spit_interface, int host_max_spit_interface,
out int module_spit_interface) {
module_spit_interface = Spit.negotiate_interfaces(host_min_spit_interface, host_max_spit_interface,
Spit.CURRENT_INTERFACE);
......@@ -68,10 +68,22 @@ public abstract class ShotwellTransitionDescriptor : Object, Spit.Pluggable, Spi
Spit.Transitions.CURRENT_INTERFACE);
}
public abstract string get_effect_id();
public abstract string get_id();
public abstract string get_effect_name();
public abstract string get_pluggable_name();
public abstract Spit.Transitions.Effect create();
public void get_info(out Spit.PluggableInfo info) {
info.authors = "Maxim Kartashev, Jim Nelson";
info.copyright = _("Copyright 2010 Maxim Kartashev, Copyright 2011 Yorba Foundation");
// TODO: Include license here
info.license = null;
info.is_licensed_wordwrapped = false;
info.translators = _("translator-credits");
info.version = _VERSION;
info.website_name = _("Visit the Yorba web site");
info.website_url = "http://www.yorba.org";
}
public abstract Spit.Transitions.Effect create(Spit.HostInterface host);
}
......@@ -12,7 +12,7 @@
extern const string _VERSION;
private class Spitter : Object, Spit.Wad {
private class Spitter : Object, Spit.Module {
~Spitter() {
debug("DTOR: Spitter");
}
......@@ -25,7 +25,7 @@ private class Spitter : Object, Spit.Wad {
return _VERSION;
}
public string get_wad_name() {
public string get_id() {
return "org.yorba.shotwell.spitter";
}
......@@ -38,7 +38,7 @@ private Spitter? spitter = null;
private Spit.EntryPoint? compiler_entry_point = null;
// This entry point is required for all SPIT modules.
public unowned Spit.Wad? spit_entry_point(int host_min_spit_interface, int host_max_spit_interface,
public unowned Spit.Module? spit_entry_point(int host_min_spit_interface, int host_max_spit_interface,
out int module_spit_interface) {
// this is purely for compilation, to verify that the entry point matches SpitEntryPoint's sig;
// it does nothing functionally
......
......@@ -21,6 +21,10 @@ class AppDirs {
public static void terminate() {
}
public static File get_home_dir() {
return File.new_for_path(Environment.get_home_dir());
}
// This can only be called once, and it better be called at startup
public static void set_data_dir(string user_data_dir) requires (!is_string_empty(user_data_dir)) {
assert(data_dir == null);
......@@ -28,7 +32,7 @@ class AppDirs {
// fix up to absolute path
string path = strip_pretty_path(user_data_dir);
if (!Path.is_absolute(path))
data_dir = File.new_for_path(Environment.get_home_dir()).get_child(path);
data_dir = get_home_dir().get_child(path);
else
data_dir = File.new_for_path(path);
......@@ -52,9 +56,7 @@ class AppDirs {
}
public static File get_data_dir() {
return (data_dir == null)
? File.new_for_path(Environment.get_home_dir()).get_child(DEFAULT_DATA_DIR)
: data_dir;
return (data_dir == null) ? get_home_dir().get_child(DEFAULT_DATA_DIR) : data_dir;
}
// The "import directory" is the same as the library directory, and are often used
......@@ -67,7 +69,7 @@ class AppDirs {
// if non-empty and relative, make it relative to the user's home directory
if (!Path.is_absolute(path))
return File.new_for_path(Environment.get_home_dir()).get_child(path);
return get_home_dir().get_child(path);
// non-empty and absolute, it's golden
return File.new_for_path(path);
......@@ -79,7 +81,7 @@ class AppDirs {
return File.new_for_path(path);
// If XDG yarfed, use ~/Pictures
return File.new_for_path(Environment.get_home_dir()).get_child(_("Pictures"));
return get_home_dir().get_child(_("Pictures"));
}
// Library folder + photo folder, based on user's prefered directory pattern.
......@@ -160,7 +162,7 @@ class AppDirs {
}
public static File get_user_plugins_dir() {
return get_data_subdir("plugins");
return get_home_dir().get_child(".gnome2").get_child("shotwell").get_child("plugins");
}
public static File? get_log_file() {
......
......@@ -5,11 +5,12 @@
*/
public class Config {
public const string PATH_SHOTWELL = "/apps/shotwell/preferences";
public const string PATH_SHOTWELL = "/apps/shotwell";
public const string PATH_SHOTWELL_PREFS = PATH_SHOTWELL + "/preferences";
public const string BOOL_COMMIT_METADATA_TO_MASTERS = PATH_SHOTWELL + "/files/commit_metadata";
public const string BOOL_AUTO_IMPORT_FROM_LIBRARY = PATH_SHOTWELL + "/files/auto_import";
public const string STRING_IMPORT_DIRECTORY = PATH_SHOTWELL + "/files/import_dir";
public const string BOOL_COMMIT_METADATA_TO_MASTERS = PATH_SHOTWELL_PREFS + "/files/commit_metadata";
public const string BOOL_AUTO_IMPORT_FROM_LIBRARY = PATH_SHOTWELL_PREFS + "/files/auto_import";
public const string STRING_IMPORT_DIRECTORY = PATH_SHOTWELL_PREFS + "/files/import_dir";
public const double SLIDESHOW_DELAY_MAX = 30.0;
public const double SLIDESHOW_DELAY_MIN = 1.0;
......@@ -267,7 +268,47 @@ public class Config {
public int get_picasa_default_size() {
return get_int("/apps/shotwell/sharing/picasa/default_size", 3) - 1;
}
private string make_plugin_path(string domain, string id, string key) {
return "%s/%s/%s/%s".printf(PATH_SHOTWELL, domain, id, key);
}
public bool get_plugin_bool(string domain, string id, string key, bool def) {
return get_bool(make_plugin_path(domain, id, key), def);
}
public void set_plugin_bool(string domain, string id, string key, bool val) {
set_bool(make_plugin_path(domain, id, key), val);
}
public int get_plugin_int(string domain, string id, string key, int def) {
return get_int(make_plugin_path(domain, id, key), def);
}
public void set_plugin_int(string domain, string id, string key, int val) {
set_int(make_plugin_path(domain, id, key), val);
}
public string? get_plugin_string(string domain, string id, string key, string? def) {
return get_string(make_plugin_path(domain, id, key), def);
}
public void set_plugin_string(string domain, string id, string key, string? val) {
set_string(make_plugin_path(domain, id, key), val);
}
public double get_plugin_double(string domain, string id, string key, double def) {
return get_double(make_plugin_path(domain, id, key), def);
}
public void set_plugin_double(string domain, string id, string key, double val) {
set_double(make_plugin_path(domain, id, key), val);
}
public void unset_plugin_key(string domain, string id, string key) {
unset(make_plugin_path(domain, id, key));
}
public string? get_publishing_string(string domain, string key, string? default_value = null) {
return get_string("/apps/shotwell/sharing/%s/%s".printf(domain, key), default_value);
}
......@@ -713,30 +754,30 @@ public class Config {
}
public string? get_directory_pattern() {
return (get_string(PATH_SHOTWELL + "/files/directory_pattern", null));
return (get_string(PATH_SHOTWELL_PREFS + "/files/directory_pattern", null));
}
public bool set_directory_pattern(string s) {
return set_string(PATH_SHOTWELL + "/files/directory_pattern", s);
return set_string(PATH_SHOTWELL_PREFS + "/files/directory_pattern", s);
}
public bool unset_directory_pattern() {
return unset(PATH_SHOTWELL + "/files/directory_pattern");
return unset(PATH_SHOTWELL_PREFS + "/files/directory_pattern");
}
public string get_directory_pattern_custom() {
return (get_string(PATH_SHOTWELL + "/files/directory_pattern_custom", ""));
return (get_string(PATH_SHOTWELL_PREFS + "/files/directory_pattern_custom", ""));
}
public bool set_directory_pattern_custom(string s) {
return set_string(PATH_SHOTWELL + "/files/directory_pattern_custom", s);
return set_string(PATH_SHOTWELL_PREFS + "/files/directory_pattern_custom", s);
}
public bool get_use_lowercase_filenames() {
return get_bool(PATH_SHOTWELL + "/files/user_lowercase_filenames", false);
return get_bool(PATH_SHOTWELL_PREFS + "/files/user_lowercase_filenames", false);
}
public void set_use_lowercase_filenames(bool b) {
set_bool(PATH_SHOTWELL + "/files/user_lowercase_filenames", b);
set_bool(PATH_SHOTWELL_PREFS + "/files/user_lowercase_filenames", b);
}
}
......@@ -16,21 +16,23 @@ private const int MIN_SPIT_INTERFACE = 0;
private const int MAX_SPIT_INTERFACE = 0;
private class SpitModule {
public File file;
public Module? module;
public unowned Spit.Wad? spitwad = null;
public unowned Spit.Module? spitmodule = null;
public int spit_interface = Spit.UNSUPPORTED_INTERFACE;
public string? wad_name = null;
public string? id = null;
private SpitModule() {
private SpitModule(File file) {
this.file = file;
module = Module.open(file.get_path(), ModuleFlags.BIND_LAZY);
}
// Have to use this funky static factory because GModule is a compact class and has no copy
// constructor. The handle must be kept open for the lifetime of the application (or until
// the module is ready to be discarded), as dropping the reference will unload the binary.
public static SpitModule? open(File file) {
SpitModule spit_module = new SpitModule();
spit_module.module = Module.open(file.get_path(), ModuleFlags.BIND_LAZY);
SpitModule spit_module = new SpitModule(file);
return (spit_module.module != null) ? spit_module : null;
}
......@@ -40,18 +42,20 @@ private File[] search_dirs;
private Gee.HashMap<string, SpitModule> module_table;
public void init() throws Error {
if (!Module.supported()) {
warning("Plugins not support: GModule not supported on this platform.");
return;
}
search_dirs = new File[0];
search_dirs += AppDirs.get_user_plugins_dir();
search_dirs += AppDirs.get_system_plugins_dir();
module_table = new Gee.HashMap<string, SpitModule>();
// do this after constructing member variables so accessors don't blow up if GModule isn't
// supported
if (!Module.supported()) {
warning("Plugins not support: GModule not supported on this platform.");
return;
}
foreach (File dir in search_dirs) {
try {
search_for_plugins(dir);
......@@ -66,10 +70,30 @@ public void terminate() {
module_table = null;
}
private SpitModule? get_module_for_pluggable(Spit.Pluggable needle) {
foreach (SpitModule module in module_table.values) {
Spit.Pluggable[]? pluggables = module.spitmodule.get_pluggables();
if (pluggables != null) {
foreach (Spit.Pluggable pluggable in pluggables) {
if (pluggable == needle)
return module;
}
}
}
return null;
}
public string? get_pluggable_module_id(Spit.Pluggable needle) {
SpitModule? module = get_module_for_pluggable(needle);
return (module != null) ? module.spitmodule.get_id() : null;
}
public Gee.Collection<Spit.Pluggable> get_pluggables_for_type(Type type) {
Gee.Collection<Spit.Pluggable> for_type = new Gee.HashSet<Spit.Pluggable>();
foreach (SpitModule module in module_table.values) {
Spit.Pluggable[]? pluggables = module.spitwad.get_pluggables();
Spit.Pluggable[]? pluggables = module.spitmodule.get_pluggables();
if (pluggables != null) {
foreach (Spit.Pluggable pluggable in pluggables) {
if (pluggable.get_type().is_a(type))
......@@ -81,6 +105,12 @@ public Gee.Collection<Spit.Pluggable> get_pluggables_for_type(Type type) {
return for_type;
}
public File get_pluggable_module_file(Spit.Pluggable pluggable) {
SpitModule? module = get_module_for_pluggable(pluggable);
return (module != null) ? module.file : null;
}
private bool is_shared_library(File file) {
string name, ext;
disassemble_filename(file.get_basename(), out name, out ext);
......@@ -152,7 +182,8 @@ private void load_module(File file) {
assert(MIN_SPIT_INTERFACE <= Spit.CURRENT_INTERFACE && Spit.CURRENT_INTERFACE <= MAX_SPIT_INTERFACE);
spit_module.spit_interface = Spit.UNSUPPORTED_INTERFACE;
spit_module.spitwad = spit_entry_point(MIN_SPIT_INTERFACE, MAX_SPIT_INTERFACE, out spit_module.spit_interface);
spit_module.spitmodule = spit_entry_point(MIN_SPIT_INTERFACE, MAX_SPIT_INTERFACE,
out spit_module.spit_interface);
if (spit_module.spit_interface == Spit.UNSUPPORTED_INTERFACE) {
critical("Unable to load module %s: module reports no support for SPIT interfaces %d to %d",
file.get_path(), MIN_SPIT_INTERFACE, MAX_SPIT_INTERFACE);
......@@ -168,11 +199,11 @@ private void load_module(File file) {
}
// verify type (as best as possible; still potential to segfault inside GType here)
if (!(spit_module.spitwad is Spit.Wad))
spit_module.spitwad = null;
if (!(spit_module.spitmodule is Spit.Module))
spit_module.spitmodule = null;
if (spit_module.spitwad == null) {
critical("Unable to load module %s (SPIT %d): no spitwad returned", file.get_path(),
if (spit_module.spitmodule == null) {
critical("Unable to load module %s (SPIT %d): no spit module returned", file.get_path(),
spit_module.spit_interface);
return;
......@@ -180,27 +211,26 @@ private void load_module(File file) {
// if module has already been loaded, drop this one (search path is set up to load user-installed
// binaries prior to system binaries)
spit_module.wad_name = prepare_input_text(spit_module.spitwad.get_wad_name(),
PrepareInputTextOptions.DEFAULT);
if (spit_module.wad_name == null) {
critical("Unable to load module %s (SPIT %d): invalid or empty wad name",
spit_module.id = prepare_input_text(spit_module.spitmodule.get_id(), PrepareInputTextOptions.DEFAULT);
if (spit_module.id == null) {
critical("Unable to load module %s (SPIT %d): invalid or empty module name",
file.get_path(), spit_module.spit_interface);
return;
}
if (module_table.has_key(spit_module.wad_name)) {
critical("Not loading module %s (SPIT %d): wad with name \"%s\" already loaded",
file.get_path(), spit_module.spit_interface, spit_module.wad_name);
if (module_table.has_key(spit_module.id)) {
critical("Not loading module %s (SPIT %d): module with name \"%s\" already loaded",
file.get_path(), spit_module.spit_interface, spit_module.id);
return;
}
debug("Loaded SPIT module \"%s %s\" (%s) [%s]", spit_module.spitwad.get_name(),
spit_module.spitwad.get_version(), spit_module.wad_name, file.get_path());
debug("Loaded SPIT module \"%s %s\" (%s) [%s]", spit_module.spitmodule.get_name(),
spit_module.spitmodule.get_version(), spit_module.id, file.get_path());
// stash in module table
module_table.set(spit_module.wad_name, spit_module);
module_table.set(spit_module.id, spit_module);
}
}
......
......@@ -85,7 +85,7 @@ public interface PublishingInteractor : GLib.Object {
public abstract void install_welcome_pane(string welcome_message,
LoginCallback on_login_clicked);
public abstract void set_service_locked(bool locked);
public abstract void set_button_mode(ButtonMode mode);
......@@ -132,10 +132,6 @@ public interface Publishable : GLib.Object {
}
public interface PublishingService : Object, Spit.Pluggable {
public abstract string get_service_id();
public abstract string get_service_name();
public abstract Spit.Publishing.Publisher create_publisher();
}
......
......@@ -20,21 +20,19 @@ public const int CURRENT_INTERFACE = 0;
// Note that this only works if the caller operates on only one interface version (and cannot mutate
// between multiple ones).
public int negotiate_interfaces(int min_host_interface, int max_host_interface, int plugin_interface) {
if (min_host_interface > plugin_interface || max_host_interface < plugin_interface)
return UNSUPPORTED_INTERFACE;
else
return plugin_interface;
return (min_host_interface > plugin_interface || max_host_interface < plugin_interface)
? UNSUPPORTED_INTERFACE : plugin_interface;
}
//
// SPIT API entry point. Host application passes in the minimum and maximum version of the SPIT
// inteface it supports (values are inclusive). The module returns the version it wishes to
// use and a pointer to a SpitWad (which will remain ref'ed as long as the module is loaded in
// use and a pointer to a Spit.Module (which will remain ref'ed as long as the module is loaded in
// memory). The module should return UNSUPPORTED_SPIT_VERSION is the min/max are out of its
// range and null for its SpitWad.
// range and null for its Spit.Module.
//
[CCode (has_target = false)]
public delegate unowned Wad? EntryPoint(int host_min_spit_interface, int host_max_spit_interface,
public delegate unowned Module? EntryPoint(int host_min_spit_interface, int host_max_spit_interface,
out int module_spit_interface);
//
......@@ -43,12 +41,12 @@ public delegate unowned Wad? EntryPoint(int host_min_spit_interface, int host_ma
public const string ENTRY_POINT_NAME = "spit_entry_point";
//
// A Wad represents an entire module (i.e. a .so/.la) which contains zero or more Shotwell
// A Module represents an entire module (i.e. a .so/.la) which contains zero or more Shotwell
// plug-ins. Once the module has been loaded into process space and this object has been
// loaded and held by Shotwell, all calls to the module and plug-ins are resolved through this
// interface.
//
public interface Wad : Object {
public interface Module : Object {
//
// Returns a (potentially) user-visible string describing the module (i.e. the .so/.la file).
//
......@@ -68,7 +66,7 @@ public interface Wad : Object {
// Best practice: use a reverse-DNS-order scheme, a la Java's packages
// (i.e. "org.yorba.shotwell.frotz").
//
public abstract string get_wad_name();
public abstract string get_id();
//
// Returns an array of Pluggables that represent each plug-in available in the module.
......@@ -77,15 +75,74 @@ public interface Wad : Object {
public abstract Pluggable[]? get_pluggables();
}
public struct PluggableInfo {
public string? version;
public string? brief_description;
public string? authors;
public string? copyright;
public string? license;
public bool is_licensed_wordwrapped;
public string? website_url;
public string? website_name;
public string? translators;
public Gdk.Pixbuf? icon;
}
//
// Each plug-in in a module needs to implement this interface at a minimum. Specific plug-in
// points may have (and probably will have) specific interface requirements as well.
//
public interface Pluggable : Object {
//
// Like the Spit entry point, this mechanism allows for the host to negotiate with the Pluggable
// for its interface version. If the pluggable does not support an interface between the
// two ranges (inclusive), it should return UNSUPPORTED_INTERFACE.
//
public abstract int get_pluggable_interface(int min_host_interface, int max_host_interface);
//
// Returns a unique identifier for this Pluggable. Like Spit.Module.get_id(), best practice is
// to use a reverse-DNS-order scheme to avoid conflicts.
//
public abstract string get_id();
//
// Returns a user-visible name for the Pluggable.
//
public abstract string get_pluggable_name();
//
// Returns extra information about the Pluggable that is used to identify it to the user.
//
public abstract void get_info(out PluggableInfo info);
}
//
// Each Pluggable is offered a Host interface for needs common to most plug-ins. Note that the
// Host is not explicitly handed to the Pluggable through that interface, but is expected to be
// offer to the Pluggable through the interface applicable to the extension point. This also allows
// the extension point to extend Host to offer other services applicable to the type of plug-in.
//
public interface HostInterface : Object {
public abstract File get_module_file();
public abstract bool get_config_bool(string key, bool def);
public abstract void set_config_bool(string key, bool val);
public abstract int get_config_int(string key, int def);
public abstract void set_config_int(string key, int val);
public abstract string? get_config_string(string key, string? def);
public abstract void set_config_string(string key, string? val);
public abstract double get_config_double(string key, double def);
public abstract void set_config_double(string key, double val);
public abstract void unset_config_key(string key);
}
}
......
/* Copyright 2011 Yorba Foundation
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
namespace Plugins {
public class StandardHostInterface : Object, Spit.HostInterface {
private string config_domain;
private string config_id;
private File module_file;
private Spit.PluggableInfo info;
public StandardHostInterface(Spit.Pluggable pluggable, string config_domain) {
this.config_domain = config_domain;
config_id = parse_key(pluggable.get_id());
module_file = get_pluggable_module_file(pluggable);
pluggable.get_info(out info);
}
private static string parse_key(string id) {
// special case: legacy plugins (Web publishers moved into SPIT) have special names
// new plugins will use their full ID
string subkey;
switch (id) {
case "org.yorba.shotwell.publishing.facebook":
subkey = "facebook";
break;
default:
subkey = id;
break;
}
// convert illegal key characters
subkey = subkey.replace("/", "_");
return subkey;
}
public File get_module_file() {