An error occurred while loading the file. Please try again.
Select Git revision
-
Amin Bandali authored
Change-Id: I4b43f2449d8fccb616536b2bc098aee48d1bb71a
Amin Bandali authoredChange-Id: I4b43f2449d8fccb616536b2bc098aee48d1bb71a
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
client.cpp 25.32 KiB
/*
* Copyright (C) 2015-2022 Savoir-faire Linux Inc.
* Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "client.h"
// system
#include <memory>
#include <regex>
// GTK+ related
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <clutter-gtk/clutter-gtk.h>
// Qt
#include <QtCore/QTranslator>
#include <QtCore/QCoreApplication>
#include <QtCore/QString>
#include <QtCore/QByteArray>
#include <QtCore/QItemSelectionModel>
#include <QtCore/QStandardPaths>
// LRC
#include <smartinfohub.h>
#include <globalinstances.h>
// Jami Client
#include "client_options.h"
#include "mainwindow.h"
#include "dialogs.h"
#include "native/dbuserrorhandler.h"
#include "notifier.h"
#include "config.h"
#include "utils/drawing.h"
#include "utils/files.h"
#if HAVE_AYATANAAPPINDICATOR
#include <libayatana-appindicator/app-indicator.h>
#elif HAVE_APPINDICATOR
#include <libappindicator/app-indicator.h>
#endif
struct _ClientClass
{
GtkApplicationClass parent_class;
};
struct _Client
{
GtkApplication parent;
};
typedef struct _ClientPrivate ClientPrivate;
struct _ClientPrivate {
/* args */
int argc;
char **argv;
GSettings *settings;
/* main window */
GtkWidget *win;
/* for libRingClient */
QCoreApplication *qtapp;
/* UAM */
QMetaObject::Connection uam_updated;
std::unique_ptr<QTranslator> translator_lang;
std::unique_ptr<QTranslator> translator_full;
gboolean restore_window_state;
gpointer systray_icon;
GtkWidget *icon_menu;
};
/* this union is used to pass ints as pointers and vice versa for GAction parameters*/
typedef union _int_ptr_t
{
int value;
gint64 value64;
gpointer ptr;
} int_ptr_t;
G_DEFINE_TYPE_WITH_PRIVATE(Client, client, GTK_TYPE_APPLICATION);
#define CLIENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLIENT_TYPE, ClientPrivate))
static void
exception_dialog(const char* msg)
{
g_critical("%s", msg);
GtkWidget *dialog = gtk_message_dialog_new(NULL,
(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
_("Unable to initialize.\nMake sure the Jami daemon (jamid) is running.\nError: %s"),
msg);
gtk_window_set_title(GTK_WINDOW(dialog), _("Jami Error"));
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
static void
accelerators(Client *client)
{
#if GTK_CHECK_VERSION(3,12,0)
const gchar *quit_accels[2] = {"<Ctrl>Q", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.quit", quit_accels);
const gchar *fullscreen_accels[2] = {"F11", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.toggle_fullscreen", fullscreen_accels);
const gchar *accounts_accels[2] = {"<Ctrl>J", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.display_account_list", accounts_accels);
const gchar *search_accels[2] = {"<Ctrl>F", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.search", search_accels);
const gchar *conversations_list_accels[2] = {"<Ctrl>L", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.conversations_list", conversations_list_accels);
const gchar *requests_list_accels[2] = {"<Ctrl>R", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.requests_list", requests_list_accels);
const gchar *audio_call_accels[2] = {"<Ctrl><Shift>C", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.audio_call", audio_call_accels);
const gchar *clear_history_accels[2] = {"<Ctrl><Shift>L", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.clear_history", clear_history_accels);
const gchar *remove_conversation_accels[2] = {"<Ctrl><Shift>Delete", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.remove_conversation", remove_conversation_accels);
const gchar *block_contact_accels[2] = {"<Ctrl><Shift>B", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.block_contact", block_contact_accels);
const gchar *unblock_contact_accels[2] = {"<Ctrl><Shift>U", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.unblock_contact", unblock_contact_accels);
const gchar *copy_contact_accels[2] = {"<Ctrl><Shift>J", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.copy_contact", copy_contact_accels);
const gchar *add_contact_accels[2] = {"<Ctrl><Shift>A", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.add_contact", add_contact_accels);
const gchar *accept_call_accels[2] = {"<Ctrl>Y", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.accept_call", accept_call_accels);
const gchar *decline_call_accels[2] = {"<Ctrl>D", NULL};
gtk_application_set_accels_for_action(GTK_APPLICATION(client), "app.decline_call", decline_call_accels);
#else
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control>Q", "app.quit", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "F11", "app.toggle_fullscreen", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control>J", "app.display_account_list", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control>F", "app.search", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control>L", "app.conversations_list", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control>R", "app.requests_list", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control><Shift>C", "app.audio_call", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control><Shift>L", "app.clear_history", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control><Shift>Delete", "app.remove_conversation", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control><Shift>B", "app.block_contact", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control><Shift>U", "app.unblock_contact", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control><Shift>J", "app.copy_contact", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control><Shift>A", "app.add_contact", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control>Y", "app.accept_call", NULL);
gtk_application_add_accelerator(GTK_APPLICATION(client), "<Control>D", "app.decline_call", NULL);
#endif
}
static void
action_quit(G_GNUC_UNUSED GSimpleAction *simple,
G_GNUC_UNUSED GVariant *parameter,
gpointer user_data)
{
g_return_if_fail(G_IS_APPLICATION(user_data));
#if GLIB_CHECK_VERSION(2,32,0)
g_application_quit(G_APPLICATION(user_data));
#else
ClientPrivate *priv = CLIENT_GET_PRIVATE(user_data);
gtk_widget_destroy(priv->win);
#endif
}
static void
action_about(G_GNUC_UNUSED GSimpleAction *simple,
G_GNUC_UNUSED GVariant *parameter,
gpointer user_data)
{
g_return_if_fail(G_IS_APPLICATION(user_data));
ClientPrivate *priv = CLIENT_GET_PRIVATE(user_data);
about_dialog(priv->win);
}
static void
exec_action(GSimpleAction *simple,
G_GNUC_UNUSED GVariant *parameter,
gpointer user_data)
{
g_return_if_fail(G_IS_APPLICATION(user_data));
ClientPrivate *priv = CLIENT_GET_PRIVATE(user_data);
GValue value = G_VALUE_INIT;
g_value_init(&value, G_TYPE_STRING);
g_object_get_property(G_OBJECT(simple), "name", &value);
if (!g_value_get_string(&value)) return;
std::string name = g_value_get_string(&value);
if (name == "display_account_list")
main_window_display_account_list(MAIN_WINDOW(priv->win));
else if (name == "search")
main_window_search(MAIN_WINDOW(priv->win));
else if (name == "conversations_list")
main_window_conversations_list(MAIN_WINDOW(priv->win));
else if (name == "requests_list")
main_window_requests_list(MAIN_WINDOW(priv->win));
else if (name == "audio_call")
main_window_audio_call(MAIN_WINDOW(priv->win));
else if (name == "clear_history")
main_window_clear_history(MAIN_WINDOW(priv->win));
else if (name == "remove_conversation")
main_window_remove_conversation(MAIN_WINDOW(priv->win));
else if (name == "block_contact")
main_window_block_contact(MAIN_WINDOW(priv->win));
else if (name == "unblock_contact")
main_window_unblock_contact(MAIN_WINDOW(priv->win));
else if (name == "copy_contact")
main_window_copy_contact(MAIN_WINDOW(priv->win));
else if (name == "add_contact")
main_window_add_contact(MAIN_WINDOW(priv->win));
else if (name == "accept_call")
main_window_accept_call(MAIN_WINDOW(priv->win));
else if (name == "decline_call")
main_window_decline_call(MAIN_WINDOW(priv->win));
else if (name == "toggle_fullscreen")
main_window_toggle_fullscreen(MAIN_WINDOW(priv->win));
else
g_warning("Missing implementation for this action: %s", name.c_str());
}
static void
toggle_smartinfo(GSimpleAction *action, GVariant *parameter, gpointer)
{
g_simple_action_set_state(action, parameter);
if (g_variant_get_boolean(parameter)) {
SmartInfoHub::instance().start();
} else {
SmartInfoHub::instance().stop();
}
}
static void
action_show_shortcuts(G_GNUC_UNUSED GSimpleAction *action, G_GNUC_UNUSED GVariant *parameter, gpointer user_data)
{
g_return_if_fail(G_IS_APPLICATION(user_data));
ClientPrivate *priv = CLIENT_GET_PRIVATE(user_data);
GtkBuilder *builder = gtk_builder_new_from_resource("/net/jami/JamiGnome/help-overlay.ui");
GtkWidget *overlay = GTK_WIDGET(gtk_builder_get_object (builder, "help_overlay"));
gtk_window_set_transient_for(GTK_WINDOW(overlay), GTK_WINDOW(priv->win));
gtk_widget_show(overlay);
g_object_unref(builder);
}
static const GActionEntry actions[] = {
{"accept", NULL, NULL, NULL, NULL, {0}},
{"hangup", NULL, NULL, NULL, NULL, {0}},
{"hold", NULL, NULL, "false", NULL, {0}},
{"quit", action_quit, NULL, NULL, NULL, {0}},
{"about", action_about, NULL, NULL, NULL, {0}},
{"mute_audio", NULL, NULL, "false", NULL, {0}},
{"mute_video", NULL, NULL, "false", NULL, {0}},
{"record", NULL, NULL, "false", NULL, {0}},
{"display-smartinfo", NULL, NULL, "false", toggle_smartinfo, {0}},
{"display_account_list", exec_action, NULL, NULL, NULL, {0}},
{"search", exec_action, NULL, NULL, NULL, {0}},
{"conversations_list", exec_action, NULL, NULL, NULL, {0}},
{"requests_list", exec_action, NULL, NULL, NULL, {0}},
{"audio_call", exec_action, NULL, NULL, NULL, {0}},
{"clear_history", exec_action, NULL, NULL, NULL, {0}},
{"remove_conversation", exec_action, NULL, NULL, NULL, {0}},
{"block_contact", exec_action, NULL, NULL, NULL, {0}},
{"unblock_contact", exec_action, NULL, NULL, NULL, {0}},
{"copy_contact", exec_action, NULL, NULL, NULL, {0}},
{"add_contact", exec_action, NULL, NULL, NULL, {0}},
{"accept_call", exec_action, NULL, NULL, NULL, {0}},
{"decline_call", exec_action, NULL, NULL, NULL, {0}},
{"show_shortcuts", action_show_shortcuts, NULL, NULL, NULL, {0}},
{"toggle_fullscreen", exec_action, NULL, NULL, NULL, {0}},
};
static void
autostart_toggled(GSettings *settings, G_GNUC_UNUSED gchar *key, G_GNUC_UNUSED gpointer user_data)
{
autostart_symlink(g_settings_get_boolean(settings, "start-on-login"));
}
static void
show_main_window_toggled(Client *client)
{
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
if (g_settings_get_boolean(priv->settings, "show-main-window")) {
gtk_window_present(GTK_WINDOW(priv->win));
} else {
gtk_widget_hide(priv->win);
}
}
static void
window_show(Client *client)
{
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
g_settings_set_boolean(priv->settings, "show-main-window", TRUE);
}
static void
window_hide(Client *client)
{
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
g_settings_set_boolean(priv->settings, "show-main-window", FALSE);
}
static gboolean
on_close_window(GtkWidget *window, G_GNUC_UNUSED GdkEvent *event, Client *client)
{
g_return_val_if_fail(GTK_IS_WINDOW(window) && IS_CLIENT(client), FALSE);
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
if (g_settings_get_boolean(priv->settings, "show-status-icon")) {
/* we want to simply hide the window and keep the client running */
auto closeWindow = main_window_can_close(MAIN_WINDOW(window));
if (closeWindow) {
window_hide(client);
main_window_reset(MAIN_WINDOW(window));
}
return TRUE; /* do not propagate event */
} else {
/* we want to quit the application, so just propagate the event */
return FALSE;
}
}
void
on_main_window_urgency_changed(GtkWidget *, gboolean urgent, Client *client)
{
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
g_return_if_fail(priv);
#if HAVE_APPINDICATOR
app_indicator_set_status((AppIndicator *) priv->systray_icon,
urgent
? APP_INDICATOR_STATUS_ATTENTION
: APP_INDICATOR_STATUS_ACTIVE);
#else
GError *error = NULL;
GdkPixbuf* icon =
gdk_pixbuf_new_from_resource(urgent
? "/net/jami/JamiGnome/jami-symbol-blue-new"
: "/net/jami/JamiGnome/jami-symbol-blue",
&error);
if (icon) {
// GtkStatusIcon is deprecated since 3.14, but we fallback on it
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gtk_status_icon_set_from_pixbuf((GtkStatusIcon *) priv->systray_icon, icon);
gtk_status_icon_set_title((GtkStatusIcon *) priv->systray_icon,
urgent
? _("Jami needs your attention")
: "jami-gnome");
G_GNUC_END_IGNORE_DEPRECATIONS
} else {
g_debug("Could not load icon: %s", error->message);
g_clear_error(&error);
}
#endif
}
#if !HAVE_APPINDICATOR
static void
popup_menu(GtkStatusIcon *self,
guint button,
guint when,
Client *client)
{
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS // GtkStatusIcon is deprecated since 3.14, but we fallback on it
gtk_menu_popup(GTK_MENU(priv->icon_menu), NULL, NULL, gtk_status_icon_position_menu, self, button, when);
G_GNUC_END_IGNORE_DEPRECATIONS
}
#endif
static void
init_systray(Client *client)
{
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
// init menu
if (!priv->icon_menu) {
/* for some reason AppIndicator doesn't like the menu being built from a GMenuModel and/or
* the GMenuModel being built from an xml resource. So we build the menu in code.
*/
priv->icon_menu = gtk_menu_new();
g_object_ref_sink(priv->icon_menu);
auto item = gtk_check_menu_item_new_with_label(C_("In the status icon menu, toggle action to show or hide the Jami main window", "Show Jami"));
gtk_actionable_set_action_name(GTK_ACTIONABLE(item), "app.show-main-window");
gtk_menu_shell_append(GTK_MENU_SHELL(priv->icon_menu), item);
item = gtk_menu_item_new_with_label(_("Quit"));
gtk_actionable_set_action_name(GTK_ACTIONABLE(item), "app.quit");
gtk_menu_shell_append(GTK_MENU_SHELL(priv->icon_menu), item);
gtk_widget_insert_action_group(priv->icon_menu, "app", G_ACTION_GROUP(client));
gtk_widget_show_all(priv->icon_menu);
}
#if HAVE_APPINDICATOR
auto indicator = app_indicator_new("jami-gnome", "jami-gnome", APP_INDICATOR_CATEGORY_COMMUNICATIONS);
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE);
app_indicator_set_title(indicator, JAMI_CLIENT_NAME);
app_indicator_set_attention_icon_full(
indicator, "jami-gnome-new", _("Jami needs your attention"));
/* app indicator requires a menu */
app_indicator_set_menu(indicator, GTK_MENU(priv->icon_menu));
priv->systray_icon = indicator;
#else
GError *error = NULL;
GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/net/jami/JamiGnome/jami-symbol-blue", &error);
if (icon == nullptr) {
g_debug("Could not load icon: %s", error->message);
g_clear_error(&error);
} else {
G_GNUC_BEGIN_IGNORE_DEPRECATIONS // GtkStatusIcon is deprecated since 3.14, but we fallback on it
auto status_icon = gtk_status_icon_new_from_pixbuf(icon);
gtk_status_icon_set_title(status_icon, "jami-gnome");
G_GNUC_END_IGNORE_DEPRECATIONS
g_signal_connect_swapped(status_icon, "activate", G_CALLBACK(window_show), client);
g_signal_connect(status_icon, "popup-menu", G_CALLBACK(popup_menu), client);
priv->systray_icon = status_icon;
}
#endif
g_signal_connect(
priv->win, "main-window-urgency-changed",
G_CALLBACK(on_main_window_urgency_changed),
client);
on_main_window_urgency_changed(
priv->win,
main_window_get_urgency(MAIN_WINDOW(priv->win)),
client);
}
static void
systray_toggled(GSettings *settings, const gchar *key, Client *client)
{
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
if (g_settings_get_boolean(settings, key)) {
if (!priv->systray_icon)
init_systray(client);
} else {
if (priv->systray_icon)
g_clear_object(&priv->systray_icon);
}
}
static void
client_activate(GApplication *app)
{
Client *client = CLIENT(app);
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
if (priv->win == NULL) {
// activate being called for the first time
priv->win = main_window_new(GTK_APPLICATION(app));
/* make sure win is set to NULL when the window is destroyed */
g_object_add_weak_pointer(G_OBJECT(priv->win), (gpointer *)&priv->win);
/* check if the window should be destoryed or not on close */
g_signal_connect(priv->win, "delete-event", G_CALLBACK(on_close_window), client);
/* if we didn't launch with the '-r' (--restore-last-window-state) option then force the
* show-main-window to true */
if (!priv->restore_window_state)
window_show(client);
show_main_window_toggled(client);
g_signal_connect_swapped(priv->settings, "changed::show-main-window", G_CALLBACK(show_main_window_toggled), client);
// track sys icon state
g_signal_connect(priv->settings, "changed::show-status-icon", G_CALLBACK(systray_toggled), client);
systray_toggled(priv->settings, "show-status-icon", client);
} else {
// activate not being called for the first time, force showing of main window
window_show(client);
}
}
// TODO add some args!
static void
client_open(GApplication *app, GFile ** /*file*/, gint /*arg3*/, const gchar* /*arg4*/)
{
client_activate(app);
// TODO migrate place call at begining
}
static void
client_startup(GApplication *app)
{
Client *client = CLIENT(app);
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
g_message("Jami GNOME client version: %s", VERSION);
/* make sure that the system corresponds to the autostart setting */
autostart_symlink(g_settings_get_boolean(priv->settings, "start-on-login"));
g_signal_connect(priv->settings, "changed::start-on-login", G_CALLBACK(autostart_toggled), NULL);
/* init clutter */
int clutter_error;
if ((clutter_error = gtk_clutter_init(&priv->argc, &priv->argv)) != CLUTTER_INIT_SUCCESS) {
g_error("Could not init clutter : %d\n", clutter_error);
exit(1); /* the g_error above should normally cause the application to exit */
}
/* init libRingClient and make sure its connected to the dbus */
try {
priv->qtapp = new QCoreApplication(priv->argc, priv->argv);
/* the call model will try to connect to jamid via dbus */
} catch(const char * msg) {
exception_dialog(msg);
exit(1);
} catch(QString& msg) {
exception_dialog(msg.toLocal8Bit().constData());
exit(1);
}
/* load translations from LRC */
const auto locale_name = QLocale::system().name();
const auto locale_lang = locale_name.split('_')[0];
if (locale_name != locale_lang) {
/* Install language first to have lowest priority */
priv->translator_lang.reset(new QTranslator);
if (priv->translator_lang->load(JAMI_CLIENT_INSTALL "/share/libringclient/translations/lrc_" + locale_lang)) {
g_debug("installed translations for %s", locale_lang.toUtf8().constData());
priv->qtapp->installTranslator(priv->translator_lang.get());
}
}
priv->translator_full.reset(new QTranslator);
if (priv->translator_full->load(JAMI_CLIENT_INSTALL "/share/libringclient/translations/lrc_" + locale_name)) {
g_debug("installed translations for %s", locale_name.toUtf8().constData());
}
if (not priv->translator_lang and not priv->translator_full) {
g_debug("could not load LRC translations for %s, %s",
QLocale::languageToString(QLocale::system().language()).toUtf8().constData(),
QLocale::countryToString(QLocale::system().country()).toUtf8().constData()
);
}
/* init delegates */
GlobalInstances::setDBusErrorHandler(std::unique_ptr<Interfaces::DBusErrorHandler>(new Interfaces::DBusErrorHandler()));
/* Override theme since we don't have appropriate icons for a dark them (yet) */
GtkSettings *gtk_settings = gtk_settings_get_default();
g_object_set(G_OBJECT(gtk_settings), "gtk-application-prefer-dark-theme",
FALSE, NULL);
/* enable button icons */
g_object_set(G_OBJECT(gtk_settings), "gtk-button-images",
TRUE, NULL);
/* enable sound (for notification) */
g_object_set(G_OBJECT(gtk_settings), "gtk-enable-event-sounds",
TRUE, NULL);
/* add GActions */
g_action_map_add_action_entries(
G_ACTION_MAP(app), actions, G_N_ELEMENTS(actions), app);
/* GActions for settings */
auto action_window_visible = g_settings_create_action(priv->settings, "show-main-window");
g_action_map_add_action(G_ACTION_MAP(app), action_window_visible);
/* add accelerators */
accelerators(CLIENT(app));
G_APPLICATION_CLASS(client_parent_class)->startup(app);
}
static void
client_shutdown(GApplication *app)
{
Client *self = CLIENT(app);
ClientPrivate *priv = CLIENT_GET_PRIVATE(self);
gtk_widget_destroy(priv->win);
QObject::disconnect(priv->uam_updated);
/* free the QCoreApplication, which will destroy all libRingClient models
* and thus send the Unregister signal over dbus to jamid */
if (priv->qtapp) {
delete priv->qtapp;
priv->qtapp = nullptr;
}
/* free the copied cmd line args */
g_strfreev(priv->argv);
g_clear_object(&priv->settings);
/* Chain up to the parent class */
G_APPLICATION_CLASS(client_parent_class)->shutdown(app);
}
static void
client_init(Client *self)
{
ClientPrivate *priv = CLIENT_GET_PRIVATE(self);
priv->win = NULL;
priv->qtapp = NULL;
priv->settings = g_settings_new_full(get_settings_schema(), NULL, NULL);
/* add custom cmd line options */
client_add_options(G_APPLICATION(self));
}
static void
client_class_init(ClientClass *klass)
{
G_APPLICATION_CLASS(klass)->startup = client_startup;
G_APPLICATION_CLASS(klass)->activate = client_activate;
G_APPLICATION_CLASS(klass)->open = client_open;
G_APPLICATION_CLASS(klass)->shutdown = client_shutdown;
}
Client*
client_new(int argc, char *argv[])
{
Client *client = (Client *)g_object_new(client_get_type(),
"application-id", JAMI_CLIENT_APP_ID,
"flags", G_APPLICATION_HANDLES_OPEN ,
NULL);
/* copy the cmd line args before they get processed by the GApplication*/
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
priv->argc = argc;
priv->argv = g_strdupv((gchar **)argv);
return client;
}
GtkWindow*
client_get_main_window(Client *client)
{
g_return_val_if_fail(IS_CLIENT(client), NULL);
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
return (GtkWindow *)priv->win;
}
void
client_set_restore_main_window_state(Client *client, gboolean restore)
{
g_return_if_fail(IS_CLIENT(client));
ClientPrivate *priv = CLIENT_GET_PRIVATE(client);
priv->restore_window_state = restore;
}