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 | |
---|
48 | struct addcb { |
---|
49 | add_torrents_func_t addfunc; |
---|
50 | void *data; |
---|
51 | gboolean autostart; |
---|
52 | gboolean usingaltdir; |
---|
53 | GtkFileChooser *altdir; |
---|
54 | GtkButtonBox *altbox; |
---|
55 | }; |
---|
56 | |
---|
57 | struct dirdata |
---|
58 | { |
---|
59 | add_torrents_func_t addfunc; |
---|
60 | void * cbdata; |
---|
61 | GList * files; |
---|
62 | guint flags; |
---|
63 | }; |
---|
64 | |
---|
65 | struct quitdata |
---|
66 | { |
---|
67 | callbackfunc_t func; |
---|
68 | void * cbdata; |
---|
69 | }; |
---|
70 | |
---|
71 | struct fileswind |
---|
72 | { |
---|
73 | GtkWidget * widget; |
---|
74 | TrTorrent * tor; |
---|
75 | GtkTreeModel * model; |
---|
76 | guint timer; |
---|
77 | }; |
---|
78 | |
---|
79 | static void |
---|
80 | autoclick(GtkWidget *widget, gpointer gdata); |
---|
81 | static void |
---|
82 | dirclick(GtkWidget *widget, gpointer gdata); |
---|
83 | static void |
---|
84 | addresp(GtkWidget *widget, gint resp, gpointer gdata); |
---|
85 | static void |
---|
86 | promptresp( GtkWidget * widget, gint resp, gpointer data ); |
---|
87 | static void |
---|
88 | quitresp( GtkWidget * widget, gint resp, gpointer data ); |
---|
89 | static void |
---|
90 | stylekludge( GObject * obj, GParamSpec * spec, gpointer data ); |
---|
91 | static void |
---|
92 | fileswinddead( GtkWidget * widget, gpointer data ); |
---|
93 | static void |
---|
94 | filestorclosed( gpointer data, GObject * tor ); |
---|
95 | static void |
---|
96 | parsepath( GtkTreeStore * store, GtkTreeIter * ret, |
---|
97 | const char * path, int index, uint64_t size ); |
---|
98 | static uint64_t |
---|
99 | getdirtotals( GtkTreeStore * store, GtkTreeIter * parent ); |
---|
100 | static gboolean |
---|
101 | fileswindupdate( gpointer data ); |
---|
102 | static float |
---|
103 | updateprogress( GtkTreeModel * model, GtkTreeIter * parent, |
---|
104 | uint64_t total, float * progress ); |
---|
105 | |
---|
106 | void |
---|
107 | makeaddwind(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 | |
---|
165 | static void |
---|
166 | autoclick(GtkWidget *widget, gpointer gdata) { |
---|
167 | struct addcb *data = gdata; |
---|
168 | |
---|
169 | data->autostart = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); |
---|
170 | } |
---|
171 | |
---|
172 | static void |
---|
173 | dirclick(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 | |
---|
180 | static void |
---|
181 | addresp(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 | |
---|
245 | void |
---|
246 | makeinfowind(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 | |
---|
323 | void |
---|
324 | promptfordir( 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 | |
---|
352 | static void |
---|
353 | promptresp( 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 | |
---|
374 | void |
---|
375 | askquit( 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 | |
---|
395 | static void |
---|
396 | quitresp( 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 | |
---|
411 | enum filescols |
---|
412 | { |
---|
413 | FC_STOCK = 0, FC_LABEL, FC_PROG, FC_KEY, FC_INDEX, FC_SIZE, FC__MAX |
---|
414 | }; |
---|
415 | |
---|
416 | void |
---|
417 | makefileswind( 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 */ |
---|
529 | static void |
---|
530 | stylekludge( 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 | |
---|
541 | static void |
---|
542 | fileswinddead( 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 | |
---|
550 | static void |
---|
551 | filestorclosed( 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 | |
---|
561 | static void |
---|
562 | parsepath( 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 | |
---|
619 | static uint64_t |
---|
620 | getdirtotals( 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 | |
---|
658 | static gboolean |
---|
659 | fileswindupdate( 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 | |
---|
674 | static float |
---|
675 | updateprogress( 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 | } |
---|