1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <libxml/parser.h>
27 /** Gtk/glib glade stuff */
30 #include <libmpd/debug_printf.h>
33 #include "playlist3.h"
36 #include "advanced-search.h"
37 #include "gmpc_easy_download.h"
39 #include "setup-assistant.h"
41 #include "gmpc-mpddata-model-playlist.h"
42 #include "bug-information.h"
44 #include "pixbuf-cache.h"
46 #include "preferences.h"
48 #include "plugin-man.h"
49 #include "browsers/playlist3-playlist-editor.h"
55 #define LOG_DOMAIN "Gmpc"
61 #include "internal-plugins.h"
63 #include "mpd-easy-commands.h"
66 * Global objects that give signals
69 GtkApplication
*gmpc_application
= NULL
;
72 /* gives signal on connection changes, and state changes of mpd.*/
73 GmpcConnection
*gmpcconn
= NULL
;
74 /* Implements, and gives signals on profiles */
75 GmpcProfiles
*gmpc_profiles
= NULL
;
76 /* Implements, and gives signals on meta_data*/
77 GmpcMetaWatcher
*gmw
= NULL
;
79 GmpcEasyCommand
*gmpc_easy_command
= NULL
;
80 /* Playlist3 messages */
81 Playlist3MessagePlugin
*pl3_messages
= NULL
;
83 /* The playlist backend */
84 GtkTreeModel
*playlist
= NULL
;
86 GObject
*paned_size_group
= NULL
;
88 * This flag indicate the requested connection state by the user.
89 * If the user presses disconnect, you don't want to auto-connect anymore.
91 int gmpc_connected
= FALSE
;
93 static void connection_changed_real(
94 GmpcConnection
* gmpcconn
,
99 static void gmpc_status_changed_callback_real(
100 GmpcConnection
* gmpcconn
,
102 ChangedStatusType what
,
106 * Define some local functions
109 static void gmpc_easy_command_set_default_entries(void);
110 static void gmpc_mmkeys_connect_signals(GObject
*keys
);
111 /** handle connection changed */
112 static void connection_changed(MpdObj
* mi
, int connect
, gpointer data
);
114 /** Error callback */
115 static int error_callback(MpdObj
* mi
,
120 /** init stock icons */
121 static void init_stock_icons(void);
124 * the xml fle pointer to the player window
126 static GtkBuilder
*xml_password_window
= NULL
;
127 static int autoconnect_callback(void);
130 * the ID of the autoconnect timeout callback
132 static guint autoconnect_timeout
= 0;
137 config_obj
*config
= NULL
;
140 * The Connection object
142 MpdObj
*connection
= NULL
;
144 /* Glade prototypes, these would be static otherwise */
145 void send_password(void);
147 static void print_version(void);
151 * Forward libxml errors into GLib.log errors with LibXML error domain
153 static void xml_error_func(void *ctx
, const char *msg
, ...)
157 g_logv("LibXML", G_LOG_LEVEL_DEBUG
, msg
, ap
);
160 /* \todo why is this here? */
161 static xmlGenericErrorFunc handler
= (xmlGenericErrorFunc
) xml_error_func
;
164 static gboolean
hide_on_start(void)
169 static int handle_commandline(GApplication
*app
, GApplicationCommandLine
*cmd
, gpointer data
)
174 argv
= g_application_command_line_get_arguments(cmd
, &argc
);
175 printf("Commandline handler: %i\n", argc
);
178 for ( i
= 1; i
< argc
; i
++) {
179 printf("executing: %s\n" , argv
[i
]);
180 gmpc_easy_command_do_query(gmpc_easy_command
, argv
[i
]);
183 printf("activate\n");
184 g_application_activate(gmpc_application
);
191 int main(int argc
, char **argv
)
194 gchar
*packagedir
= NULL
;
200 /* A string used severall times to create a path */
211 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Setting NLS");
212 bindtextdomain(GETTEXT_PACKAGE
, PACKAGE_LOCALE_DIR
);
213 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8");
214 textdomain(GETTEXT_PACKAGE
);
220 TEC("Setting up locale");
222 parse_options(&argc
, &argv
);
224 /* Show the version, if requested */
225 if (settings
.show_version
)
230 TEC("Parsing command line options");
232 log_set_debug_level(settings
.debug_level
);
233 TEC("Set debug level")
234 /* Show the bug-information dialog */
235 if (settings
.show_bug_information
)
237 bug_information_file_new(stdout
);
244 * Libxml is not used (directly) by gmpc.
245 * But via glade and several plugins use it.
246 * I need to initialize it before the threading is started.
249 * This fixes the plugin crasher bug on windows.
252 initGenericErrorDefaultFunc(&handler
);
255 * Check if threading is supported, if so, start it.
256 * Don't fail here, stuff like can cause that it is allready initialized.
258 if (!g_thread_supported())
261 TEC("Initializing threading");
265 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Initializing gtk ");
269 * This loads an extra gtk rc file on windows.
270 * This is used to re-enable rule-hint in the treeview.
271 * (this is forced off on windows).
273 packagedir
= g_win32_get_package_installation_directory_of_module(NULL
);
274 url
= g_build_filename(packagedir
,
276 "gmpc-gtk-win32.rc", NULL
);
278 gtk_rc_add_default_file(url
);
283 gmpc_application
= gtk_application_new("org.gmpclient.gmpc", G_APPLICATION_HANDLES_COMMAND_LINE
);
285 GError
*error
= NULL
;
288 if(!g_application_register(G_APPLICATION(gmpc_application
),NULL
, &error
))
290 g_error("Failed to register instance: %s", error
->message
);
294 // Check if we are already running.
295 if(g_application_get_is_remote(G_APPLICATION(gmpc_application
)))
297 g_debug("Already running....");
298 return g_application_run(G_APPLICATION(gmpc_application
), argc
, argv
);
301 g_signal_connect(G_OBJECT(gmpc_application
), "command-line", G_CALLBACK(handle_commandline
), NULL
);
302 g_signal_connect(G_OBJECT(gmpc_application
), "activate", G_CALLBACK(pl3_show_and_position_window
), NULL
);
308 GError
*error
= NULL
;
309 GtkCssProvider
*provider
= gtk_css_provider_new ();
310 gchar
*path
= g_build_filename(PACKAGE_DATA_DIR
, "gmpc", "gmpc.css", NULL
);
312 gtk_css_provider_load_from_path(provider
, path
, &error
);
314 printf("Loading: %s\n", path
);
318 g_error("Failed to load css file: %s:%s",path
, error
->message
);
322 GdkDisplay
*display
= gdk_display_get_default ();
323 GdkScreen
*screen
= gdk_display_get_default_screen (display
);
325 gtk_style_context_add_provider_for_screen (
326 screen
, GTK_STYLE_PROVIDER (provider
),
327 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
329 g_object_unref (provider
);
334 /* Hack to override the icon theme, on recursive zeltak request */
335 if(settings
.icon_theme
!= NULL
) {
336 gtk_settings_set_string_property(gtk_settings_get_default(),
337 "gtk-icon-theme-name" , settings
.icon_theme
,NULL
);
341 * Call create_gmpc_paths();
342 * This function checks if the path needed path are available
343 * and creates them if needed.
346 TEC("Check version and create paths");
350 * Open the config file
353 * Check if the user has forced a different config file location.
354 * else set to ~/.gmpc/gmpc.cfg
356 if (!settings
.config_path
)
358 url
= gmpc_get_user_path("gmpc.cfg");
361 url
= g_strdup(settings
.config_path
);
367 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
368 "Trying to open the config file: %s", url
);
369 config
= cfg_open(url
);
372 * Show gtk error message and quit if config failed to open.
376 g_log(LOG_DOMAIN
, G_LOG_LEVEL_ERROR
,
377 "Failed to save/load configuration:\n%s\n", url
);
378 show_error_message(_("Failed to load the configuration system."));
381 TEC("Opening config file: %s", url
);
385 * \TODO, Check if version changed, then say something about it
387 * Enable this function if we need todo some upgrading on version change.
388 * Removal of this current content destroys config conversion from 0.17 and
391 url
= cfg_get_single_value_as_string(config
, "Default", "version");
392 if (url
== NULL
|| strcmp(url
, VERSION
))
394 cfg_set_single_value_as_string(config
, "Default", "version", VERSION
);
396 if (url
) q_free(url
);
397 TEC("New version check");
407 gmpc_easy_command
= gmpc_easy_command_new();
408 TEC("Init easy command")
409 gmpc_easy_command_set_default_entries();
410 TEC("Set easy commands")
411 mpd_easy_commands_init();
412 TEC("Set MPD Easy commands");
413 /* Advanced search */
414 advanced_search_init();
415 TEC("Init advanced search");
418 paned_size_group
= (GObject
*) gmpc_paned_size_group_new();
420 gmpc_profiles
= gmpc_profiles_new();
421 /* If user requested a profile, look it up and set it active */
422 if (settings
.profile_name
)
424 gmpc_profiles_set_profile_from_name(gmpc_profiles
,
425 settings
.profile_name
);
427 TEC("Setting up gmpc idle,signals and profiles");
429 * Initialize the new metadata subsystem.
433 TEC("Initializing metadata system");
434 pixbuf_cache_create();
435 TEC("Pixbuf cache create()");
440 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Loading stock icons");
442 TEC("Init stock icons");
444 * Create connection object
446 connection
= mpd_new_default();
447 if (connection
== NULL
)
450 * if failed, print error message
454 "Failed to create connection object\n");
455 show_error_message(_("Failed to setup libmpd"));
458 TEC("Setting up mpd connection object");
460 * Connect signals to the connection object
462 mpd_signal_connect_status_changed(connection
,
463 GmpcStatusChangedCallback
, NULL
);
464 mpd_signal_connect_error(connection
,
465 error_callback
, NULL
);
466 mpd_signal_connect_connection_changed(connection
,
467 connection_changed
, NULL
);
469 * Just some trick to provide glib signals
471 gmpcconn
= (GmpcConnection
*) gmpc_connection_new();
472 g_signal_connect(G_OBJECT(gmpcconn
),
473 "connection_changed",
474 G_CALLBACK(connection_changed_real
), NULL
);
475 g_signal_connect(G_OBJECT(gmpcconn
),
477 G_CALLBACK(gmpc_status_changed_callback_real
), NULL
);
479 TEC("Setting up mpd object signal system");
481 * New Metadata object
483 gmw
= gmpc_meta_watcher_new();
484 TEC("Initializing metadata watcher");
488 /** init the error messages */
489 pl3_messages
= playlist3_message_plugin_new();
491 playlist
= (GtkTreeModel
*)gmpc_mpddata_model_playlist_new(
494 gmpc_mpddata_model_disable_image(GMPC_MPDDATA_MODEL(playlist
));
497 * Add the internall plugins
499 plugin_manager_load_internal_plugins();
503 * load dynamic plugins
505 if (!settings
.disable_plugins
)
507 plugin_manager_load_plugins();
509 /* time todo some initialisation of plugins */
510 plugin_manager_initialize_plugins();
513 * Create the main window
516 TEC("Creating playlist window");
521 * If gmpc is ran for the first time, we want to show a wizard that helps
522 * the user getting started.
524 if (cfg_get_single_value_as_int_with_default(config
,
530 cfg_set_single_value_as_int(config
, "Default", "first-run", 0);
531 TEC("Setup first run assistant");
535 * If autoconnect is enabled, tell gmpc that it's in state it should connect
537 if (cfg_get_single_value_as_int_with_default(config
,
540 DEFAULT_AUTOCONNECT
))
542 gmpc_connected
= TRUE
;
546 * get the status every 1/2 second should be enough, but it's configurable.
548 g_timeout_add(cfg_get_single_value_as_int_with_default(config
,
551 500), (GSourceFunc
) update_mpd_status
, NULL
);
553 * create the autoconnect timeout,
554 * if autoconnect enable, it will check every 5 seconds
555 * if you are still connected, and reconnects you if not.
557 autoconnect_timeout
= g_timeout_add_seconds(5,
558 (GSourceFunc
) autoconnect_callback
, NULL
);
561 * Call this when entering the main loop,
562 * so you are connected on startup, not 5 seconds later
564 g_idle_add((GSourceFunc
) autoconnect_callback
, NULL
);
565 if (settings
.fullscreen
)
567 g_idle_add((GSourceFunc
) pl3_window_fullscreen
, NULL
);
571 * If the user wants gmpc to be started hidden,
572 * call pl3_hide after the mainloop started running
574 if (cfg_get_single_value_as_int_with_default(config
,
578 settings
.start_hidden
)
580 g_timeout_add(250, (GSourceFunc
) hide_on_start
, NULL
);
582 TEC("Setting up timers");
586 * Setup Multimedia Keys
589 gmpc_mmkeys_connect_signals(G_OBJECT(keys
));
590 TEC("Setting up multimedia keys");
593 url
= gmpc_get_user_path("gmpc.key");
594 gtk_accel_map_load(url
);
602 int retv
= g_application_run(G_APPLICATION(gmpc_application
), argc
, argv
);
608 url
= gmpc_get_user_path("gmpc.key");
609 gtk_accel_map_save(url
);
612 /* Quit _all_ downloads */
613 gmpc_easy_async_quit();
615 /* tell the plugins to save themself. */
616 plugin_manager_save_state();
618 /* Should fix some possible crashes */
619 gtk_tree_view_set_model(playlist3_get_category_tree_view(), NULL
);
622 * Clear metadata struct
626 /* time todo some destruction of plugins */
627 plugin_manager_destroy_plugins();
631 g_object_unref(playlist
);
632 g_object_unref(G_OBJECT(gmw
));
634 /* Destroy PanedSizeGroup */
635 g_object_unref(paned_size_group
);
637 * Close the config file
639 TOC("Starting save config");
642 g_object_unref(gmpc_profiles
);
643 g_object_unref(gmpcconn
);
645 pixbuf_cache_destroy();
647 * This now gets destroyed with the plugins
649 advanced_search_destroy();
651 * Destroy the connection object
653 mpd_free(connection
);
655 /* Reset xml error function and cleanup */
656 initGenericErrorDefaultFunc((xmlGenericErrorFunc
*) NULL
);
659 gmpc_mpddata_treeview_cleanup();
661 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Quit....\n");
667 * Function to quiet the program
671 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Quiting gmpc....");
673 * close playlist and store size
677 * Remove the autoconnect timeout,
679 if (autoconnect_timeout
)
680 g_source_remove(autoconnect_timeout
);
683 * Call the connection changed.
684 * so it saves the playlist pos
686 mpd_signal_connect_connection_changed(connection
, NULL
, NULL
);
689 * Disconnect when connected
691 if (mpd_check_connected(connection
))
693 if (cfg_get_single_value_as_int_with_default(config
,
694 "connection", "stop-on-exit", FALSE
))
696 mpd_player_stop(connection
);
698 mpd_disconnect(connection
);
703 gtk_widget_destroy(playlist3_get_window());
708 * Callback that get's called every 5 seconds,
709 * and tries to autoconnect
713 static int autoconnect_backoff
= 0;
714 static int autoconnect_callback(void)
716 /* Don't autoconnect while showing the first start assistant */
717 if (setup_assistant_is_running())
720 /* check if there is an connection. */
721 if (!mpd_check_connected(connection
))
723 /* connect when autoconnect is enabled, the user wants to be connected
726 && cfg_get_single_value_as_int_with_default(config
,
727 "connection", "autoconnect", DEFAULT_AUTOCONNECT
))
732 if (autoconnect_backoff
< 60)
733 autoconnect_backoff
+= 1;
734 /* keep the timeout running */
735 if (autoconnect_timeout
)
736 g_source_remove(autoconnect_timeout
);
737 autoconnect_timeout
= g_timeout_add_seconds(5 + autoconnect_backoff
,
738 (GSourceFunc
) autoconnect_callback
, NULL
);
743 static void init_stock_icons(void)
747 path
= gmpc_get_full_image_path();
748 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), path
);
751 gtk_window_set_default_icon_name("gmpc");
754 /* The Windows gtkrc sets this to 0, so images don't work on buttons */
755 gtk_settings_set_long_property(gtk_settings_get_default(),
756 "gtk-button-images", TRUE
, "main");
764 * Handle status changed callback from the libmpd object
765 * This involves propegating the signal
767 void GmpcStatusChangedCallback(MpdObj
* mi
,
768 ChangedStatusType what
,
771 g_signal_emit_by_name(gmpcconn
, "status-changed", mi
, what
);
775 /* The actual handling of the status changed signal */
776 static void gmpc_status_changed_callback_real(GmpcConnection
* conn
,
778 ChangedStatusType what
,
781 /* When permission changes, update the advanced search regex */
782 if (what
& MPD_CST_PERMISSION
)
784 advanced_search_update_taglist();
787 * Make the plugins recieve the signals
789 plugin_manager_status_changed(mi
, what
);
793 /*******************************
795 * TODO: Needs to be redone/rethought
798 static void password_dialog_response(
808 case GTK_RESPONSE_OK
:
810 path
= (char *)gtk_entry_get_text(
811 GTK_ENTRY(gtk_builder_get_object(xml_password_window
,
813 mpd_set_password(connection
, path
);
814 if (gtk_toggle_button_get_active
815 (GTK_TOGGLE_BUTTON(gtk_builder_get_object(
819 connection_set_password(path
);
821 mpd_send_password(connection
);
825 if (mpd_server_check_command_allowed(connection
, "status") !=
826 MPD_SERVER_COMMAND_ALLOWED
)
828 playlist3_show_error_message(
829 _("GMPC has insufficient permissions on the mpd server."),
831 mpd_disconnect(connection
);
836 (GtkWidget
*) gtk_builder_get_object(xml_password_window
,
838 g_object_unref(xml_password_window
);
839 xml_password_window
= NULL
;
843 static void password_dialog(int failed
)
845 GtkWidget
*pl3_win
= playlist3_get_window();
847 if (xml_password_window
)
849 path
= gmpc_get_full_glade_path("password-dialog.ui");
850 xml_password_window
= gtk_builder_new();
851 gtk_builder_add_from_file(xml_password_window
, path
, NULL
);
852 gtk_window_set_transient_for(GTK_WINDOW
853 (gtk_builder_get_object(xml_password_window
, "password-dialog")),
854 GTK_WINDOW(pl3_win
));
856 if (!xml_password_window
)
860 path
= g_strdup_printf(
861 _("Failed to set password on: '%s'\nPlease try again"),
862 mpd_get_hostname(connection
));
865 path
= g_strdup_printf(
866 _("Please enter your password for: '%s'"),
867 mpd_get_hostname(connection
));
870 GTK_LABEL(gtk_builder_get_object(xml_password_window
, "pass_label")),
874 g_signal_connect(G_OBJECT
875 (gtk_builder_get_object
876 (xml_password_window
, "password-dialog")), "response",
877 G_CALLBACK(password_dialog_response
), xml_password_window
);
881 void send_password(void)
883 password_dialog(FALSE
);
886 static void playlist_support_help_button_clicked(GObject
*a
)
888 open_help("ghelp:gmpc?ProblemSolving");
892 static int error_callback(MpdObj
* mi
,
897 int autoconnect
= cfg_get_single_value_as_int_with_default(config
,
900 DEFAULT_AUTOCONNECT
);
902 /* if we are not connected we show a reconnect */
903 if (!mpd_check_connected(mi
))
907 /* no response? then we just ignore it when autoconnecting. */
908 if (error_id
== 15 && autoconnect
)
911 str
= g_markup_printf_escaped("<b>%s %i: %s</b>",
915 playlist3_show_error_message(str
, ERROR_CRITICAL
);
916 button
= gtk_button_new_from_stock(GTK_STOCK_CONNECT
);
917 g_signal_connect(G_OBJECT(button
),
919 G_CALLBACK(connect_to_mpd
), NULL
);
920 playlist3_error_add_widget(button
);
924 if (setup_assistant_is_running()
925 && (error_id
== MPD_ACK_ERROR_PERMISSION
||
926 error_id
== MPD_ACK_ERROR_PASSWORD
))
928 gchar
*str
= g_markup_printf_escaped("<b>%s</b>",
929 _("Insufficient permission to connect to mpd. Check password"));
930 setup_assistant_set_error(str
);
934 if(error_id
== MPD_ACK_ERROR_SYSTEM
|| error_id
== MPD_ACK_ERROR_NO_EXIST
) {
935 if(g_regex_match_simple(".*{.*playlist.*}.*", error_msg
,
936 0,G_REGEX_MATCH_NOTEMPTY
))
938 GtkWidget
*button
= NULL
;
939 if(favorites
!= NULL
) {
940 gmpc_favorites_list_set_disable(favorites
,TRUE
);
942 playlist_editor_set_disabled();
943 playlist3_show_error_message(
944 _("Playlist support in MPD is not working. See the "
945 "manual on possible fixes.\n"
946 "Playlist editor and favorites are now disabled."
950 button
= gtk_button_new_from_stock(GTK_STOCK_HELP
);
951 g_signal_connect(G_OBJECT(button
), "clicked",
952 G_CALLBACK(playlist_support_help_button_clicked
), NULL
);
953 playlist3_error_add_widget(button
);
957 if (error_id
== MPD_ACK_ERROR_PASSWORD
)
959 password_dialog(TRUE
);
960 } else if (error_id
== MPD_ACK_ERROR_PERMISSION
)
962 password_dialog(FALSE
);
965 gchar
*str
= g_markup_printf_escaped("<b>%s %i: %s</b>",
966 _("error code"), error_id
,
968 playlist3_show_error_message(str
, ERROR_CRITICAL
);
977 * handle a connection changed
979 static void connection_changed(MpdObj
* mi
, int connected
, gpointer data
)
981 /* propagate the signal to the connection object */
982 if (mpd_check_connected(mi
) != connected
)
986 "Connection state differs from actual state: act: %i\n",
992 if (connected
&& !mpd_server_check_version(mi
, 0, 13, 0))
994 gchar
*value
= g_markup_printf_escaped("<b>%s</b>",
995 _("MPD versions before 0.13.0 are not supported"));
996 /* disable user connect ! */
997 gmpc_connected
= FALSE
;
1000 playlist3_show_error_message(value
, ERROR_CRITICAL
);
1003 /* Remove timeout */
1006 if (autoconnect_timeout
)
1007 g_source_remove(autoconnect_timeout
);
1008 autoconnect_timeout
= 0;
1009 autoconnect_backoff
= 0;
1013 advanced_search_update_taglist();
1016 * force an update of status, to check password
1020 mpd_status_update(mi
);
1021 if (connected
!= mpd_check_connected(mi
))
1023 g_log(LOG_DOMAIN
, G_LOG_LEVEL_WARNING
, "State differs, exit");
1024 /* Probly disconnected when getting status.. exiting */
1029 /* remove this when it does not fix it */
1030 g_signal_emit_by_name(gmpcconn
,
1031 "connection-changed",
1033 mpd_check_connected(mi
));
1037 static void connection_changed_real(
1038 GmpcConnection
* obj
,
1048 "Connection changed %i-%i \n",
1050 mpd_check_connected(mi
));
1051 plugin_manager_connection_changed(mi
, connected
);
1054 * force an update of status
1057 mpd_status_update(mi
);
1061 playlist3_show_error_message(_("Connected to mpd"), ERROR_INFO
);
1064 playlist3_show_error_message(_("Disconnected from mpd"), ERROR_INFO
);
1069 if (autoconnect_timeout
)
1070 g_source_remove(autoconnect_timeout
);
1071 autoconnect_timeout
= g_timeout_add_seconds(5,
1072 (GSourceFunc
) autoconnect_callback
, NULL
);
1073 autoconnect_backoff
= 0;
1079 * Shows an error message.
1081 void show_error_message(const gchar
* string
)
1083 GtkWidget
*dialog
= gtk_message_dialog_new_with_markup(NULL
,
1088 gtk_widget_show(dialog
);
1089 gtk_dialog_run(GTK_DIALOG(dialog
));
1092 static void print_version(void)
1094 printf("%s\n", _("Gnome Music Player Client"));
1095 printf(GMPC_COPYRIGHT
"\n\n");
1096 printf("%-25s: %s\n", _("Tagline"), GMPC_TAGLINE
);
1097 printf("%-25s: %i.%i.%i\n", _("Version"),
1100 GMPC_MICRO_VERSION
);
1101 if (revision
&& revision
[0] != '\0')
1103 printf("%-25s: %s\n", _("Revision"), revision
);
1108 * Set a basic set of easycommand handlers.
1110 static void gmpc_easy_command_set_default_entries(void)
1112 gmpc_easy_command_add_entry_stock_id(gmpc_easy_command
,
1115 (GmpcEasyCommandCallback
*) main_quit
,
1116 NULL
,GTK_STOCK_QUIT
);
1118 gmpc_easy_command_add_entry(gmpc_easy_command
,
1121 (GmpcEasyCommandCallback
*) pl3_hide
,
1124 gmpc_easy_command_add_entry(gmpc_easy_command
,
1127 (GmpcEasyCommandCallback
*) create_playlist3
,
1130 gmpc_easy_command_add_entry(gmpc_easy_command
,
1132 _("Toggle gmpc visibility"),
1133 (GmpcEasyCommandCallback
*) pl3_toggle_hidden
,
1136 gmpc_easy_command_add_entry(gmpc_easy_command
,
1137 _("show notification"), "",
1138 _("Show trayicon notification"),
1139 (GmpcEasyCommandCallback
*) tray_icon2_create_tooltip
,
1141 gmpc_easy_command_add_entry_stock_id(gmpc_easy_command
,
1142 _("preferences"), "",
1143 _("Show preferences window"),
1144 (GmpcEasyCommandCallback
*) create_preferences_window
,
1145 NULL
, GTK_STOCK_PREFERENCES
);
1147 gmpc_easy_command_add_entry(gmpc_easy_command
,
1148 _("bug information"), "",
1149 _("Show bug information"),
1150 (GmpcEasyCommandCallback
*) bug_information_window_new
,
1153 gmpc_easy_command_add_entry_icon_name(gmpc_easy_command
,
1155 _("Show add url window"),
1156 (GmpcEasyCommandCallback
*) url_start
,
1159 gmpc_easy_command_add_entry_icon_name(gmpc_easy_command
,
1160 _("url"), ".*://.*",
1161 _("Add url <scheme>://<path>"),
1162 (GmpcEasyCommandCallback
*) url_start_easy_command
,
1166 static void gmpc_mmkeys_connect_signals(GObject
*keys
)
1168 g_signal_connect(keys
,
1170 G_CALLBACK(play_song
), NULL
);
1172 g_signal_connect(keys
,
1174 G_CALLBACK(real_play_song
), NULL
);
1176 g_signal_connect(keys
,
1178 G_CALLBACK(real_pause_song
), NULL
);
1180 g_signal_connect(keys
,
1182 G_CALLBACK(next_song
), NULL
);
1184 g_signal_connect(keys
,
1186 G_CALLBACK(prev_song
), NULL
);
1188 g_signal_connect(keys
,
1190 G_CALLBACK(stop_song
), NULL
);
1192 g_signal_connect(keys
,
1194 G_CALLBACK(song_fastforward
), NULL
);
1196 g_signal_connect(keys
,
1198 G_CALLBACK(song_fastbackward
), NULL
);
1200 g_signal_connect(keys
,
1202 G_CALLBACK(repeat_toggle
), NULL
);
1204 g_signal_connect(keys
,
1206 G_CALLBACK(random_toggle
), NULL
);
1208 g_signal_connect(keys
,
1210 G_CALLBACK(create_playlist3
), NULL
);
1212 g_signal_connect(keys
,
1214 G_CALLBACK(pl3_hide
), NULL
);
1216 g_signal_connect(keys
,
1218 G_CALLBACK(pl3_toggle_hidden
), NULL
);
1220 g_signal_connect(keys
,
1222 G_CALLBACK(volume_up
), NULL
);
1224 g_signal_connect(keys
,
1226 G_CALLBACK(volume_down
), NULL
);
1228 g_signal_connect(keys
,
1230 G_CALLBACK(volume_toggle_mute
), NULL
);
1232 g_signal_connect(keys
,
1233 "mm_show_notification",
1234 G_CALLBACK(tray_icon2_create_tooltip
), NULL
);
1236 g_signal_connect_swapped(keys
,
1237 "mm_show_easy_command",
1238 G_CALLBACK(gmpc_easy_command_popup
),
1242 /* vim: set noexpandtab ts=4 sw=4 sts=4 tw=80: */