source: trunk/gtk/util.c @ 12396

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

(trunk gtk) gtr_get_host_from_url(): avoid a couple of malloc() + free() calls.

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