source: trunk/gtk/util.c @ 5996

Last change on this file since 5996 was 5996, checked in by charles, 14 years ago

make the `new torrent' dialog a little prettier

  • Property svn:keywords set to Date Rev Author Id
File size: 13.2 KB
Line 
1/******************************************************************************
2 * $Id: util.c 5996 2008-06-02 15:07:26Z charles $
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
50int
51tr_strcmp( const char * a, const char * b )
52{
53    if( a && b ) return strcmp( a, b );
54    if( a ) return 1;
55    if( b ) return -1;
56    return 0;
57}
58
59char*
60tr_strlratio( char * buf, double ratio, size_t buflen )
61{
62    if( (int)ratio == TR_RATIO_NA )
63        g_strlcpy( buf, _( "None" ), buflen );
64    else if( (int)ratio == TR_RATIO_INF )
65        g_strlcpy( buf, "\xE2\x88\x9E", buflen );
66    else if( ratio < 10.0 )
67        g_snprintf( buf, buflen, "%'.2f", ratio );
68    else if( ratio < 100.0 )
69        g_snprintf( buf, buflen, "%'.1f", ratio );
70    else
71        g_snprintf( buf, buflen, "%'.0f", ratio );
72    return buf;
73}
74
75#define KILOBYTE_FACTOR 1024.0
76#define MEGABYTE_FACTOR (1024.0 * 1024.0)
77#define GIGABYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
78
79char*
80tr_strlsize( char * buf, guint64 size, size_t buflen )
81{
82    if( !size )
83        g_strlcpy( buf, _( "None" ), buflen );
84#if GLIB_CHECK_VERSION(2,16,0)
85    else{ 
86        char * tmp = g_format_size_for_display( size );
87        g_strlcpy( buf, tmp, buflen );
88        g_free( tmp );
89    }
90#else
91    else if( size < (guint64)KILOBYTE_FACTOR )
92        g_snprintf( buf, buflen, ngettext("%'u byte", "%'u bytes", (guint)size), (guint)size );
93    else {
94        gdouble displayed_size;
95        if (size < (guint64)MEGABYTE_FACTOR) {
96            displayed_size = (gdouble) size / KILOBYTE_FACTOR;
97            g_snprintf( buf, buflen, _("%'.1f KB"), displayed_size );
98        } else if (size < (guint64)GIGABYTE_FACTOR) {
99            displayed_size = (gdouble) size / MEGABYTE_FACTOR;
100            g_snprintf( buf, buflen, _("%'.1f MB"), displayed_size );
101        } else {
102            displayed_size = (gdouble) size / GIGABYTE_FACTOR;
103            g_snprintf( buf, buflen, _("%'.1f GB"), displayed_size );
104        }
105    }
106#endif
107    return buf;
108}
109
110char*
111tr_strlspeed( char * buf, double kb_sec, size_t buflen )
112{
113    const double speed = kb_sec;
114
115    if ( speed < 1000.0 ) /* 0.0 KB to 999.9 KB */
116        g_snprintf( buf, buflen, _( "%'.1f KB/s" ), speed );
117    else if( speed < 102400.0 ) /* 0.98 MB to 99.99 MB */
118        g_snprintf( buf, buflen, _( "%'.2f MB/s" ), (speed/1024) );
119    else if( speed < 1024000.0 ) /* 100.0 MB to 999.9 MB */
120        g_snprintf( buf, buflen, _( "%'.1f MB/s" ), (speed/1024) );
121    else /* insane speeds */
122        g_snprintf( buf, buflen, _( "%'.2f GB/s" ), (speed/1048576) );
123
124    return buf;
125}
126
127char*
128tr_strltime( char * buf, int seconds, size_t buflen )
129{
130    int hours;
131    int days;
132
133    if( seconds < 0 )
134        seconds = 0;
135
136    if( seconds < 60 )
137    {
138        g_snprintf( buf, buflen, ngettext( "%'d second", "%'d seconds", (int)seconds ), (int) seconds );
139        return buf;
140    }
141
142    if( seconds < ( 60 * 60 ) )
143    {
144        const int minutes = ( seconds + 30 ) / 60;
145        g_snprintf( buf, buflen, ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
146        return buf;
147    }
148
149    hours = seconds / ( 60 * 60 );
150
151    if( seconds < ( 60 * 60 * 4 ) )
152    {
153        char h[64];
154        char m[64];
155
156        const int minutes = ( seconds - hours * 60 * 60 + 30 ) / 60;
157
158        g_snprintf( h, sizeof(h), ngettext( "%'d hour", "%'d hours", hours ), hours );
159        g_snprintf( m, sizeof(m), ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
160        g_snprintf( buf, buflen, "%s, %s", h, m );
161        return buf;
162    }
163
164    if( hours < 24 )
165    {
166        g_snprintf( buf, buflen, ngettext( "%'d hour", "%'d hours", hours ), hours );
167        return buf;
168    }
169
170    days = seconds / ( 60 * 60 * 24 );
171    g_snprintf( buf, buflen, ngettext( "%'d day", "%'d days", days ), days );
172    return buf;
173}
174
175
176char *
177rfc822date( time_t time )
178{
179    const struct tm tm = *localtime( &time );
180    char buf[128];
181    strftime( buf, sizeof(buf), "%a, %d %b %Y %T %Z", &tm );
182    return g_locale_to_utf8( buf, -1, NULL, NULL, NULL );
183}
184
185gboolean
186mkdir_p( const char * path, mode_t mode )
187{
188#if GLIB_CHECK_VERSION( 2, 8, 0)
189    return !g_mkdir_with_parents( path, mode );
190#else
191    return !tr_mkdirp( path, mode );
192#endif
193}
194
195GSList *
196dupstrlist( GSList * l )
197{
198    GSList * ret = NULL;
199    for( ; l!=NULL; l=l->next )
200        ret = g_slist_prepend( ret, g_strdup( l->data ) );
201    return g_slist_reverse( ret );
202}
203
204char *
205joinstrlist(GSList *list, char *sep)
206{
207  GSList *l;
208  GString *gstr = g_string_new (NULL);
209  for (l=list; l!=NULL; l=l->next) {
210    g_string_append (gstr, (char*)l->data);
211    if (l->next != NULL)
212      g_string_append (gstr, (sep));
213  }
214  return g_string_free (gstr, FALSE);
215}
216
217void
218freestrlist(GSList *list)
219{
220  g_slist_foreach (list, (GFunc)g_free, NULL);
221  g_slist_free (list);
222}
223
224char *
225decode_uri( const char * uri )
226{
227    gboolean in_query = FALSE;
228    char * ret = g_new( char, strlen( uri ) + 1 );
229    char * out = ret;
230    for( ; uri && *uri; ) {
231        char ch = *uri;
232        if( ch=='?' )
233            in_query = TRUE;
234        else if( ch=='+' && in_query )
235            ch = ' ';
236        else if( ch=='%' && isxdigit((unsigned char)uri[1])
237                         && isxdigit((unsigned char)uri[2])) {
238            char buf[3] = { uri[1], uri[2], '\0' };
239            ch = (char) g_ascii_strtoull( buf, NULL, 16 );
240            uri += 2;
241       }
242
243       ++uri;
244       *out++ = ch;
245    }
246
247    *out = '\0';
248    return ret;
249}
250
251GSList *
252checkfilenames( int argc, char **argv )
253{
254    int i;
255    GSList * ret = NULL;
256    char * pwd = g_get_current_dir( );
257
258    for( i=0; i<argc; ++i )
259    {
260        char * filename = g_path_is_absolute( argv[i] )
261            ? g_strdup ( argv[i] )
262            : g_build_filename( pwd, argv[i], NULL );
263
264        if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
265            ret = g_slist_prepend( ret, filename );
266        else
267            g_free( filename );
268    }
269
270    g_free( pwd );
271    return g_slist_reverse( ret );
272}
273
274static void
275onErrorResponse(GtkWidget * dialog, int resp UNUSED, gpointer glist)
276{
277    GSList * list = glist;
278    if( list )
279    {
280        callbackfunc_t func = list->data;
281        gpointer user_data = list->next->data;
282        func( user_data );
283        g_slist_free( list );
284    }
285
286    gtk_widget_destroy( dialog );
287}
288
289static GtkWidget *
290verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
291              const char * format, va_list ap )
292{
293  GtkWidget *dialog;
294  char *msg;
295  GSList *funcdata = NULL;
296
297  msg = g_strdup_vprintf(format, ap);
298
299  if(NULL == wind)
300    dialog = gtk_message_dialog_new(
301      NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
302  else
303    dialog = gtk_message_dialog_new(wind,
304      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
305      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
306
307  if( func ) {
308    funcdata = g_slist_append( funcdata, (gpointer)func );
309    funcdata = g_slist_append( funcdata, data );
310  }
311  g_signal_connect(dialog, "response", G_CALLBACK(onErrorResponse), funcdata);
312  g_free(msg);
313
314  return dialog;
315}
316
317void
318errmsg( GtkWindow * wind, const char * format, ... )
319{
320    GtkWidget * dialog;
321    va_list     ap;
322
323    va_start( ap, format );
324    dialog = verrmsg_full( wind, NULL, NULL, format, ap );
325    va_end( ap );
326
327    if( NULL != wind && !GTK_WIDGET_MAPPED( GTK_WIDGET( wind ) ) )
328    {
329        g_signal_connect_swapped( wind, "map",
330                                  G_CALLBACK( gtk_widget_show ), dialog );
331    }
332    else
333    {
334        gtk_widget_show( dialog );
335    }
336}
337
338GtkWidget *
339errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
340             const char * format, ... )
341{
342    GtkWidget * dialog;
343    va_list     ap;
344
345    va_start( ap, format );
346    dialog = verrmsg_full( wind, func, data, format, ap );
347    va_end( ap );
348
349    return dialog;
350}
351
352typedef void (PopupFunc)(GtkWidget*, GdkEventButton*); 
353
354/* pop up the context menu if a user right-clicks.
355   if the row they right-click on isn't selected, select it. */
356
357gboolean
358on_tree_view_button_pressed (GtkWidget       * view,
359                             GdkEventButton  * event,
360                             gpointer          func)
361{
362  GtkTreeView * tv = GTK_TREE_VIEW( view );
363
364  if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3)
365  {
366    GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
367    GtkTreePath *path;
368    if (gtk_tree_view_get_path_at_pos (tv,
369                                       (gint) event->x,
370                                       (gint) event->y,
371                                       &path, NULL, NULL, NULL))
372    {
373      if (!gtk_tree_selection_path_is_selected (selection, path))
374      {
375        gtk_tree_selection_unselect_all (selection);
376        gtk_tree_selection_select_path (selection, path);
377      }
378      gtk_tree_path_free(path);
379    }
380   
381    ((PopupFunc*)func)(view, event);
382
383    return TRUE;
384  }
385
386  return FALSE;
387}
388
389gpointer
390tr_object_ref_sink( gpointer object )
391{
392#if GLIB_CHECK_VERSION(2,10,0)
393    g_object_ref_sink( object );
394#else
395    g_object_ref( object );
396    gtk_object_sink( GTK_OBJECT( object ) );
397#endif
398    return object;
399}
400
401void
402tr_file_trash_or_unlink( const char * filename )
403{
404    if( filename && *filename )
405    {
406        gboolean trashed = FALSE;
407#ifdef HAVE_GIO
408        GError * err = NULL;
409        GFile * file = g_file_new_for_path( filename );
410        trashed = g_file_trash( file, NULL, &err );
411        g_object_unref( G_OBJECT( file ) );
412#endif
413        if( !trashed )
414            g_unlink( filename );
415    }
416}
417
418char*
419gtr_get_help_url( void )
420{
421    const char * fmt = "http://www.transmissionbt.com/help/gtk/%d.%dx";
422    int major, minor;
423    sscanf( SHORT_VERSION_STRING, "%d.%d", &major, &minor );
424    return g_strdup_printf( fmt, major, minor/10 );
425}
426
427void
428gtr_open_file( const char * path )
429{
430    if( path )
431    {
432        gboolean opened = FALSE;
433#ifdef HAVE_GIO
434        if( !opened )
435        {
436            GFile * file = g_file_new_for_path( path );
437            char * uri = g_file_get_uri( file );
438            opened = g_app_info_launch_default_for_uri( uri, NULL, NULL );
439            g_free( uri );
440            g_object_unref( G_OBJECT( file ) );
441        }
442#endif
443        if( !opened )
444        {
445            char * argv[] = { "xdg-open", (char*)path, NULL };
446            g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
447                           NULL, NULL, NULL, NULL );
448        }
449    }
450}
451
452#define VALUE_SERVICE_NAME        "com.transmissionbt.Transmission"
453#define VALUE_SERVICE_OBJECT_PATH "/com/transmissionbt/Transmission"
454#define VALUE_SERVICE_INTERFACE   "com.transmissionbt.Transmission"
455
456gboolean
457gtr_dbus_add_torrent( const char * filename )
458{
459    static gboolean success = FALSE;
460#ifdef HAVE_DBUS_GLIB
461    DBusGProxy * proxy = NULL;
462    GError * err = NULL;
463    DBusGConnection * conn;
464    if(( conn = dbus_g_bus_get( DBUS_BUS_SESSION, &err )))
465        proxy = dbus_g_proxy_new_for_name (conn, VALUE_SERVICE_NAME,
466                                                 VALUE_SERVICE_OBJECT_PATH,
467                                                 VALUE_SERVICE_INTERFACE );
468    else if( err )
469       g_message( "err: %s", err->message );
470    if( proxy )
471        dbus_g_proxy_call( proxy, "AddFile", &err,
472                           G_TYPE_STRING, filename,
473                           G_TYPE_INVALID,
474                           G_TYPE_BOOLEAN, &success,
475                           G_TYPE_INVALID );
476    if( err )
477       g_message( "err: %s", err->message );
478#endif
479    return success;
480}
481
482GtkWidget *
483tr_button_new_from_stock( const char * stock,
484                          const char * mnemonic )
485{
486    GtkWidget * image = gtk_image_new_from_stock( stock, GTK_ICON_SIZE_BUTTON );
487    GtkWidget * button = gtk_button_new_with_mnemonic( mnemonic );
488    gtk_button_set_image( GTK_BUTTON( button ), image );
489    return button;
490}
Note: See TracBrowser for help on using the repository browser.