Also removing the erroneous space that had crept in at the end of the line in most of our header comments.
189 lines
5.6 KiB
Vala
189 lines
5.6 KiB
Vala
/* Copyright 2011-2013 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.
|
|
*/
|
|
|
|
/**
|
|
* YorbaApplication is a poor man's lookalike of GNOME 3's GApplication, with a couple of additions.
|
|
* The idea is to ease a future migration to GTK 3 once some outstanding problems with Gtk.Application
|
|
* are resolved.
|
|
*
|
|
* For more information about why we built this class intead of using Gtk.Application, see the
|
|
* following ticket:
|
|
* http://redmine.yorba.org/issues/4266
|
|
*/
|
|
|
|
extern const string GETTEXT_PACKAGE;
|
|
|
|
public abstract class YorbaApplication {
|
|
|
|
public bool registered { get; private set; }
|
|
public string[]? args { get; private set; }
|
|
|
|
private string app_id;
|
|
private bool running = false;
|
|
private bool exiting_fired = false;
|
|
private int exitcode = 0;
|
|
private Unique.App? unique_app = null;
|
|
|
|
/**
|
|
* This signal is fired only when the application is starting the first time, not on
|
|
* subsequent activations (i.e. the application is launched while running by the user).
|
|
*
|
|
* The args[] array will be available when this signal is fired.
|
|
*/
|
|
public virtual signal int startup() {
|
|
unowned string[] a = args;
|
|
Gtk.init(ref a);
|
|
|
|
// Sanitize the args. Gtk's init function will leave null elements
|
|
// in the array, which then causes OptionContext to crash.
|
|
// See ticket: https://bugzilla.gnome.org/show_bug.cgi?id=674837
|
|
string[] fixed_args = new string[0];
|
|
for (int i = 0; i < args.length; i++) {
|
|
if (args[i] != null)
|
|
fixed_args += args[i];
|
|
}
|
|
args = fixed_args;
|
|
|
|
return 0;
|
|
}
|
|
|
|
public virtual signal void activate(string[] args) {
|
|
}
|
|
|
|
/**
|
|
* Signal that is activated when 'exit' is called, but before the application actually exits.
|
|
*
|
|
* To cancel an exit, a callback should return YorbaApplication.cancel_exit(). To procede with
|
|
* an exit, a callback should return true.
|
|
*/
|
|
public virtual signal bool exiting(bool panicked) {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* application_title is a localized name of the application. program_name is non-localized
|
|
* and used by the system. app_id is a CORBA-esque program identifier.
|
|
*
|
|
* Only one YorbaApplication instance may be created in an program.
|
|
*/
|
|
public YorbaApplication(string application_title, string program_name, string app_id) {
|
|
this.app_id = app_id;
|
|
|
|
Environment.set_application_name(application_title);
|
|
Environment.set_prgname(program_name);
|
|
}
|
|
|
|
public bool register(Cancellable? cancellable = null) throws Error {
|
|
if (registered)
|
|
return false;
|
|
|
|
unique_app = new Unique.App(app_id, null);
|
|
unique_app.message_received.connect(on_unique_app_message);
|
|
|
|
// If app already running, activate it and exit
|
|
if (unique_app.is_running()) {
|
|
Unique.MessageData data = new Unique.MessageData();
|
|
string argstr = string.joinv(", ", args);
|
|
data.set_text(argstr, argstr.length);
|
|
unique_app.send_message((int) Unique.Command.ACTIVATE, data);
|
|
|
|
return false;
|
|
}
|
|
|
|
registered = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
private Unique.Response on_unique_app_message(Unique.App app, int command,
|
|
Unique.MessageData data, uint timestamp) {
|
|
switch (command) {
|
|
case Unique.Command.ACTIVATE:
|
|
activate(data.get_text().split(", "));
|
|
break;
|
|
|
|
default:
|
|
return Unique.Response.PASSTHROUGH;
|
|
}
|
|
|
|
return Unique.Response.OK;
|
|
}
|
|
|
|
public void add_window(Gtk.Window window) {
|
|
unique_app.watch_window(window);
|
|
}
|
|
|
|
public int run(string[] args) {
|
|
if (running)
|
|
error("run() called twice.");
|
|
|
|
this.args = args;
|
|
International.init(GETTEXT_PACKAGE, args[0]);
|
|
|
|
running = true;
|
|
exitcode = startup();
|
|
if (exitcode != 0)
|
|
return exitcode;
|
|
|
|
try {
|
|
if (!register()) {
|
|
return exitcode;
|
|
}
|
|
} catch (Error e) {
|
|
error("Unable to register application: %s", e.message);
|
|
}
|
|
|
|
activate(args);
|
|
|
|
// enter the main loop
|
|
if (exitcode == 0)
|
|
Gtk.main();
|
|
|
|
return exitcode;
|
|
}
|
|
|
|
// This call will fire "exiting" only if it's not already been fired.
|
|
public void exit(int exitcode = 0) {
|
|
if (exiting_fired || !running)
|
|
return;
|
|
|
|
this.exitcode = exitcode;
|
|
|
|
exiting_fired = true;
|
|
if (!exiting(false)) {
|
|
exiting_fired = false;
|
|
this.exitcode = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
if (Gtk.main_level() > 0)
|
|
Gtk.main_quit();
|
|
else
|
|
Posix.exit(exitcode);
|
|
}
|
|
|
|
/**
|
|
* A callback for GearyApplication.exiting should return cancel_exit() to prevent the
|
|
* application from exiting.
|
|
*/
|
|
public bool cancel_exit() {
|
|
Signal.stop_emission_by_name(this, "exiting");
|
|
return false;
|
|
}
|
|
|
|
// This call will fire "exiting" only if it's not already been fired and halt the application
|
|
// in its tracks.
|
|
public void panic() {
|
|
if (!exiting_fired) {
|
|
exiting_fired = true;
|
|
exiting(true);
|
|
}
|
|
|
|
Posix.exit(1);
|
|
}
|
|
}
|
|
|