source: trunk/gtk/util.c @ 10937

Last change on this file since 10937 was 10937, checked in by charles, 12 years ago

(trunk) #3045 "speed units" -- change the public API of libtransmission based on feedback from livings

  • Property svn:keywords set to Date Rev Author Id
File size: 23.9 KB
Line 
1/*
2 * This file Copyright (C) 2008-2010 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: util.c 10937 2010-07-04 06:07:21Z charles $
11 */
12
13#include <ctype.h> /* isxdigit() */
14#include <errno.h>
15#include <math.h> /* pow() */
16#include <stdlib.h> /* free() */
17#include <string.h> /* strcmp() */
18
19#include <sys/types.h> /* for gtr_lockfile()'s open() */
20#include <sys/stat.h> /* for gtr_lockfile()'s open() */
21#include <fcntl.h> /* for gtr_lockfile()'s open() */
22
23#include <gtk/gtk.h>
24#include <glib/gi18n.h>
25#include <glib/gstdio.h> /* g_unlink() */
26#ifdef HAVE_GIO
27 #include <gio/gio.h> /* g_file_trash() */
28#endif
29#ifdef HAVE_DBUS_GLIB
30 #include <dbus/dbus-glib.h>
31#endif
32
33#include <evhttp.h>
34
35#include <libtransmission/transmission.h> /* TR_RATIO_NA, TR_RATIO_INF */
36#include <libtransmission/utils.h> /* tr_inf */
37#include <libtransmission/web.h> /* tr_webResponseStr() */
38#include <libtransmission/version.h> /* tr_inf */
39
40#include "conf.h"
41#include "hig.h"
42#include "tr-prefs.h"
43#include "util.h"
44
45/***
46****  UNITS
47***/
48
49const int mem_K = 1024;
50/* abbreviation for bytes */
51const char * mem_B_str = N_("B");
52/* abbreviation IEC base 2 units kilobyte */
53const char * mem_K_str = N_("KiB");
54/* abbreviation IEC base 2 units megabyte */
55const char * mem_M_str = N_("MiB");
56/* abbreviation IEC base 2 units gigabyte */
57const char * mem_G_str = N_("GiB");
58
59const int disk_K = 1000;
60/* abbreviation for bytes */
61const char * disk_B_str = N_("B");
62/* abbreviation for SI base 10 kilobyte */
63const char * disk_K_str = N_("kB");
64/* abbreviation for SI base 10 megabyte */
65const char * disk_M_str = N_("MB");
66/* abbreviation for SI base 10 gigabyte */
67const char * disk_G_str = N_("GB");
68
69const int speed_K = 1000;
70/* abbreviation for bytes per second */
71const char * speed_B_str = N_("B/s");
72/* abbreviation for kilobytes per second */
73const char * speed_K_str = N_("kB/s");
74/* abbreviation for megabytes per second */
75const char * speed_M_str = N_("MB/s");
76/* abbreviation for gigabytes per second */
77const char * speed_G_str = N_("GB/s");
78
79/***
80****
81***/
82
83gtr_lockfile_state_t
84gtr_lockfile( const char * filename )
85{
86    gtr_lockfile_state_t ret;
87
88#ifdef WIN32
89
90    HANDLE file = CreateFile( filename,
91                              GENERIC_READ | GENERIC_WRITE,
92                              FILE_SHARE_READ | FILE_SHARE_WRITE,
93                              NULL,
94                              OPEN_ALWAYS,
95                              FILE_ATTRIBUTE_NORMAL,
96                              NULL );
97    if( file == INVALID_HANDLE_VALUE )
98        ret = GTR_LOCKFILE_EOPEN;
99    else if( !LockFile( file, 0, 0, 1, 1 ) )
100        ret = GTR_LOCKFILE_ELOCK;
101    else
102        ret = GTR_LOCKFILE_SUCCESS;
103
104#else
105
106    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
107    if( fd < 0 )
108        ret = GTR_LOCKFILE_EOPEN;
109    else {
110        struct flock lk;
111        memset( &lk, 0,  sizeof( lk ) );
112        lk.l_start = 0;
113        lk.l_len = 0;
114        lk.l_type = F_WRLCK;
115        lk.l_whence = SEEK_SET;
116        if( -1 == fcntl( fd, F_SETLK, &lk ) )
117            ret = GTR_LOCKFILE_ELOCK;
118        else
119            ret = GTR_LOCKFILE_SUCCESS;
120    }
121
122#endif
123
124    return ret;
125}
126
127/***
128****
129***/
130
131int
132gtr_compare_double( const double a, const double b, int decimal_places )
133{
134    const int64_t ia = (int64_t)(a * pow( 10, decimal_places ) );
135    const int64_t ib = (int64_t)(b * pow( 10, decimal_places ) );
136    if( ia < ib ) return -1;
137    if( ia > ib ) return  1; 
138    return 0;
139}
140
141/***
142****
143***/
144
145const char*
146gtr_get_unicode_string( int i )
147{
148    switch( i ) {
149        case GTR_UNICODE_UP:   return "\xE2\x86\x91";
150        case GTR_UNICODE_DOWN: return "\xE2\x86\x93";
151        case GTR_UNICODE_INF:  return "\xE2\x88\x9E";
152        default:               return "err";
153    }
154}
155
156char*
157tr_strlratio( char * buf, double ratio, size_t buflen )
158{
159    return tr_strratio( buf, buflen, ratio, gtr_get_unicode_string( GTR_UNICODE_INF ) );
160}
161
162char*
163tr_strlpercent( char * buf, double x, size_t buflen )
164{
165    return tr_strpercent( buf, x, buflen );
166}
167
168char*
169tr_strlsize( char * buf, guint64 bytes, size_t buflen )
170{
171    if( !bytes )
172        g_strlcpy( buf, _( "None" ), buflen );
173    else
174        tr_formatter_size_B( buf, bytes, buflen );
175
176    return buf;
177}
178
179char*
180tr_strlspeed( char * buf, int bytes_per_second, size_t buflen )
181{
182    if( bytes_per_second < 1 )
183        g_strlcpy( buf, _( "None" ), buflen );
184    else
185        tr_formatter_speed_Bps( buf, bytes_per_second, buflen );
186
187    return buf;
188}
189
190char*
191tr_strltime( char * buf, int seconds, size_t buflen )
192{
193    int  days, hours, minutes;
194    char d[128], h[128], m[128], s[128];
195
196    if( seconds < 0 )
197        seconds = 0;
198
199    days = seconds / 86400;
200    hours = ( seconds % 86400 ) / 3600;
201    minutes = ( seconds % 3600 ) / 60;
202    seconds = ( seconds % 3600 ) % 60;
203
204    g_snprintf( d, sizeof( d ), ngettext( "%'d day", "%'d days",
205                                          days ), days );
206    g_snprintf( h, sizeof( h ), ngettext( "%'d hour", "%'d hours",
207                                          hours ), hours );
208    g_snprintf( m, sizeof( m ),
209                ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
210    g_snprintf( s, sizeof( s ),
211                ngettext( "%'d second", "%'d seconds", seconds ), seconds );
212
213    if( days )
214    {
215        if( days >= 4 || !hours )
216        {
217            g_strlcpy( buf, d, buflen );
218        }
219        else
220        {
221            g_snprintf( buf, buflen, "%s, %s", d, h );
222        }
223    }
224    else if( hours )
225    {
226        if( hours >= 4 || !minutes )
227        {
228            g_strlcpy( buf, h, buflen );
229        }
230        else
231        {
232            g_snprintf( buf, buflen, "%s, %s", h, m );
233        }
234    }
235    else if( minutes )
236    {
237        if( minutes >= 4 || !seconds )
238        {
239            g_strlcpy( buf, m, buflen );
240        }
241        else
242        {
243            g_snprintf( buf, buflen, "%s, %s", m, s );
244        }
245    }
246    else
247    {
248        g_strlcpy( buf, s, buflen );
249    }
250
251    return buf;
252}
253
254char *
255gtr_localtime( time_t time )
256{
257    const struct tm tm = *localtime( &time );
258    char            buf[256], *eoln;
259
260    g_strlcpy( buf, asctime( &tm ), sizeof( buf ) );
261    if( ( eoln = strchr( buf, '\n' ) ) )
262        *eoln = '\0';
263
264    return g_locale_to_utf8( buf, -1, NULL, NULL, NULL );
265}
266
267int
268gtr_mkdir_with_parents( const char * path, int mode )
269{
270#if GLIB_CHECK_VERSION( 2, 8, 0 )
271    return !g_mkdir_with_parents( path, mode );
272#else
273    return !tr_mkdirp( path, mode );
274#endif
275}
276
277GSList *
278dupstrlist( GSList * l )
279{
280    GSList * ret = NULL;
281
282    for( ; l != NULL; l = l->next )
283        ret = g_slist_prepend( ret, g_strdup( l->data ) );
284    return g_slist_reverse( ret );
285}
286
287char *
288joinstrlist( GSList *list,
289             char *  sep )
290{
291    GSList * l;
292    GString *gstr = g_string_new ( NULL );
293
294    for( l = list; l != NULL; l = l->next )
295    {
296        g_string_append ( gstr, (char*)l->data );
297        if( l->next != NULL )
298            g_string_append ( gstr, ( sep ) );
299    }
300    return g_string_free ( gstr, FALSE );
301}
302
303void
304freestrlist( GSList *list )
305{
306    g_slist_foreach ( list, (GFunc)g_free, NULL );
307    g_slist_free ( list );
308}
309
310char *
311decode_uri( const char * uri )
312{
313    gboolean in_query = FALSE;
314    char *   ret = g_new( char, strlen( uri ) + 1 );
315    char *   out = ret;
316
317    for( ; uri && *uri; )
318    {
319        char ch = *uri;
320        if( ch == '?' )
321            in_query = TRUE;
322        else if( ch == '+' && in_query )
323            ch = ' ';
324        else if( ch == '%' && isxdigit( (unsigned char)uri[1] )
325               && isxdigit( (unsigned char)uri[2] ) )
326        {
327            char buf[3] = { uri[1], uri[2], '\0' };
328            ch = (char) g_ascii_strtoull( buf, NULL, 16 );
329            uri += 2;
330        }
331
332        ++uri;
333        *out++ = ch;
334    }
335
336    *out = '\0';
337    return ret;
338}
339
340/* pattern-matching text; ie, legaltorrents.com */
341char*
342gtr_get_host_from_url( const char * url )
343{
344    char * h = NULL;
345    char * name;
346    const char * first_dot;
347    const char * last_dot;
348
349    tr_urlParse( url, -1, NULL, &h, NULL, NULL );
350    first_dot = strchr( h, '.' );
351    last_dot = strrchr( h, '.' );
352
353    if( ( first_dot ) && ( last_dot ) && ( first_dot != last_dot ) )
354        name = g_strdup( first_dot + 1 );
355    else
356        name = g_strdup( h );
357
358    tr_free( h );
359    return name;
360}
361
362gboolean
363gtr_is_supported_url( const char * str )
364{
365    return !strncmp( str, "ftp://", 6 )
366        || !strncmp( str, "http://", 7 )
367        || !strncmp( str, "https://", 8 );
368}
369
370gboolean
371gtr_is_magnet_link( const char * str )
372{
373    return !strncmp( str, "magnet:?", 8 );
374}
375
376gboolean
377gtr_is_hex_hashcode( const char * str )
378{
379    int i;
380
381    if( !str || ( strlen( str ) != 40 ) )
382        return FALSE;
383
384    for( i=0; i<40; ++i )
385        if( !isxdigit( str[i] ) )
386            return FALSE;
387
388    return TRUE;
389}
390
391static GtkWindow *
392getWindow( GtkWidget * w )
393{
394    if( w == NULL )
395        return NULL;
396
397    if( GTK_IS_WINDOW( w ) )
398        return GTK_WINDOW( w );
399
400    return GTK_WINDOW( gtk_widget_get_ancestor( w, GTK_TYPE_WINDOW ) );
401}
402
403void
404addTorrentErrorDialog( GtkWidget *  child,
405                       int          err,
406                       const char * filename )
407{
408    char * secondary;
409    const char * fmt;
410    GtkWidget * w;
411    GtkWindow * win = getWindow( child );
412
413    switch( err )
414    {
415        case TR_PARSE_ERR: fmt = _( "The torrent file \"%s\" contains invalid data." ); break;
416        case TR_PARSE_DUPLICATE: fmt = _( "The torrent file \"%s\" is already in use." ); break;
417        default: fmt = _( "The torrent file \"%s\" encountered an unknown error." ); break;
418    }
419    secondary = g_strdup_printf( fmt, filename );
420
421    w = gtk_message_dialog_new( win,
422                                GTK_DIALOG_DESTROY_WITH_PARENT,
423                                GTK_MESSAGE_ERROR,
424                                GTK_BUTTONS_CLOSE,
425                                "%s", _( "Error opening torrent" ) );
426    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ),
427                                              "%s", secondary );
428    g_signal_connect_swapped( w, "response",
429                              G_CALLBACK( gtk_widget_destroy ), w );
430    gtk_widget_show_all( w );
431    g_free( secondary );
432}
433
434typedef void ( PopupFunc )( GtkWidget*, GdkEventButton* );
435
436/* pop up the context menu if a user right-clicks.
437   if the row they right-click on isn't selected, select it. */
438
439gboolean
440on_tree_view_button_pressed( GtkWidget *      view,
441                             GdkEventButton * event,
442                             gpointer         func )
443{
444    GtkTreeView * tv = GTK_TREE_VIEW( view );
445
446    if( event->type == GDK_BUTTON_PRESS  &&  event->button == 3 )
447    {
448        GtkTreeSelection * selection = gtk_tree_view_get_selection( tv );
449        GtkTreePath *      path;
450        if( gtk_tree_view_get_path_at_pos ( tv,
451                                            (gint) event->x,
452                                            (gint) event->y,
453                                            &path, NULL, NULL, NULL ) )
454        {
455            if( !gtk_tree_selection_path_is_selected ( selection, path ) )
456            {
457                gtk_tree_selection_unselect_all ( selection );
458                gtk_tree_selection_select_path ( selection, path );
459            }
460            gtk_tree_path_free( path );
461        }
462
463        if( func != NULL )
464            ( (PopupFunc*)func )( view, event );
465
466        return TRUE;
467    }
468
469    return FALSE;
470}
471
472/* if the user clicked in an empty area of the list,
473 * clear all the selections. */
474gboolean
475on_tree_view_button_released( GtkWidget *      view,
476                              GdkEventButton * event,
477                              gpointer         unused UNUSED )
478{
479    GtkTreeView * tv = GTK_TREE_VIEW( view );
480
481    if( !gtk_tree_view_get_path_at_pos ( tv,
482                                         (gint) event->x,
483                                         (gint) event->y,
484                                         NULL, NULL, NULL, NULL ) )
485    {
486        GtkTreeSelection * selection = gtk_tree_view_get_selection( tv );
487        gtk_tree_selection_unselect_all ( selection );
488    }
489
490    return FALSE;
491}
492
493gpointer
494gtr_object_ref_sink( gpointer object )
495{
496#if GLIB_CHECK_VERSION( 2, 10, 0 )
497    g_object_ref_sink( object );
498#else
499    g_object_ref( object );
500    gtk_object_sink( GTK_OBJECT( object ) );
501#endif
502    return object;
503}
504
505int
506gtr_file_trash_or_remove( const char * filename )
507{
508    if( filename && g_file_test( filename, G_FILE_TEST_EXISTS ) )
509    {
510        gboolean trashed = FALSE;
511#ifdef HAVE_GIO
512        GError * err = NULL;
513        GFile *  file = g_file_new_for_path( filename );
514        trashed = g_file_trash( file, NULL, &err );
515        if( err )
516            g_message( "Unable to trash file \"%s\": %s", filename, err->message );
517        g_clear_error( &err );
518        g_object_unref( G_OBJECT( file ) );
519#endif
520
521        if( !trashed && g_remove( filename ) )
522        {
523            const int err = errno;
524            g_message( "Unable to remove file \"%s\": %s", filename, g_strerror( err ) );
525        }
526    }
527
528    return 0;
529}
530
531char*
532gtr_get_help_url( void )
533{
534    const char * fmt = "http://www.transmissionbt.com/help/gtk/%d.%dx";
535    int          major, minor;
536
537    sscanf( SHORT_VERSION_STRING, "%d.%d", &major, &minor );
538    return g_strdup_printf( fmt, major, minor / 10 );
539}
540
541void
542gtr_open_file( const char * path )
543{
544    if( path )
545    {
546        gboolean opened = FALSE;
547#ifdef HAVE_GIO
548        if( !opened )
549        {
550            GFile * file = g_file_new_for_path( path );
551            char *  uri = g_file_get_uri( file );
552            opened = g_app_info_launch_default_for_uri( uri, NULL, NULL );
553            g_free( uri );
554            g_object_unref( G_OBJECT( file ) );
555        }
556#endif
557        if( !opened )
558        {
559            char * argv[] = { (char*)"xdg-open", (char*)path, NULL };
560            opened = g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
561                                    NULL, NULL, NULL, NULL );
562        }
563
564        if( !opened )
565        {
566            g_message( "Unable to open \"%s\"", path );
567        }
568    }
569}
570
571#define VALUE_SERVICE_NAME        "com.transmissionbt.Transmission"
572#define VALUE_SERVICE_OBJECT_PATH "/com/transmissionbt/Transmission"
573#define VALUE_SERVICE_INTERFACE   "com.transmissionbt.Transmission"
574
575gboolean
576gtr_dbus_add_torrent( const char * filename )
577{
578    /* FIXME: why is this static? */
579    static gboolean handled = FALSE;
580
581#ifdef HAVE_DBUS_GLIB
582    char * payload;
583    gsize file_length;
584    char * file_contents = NULL;
585
586    /* If it's a file, load its contents and send them over the wire...
587     * it might be a temporary file that's going to disappear. */
588    if( g_file_get_contents( filename, &file_contents, &file_length, NULL ) )
589        payload = tr_base64_encode( file_contents, file_length, NULL );
590    else if( gtr_is_supported_url( filename ) || gtr_is_magnet_link( filename ) )
591        payload = tr_strdup( filename );
592    else
593        payload = NULL;
594
595    if( payload != NULL )
596    {
597        GError * err = NULL;
598        DBusGConnection * conn;
599        DBusGProxy * proxy = NULL;
600
601        if(( conn = dbus_g_bus_get( DBUS_BUS_SESSION, &err )))
602            proxy = dbus_g_proxy_new_for_name (conn, VALUE_SERVICE_NAME,
603                                                     VALUE_SERVICE_OBJECT_PATH,
604                                                     VALUE_SERVICE_INTERFACE );
605        else if( err )
606           g_message( "err: %s", err->message );
607
608        if( proxy )
609            dbus_g_proxy_call( proxy, "AddMetainfo", &err,
610                               G_TYPE_STRING, payload,
611                               G_TYPE_STRING, filename,
612                               G_TYPE_INVALID,
613                               G_TYPE_BOOLEAN, &handled,
614                               G_TYPE_INVALID );
615        if( err )
616           g_message( "err: %s", err->message );
617
618        if( proxy )
619            g_object_unref( proxy );
620        if( conn )
621            dbus_g_connection_unref( conn );
622
623        tr_free( payload );
624    }
625
626    g_free( file_contents );
627
628#endif
629    return handled;
630}
631
632gboolean
633gtr_dbus_present_window( void )
634{
635    static gboolean   success = FALSE;
636
637#ifdef HAVE_DBUS_GLIB
638    DBusGProxy *      proxy = NULL;
639    GError *          err = NULL;
640    DBusGConnection * conn;
641    if( ( conn = dbus_g_bus_get( DBUS_BUS_SESSION, &err ) ) )
642        proxy = dbus_g_proxy_new_for_name ( conn, VALUE_SERVICE_NAME,
643                                            VALUE_SERVICE_OBJECT_PATH,
644                                            VALUE_SERVICE_INTERFACE );
645    else if( err )
646        g_message( "err: %s", err->message );
647    if( proxy )
648        dbus_g_proxy_call( proxy, "PresentWindow", &err,
649                           G_TYPE_INVALID,
650                           G_TYPE_BOOLEAN, &success,
651                           G_TYPE_INVALID );
652    if( err )
653        g_message( "err: %s", err->message );
654
655    g_object_unref( proxy );
656    dbus_g_connection_unref( conn );
657#endif
658    return success;
659}
660
661GtkWidget *
662gtr_button_new_from_stock( const char * stock,
663                           const char * mnemonic )
664{
665    GtkWidget * image = gtk_image_new_from_stock( stock,
666                                                  GTK_ICON_SIZE_BUTTON );
667    GtkWidget * button = gtk_button_new_with_mnemonic( mnemonic );
668
669    gtk_button_set_image( GTK_BUTTON( button ), image );
670    return button;
671}
672
673/***
674****
675***/
676
677void
678gtr_priority_combo_set_value( GtkWidget * w, tr_priority_t value )
679{
680    int i;
681    int currentValue;
682    const int column = 0;
683    GtkTreeIter iter;
684    GtkComboBox * combobox = GTK_COMBO_BOX( w );
685    GtkTreeModel * model = gtk_combo_box_get_model( combobox );
686
687    /* do the value and current value match? */
688    if( gtk_combo_box_get_active_iter( combobox, &iter ) ) {
689        gtk_tree_model_get( model, &iter, column, &currentValue, -1 );
690        if( currentValue == value )
691            return;
692    }
693
694    /* find the one to select */
695    i = 0;
696    while(( gtk_tree_model_iter_nth_child( model, &iter, NULL, i++ ))) {
697        gtk_tree_model_get( model, &iter, column, &currentValue, -1 );
698        if( currentValue == value ) {
699            gtk_combo_box_set_active_iter( combobox, &iter );
700            return;
701        }
702    }
703}
704
705tr_priority_t
706gtr_priority_combo_get_value( GtkWidget * w )
707{
708    int value = 0;
709    GtkTreeIter iter;
710    GtkComboBox * combo_box = GTK_COMBO_BOX( w );
711
712    if( gtk_combo_box_get_active_iter( combo_box, &iter ) )
713        gtk_tree_model_get( gtk_combo_box_get_model( combo_box ), &iter, 0, &value, -1 );
714
715    return value;
716}
717
718GtkWidget *
719gtr_priority_combo_new( void )
720{
721    int i;
722    GtkWidget * w;
723    GtkCellRenderer * r;
724    GtkListStore * store;
725    const struct {
726        int value;
727        const char * text;
728    } items[] = {
729        { TR_PRI_HIGH,   N_( "High" )  },
730        { TR_PRI_NORMAL, N_( "Normal" ) },
731        { TR_PRI_LOW,    N_( "Low" )  }
732    };
733
734    store = gtk_list_store_new( 2, G_TYPE_INT, G_TYPE_STRING );
735    for( i=0; i<(int)G_N_ELEMENTS(items); ++i ) {
736        GtkTreeIter iter;
737        gtk_list_store_append( store, &iter );
738        gtk_list_store_set( store, &iter, 0, items[i].value,
739                                          1, _( items[i].text ),
740                                         -1 );
741    }
742
743    w = gtk_combo_box_new_with_model( GTK_TREE_MODEL( store ) );
744    r = gtk_cell_renderer_text_new( );
745    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( w ), r, TRUE );
746    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( w ), r, "text", 1, NULL );
747
748    /* cleanup */
749    g_object_unref( store );
750    return w;
751}
752
753/***
754****
755***/
756
757void
758gtr_widget_set_tooltip_text( GtkWidget * w, const char * tip )
759{
760#if GTK_CHECK_VERSION( 2,12,0 )
761    gtk_widget_set_tooltip_text( w, tip );
762#else
763    static GtkTooltips * tips = NULL;
764    if( tips == NULL )
765        tips = gtk_tooltips_new( );
766    gtk_tooltips_set_tip( tips, w, tip, NULL );
767#endif
768}
769
770gboolean
771gtr_widget_get_realized( GtkWidget * w )
772{
773#if GTK_CHECK_VERSION( 2,20,0 )
774    return gtk_widget_get_realized( w );
775#else
776    return GTK_WIDGET_REALIZED( w ) != 0;
777#endif
778}
779
780void
781gtr_toolbar_set_orientation( GtkToolbar      * toolbar,
782                             GtkOrientation    orientation )
783{
784#if GTK_CHECK_VERSION( 2,16,0 )
785    gtk_orientable_set_orientation( GTK_ORIENTABLE( toolbar ), orientation );
786#else
787    gtk_toolbar_set_orientation( toolbar, orientation );
788#endif
789}
790
791
792/***
793****
794***/
795
796#if !GTK_CHECK_VERSION( 2,12,0 )
797struct gtr_func_data
798{
799    GSourceFunc function;
800    gpointer data;
801};
802
803static void
804gtr_func_data_free( gpointer data )
805{
806#if GTK_CHECK_VERSION( 2,10,0 )
807    g_slice_free( struct gtr_func_data, data );
808#else
809    g_free( data );
810#endif
811}
812
813static struct gtr_func_data *
814gtr_func_data_new( GSourceFunc function, gpointer data )
815{
816#if GTK_CHECK_VERSION( 2,10,0 )
817    struct gtr_func_data * d = g_slice_new( struct gtr_func_data );
818#else
819    struct gtr_func_data * d = g_new( struct gtr_func_data, 1 );
820#endif
821    d->function = function;
822    d->data = data;
823    return d;
824}
825
826static gboolean
827gtr_thread_func( gpointer data )
828{
829    gboolean more;
830    struct gtr_func_data * idle_data = data;
831
832    gdk_threads_enter( );
833    more = idle_data->function( idle_data->data );
834    gdk_threads_leave( );
835
836    return more;
837}
838#endif
839
840void
841gtr_idle_add( GSourceFunc function, gpointer data )
842{
843#if GTK_CHECK_VERSION( 2,12,0 )
844    gdk_threads_add_idle( function, data );
845#else
846    g_idle_add_full( G_PRIORITY_DEFAULT,
847                     gtr_thread_func,
848                     gtr_func_data_new( function, data ),
849                     gtr_func_data_free );
850#endif
851}
852
853guint
854gtr_timeout_add_seconds( guint seconds, GSourceFunc function, gpointer data )
855{
856#if GTK_CHECK_VERSION( 2,14,0 )
857    return gdk_threads_add_timeout_seconds( seconds, function, data );
858#elif GTK_CHECK_VERSION( 2,12,0 )
859    return gdk_threads_add_timeout( seconds*1000, function, data );
860#else
861    return g_timeout_add_full( G_PRIORITY_DEFAULT,
862                               seconds * 1000,
863                               gtr_thread_func,
864                               gtr_func_data_new( function, data ),
865                               gtr_func_data_free );
866#endif
867}
868
869void
870gtr_http_failure_dialog( GtkWidget * parent, const char * url, long response_code )
871{
872    GtkWindow * window = getWindow( parent );
873
874    GtkWidget * w = gtk_message_dialog_new( window, 0,
875                                            GTK_MESSAGE_ERROR,
876                                            GTK_BUTTONS_CLOSE,
877                                            _( "Error opening \"%s\"" ), url );
878
879    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ),
880                                              _( "Server returned \"%1$ld %2$s\"" ),
881                                              response_code,
882                                              tr_webGetResponseStr( response_code ) );
883
884    g_signal_connect_swapped( w, "response", G_CALLBACK( gtk_widget_destroy ), w );
885    gtk_widget_show( w );
886}
887
888void
889gtr_unrecognized_url_dialog( GtkWidget * parent, const char * url )
890{
891    const char * xt = "xt=urn:btih";
892
893    GtkWindow * window = getWindow( parent );
894
895    GString * gstr = g_string_new( NULL );
896
897    GtkWidget * w = gtk_message_dialog_new( window, 0,
898                                            GTK_MESSAGE_ERROR,
899                                            GTK_BUTTONS_CLOSE,
900                                            "%s", _( "Unrecognized URL" ) );
901
902    g_string_append_printf( gstr, _( "Transmission doesn't know how to use \"%s\"" ), url );
903
904    if( gtr_is_magnet_link( url ) && ( strstr( url, xt ) == NULL ) )
905    {
906        g_string_append_printf( gstr, "\n \n" );
907        g_string_append_printf( gstr, _( "This magnet link appears to be intended for something other than BitTorrent.  BitTorrent magnet links have a section containing \"%s\"." ), xt );
908    }
909
910    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ), "%s", gstr->str );
911    g_signal_connect_swapped( w, "response", G_CALLBACK( gtk_widget_destroy ), w );
912    gtk_widget_show( w );
913    g_string_free( gstr, TRUE );
914}
Note: See TracBrowser for help on using the repository browser.