source: trunk/gtk/util.c @ 5662

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

(gtk) remember window size & location between sessions

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