source: trunk/gtk/util.c @ 6559

Last change on this file since 6559 was 6559, checked in by muks, 13 years ago

Don't show an error when transmission is run twice

Instead, present the main window. This commit also auto-generates
the dbus bindings.

  • Property svn:keywords set to Date Rev Author Id
File size: 15.3 KB
Line 
1/******************************************************************************
2 * $Id: util.c 6559 2008-08-17 12:39:26Z muks $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <ctype.h> /* isxdigit() */
26#include <stdarg.h>
27#include <stdlib.h> /* free() */
28#include <string.h> /* strcmp() */
29
30#include <gtk/gtk.h>
31#include <glib/gi18n.h>
32#include <glib/gstdio.h> /* g_unlink() */
33#ifdef HAVE_GIO
34#include <gio/gio.h> /* g_file_trash() */
35#endif
36#ifdef HAVE_DBUS_GLIB
37#include <dbus/dbus-glib.h>
38#endif
39
40#include <libevent/evhttp.h>
41
42#include <libtransmission/transmission.h> /* TR_RATIO_NA, TR_RATIO_INF */
43#include <libtransmission/utils.h> /* tr_inf */
44
45#include "conf.h"
46#include "hig.h"
47#include "tr-prefs.h"
48#include "util.h"
49
50char*
51tr_strlratio( char * buf, double ratio, size_t buflen )
52{
53    if( (int)ratio == TR_RATIO_NA )
54        g_strlcpy( buf, _( "None" ), buflen );
55    else if( (int)ratio == TR_RATIO_INF )
56        g_strlcpy( buf, "\xE2\x88\x9E", buflen );
57    else if( ratio < 10.0 )
58        g_snprintf( buf, buflen, "%'.2f", ratio );
59    else if( ratio < 100.0 )
60        g_snprintf( buf, buflen, "%'.1f", ratio );
61    else
62        g_snprintf( buf, buflen, "%'.0f", ratio );
63    return buf;
64}
65
66#define KILOBYTE_FACTOR 1024.0
67#define MEGABYTE_FACTOR (1024.0 * 1024.0)
68#define GIGABYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
69
70char*
71tr_strlsize( char * buf, guint64 size, size_t buflen )
72{
73    if( !size )
74        g_strlcpy( buf, _( "None" ), buflen );
75#if GLIB_CHECK_VERSION(2,16,0)
76    else{ 
77        char * tmp = g_format_size_for_display( size );
78        g_strlcpy( buf, tmp, buflen );
79        g_free( tmp );
80    }
81#else
82    else if( size < (guint64)KILOBYTE_FACTOR )
83        g_snprintf( buf, buflen, ngettext("%'u byte", "%'u bytes", (guint)size), (guint)size );
84    else {
85        gdouble displayed_size;
86        if (size < (guint64)MEGABYTE_FACTOR) {
87            displayed_size = (gdouble) size / KILOBYTE_FACTOR;
88            g_snprintf( buf, buflen, _("%'.1f KB"), displayed_size );
89        } else if (size < (guint64)GIGABYTE_FACTOR) {
90            displayed_size = (gdouble) size / MEGABYTE_FACTOR;
91            g_snprintf( buf, buflen, _("%'.1f MB"), displayed_size );
92        } else {
93            displayed_size = (gdouble) size / GIGABYTE_FACTOR;
94            g_snprintf( buf, buflen, _("%'.1f GB"), displayed_size );
95        }
96    }
97#endif
98    return buf;
99}
100
101char*
102tr_strlspeed( char * buf, double kb_sec, size_t buflen )
103{
104    const double speed = kb_sec;
105
106    if ( speed < 1000.0 ) /* 0.0 KB to 999.9 KB */
107        g_snprintf( buf, buflen, _( "%'.1f KB/s" ), speed );
108    else if( speed < 102400.0 ) /* 0.98 MB to 99.99 MB */
109        g_snprintf( buf, buflen, _( "%'.2f MB/s" ), (speed/1024) );
110    else if( speed < 1024000.0 ) /* 100.0 MB to 999.9 MB */
111        g_snprintf( buf, buflen, _( "%'.1f MB/s" ), (speed/1024) );
112    else /* insane speeds */
113        g_snprintf( buf, buflen, _( "%'.2f GB/s" ), (speed/1048576) );
114
115    return buf;
116}
117
118char*
119tr_strltime( char * buf, int seconds, size_t buflen )
120{
121    int days, hours, minutes;
122    char d[128], h[128], m[128], s[128];
123
124    if( seconds < 0 )
125        seconds = 0;
126
127    days = seconds / 86400;
128    hours = (seconds % 86400) / 3600;
129    minutes = (seconds % 3600) / 60;
130    seconds = (seconds % 3600) % 60;
131
132    g_snprintf( d, sizeof( d ), ngettext( "%'d day", "%'d days", days ), days );
133    g_snprintf( h, sizeof( h ), ngettext( "%'d hour", "%'d hours", hours ), hours );
134    g_snprintf( m, sizeof( m ), ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
135    g_snprintf( s, sizeof( s ), ngettext( "%'d second", "%'d seconds", seconds ), seconds );
136
137    if( days ) {
138        if( days >= 4 || !hours ) {
139            g_strlcpy( buf, d, buflen );
140        } else {
141            g_snprintf( buf, buflen, "%s, %s", d, h );
142        }
143    } else if( hours ) {
144        if( hours >= 4 || !minutes ) {
145            g_strlcpy( buf, h, buflen );
146        } else {
147            g_snprintf( buf, buflen, "%s, %s", h, m );
148        }
149    } else if( minutes ) {
150        if( minutes >= 4 || !seconds ) {
151            g_strlcpy( buf, m, buflen );
152        } else {
153            g_snprintf( buf, buflen, "%s, %s", m, s );
154        }
155    } else {
156        g_strlcpy( buf, s, buflen );
157    }
158
159    return buf;
160}
161
162char *
163gtr_localtime( time_t time )
164{
165    const struct tm tm = *localtime( &time );
166    char buf[256], *eoln;
167
168    g_strlcpy( buf, asctime( &tm ), sizeof( buf ) );
169    if(( eoln = strchr( buf, '\n' )))
170        *eoln = '\0';
171
172    return g_locale_to_utf8( buf, -1, NULL, NULL, NULL );
173}
174
175gboolean
176mkdir_p( const char * path, mode_t mode )
177{
178#if GLIB_CHECK_VERSION( 2, 8, 0)
179    return !g_mkdir_with_parents( path, mode );
180#else
181    return !tr_mkdirp( path, mode );
182#endif
183}
184
185GSList *
186dupstrlist( GSList * l )
187{
188    GSList * ret = NULL;
189    for( ; l!=NULL; l=l->next )
190        ret = g_slist_prepend( ret, g_strdup( l->data ) );
191    return g_slist_reverse( ret );
192}
193
194char *
195joinstrlist(GSList *list, char *sep)
196{
197  GSList *l;
198  GString *gstr = g_string_new (NULL);
199  for (l=list; l!=NULL; l=l->next) {
200    g_string_append (gstr, (char*)l->data);
201    if (l->next != NULL)
202      g_string_append (gstr, (sep));
203  }
204  return g_string_free (gstr, FALSE);
205}
206
207void
208freestrlist(GSList *list)
209{
210  g_slist_foreach (list, (GFunc)g_free, NULL);
211  g_slist_free (list);
212}
213
214char *
215decode_uri( const char * uri )
216{
217    gboolean in_query = FALSE;
218    char * ret = g_new( char, strlen( uri ) + 1 );
219    char * out = ret;
220    for( ; uri && *uri; ) {
221        char ch = *uri;
222        if( ch=='?' )
223            in_query = TRUE;
224        else if( ch=='+' && in_query )
225            ch = ' ';
226        else if( ch=='%' && isxdigit((unsigned char)uri[1])
227                         && isxdigit((unsigned char)uri[2])) {
228            char buf[3] = { uri[1], uri[2], '\0' };
229            ch = (char) g_ascii_strtoull( buf, NULL, 16 );
230            uri += 2;
231       }
232
233       ++uri;
234       *out++ = ch;
235    }
236
237    *out = '\0';
238    return ret;
239}
240
241GSList *
242checkfilenames( int argc, char **argv )
243{
244    int i;
245    GSList * ret = NULL;
246    char * pwd = g_get_current_dir( );
247
248    for( i=0; i<argc; ++i )
249    {
250        char * filename = g_path_is_absolute( argv[i] )
251            ? g_strdup ( argv[i] )
252            : g_build_filename( pwd, argv[i], NULL );
253
254        if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
255            ret = g_slist_prepend( ret, filename );
256        else
257            g_free( filename );
258    }
259
260    g_free( pwd );
261    return g_slist_reverse( ret );
262}
263
264static void
265onErrorResponse(GtkWidget * dialog, int resp UNUSED, gpointer glist)
266{
267    GSList * list = glist;
268    if( list )
269    {
270        callbackfunc_t func = list->data;
271        gpointer user_data = list->next->data;
272        func( user_data );
273        g_slist_free( list );
274    }
275
276    gtk_widget_destroy( dialog );
277}
278
279static GtkWidget *
280verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
281              const char * format, va_list ap )
282{
283  GtkWidget *dialog;
284  char *msg;
285  GSList *funcdata = NULL;
286
287  msg = g_strdup_vprintf(format, ap);
288
289  if(NULL == wind)
290    dialog = gtk_message_dialog_new(
291      NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
292  else
293    dialog = gtk_message_dialog_new(wind,
294      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
295      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
296
297  if( func ) {
298    funcdata = g_slist_append( funcdata, (gpointer)func );
299    funcdata = g_slist_append( funcdata, data );
300  }
301  g_signal_connect(dialog, "response", G_CALLBACK(onErrorResponse), funcdata);
302  g_free(msg);
303
304  return dialog;
305}
306
307void
308addTorrentErrorDialog( GtkWidget * child, int err, const char * filename )
309{
310    GtkWidget * w;
311    GtkWidget * win;
312    const char * fmt;
313    char * secondary;
314    switch( err ) {
315        case TR_EINVALID: fmt = _( "The torrent file \"%s\" contains invalid data." ); break;
316        case TR_EDUPLICATE: fmt = _( "The torrent file \"%s\" is already in use." ); break;
317        default: fmt = _( "The torrent file \"%s\" encountered an unknown error." ); break;
318    }
319    secondary = g_strdup_printf( fmt, filename );
320    win = ( !child || GTK_IS_WINDOW( child ) )
321        ? child
322        : gtk_widget_get_ancestor( child ? GTK_WIDGET( child ) : NULL, GTK_TYPE_WINDOW );
323    w = gtk_message_dialog_new( GTK_WINDOW( win ),
324                                GTK_DIALOG_DESTROY_WITH_PARENT,
325                                GTK_MESSAGE_ERROR,
326                                GTK_BUTTONS_CLOSE,
327                                _( "Error opening torrent" ) );
328    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ), secondary );
329    g_signal_connect_swapped( w, "response",
330                              G_CALLBACK( gtk_widget_destroy ), w );
331    gtk_widget_show_all( w );
332    g_free( secondary );
333}
334
335void
336errmsg( GtkWindow * wind, const char * format, ... )
337{
338    GtkWidget * dialog;
339    va_list     ap;
340
341    va_start( ap, format );
342    dialog = verrmsg_full( wind, NULL, NULL, format, ap );
343    va_end( ap );
344
345    if( NULL != wind && !GTK_WIDGET_MAPPED( GTK_WIDGET( wind ) ) )
346    {
347        g_signal_connect_swapped( wind, "map",
348                                  G_CALLBACK( gtk_widget_show ), dialog );
349    }
350    else
351    {
352        gtk_widget_show( dialog );
353    }
354}
355
356GtkWidget *
357errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
358             const char * format, ... )
359{
360    GtkWidget * dialog;
361    va_list     ap;
362
363    va_start( ap, format );
364    dialog = verrmsg_full( wind, func, data, format, ap );
365    va_end( ap );
366
367    return dialog;
368}
369
370typedef void (PopupFunc)(GtkWidget*, GdkEventButton*); 
371
372/* pop up the context menu if a user right-clicks.
373   if the row they right-click on isn't selected, select it. */
374
375gboolean
376on_tree_view_button_pressed (GtkWidget       * view,
377                             GdkEventButton  * event,
378                             gpointer          func)
379{
380  GtkTreeView * tv = GTK_TREE_VIEW( view );
381
382  if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3)
383  {
384    GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
385    GtkTreePath *path;
386    if (gtk_tree_view_get_path_at_pos (tv,
387                                       (gint) event->x,
388                                       (gint) event->y,
389                                       &path, NULL, NULL, NULL))
390    {
391      if (!gtk_tree_selection_path_is_selected (selection, path))
392      {
393        gtk_tree_selection_unselect_all (selection);
394        gtk_tree_selection_select_path (selection, path);
395      }
396      gtk_tree_path_free(path);
397    }
398   
399    ((PopupFunc*)func)(view, event);
400
401    return TRUE;
402  }
403
404  return FALSE;
405}
406
407gpointer
408tr_object_ref_sink( gpointer object )
409{
410#if GLIB_CHECK_VERSION(2,10,0)
411    g_object_ref_sink( object );
412#else
413    g_object_ref( object );
414    gtk_object_sink( GTK_OBJECT( object ) );
415#endif
416    return object;
417}
418
419void
420tr_file_trash_or_unlink( const char * filename )
421{
422    if( filename && *filename )
423    {
424        gboolean trashed = FALSE;
425#ifdef HAVE_GIO
426        GError * err = NULL;
427        GFile * file = g_file_new_for_path( filename );
428        trashed = g_file_trash( file, NULL, &err );
429        g_object_unref( G_OBJECT( file ) );
430#endif
431        if( !trashed )
432            g_unlink( filename );
433    }
434}
435
436char*
437gtr_get_help_url( void )
438{
439    const char * fmt = "http://www.transmissionbt.com/help/gtk/%d.%dx";
440    int major, minor;
441    sscanf( SHORT_VERSION_STRING, "%d.%d", &major, &minor );
442    return g_strdup_printf( fmt, major, minor/10 );
443}
444
445void
446gtr_open_file( const char * path )
447{
448    if( path )
449    {
450        gboolean opened = FALSE;
451#ifdef HAVE_GIO
452        if( !opened )
453        {
454            GFile * file = g_file_new_for_path( path );
455            char * uri = g_file_get_uri( file );
456            opened = g_app_info_launch_default_for_uri( uri, NULL, NULL );
457            g_free( uri );
458            g_object_unref( G_OBJECT( file ) );
459        }
460#endif
461        if( !opened )
462        {
463            char * argv[] = { "xdg-open", (char*)path, NULL };
464            g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
465                           NULL, NULL, NULL, NULL );
466        }
467    }
468}
469
470#define VALUE_SERVICE_NAME        "com.transmissionbt.Transmission"
471#define VALUE_SERVICE_OBJECT_PATH "/com/transmissionbt/Transmission"
472#define VALUE_SERVICE_INTERFACE   "com.transmissionbt.Transmission"
473
474gboolean
475gtr_dbus_add_torrent( const char * filename )
476{
477    static gboolean success = FALSE;
478#ifdef HAVE_DBUS_GLIB
479    DBusGProxy * proxy = NULL;
480    GError * err = NULL;
481    DBusGConnection * conn;
482    if(( conn = dbus_g_bus_get( DBUS_BUS_SESSION, &err )))
483        proxy = dbus_g_proxy_new_for_name (conn, VALUE_SERVICE_NAME,
484                                                 VALUE_SERVICE_OBJECT_PATH,
485                                                 VALUE_SERVICE_INTERFACE );
486    else if( err )
487       g_message( "err: %s", err->message );
488    if( proxy )
489        dbus_g_proxy_call( proxy, "AddFile", &err,
490                           G_TYPE_STRING, filename,
491                           G_TYPE_INVALID,
492                           G_TYPE_BOOLEAN, &success,
493                           G_TYPE_INVALID );
494    if( err )
495       g_message( "err: %s", err->message );
496
497    g_object_unref( proxy );
498    dbus_g_connection_unref( conn );
499#endif
500    return success;
501}
502
503gboolean
504gtr_dbus_present_window()
505{
506    static gboolean success = FALSE;
507#ifdef HAVE_DBUS_GLIB
508    DBusGProxy * proxy = NULL;
509    GError * err = NULL;
510    DBusGConnection * conn;
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    if( proxy )
518        dbus_g_proxy_call( proxy, "PresentWindow", &err,
519                           G_TYPE_INVALID,
520                           G_TYPE_BOOLEAN, &success,
521                           G_TYPE_INVALID );
522    if( err )
523       g_message( "err: %s", err->message );
524
525    g_object_unref( proxy );
526    dbus_g_connection_unref( conn );
527#endif
528    return success;
529}
530
531GtkWidget *
532tr_button_new_from_stock( const char * stock,
533                          const char * mnemonic )
534{
535    GtkWidget * image = gtk_image_new_from_stock( stock, GTK_ICON_SIZE_BUTTON );
536    GtkWidget * button = gtk_button_new_with_mnemonic( mnemonic );
537    gtk_button_set_image( GTK_BUTTON( button ), image );
538    return button;
539}
Note: See TracBrowser for help on using the repository browser.