source: trunk/gtk/util.c @ 8846

Last change on this file since 8846 was 8846, checked in by charles, 13 years ago

remove some unused utilities. better commenting on the utils that remain.

  • Property svn:keywords set to Date Rev Author Id
File size: 16.8 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.com>
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:$
11 */
12
13#include <ctype.h> /* isxdigit() */
14#include <errno.h>
15#include <stdarg.h>
16#include <stdlib.h> /* free() */
17#include <string.h> /* strcmp() */
18
19#include <gtk/gtk.h>
20#include <glib/gi18n.h>
21#include <glib/gstdio.h> /* g_unlink() */
22#ifdef HAVE_GIO
23 #include <gio/gio.h> /* g_file_trash() */
24#endif
25#ifdef HAVE_DBUS_GLIB
26 #include <dbus/dbus-glib.h>
27#endif
28
29#include <evhttp.h>
30
31#include <libtransmission/transmission.h> /* TR_RATIO_NA, TR_RATIO_INF */
32#include <libtransmission/utils.h> /* tr_inf */
33#include <libtransmission/version.h> /* tr_inf */
34
35#include "conf.h"
36#include "hig.h"
37#include "tr-prefs.h"
38#include "util.h"
39
40char*
41tr_strlratio( char * buf, double ratio, size_t buflen )
42{
43    return tr_strratio( buf, buflen, ratio, "\xE2\x88\x9E" );
44}
45
46#define KILOBYTE_FACTOR 1024.0
47#define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
48#define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
49
50char*
51tr_strlsize( char *  buf,
52             guint64 size,
53             size_t  buflen )
54{
55    if( !size )
56        g_strlcpy( buf, _( "None" ), buflen );
57#if GLIB_CHECK_VERSION( 2, 16, 0 )
58    else
59    {
60        char * tmp = g_format_size_for_display( size );
61        g_strlcpy( buf, tmp, buflen );
62        g_free( tmp );
63    }
64#else
65    else if( size < (guint64)KILOBYTE_FACTOR )
66        g_snprintf( buf, buflen,
67                    ngettext( "%'u byte", "%'u bytes",
68                              (guint)size ), (guint)size );
69    else
70    {
71        gdouble displayed_size;
72        if( size < (guint64)MEGABYTE_FACTOR )
73        {
74            displayed_size = (gdouble) size / KILOBYTE_FACTOR;
75            g_snprintf( buf, buflen, _( "%'.1f KB" ), displayed_size );
76        }
77        else if( size < (guint64)GIGABYTE_FACTOR )
78        {
79            displayed_size = (gdouble) size / MEGABYTE_FACTOR;
80            g_snprintf( buf, buflen, _( "%'.1f MB" ), displayed_size );
81        }
82        else
83        {
84            displayed_size = (gdouble) size / GIGABYTE_FACTOR;
85            g_snprintf( buf, buflen, _( "%'.1f GB" ), displayed_size );
86        }
87    }
88#endif
89    return buf;
90}
91
92char*
93tr_strlspeed( char * buf,
94              double kb_sec,
95              size_t buflen )
96{
97    const double speed = kb_sec;
98
99    if( speed < 1000.0 )  /* 0.0 KB to 999.9 KB */
100        g_snprintf( buf, buflen, _( "%'.1f KB/s" ), speed );
101    else if( speed < 102400.0 ) /* 0.98 MB to 99.99 MB */
102        g_snprintf( buf, buflen, _( "%'.2f MB/s" ), ( speed / KILOBYTE_FACTOR ) );
103    else if( speed < 1024000.0 ) /* 100.0 MB to 999.9 MB */
104        g_snprintf( buf, buflen, _( "%'.1f MB/s" ), ( speed / MEGABYTE_FACTOR ) );
105    else /* insane speeds */
106        g_snprintf( buf, buflen, _( "%'.2f GB/s" ), ( speed / GIGABYTE_FACTOR ) );
107
108    return buf;
109}
110
111char*
112tr_strltime( char * buf,
113             int    seconds,
114             size_t buflen )
115{
116    int  days, hours, minutes;
117    char d[128], h[128], m[128], s[128];
118
119    if( seconds < 0 )
120        seconds = 0;
121
122    days = seconds / 86400;
123    hours = ( seconds % 86400 ) / 3600;
124    minutes = ( seconds % 3600 ) / 60;
125    seconds = ( seconds % 3600 ) % 60;
126
127    g_snprintf( d, sizeof( d ), ngettext( "%'d day", "%'d days",
128                                          days ), days );
129    g_snprintf( h, sizeof( h ), ngettext( "%'d hour", "%'d hours",
130                                          hours ), hours );
131    g_snprintf( m, sizeof( m ),
132                ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
133    g_snprintf( s, sizeof( s ),
134                ngettext( "%'d second", "%'d seconds", seconds ), seconds );
135
136    if( days )
137    {
138        if( days >= 4 || !hours )
139        {
140            g_strlcpy( buf, d, buflen );
141        }
142        else
143        {
144            g_snprintf( buf, buflen, "%s, %s", d, h );
145        }
146    }
147    else if( hours )
148    {
149        if( hours >= 4 || !minutes )
150        {
151            g_strlcpy( buf, h, buflen );
152        }
153        else
154        {
155            g_snprintf( buf, buflen, "%s, %s", h, m );
156        }
157    }
158    else if( minutes )
159    {
160        if( minutes >= 4 || !seconds )
161        {
162            g_strlcpy( buf, m, buflen );
163        }
164        else
165        {
166            g_snprintf( buf, buflen, "%s, %s", m, s );
167        }
168    }
169    else
170    {
171        g_strlcpy( buf, s, buflen );
172    }
173
174    return buf;
175}
176
177char *
178gtr_localtime( time_t time )
179{
180    const struct tm tm = *localtime( &time );
181    char            buf[256], *eoln;
182
183    g_strlcpy( buf, asctime( &tm ), sizeof( buf ) );
184    if( ( eoln = strchr( buf, '\n' ) ) )
185        *eoln = '\0';
186
187    return g_locale_to_utf8( buf, -1, NULL, NULL, NULL );
188}
189
190char *
191gtr_localtime2( char * buf, time_t time, size_t buflen )
192{
193    char * tmp = gtr_localtime( time );
194    g_strlcpy( buf, tmp, buflen );
195    g_free( tmp );
196    return buf;
197}
198
199int
200mkdir_p( const char * path,
201         mode_t       mode )
202{
203#if GLIB_CHECK_VERSION( 2, 8, 0 )
204    return !g_mkdir_with_parents( path, mode );
205#else
206    return !tr_mkdirp( path, mode );
207#endif
208}
209
210GSList *
211dupstrlist( GSList * l )
212{
213    GSList * ret = NULL;
214
215    for( ; l != NULL; l = l->next )
216        ret = g_slist_prepend( ret, g_strdup( l->data ) );
217    return g_slist_reverse( ret );
218}
219
220char *
221joinstrlist( GSList *list,
222             char *  sep )
223{
224    GSList * l;
225    GString *gstr = g_string_new ( NULL );
226
227    for( l = list; l != NULL; l = l->next )
228    {
229        g_string_append ( gstr, (char*)l->data );
230        if( l->next != NULL )
231            g_string_append ( gstr, ( sep ) );
232    }
233    return g_string_free ( gstr, FALSE );
234}
235
236void
237freestrlist( GSList *list )
238{
239    g_slist_foreach ( list, (GFunc)g_free, NULL );
240    g_slist_free ( list );
241}
242
243char *
244decode_uri( const char * uri )
245{
246    gboolean in_query = FALSE;
247    char *   ret = g_new( char, strlen( uri ) + 1 );
248    char *   out = ret;
249
250    for( ; uri && *uri; )
251    {
252        char ch = *uri;
253        if( ch == '?' )
254            in_query = TRUE;
255        else if( ch == '+' && in_query )
256            ch = ' ';
257        else if( ch == '%' && isxdigit( (unsigned char)uri[1] )
258               && isxdigit( (unsigned char)uri[2] ) )
259        {
260            char buf[3] = { uri[1], uri[2], '\0' };
261            ch = (char) g_ascii_strtoull( buf, NULL, 16 );
262            uri += 2;
263        }
264
265        ++uri;
266        *out++ = ch;
267    }
268
269    *out = '\0';
270    return ret;
271}
272
273GSList *
274checkfilenames( int    argc,
275                char **argv )
276{
277    int      i;
278    GSList * ret = NULL;
279    char *   pwd = g_get_current_dir( );
280
281    for( i = 0; i < argc; ++i )
282    {
283        char * filename = g_path_is_absolute( argv[i] )
284                          ? g_strdup ( argv[i] )
285                          : g_build_filename( pwd, argv[i], NULL );
286
287        if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
288            ret = g_slist_prepend( ret, filename );
289        else
290            g_free( filename );
291    }
292
293    g_free( pwd );
294    return g_slist_reverse( ret );
295}
296
297void
298addTorrentErrorDialog( GtkWidget *  child,
299                       int          err,
300                       const char * filename )
301{
302    GtkWidget *  w;
303    GtkWidget *  win;
304    const char * fmt;
305    char *       secondary;
306
307    switch( err )
308    {
309        case TR_EINVALID:
310            fmt = _( "The torrent file \"%s\" contains invalid data." );
311            break;
312
313        case TR_EDUPLICATE:
314            fmt = _( "The torrent file \"%s\" is already in use." ); break;
315
316        default:
317            fmt = _(
318                "The torrent file \"%s\" encountered an unknown error." );
319            break;
320    }
321    secondary = g_strdup_printf( fmt, filename );
322    win = ( !child || GTK_IS_WINDOW( child ) )
323          ? child
324          : gtk_widget_get_ancestor( child ? GTK_WIDGET(
325                                         child ) : NULL, GTK_TYPE_WINDOW );
326    w = gtk_message_dialog_new( GTK_WINDOW( win ),
327                               GTK_DIALOG_DESTROY_WITH_PARENT,
328                               GTK_MESSAGE_ERROR,
329                               GTK_BUTTONS_CLOSE,
330                               "%s", _( "Error opening torrent" ) );
331    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ),
332                                              "%s", secondary );
333    g_signal_connect_swapped( w, "response",
334                              G_CALLBACK( gtk_widget_destroy ), w );
335    gtk_widget_show_all( w );
336    g_free( secondary );
337}
338typedef void ( PopupFunc )( GtkWidget*, GdkEventButton* );
339
340/* pop up the context menu if a user right-clicks.
341   if the row they right-click on isn't selected, select it. */
342
343gboolean
344on_tree_view_button_pressed( GtkWidget *      view,
345                             GdkEventButton * event,
346                             gpointer         func )
347{
348    GtkTreeView * tv = GTK_TREE_VIEW( view );
349
350    if( event->type == GDK_BUTTON_PRESS  &&  event->button == 3 )
351    {
352        GtkTreeSelection * selection = gtk_tree_view_get_selection( tv );
353        GtkTreePath *      path;
354        if( gtk_tree_view_get_path_at_pos ( tv,
355                                            (gint) event->x,
356                                            (gint) event->y,
357                                            &path, NULL, NULL, NULL ) )
358        {
359            if( !gtk_tree_selection_path_is_selected ( selection, path ) )
360            {
361                gtk_tree_selection_unselect_all ( selection );
362                gtk_tree_selection_select_path ( selection, path );
363            }
364            gtk_tree_path_free( path );
365        }
366
367        ( (PopupFunc*)func )( view, event );
368
369        return TRUE;
370    }
371
372    return FALSE;
373}
374
375/* if the user clicked in an empty area of the list,
376 * clear all the selections. */
377gboolean
378on_tree_view_button_released( GtkWidget *      view,
379                              GdkEventButton * event,
380                              gpointer         unused UNUSED )
381{
382    GtkTreeView * tv = GTK_TREE_VIEW( view );
383
384    if( !gtk_tree_view_get_path_at_pos ( tv,
385                                         (gint) event->x,
386                                         (gint) event->y,
387                                         NULL, NULL, NULL, NULL ) )
388    {
389        GtkTreeSelection * selection = gtk_tree_view_get_selection( tv );
390        gtk_tree_selection_unselect_all ( selection );
391        return TRUE;
392    }
393
394    return FALSE;
395}
396
397gpointer
398tr_object_ref_sink( gpointer object )
399{
400#if GLIB_CHECK_VERSION( 2, 10, 0 )
401    g_object_ref_sink( object );
402#else
403    g_object_ref( object );
404    gtk_object_sink( GTK_OBJECT( object ) );
405#endif
406    return object;
407}
408
409int
410tr_file_trash_or_remove( const char * filename )
411{
412    if( filename && *filename )
413    {
414        gboolean trashed = FALSE;
415#ifdef HAVE_GIO
416        GError * err = NULL;
417        GFile *  file = g_file_new_for_path( filename );
418        trashed = g_file_trash( file, NULL, &err );
419        if( err )
420            g_message( "Unable to trash file \"%s\": %s", filename, err->message );
421        g_clear_error( &err );
422        g_object_unref( G_OBJECT( file ) );
423#endif
424
425        if( !trashed && g_remove( filename ) )
426        {
427            const int err = errno;
428            g_message( "Unable to remove file \"%s\": %s", filename, g_strerror( err ) );
429        }
430    }
431
432    return 0;
433}
434
435char*
436gtr_get_help_url( void )
437{
438    const char * fmt = "http://www.transmissionbt.com/help/gtk/%d.%dx";
439    int          major, minor;
440
441    sscanf( SHORT_VERSION_STRING, "%d.%d", &major, &minor );
442    return g_strdup_printf( fmt, major, minor / 10 );
443}
444
445void
446gtr_open_file( const char * path )
447{
448    if( path )
449    {
450        gboolean opened = FALSE;
451#ifdef HAVE_GIO
452        if( !opened )
453        {
454            GFile * file = g_file_new_for_path( path );
455            char *  uri = g_file_get_uri( file );
456            opened = g_app_info_launch_default_for_uri( uri, NULL, NULL );
457            g_free( uri );
458            g_object_unref( G_OBJECT( file ) );
459        }
460#endif
461        if( !opened )
462        {
463            char * argv[] = { (char*)"xdg-open", (char*)path, NULL };
464            g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
465                           NULL, NULL, NULL, NULL );
466        }
467    }
468}
469
470#define VALUE_SERVICE_NAME        "com.transmissionbt.Transmission"
471#define VALUE_SERVICE_OBJECT_PATH "/com/transmissionbt/Transmission"
472#define VALUE_SERVICE_INTERFACE   "com.transmissionbt.Transmission"
473
474gboolean
475gtr_dbus_add_torrent( const char * filename )
476{
477    /* FIXME: why is this static? */
478    static gboolean success = FALSE;
479#ifdef HAVE_DBUS_GLIB
480    DBusGProxy * proxy = NULL;
481    GError * err = NULL;
482    DBusGConnection * conn;
483    char * file_contents;
484    gsize file_length;
485
486    if( g_file_get_contents( filename, &file_contents, &file_length, NULL ) )
487    {
488        char * b64 = tr_base64_encode( file_contents, file_length, NULL );
489        if(( conn = dbus_g_bus_get( DBUS_BUS_SESSION, &err )))
490            proxy = dbus_g_proxy_new_for_name (conn, VALUE_SERVICE_NAME,
491                                                     VALUE_SERVICE_OBJECT_PATH,
492                                                     VALUE_SERVICE_INTERFACE );
493        else if( err )
494           g_message( "err: %s", err->message );
495        if( proxy )
496            dbus_g_proxy_call( proxy, "AddMetainfo", &err,
497                               G_TYPE_STRING, b64,
498                               G_TYPE_INVALID,
499                               G_TYPE_BOOLEAN, &success,
500                               G_TYPE_INVALID );
501        if( err )
502           g_message( "err: %s", err->message );
503
504        if( proxy )
505            g_object_unref( proxy );
506        if( conn )
507            dbus_g_connection_unref( conn );
508
509        tr_free( b64 );
510        g_free( file_contents );
511    }
512    else g_message( "couldn't read %s", filename );
513
514#endif
515    return success;
516}
517
518gboolean
519gtr_dbus_present_window( void )
520{
521    static gboolean   success = FALSE;
522
523#ifdef HAVE_DBUS_GLIB
524    DBusGProxy *      proxy = NULL;
525    GError *          err = NULL;
526    DBusGConnection * conn;
527    if( ( conn = dbus_g_bus_get( DBUS_BUS_SESSION, &err ) ) )
528        proxy = dbus_g_proxy_new_for_name ( conn, VALUE_SERVICE_NAME,
529                                            VALUE_SERVICE_OBJECT_PATH,
530                                            VALUE_SERVICE_INTERFACE );
531    else if( err )
532        g_message( "err: %s", err->message );
533    if( proxy )
534        dbus_g_proxy_call( proxy, "PresentWindow", &err,
535                           G_TYPE_INVALID,
536                           G_TYPE_BOOLEAN, &success,
537                           G_TYPE_INVALID );
538    if( err )
539        g_message( "err: %s", err->message );
540
541    g_object_unref( proxy );
542    dbus_g_connection_unref( conn );
543#endif
544    return success;
545}
546
547GtkWidget *
548gtr_button_new_from_stock( const char * stock,
549                           const char * mnemonic )
550{
551    GtkWidget * image = gtk_image_new_from_stock( stock,
552                                                  GTK_ICON_SIZE_BUTTON );
553    GtkWidget * button = gtk_button_new_with_mnemonic( mnemonic );
554
555    gtk_button_set_image( GTK_BUTTON( button ), image );
556    return button;
557}
558
559/***
560****
561***/
562
563void
564gtr_widget_set_tooltip_text( GtkWidget * w, const char * tip )
565{
566#if GTK_CHECK_VERSION( 2,12,0 )
567    gtk_widget_set_tooltip_text( w, tip );
568#else
569    static GtkTooltips * tips = NULL;
570    if( tips == NULL )
571        tips = gtk_tooltips_new( );
572    gtk_tooltips_set_tip( tips, w, tip, NULL );
573#endif
574}
575
576void
577gtr_toolbar_set_orientation( GtkToolbar      * toolbar,
578                             GtkOrientation    orientation )
579{
580#if GTK_CHECK_VERSION( 2,16,0 )
581    gtk_orientable_set_orientation( GTK_ORIENTABLE( toolbar ), orientation );
582#else
583    gtk_toolbar_set_orientation( toolbar, orientation );
584#endif
585}
586
587/***
588****
589***/
590
591#if !GTK_CHECK_VERSION( 2,12,0 )
592struct gtr_func_data
593{
594    GSourceFunc function;
595    gpointer data;
596};
597
598static gboolean
599gtr_thread_func( gpointer data )
600{
601    struct gtr_func_data * idle_data = data;
602    gboolean more;
603
604    gdk_threads_enter( );
605    more = idle_data->function( idle_data->data );
606    gdk_threads_leave( );
607
608    if( !more )
609        g_free( data );
610
611    return more;
612}
613#endif
614
615void
616gtr_idle_add( GSourceFunc function, gpointer data )
617{
618#if GTK_CHECK_VERSION( 2,12,0 )
619    gdk_threads_add_idle( function, data );
620#else
621    struct gtr_func_data * d = g_new( struct gtr_func_data, 1 );
622    d->function = function;
623    d->data = data;
624    g_idle_add( gtr_thread_func, d );
625#endif
626}
627
628guint
629gtr_timeout_add_seconds( guint seconds, GSourceFunc function, gpointer data )
630{
631#if GTK_CHECK_VERSION( 2,14,0 )
632    return gdk_threads_add_timeout_seconds( seconds, function, data );
633#elif GTK_CHECK_VERSION( 2,12,0 )
634    return gdk_threads_add_timeout( seconds*1000, function, data );
635#else
636    struct gtr_func_data * d = g_new( struct gtr_func_data, 1 );
637    d->function = function;
638    d->data = data;
639    return g_timeout_add( seconds*1000, gtr_thread_func, d );
640#endif
641}
Note: See TracBrowser for help on using the repository browser.