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

Last change on this file since 14320 was 14320, checked in by jordan, 7 years ago

(trunk, libT) #4160 'foreign character support' -- merge mike.dld's 4160-02b-path.patch, which updates the codebase to use the new tr_sys_path_*() portability wrappers introduced in 4160-02a

  • Property svn:keywords set to Date Rev Author Id
File size: 15.8 KB
Line 
1/*
2 * This file Copyright (C) 2010-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: open-dialog.c 14320 2014-07-08 00:08:43Z jordan $
8 */
9
10#include <glib/gi18n.h>
11#include <gtk/gtk.h>
12
13#include <string.h>
14
15#include <libtransmission/transmission.h>
16#include <libtransmission/file.h> /* tr_sys_path_is_same () */
17
18#include "conf.h"
19#include "file-list.h"
20#include "hig.h"
21#include "open-dialog.h"
22#include "tr-prefs.h"
23#include "util.h" /* gtr_priority_combo_get_value () */
24
25/****
26*****
27****/
28
29#define N_RECENT 4
30
31static GSList*
32get_recent_destinations (void)
33{
34  int i;
35  GSList * list = NULL;
36
37  for (i=0; i<N_RECENT; ++i)
38    {
39      char key[64];
40      const char * val;
41      g_snprintf (key, sizeof (key), "recent-download-dir-%d", i+1);
42      if ((val = gtr_pref_string_get (tr_quark_new(key,-1))))
43        list = g_slist_append (list, (void*)val);
44    }
45
46  return list;
47}
48
49static void
50save_recent_destination (TrCore * core, const char * dir)
51{
52  int i;
53  GSList * l;
54  GSList * list = get_recent_destinations ();
55
56  if (dir == NULL)
57    return;
58
59  /* if it was already in the list, remove it */
60  if ((l = g_slist_find_custom (list, dir, (GCompareFunc)g_strcmp0)))
61    list = g_slist_delete_link (list, l);
62
63  /* add it to the front of the list */
64  list = g_slist_prepend (list, (void*)dir);
65
66  /* make local copies of the strings that aren't
67   * invalidated by gtr_pref_string_set () */
68  for (l=list; l; l=l->next)
69    l->data = g_strdup (l->data);
70
71  /* save the first N_RECENT directories */
72  for (l=list, i=0; l && (i<N_RECENT); ++i, l=l->next)
73    {
74      char key[64];
75      g_snprintf (key, sizeof (key), "recent-download-dir-%d", i + 1);
76      gtr_pref_string_set (tr_quark_new(key,-1), l->data);
77    }
78  gtr_pref_save (gtr_core_session (core));
79
80  /* cleanup */
81  g_slist_foreach (list, (GFunc)g_free, NULL);
82  g_slist_free (list);
83}
84
85/****
86*****
87****/
88
89struct OpenData
90{
91  TrCore *     core;
92  GtkWidget *  file_list;
93  GtkWidget *  run_check;
94  GtkWidget *  trash_check;
95  GtkWidget *  priority_combo;
96  GtkWidget *  freespace_label;
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, NULL, NULL);
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      int duplicate_id = 0;
189      tr_torrent * torrent;
190
191      if (filename && (!o->filename || !tr_sys_path_is_same (filename, o->filename, NULL)))
192        {
193          g_free (o->filename);
194          o->filename = g_strdup (filename);
195          tr_ctorSetMetainfoFromFile (o->ctor, o->filename);
196          new_file = 1;
197        }
198
199      tr_ctorSetDownloadDir (o->ctor, TR_FORCE, o->downloadDir);
200      tr_ctorSetPaused (o->ctor, TR_FORCE, TRUE);
201      tr_ctorSetDeleteSource (o->ctor, FALSE);
202
203      if ((torrent = tr_torrentNew (o->ctor, &err, &duplicate_id)))
204        {
205          removeOldTorrent (o);
206          o->tor = torrent;
207        }
208      else if (new_file)
209        {
210          tr_torrent * tor;
211
212          if (duplicate_id)
213            tor = gtr_core_find_torrent (o->core, duplicate_id);
214          else
215            tor = NULL;
216
217          gtr_add_torrent_error_dialog (GTK_WIDGET (b), err, tor, o->filename);
218        }
219
220      updateTorrent (o);
221    }
222
223  g_free (filename);
224}
225
226static void
227downloadDirChanged (GtkFileChooserButton * b, gpointer gdata)
228{
229  struct OpenData * data = gdata;
230  char * fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (b));
231
232  if (fname && (!data->downloadDir || !tr_sys_path_is_same (fname, data->downloadDir, NULL)))
233    {
234      g_free (data->downloadDir);
235      data->downloadDir = g_strdup (fname);
236      updateTorrent (data);
237
238      gtr_freespace_label_set_dir (data->freespace_label, data->downloadDir);
239    }
240
241  g_free (fname);
242}
243
244static void
245addTorrentFilters (GtkFileChooser * chooser)
246{
247  GtkFileFilter * filter;
248
249  filter = gtk_file_filter_new ();
250  gtk_file_filter_set_name (filter, _("Torrent files"));
251  gtk_file_filter_add_pattern (filter, "*.torrent");
252  gtk_file_chooser_add_filter (chooser, filter);
253
254  filter = gtk_file_filter_new ();
255  gtk_file_filter_set_name (filter, _("All files"));
256  gtk_file_filter_add_pattern (filter, "*");
257  gtk_file_chooser_add_filter (chooser, filter);
258}
259
260/****
261*****
262****/
263
264GtkWidget*
265gtr_torrent_options_dialog_new (GtkWindow * parent, TrCore * core, tr_ctor * ctor)
266{
267  const char *     str;
268  GtkWidget *      w;
269  GtkWidget *      d;
270  GtkGrid        * grid;
271  int              row;
272  GtkWidget *      l;
273  GtkWidget *      source_chooser;
274  struct OpenData * data;
275  bool             flag;
276  GSList *         list;
277  GSList *         walk;
278
279  /* make the dialog */
280  d = gtk_dialog_new_with_buttons (_("Torrent Options"), parent,
281                                   GTK_DIALOG_DESTROY_WITH_PARENT,
282                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
283                                   GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
284                                   NULL);
285  gtk_dialog_set_default_response (GTK_DIALOG (d),
286                                   GTK_RESPONSE_ACCEPT);
287  gtk_dialog_set_alternative_button_order (GTK_DIALOG (d),
288                                           GTK_RESPONSE_ACCEPT,
289                                           GTK_RESPONSE_CANCEL,
290                                           -1);
291
292  if (tr_ctorGetDownloadDir (ctor, TR_FORCE, &str))
293    g_assert_not_reached ();
294  g_assert (str);
295
296  data = g_new0 (struct OpenData, 1);
297  data->core = core;
298  data->ctor = ctor;
299  data->filename = g_strdup (tr_ctorGetSourceFile (ctor));
300  data->downloadDir = g_strdup (str);
301  data->file_list = gtr_file_list_new (core, 0);
302  str = _("Mo_ve .torrent file to the trash");
303  data->trash_check = gtk_check_button_new_with_mnemonic (str);
304  str = _("_Start when added");
305  data->run_check = gtk_check_button_new_with_mnemonic (str);
306
307  w = data->priority_combo = gtr_priority_combo_new ();
308  gtr_priority_combo_set_value (GTK_COMBO_BOX (w), TR_PRI_NORMAL);
309
310  g_signal_connect (G_OBJECT (d), "response",
311                    G_CALLBACK (addResponseCB), data);
312
313  row = 0;
314  grid = GTK_GRID (gtk_grid_new ());
315  gtk_container_set_border_width (GTK_CONTAINER (grid), GUI_PAD_BIG);
316  gtk_grid_set_row_spacing (grid, GUI_PAD);
317  gtk_grid_set_column_spacing (grid, GUI_PAD_BIG);
318
319  /* "torrent file" row */
320  l = gtk_label_new_with_mnemonic (_("_Torrent file:"));
321  gtk_misc_set_alignment (GTK_MISC (l), 0.0f, 0.5f);
322  gtk_grid_attach (grid, l, 0, row, 1, 1);
323  w = gtk_file_chooser_button_new (_("Select Source File"),
324                                   GTK_FILE_CHOOSER_ACTION_OPEN);
325  source_chooser = w;
326  gtk_widget_set_hexpand (w, TRUE);
327  gtk_grid_attach_next_to (grid, w, l, GTK_POS_RIGHT, 1, 1);
328  gtk_label_set_mnemonic_widget (GTK_LABEL (l), w);
329  addTorrentFilters (GTK_FILE_CHOOSER (w));
330  g_signal_connect (w, "selection-changed",
331                    G_CALLBACK (sourceChanged), data);
332
333  /* "destination folder" row */
334  row++;
335  l = gtk_label_new_with_mnemonic (_("_Destination folder:"));
336  gtk_misc_set_alignment (GTK_MISC (l), 0.0f, 0.5f);
337  gtk_grid_attach (grid, l, 0, row, 1, 1);
338  w = gtk_file_chooser_button_new (_("Select Destination Folder"),
339                                   GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
340  if (!gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (w),
341                                            data->downloadDir))
342    g_warning ("couldn't select '%s'", data->downloadDir);
343  list = get_recent_destinations ();
344  for (walk = list; walk; walk = walk->next)
345    gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (w), walk->data, NULL);
346  g_slist_free (list);
347  gtk_grid_attach_next_to (grid, w, l, GTK_POS_RIGHT, 1, 1);
348  gtk_label_set_mnemonic_widget (GTK_LABEL (l), w);
349  g_signal_connect (w, "selection-changed",
350                    G_CALLBACK (downloadDirChanged), data);
351
352  row++;
353  l = data->freespace_label = gtr_freespace_label_new (core, data->downloadDir);
354  gtk_widget_set_margin_bottom (l, GUI_PAD_BIG);
355  gtk_misc_set_alignment (GTK_MISC (l), 1.0f, 0.5f);
356  gtk_grid_attach (grid, l, 0, row, 2, 1);
357
358
359  /* file list row */
360  row++;
361  w = data->file_list;
362  gtk_widget_set_vexpand (w, TRUE);
363  gtk_widget_set_size_request (w, 466u, 300u);
364  gtk_grid_attach (grid, w, 0, row, 2, 1);
365
366  /* torrent priority row */
367  row++;
368  l = gtk_label_new_with_mnemonic (_("Torrent _priority:"));
369  gtk_misc_set_alignment (GTK_MISC (l), 0.0f, 0.5f);
370  gtk_grid_attach (grid, l, 0, row, 1, 1);
371  w = data->priority_combo;
372  gtk_label_set_mnemonic_widget (GTK_LABEL (l), w);
373  gtk_grid_attach_next_to (grid, w, l, GTK_POS_RIGHT, 1, 1);
374
375  /* torrent priority row */
376  row++;
377  w = data->run_check;
378  if (tr_ctorGetPaused (ctor, TR_FORCE, &flag))
379    g_assert_not_reached ();
380  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), !flag);
381  gtk_grid_attach (grid, w, 0, row, 2, 1);
382
383  /* "trash .torrent file" row */
384  row++;
385  w = data->trash_check;
386  if (tr_ctorGetDeleteSource (ctor, &flag))
387    g_assert_not_reached ();
388  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), flag);
389  gtk_grid_attach (grid, w, 0, row, 2, 1);
390
391  /* trigger sourceChanged, either directly or indirectly,
392   * so that it creates the tor/gtor objects */
393  w = source_chooser;
394  if (data->filename)
395    gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (w), data->filename);
396  else
397    sourceChanged (GTK_FILE_CHOOSER_BUTTON (w), data);
398
399  gtr_dialog_set_content (GTK_DIALOG (d), GTK_WIDGET (grid));
400  w = gtk_dialog_get_widget_for_response (GTK_DIALOG (d), GTK_RESPONSE_ACCEPT);
401  gtk_widget_grab_focus (w);
402  return d;
403}
404
405/****
406*****
407****/
408
409static void
410onOpenDialogResponse (GtkDialog * dialog, int response, gpointer core)
411{
412  char * folder;
413
414  /* remember this folder the next time we use this dialog */
415  folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
416  gtr_pref_string_set (TR_KEY_open_dialog_dir, folder);
417  g_free (folder);
418
419  if (response == GTK_RESPONSE_ACCEPT)
420    {
421      GtkFileChooser  * chooser = GTK_FILE_CHOOSER (dialog);
422      GtkWidget       * w = gtk_file_chooser_get_extra_widget (chooser);
423      GtkToggleButton * tb = GTK_TOGGLE_BUTTON (w);
424      const gboolean    do_start = gtr_pref_flag_get (TR_KEY_start_added_torrents);
425      const gboolean    do_prompt = gtk_toggle_button_get_active (tb);
426      const gboolean    do_notify = FALSE;
427      GSList * files = gtk_file_chooser_get_files (chooser);
428
429      gtr_core_add_files (core, files, do_start, do_prompt, do_notify);
430      g_slist_foreach (files, (GFunc)g_object_unref, NULL);
431      g_slist_free (files);
432    }
433
434  gtk_widget_destroy (GTK_WIDGET (dialog));
435}
436
437GtkWidget*
438gtr_torrent_open_from_file_dialog_new (GtkWindow * parent, TrCore * core)
439{
440  GtkWidget *  w;
441  GtkWidget *  c;
442  const char * folder;
443
444  w = gtk_file_chooser_dialog_new (_("Open a Torrent"), parent,
445                                   GTK_FILE_CHOOSER_ACTION_OPEN,
446                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
447                                   GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
448                                   NULL);
449  gtk_dialog_set_alternative_button_order (GTK_DIALOG (w),
450                                           GTK_RESPONSE_ACCEPT,
451                                           GTK_RESPONSE_CANCEL,
452                                           -1);
453  gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (w), TRUE);
454  addTorrentFilters (GTK_FILE_CHOOSER (w));
455  g_signal_connect (w, "response", G_CALLBACK (onOpenDialogResponse), core);
456
457  if ((folder = gtr_pref_string_get (TR_KEY_open_dialog_dir)))
458    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (w), folder);
459
460  c = gtk_check_button_new_with_mnemonic (_("Show _options dialog"));
461  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (c),
462                                gtr_pref_flag_get (TR_KEY_show_options_window));
463  gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (w), c);
464  gtk_widget_show (c);
465
466  return w;
467}
468
469/***
470****
471***/
472
473static void
474onOpenURLResponse (GtkDialog * dialog, int response, gpointer user_data)
475{
476  bool handled = false;
477
478  if (response == GTK_RESPONSE_ACCEPT)
479    {
480      GtkWidget * e = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "url-entry"));
481      char * url = g_strdup (gtk_entry_get_text (GTK_ENTRY (e)));
482      g_strstrip (url);
483
484      if (url)
485        {
486          handled = gtr_core_add_from_url (user_data, url);
487          if (!handled)
488            gtr_unrecognized_url_dialog (GTK_WIDGET (dialog), url);
489          g_free (url);
490        }
491    }
492  else if (response == GTK_RESPONSE_CANCEL)
493    {
494      handled = TRUE;
495    }
496
497  if (handled)
498    gtk_widget_destroy (GTK_WIDGET (dialog));
499}
500
501GtkWidget*
502gtr_torrent_open_from_url_dialog_new (GtkWindow * parent, TrCore * core)
503{
504  guint row;
505  GtkWidget * e;
506  GtkWidget * t;
507  GtkWidget * w;
508
509  w = gtk_dialog_new_with_buttons (_("Open URL"), parent,
510                                   GTK_DIALOG_DESTROY_WITH_PARENT,
511                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
512                                   GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
513                                   NULL);
514  gtk_dialog_set_alternative_button_order (GTK_DIALOG (w),
515                                           GTK_RESPONSE_ACCEPT,
516                                           GTK_RESPONSE_CANCEL,
517                                           -1);
518  g_signal_connect (w, "response", G_CALLBACK (onOpenURLResponse), core);
519
520  row = 0;
521  t = hig_workarea_create ();
522  hig_workarea_add_section_title (t, &row, _("Open torrent from URL"));
523  e = gtk_entry_new ();
524  gtk_widget_set_size_request (e, 400, -1);
525  gtr_paste_clipboard_url_into_entry (e);
526  g_object_set_data (G_OBJECT (w), "url-entry", e);
527  hig_workarea_add_row (t, &row, _("_URL"), e, NULL);
528
529  gtr_dialog_set_content (GTK_DIALOG (w), t);
530
531  if (gtk_entry_get_text_length (GTK_ENTRY (e)) == 0)
532    gtk_widget_grab_focus (e);
533  else
534    gtk_widget_grab_focus (gtk_dialog_get_widget_for_response (GTK_DIALOG (w), GTK_RESPONSE_ACCEPT));
535
536  return w;
537}
Note: See TracBrowser for help on using the repository browser.