source: trunk/gtk/util.c @ 3821

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

cruft removal: use tr_stat's ratio field instead of calculating it ourself.

  • Property svn:keywords set to Date Rev Author Id
File size: 10.5 KB
Line 
1/******************************************************************************
2 * $Id: util.c 3821 2007-11-15 04:19:53Z 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
124gboolean
125mkdir_p(const char *name, mode_t mode)
126{
127#if GLIB_CHECK_VERSION(2,8,0)
128    return !g_mkdir_with_parents( name, mode );
129#else
130    struct stat sb;
131    char *parent;
132    gboolean ret;
133    int oerrno;
134
135    if(0 != stat(name, &sb)) {
136      if(ENOENT != errno)
137        return FALSE;
138      parent = g_path_get_dirname(name);
139      ret = mkdir_p(parent, mode);
140      oerrno = errno;
141      g_free(parent);
142      errno = oerrno;
143      return (ret ? (0 == mkdir(name, mode)) : FALSE);
144    }
145
146    if(!S_ISDIR(sb.st_mode)) {
147      errno = ENOTDIR;
148      return FALSE;
149    }
150
151    return TRUE;
152#endif
153}
154
155GList *
156dupstrlist( GList * l )
157{
158    GList * ret = NULL;
159    for( ; l!=NULL; l=l->next )
160        ret = g_list_prepend( ret, g_strdup( l->data ) );
161    return g_list_reverse( ret );
162}
163
164char *
165joinstrlist(GList *list, char *sep)
166{
167  GList *l;
168  GString *gstr = g_string_new (NULL);
169  for (l=list; l!=NULL; l=l->next) {
170    g_string_append (gstr, (char*)l->data);
171    if (l->next != NULL)
172      g_string_append (gstr, (sep));
173  }
174  return g_string_free (gstr, FALSE);
175}
176
177void
178freestrlist(GList *list)
179{
180  g_list_foreach (list, (GFunc)g_free, NULL);
181  g_list_free (list);
182}
183
184char *
185urldecode(const char *str, int len) {
186  int ii, jj;
187  char *ret;
188
189  if( len <= 0 )
190      len = strlen( str );
191
192  for(ii = jj = 0; ii < len; ii++, jj++)
193    if('%' == str[ii])
194      ii += 2;
195
196  ret = g_new(char, jj + 1);
197
198  for(ii = jj = 0; ii < len; ii++, jj++) {
199    switch(str[ii]) {
200      case '%':
201        if(ii + 2 < len) {
202          char buf[3] = { str[ii+1], str[ii+2], '\0' };
203          ret[jj] = g_ascii_strtoull(buf, NULL, 16);
204        }
205        ii += 2;
206        break;
207      case '+':
208        ret[jj] = ' ';
209      default:
210        ret[jj] = str[ii];
211    }
212  }
213  ret[jj] = '\0';
214
215  return ret;
216}
217
218GList *
219checkfilenames( int argc, char **argv )
220{
221    int i;
222    GList * ret = NULL;
223    char * pwd = g_get_current_dir( );
224
225    for( i=0; i<argc; ++i )
226    {
227        char * filename = g_path_is_absolute( argv[i] )
228            ? g_strdup ( argv[i] )
229            : g_build_filename( pwd, argv[i], NULL );
230
231        if( g_file_test( filename, G_FILE_TEST_EXISTS ) )
232            ret = g_list_append( ret, filename );
233        else
234            g_free( filename );
235    }
236
237    g_free( pwd );
238    return ret;
239}
240
241enum tr_torrent_action
242toraddaction( const char * action )
243{
244    if( !action || !strcmp( "copy", action ) )
245        return TR_TOR_COPY;
246
247    if( !strcmp( "move", action ) )
248        return TR_TOR_MOVE;
249
250    return TR_TOR_LEAVE;
251}
252
253const char *
254toractionname( enum tr_torrent_action action )
255{
256    switch( action )
257    {
258        case TR_TOR_COPY:
259            return "copy";
260
261        case TR_TOR_MOVE:
262            return "move";
263
264        default:
265            return "leave";
266    }
267}
268
269char *
270getdownloaddir( void )
271{
272    static char * wd = NULL;
273    char * dir = pref_string_get( PREF_KEY_DIR_DEFAULT );
274    if ( dir == NULL ) {
275        if( wd == NULL )
276            wd = g_get_current_dir();
277        dir = g_strdup( wd );
278    }
279    return dir;
280}
281
282/**
283 * don't use more than 50% the height of the screen, nor 80% the width.
284 * but don't be too small, either -- set the minimums to 500 x 300
285 */
286void
287sizingmagic( GtkWindow         * wind,
288             GtkScrolledWindow * scroll,
289             GtkPolicyType       hscroll,
290             GtkPolicyType       vscroll )
291{
292    int width;
293    int height;
294    GtkRequisition req;
295
296    GdkScreen * screen = gtk_widget_get_screen( GTK_WIDGET( wind ) );
297
298    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER,
299                                            GTK_POLICY_NEVER );
300
301    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
302    req.height = MAX( req.height, 300 );
303    height = MIN( req.height, gdk_screen_get_height( screen ) / 5 * 4 );
304
305    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER, vscroll );
306    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
307    req.width = MAX( req.width, 500 );
308    width = MIN( req.width, gdk_screen_get_width( screen ) / 2 );
309
310    gtk_window_set_default_size( wind, width, height );
311    gtk_scrolled_window_set_policy( scroll, hscroll, vscroll );
312}
313
314void
315errmsg( GtkWindow * wind, const char * format, ... )
316{
317    GtkWidget * dialog;
318    va_list     ap;
319
320    va_start( ap, format );
321    dialog = verrmsg_full( wind, NULL, NULL, format, ap );
322    va_end( ap );
323
324    if( NULL != wind && !GTK_WIDGET_MAPPED( GTK_WIDGET( wind ) ) )
325    {
326        g_signal_connect_swapped( wind, "map",
327                                  G_CALLBACK( gtk_widget_show ), dialog );
328    }
329    else
330    {
331        gtk_widget_show( dialog );
332    }
333}
334
335GtkWidget *
336errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
337             const char * format, ... )
338{
339    GtkWidget * dialog;
340    va_list     ap;
341
342    va_start( ap, format );
343    dialog = verrmsg_full( wind, func, data, format, ap );
344    va_end( ap );
345
346    return dialog;
347}
348
349GtkWidget *
350verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
351              const char * format, va_list ap )
352{
353  GtkWidget *dialog;
354  char *msg;
355  GList *funcdata;
356
357  msg = g_strdup_vprintf(format, ap);
358
359  if(NULL == wind)
360    dialog = gtk_message_dialog_new(
361      NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
362  else
363    dialog = gtk_message_dialog_new(wind,
364      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
365      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
366
367  if(NULL == func)
368    funcdata = NULL;
369  else
370      funcdata = g_list_append(g_list_append(NULL, (void *) func), data);
371  g_signal_connect(dialog, "response", G_CALLBACK(errcb), funcdata);
372  g_free(msg);
373
374  return dialog;
375}
376
377static void
378errcb(GtkWidget *widget, int resp SHUTUP, gpointer data) {
379  GList *funcdata;
380  callbackfunc_t func;
381
382  if(NULL != data) {
383    funcdata = g_list_first(data);
384    func = (callbackfunc_t) funcdata->data;
385    data = funcdata->next->data;
386    func(data);
387    g_list_free(funcdata);
388  }
389
390  gtk_widget_destroy(widget);
391}
392
393typedef void (PopupFunc)(GtkWidget*, GdkEventButton*); 
394
395/* pop up the context menu if a user right-clicks.
396   if the row they right-click on isn't selected, select it. */
397
398gboolean
399on_tree_view_button_pressed (GtkWidget       * view,
400                             GdkEventButton  * event,
401                             gpointer          func)
402{
403  GtkTreeView * tv = GTK_TREE_VIEW( view );
404
405  if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3)
406  {
407    GtkTreeSelection * selection = gtk_tree_view_get_selection(tv);
408    GtkTreePath *path;
409    if (gtk_tree_view_get_path_at_pos (tv,
410                                       (gint) event->x,
411                                       (gint) event->y,
412                                       &path, NULL, NULL, NULL))
413    {
414      if (!gtk_tree_selection_path_is_selected (selection, path))
415      {
416        gtk_tree_selection_unselect_all (selection);
417        gtk_tree_selection_select_path (selection, path);
418      }
419      gtk_tree_path_free(path);
420    }
421   
422    ((PopupFunc*)func)(view, event);
423
424    return TRUE;
425  }
426
427  return FALSE;
428}
429
Note: See TracBrowser for help on using the repository browser.