source: trunk/gtk/util.c @ 2392

Last change on this file since 2392 was 2392, checked in by joshe, 15 years ago

Replace bzero() with memset().
Define AF_LOCAL and SUN_LEN for systems that lack them.
Don't mix function and non function pointers without casting.
Fix a potential type mismatch in assignment.

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