source: trunk/gtk/util.c @ 1647

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

Make evil initial window sizing magic a bit less evil and a bit less magic.

  • Property svn:keywords set to Date Rev Author Id
File size: 12.4 KB
Line 
1/******************************************************************************
2 * $Id: util.c 1647 2007-04-03 08:18:53Z 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
45gboolean
46strbool(const char *str) {
47  switch(str[0]) {
48    case 'y':
49    case 'Y':
50    case '1':
51    case 'j':
52    case 'e':
53      return TRUE;
54    default:
55      if(0 == g_ascii_strcasecmp("on", str))
56        return TRUE;
57      break;
58  }
59
60  return FALSE;
61}
62
63static const char *sizestrs[] = {
64  N_("B"), N_("KiB"), N_("MiB"), N_("GiB"), N_("TiB"), N_("PiB"), N_("EiB"),
65};
66
67char *
68readablesize(guint64 size) {
69  int ii;
70  double small = size;
71
72  for(ii = 0; ii + 1 < ALEN(sizestrs) && 1024.0 <= small / 1024.0; ii++)
73    small /= 1024.0;
74
75  if(1024.0 <= small) {
76    small /= 1024.0;
77    ii++;
78  }
79
80  return g_strdup_printf("%.*f %s", BESTDECIMAL(small), small,
81                         gettext(sizestrs[ii]));
82}
83
84#define SECONDS(s)              ((s) % 60)
85#define MINUTES(s)              ((s) / 60 % 60)
86#define HOURS(s)                ((s) / 60 / 60 % 24)
87#define DAYS(s)                 ((s) / 60 / 60 / 24 % 7)
88#define WEEKS(s)                ((s) / 60 / 60 / 24 / 7)
89
90char *
91readabletime(int secs) {
92  if(60 > secs)
93    return g_strdup_printf(_("%i %s"),
94      SECONDS(secs), ngettext("second", "seconds", SECONDS(secs)));
95  else if(60 * 60 > secs)
96    return g_strdup_printf(_("%i %s %i %s"),
97      MINUTES(secs), ngettext("minute", "minutes", MINUTES(secs)),
98      SECONDS(secs), ngettext("second", "seconds", SECONDS(secs)));
99  else if(60 * 60 * 24 > secs)
100    return g_strdup_printf(_("%i %s %i %s"),
101      HOURS(secs),   ngettext("hour", "hours", HOURS(secs)),
102      MINUTES(secs), ngettext("minute", "minutes", MINUTES(secs)));
103  else if(60 * 60 * 24 * 7 > secs)
104    return g_strdup_printf(_("%i %s %i %s"),
105      DAYS(secs),    ngettext("day", "days", DAYS(secs)),
106      HOURS(secs),   ngettext("hour", "hours", HOURS(secs)));
107  else
108    return g_strdup_printf(_("%i %s %i %s"),
109      WEEKS(secs),   ngettext("week", "weeks", WEEKS(secs)),
110      DAYS(secs),    ngettext("hour", "hours", DAYS(secs)));
111}
112
113char *
114ratiostr(guint64 down, guint64 up) {
115  double ratio;
116
117  if(0 == up && 0 == down)
118    return g_strdup(_("N/A"));
119
120  if(0 == down)
121    /* this is a UTF-8 infinity symbol */
122    return g_strdup(_("\xE2\x88\x9E"));
123
124  ratio = (double)up / (double)down;
125
126  return g_strdup_printf("%.*f", BESTDECIMAL(ratio), ratio);
127}
128
129gboolean
130mkdir_p(const char *name, mode_t mode) {
131  struct stat sb;
132  char *parent;
133  gboolean ret;
134  int oerrno;
135
136  if(0 != stat(name, &sb)) {
137    if(ENOENT != errno)
138      return FALSE;
139    parent = g_path_get_dirname(name);
140    ret = mkdir_p(parent, mode);
141    oerrno = errno;
142    g_free(parent);
143    errno = oerrno;
144    return (ret ? (0 == mkdir(name, mode)) : FALSE);
145  }
146
147  if(!S_ISDIR(sb.st_mode)) {
148    errno = ENOTDIR;
149    return FALSE;
150  }
151
152  return TRUE;
153}
154
155GList *
156dupstrlist( GList * list )
157{
158    GList * ii, * ret;
159
160    ret = NULL;
161    for( ii = g_list_first( list ); NULL != ii; ii = ii->next )
162    {
163        ret = g_list_append( ret, g_strdup( ii->data ) );
164    }
165
166    return ret;
167}
168
169char *
170joinstrlist(GList *list, char *sep) {
171  GList *ii;
172  int len;
173  char *ret, *dest;
174
175  if(0 > (len = strlen(sep) * (g_list_length(list) - 1)))
176    return NULL;
177
178  for(ii = g_list_first(list); NULL != ii; ii = ii->next)
179    len += strlen(ii->data);
180
181  dest = ret = g_new(char, len + 1);
182
183  for(ii = g_list_first(list); NULL != ii; ii = ii->next) {
184    dest = g_stpcpy(dest, ii->data);
185    if(NULL != ii->next)
186      dest = g_stpcpy(dest, sep);
187  }
188
189  return ret;
190}
191
192void
193freestrlist(GList *list) {
194  GList *ii;
195
196  if(NULL != list) {
197    for(ii = g_list_first(list); NULL != ii; ii = ii->next)
198      g_free(ii->data);
199    g_list_free(list);
200  }
201}
202
203char *
204urldecode(const char *str, int len) {
205  int ii, jj;
206  char *ret;
207  char buf[3];
208
209  if(0 >= len)
210    len = strlen(str);
211
212  for(ii = jj = 0; ii < len; ii++, jj++)
213    if('%' == str[ii])
214      ii += 2;
215
216  ret = g_new(char, jj + 1);
217
218  buf[2] = '\0';
219  for(ii = jj = 0; ii < len; ii++, jj++) {
220    switch(str[ii]) {
221      case '%':
222        if(ii + 2 < len) {
223          buf[0] = str[ii+1];
224          buf[1] = str[ii+2];
225          ret[jj] = g_ascii_strtoull(buf, NULL, 16);
226        }
227        ii += 2;
228        break;
229      case '+':
230        ret[jj] = ' ';
231      default:
232        ret[jj] = str[ii];
233    }
234  }
235  ret[jj] = '\0';
236
237  return ret;
238}
239
240GList *
241checkfilenames(int argc, char **argv) {
242  char *pwd = g_get_current_dir();
243  int ii, cd;
244  char *dirstr, *filestr;
245  GList *ret = NULL;
246
247  for(ii = 0; ii < argc; ii++) {
248    dirstr = g_path_get_dirname(argv[ii]);
249    if(!g_path_is_absolute(argv[ii])) {
250      filestr = g_build_filename(pwd, dirstr, NULL);
251      g_free(dirstr);
252      dirstr = filestr;
253    }
254    cd = chdir(dirstr);
255    g_free(dirstr);
256    if(0 > cd)
257      continue;
258    dirstr = g_get_current_dir();
259    filestr = g_path_get_basename(argv[ii]);
260    ret = g_list_append(ret, g_build_filename(dirstr, filestr, NULL));
261    g_free(dirstr);
262    g_free(filestr);
263  }
264
265  chdir(pwd);
266  g_free(pwd);
267
268  return ret;
269}
270
271guint
272addactionflag(const char *action) {
273  if(NULL == action)
274    return TR_TORNEW_SAVE_COPY;
275  else if(0 == strcmp("copy", action))
276    return TR_TORNEW_SAVE_COPY;
277  else if(0 == strcmp("move", action))
278    return TR_TORNEW_SAVE_MOVE;
279  else
280    return 0;
281}
282
283const char *
284addactionname( guint flag )
285{
286    static char name[6];
287
288    snprintf( name, sizeof name, "%s",
289              ( TR_TORNEW_SAVE_COPY & flag ? "copy" :
290              ( TR_TORNEW_SAVE_MOVE & flag ? "move" :
291                                             "leave" ) ) );
292
293    return name;
294}
295
296const char *
297getdownloaddir( void )
298{
299    static char * wd = NULL;
300    const char  * dir;
301
302    dir = tr_prefs_get( PREF_ID_DIR );
303    if( NULL == dir )
304    {
305        if( NULL == wd )
306        {
307            wd = g_new( char, MAX_PATH_LENGTH + 1 );
308            if( NULL == getcwd( wd, MAX_PATH_LENGTH + 1 ) )
309            {
310                snprintf( wd, MAX_PATH_LENGTH + 1, "." );
311            }
312        }
313        dir = wd;
314    }
315
316    return dir;
317}
318
319void
320sizingmagic( GtkWindow * wind, GtkScrolledWindow * scroll,
321             GtkPolicyType hscroll, GtkPolicyType vscroll )
322{
323    GtkRequisition req;
324    GdkScreen    * screen;
325    int            width, height;
326
327    screen = gtk_widget_get_screen( GTK_WIDGET( wind ) );
328
329    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER,
330                                    GTK_POLICY_NEVER );
331
332    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
333    height = MIN( req.height, gdk_screen_get_height( screen ) / 5 * 4 );
334
335    gtk_scrolled_window_set_policy( scroll, GTK_POLICY_NEVER, vscroll );
336
337    gtk_widget_size_request( GTK_WIDGET( wind ), &req );
338    width = MIN( req.width, gdk_screen_get_width( screen ) / 2 );
339
340    gtk_window_set_default_size( wind, width, height );
341
342    gtk_scrolled_window_set_policy( scroll, hscroll, vscroll );
343}
344
345struct action *
346action_new( int id, int flags, const char * label, const char * stock )
347{
348    struct action * act;
349
350    act        = g_new0( struct action, 1 );
351    act->id    = id;
352    act->flags = flags;
353    act->label = g_strdup( label );
354    act->stock = g_strdup( stock );
355    act->tool  = NULL;
356    act->menu  = NULL;
357
358    return act;
359}
360
361void
362action_free( struct action * act )
363{
364    g_free( act->label );
365    g_free( act->stock );
366    g_free( act );
367}
368
369GtkWidget *
370action_maketool( struct action * act, const char * key,
371                 GCallback func, gpointer data )
372{
373    GtkToolItem * item;
374
375    item = gtk_tool_button_new_from_stock( act->stock );
376    if( NULL != act->label )
377    {
378        gtk_tool_button_set_label( GTK_TOOL_BUTTON( item ), act->label );
379    }
380    g_object_set_data( G_OBJECT( item ), key, act );
381    g_signal_connect( item, "clicked", func, data );
382    gtk_widget_show( GTK_WIDGET( item ) );
383
384    return GTK_WIDGET( item );
385}
386
387GtkWidget *
388action_makemenu( struct action * act, const char * actkey,
389                 GtkAccelGroup * accel, const char * path, guint keyval,
390                 GCallback func, gpointer data )
391{
392    GtkWidget  * item, * label;
393    GdkModifierType mod;
394    GtkStockItem stock;
395    const char * name;
396    char       * joined;
397
398    mod = GDK_CONTROL_MASK;
399    name = act->label;
400    if( NULL == act->stock )
401    {
402        item = gtk_menu_item_new_with_label( act->label );
403    }
404    else
405    {
406        item = gtk_image_menu_item_new_from_stock( act->stock, NULL );
407        if( NULL == act->label )
408        {
409            if( gtk_stock_lookup( act->stock, &stock ) )
410            {
411                name = stock.label;
412                if( 0 == keyval )
413                {
414                    keyval = stock.keyval;
415                    mod    = stock.modifier;
416                }
417            }
418        }
419        else
420        {
421            label = gtk_bin_get_child( GTK_BIN( item ) );
422            gtk_label_set_text( GTK_LABEL( label ), act->label );
423           
424        }
425    }
426
427    if( NULL != accel && 0 < keyval && NULL != name )
428    {
429        joined = g_strjoin( "/", path, name, NULL );
430        gtk_accel_map_add_entry( joined, keyval, mod );
431        gtk_widget_set_accel_path( item, joined, accel );
432        g_free( joined );
433    }
434    g_object_set_data( G_OBJECT( item ), actkey, act );
435    g_signal_connect( item, "activate", func, data );
436    gtk_widget_show( item );
437
438    return item;
439}
440
441void
442errmsg( GtkWindow * wind, const char * format, ... )
443{
444    GtkWidget * dialog;
445    va_list     ap;
446
447    va_start( ap, format );
448    dialog = verrmsg_full( wind, NULL, NULL, format, ap );
449    va_end( ap );
450
451    if( NULL != wind && !GTK_WIDGET_MAPPED( GTK_WIDGET( wind ) ) )
452    {
453        g_signal_connect_swapped( wind, "map",
454                                  G_CALLBACK( gtk_widget_show ), dialog );
455    }
456    else
457    {
458        gtk_widget_show( dialog );
459    }
460}
461
462GtkWidget *
463errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
464             const char * format, ... )
465{
466    GtkWidget * dialog;
467    va_list     ap;
468
469    va_start( ap, format );
470    dialog = verrmsg_full( wind, func, data, format, ap );
471    va_end( ap );
472
473    return dialog;
474}
475
476GtkWidget *
477verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
478              const char * format, va_list ap )
479{
480  GtkWidget *dialog;
481  char *msg;
482  GList *funcdata;
483
484  msg = g_strdup_vprintf(format, ap);
485
486  if(NULL == wind)
487    dialog = gtk_message_dialog_new(
488      NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
489  else
490    dialog = gtk_message_dialog_new(wind,
491      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
492      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", msg);
493
494  if(NULL == func)
495    funcdata = NULL;
496  else
497    funcdata = g_list_append(g_list_append(NULL, func), data);
498  g_signal_connect(dialog, "response", G_CALLBACK(errcb), funcdata);
499  g_free(msg);
500
501  return dialog;
502}
503
504static void
505errcb(GtkWidget *widget, int resp SHUTUP, gpointer data) {
506  GList *funcdata;
507  callbackfunc_t func;
508
509  if(NULL != data) {
510    funcdata = g_list_first(data);
511    func = funcdata->data;
512    data = funcdata->next->data;
513    func(data);
514    g_list_free(funcdata);
515  }
516
517  gtk_widget_destroy(widget);
518}
Note: See TracBrowser for help on using the repository browser.