source: trunk/gtk/dialogs.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: 22.9 KB
Line 
1/******************************************************************************
2 * $Id: dialogs.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 <errno.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <gtk/gtk.h>
30#include <glib/gi18n.h>
31
32#include "conf.h"
33#include "dialogs.h"
34#include "tr_cell_renderer_progress.h"
35#include "tr_icon.h"
36#include "tr_prefs.h"
37#include "util.h"
38
39#include "transmission.h"
40
41#define UPDATE_INTERVAL         1000
42#define PREFNAME                "transmission-dialog-pref-name"
43#define FILESWIND_EXTRA_INDENT  4
44
45#define STRIPROOT( path )                                                     \
46    ( g_path_is_absolute( (path) ) ? g_path_skip_root( (path) ) : (path) )
47
48struct addcb {
49  add_torrents_func_t addfunc;
50  void *data;
51  gboolean autostart;
52  gboolean usingaltdir;
53  GtkFileChooser *altdir;
54  GtkButtonBox *altbox;
55};
56
57struct dirdata
58{
59    add_torrents_func_t addfunc;
60    void              * cbdata;
61    GList             * files;
62    guint               flags;
63};
64
65struct quitdata
66{
67    callbackfunc_t func;
68    void         * cbdata;
69};
70
71struct fileswind
72{
73    GtkWidget    * widget;
74    TrTorrent    * tor;
75    GtkTreeModel * model;
76    guint          timer;
77};
78
79static void
80autoclick(GtkWidget *widget, gpointer gdata);
81static void
82dirclick(GtkWidget *widget, gpointer gdata);
83static void
84addresp(GtkWidget *widget, gint resp, gpointer gdata);
85static void
86promptresp( GtkWidget * widget, gint resp, gpointer data );
87static void
88quitresp( GtkWidget * widget, gint resp, gpointer data );
89static void
90stylekludge( GObject * obj, GParamSpec * spec, gpointer data );
91static void
92fileswinddead( GtkWidget * widget, gpointer data );
93static void
94filestorclosed( gpointer data, GObject * tor );
95static void
96parsepath( GtkTreeStore * store, GtkTreeIter * ret,
97           const char * path, int index, uint64_t size );
98static uint64_t
99getdirtotals( GtkTreeStore * store, GtkTreeIter * parent );
100static gboolean
101fileswindupdate( gpointer data );
102static float
103updateprogress( GtkTreeModel * model, GtkTreeIter * parent,
104                uint64_t total, float * progress );
105
106void
107makeaddwind(GtkWindow *parent, add_torrents_func_t addfunc, void *cbdata) {
108  GtkWidget *wind = gtk_file_chooser_dialog_new(_("Add a Torrent"), parent,
109    GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
110    GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
111  struct addcb *data = g_new(struct addcb, 1);
112  GtkWidget *vbox = gtk_vbox_new(FALSE, 3);
113  GtkWidget *bbox = gtk_hbutton_box_new();
114  GtkWidget *autocheck = gtk_check_button_new_with_mnemonic(
115    _("Automatically _start torrent"));
116  GtkWidget *dircheck = gtk_check_button_new_with_mnemonic(
117    _("Use alternate _download directory"));
118  GtkFileFilter *filter = gtk_file_filter_new();
119  GtkFileFilter *unfilter = gtk_file_filter_new();
120  GtkWidget *getdir = gtk_file_chooser_button_new(
121    _("Choose a download directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
122  const char * pref;
123
124  data->addfunc = addfunc;
125  data->data = cbdata;
126  data->autostart = TRUE;
127  data->usingaltdir = FALSE;
128  data->altdir = GTK_FILE_CHOOSER(getdir);
129  data->altbox = GTK_BUTTON_BOX(bbox);
130
131  gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_EDGE);
132  gtk_box_pack_start_defaults(GTK_BOX(bbox), dircheck);
133  gtk_box_pack_start_defaults(GTK_BOX(bbox), getdir);
134
135  gtk_box_pack_start_defaults(GTK_BOX(vbox), autocheck);
136  gtk_box_pack_start_defaults(GTK_BOX(vbox), bbox);
137
138  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autocheck), TRUE);
139  pref = tr_prefs_get( PREF_ID_DIR );
140  if( NULL != pref )
141  {
142      gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( wind ), pref );
143  }
144
145  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dircheck), FALSE);
146  gtk_widget_set_sensitive(getdir, FALSE);
147
148  gtk_file_filter_set_name(filter, _("Torrent files"));
149  gtk_file_filter_add_pattern(filter, "*.torrent");
150  gtk_file_filter_set_name(unfilter, _("All files"));
151  gtk_file_filter_add_pattern(unfilter, "*");
152
153  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wind), filter);
154  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wind), unfilter);
155  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(wind), TRUE);
156  gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(wind), vbox);
157
158  g_signal_connect(G_OBJECT(autocheck), "clicked", G_CALLBACK(autoclick),data);
159  g_signal_connect(G_OBJECT(dircheck), "clicked", G_CALLBACK(dirclick), data);
160  g_signal_connect(G_OBJECT(wind), "response", G_CALLBACK(addresp), data);
161
162  gtk_widget_show_all(wind);
163}
164
165static void
166autoclick(GtkWidget *widget, gpointer gdata) {
167  struct addcb *data = gdata;
168
169  data->autostart = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
170}
171
172static void
173dirclick(GtkWidget *widget, gpointer gdata) {
174  struct addcb *data = gdata;
175
176  data->usingaltdir = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
177  gtk_widget_set_sensitive(GTK_WIDGET(data->altdir), data->usingaltdir);
178}
179
180static void
181addresp(GtkWidget *widget, gint resp, gpointer gdata) {
182  struct addcb *data = gdata;
183  GSList *files, *ii;
184  GList *stupidgtk;
185  int flags;
186  char *dir;
187
188  if(GTK_RESPONSE_ACCEPT == resp) {
189    dir = NULL;
190    if(data->usingaltdir)
191      dir = gtk_file_chooser_get_filename(data->altdir);
192    files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
193    stupidgtk = NULL;
194    for(ii = files; NULL != ii; ii = ii->next)
195      stupidgtk = g_list_append(stupidgtk, ii->data);
196    flags = ( data->autostart ? TR_TORNEW_RUNNING : TR_TORNEW_PAUSED );
197    flags |= addactionflag( tr_prefs_get( PREF_ID_ADDSTD ) );
198    data->addfunc( data->data, NULL, stupidgtk, dir, flags );
199    if(NULL != dir)
200      g_free(dir);
201    g_slist_free(files);
202    freestrlist(stupidgtk);
203  }
204
205  g_free( data );
206  gtk_widget_destroy(widget);
207}
208
209#define INFOLINE(tab, ii, nam, val) \
210  do { \
211    char *txt = g_markup_printf_escaped("<b>%s</b>", nam); \
212    GtkWidget *wid = gtk_label_new(NULL); \
213    gtk_misc_set_alignment(GTK_MISC(wid), 0, .5); \
214    gtk_label_set_markup(GTK_LABEL(wid), txt); \
215    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 0, 1, ii, ii + 1); \
216    wid = gtk_label_new(val); \
217    gtk_label_set_selectable(GTK_LABEL(wid), TRUE); \
218    gtk_misc_set_alignment(GTK_MISC(wid), 0, .5); \
219    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 1, 2, ii, ii + 1); \
220    ii++; \
221    g_free(txt); \
222  } while(0)
223
224#define INFOLINEF(tab, ii, fmt, nam, val) \
225  do { \
226    char *buf = g_strdup_printf(fmt, val); \
227    INFOLINE(tab, ii, nam, buf); \
228    g_free(buf); \
229  } while(0)
230
231#define INFOLINEA(tab, ii, nam, val) \
232  do { \
233    char *buf = val; \
234    INFOLINE(tab, ii, nam, buf); \
235    g_free(buf); \
236  } while(0)
237
238#define INFOSEP(tab, ii) \
239  do { \
240    GtkWidget *wid = gtk_hseparator_new(); \
241    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 0, 2, ii, ii + 1); \
242    ii++; \
243  } while(0)
244
245void
246makeinfowind(GtkWindow *parent, TrTorrent *tor) {
247  tr_stat_t *sb;
248  tr_info_t *in;
249  GtkWidget *wind, *label;
250  int ii;
251  char *str;
252  const int rowcount = 15;
253  GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
254
255  /* XXX should use model and update this window regularly */
256
257  sb = tr_torrent_stat(tor);
258  in = tr_torrent_info(tor);
259  str = g_strdup_printf(_("%s Properties"), in->name);
260  wind = gtk_dialog_new_with_buttons(str, parent,
261    GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
262    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
263  g_free(str);
264
265  gtk_window_set_role( GTK_WINDOW( wind ), "tr-info" );
266  gtk_widget_set_name(wind, "TransmissionDialog");
267  gtk_table_set_col_spacings(GTK_TABLE(table), 12);
268  gtk_table_set_row_spacings(GTK_TABLE(table), 12);
269  gtk_dialog_set_default_response(GTK_DIALOG(wind), GTK_RESPONSE_ACCEPT);
270  gtk_container_set_border_width(GTK_CONTAINER(table), 6);
271  gtk_window_set_resizable(GTK_WINDOW(wind), FALSE);
272
273  label = gtk_label_new(NULL);
274  gtk_label_set_selectable(GTK_LABEL(label), TRUE);
275  str = g_markup_printf_escaped("<big>%s</big>", in->name);
276  gtk_label_set_markup(GTK_LABEL(label), str);
277  g_free(str);
278  gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
279
280  ii = 1;
281
282  INFOSEP(table, ii);
283
284  if(80 == sb->tracker->port)
285    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s",
286              sb->tracker->address));
287  else
288    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s:%i",
289              sb->tracker->address, sb->tracker->port));
290  INFOLINE(table, ii, _("Announce:"), sb->tracker->announce);
291  INFOLINEA(table, ii, _("Piece Size:"), readablesize(in->pieceSize));
292  INFOLINEF(table, ii, "%i", _("Pieces:"), in->pieceCount);
293  INFOLINEA(table, ii, _("Total Size:"), readablesize(in->totalSize));
294  if(0 > sb->seeders)
295    INFOLINE(table, ii, _("Seeders:"), _("?"));
296  else
297    INFOLINEF(table, ii, "%i", _("Seeders:"), sb->seeders);
298  if(0 > sb->leechers)
299    INFOLINE(table, ii, _("Leechers:"), _("?"));
300  else
301    INFOLINEF(table, ii, "%i", _("Leechers:"), sb->leechers);
302  if(0 > sb->completedFromTracker)
303    INFOLINE(table, ii, _("Completed:"), _("?"));
304  else
305    INFOLINEF(table, ii, "%i", _("Completed:"), sb->completedFromTracker);
306
307  INFOSEP(table, ii);
308
309  INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tr_torrent_handle(tor)));
310  INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb->downloaded));
311  INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb->uploaded));
312
313  INFOSEP(table, ii);
314
315  g_assert(rowcount == ii);
316
317  gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
318  g_signal_connect(G_OBJECT(wind), "response",
319                   G_CALLBACK(gtk_widget_destroy), NULL);
320  gtk_widget_show_all(wind);
321}
322
323void
324promptfordir( GtkWindow * parent, add_torrents_func_t addfunc, void *cbdata,
325              GList * files, guint flags )
326{
327    struct dirdata * stuff;
328    GtkWidget      * wind;
329
330    stuff = g_new( struct dirdata, 1 );
331    stuff->addfunc = addfunc;
332    stuff->cbdata  = cbdata;
333    stuff->files   = dupstrlist( files );
334    stuff->flags   = flags;
335
336    wind =  gtk_file_chooser_dialog_new( _("Choose a directory"), parent,
337                                         GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
338                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
339                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
340                                         NULL );
341    gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( wind ), TRUE );
342    gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( wind ), FALSE );
343    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( wind ),
344                                   getdownloaddir() );
345
346    g_signal_connect( G_OBJECT( wind ), "response",
347                      G_CALLBACK( promptresp ), stuff );
348
349    gtk_widget_show_all(wind);
350}
351
352static void
353promptresp( GtkWidget * widget, gint resp, gpointer data )
354{
355    struct dirdata * stuff;
356    char           * dir;
357
358    stuff = data;
359
360    if( GTK_RESPONSE_ACCEPT == resp )
361    {
362        dir = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( widget ) );
363        /* it seems that we will always get a directory */
364        g_assert( NULL != dir );
365        stuff->addfunc( stuff->cbdata, NULL, stuff->files, dir, stuff->flags );
366        g_free( dir );
367    }
368
369    freestrlist( stuff->files );
370    g_free( stuff );
371    gtk_widget_destroy( widget );
372}
373
374void
375askquit( GtkWindow * parent, callbackfunc_t func, void * cbdata )
376{
377    struct quitdata * stuff;
378    GtkWidget * wind;
379
380    stuff          = g_new( struct quitdata, 1 );
381    stuff->func    = func;
382    stuff->cbdata  = cbdata;
383
384    wind = gtk_message_dialog_new( parent, GTK_DIALOG_DESTROY_WITH_PARENT,
385                                   GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
386                                   _("Are you sure you want to quit %s?"),
387                                   g_get_application_name() );
388    gtk_dialog_set_default_response( GTK_DIALOG( wind ), GTK_RESPONSE_YES );
389    g_signal_connect( G_OBJECT( wind ), "response",
390                      G_CALLBACK( quitresp ), stuff );
391
392    gtk_widget_show_all( wind );
393}
394
395static void
396quitresp( GtkWidget * widget, gint resp, gpointer data )
397{
398    struct quitdata * stuff;
399
400    stuff = data;
401
402    if( GTK_RESPONSE_YES == resp )
403    {
404        stuff->func( stuff->cbdata );
405    }
406
407    g_free( stuff );
408    gtk_widget_destroy( widget );
409}
410
411enum filescols
412{
413    FC_STOCK = 0, FC_LABEL, FC_PROG, FC_KEY, FC_INDEX, FC_SIZE, FC__MAX
414};
415
416void
417makefileswind( GtkWindow * parent, TrTorrent * tor )
418{
419    GType cols[] =
420    {
421        G_TYPE_STRING, G_TYPE_STRING, G_TYPE_FLOAT,
422        G_TYPE_STRING, G_TYPE_INT, G_TYPE_UINT64
423    };
424    tr_info_t         * inf;
425    GtkTreeStore      * store;
426    int                 ii;
427    GtkWidget         * view, * scroll, * frame, * wind;
428    GtkCellRenderer   * rend, * elip;
429    GtkTreeViewColumn * col;
430    GtkTreeSelection  * sel;
431    char              * label;
432    struct fileswind  * fw;
433
434    g_assert( ALEN( cols ) == FC__MAX );
435
436    /* set up the model */
437    inf   = tr_torrent_info( tor );
438    store = gtk_tree_store_newv( FC__MAX, cols );
439    for( ii = 0; ii < inf->fileCount; ii++ )
440    {
441        parsepath( store, NULL, STRIPROOT( inf->files[ii].name ),
442                   ii, inf->files[ii].length );
443    }
444    getdirtotals( store, NULL );
445    gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( store ),
446                                          FC_KEY, GTK_SORT_ASCENDING );
447
448    /* create the view */
449    view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
450    /* add file column */
451    col = gtk_tree_view_column_new();
452    gtk_tree_view_column_set_expand( col, TRUE );
453    gtk_tree_view_column_set_sizing( col, GTK_TREE_VIEW_COLUMN_AUTOSIZE );
454    gtk_tree_view_column_set_title( col, _("File") );
455    /* add icon renderer */
456    rend = gtk_cell_renderer_pixbuf_new();
457    gtk_tree_view_column_pack_start( col, rend, FALSE );
458    gtk_tree_view_column_add_attribute( col, rend, "stock-id", FC_STOCK );
459    /* add text renderer */
460    rend = gtk_cell_renderer_text_new();
461    elip = rend;
462    gtk_tree_view_column_pack_start( col, rend, TRUE );
463    gtk_tree_view_column_add_attribute( col, rend, "markup", FC_LABEL );
464    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
465    /* add progress column */
466    col = gtk_tree_view_column_new();
467    gtk_tree_view_column_set_title( col, _("Progress") );
468    rend = tr_cell_renderer_progress_new();
469    /* this string is only used to determing the size of the progress bar */
470    label = g_markup_printf_escaped( "<small>%s</small>", _("  fnord    fnord  ") );
471    g_object_set( rend, "show-text", FALSE, "bar-sizing", label, NULL );
472    g_free( label );
473    gtk_tree_view_column_pack_start( col, rend, FALSE );
474    gtk_tree_view_column_add_attribute( col, rend, "progress", FC_PROG );
475    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
476    /* XXX this shouldn't be necessary */
477    g_signal_connect( view, "notify::style", G_CALLBACK( stylekludge ), rend );
478    /* set up view */
479    sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
480    gtk_tree_selection_set_mode( sel, GTK_SELECTION_NONE );
481    gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
482    gtk_tree_view_set_search_column( GTK_TREE_VIEW( view ), FC_LABEL );
483    gtk_widget_show( view );
484
485    /* create the scrolled window and stick the view in it */
486    scroll = gtk_scrolled_window_new( NULL, NULL );
487    gtk_container_add( GTK_CONTAINER( scroll ), view );
488    gtk_widget_show( scroll );
489
490    /* create a frame around the scroll to make it look a little better */
491    frame = gtk_frame_new( NULL );
492    gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_IN );
493    gtk_container_add( GTK_CONTAINER( frame ), scroll );
494    gtk_widget_show( frame );
495
496    /* create the window */
497    label = g_strdup_printf( _("%s - Files for %s"),
498                             g_get_application_name(), inf->name );
499    wind = gtk_dialog_new_with_buttons( label, parent,
500                                        GTK_DIALOG_DESTROY_WITH_PARENT |
501                                        GTK_DIALOG_NO_SEPARATOR, 
502                                        GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
503                                        NULL );
504    g_free( label );
505    gtk_dialog_set_default_response( GTK_DIALOG( wind ), GTK_RESPONSE_ACCEPT );
506    gtk_window_set_resizable( GTK_WINDOW( wind ), TRUE );
507    gtk_box_pack_start_defaults( GTK_BOX( GTK_DIALOG( wind )->vbox ), frame );
508    gtk_window_set_role( GTK_WINDOW( wind ), "tr-files" );
509
510    /* set up the callback data */
511    fw         = g_new0( struct fileswind, 1 );
512    fw->widget = wind;
513    fw->tor    = tor;
514    fw->model  = GTK_TREE_MODEL( store );
515    fw->timer  = g_timeout_add( UPDATE_INTERVAL, fileswindupdate, fw );
516
517    g_object_weak_ref( G_OBJECT( tor ), filestorclosed, fw );
518    g_signal_connect( wind, "destroy", G_CALLBACK( fileswinddead ), fw );
519    g_signal_connect( wind, "response", G_CALLBACK( gtk_widget_destroy ), 0 );
520    fileswindupdate( fw );
521
522    sizingmagic( GTK_WINDOW( wind ), GTK_SCROLLED_WINDOW( scroll ),
523                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
524    g_object_set( elip, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
525    gtk_widget_show( wind );
526}
527
528/* kludge to have the progress bars notice theme changes */
529static void
530stylekludge( GObject * obj, GParamSpec * spec, gpointer data )
531{
532    TrCellRendererProgress * rend = TR_CELL_RENDERER_PROGRESS( data );
533
534    if( 0 == strcmp( "style", spec->name ) )
535    {
536        tr_cell_renderer_progress_reset_style( rend );
537        gtk_widget_queue_draw( GTK_WIDGET( obj ) );
538    }
539}
540
541static void
542fileswinddead( GtkWidget * widget SHUTUP, gpointer data )
543{
544    struct fileswind * fw = data;
545
546    g_object_weak_unref( G_OBJECT( fw->tor ), filestorclosed, fw );
547    filestorclosed( fw, G_OBJECT( fw->tor ) );
548}
549
550static void
551filestorclosed( gpointer data, GObject * tor SHUTUP )
552{
553    struct fileswind * fw = data;
554
555    g_source_remove( fw->timer );
556    g_object_unref( fw->model );
557    gtk_widget_destroy( fw->widget );
558    g_free( fw );
559}
560
561static void
562parsepath( GtkTreeStore * store, GtkTreeIter * ret,
563           const char * path, int index, uint64_t size )
564{
565    GtkTreeModel * model;
566    GtkTreeIter  * parent, start, iter;
567    char         * file, * dir, * lower, * mykey, * modelkey;
568    const char   * stock;
569
570    model  = GTK_TREE_MODEL( store );
571    parent = NULL;
572    file   = g_path_get_basename( path );
573    if( 0 != strcmp( file, path ) )
574    {
575        dir = g_path_get_dirname( path );
576        parsepath( store, &start, dir, index, size );
577        g_free( dir );
578        parent = &start;
579    }
580
581    lower = g_utf8_casefold( file, -1 );
582    mykey = g_utf8_collate_key( lower, -1 );
583    if( gtk_tree_model_iter_children( model, &iter, parent ) )
584    {
585        do
586        {
587            gtk_tree_model_get( model, &iter, FC_KEY, &modelkey, -1 );
588            if( NULL != modelkey && 0 == strcmp( mykey, modelkey ) )
589            {
590                goto done;
591            }
592        }
593        while( gtk_tree_model_iter_next( model, &iter ) );
594    }
595
596    gtk_tree_store_append( store, &iter, parent );
597    if( NULL == ret )
598    {
599        stock = GTK_STOCK_FILE;
600    }
601    else
602    {
603        stock = GTK_STOCK_DIRECTORY;
604        size  = 0;
605        index = -1;
606    }
607    gtk_tree_store_set( store, &iter, FC_INDEX, index, FC_LABEL, file,
608                        FC_KEY, mykey, FC_STOCK, stock, FC_SIZE, size, -1 );
609  done:
610    g_free( mykey );
611    g_free( lower );
612    g_free( file );
613    if( NULL != ret )
614    {
615        memcpy( ret, &iter, sizeof( iter ) );
616    }
617}
618
619static uint64_t
620getdirtotals( GtkTreeStore * store, GtkTreeIter * parent )
621{
622    GtkTreeModel * model;
623    GtkTreeIter    iter;
624    uint64_t       mysize, subsize;
625    char         * sizestr, * name, * label;
626
627    model  = GTK_TREE_MODEL( store );
628    mysize = 0;
629    if( gtk_tree_model_iter_children( model, &iter, parent ) )
630    {
631        do
632        {
633            if( gtk_tree_model_iter_has_child( model, &iter ) )
634            {
635                subsize = getdirtotals( store, &iter );
636                gtk_tree_store_set( store, &iter, FC_SIZE, subsize, -1 );
637            }
638            else
639            {
640                gtk_tree_model_get( model, &iter, FC_SIZE, &subsize, -1 );
641            }
642            gtk_tree_model_get( model, &iter, FC_LABEL, &name, -1 );
643            sizestr = readablesize( subsize );
644            label = g_markup_printf_escaped( "<small>%s (%s)</small>",
645                                             name, sizestr );
646            g_free( sizestr );
647            g_free( name );
648            gtk_tree_store_set( store, &iter, FC_LABEL, label, -1 );
649            g_free( label );
650            mysize += subsize;
651        }
652        while( gtk_tree_model_iter_next( model, &iter ) );
653    }
654
655    return mysize;
656}
657
658static gboolean
659fileswindupdate( gpointer data )
660{
661    struct fileswind * fw;
662    tr_info_t        * inf;
663    float            * progress;
664
665    fw       = data;
666    inf      = tr_torrent_info( fw->tor );
667    progress = tr_torrentCompletion( tr_torrent_handle( fw->tor ) );
668    updateprogress( fw->model, NULL, inf->totalSize, progress );
669    free( progress );
670
671    return TRUE;
672}
673
674static float
675updateprogress( GtkTreeModel * model, GtkTreeIter * parent,
676                uint64_t total, float * progress )
677{
678    GtkTreeIter    iter;
679    float          myprog, subprog;
680    int            index;
681    uint64_t       size;
682
683    myprog = 0.0;
684    if( gtk_tree_model_iter_children( model, &iter, parent ) )
685    {
686        do
687        {
688            if( gtk_tree_model_iter_has_child( model, &iter ) )
689            {
690                gtk_tree_model_get( model, &iter, FC_SIZE, &size, -1 );
691                subprog = updateprogress( model, &iter, size, progress );
692            }
693            else
694            {
695                gtk_tree_model_get( model, &iter,
696                                    FC_SIZE, &size, FC_INDEX, &index, -1 );
697                g_assert( 0 <= index );
698                subprog = progress[index];
699            }
700            gtk_tree_store_set( GTK_TREE_STORE( model ), &iter,
701                                FC_PROG, subprog, -1 );
702            myprog += subprog * ( total ? ( float )size / ( float )total : 1 );
703        }
704        while( gtk_tree_model_iter_next( model, &iter ) );
705    }
706
707    return myprog;
708}
Note: See TracBrowser for help on using the repository browser.