source: trunk/gtk/util.c @ 5301

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

typo fix when time estimation is "1 day"

  • Property svn:keywords set to Date Rev Author Id
File size: 11.7 KB
Line 
1/******************************************************************************
2 * $Id: util.c 5301 2008-03-19 02:19:34Z 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
36#include <libevent/evhttp.h>
37
38#include <libtransmission/transmission.h> /* TR_RATIO_NA, TR_RATIO_INF */
39
40#include "conf.h"
41#include "tr-prefs.h"
42#include "util.h"
43
44int
45tr_strcmp( const char * a, const char * b )
46{
47    if( a && b ) return strcmp( a, b );
48    if( a ) return 1;
49    if( b ) return -1;
50    return 0;
51}
52
53char*
54tr_strlratio( char * buf, double ratio, size_t buflen )
55{
56    if( (int)ratio == TR_RATIO_NA )
57        g_strlcpy( buf, _( "None" ), buflen );
58    else if( (int)ratio == TR_RATIO_INF )
59        g_strlcpy( buf, "\xE2\x88\x9E", buflen );
60    else if( ratio < 10.0 )
61        g_snprintf( buf, buflen, "%.2f", ratio );
62    else if( ratio < 100.0 )
63        g_snprintf( buf, buflen, "%.1f", ratio );
64    else
65        g_snprintf( buf, buflen, "%.0f", ratio );
66    return buf;
67}
68
69#define KILOBYTE_FACTOR 1024.0
70#define MEGABYTE_FACTOR (1024.0 * 1024.0)
71#define GIGABYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
72
73char*
74tr_strlsize( char * buf, guint64 size, size_t buflen )
75{
76    if( !size )
77        g_strlcpy( buf, _( "None" ), buflen );
78#if GLIB_CHECK_VERSION(2,16,0)
79    else{ 
80        char * tmp = g_format_size_for_display( size );
81        g_strlcpy( buf, tmp, buflen );
82        g_free( tmp );
83    }
84#else
85    else if( size < (guint64)KILOBYTE_FACTOR )
86        g_snprintf( buf, buflen, ngettext("%u byte", "%u bytes", (guint)size), (guint)size );
87    else {
88        gdouble displayed_size;
89        if (size < (guint64)MEGABYTE_FACTOR) {
90            displayed_size = (gdouble) size / KILOBYTE_FACTOR;
91            g_snprintf( buf, buflen, _("%.1f KB"), displayed_size );
92        } else if (size < (guint64)GIGABYTE_FACTOR) {
93            displayed_size = (gdouble) size / MEGABYTE_FACTOR;
94            g_snprintf( buf, buflen, _("%.1f MB"), displayed_size );
95        } else {
96            displayed_size = (gdouble) size / GIGABYTE_FACTOR;
97            g_snprintf( buf, buflen, _("%.1f GB"), displayed_size );
98        }
99    }
100#endif
101    return buf;
102}
103
104char*
105tr_strlspeed( char * buf, double kb_sec, size_t buflen )
106{
107    const double speed = kb_sec;
108
109    if ( speed < 1000.0 ) /* 0.0 KB to 999.9 KB */
110        g_snprintf( buf, buflen, _( "%.1f KB/s" ), speed );
111    else if( speed < 102400.0 ) /* 0.98 MB to 99.99 MB */
112        g_snprintf( buf, buflen, _( "%.2f MB/s" ), (speed/1024) );
113    else if( speed < 1024000.0 ) /* 100.0 MB to 999.9 MB */
114        g_snprintf( buf, buflen, _( "%.1f MB/s" ), (speed/1024) );
115    else /* insane speeds */
116        g_snprintf( buf, buflen, _( "%.2f GB/s" ), (speed/1048576) );
117
118    return buf;
119}
120
121char*
122tr_strltime( char * buf, int seconds, size_t buflen )
123{
124    int hours;
125    int days;
126
127    if( seconds < 0 )
128        seconds = 0;
129
130    if( seconds < 60 )
131    {
132        g_snprintf( buf, buflen, ngettext( "%'d second", "%'d seconds", (int)seconds ), (int) seconds );
133        return buf;
134    }
135
136    if( seconds < ( 60 * 60 ) )
137    {
138        const int minutes = ( seconds + 30 ) / 60;
139        g_snprintf( buf, buflen, ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
140        return buf;
141    }
142
143    hours = seconds / ( 60 * 60 );
144
145    if( seconds < ( 60 * 60 * 4 ) )
146    {
147        char h[64];
148        char m[64];
149
150        const int minutes = ( seconds - hours * 60 * 60 + 30 ) / 60;
151
152        g_snprintf( h, sizeof(h), ngettext( "%'d hour", "%'d hours", hours ), hours );
153        g_snprintf( m, sizeof(m), ngettext( "%'d minute", "%'d minutes", minutes ), minutes );
154        g_snprintf( buf, buflen, "%s, %s", h, m );
155        return buf;
156    }
157
158    if( hours < 24 )
159    {
160        g_snprintf( buf, buflen, ngettext( "%'d hour", "%'d hours", hours ), hours );
161        return buf;
162    }
163
164    days = seconds / ( 60 * 60 * 24 );
165    g_snprintf( buf, buflen, ngettext( "%'d day", "%'d days", days ), days );
166    return buf;
167}
168
169
170char *
171rfc822date (guint64 epoch_msec)
172{
173    const time_t secs = epoch_msec / 1000;
174    const struct tm tm = *localtime (&secs);
175    char buf[128];
176    strftime( buf, sizeof(buf), "%a, %d %b %Y %T %Z", &tm );
177    return g_locale_to_utf8( buf, -1, NULL, NULL, NULL );
178}
179
180gboolean
181mkdir_p( const char * path, mode_t mode )
182{
183#if GLIB_CHECK_VERSION( 2, 8, 0)
184    return !g_mkdir_with_parents( path, mode );
185#else
186    return !tr_mkdirp( path, mode );
187#endif
188}
189
190GSList *
191dupstrlist( GSList * l )
192{
193    GSList * ret = NULL;
194    for( ; l!=NULL; l=l->next )
195        ret = g_slist_prepend( ret, g_strdup( l->data ) );
196    return g_slist_reverse( ret );
197}
198
199char *
200joinstrlist(GSList *list, char *sep)
201{
202  GSList *l;
203  GString *gstr = g_string_new (NULL);
204  for (l=list; l!=NULL; l=l->next) {
205    g_string_append (gstr, (char*)l->data);
206    if (l->next != NULL)
207      g_string_append (gstr, (sep));
208  }
209  return g_string_free (gstr, FALSE);
210}
211
212void
213freestrlist(GSList *list)
214{
215  g_slist_foreach (list, (GFunc)g_free, NULL);
216  g_slist_free (list);
217}
218
219char *
220decode_uri( const char * uri )
221{
222    char * filename = evhttp_decode_uri( uri );
223    char * ret = g_strdup( filename );
224    free( filename );
225    return ret;
226}
227
228GSList *
229checkfilenames( int argc, char **argv )
230{
231    int i;
232    GSList * ret = NULL;
233    char * pwd = g_get_current_dir( );
234
235    for( i=0; i<argc; ++i )
236    {
237        char * filename = g_path_is_absolute( argv[i] )
238            ? g_strdup ( argv[i] )
239            : g_build_filename( pwd, argv[i], NULL );
240
241        if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
242            ret = g_slist_prepend( ret, filename );
243        else
244            g_free( filename );
245    }
246
247    g_free( pwd );
248    return g_slist_reverse( ret );
249}
250
251char *
252getdownloaddir( void )
253{
254    static char * wd = NULL;
255    char * dir = pref_string_get( PREF_KEY_DIR_DEFAULT );
256    if ( dir == NULL ) {
257        if( wd == NULL )
258            wd = g_get_current_dir();
259        dir = g_strdup( wd );
260    }
261    return dir;
262}
263
264/**
265 * don't use more than 50% the height of the screen, nor 80% the width.
266 * but don't be too small, either -- set the minimums to 500 x 300
267 */
268void
269sizingmagic( GtkWindow         * wind,
270             GtkScrolledWindow * scroll,
271             GtkPolicyType       hscroll,
272             GtkPolicyType       vscroll )
273{
274    int width;
275    int height;
276    GtkRequisition req;
277
278    GdkScreen * screen = gtk_widget_get_screen( GTK_WIDGET( wind ) );
279
280    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER,
281                                            GTK_POLICY_NEVER );
282
283    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
284    req.height = MAX( req.height, 300 );
285    height = MIN( req.height, gdk_screen_get_height( screen ) / 5 * 4 );
286
287    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER, vscroll );
288    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
289    req.width = MAX( req.width, 500 );
290    width = MIN( req.width, gdk_screen_get_width( screen ) / 2 );
291
292    gtk_window_set_default_size( wind, width, height );
293    gtk_scrolled_window_set_policy( scroll, hscroll, vscroll );
294}
295
296static void
297onErrorResponse(GtkWidget * dialog, int resp UNUSED, gpointer glist)
298{
299    GSList * list = glist;
300    if( list )
301    {
302        callbackfunc_t func = list->data;
303        gpointer user_data = list->next->data;
304        func( user_data );
305        g_slist_free( list );
306    }
307
308    gtk_widget_destroy( dialog );
309}
310
311static GtkWidget *
312verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
313              const char * format, va_list ap )
314{
315  GtkWidget *dialog;
316  char *msg;
317  GSList *funcdata = NULL;
318
319  msg = g_strdup_vprintf(format, ap);
320
321  if(NULL == wind)
322    dialog = gtk_message_dialog_new(
323      NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
324  else
325    dialog = gtk_message_dialog_new(wind,
326      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
327      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
328
329  if( func ) {
330    funcdata = g_slist_append( funcdata, (gpointer)func );
331    funcdata = g_slist_append( funcdata, data );
332  }
333  g_signal_connect(dialog, "response", G_CALLBACK(onErrorResponse), funcdata);
334  g_free(msg);
335
336  return dialog;
337}
338
339void
340errmsg( GtkWindow * wind, const char * format, ... )
341{
342    GtkWidget * dialog;
343    va_list     ap;
344
345    va_start( ap, format );
346    dialog = verrmsg_full( wind, NULL, NULL, format, ap );
347    va_end( ap );
348
349    if( NULL != wind && !GTK_WIDGET_MAPPED( GTK_WIDGET( wind ) ) )
350    {
351        g_signal_connect_swapped( wind, "map",
352                                  G_CALLBACK( gtk_widget_show ), dialog );
353    }
354    else
355    {
356        gtk_widget_show( dialog );
357    }
358}
359
360GtkWidget *
361errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
362             const char * format, ... )
363{
364    GtkWidget * dialog;
365    va_list     ap;
366
367    va_start( ap, format );
368    dialog = verrmsg_full( wind, func, data, format, ap );
369    va_end( ap );
370
371    return dialog;
372}
373
374typedef void (PopupFunc)(GtkWidget*, GdkEventButton*); 
375
376/* pop up the context menu if a user right-clicks.
377   if the row they right-click on isn't selected, select it. */
378
379gboolean
380on_tree_view_button_pressed (GtkWidget       * view,
381                             GdkEventButton  * event,
382                             gpointer          func)
383{
384  GtkTreeView * tv = GTK_TREE_VIEW( view );
385
386  if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3)
387  {
388    GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
389    GtkTreePath *path;
390    if (gtk_tree_view_get_path_at_pos (tv,
391                                       (gint) event->x,
392                                       (gint) event->y,
393                                       &path, NULL, NULL, NULL))
394    {
395      if (!gtk_tree_selection_path_is_selected (selection, path))
396      {
397        gtk_tree_selection_unselect_all (selection);
398        gtk_tree_selection_select_path (selection, path);
399      }
400      gtk_tree_path_free(path);
401    }
402   
403    ((PopupFunc*)func)(view, event);
404
405    return TRUE;
406  }
407
408  return FALSE;
409}
410
411gpointer
412tr_object_ref_sink( gpointer object )
413{
414#if GLIB_CHECK_VERSION(2,10,0)
415    g_object_ref_sink( object );
416#else
417    g_object_ref( object );
418    gtk_object_sink( GTK_OBJECT( object ) );
419#endif
420    return object;
421}
422
423void
424tr_file_trash_or_unlink( const char * filename )
425{
426    if( filename && *filename )
427    {
428        gboolean trashed = FALSE;
429#ifdef HAVE_GIO
430        GError * err = NULL;
431        GFile * file = g_file_new_for_path( filename );
432        trashed = g_file_trash( file, NULL, &err );
433        g_object_unref( G_OBJECT( file ) );
434#endif
435        if( !trashed )
436            g_unlink( filename );
437    }
438}
Note: See TracBrowser for help on using the repository browser.