source: trunk/gtk/util.c @ 5605

Last change on this file since 5605 was 5605, checked in by charles, 14 years ago

(gtk) #307: inhibit/prevent hibernation when downloading

  • Property svn:keywords set to Date Rev Author Id
File size: 14.7 KB
Line 
1/******************************************************************************
2 * $Id: util.c 5605 2008-04-13 02:56:26Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <stdarg.h>
26#include <stdlib.h> /* free() */
27#include <string.h> /* strcmp() */
28
29#include <gtk/gtk.h>
30#include <glib/gi18n.h>
31#include <glib/gstdio.h> /* g_unlink() */
32#ifdef HAVE_GIO
33#include <gio/gio.h> /* g_file_trash() */
34#endif
35#ifdef HAVE_DBUS_GLIB
36#include <dbus/dbus-glib.h>
37#endif
38
39#include <libevent/evhttp.h>
40
41#include <libtransmission/transmission.h> /* TR_RATIO_NA, TR_RATIO_INF */
42#include <libtransmission/utils.h> /* tr_inf */
43
44#include "conf.h"
45#include "tr-prefs.h"
46#include "util.h"
47
48int
49tr_strcmp( const char * a, const char * b )
50{
51    if( a && b ) return strcmp( a, b );
52    if( a ) return 1;
53    if( b ) return -1;
54    return 0;
55}
56
57char*
58tr_strlratio( char * buf, double ratio, size_t buflen )
59{
60    if( (int)ratio == TR_RATIO_NA )
61        g_strlcpy( buf, _( "None" ), buflen );
62    else if( (int)ratio == TR_RATIO_INF )
63        g_strlcpy( buf, "\xE2\x88\x9E", buflen );
64    else if( ratio < 10.0 )
65        g_snprintf( buf, buflen, "%'.2f", ratio );
66    else if( ratio < 100.0 )
67        g_snprintf( buf, buflen, "%'.1f", ratio );
68    else
69        g_snprintf( buf, buflen, "%'.0f", ratio );
70    return buf;
71}
72
73#define KILOBYTE_FACTOR 1024.0
74#define MEGABYTE_FACTOR (1024.0 * 1024.0)
75#define GIGABYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
76
77char*
78tr_strlsize( char * buf, guint64 size, size_t buflen )
79{
80    if( !size )
81        g_strlcpy( buf, _( "None" ), buflen );
82#if GLIB_CHECK_VERSION(2,16,0)
83    else{ 
84        char * tmp = g_format_size_for_display( size );
85        g_strlcpy( buf, tmp, buflen );
86        g_free( tmp );
87    }
88#else
89    else if( size < (guint64)KILOBYTE_FACTOR )
90        g_snprintf( buf, buflen, ngettext("%'u byte", "%'u bytes", (guint)size), (guint)size );
91    else {
92        gdouble displayed_size;
93        if (size < (guint64)MEGABYTE_FACTOR) {
94            displayed_size = (gdouble) size / KILOBYTE_FACTOR;
95            g_snprintf( buf, buflen, _("%'.1f KB"), displayed_size );
96        } else if (size < (guint64)GIGABYTE_FACTOR) {
97            displayed_size = (gdouble) size / MEGABYTE_FACTOR;
98            g_snprintf( buf, buflen, _("%'.1f MB"), displayed_size );
99        } else {
100            displayed_size = (gdouble) size / GIGABYTE_FACTOR;
101            g_snprintf( buf, buflen, _("%'.1f GB"), displayed_size );
102        }
103    }
104#endif
105    return buf;
106}
107
108char*
109tr_strlspeed( char * buf, double kb_sec, size_t buflen )
110{
111    const double speed = kb_sec;
112
113    if ( speed < 1000.0 ) /* 0.0 KB to 999.9 KB */
114        g_snprintf( buf, buflen, _( "%'.1f KB/s" ), speed );
115    else if( speed < 102400.0 ) /* 0.98 MB to 99.99 MB */
116        g_snprintf( buf, buflen, _( "%'.2f MB/s" ), (speed/1024) );
117    else if( speed < 1024000.0 ) /* 100.0 MB to 999.9 MB */
118        g_snprintf( buf, buflen, _( "%'.1f MB/s" ), (speed/1024) );
119    else /* insane speeds */
120        g_snprintf( buf, buflen, _( "%'.2f GB/s" ), (speed/1048576) );
121
122    return buf;
123}
124
125char*
126tr_strltime( char * buf, int seconds, size_t buflen )
127{
128    int hours;
129    int days;
130
131    if( seconds < 0 )
132        seconds = 0;
133
134    if( seconds < 60 )
135    {
136        g_snprintf( buf, buflen, ngettext( "%'d second", "%'d seconds", (int)seconds ), (int) seconds );
137        return buf;
138    }
139
140    if( seconds < ( 60 * 60 ) )
141    {
142        const int minutes = ( seconds + 30 ) / 60;
143        g_snprintf( buf, buflen, ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
144        return buf;
145    }
146
147    hours = seconds / ( 60 * 60 );
148
149    if( seconds < ( 60 * 60 * 4 ) )
150    {
151        char h[64];
152        char m[64];
153
154        const int minutes = ( seconds - hours * 60 * 60 + 30 ) / 60;
155
156        g_snprintf( h, sizeof(h), ngettext( "%'d hour", "%'d hours", hours ), hours );
157        g_snprintf( m, sizeof(m), ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
158        g_snprintf( buf, buflen, "%s, %s", h, m );
159        return buf;
160    }
161
162    if( hours < 24 )
163    {
164        g_snprintf( buf, buflen, ngettext( "%'d hour", "%'d hours", hours ), hours );
165        return buf;
166    }
167
168    days = seconds / ( 60 * 60 * 24 );
169    g_snprintf( buf, buflen, ngettext( "%'d day", "%'d days", days ), days );
170    return buf;
171}
172
173
174char *
175rfc822date (guint64 epoch_msec)
176{
177    const time_t secs = epoch_msec / 1000;
178    const struct tm tm = *localtime (&secs);
179    char buf[128];
180    strftime( buf, sizeof(buf), "%a, %d %b %Y %T %Z", &tm );
181    return g_locale_to_utf8( buf, -1, NULL, NULL, NULL );
182}
183
184gboolean
185mkdir_p( const char * path, mode_t mode )
186{
187#if GLIB_CHECK_VERSION( 2, 8, 0)
188    return !g_mkdir_with_parents( path, mode );
189#else
190    return !tr_mkdirp( path, mode );
191#endif
192}
193
194GSList *
195dupstrlist( GSList * l )
196{
197    GSList * ret = NULL;
198    for( ; l!=NULL; l=l->next )
199        ret = g_slist_prepend( ret, g_strdup( l->data ) );
200    return g_slist_reverse( ret );
201}
202
203char *
204joinstrlist(GSList *list, char *sep)
205{
206  GSList *l;
207  GString *gstr = g_string_new (NULL);
208  for (l=list; l!=NULL; l=l->next) {
209    g_string_append (gstr, (char*)l->data);
210    if (l->next != NULL)
211      g_string_append (gstr, (sep));
212  }
213  return g_string_free (gstr, FALSE);
214}
215
216void
217freestrlist(GSList *list)
218{
219  g_slist_foreach (list, (GFunc)g_free, NULL);
220  g_slist_free (list);
221}
222
223char *
224decode_uri( const char * uri )
225{
226    char * filename = evhttp_decode_uri( uri );
227    char * ret = g_strdup( filename );
228    free( filename );
229    return ret;
230}
231
232GSList *
233checkfilenames( int argc, char **argv )
234{
235    int i;
236    GSList * ret = NULL;
237    char * pwd = g_get_current_dir( );
238
239    for( i=0; i<argc; ++i )
240    {
241        char * filename = g_path_is_absolute( argv[i] )
242            ? g_strdup ( argv[i] )
243            : g_build_filename( pwd, argv[i], NULL );
244
245        if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
246            ret = g_slist_prepend( ret, filename );
247        else
248            g_free( filename );
249    }
250
251    g_free( pwd );
252    return g_slist_reverse( ret );
253}
254
255char *
256getdownloaddir( void )
257{
258    static char * wd = NULL;
259    char * dir = pref_string_get( PREF_KEY_DIR_DEFAULT );
260    if ( dir == NULL ) {
261        if( wd == NULL )
262            wd = g_get_current_dir();
263        dir = g_strdup( wd );
264    }
265    return dir;
266}
267
268/**
269 * don't use more than 50% the height of the screen, nor 80% the width.
270 * but don't be too small, either -- set the minimums to 500 x 300
271 */
272void
273sizingmagic( GtkWindow         * wind,
274             GtkScrolledWindow * scroll,
275             GtkPolicyType       hscroll,
276             GtkPolicyType       vscroll )
277{
278    int width;
279    int height;
280    GtkRequisition req;
281
282    GdkScreen * screen = gtk_widget_get_screen( GTK_WIDGET( wind ) );
283
284    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER,
285                                            GTK_POLICY_NEVER );
286
287    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
288    req.height = MAX( req.height, 300 );
289    height = MIN( req.height, gdk_screen_get_height( screen ) / 5 * 4 );
290
291    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER, vscroll );
292    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
293    req.width = MAX( req.width, 500 );
294    width = MIN( req.width, gdk_screen_get_width( screen ) / 2 );
295
296    gtk_window_set_default_size( wind, width, height );
297    gtk_scrolled_window_set_policy( scroll, hscroll, vscroll );
298}
299
300static void
301onErrorResponse(GtkWidget * dialog, int resp UNUSED, gpointer glist)
302{
303    GSList * list = glist;
304    if( list )
305    {
306        callbackfunc_t func = list->data;
307        gpointer user_data = list->next->data;
308        func( user_data );
309        g_slist_free( list );
310    }
311
312    gtk_widget_destroy( dialog );
313}
314
315static GtkWidget *
316verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
317              const char * format, va_list ap )
318{
319  GtkWidget *dialog;
320  char *msg;
321  GSList *funcdata = NULL;
322
323  msg = g_strdup_vprintf(format, ap);
324
325  if(NULL == wind)
326    dialog = gtk_message_dialog_new(
327      NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
328  else
329    dialog = gtk_message_dialog_new(wind,
330      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
331      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
332
333  if( func ) {
334    funcdata = g_slist_append( funcdata, (gpointer)func );
335    funcdata = g_slist_append( funcdata, data );
336  }
337  g_signal_connect(dialog, "response", G_CALLBACK(onErrorResponse), funcdata);
338  g_free(msg);
339
340  return dialog;
341}
342
343void
344errmsg( GtkWindow * wind, const char * format, ... )
345{
346    GtkWidget * dialog;
347    va_list     ap;
348
349    va_start( ap, format );
350    dialog = verrmsg_full( wind, NULL, NULL, format, ap );
351    va_end( ap );
352
353    if( NULL != wind && !GTK_WIDGET_MAPPED( GTK_WIDGET( wind ) ) )
354    {
355        g_signal_connect_swapped( wind, "map",
356                                  G_CALLBACK( gtk_widget_show ), dialog );
357    }
358    else
359    {
360        gtk_widget_show( dialog );
361    }
362}
363
364GtkWidget *
365errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
366             const char * format, ... )
367{
368    GtkWidget * dialog;
369    va_list     ap;
370
371    va_start( ap, format );
372    dialog = verrmsg_full( wind, func, data, format, ap );
373    va_end( ap );
374
375    return dialog;
376}
377
378typedef void (PopupFunc)(GtkWidget*, GdkEventButton*); 
379
380/* pop up the context menu if a user right-clicks.
381   if the row they right-click on isn't selected, select it. */
382
383gboolean
384on_tree_view_button_pressed (GtkWidget       * view,
385                             GdkEventButton  * event,
386                             gpointer          func)
387{
388  GtkTreeView * tv = GTK_TREE_VIEW( view );
389
390  if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3)
391  {
392    GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
393    GtkTreePath *path;
394    if (gtk_tree_view_get_path_at_pos (tv,
395                                       (gint) event->x,
396                                       (gint) event->y,
397                                       &path, NULL, NULL, NULL))
398    {
399      if (!gtk_tree_selection_path_is_selected (selection, path))
400      {
401        gtk_tree_selection_unselect_all (selection);
402        gtk_tree_selection_select_path (selection, path);
403      }
404      gtk_tree_path_free(path);
405    }
406   
407    ((PopupFunc*)func)(view, event);
408
409    return TRUE;
410  }
411
412  return FALSE;
413}
414
415gpointer
416tr_object_ref_sink( gpointer object )
417{
418#if GLIB_CHECK_VERSION(2,10,0)
419    g_object_ref_sink( object );
420#else
421    g_object_ref( object );
422    gtk_object_sink( GTK_OBJECT( object ) );
423#endif
424    return object;
425}
426
427void
428tr_file_trash_or_unlink( const char * filename )
429{
430    if( filename && *filename )
431    {
432        gboolean trashed = FALSE;
433#ifdef HAVE_GIO
434        GError * err = NULL;
435        GFile * file = g_file_new_for_path( filename );
436        trashed = g_file_trash( file, NULL, &err );
437        g_object_unref( G_OBJECT( file ) );
438#endif
439        if( !trashed )
440            g_unlink( filename );
441    }
442}
443
444void
445gtr_open_file( const char * path )
446{
447    if( path )
448    {
449        gboolean opened = FALSE;
450#ifdef HAVE_GIO
451        if( !opened )
452        {
453            GFile * file = g_file_new_for_path( path );
454            char * uri = g_file_get_uri( file );
455            opened = g_app_info_launch_default_for_uri( uri, NULL, NULL );
456            g_free( uri );
457            g_object_unref( G_OBJECT( file ) );
458        }
459#endif
460        if( !opened )
461        {
462            char * argv[] = { "xdg-open", (char*)path, NULL };
463            g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
464                           NULL, NULL, NULL, NULL );
465        }
466    }
467}
468
469#ifdef HAVE_DBUS_GLIB
470static DBusGProxy*
471get_hibernation_inhibit_proxy( void )
472{
473    GError * error = NULL;
474    DBusGConnection * conn;
475
476    conn = dbus_g_bus_get( DBUS_BUS_SESSION, &error );
477    if( error )
478    {
479        g_warning ("DBUS cannot connect : %s", error->message);
480        g_error_free (error);
481        return NULL;
482    }
483
484    return dbus_g_proxy_new_for_name (conn,
485               "org.freedesktop.PowerManagement",
486               "/org/freedesktop/PowerManagement/Inhibit",
487               "org.freedesktop.PowerManagement.Inhibit" );
488}
489#endif
490
491guint
492gtr_inhibit_hibernation( void )
493{
494    guint inhibit_cookie = 0;
495#ifdef HAVE_DBUS_GLIB
496    DBusGProxy * proxy = get_hibernation_inhibit_proxy( );
497    if( proxy )
498    {
499        GError * error = NULL;
500        const char * application = _( "Transmission Bittorrent Client" );
501        const char * reason = _( "BitTorrent Activity" );
502        gboolean success = dbus_g_proxy_call( proxy, "Inhibit", &error,
503                                              G_TYPE_STRING, application,
504                                              G_TYPE_STRING, reason,
505                                              G_TYPE_INVALID,
506                                              G_TYPE_UINT, &inhibit_cookie,
507                                              G_TYPE_INVALID );
508        if( success )
509            tr_inf( _( "Desktop hibernation disabled while Transmission is running" ) );
510        else {
511            tr_err( _( "Couldn't disable desktop hibernation: %s." ), error->message );
512            g_error_free( error );
513        }
514
515        g_object_unref( G_OBJECT( proxy ) );
516    }
517#endif
518    return inhibit_cookie;
519}
520
521void
522gtr_uninhibit_hibernation( guint inhibit_cookie )
523{
524#ifdef HAVE_DBUS_GLIB
525    DBusGProxy * proxy = get_hibernation_inhibit_proxy( );
526    if( proxy )
527    {
528        GError * error = NULL;
529        gboolean success = dbus_g_proxy_call( proxy, "UnInhibit", &error,
530                                              G_TYPE_UINT, inhibit_cookie,
531                                              G_TYPE_INVALID,
532                                              G_TYPE_INVALID );
533        if( !success ) {
534            g_warning( "Couldn't uninhibit the system from suspending: %s.", error->message );
535            g_error_free( error );
536        }
537
538        g_object_unref( G_OBJECT( proxy ) );
539    }
540#endif
541}
Note: See TracBrowser for help on using the repository browser.