source: trunk/gtk/open-dialog.c @ 13683

Last change on this file since 13683 was 13683, checked in by jordan, 8 years ago

(trunk, libT) first drop of the tr_quark patch.

  • Property svn:keywords set to Date Rev Author Id
File size: 16.0 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: open-dialog.c 13683 2012-12-22 20:35:19Z jordan $
11 */
12
13#include <glib/gi18n.h>
14#include <gtk/gtk.h>
15
16#include <string.h>
17
18#include <libtransmission/transmission.h>
19#include <libtransmission/utils.h> /* tr_is_same_file () */
20
21#include "conf.h"
22#include "file-list.h"
23#include "hig.h"
24#include "open-dialog.h"
25#include "tr-prefs.h"
26#include "util.h" /* gtr_priority_combo_get_value () */
27
28/****
29*****
30****/
31
32#define N_RECENT 4
33
34static GSList*
35get_recent_destinations (void)
36{
37    int i;
38    GSList * list = NULL;
39
40    for (i=0; i<N_RECENT; ++i)
41    {
42        char key[64];
43        const char * val;
44        g_snprintf (key, sizeof (key), "recent-download-dir-%d", i+1);
45        if ((val = gtr_pref_string_get (tr_quark_new(key,-1))))
46            list = g_slist_append (list, (void*)val);
47    }
48    return list;
49}
50
51static void
52save_recent_destination (TrCore * core, const char * dir)
53{
54    int i;
55    GSList * l;
56    GSList * list = get_recent_destinations ();
57
58    if (dir == NULL)
59        return;
60
61    /* if it was already in the list, remove it */
62    if ((l = g_slist_find_custom (list, dir, (GCompareFunc)strcmp)))
63        list = g_slist_delete_link (list, l);
64
65    /* add it to the front of the list */
66    list = g_slist_prepend (list, (void*)dir);
67
68    /* make local copies of the strings that aren't
69     * invalidated by gtr_pref_string_set () */
70    for (l=list; l; l=l->next)
71        l->data = g_strdup (l->data);
72
73    /* save the first N_RECENT directories */
74    for (l=list, i=0; l && (i<N_RECENT); ++i, l=l->next) {
75        char key[64];
76        g_snprintf (key, sizeof (key), "recent-download-dir-%d", i + 1);
77        gtr_pref_string_set (tr_quark_new(key,-1), l->data);
78    }
79    gtr_pref_save (gtr_core_session (core));
80
81    /* cleanup */
82    g_slist_foreach (list, (GFunc)g_free, NULL);
83    g_slist_free (list);
84}
85
86/****
87*****
88****/
89
90struct OpenData
91{
92    TrCore *     core;
93    GtkWidget *  file_list;
94    GtkWidget *  run_check;
95    GtkWidget *  trash_check;
96    GtkWidget *  priority_combo;
97    char *       filename;
98    char *       downloadDir;
99    tr_torrent * tor;
100    tr_ctor *    ctor;
101};
102
103static void
104removeOldTorrent (struct OpenData * o)
105{
106    if (o->tor)
107    {
108        gtr_file_list_clear (o->file_list);
109        tr_torrentRemove (o->tor, FALSE, NULL);
110        o->tor = NULL;
111    }
112}
113
114static void
115addResponseCB (GtkDialog * dialog,
116               gint        response,
117               gpointer    gdata)
118{
119    struct OpenData * o = gdata;
120
121    if (o->tor)
122    {
123        if (response != GTK_RESPONSE_ACCEPT)
124        {
125            removeOldTorrent (o);
126        }
127        else
128        {
129            tr_torrentSetPriority (o->tor, gtr_priority_combo_get_value (GTK_COMBO_BOX (o->priority_combo)));
130
131            if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (o->run_check)))
132                tr_torrentStart (o->tor);
133
134            gtr_core_add_torrent (o->core, o->tor, FALSE);
135
136            if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (o->trash_check)))
137                gtr_file_trash_or_remove (o->filename);
138
139            save_recent_destination (o->core, o->downloadDir);
140        }
141    }
142
143    tr_ctorFree (o->ctor);
144    g_free (o->filename);
145    g_free (o->downloadDir);
146    g_free (o);
147    gtk_widget_destroy (GTK_WIDGET (dialog));
148}
149
150static void
151updateTorrent (struct OpenData * o)
152{
153    const gboolean isLocalFile = tr_ctorGetSourceFile (o->ctor) != NULL;
154    gtk_widget_set_sensitive (o->trash_check, isLocalFile);
155
156    if (!o->tor)
157    {
158        gtr_file_list_clear (o->file_list);
159        gtk_widget_set_sensitive (o->file_list, FALSE);
160    }
161    else
162    {
163        tr_torrentSetDownloadDir (o->tor, o->downloadDir);
164        gtk_widget_set_sensitive (o->file_list, tr_torrentHasMetadata (o->tor));
165        gtr_file_list_set_torrent (o->file_list, tr_torrentId (o->tor));
166        tr_torrentVerify (o->tor);
167    }
168}
169
170/**
171 * When the source .torrent file is deleted
172 * (such as, if it was a temp file that a web browser passed to us),
173 * gtk invokes this callback and `filename' will be NULL.
174 * The `filename' tests here are to prevent us from losing the current
175 * metadata when that happens.
176 */
177static void
178sourceChanged (GtkFileChooserButton * b, gpointer gdata)
179{
180    struct OpenData * o = gdata;
181    char * filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (b));
182
183    /* maybe instantiate a torrent */
184    if (filename || !o->tor)
185    {
186        int err = 0;
187        int new_file = 0;
188        tr_torrent * torrent;
189
190        if (filename && (!o->filename || !tr_is_same_file (filename, o->filename)))
191        {
192            g_free (o->filename);
193            o->filename = g_strdup (filename);
194            tr_ctorSetMetainfoFromFile (o->ctor, o->filename);
195            new_file = 1;
196        }
197
198        tr_ctorSetDownloadDir (o->ctor, TR_FORCE, o->downloadDir);
199        tr_ctorSetPaused (o->ctor, TR_FORCE, TRUE);
200        tr_ctorSetDeleteSource (o->ctor, FALSE);
201
202        if ((torrent = tr_torrentNew (o->ctor, &err)))
203        {
204            removeOldTorrent (o);
205            o->tor = torrent;
206        }
207        else if (new_file)
208        {
209            gtr_add_torrent_error_dialog (GTK_WIDGET (b), err, o->filename);
210        }
211
212        updateTorrent (o);
213    }
214
215    g_free (filename);
216}
217
218static void
219downloadDirChanged (GtkFileChooserButton * b, gpointer gdata)
220{
221    struct OpenData * data = gdata;
222    char * fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (b));
223
224    if (fname && (!data->downloadDir || !tr_is_same_file (fname, data->downloadDir)))
225    {
226        g_free (data->downloadDir);
227        data->downloadDir = g_strdup (fname);
228        updateTorrent (data);
229    }
230
231    g_free (fname);
232}
233
234static void
235addTorrentFilters (GtkFileChooser * chooser)
236{
237    GtkFileFilter * filter;
238
239    filter = gtk_file_filter_new ();
240    gtk_file_filter_set_name (filter, _("Torrent files"));
241    gtk_file_filter_add_pattern (filter, "*.torrent");
242    gtk_file_chooser_add_filter (chooser, filter);
243
244    filter = gtk_file_filter_new ();
245    gtk_file_filter_set_name (filter, _("All files"));
246    gtk_file_filter_add_pattern (filter, "*");
247    gtk_file_chooser_add_filter (chooser, filter);
248}
249
250/****
251*****
252****/
253
254GtkWidget*
255gtr_torrent_options_dialog_new (GtkWindow * parent, TrCore * core, tr_ctor * ctor)
256{
257    const char *     str;
258    GtkWidget *      w;
259    GtkWidget *      d;
260    GtkGrid        * grid;
261    int              row;
262    GtkWidget *      l;
263    GtkWidget *      source_chooser;
264    struct OpenData * data;
265    bool             flag;
266    GSList *         list;
267    GSList *         walk;
268
269    /* make the dialog */
270    d = gtk_dialog_new_with_buttons (_("Torrent Options"), parent,
271                                     GTK_DIALOG_DESTROY_WITH_PARENT,
272                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
273                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
274                                     NULL);
275    gtk_dialog_set_default_response (GTK_DIALOG (d),
276                                     GTK_RESPONSE_ACCEPT);
277    gtk_dialog_set_alternative_button_order (GTK_DIALOG (d),
278                                             GTK_RESPONSE_ACCEPT,
279                                             GTK_RESPONSE_CANCEL,
280                                             -1);
281
282    if (tr_ctorGetDownloadDir (ctor, TR_FORCE, &str))
283        g_assert_not_reached ();
284    g_assert (str);
285
286    data = g_new0 (struct OpenData, 1);
287    data->core = core;
288    data->ctor = ctor;
289    data->filename = g_strdup (tr_ctorGetSourceFile (ctor));
290    data->downloadDir = g_strdup (str);
291    data->file_list = gtr_file_list_new (core, 0);
292    str = _("Mo_ve .torrent file to the trash");
293    data->trash_check = gtk_check_button_new_with_mnemonic (str);
294    str = _("_Start when added");
295    data->run_check = gtk_check_button_new_with_mnemonic (str);
296
297    w = data->priority_combo = gtr_priority_combo_new ();
298    gtr_priority_combo_set_value (GTK_COMBO_BOX (w), TR_PRI_NORMAL);
299
300    g_signal_connect (G_OBJECT (d), "response",
301                      G_CALLBACK (addResponseCB), data);
302
303    row = 0;
304    grid = GTK_GRID (gtk_grid_new ());
305    gtk_container_set_border_width (GTK_CONTAINER (grid), GUI_PAD_BIG);
306    gtk_grid_set_row_spacing (grid, GUI_PAD);
307    gtk_grid_set_column_spacing (grid, GUI_PAD_BIG);
308
309    // "torrent file" row
310    l = gtk_label_new_with_mnemonic (_("_Torrent file:"));
311    gtk_misc_set_alignment (GTK_MISC (l), 0.0f, 0.5f);
312    gtk_grid_attach (grid, l, 0, row, 1, 1);
313    w = gtk_file_chooser_button_new (_("Select Source File"),
314                                     GTK_FILE_CHOOSER_ACTION_OPEN);
315    source_chooser = w;
316    gtk_widget_set_hexpand (w, TRUE);
317    gtk_grid_attach_next_to (grid, w, l, GTK_POS_RIGHT, 1, 1);
318    gtk_label_set_mnemonic_widget (GTK_LABEL (l), w);
319    addTorrentFilters (GTK_FILE_CHOOSER (w));
320    g_signal_connect (w, "selection-changed",
321                      G_CALLBACK (sourceChanged), data);
322
323    // "destination folder" row
324    row++;
325    l = gtk_label_new_with_mnemonic (_("_Destination folder:"));
326    gtk_misc_set_alignment (GTK_MISC (l), 0.0f, 0.5f);
327    gtk_grid_attach (grid, l, 0, row, 1, 1);
328    w = gtk_file_chooser_button_new (_("Select Destination Folder"),
329                                     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
330    if (!gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (w),
331                                              data->downloadDir))
332        g_warning ("couldn't select '%s'", data->downloadDir);
333    list = get_recent_destinations ();
334    for (walk = list; walk; walk = walk->next)
335        gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (w), walk->data, NULL);
336    g_slist_free (list);
337    gtk_grid_attach_next_to (grid, w, l, GTK_POS_RIGHT, 1, 1);
338    gtk_label_set_mnemonic_widget (GTK_LABEL (l), w);
339    g_signal_connect (w, "selection-changed",
340                      G_CALLBACK (downloadDirChanged), data);
341
342    // file list row
343    row++;
344    w = data->file_list;
345    gtk_widget_set_vexpand (w, TRUE);
346    gtk_widget_set_size_request (w, 466u, 300u);
347    gtk_grid_attach (grid, w, 0, row, 2, 1);
348
349    // torrent priority row
350    row++;
351    l = gtk_label_new_with_mnemonic (_("Torrent _priority:"));
352    gtk_misc_set_alignment (GTK_MISC (l), 0.0f, 0.5f);
353    gtk_grid_attach (grid, l, 0, row, 1, 1);
354    w = data->priority_combo;
355    gtk_label_set_mnemonic_widget (GTK_LABEL (l), w);
356    gtk_grid_attach_next_to (grid, w, l, GTK_POS_RIGHT, 1, 1);
357
358    // torrent priority row
359    row++;
360    w = data->run_check;
361    if (tr_ctorGetPaused (ctor, TR_FORCE, &flag))
362        g_assert_not_reached ();
363    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), !flag);
364    gtk_grid_attach (grid, w, 0, row, 2, 1);
365
366    // "trash .torrent file" row
367    row++;
368    w = data->trash_check;
369    if (tr_ctorGetDeleteSource (ctor, &flag))
370        g_assert_not_reached ();
371    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), flag);
372    gtk_grid_attach (grid, w, 0, row, 2, 1);
373
374    /* trigger sourceChanged, either directly or indirectly,
375     * so that it creates the tor/gtor objects */
376    w = source_chooser;
377    if (data->filename)
378        gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (w), data->filename);
379    else
380        sourceChanged (GTK_FILE_CHOOSER_BUTTON (w), data);
381
382    gtr_dialog_set_content (GTK_DIALOG (d), GTK_WIDGET (grid));
383    w = gtk_dialog_get_widget_for_response (GTK_DIALOG (d), GTK_RESPONSE_ACCEPT);
384    gtk_widget_grab_focus (w);
385    return d;
386}
387
388/****
389*****
390****/
391
392static void
393onOpenDialogResponse (GtkDialog * dialog, int response, gpointer core)
394{
395    char * folder;
396
397    /* remember this folder the next time we use this dialog */
398    folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
399    gtr_pref_string_set (TR_KEY_open_dialog_dir, folder);
400    g_free (folder);
401
402    if (response == GTK_RESPONSE_ACCEPT)
403    {
404        GtkFileChooser  * chooser = GTK_FILE_CHOOSER (dialog);
405        GtkWidget       * w = gtk_file_chooser_get_extra_widget (chooser);
406        GtkToggleButton * tb = GTK_TOGGLE_BUTTON (w);
407        const gboolean    do_start = gtr_pref_flag_get (TR_KEY_start_added_torrents);
408        const gboolean    do_prompt = gtk_toggle_button_get_active (tb);
409        const gboolean    do_notify = FALSE;
410        GSList * files = gtk_file_chooser_get_files (chooser);
411
412        gtr_core_add_files (core, files, do_start, do_prompt, do_notify);
413        g_slist_foreach (files, (GFunc)g_object_unref, NULL);
414        g_slist_free (files);
415    }
416
417    gtk_widget_destroy (GTK_WIDGET (dialog));
418}
419
420GtkWidget*
421gtr_torrent_open_from_file_dialog_new (GtkWindow * parent, TrCore * core)
422{
423    GtkWidget *  w;
424    GtkWidget *  c;
425    const char * folder;
426
427    w = gtk_file_chooser_dialog_new (_("Open a Torrent"), parent,
428                                     GTK_FILE_CHOOSER_ACTION_OPEN,
429                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
430                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
431                                     NULL);
432    gtk_dialog_set_alternative_button_order (GTK_DIALOG (w),
433                                             GTK_RESPONSE_ACCEPT,
434                                             GTK_RESPONSE_CANCEL,
435                                             -1);
436    gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (w), TRUE);
437    addTorrentFilters (GTK_FILE_CHOOSER (w));
438    g_signal_connect (w, "response", G_CALLBACK (onOpenDialogResponse), core);
439
440    if ((folder = gtr_pref_string_get (TR_KEY_open_dialog_dir)))
441        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (w), folder);
442
443    c = gtk_check_button_new_with_mnemonic (_("Show _options dialog"));
444    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (c),
445                                 gtr_pref_flag_get (TR_KEY_show_options_window));
446    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (w), c);
447    gtk_widget_show (c);
448
449    return w;
450}
451
452/***
453****
454***/
455
456static void
457onOpenURLResponse (GtkDialog * dialog, int response, gpointer user_data)
458{
459    bool handled = false;
460
461    if (response == GTK_RESPONSE_ACCEPT)
462    {
463        GtkWidget * e = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "url-entry"));
464        char * url = g_strdup (gtk_entry_get_text (GTK_ENTRY (e)));
465        g_strstrip (url);
466
467        if (url) {
468            handled = gtr_core_add_from_url (user_data, url);
469            if (!handled)
470                gtr_unrecognized_url_dialog (GTK_WIDGET (dialog), url);
471            g_free (url);
472        }
473    }
474    else if (response == GTK_RESPONSE_CANCEL)
475    {
476        handled = TRUE;
477    }
478
479    if (handled)
480        gtk_widget_destroy (GTK_WIDGET (dialog));
481}
482
483GtkWidget*
484gtr_torrent_open_from_url_dialog_new (GtkWindow * parent, TrCore * core)
485{
486    guint row;
487    GtkWidget * e;
488    GtkWidget * t;
489    GtkWidget * w;
490
491    w = gtk_dialog_new_with_buttons (_("Open URL"), parent,
492                                     GTK_DIALOG_DESTROY_WITH_PARENT,
493                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
494                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
495                                     NULL);
496    gtk_dialog_set_alternative_button_order (GTK_DIALOG (w),
497                                             GTK_RESPONSE_ACCEPT,
498                                             GTK_RESPONSE_CANCEL,
499                                             -1);
500    g_signal_connect (w, "response", G_CALLBACK (onOpenURLResponse), core);
501
502    row = 0;
503    t = hig_workarea_create ();
504    hig_workarea_add_section_title (t, &row, _("Open torrent from URL"));
505    e = gtk_entry_new ();
506    gtk_widget_set_size_request (e, 400, -1);
507    gtr_paste_clipboard_url_into_entry (e);
508    g_object_set_data (G_OBJECT (w), "url-entry", e);
509    hig_workarea_add_row (t, &row, _("_URL"), e, NULL);
510
511    gtr_dialog_set_content (GTK_DIALOG (w), t);
512
513    if (gtk_entry_get_text_length (GTK_ENTRY (e)) == 0)
514        gtk_widget_grab_focus (e);
515    else
516        gtk_widget_grab_focus (gtk_dialog_get_widget_for_response (GTK_DIALOG (w), GTK_RESPONSE_ACCEPT));
517
518    return w;
519}
Note: See TracBrowser for help on using the repository browser.