source: trunk/gtk/util.c @ 3648

Last change on this file since 3648 was 3648, checked in by charles, 15 years ago

ensure dates in the torrent inspector are readable by converting them from the system's locale to UTF-8. (bug found and patched by goyko, ticket #435)

  • Property svn:keywords set to Date Rev Author Id
File size: 10.8 KB
Line 
1/******************************************************************************
2 * $Id: util.c 3648 2007-10-29 23:49:00Z charles $
3 *
4 * Copyright (c) 2005-2007 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 <sys/types.h>
26#include <sys/stat.h>
27#include <errno.h>
28#include <signal.h>
29#include <stdarg.h>
30#include <string.h>
31#include <unistd.h>
32
33#include <gtk/gtk.h>
34#include <glib/gi18n.h>
35
36#include "tr_prefs.h"
37#include "tr_torrent.h"
38#include "conf.h"
39#include "util.h"
40
41#define BESTDECIMAL(d) ( (d)<100 ? 1 : 0 )
42
43static void
44errcb(GtkWidget *wind, int resp, gpointer data);
45
46int
47tr_strcmp( const char * a, const char * b )
48{
49    if( a && b ) return strcmp( a, b );
50    if( a ) return 1;
51    if( b ) return -1;
52    return 0;
53}
54
55static const char *sizestrs[] = {
56  N_("B"), N_("KiB"), N_("MiB"), N_("GiB"), N_("TiB"), N_("PiB"), N_("EiB"),
57};
58
59char *
60readablesize(guint64 size) {
61  int ii;
62  double small = size;
63
64  if( !size )
65    return g_strdup_printf( _("None") );
66
67  for(ii = 0; ii + 1 < ALEN(sizestrs) && 1024.0 <= small / 1024.0; ii++)
68    small /= 1024.0;
69
70  if(1024.0 <= small) {
71    small /= 1024.0;
72    ii++;
73  }
74
75  return g_strdup_printf("%.*f %s", BESTDECIMAL(small), small,
76                         gettext(sizestrs[ii]));
77}
78
79char *
80readablespeed (double KiBps)
81{
82  const guint64 bps = KiBps * 1024;
83  char * str = readablesize (bps);
84  char * ret = bps ? g_strdup_printf ("%s/s", str) : g_strdup( str );
85  g_free (str);
86  return ret;
87}
88 
89
90#define SECONDS(s)              ((s) % 60)
91#define MINUTES(s)              ((s) / 60 % 60)
92#define HOURS(s)                ((s) / 60 / 60 % 24)
93#define DAYS(s)                 ((s) / 60 / 60 / 24 % 7)
94
95char *
96readabletime(int secs) {
97  if(60 > secs)
98    return g_strdup_printf(_("%i %s"),
99      SECONDS(secs), ngettext("sec", "secs", SECONDS(secs)));
100  else if(60 * 60 > secs)
101    return g_strdup_printf(_("%i %s %i %s"),
102      MINUTES(secs), ngettext("min", "mins", MINUTES(secs)),
103      SECONDS(secs), ngettext("sec", "secs", SECONDS(secs)));
104  else if(60 * 60 * 24 > secs)
105    return g_strdup_printf(_("%i %s %i %s"),
106      HOURS(secs),   ngettext("hr", "hrs", HOURS(secs)),
107      MINUTES(secs), ngettext("min", "mins", MINUTES(secs)));
108  else
109    return g_strdup_printf(_("%i %s %i %s"),
110      DAYS(secs),    ngettext("day", "days", DAYS(secs)),
111      HOURS(secs),   ngettext("hr", "hrs", HOURS(secs)));
112}
113
114char *
115rfc822date (guint64 epoch_msec)
116{
117    const time_t secs = epoch_msec / 1000;
118    const struct tm tm = *localtime (&secs);
119    char buf[128];
120    strftime( buf, sizeof(buf), "%a, %d %b %Y %T %Z", &tm );
121    return g_locale_to_utf8( buf, -1, NULL, NULL, NULL );
122}
123
124char *
125ratiostr(guint64 down, guint64 up) {
126  double ratio;
127
128  if(0 == up && 0 == down)
129    return g_strdup(_("N/A"));
130
131  if(0 == down)
132    /* this is a UTF-8 infinity symbol */
133    return g_strdup("\xE2\x88\x9E");
134
135  ratio = (double)up / (double)down;
136
137  return g_strdup_printf("%.*f", BESTDECIMAL(ratio), ratio);
138}
139
140gboolean
141mkdir_p(const char *name, mode_t mode)
142{
143#if GLIB_CHECK_VERSION(2,8,0)
144    return !g_mkdir_with_parents( name, mode );
145#else
146    struct stat sb;
147    char *parent;
148    gboolean ret;
149    int oerrno;
150
151    if(0 != stat(name, &sb)) {
152      if(ENOENT != errno)
153        return FALSE;
154      parent = g_path_get_dirname(name);
155      ret = mkdir_p(parent, mode);
156      oerrno = errno;
157      g_free(parent);
158      errno = oerrno;
159      return (ret ? (0 == mkdir(name, mode)) : FALSE);
160    }
161
162    if(!S_ISDIR(sb.st_mode)) {
163      errno = ENOTDIR;
164      return FALSE;
165    }
166
167    return TRUE;
168#endif
169}
170
171GList *
172dupstrlist( GList * l )
173{
174    GList * ret = NULL;
175    for( ; l!=NULL; l=l->next )
176        ret = g_list_prepend( ret, g_strdup( l->data ) );
177    return g_list_reverse( ret );
178}
179
180char *
181joinstrlist(GList *list, char *sep)
182{
183  GList *l;
184  GString *gstr = g_string_new (NULL);
185  for (l=list; l!=NULL; l=l->next) {
186    g_string_append (gstr, (char*)l->data);
187    if (l->next != NULL)
188      g_string_append (gstr, (sep));
189  }
190  return g_string_free (gstr, FALSE);
191}
192
193void
194freestrlist(GList *list)
195{
196  g_list_foreach (list, (GFunc)g_free, NULL);
197  g_list_free (list);
198}
199
200char *
201urldecode(const char *str, int len) {
202  int ii, jj;
203  char *ret;
204
205  if( len <= 0 )
206      len = strlen( str );
207
208  for(ii = jj = 0; ii < len; ii++, jj++)
209    if('%' == str[ii])
210      ii += 2;
211
212  ret = g_new(char, jj + 1);
213
214  for(ii = jj = 0; ii < len; ii++, jj++) {
215    switch(str[ii]) {
216      case '%':
217        if(ii + 2 < len) {
218          char buf[3] = { str[ii+1], str[ii+2], '\0' };
219          ret[jj] = g_ascii_strtoull(buf, NULL, 16);
220        }
221        ii += 2;
222        break;
223      case '+':
224        ret[jj] = ' ';
225      default:
226        ret[jj] = str[ii];
227    }
228  }
229  ret[jj] = '\0';
230
231  return ret;
232}
233
234GList *
235checkfilenames( int argc, char **argv )
236{
237    int i;
238    GList * ret = NULL;
239    char * pwd = g_get_current_dir( );
240
241    for( i=0; i<argc; ++i )
242    {
243        char * filename = g_path_is_absolute( argv[i] )
244            ? g_strdup ( argv[i] )
245            : g_build_filename( pwd, argv[i], NULL );
246
247        if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
248            ret = g_list_append( ret, filename );
249        else
250            g_free( filename );
251    }
252
253    g_free( pwd );
254    return ret;
255}
256
257enum tr_torrent_action
258toraddaction( const char * action )
259{
260    if( !action || !strcmp( "copy", action ) )
261        return TR_TOR_COPY;
262
263    if( !strcmp( "move", action ) )
264        return TR_TOR_MOVE;
265
266    return TR_TOR_LEAVE;
267}
268
269const char *
270toractionname( enum tr_torrent_action action )
271{
272    switch( action )
273    {
274        case TR_TOR_COPY:
275            return "copy";
276
277        case TR_TOR_MOVE:
278            return "move";
279
280        default:
281            return "leave";
282    }
283}
284
285char *
286getdownloaddir( void )
287{
288    static char * wd = NULL;
289    char * dir = pref_string_get( PREF_KEY_DIR_DEFAULT );
290    if ( dir == NULL ) {
291        if( wd == NULL )
292            wd = g_get_current_dir();
293        dir = g_strdup( wd );
294    }
295    return dir;
296}
297
298/**
299 * don't use more than 50% the height of the screen, nor 80% the width.
300 * but don't be too small, either -- set the minimums to 500 x 300
301 */
302void
303sizingmagic( GtkWindow         * wind,
304             GtkScrolledWindow * scroll,
305             GtkPolicyType       hscroll,
306             GtkPolicyType       vscroll )
307{
308    int width;
309    int height;
310    GtkRequisition req;
311
312    GdkScreen * screen = gtk_widget_get_screen( GTK_WIDGET( wind ) );
313
314    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER,
315                                            GTK_POLICY_NEVER );
316
317    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
318    req.height = MAX( req.height, 300 );
319    height = MIN( req.height, gdk_screen_get_height( screen ) / 5 * 4 );
320
321    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER, vscroll );
322    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
323    req.width = MAX( req.width, 500 );
324    width = MIN( req.width, gdk_screen_get_width( screen ) / 2 );
325
326    gtk_window_set_default_size( wind, width, height );
327    gtk_scrolled_window_set_policy( scroll, hscroll, vscroll );
328}
329
330void
331errmsg( GtkWindow * wind, const char * format, ... )
332{
333    GtkWidget * dialog;
334    va_list     ap;
335
336    va_start( ap, format );
337    dialog = verrmsg_full( wind, NULL, NULL, format, ap );
338    va_end( ap );
339
340    if( NULL != wind && !GTK_WIDGET_MAPPED( GTK_WIDGET( wind ) ) )
341    {
342        g_signal_connect_swapped( wind, "map",
343                                  G_CALLBACK( gtk_widget_show ), dialog );
344    }
345    else
346    {
347        gtk_widget_show( dialog );
348    }
349}
350
351GtkWidget *
352errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
353             const char * format, ... )
354{
355    GtkWidget * dialog;
356    va_list     ap;
357
358    va_start( ap, format );
359    dialog = verrmsg_full( wind, func, data, format, ap );
360    va_end( ap );
361
362    return dialog;
363}
364
365GtkWidget *
366verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
367              const char * format, va_list ap )
368{
369  GtkWidget *dialog;
370  char *msg;
371  GList *funcdata;
372
373  msg = g_strdup_vprintf(format, ap);
374
375  if(NULL == wind)
376    dialog = gtk_message_dialog_new(
377      NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
378  else
379    dialog = gtk_message_dialog_new(wind,
380      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
381      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
382
383  if(NULL == func)
384    funcdata = NULL;
385  else
386      funcdata = g_list_append(g_list_append(NULL, (void *) func), data);
387  g_signal_connect(dialog, "response", G_CALLBACK(errcb), funcdata);
388  g_free(msg);
389
390  return dialog;
391}
392
393static void
394errcb(GtkWidget *widget, int resp SHUTUP, gpointer data) {
395  GList *funcdata;
396  callbackfunc_t func;
397
398  if(NULL != data) {
399    funcdata = g_list_first(data);
400    func = (callbackfunc_t) funcdata->data;
401    data = funcdata->next->data;
402    func(data);
403    g_list_free(funcdata);
404  }
405
406  gtk_widget_destroy(widget);
407}
408
409typedef void (PopupFunc)(GtkWidget*, GdkEventButton*); 
410
411/* pop up the context menu if a user right-clicks.
412   if the row they right-click on isn't selected, select it. */
413
414gboolean
415on_tree_view_button_pressed (GtkWidget       * view,
416                             GdkEventButton  * event,
417                             gpointer          func)
418{
419  GtkTreeView * tv = GTK_TREE_VIEW( view );
420
421  if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3)
422  {
423    GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
424    GtkTreePath *path;
425    if (gtk_tree_view_get_path_at_pos (tv,
426                                       (gint) event->x,
427                                       (gint) event->y,
428                                       &path, NULL, NULL, NULL))
429    {
430      if (!gtk_tree_selection_path_is_selected (selection, path))
431      {
432        gtk_tree_selection_unselect_all (selection);
433        gtk_tree_selection_select_path (selection, path);
434      }
435      gtk_tree_path_free(path);
436    }
437   
438    ((PopupFunc*)func)(view, event);
439
440    return TRUE;
441  }
442
443  return FALSE;
444}
445
Note: See TracBrowser for help on using the repository browser.