1/* 2 * This file Copyright (C) Mnemosyne LLC 3 * 4 * This file is licensed by the GPL version 2. Works owned by the 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 7 * This exemption does not extend to derived works not owned by 8 * the Transmission project. 9 * 10 * $Id: actions.c 13188 2012-02-03 15:51:36Z jordan $ 11 */ 12 13#include <string.h> 14 15#include <glib/gi18n.h> 16#include <gtk/gtk.h> 17 18#include <libtransmission/transmission.h> 19 20#include "actions.h" 21#include "conf.h" 22#include "tr-core.h" 23#include "tr-prefs.h" 24 25#include "icon-lock.h" 26#include "icon-logo-24.h" 27#include "icon-logo-48.h" 28#include "icon-ratio.h" 29#include "icon-turtle.h" 30#include "icon-utilities.h" 31 32#define UNUSED G_GNUC_UNUSED 33 34static TrCore * myCore = NULL; 35static GtkActionGroup * myGroup = NULL; 36 37static void 38action_cb( GtkAction * a, gpointer user_data ) 39{ 40 gtr_actions_handler( gtk_action_get_name( a ), user_data ); 41} 42 43static GtkRadioActionEntry sort_radio_entries[] = 44{ 45 { "sort-by-activity", NULL, N_( "Sort by _Activity" ), NULL, NULL, 0 }, 46 { "sort-by-name", NULL, N_( "Sort by _Name" ), NULL, NULL, 1 }, 47 { "sort-by-progress", NULL, N_( "Sort by _Progress" ), NULL, NULL, 2 }, 48 { "sort-by-queue", NULL, N_( "Sort by _Queue" ), NULL, NULL, 3 }, 49 { "sort-by-ratio", NULL, N_( "Sort by Rati_o" ), NULL, NULL, 4 }, 50 { "sort-by-state", NULL, N_( "Sort by Stat_e" ), NULL, NULL, 5 }, 51 { "sort-by-age", NULL, N_( "Sort by A_ge" ), NULL, NULL, 6 }, 52 { "sort-by-time-left", NULL, N_( "Sort by Time _Left" ), NULL, NULL, 7 }, 53 { "sort-by-size", NULL, N_( "Sort by Si_ze" ), NULL, NULL, 8 } 54}; 55 56static void 57sort_changed_cb( GtkAction * action UNUSED, 58 GtkRadioAction * current, 59 gpointer user_data UNUSED ) 60{ 61 const char * key = PREF_KEY_SORT_MODE; 62 const int i = gtk_radio_action_get_current_value( current ); 63 const char * val = sort_radio_entries[i].name; 64 65 gtr_core_set_pref( myCore, key, val ); 66} 67 68static GtkToggleActionEntry show_toggle_entries[] = 69{ 70 { "toggle-main-window", NULL, N_( "_Show Transmission" ), NULL, NULL, G_CALLBACK( action_cb ), TRUE }, 71 { "toggle-message-log", NULL, N_( "Message _Log" ), NULL, NULL, G_CALLBACK( action_cb ), FALSE } 72}; 73 74static void 75toggle_pref_cb( GtkToggleAction * action, 76 gpointer user_data UNUSED ) 77{ 78 const char * key = gtk_action_get_name( GTK_ACTION( action ) ); 79 const gboolean val = gtk_toggle_action_get_active( action ); 80 81 gtr_core_set_pref_bool( myCore, key, val ); 82} 83 84static GtkToggleActionEntry pref_toggle_entries[] = 85{ 86 { "alt-speed-enabled", NULL, N_( "Enable Alternative Speed _Limits" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE }, 87 { "compact-view", NULL, N_( "_Compact View" ), "<alt>C", NULL, G_CALLBACK( toggle_pref_cb ), FALSE }, 88 { "sort-reversed", NULL, N_( "Re_verse Sort Order" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE }, 89 { "show-filterbar", NULL, N_( "_Filterbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE }, 90 { "show-statusbar", NULL, N_( "_Statusbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE }, 91 { "show-toolbar", NULL, N_( "_Toolbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE } 92}; 93 94static GtkActionEntry entries[] = 95{ 96 { "file-menu", NULL, N_( "_File" ), NULL, NULL, NULL }, 97 { "torrent-menu", NULL, N_( "_Torrent" ), NULL, NULL, NULL }, 98 { "view-menu", NULL, N_( "_View" ), NULL, NULL, NULL }, 99 { "sort-menu", NULL, N_( "_Sort Torrents By" ), NULL, NULL, NULL }, 100 { "queue-menu", NULL, N_( "_Queue" ), NULL, NULL, NULL }, 101 { "edit-menu", NULL, N_( "_Edit" ), NULL, NULL, NULL }, 102 { "help-menu", NULL, N_( "_Help" ), NULL, NULL, NULL }, 103 { "copy-magnet-link-to-clipboard", GTK_STOCK_COPY, N_("Copy _Magnet Link to Clipboard" ), "", NULL, G_CALLBACK( action_cb ) }, 104 { "open-torrent-from-url", GTK_STOCK_OPEN, N_("Open _URL���" ), "<control>U", N_( "Open URL���" ), G_CALLBACK( action_cb ) }, 105 { "open-torrent-toolbar", GTK_STOCK_OPEN, NULL, NULL, N_( "Open a torrent" ), G_CALLBACK( action_cb ) }, 106 { "open-torrent-menu", GTK_STOCK_OPEN, NULL, NULL, N_( "Open a torrent" ), G_CALLBACK( action_cb ) }, 107 { "torrent-start", GTK_STOCK_MEDIA_PLAY, N_( "_Start" ), "<control>S", N_( "Start torrent" ), G_CALLBACK( action_cb ) }, 108 { "torrent-start-now", GTK_STOCK_MEDIA_PLAY, N_( "Start _Now" ), "<shift><control>S", N_( "Start torrent now" ), G_CALLBACK( action_cb ) }, 109 { "show-stats", NULL, N_( "_Statistics" ), NULL, NULL, G_CALLBACK( action_cb ) }, 110 { "donate", NULL, N_( "_Donate" ), NULL, NULL, G_CALLBACK( action_cb ) }, 111 { "torrent-verify", NULL, N_( "_Verify Local Data" ), "<control>V", NULL, G_CALLBACK( action_cb ) }, 112 { "torrent-stop", GTK_STOCK_MEDIA_PAUSE, N_( "_Pause" ), "<control>P", N_( "Pause torrent" ), G_CALLBACK( action_cb ) }, 113 { "pause-all-torrents", GTK_STOCK_MEDIA_PAUSE, N_( "_Pause All" ), NULL, N_( "Pause all torrents" ), G_CALLBACK( action_cb ) }, 114 { "start-all-torrents", GTK_STOCK_MEDIA_PLAY, N_( "_Start All" ), NULL, N_( "Start all torrents" ), G_CALLBACK( action_cb ) }, 115 { "relocate-torrent", NULL, N_("Set _Location���" ), NULL, NULL, G_CALLBACK( action_cb ) }, 116 { "remove-torrent", GTK_STOCK_REMOVE, NULL, "Delete", N_( "Remove torrent" ), G_CALLBACK( action_cb ) }, 117 { "delete-torrent", GTK_STOCK_DELETE, N_( "_Delete Files and Remove" ), "<shift>Delete", NULL, G_CALLBACK( action_cb ) }, 118 { "new-torrent", GTK_STOCK_NEW, N_( "_New���" ), NULL, N_( "Create a torrent" ), G_CALLBACK( action_cb ) }, 119 { "quit", GTK_STOCK_QUIT, N_( "_Quit" ), NULL, NULL, G_CALLBACK( action_cb ) }, 120 { "select-all", GTK_STOCK_SELECT_ALL, N_( "Select _All" ), "<control>A", NULL, G_CALLBACK( action_cb ) }, 121 { "deselect-all", NULL, N_( "Dese_lect All" ), "<shift><control>A", NULL, G_CALLBACK( action_cb ) }, 122 { "edit-preferences", GTK_STOCK_PREFERENCES, NULL, NULL, NULL, G_CALLBACK( action_cb ) }, 123 { "show-torrent-properties", GTK_STOCK_PROPERTIES, NULL, "<alt>Return", N_( "Torrent properties" ), G_CALLBACK( action_cb ) }, 124 { "open-torrent-folder", GTK_STOCK_OPEN, N_( "Open Fold_er" ), "<control>E", NULL, G_CALLBACK( action_cb ) }, 125 { "show-about-dialog", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK( action_cb ) }, 126 { "help", GTK_STOCK_HELP, N_( "_Contents" ), "F1", NULL, G_CALLBACK( action_cb ) }, 127 { "torrent-reannounce", GTK_STOCK_NETWORK, N_( "Ask Tracker for _More Peers" ), NULL, NULL, G_CALLBACK( action_cb ) }, 128 { "queue-move-top", GTK_STOCK_GOTO_TOP, N_( "Move to _Top" ), NULL, NULL, G_CALLBACK( action_cb ) }, 129 { "queue-move-up", GTK_STOCK_GO_UP, N_( "Move _Up" ), NULL, NULL, G_CALLBACK( action_cb ) }, 130 { "queue-move-down", GTK_STOCK_GO_DOWN, N_( "Move _Down" ), NULL, NULL, G_CALLBACK( action_cb ) }, 131 { "queue-move-bottom", GTK_STOCK_GOTO_BOTTOM, N_( "Move to _Bottom" ), NULL, NULL, G_CALLBACK( action_cb ) }, 132 { "present-main-window", NULL, N_( "Present Main Window" ), NULL, NULL, G_CALLBACK( action_cb ) } 133}; 134 135typedef struct 136{ 137 const guint8* raw; 138 const char * name; 139} 140BuiltinIconInfo; 141 142static const BuiltinIconInfo my_fallback_icons[] = 143{ 144 { tr_icon_logo_48, WINDOW_ICON }, 145 { tr_icon_logo_24, TRAY_ICON }, 146 { tr_icon_logo_48, NOTIFICATION_ICON }, 147 { tr_icon_lock, "transmission-lock" }, 148 { utilities_icon, "utilities" }, 149 { blue_turtle, "alt-speed-on" }, 150 { grey_turtle, "alt-speed-off" }, 151 { ratio_icon, "ratio" } 152}; 153 154static void 155register_my_icons( void ) 156{ 157 int i; 158 const int n = G_N_ELEMENTS( my_fallback_icons ); 159 GtkIconFactory * factory = gtk_icon_factory_new( ); 160 GtkIconTheme * theme = gtk_icon_theme_get_default( ); 161 162 gtk_icon_factory_add_default( factory ); 163 164 for( i = 0; i < n; ++i ) 165 { 166 const char * name = my_fallback_icons[i].name; 167 168 if( !gtk_icon_theme_has_icon( theme, name ) ) 169 { 170 int width; 171 GdkPixbuf * p; 172 GtkIconSet * icon_set; 173 174 p = 175 gdk_pixbuf_new_from_inline( -1, my_fallback_icons[i].raw, 176 FALSE, 177 NULL ); 178 width = gdk_pixbuf_get_width( p ); 179 icon_set = gtk_icon_set_new_from_pixbuf( p ); 180 gtk_icon_theme_add_builtin_icon( name, width, p ); 181 gtk_icon_factory_add( factory, name, icon_set ); 182 183 g_object_unref( p ); 184 gtk_icon_set_unref( icon_set ); 185 } 186 } 187 188 g_object_unref ( G_OBJECT ( factory ) ); 189} 190 191static GtkUIManager * myUIManager = NULL; 192 193void 194gtr_actions_set_core( TrCore * core ) 195{ 196 myCore = core; 197} 198 199void 200gtr_actions_init( GtkUIManager * ui_manager, gpointer callback_user_data ) 201{ 202 int i, n; 203 int active; 204 const char * match; 205 const int n_entries = G_N_ELEMENTS( entries ); 206 GtkActionGroup * action_group; 207 208 myUIManager = ui_manager; 209 210 register_my_icons( ); 211 212 action_group = myGroup = gtk_action_group_new( "Actions" ); 213 gtk_action_group_set_translation_domain( action_group, NULL ); 214 215 216 match = gtr_pref_string_get( PREF_KEY_SORT_MODE ); 217 for( i = 0, n = G_N_ELEMENTS( sort_radio_entries ), active = -1; 218 active == -1 && i < n; ++i ) 219 if( !strcmp( sort_radio_entries[i].name, match ) ) 220 active = i; 221 222 gtk_action_group_add_radio_actions( action_group, 223 sort_radio_entries, 224 G_N_ELEMENTS( sort_radio_entries ), 225 active, 226 G_CALLBACK( sort_changed_cb ), 227 NULL ); 228 229 gtk_action_group_add_toggle_actions( action_group, 230 show_toggle_entries, 231 G_N_ELEMENTS( show_toggle_entries ), 232 callback_user_data ); 233 234 for( i = 0, n = G_N_ELEMENTS( pref_toggle_entries ); i < n; ++i ) 235 pref_toggle_entries[i].is_active = 236 gtr_pref_flag_get( pref_toggle_entries[i].name ); 237 238 gtk_action_group_add_toggle_actions( action_group, 239 pref_toggle_entries, 240 G_N_ELEMENTS( pref_toggle_entries ), 241 callback_user_data ); 242 243 gtk_action_group_add_actions( action_group, 244 entries, n_entries, 245 callback_user_data ); 246 247 gtk_ui_manager_insert_action_group( ui_manager, action_group, 0 ); 248 g_object_unref ( G_OBJECT( action_group ) ); 249} 250 251/**** 252***** 253****/ 254 255static GHashTable * key_to_action = NULL; 256 257static void 258ensure_action_map_loaded( GtkUIManager * uim ) 259{ 260 GList * l; 261 262 if( key_to_action != NULL ) 263 return; 264 265 key_to_action = 266 g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL ); 267 268 for( l = gtk_ui_manager_get_action_groups( uim ); l != NULL; 269 l = l->next ) 270 { 271 GtkActionGroup * action_group = GTK_ACTION_GROUP( l->data ); 272 GList * ait, *actions = gtk_action_group_list_actions( 273 action_group ); 274 for( ait = actions; ait != NULL; ait = ait->next ) 275 { 276 GtkAction * action = GTK_ACTION( ait->data ); 277 const char * name = gtk_action_get_name( action ); 278 g_hash_table_insert( key_to_action, g_strdup( name ), action ); 279 } 280 g_list_free( actions ); 281 } 282} 283 284static GtkAction* 285get_action( const char* name ) 286{ 287 ensure_action_map_loaded( myUIManager ); 288 return ( GtkAction* ) g_hash_table_lookup( key_to_action, name ); 289} 290 291void 292gtr_action_activate( const char * name ) 293{ 294 GtkAction * action = get_action( name ); 295 296 g_assert( action != NULL ); 297 gtk_action_activate( action ); 298} 299 300void 301gtr_action_set_sensitive( const char * name, gboolean b ) 302{ 303 GtkAction * action = get_action( name ); 304 305 g_assert( action != NULL ); 306 g_object_set( action, "sensitive", b, NULL ); 307} 308 309void 310gtr_action_set_important( const char * name, gboolean b ) 311{ 312 GtkAction * action = get_action( name ); 313 314 g_assert( action != NULL ); 315 g_object_set( action, "is-important", b, NULL ); 316} 317 318void 319gtr_action_set_toggled( const char * name, gboolean b ) 320{ 321 GtkAction * action = get_action( name ); 322 323 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), b ); 324} 325 326GtkWidget* 327gtr_action_get_widget( const char * path ) 328{ 329 return gtk_ui_manager_get_widget( myUIManager, path ); 330} 331 332