Commit 0b1cd2bc authored by Lucas Beeler's avatar Lucas Beeler

Ports the Facebook connector to the new publishing API. Includes refinements...

Ports the Facebook connector to the new publishing API. Includes refinements to the publishing API to support the ported Facebook connector as well as the soon-to-be-ported Picasa connector.
parent c708b1b6
......@@ -251,74 +251,52 @@ public class Config {
return get_string("/apps/shotwell/sharing/last_used_service");
}
public bool clear_facebook_session_key() {
return set_facebook_session_key("");
}
public bool set_facebook_session_key(string key) {
return set_string("/apps/shotwell/sharing/facebook/session_key", key);
}
public string? get_facebook_session_key() {
return get_string("/apps/shotwell/sharing/facebook/session_key");
}
public bool clear_facebook_session_secret() {
return set_facebook_session_secret("");
}
public bool set_facebook_session_secret(string secret) {
return set_string("/apps/shotwell/sharing/facebook/session_secret", secret);
}
public string? get_facebook_session_secret() {
return get_string("/apps/shotwell/sharing/facebook/session_secret");
public bool set_picasa_default_size(int default_size) {
return set_int("/apps/shotwell/sharing/picasa/default_size", default_size + 1);
}
public bool clear_facebook_uid() {
return set_facebook_uid("");
public int get_picasa_default_size() {
return get_int("/apps/shotwell/sharing/picasa/default_size", 3) - 1;
}
public bool set_facebook_uid(string uid) {
return set_string("/apps/shotwell/sharing/facebook/uid", uid);
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);
}
public string? get_facebook_uid() {
return get_string("/apps/shotwell/sharing/facebook/uid");
public void set_publishing_string(string domain, string key, string value) {
set_string("/apps/shotwell/sharing/%s/%s".printf(domain, key), value);
}
public bool clear_facebook_user_name() {
return set_facebook_user_name("");
public int get_publishing_int(string domain, string key, int default_value = -1) {
return get_int("/apps/shotwell/sharing/%s/%s".printf(domain, key), default_value);
}
public bool set_facebook_user_name(string user_name) {
return set_string("/apps/shotwell/sharing/facebook/user_name", user_name);
public void set_publishing_int(string domain, string key, int value) {
set_int("/apps/shotwell/sharing/%s/%s".printf(domain, key), value);
}
public string? get_facebook_user_name() {
return get_string("/apps/shotwell/sharing/facebook/user_name");
public bool get_publishing_bool(string domain, string key, bool default_value = false) {
return get_bool("/apps/shotwell/sharing/%s/%s".printf(domain, key), default_value);
}
public bool set_picasa_default_size(int default_size) {
return set_int("/apps/shotwell/sharing/picasa/default_size", default_size + 1);
public void set_publishing_bool(string domain, string key, bool value) {
set_bool("/apps/shotwell/sharing/%s/%s".printf(domain, key), value);
}
public int get_picasa_default_size() {
return get_int("/apps/shotwell/sharing/picasa/default_size", 3) - 1;
public double get_publishing_double(string domain, string key, double default_value = 0.0) {
return get_double("/apps/shotwell/sharing/%s/%s".printf(domain, key), default_value);
}
public string? get_publishing_string(string id, string key) {
return get_string(_("/apps/shotwell/sharing/%s/%s").printf(id, key));
public void set_publishing_double(string domain, string key, double value) {
set_double("/apps/shotwell/sharing/%s/%s".printf(domain, key), value);
}
public void set_publishing_string(string id, string key, string value) {
set_string(_("/apps/shotwell/sharing/%s/%s").printf(id, key), value);
}
public void unset_publishing_string(string id, string key) {
public void unset_publishing_string(string domain, string key) {
string path = "/apps/shotwell/sharing/%s/%s".printf(domain, key);
try {
client.recursive_unset(_("/apps/shotwell/sharing/%s/%s").printf(id, key), GConf.UnsetFlags.NAMES);
client.recursive_unset(path, GConf.UnsetFlags.NAMES);
} catch (GLib.Error err) {
warning("Unable to unset GConf value at %s: %s", path, err.message);
}
}
......
This diff is collapsed.
......@@ -48,8 +48,8 @@ public class Capabilities : ServiceCapabilities {
return SERVICE_NAME;
}
public override ServiceCapabilities.MediaType get_supported_media() {
return MediaType.PHOTO | MediaType.VIDEO;
public override Spit.Publishing.Publisher.MediaType get_supported_media() {
return Spit.Publishing.Publisher.MediaType.PHOTO | Spit.Publishing.Publisher.MediaType.VIDEO;
}
public override ServiceInteractor factory(PublishingDialog host) {
......@@ -872,7 +872,7 @@ private class PublishingOptionsPane : PublishingDialogPane {
public signal void publish();
public signal void logout();
public PublishingOptionsPane(PublishingParameters parameters, MediaType media_type) {
public PublishingOptionsPane(PublishingParameters parameters, Spit.Publishing.Publisher.MediaType media_type) {
this.parameters = parameters;
visibilities = create_visibilities();
......@@ -908,9 +908,9 @@ private class PublishingOptionsPane : PublishingDialogPane {
Gtk.Table combos_layouter = new Gtk.Table(2, 2, false);
combos_layouter.set_row_spacing(0, 12);
string visibility_label_text = _("Photos _visible to:");
if ((media_type == MediaType.VIDEO)) {
if ((media_type == Spit.Publishing.Publisher.MediaType.VIDEO)) {
visibility_label_text = _("Videos _visible to:");
} else if ((media_type == MediaType.ALL)) {
} else if ((media_type == (Spit.Publishing.Publisher.MediaType.PHOTO | Spit.Publishing.Publisher.MediaType.VIDEO))) {
visibility_label_text = _("Photos and videos _visible to:");
}
Gtk.Label visibility_label = new Gtk.Label.with_mnemonic(visibility_label_text);
......@@ -932,7 +932,7 @@ private class PublishingOptionsPane : PublishingDialogPane {
combos_layouter.attach_defaults(vis_label_aligner, 0, 1, 0, 1);
combos_layouter.attach_defaults(visibility_combo_aligner, 1, 2, 0, 1);
if ((media_type & MediaType.PHOTO) != 0) {
if ((media_type & Spit.Publishing.Publisher.MediaType.PHOTO) != 0) {
combos_layouter.attach_defaults(size_label_aligner, 0, 1, 1, 2);
combos_layouter.attach_defaults(size_combo_aligner, 1, 2, 1, 2);
}
......
......@@ -86,8 +86,8 @@ public class Capabilities : ServiceCapabilities {
return SERVICE_NAME;
}
public override ServiceCapabilities.MediaType get_supported_media() {
return MediaType.PHOTO | MediaType.VIDEO;
public override Spit.Publishing.Publisher.MediaType get_supported_media() {
return Spit.Publishing.Publisher.MediaType.PHOTO | Spit.Publishing.Publisher.MediaType.VIDEO;
}
public override ServiceInteractor factory(PublishingDialog host) {
......
......@@ -70,8 +70,8 @@ public class Capabilities : ServiceCapabilities {
return SERVICE_NAME;
}
public override ServiceCapabilities.MediaType get_supported_media() {
return MediaType.PHOTO;
public override Spit.Publishing.Publisher.MediaType get_supported_media() {
return Spit.Publishing.Publisher.MediaType.PHOTO;
}
public override ServiceInteractor factory(PublishingDialog host) {
......@@ -517,7 +517,7 @@ public class Interactor : ServiceInteractor {
get_host().unlock_service();
get_host().set_close_button_mode();
get_host().install_pane(new SuccessPane(MediaType.PHOTO));
get_host().install_pane(new SuccessPane(Spit.Publishing.Publisher.MediaType.PHOTO));
}
internal new PublishingDialog get_host() {
......
......@@ -19,13 +19,6 @@ public errordomain PublishingError {
LOCAL_FILE_ERROR
}
public enum MediaType {
NONE = 0,
PHOTO = 1 << 0,
VIDEO = 1 << 1,
ALL = PHOTO | VIDEO
}
public enum HttpMethod {
GET,
POST,
......@@ -657,11 +650,11 @@ public class ProgressPane : PublishingDialogPane {
}
public class SuccessPane : StaticMessagePane {
public SuccessPane(MediaType published_media) {
public SuccessPane(Spit.Publishing.Publisher.MediaType published_media) {
string? message_string = null;
if (published_media == MediaType.ALL)
if (published_media == (Spit.Publishing.Publisher.MediaType.PHOTO | Spit.Publishing.Publisher.MediaType.VIDEO))
message_string = _("The selected photos/videos were successfully published.");
else if (published_media == MediaType.VIDEO)
else if (published_media == Spit.Publishing.Publisher.MediaType.VIDEO)
message_string = _("The selected videos were successfully published.");
else
message_string = _("The selected photos were successfully published.");
......@@ -701,32 +694,32 @@ public class PublishingDialog : Gtk.Dialog {
private Photo[] photos = new Photo[0];
private Video[] videos = new Video[0];
private MediaSource[] media_sources = new MediaSource[0];
private PublishingDialogPane active_pane;
private Gtk.Widget active_pane = null;
private ServiceInteractor interactor;
private MediaType media_type = MediaType.NONE;
private Spit.Publishing.Publisher.MediaType media_type = Spit.Publishing.Publisher.MediaType.NONE;
private PublishingDialog(Gee.Collection<MediaSource> to_publish) {
protected PublishingDialog(Gee.Collection<MediaSource> to_publish) {
resizable = false;
delete_event.connect(on_window_close);
foreach (MediaSource media in to_publish) {
if (media is Photo) {
photos += (Photo) media;
media_type |= MediaType.PHOTO;
media_type |= Spit.Publishing.Publisher.MediaType.PHOTO;
} else {
assert(media is Video);
videos += (Video) media;
media_type |= MediaType.VIDEO;
media_type |= Spit.Publishing.Publisher.MediaType.VIDEO;
}
media_sources += media;
}
string title = _("Publish Photos");
string label = _("Publish photos _to:");
if (media_type == MediaType.VIDEO) {
if (media_type == Spit.Publishing.Publisher.MediaType.VIDEO) {
title = _("Publish Videos");
label = _("Publish videos _to");
} else if (media_type == MediaType.ALL) {
} else if (media_type == (Spit.Publishing.Publisher.MediaType.PHOTO | Spit.Publishing.Publisher.MediaType.VIDEO)) {
title = _("Publish Photos and Videos");
label = _("Publish photos and videos _to");
}
......@@ -790,7 +783,9 @@ public class PublishingDialog : Gtk.Dialog {
if (active_instance != null)
return;
active_instance = new PublishingDialog(to_publish);
debug("PublishingDialog.go( )");
active_instance = new Publishing.Glue.DialogInteractorWrapper(to_publish);
// determine which service to use
......@@ -869,6 +864,7 @@ public class PublishingDialog : Gtk.Dialog {
}
private void on_close_cancel_clicked() {
debug("PublishingDialog: on_close_cancel_clicked( ): invoked.");
destroy_instance();
}
......@@ -879,6 +875,17 @@ public class PublishingDialog : Gtk.Dialog {
}
private void on_service_changed() {
string? existing_service_name = (interactor != null) ? interactor.get_name() : null;
// if the current service is already running, do nothing
if ((existing_service_name != null) && (existing_service_name == service_selector_box.get_active_text()))
return;
// if no interactor is installed, do nothing
if (interactor == null)
return;
debug("existing service = '%s'; selected service = '%s'.", existing_service_name, service_selector_box.get_active_text());
Config config = Config.get_instance();
config.set_last_used_service(service_selector_box.get_active_text());
interactor = ServiceFactory.get_instance().create_interactor(this,
......@@ -890,21 +897,26 @@ public class PublishingDialog : Gtk.Dialog {
return active_instance;
}
public void install_pane(PublishingDialogPane pane) {
public void install_pane(Gtk.Widget pane) {
debug("PublishingDialog: install_pane( ): invoked.");
// only proceed with pane installation if our interactor doesn't have an error situation;
// if an error is present, then continue to display the existing pane -- this should be
// the error pane that was installed when the error was posted
if (interactor.has_error())
return;
if (active_pane != null)
if (active_pane != null) {
debug("PublishingDialog: install_pane( ): a pane is already installed; removing it.");
central_area_layouter.remove(active_pane);
}
central_area_layouter.add(pane);
show_all();
active_pane = pane;
pane.installed();
if (pane is PublishingDialogPane)
((PublishingDialogPane) pane).installed();
}
public void set_large_window_mode() {
......@@ -925,6 +937,10 @@ public class PublishingDialog : Gtk.Dialog {
resizable = true;
}
public void clear_free_sizable_window_mode() {
resizable = false;
}
public void set_close_button_mode() {
close_cancel_button.set_label(_("_Close"));
set_default(close_cancel_button);
......@@ -995,7 +1011,7 @@ public class PublishingDialog : Gtk.Dialog {
return media_sources;
}
public MediaType get_media_type() {
public Spit.Publishing.Publisher.MediaType get_media_type() {
return media_type;
}
......@@ -1005,21 +1021,14 @@ public class PublishingDialog : Gtk.Dialog {
}
public abstract class ServiceCapabilities {
public enum MediaType {
NONE = 0,
PHOTO = 1 << 0,
VIDEO = 1 << 1,
ALL = 0xFFFFFFFF
}
public abstract string get_name();
public abstract MediaType get_supported_media();
public abstract Spit.Publishing.Publisher.MediaType get_supported_media();
public abstract ServiceInteractor factory(PublishingDialog host);
}
public abstract class ServiceInteractor {
public abstract class ServiceInteractor : GLib.Object {
private weak PublishingDialog host;
private bool error = false;
......@@ -1255,7 +1264,8 @@ public class ServiceFactory {
// This returns the first service that can handle any media type.
public ServiceCapabilities get_default_service() {
foreach (ServiceCapabilities caps in caps_map.values) {
if ((caps.get_supported_media() & ServiceCapabilities.MediaType.ALL) != 0)
if (((caps.get_supported_media() & Spit.Publishing.Publisher.MediaType.PHOTO) != 0) &&
((caps.get_supported_media() & Spit.Publishing.Publisher.MediaType.VIDEO) != 0))
return caps;
}
......@@ -1265,15 +1275,15 @@ public class ServiceFactory {
public string[] get_manifest(int photo_count, int video_count) {
string[] result = new string[0];
ServiceCapabilities.MediaType media_type = ServiceCapabilities.MediaType.NONE;
Spit.Publishing.Publisher.MediaType media_type = Spit.Publishing.Publisher.MediaType.NONE;
if (photo_count > 0)
media_type |= ServiceCapabilities.MediaType.PHOTO;
media_type |= Spit.Publishing.Publisher.MediaType.PHOTO;
if (video_count > 0)
media_type |= ServiceCapabilities.MediaType.VIDEO;
media_type |= Spit.Publishing.Publisher.MediaType.VIDEO;
if (media_type == ServiceCapabilities.MediaType.NONE)
if (media_type == Spit.Publishing.Publisher.MediaType.NONE)
return result;
foreach (ServiceCapabilities caps in caps_map.values) {
......@@ -1285,6 +1295,8 @@ public class ServiceFactory {
}
public ServiceInteractor create_interactor(PublishingDialog host, string? service_name) {
debug("ServiceFactory: create_interactor( ): creating interactor for service '%s'", service_name);
ServiceCapabilities caps = null;
if (service_name != null)
caps = caps_map.get(service_name);
......
......@@ -118,8 +118,8 @@ public class Capabilities : ServiceCapabilities {
return SERVICE_NAME;
}
public override ServiceCapabilities.MediaType get_supported_media() {
return MediaType.PHOTO;
public override Spit.Publishing.Publisher.MediaType get_supported_media() {
return Spit.Publishing.Publisher.MediaType.PHOTO;
}
public override ServiceInteractor factory(PublishingDialog host) {
......@@ -192,7 +192,7 @@ public class Capabilities : ServiceCapabilities {
get_host().unlock_service();
get_host().set_close_button_mode();
get_host().install_pane(new SuccessPane(MediaType.PHOTO));
get_host().install_pane(new SuccessPane(Spit.Publishing.Publisher.MediaType.PHOTO));
}
private void on_upload_error(BatchUploader uploader, PublishingError err) {
......
......@@ -35,8 +35,8 @@ public class Capabilities : ServiceCapabilities {
return SERVICE_NAME;
}
public override ServiceCapabilities.MediaType get_supported_media() {
return MediaType.VIDEO;
public override Spit.Publishing.Publisher.MediaType get_supported_media() {
return Spit.Publishing.Publisher.MediaType.VIDEO;
}
public override ServiceInteractor factory(PublishingDialog host) {
......@@ -347,7 +347,7 @@ public class Interactor : ServiceInteractor {
get_host().unlock_service();
get_host().set_close_button_mode();
get_host().install_pane(new SuccessPane(MediaType.VIDEO));
get_host().install_pane(new SuccessPane(Spit.Publishing.Publisher.MediaType.VIDEO));
}
internal Session get_session() {
......
......@@ -4,8 +4,6 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
using Shotwell.Plugins;
namespace Plugins {
// GModule doesn't have a truly generic way to determine if a file is a shared library by extension,
......
......@@ -4,11 +4,19 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
// Using Shotwell.Plugins namespace (rather than Plugins) for the benefit of external plugins
// and C code (so the interfaces will be seen as shotwell_plugins_publisher_get_name, etc.)
namespace Shotwell.Plugins {
namespace Spit.Publishing {
public interface Publisher {
public errordomain PublishingError {
NO_ANSWER,
COMMUNICATION_FAILED,
PROTOCOL_ERROR,
SERVICE_ERROR,
MALFORMED_RESPONSE,
LOCAL_FILE_ERROR,
EXPIRED_SESSION
}
public interface Publisher : GLib.Object {
public enum MediaType {
NONE = 0,
PHOTO = 1 << 0,
......@@ -23,11 +31,13 @@ public interface Publisher {
public abstract void start(PublishingInteractor interactor);
public abstract bool is_running();
/* plugins must relinquish their interactor reference when stop( ) is called */
public abstract void stop();
}
public interface PublishingDialogPane {
public interface PublishingDialogPane : GLib.Object {
public enum GeometryOptions {
NONE = 0,
EXTENDED_SIZE = 1 << 0,
......@@ -50,32 +60,37 @@ public delegate void ProgressCallback(string status_text, double completed_fract
public delegate void LoginCallback();
public interface PublishingInteractor {
public interface PublishingInteractor : GLib.Object {
public enum ButtonMode {
CLOSE,
CANCEL
}
public abstract void install_dialog_pane(PublishingDialogPane pane);
public abstract void install_dialog_pane(Spit.Publishing.PublishingDialogPane pane);
public abstract void post_error(Error err);
public abstract void stop_publishing();
public abstract void install_static_message_pane(string message);
public abstract void install_pango_message_pane(string markup);
public abstract void install_success_pane();
public abstract void install_success_pane(Spit.Publishing.Publisher.MediaType media_type);
public abstract void install_account_fetch_wait_pane();
public abstract void install_login_wait_pane();
public abstract LoginCallback install_login_pane(string welcome_message);
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);
public abstract void set_dialog_default_widget(Gtk.Widget widget);
public abstract ProgressCallback install_progress_pane();
public abstract int get_config_int(string key, int default_value);
......@@ -93,6 +108,23 @@ public interface PublishingInteractor {
public abstract void set_config_bool(string key, bool value);
public abstract void set_config_double(string key, double value);
public abstract Publishable[] get_publishables();
public abstract Spit.Publishing.Publisher.MediaType get_publishable_media_type();
}
public interface Publishable : GLib.Object {
public abstract GLib.File serialize_for_publishing(int content_major_axis,
bool strip_metadata = false) throws Spit.Publishing.PublishingError;
public abstract string get_publishing_name();
public abstract string get_publishing_description();
public abstract string[] get_publishing_keywords();
public abstract Spit.Publishing.Publisher.MediaType get_media_type();
}
}
/* 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 Publishing.Glue {
public class PublisherWrapperInteractor : ServiceInteractor, Spit.Publishing.Publisher {
private Spit.Publishing.Publisher wrapped;
private Spit.Publishing.PublishingInteractor new_api_interactor;
private weak PublishingDialog old_api_dialog;
public PublisherWrapperInteractor(Spit.Publishing.Publisher wrapped,
Spit.Publishing.PublishingInteractor new_api_interactor, PublishingDialog old_api_dialog) {
base(old_api_dialog);
this.wrapped = wrapped;
this.new_api_interactor = new_api_interactor;
this.old_api_dialog = old_api_dialog;
}
public string get_service_name() {
return wrapped.get_service_name();
}
public string get_user_visible_name() {
return wrapped.get_user_visible_name();
}
public Spit.Publishing.Publisher.MediaType get_supported_media() {
return wrapped.get_supported_media();
}
public void start(Spit.Publishing.PublishingInteractor interactor) {
if (interactor is Spit.Publishing.PublishingHost)
((Spit.Publishing.PublishingHost) interactor).set_active_publisher(wrapped);
wrapped.start(interactor);
}
public void stop() {
debug("PublisherWrapperInteractor: stop( ) invoked.");
old_api_dialog = null;
new_api_interactor.stop_publishing();
wrapped = null;
new_api_interactor = null;
}
public bool is_running() {
if (wrapped == null)
return false;
else
return wrapped.is_running();
}
public override string get_name() {
return get_user_visible_name();
}
public override void start_interaction() {
debug("PublisherWrapperInteractor: start_interaction( ): invoked.");
start(new_api_interactor);
}
public override void cancel_interaction() {
debug("PublisherWrapperInteractor: cancel_interaction( ): invoked.");
stop();
}
}
public class DialogInteractorWrapper : PublishingDialog, Spit.Publishing.PublishingInteractor {
Spit.Publishing.PublishingInteractor plugin_host;
Spit.Publishing.Publishable[] publishables;
public DialogInteractorWrapper(Gee.Collection<MediaSource> to_publish) {
base(to_publish);
publishables = new Spit.Publishing.Publishable[0];
foreach (MediaSource current_media_item in to_publish)
publishables += new MediaSourcePublishableWrapper(current_media_item);
}
public void set_plugin_host(Spit.Publishing.PublishingInteractor plugin_host) {
this.plugin_host = plugin_host;
}
public void install_dialog_pane(Spit.Publishing.PublishingDialogPane pane) {
debug("DialogInteractorWrapper: install_pane( ): invoked.");
plugin_host.install_dialog_pane(pane);
}
public void post_error(Error err) {
debug("DialogInteractorWrapper.post_error( ): err = '%s'.", err.message);
plugin_host.post_error(err);
}
public void stop_publishing() {
debug("DialogInteractorWrapper.stop_publishing( ): invoked.");
plugin_host.stop_publishing();
}
public void install_static_message_pane(string message) {
plugin_host.install_static_message_pane(message);
}
public void install_pango_message_pane(string markup) {
plugin_host.install_pango_message_pane(markup);
}
public void install_success_pane(Spit.Publishing.Publisher.MediaType media_type) {
plugin_host.install_success_pane(media_type);
}
public void install_account_fetch_wait_pane() {
plugin_host.install_account_fetch_wait_pane();
}
public void install_login_wait_pane() {
plugin_host.install_login_wait_pane();
}
public void install_welcome_pane(string welcome_message,
Spit.Publishing.LoginCallback on_login_clicked) {
plugin_host.install_welcome_pane(welcome_message, on_login_clicked);
}
public void set_service_locked(bool locked) {
plugin_host.set_service_locked(locked);
}
public void set_button_mode(Spit.Publishing.PublishingInteractor.ButtonMode mode) {
plugin_host.set_button_mode(mode);
}
public void set_dialog_default_widget(Gtk.Widget widget) {
plugin_host.set_dialog_default_widget(widget);
}
public Spit.Publishing.ProgressCallback install_progress_pane() {
return plugin_host.install_progress_pane();
}
public Spit.Publishing.Publisher.MediaType get_publishable_media_type() {
return plugin_host.get_publishable_media_type();
}
public int get_config_int(string key, int default_value) {
return plugin_host.get_config_int(key, default_value);
}
public string? get_config_string(string key, string? default_value) {
return plugin_host.get_config_string(key, default_value);
}
public bool get_config_bool(string key, bool default_value) {
return plugin_host.get_config_bool(key, default_value);
}
public double get_config_double(string key, double default_value) {
return plugin_host.get_config_double(key, default_value);
}
public void set_config_int(string key, int value) {