source: trunk/gtk/util.c @ 12219

Last change on this file since 12219 was 12219, checked in by jordan, 11 years ago

(trunk gtk) remove function gtr_strcmp0, which is redundant due to tr_strcmp0

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