source: trunk/gtk/util.c @ 11088

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

(trunk gtk) replace tr_strcmp() with gtr_strcmp0(), a porability wrapper around g_strcmp0()

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