/*
* Copyright (c) 2011- Osmo Antero.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 3 of the License (GPL3), or any later version.
*
* This library 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 Library General Public License 3 for more details.
*
* You should have received a copy of the GNU Library General Public
* License 3 along with this program; if not, see /usr/share/common-licenses/GPL file
* or .
*/
#include
#include
#include
#include
#include
#include // GST_STATE_* values
#include "log.h"
#include "support.h"
#include "utility.h"
#include "dconf.h"
#include "about.h"
#include "rec-window.h"
#include "rec-manager.h"
#include
// Note: Uncomment this to show debug messages from this module
//#define DEBUG_SYSTRAY
#if defined(DEBUG_SYSTRAY) || defined(DEBUG_ALL)
#define LOG_SYSTRAY LOG_MSG
#else
#define LOG_SYSTRAY(x, ...)
#endif
// Macro to support GTK 3
#define gtk_menu_append(menu, menu_item) gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item)
// Systray icon
static GtkWidget *g_tray_icon = NULL;
// Menu on the icon
static GtkWidget *g_tray_menu = NULL;
static void systray_icon_create(gboolean show);
static void systray_icon_remove();
static void systray_set_icon(gchar *icon_name);
static void systray_set_menu_items(GtkWidget *menu, gint state);
void systray_module_init() {
;
}
void systray_module_exit() {
systray_icon_remove();
}
void systray_icon_setup(gboolean show) {
systray_icon_create(show);
}
gboolean systray_icon_is_installed() {
return G_IS_OBJECT(g_tray_icon);
}
void systray_set_menu_items1(gint state) {
systray_set_menu_items(g_tray_menu, state);
}
void systray_set_menu_items2(gboolean show) {
if (!GTK_IS_MENU(g_tray_menu)) return;
// Update "Show/Hide Window" menu item
GtkWidget *menu_item = (GtkWidget*)g_object_get_data(G_OBJECT(g_tray_menu), "menu-item-show-window");
if (!(GTK_IS_MENU(g_tray_menu) && GTK_IS_MENU_ITEM(menu_item))) return;
LOG_SYSTRAY("systray_set_menu_items2, show:%d\n", show);
if (!show) {
// Tray menu.
// Translators: This belongs to the tray menu (menu on the system tray).
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), _("Show window"));
} else {
// Tray menu.
// Translators: This belongs to the tray menu (menu on the system tray).
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), _("Hide window"));
}
}
static void systray_popup_menu_cb(GtkWidget * widget, gpointer data) {
gchar *cmd = (gchar*)data;
LOG_SYSTRAY("systray_popup_menu_cb: %s\n", cmd);
if (!g_strcmp0(cmd, "start")) {
rec_manager_flip_recording();
} else if (!g_strcmp0(cmd, "continue")) {
rec_manager_flip_recording();
} else if (!g_strcmp0(cmd, "stop")) {
rec_manager_stop_recording();
} else if (!g_strcmp0(cmd, "pause")) {
rec_manager_pause_recording();
} else if (!g_strcmp0(cmd, "about")) {
about_this_app();
} else if (!g_strcmp0(cmd, "settings")) {
// Not implemented
} else if (!g_strcmp0(cmd, "quit")) {
win_close_button_cb(NULL, GINT_TO_POINTER(TRUE)/*force quit*/);
} else if (!g_strcmp0(cmd, "show-folder")) {
// Open file browser and display content of Audio-folder.
// Audio folder
gchar *audio_folder = get_audio_folder();
// Fire default file browser
GError *error = NULL;
run_tool_for_file(audio_folder, "nautilus", &error);
if (error) {
// Translator: This error message is shown in a MessageBox.
gchar *msg = g_strdup_printf(_("Cannot start file browser.\nPlease display %s manually."), audio_folder);
messagebox_error(msg, NULL);
g_free(msg);
g_error_free(error);
}
g_free(audio_folder);
} else if (!g_strcmp0(cmd, "show") || !g_strcmp0(cmd, "hide")) {
// Main window is visible?
gboolean visible = win_window_is_visible();
// Main window is minimized/iconified?
GdkWindowState state = win_get_window_state();
win_show_window(!visible || state == GDK_WINDOW_STATE_ICONIFIED);
}
}
static void systray_set_menu_items(GtkWidget *menu, gint state) {
// Update menu items to reflect state of recording
if (!GTK_IS_MENU(menu)) return;
LOG_SYSTRAY("systray_set_menu_items, state:%d\n", state);
// Get menu items
GtkWidget *item_start = (GtkWidget*)g_object_get_data(G_OBJECT(menu), "menu-item-start-recording");
GtkWidget *item_continue = (GtkWidget*)g_object_get_data(G_OBJECT(menu), "menu-item-continue-recording");
GtkWidget *item_stop = (GtkWidget*)g_object_get_data(G_OBJECT(menu), "menu-item-stop-recording");
GtkWidget *item_pause = (GtkWidget*)g_object_get_data(G_OBJECT(menu), "menu-item-pause-recording");
gchar *tray_icon = NULL;
if (state == GST_STATE_PLAYING) {
// Recording is on
gtk_widget_hide(item_start);
gtk_widget_hide(item_continue);
gtk_widget_show(item_stop);
gtk_widget_show(item_pause);
gtk_widget_set_sensitive(item_pause, TRUE);
tray_icon = "audio-recorder-on.png";
} else if (state == GST_STATE_PAUSED) {
// Paused
gtk_widget_hide(item_start);
gtk_widget_show(item_continue);
gtk_widget_show(item_stop);
gtk_widget_show(item_pause);
gtk_widget_set_sensitive(item_pause, FALSE);
tray_icon = "audio-recorder-paused.png";
} else {
// Stopped
gtk_widget_show(item_start);
gtk_widget_hide(item_continue);
gtk_widget_hide(item_stop);
gtk_widget_show(item_pause);
gtk_widget_set_sensitive(item_pause, FALSE);
tray_icon = "audio-recorder-off.png";
}
if (menu == g_tray_menu) {
// Set tray icon
systray_set_icon(tray_icon);
}
}
GtkWidget *systray_create_menu(gboolean for_tray_icon) {
GtkWidget *menu = gtk_menu_new();
GtkWidget *menu_item = NULL;
if (for_tray_icon) {
// Show main window
// Window is visible?
gboolean visible = win_window_is_visible();
// Window is minimized/nomal?
GdkWindowState state = win_get_window_state();
if (visible && state != GDK_WINDOW_STATE_ICONIFIED ) {
// Tray menu.
menu_item = gtk_menu_item_new_with_label(_("Hide window"));
g_signal_connect((gpointer)menu_item, "activate", G_CALLBACK(systray_popup_menu_cb), "hide");
} else {
// Tray menu.
menu_item = gtk_menu_item_new_with_label(_("Show window"));
g_signal_connect((gpointer)menu_item, "activate", G_CALLBACK(systray_popup_menu_cb), "show");
}
gtk_widget_show(menu_item);
gtk_menu_append(GTK_MENU(menu), menu_item);
// Save this item
g_object_set_data(G_OBJECT(menu), "menu-item-show-window", menu_item);
// Separator line
menu_item = gtk_separator_menu_item_new();
gtk_widget_show(menu_item);
gtk_menu_append(GTK_MENU(menu), menu_item);
}
// Start recording.
// Translators: This belongs to the tray menu (menu on the system tray).
menu_item = gtk_menu_item_new_with_label(_("Start recording"));
gtk_widget_show(menu_item);
g_signal_connect((gpointer)menu_item, "activate", G_CALLBACK(systray_popup_menu_cb), "start");
gtk_menu_append(GTK_MENU(menu), menu_item);
// Save this item
g_object_set_data(G_OBJECT(menu), "menu-item-start-recording", menu_item);
// Continue recording.
// Translators: This belongs to the tray menu (menu on the system tray).
menu_item = gtk_menu_item_new_with_label(_("Continue recording"));
// Hide this
gtk_widget_hide(menu_item);
g_signal_connect((gpointer)menu_item, "activate", G_CALLBACK(systray_popup_menu_cb), "continue");
gtk_menu_append(GTK_MENU(menu), menu_item);
// Save this item
g_object_set_data(G_OBJECT(menu), "menu-item-continue-recording", menu_item);
// Stop recording.
// Translators: This belongs to the tray menu (menu on the system tray).
menu_item = gtk_menu_item_new_with_label(_("Stop recording"));
// Hide this
gtk_widget_hide(menu_item);
g_signal_connect((gpointer)menu_item, "activate", G_CALLBACK(systray_popup_menu_cb), "stop");
gtk_menu_append(GTK_MENU(menu), menu_item);
// Save this item
g_object_set_data(G_OBJECT(menu), "menu-item-stop-recording", menu_item);
// Pause recording.
// Translators: This belongs to the tray menu (menu on the system tray).
menu_item = gtk_menu_item_new_with_label(_("Pause recording"));
// Hide this
gtk_widget_hide(menu_item);
g_signal_connect((gpointer)menu_item, "activate", G_CALLBACK(systray_popup_menu_cb), "pause");
gtk_menu_append(GTK_MENU(menu), menu_item);
// Save this item
g_object_set_data(G_OBJECT(menu), "menu-item-pause-recording", menu_item);
// Separator line
menu_item = gtk_separator_menu_item_new();
gtk_widget_show(menu_item);
gtk_menu_append(GTK_MENU(menu), menu_item);
// Menu item "Show saved recordings".
// Open Audio-folder and show all saved recordings.
// Translators: This belongs to the tray menu (menu on the system tray)
menu_item = gtk_menu_item_new_with_label(_("Show saved recordings"));
g_signal_connect(G_OBJECT (menu_item), "activate", G_CALLBACK(systray_popup_menu_cb), "show-folder");
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
gtk_widget_show(menu_item);
gboolean show_about_item = TRUE;
if (show_about_item) {
// Separator line
menu_item = gtk_separator_menu_item_new();
gtk_widget_show(menu_item);
gtk_menu_append(GTK_MENU(menu), menu_item);
// Deprecated in GTK 3.10
// menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
menu_item = gtk_menu_item_new_with_mnemonic (_("_About"));
g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK(systray_popup_menu_cb), "about");
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
gtk_widget_show(menu_item);
}
// Separator line
menu_item = gtk_separator_menu_item_new();
gtk_widget_show(menu_item);
gtk_menu_append(GTK_MENU(menu), menu_item);
// Deprecated in GTK 3.10
// menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
menu_item = gtk_menu_item_new_with_mnemonic (_("_Quit"));
g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK(systray_popup_menu_cb), "quit");
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
gtk_widget_show (menu_item);
gtk_widget_show(menu);
// Get recording state
gint state = -1;
gint pending = -1;
rec_manager_get_state(&state, &pending);
// Settle menu items
systray_set_menu_items(menu, state);
return menu;
}
static void systray_icon_create(gboolean show) {
// Show tray icon?
if (!show) {
systray_icon_remove();
return;
}
LOG_SYSTRAY("systray_icon_create.\n");
if (IS_APP_INDICATOR(g_tray_icon)) {
app_indicator_set_status(APP_INDICATOR(g_tray_icon), APP_INDICATOR_STATUS_ACTIVE);
return;
}
g_tray_icon = (GtkWidget*)app_indicator_new("audio-recorder application",
"audio-recorder-off", APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
g_assert(IS_APP_INDICATOR(g_tray_icon));
app_indicator_set_status(APP_INDICATOR(g_tray_icon), APP_INDICATOR_STATUS_ACTIVE);
// Attach a popup-menu
g_tray_menu = systray_create_menu(TRUE/*for_tray_icon*/);
// Get current recording state
gint state = -1;
gint pending = -1;
rec_manager_get_state(&state, &pending);
// Set initial icon color and menu state
systray_set_menu_items1(state);
app_indicator_set_menu(APP_INDICATOR(g_tray_icon), GTK_MENU(g_tray_menu));
}
static void systray_icon_remove() {
if (G_IS_OBJECT(g_tray_menu)) {
gtk_widget_destroy(GTK_WIDGET(g_tray_menu));
}
g_tray_menu = NULL;
if (IS_APP_INDICATOR(g_tray_icon)) {
LOG_SYSTRAY("systray_icon_remove().\n");
g_object_unref(G_OBJECT(g_tray_icon));
}
g_tray_icon = NULL;
}
static void systray_set_icon(gchar *icon_name) {
if (!IS_APP_INDICATOR(g_tray_icon)) return;
gchar *s = g_strdup(icon_name);
// Remove file extension ".png"
// Simply find the last '.' and put '\0' on it. This should work fine.
gchar *pos = g_strrstr(s, ".");
if (pos) {
*pos = '\0';
}
app_indicator_set_icon(APP_INDICATOR(g_tray_icon), s);
g_free(s);
}