source: trunk/gtk/tr-window.c @ 13927

Last change on this file since 13927 was 13927, checked in by jordan, 10 years ago

in the Qt and GTK+ clients, move the torrent count indicator from the statusbar to the filterbar

  • Property svn:keywords set to Date Rev Author Id
File size: 31.6 KB
Line 
1/******************************************************************************
2 * $Id: tr-window.c 13927 2013-02-01 20:58:55Z jordan $
3 *
4 * Copyright (c) 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 <string.h>
26
27#include <gtk/gtk.h>
28#include <glib/gi18n.h>
29
30#include <libtransmission/transmission.h>
31#include <libtransmission/utils.h> /* tr_formatter_speed_KBps () */
32
33#include "actions.h"
34#include "conf.h"
35#include "filter.h"
36#include "hig.h"
37#include "torrent-cell-renderer.h"
38#include "tr-prefs.h"
39#include "tr-window.h"
40#include "util.h"
41
42typedef struct
43{
44    GtkWidget          * speedlimit_on_item[2];
45    GtkWidget          * speedlimit_off_item[2];
46    GtkWidget          * ratio_on_item;
47    GtkWidget          * ratio_off_item;
48    GtkWidget          * scroll;
49    GtkWidget          * view;
50    GtkWidget          * toolbar;
51    GtkWidget          * filter;
52    GtkWidget          * status;
53    GtkWidget          * status_menu;
54    GtkLabel           * ul_lb;
55    GtkLabel           * dl_lb;
56    GtkLabel           * stats_lb;
57    GtkLabel           * freespace_lb;
58    GtkWidget          * freespace_icon;
59    GtkWidget          * alt_speed_image;
60    GtkWidget          * alt_speed_button;
61    GtkWidget          * options_menu;
62    GtkTreeSelection   * selection;
63    GtkCellRenderer    * renderer;
64    GtkTreeViewColumn  * column;
65    GtkTreeModel       * filter_model;
66    TrCore             * core;
67    gulong               pref_handler_id;
68}
69PrivateData;
70
71static GQuark
72get_private_data_key (void)
73{
74  static GQuark q = 0;
75
76  if (!q)
77    q = g_quark_from_static_string ("private-data");
78
79  return q;
80}
81
82static PrivateData*
83get_private_data (GtkWindow * w)
84{
85  return g_object_get_qdata (G_OBJECT (w), get_private_data_key ());
86}
87
88/***
89****
90***/
91
92static void
93on_popup_menu (GtkWidget * self UNUSED,
94               GdkEventButton * event)
95{
96  GtkWidget * menu = gtr_action_get_widget ("/main-window-popup");
97
98  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
99                  (event ? event->button : 0),
100                  (event ? event->time : 0));
101}
102
103static void
104view_row_activated (GtkTreeView       * tree_view UNUSED,
105                    GtkTreePath       * path      UNUSED,
106                    GtkTreeViewColumn * column    UNUSED,
107                    gpointer            user_data UNUSED)
108{
109  gtr_action_activate ("show-torrent-properties");
110}
111
112static gboolean
113tree_view_search_equal_func (GtkTreeModel * model,
114                             gint           column UNUSED,
115                             const gchar  * key,
116                             GtkTreeIter  * iter,
117                             gpointer       search_data UNUSED)
118{
119    gboolean match;
120    char * lower;
121    const char * name = NULL;
122
123    lower = g_strstrip (g_utf8_strdown (key, -1));
124    gtk_tree_model_get (model, iter, MC_NAME_COLLATED, &name, -1);
125    match = strstr (name, lower) != NULL;
126    g_free (lower);
127
128    return !match;
129}
130
131static GtkWidget*
132makeview (PrivateData * p)
133{
134  GtkWidget * view;
135  GtkTreeViewColumn * col;
136  GtkTreeSelection * sel;
137  GtkCellRenderer * r;
138  GtkTreeView * tree_view;
139
140  view = gtk_tree_view_new ();
141  tree_view = GTK_TREE_VIEW (view);
142  gtk_tree_view_set_search_column (tree_view, MC_NAME_COLLATED);
143  gtk_tree_view_set_search_equal_func (tree_view,
144                                       tree_view_search_equal_func,
145                                       NULL, NULL);
146  gtk_tree_view_set_headers_visible (tree_view, FALSE);
147  gtk_tree_view_set_fixed_height_mode (tree_view, TRUE);
148
149  p->selection = gtk_tree_view_get_selection (tree_view);
150
151  p->column = col = GTK_TREE_VIEW_COLUMN (g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
152    "title", _("Torrent"),
153    "resizable", TRUE,
154    "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
155    NULL));
156
157  p->renderer = r = torrent_cell_renderer_new ();
158  gtk_tree_view_column_pack_start (col, r, FALSE);
159  gtk_tree_view_column_add_attribute (col, r, "torrent", MC_TORRENT);
160  gtk_tree_view_column_add_attribute (col, r, "piece-upload-speed", MC_SPEED_UP);
161  gtk_tree_view_column_add_attribute (col, r, "piece-download-speed", MC_SPEED_DOWN);
162
163  gtk_tree_view_append_column (tree_view, col);
164  g_object_set (r, "xpad", GUI_PAD_SMALL, "ypad", GUI_PAD_SMALL, NULL);
165
166  gtk_tree_view_set_rules_hint (tree_view, TRUE);
167  sel = gtk_tree_view_get_selection (tree_view);
168  gtk_tree_selection_set_mode (GTK_TREE_SELECTION (sel),
169                               GTK_SELECTION_MULTIPLE);
170
171  g_signal_connect (view, "popup-menu",
172                    G_CALLBACK (on_popup_menu), NULL);
173  g_signal_connect (view, "button-press-event",
174                    G_CALLBACK (on_tree_view_button_pressed),
175                    (void *) on_popup_menu);
176  g_signal_connect (view, "button-release-event",
177                    G_CALLBACK (on_tree_view_button_released), NULL);
178  g_signal_connect (view, "row-activated",
179                    G_CALLBACK (view_row_activated), NULL);
180
181
182  gtk_tree_view_set_model (tree_view, p->filter_model);
183  g_object_unref (p->filter_model);
184
185  return view;
186}
187
188static void syncAltSpeedButton (PrivateData * p);
189
190static void
191prefsChanged (TrCore           * core UNUSED,
192              const tr_quark     key,
193              gpointer           wind)
194{
195  gboolean isEnabled;
196  PrivateData * p = get_private_data (GTK_WINDOW (wind));
197
198  switch (key)
199    {
200      case TR_KEY_compact_view:
201        g_object_set (p->renderer, "compact", gtr_pref_flag_get (key), NULL);
202        /* since the cell size has changed, we need gtktreeview to revalidate
203         * its fixed-height mode values. Unfortunately there's not an API call
204         * for that, but it *does* revalidate when it thinks the style's been tweaked */
205        g_signal_emit_by_name (p->view, "style-updated", NULL, NULL);
206        break;
207
208      case TR_KEY_show_statusbar:
209        isEnabled = gtr_pref_flag_get (key);
210        g_object_set (p->status, "visible", isEnabled, NULL);
211        break;
212
213      case TR_KEY_show_filterbar:
214        isEnabled = gtr_pref_flag_get (key);
215        g_object_set (p->filter, "visible", isEnabled, NULL);
216        break;
217
218      case TR_KEY_show_toolbar:
219        isEnabled = gtr_pref_flag_get (key);
220        g_object_set (p->toolbar, "visible", isEnabled, NULL);
221        break;
222
223      case TR_KEY_statusbar_stats:
224        gtr_window_refresh (wind);
225        break;
226
227      case TR_KEY_alt_speed_enabled:
228      case TR_KEY_alt_speed_up:
229      case TR_KEY_alt_speed_down:
230        syncAltSpeedButton (p);
231        break;
232
233      default:
234        break;
235    }
236}
237
238static void
239privateFree (gpointer vprivate)
240{
241  PrivateData * p = vprivate;
242  g_signal_handler_disconnect (p->core, p->pref_handler_id);
243  g_free (p);
244}
245
246static void
247onYinYangReleased (GtkWidget * w UNUSED, gpointer vprivate)
248{
249  PrivateData * p = vprivate;
250
251  gtk_menu_popup (GTK_MENU (p->status_menu),
252                  NULL, NULL, NULL, NULL, 0,
253                  gtk_get_current_event_time ());
254}
255
256#define STATS_MODE "stats-mode"
257
258static struct
259{
260    const char *  val, *i18n;
261} stats_modes[] = {
262    { "total-ratio",      N_("Total Ratio")                },
263    { "session-ratio",    N_("Session Ratio")              },
264    { "total-transfer",   N_("Total Transfer")             },
265    { "session-transfer", N_("Session Transfer")           }
266};
267
268static void
269status_menu_toggled_cb (GtkCheckMenuItem * menu_item,
270                        gpointer           vprivate)
271{
272  if (gtk_check_menu_item_get_active (menu_item))
273    {
274      PrivateData * p = vprivate;
275      const char *  val = g_object_get_data (G_OBJECT (menu_item), STATS_MODE);
276      gtr_core_set_pref (p->core, TR_KEY_statusbar_stats, val);
277    }
278}
279
280static void
281syncAltSpeedButton (PrivateData * p)
282{
283  char u[32];
284  char d[32];
285  char * str;
286  const char * fmt;
287  const gboolean b = gtr_pref_flag_get (TR_KEY_alt_speed_enabled);
288  const char * stock = b ? "alt-speed-on" : "alt-speed-off";
289  GtkWidget * w = p->alt_speed_button;
290
291  tr_formatter_speed_KBps (u, gtr_pref_int_get (TR_KEY_alt_speed_up), sizeof (u));
292  tr_formatter_speed_KBps (d, gtr_pref_int_get (TR_KEY_alt_speed_down), sizeof (d));
293  fmt = b ? _("Click to disable Alternative Speed Limits\n (%1$s down, %2$s up)")
294          : _("Click to enable Alternative Speed Limits\n (%1$s down, %2$s up)");
295  str = g_strdup_printf (fmt, d, u);
296
297  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), b);
298  gtk_image_set_from_stock (GTK_IMAGE (p->alt_speed_image), stock, -1);
299  gtk_button_set_alignment (GTK_BUTTON (w), 0.5, 0.5);
300  gtk_widget_set_tooltip_text (w, str);
301
302  g_free (str);
303}
304
305static void
306alt_speed_toggled_cb (GtkToggleButton * button, gpointer vprivate)
307{
308  PrivateData * p = vprivate;
309  const gboolean b = gtk_toggle_button_get_active (button);
310  gtr_core_set_pref_bool (p->core, TR_KEY_alt_speed_enabled,  b);
311}
312
313/***
314****  FILTER
315***/
316
317static void
318findMaxAnnounceTime (GtkTreeModel * model,
319                     GtkTreePath  * path UNUSED,
320                     GtkTreeIter  * iter,
321                     gpointer       gmaxTime)
322{
323  tr_torrent * tor;
324  const tr_stat * torStat;
325  time_t * maxTime = gmaxTime;
326
327  gtk_tree_model_get (model, iter, MC_TORRENT, &tor, -1);
328  torStat = tr_torrentStatCached (tor);
329  *maxTime = MAX (*maxTime, torStat->manualAnnounceTime);
330}
331
332static gboolean
333onAskTrackerQueryTooltip (GtkWidget   * widget UNUSED,
334                          gint          x UNUSED,
335                          gint          y UNUSED,
336                          gboolean      keyboard_tip UNUSED,
337                          GtkTooltip  * tooltip,
338                          gpointer      gdata)
339{
340  gboolean handled;
341  time_t maxTime = 0;
342  PrivateData * p = gdata;
343  const time_t now = time (NULL);
344
345  gtk_tree_selection_selected_foreach (p->selection,
346                                       findMaxAnnounceTime,
347                                       &maxTime);
348  if (maxTime <= now)
349    {
350      handled = FALSE;
351    }
352  else
353    {
354      char buf[512];
355      char timebuf[64];
356      const int seconds = maxTime - now;
357
358      tr_strltime (timebuf, seconds, sizeof (timebuf));
359      g_snprintf (buf, sizeof (buf),
360                  _("Tracker will allow requests in %s"), timebuf);
361      gtk_tooltip_set_text (tooltip, buf);
362      handled = TRUE;
363    }
364
365  return handled;
366}
367
368static gboolean
369onAltSpeedToggledIdle (gpointer vp)
370{
371  PrivateData * p = vp;
372  gboolean b = tr_sessionUsesAltSpeed (gtr_core_session (p->core));
373  gtr_core_set_pref_bool (p->core, TR_KEY_alt_speed_enabled, b);
374
375  return FALSE;
376}
377
378static void
379onAltSpeedToggled (tr_session * s UNUSED, bool isEnabled UNUSED, bool byUser UNUSED, void * p)
380{
381  gdk_threads_add_idle (onAltSpeedToggledIdle, p);
382}
383
384/***
385****  Speed limit menu
386***/
387
388#define DIRECTION_KEY "direction-key"
389#define ENABLED_KEY "enabled-key"
390#define SPEED_KEY "speed-key"
391
392static void
393onSpeedToggled (GtkCheckMenuItem * check, gpointer vp)
394{
395  PrivateData * p = vp;
396  GObject * o = G_OBJECT (check);
397  gboolean isEnabled = g_object_get_data (o, ENABLED_KEY) != 0;
398  tr_direction dir = GPOINTER_TO_INT (g_object_get_data (o, DIRECTION_KEY));
399  const tr_quark key = dir == TR_UP ? TR_KEY_speed_limit_up_enabled
400                                    : TR_KEY_speed_limit_down_enabled;
401
402  if (gtk_check_menu_item_get_active (check))
403    gtr_core_set_pref_bool (p->core, key, isEnabled);
404}
405
406static void
407onSpeedSet (GtkCheckMenuItem * check, gpointer vp)
408{
409  tr_quark key;
410  PrivateData * p = vp;
411  GObject * o = G_OBJECT (check);
412  const int KBps = GPOINTER_TO_INT (g_object_get_data (o, SPEED_KEY));
413  tr_direction dir = GPOINTER_TO_INT (g_object_get_data (o, DIRECTION_KEY));
414
415  key = dir==TR_UP ? TR_KEY_speed_limit_up : TR_KEY_speed_limit_down;
416  gtr_core_set_pref_int (p->core, key, KBps);
417
418  key = dir==TR_UP ? TR_KEY_speed_limit_up_enabled : TR_KEY_speed_limit_down_enabled;
419  gtr_core_set_pref_bool (p->core, key, TRUE);
420}
421
422static GtkWidget*
423createSpeedMenu (PrivateData * p, tr_direction dir)
424{
425  int i, n;
426  GObject * o;
427  GtkWidget * w;
428  GtkWidget * m;
429  GtkMenuShell * menu_shell;
430  const int speeds_KBps[] = { 5, 10, 20, 30, 40, 50, 75, 100, 150, 200, 250, 500, 750 };
431
432  m = gtk_menu_new ();
433  menu_shell = GTK_MENU_SHELL (m);
434
435  w = gtk_radio_menu_item_new_with_label (NULL, _("Unlimited"));
436  o = G_OBJECT (w);
437  p->speedlimit_off_item[dir] = w;
438  g_object_set_data (o, DIRECTION_KEY, GINT_TO_POINTER (dir));
439  g_object_set_data (o, ENABLED_KEY, GINT_TO_POINTER (FALSE));
440  g_signal_connect (w, "toggled", G_CALLBACK (onSpeedToggled), p);
441  gtk_menu_shell_append (menu_shell, w);
442
443  w = gtk_radio_menu_item_new_with_label_from_widget (GTK_RADIO_MENU_ITEM (w), "");
444  o = G_OBJECT (w);
445  p->speedlimit_on_item[dir] = w;
446  g_object_set_data (o, DIRECTION_KEY, GINT_TO_POINTER (dir));
447  g_object_set_data (o, ENABLED_KEY, GINT_TO_POINTER (TRUE));
448  g_signal_connect (w, "toggled", G_CALLBACK (onSpeedToggled), p);
449  gtk_menu_shell_append (menu_shell, w);
450
451  w = gtk_separator_menu_item_new ();
452  gtk_menu_shell_append (menu_shell, w);
453
454  for (i=0, n=G_N_ELEMENTS (speeds_KBps); i<n; ++i)
455    {
456      char buf[128];
457      tr_formatter_speed_KBps (buf, speeds_KBps[i], sizeof (buf));
458      w = gtk_menu_item_new_with_label (buf);
459      o = G_OBJECT (w);
460      g_object_set_data (o, DIRECTION_KEY, GINT_TO_POINTER (dir));
461      g_object_set_data (o, SPEED_KEY, GINT_TO_POINTER (speeds_KBps[i]));
462      g_signal_connect (w, "activate", G_CALLBACK (onSpeedSet), p);
463      gtk_menu_shell_append (menu_shell, w);
464    }
465
466  return m;
467}
468
469/***
470****  Speed limit menu
471***/
472
473#define RATIO_KEY "stock-ratio-index"
474
475static const double stockRatios[] = { 0.25, 0.5, 0.75, 1, 1.5, 2, 3 };
476
477static void
478onRatioToggled (GtkCheckMenuItem * check, gpointer vp)
479{
480  PrivateData * p = vp;
481
482  if (gtk_check_menu_item_get_active (check))
483    {
484      gboolean f = g_object_get_data (G_OBJECT (check), ENABLED_KEY) != 0;
485      gtr_core_set_pref_bool (p->core, TR_KEY_ratio_limit_enabled, f);
486    }
487}
488static void
489onRatioSet (GtkCheckMenuItem * check, gpointer vp)
490{
491  PrivateData * p = vp;
492  int i = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (check), RATIO_KEY));
493  const double ratio = stockRatios[i];
494  gtr_core_set_pref_double (p->core, TR_KEY_ratio_limit, ratio);
495  gtr_core_set_pref_bool (p->core, TR_KEY_ratio_limit_enabled, TRUE);
496}
497
498static GtkWidget*
499createRatioMenu (PrivateData * p)
500{
501  int i, n;
502  GtkWidget *m, *w;
503  GtkMenuShell * menu_shell;
504
505  m = gtk_menu_new ();
506  menu_shell = GTK_MENU_SHELL (m);
507
508  w = gtk_radio_menu_item_new_with_label (NULL, _("Seed Forever"));
509  p->ratio_off_item = w;
510  g_object_set_data (G_OBJECT (w), ENABLED_KEY, GINT_TO_POINTER (FALSE));
511  g_signal_connect (w, "toggled", G_CALLBACK (onRatioToggled), p);
512  gtk_menu_shell_append (menu_shell, w);
513
514  w = gtk_radio_menu_item_new_with_label_from_widget (GTK_RADIO_MENU_ITEM (w), "");
515  p->ratio_on_item = w;
516  g_object_set_data (G_OBJECT (w), ENABLED_KEY, GINT_TO_POINTER (TRUE));
517  g_signal_connect (w, "toggled", G_CALLBACK (onRatioToggled), p);
518  gtk_menu_shell_append (menu_shell, w);
519
520  w = gtk_separator_menu_item_new ();
521  gtk_menu_shell_append (menu_shell, w);
522
523  for (i=0, n=G_N_ELEMENTS (stockRatios); i<n; ++i)
524    {
525      char buf[128];
526      tr_strlratio (buf, stockRatios[i], sizeof (buf));
527      w = gtk_menu_item_new_with_label (buf);
528      g_object_set_data (G_OBJECT (w), RATIO_KEY, GINT_TO_POINTER (i));
529      g_signal_connect (w, "activate", G_CALLBACK (onRatioSet), p);
530      gtk_menu_shell_append (menu_shell, w);
531    }
532
533    return m;
534}
535
536/***
537****  Option menu
538***/
539
540static GtkWidget*
541createOptionsMenu (PrivateData * p)
542{
543  GtkWidget * m;
544  GtkWidget * top = gtk_menu_new ();
545  GtkMenuShell * menu_shell = GTK_MENU_SHELL (top);
546
547  m = gtk_menu_item_new_with_label (_("Limit Download Speed"));
548  gtk_menu_item_set_submenu (GTK_MENU_ITEM (m), createSpeedMenu (p, TR_DOWN));
549  gtk_menu_shell_append (menu_shell, m);
550
551  m = gtk_menu_item_new_with_label (_("Limit Upload Speed"));
552  gtk_menu_item_set_submenu (GTK_MENU_ITEM (m), createSpeedMenu (p, TR_UP));
553  gtk_menu_shell_append (menu_shell, m);
554
555  m = gtk_separator_menu_item_new ();
556  gtk_menu_shell_append (menu_shell, m);
557
558  m = gtk_menu_item_new_with_label (_("Stop Seeding at Ratio"));
559  gtk_menu_item_set_submenu (GTK_MENU_ITEM (m), createRatioMenu (p));
560  gtk_menu_shell_append (menu_shell, m);
561
562  gtk_widget_show_all (top);
563  return top;
564}
565
566static void
567onOptionsClicked (GtkButton * button UNUSED, gpointer vp)
568{
569  char buf1[512];
570  char buf2[512];
571  gboolean b;
572  GtkWidget * w;
573  PrivateData * p = vp;
574
575  w = p->speedlimit_on_item[TR_DOWN];
576  tr_formatter_speed_KBps (buf1, gtr_pref_int_get (TR_KEY_speed_limit_down), sizeof (buf1));
577  gtr_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (w))), buf1);
578
579  b = gtr_pref_flag_get (TR_KEY_speed_limit_down_enabled);
580  w = b ? p->speedlimit_on_item[TR_DOWN] : p->speedlimit_off_item[TR_DOWN];
581  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
582
583  w = p->speedlimit_on_item[TR_UP];
584  tr_formatter_speed_KBps (buf1, gtr_pref_int_get (TR_KEY_speed_limit_up), sizeof (buf1));
585  gtr_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (w))), buf1);
586
587  b = gtr_pref_flag_get (TR_KEY_speed_limit_up_enabled);
588  w = b ? p->speedlimit_on_item[TR_UP] : p->speedlimit_off_item[TR_UP];
589  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
590
591  tr_strlratio (buf1, gtr_pref_double_get (TR_KEY_ratio_limit), sizeof (buf1));
592  g_snprintf (buf2, sizeof (buf2), _("Stop at Ratio (%s)"), buf1);
593  gtr_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (p->ratio_on_item))), buf2);
594
595  b = gtr_pref_flag_get (TR_KEY_ratio_limit_enabled);
596  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (b ? p->ratio_on_item : p->ratio_off_item), TRUE);
597
598  gtk_menu_popup (GTK_MENU (p->options_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
599}
600
601/***
602****  PUBLIC
603***/
604
605GtkWidget *
606gtr_window_new (GtkApplication * app, GtkUIManager * ui_mgr, TrCore * core)
607{
608  int              i, n;
609  const char     * pch, * style;
610  PrivateData    * p;
611  GtkWidget      * sibling = NULL;
612  GtkWidget      * ul_lb, * dl_lb;
613  GtkWidget      * mainmenu, *toolbar, *filter, *list, *status;
614  GtkWidget      * vbox, *w, *self, *menu;
615  GtkWidget      * grid_w;
616  GtkWindow      * win;
617  GtkCssProvider * css_provider;
618  GSList         * l;
619  GtkGrid        * grid;
620
621  p = g_new0 (PrivateData, 1);
622
623  /* make the window */
624  self = gtk_application_window_new (app);
625  g_object_set_qdata_full (G_OBJECT (self), get_private_data_key (), p, privateFree);
626  win = GTK_WINDOW (self);
627  gtk_window_set_title (win, g_get_application_name ());
628  gtk_window_set_role (win, "tr-main");
629  gtk_window_set_default_size (win,
630                               gtr_pref_int_get (TR_KEY_main_window_width),
631                               gtr_pref_int_get (TR_KEY_main_window_height));
632  gtk_window_move (win, gtr_pref_int_get (TR_KEY_main_window_x),
633                        gtr_pref_int_get (TR_KEY_main_window_y));
634  if (gtr_pref_flag_get (TR_KEY_main_window_is_maximized))
635    gtk_window_maximize (win);
636  gtk_window_add_accel_group (win, gtk_ui_manager_get_accel_group (ui_mgr));
637  /* Add style provider to the window. */
638  /* Please move it to separate .css file if you’re adding more styles here. */
639  style = ".tr-workarea {border-width: 1px 0; border-style: solid; border-radius: 0;}";
640  css_provider = gtk_css_provider_new ();
641  gtk_css_provider_load_from_data (css_provider, style,
642                                   strlen (style), NULL);
643  gtk_style_context_add_provider_for_screen(gdk_screen_get_default (),
644                                            GTK_STYLE_PROVIDER (css_provider),
645                                            GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
646
647  /* window's main container */
648  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
649  gtk_container_add (GTK_CONTAINER (self), vbox);
650
651  /* main menu */
652  mainmenu = gtr_action_get_widget ("/main-window-menu");
653  w = gtr_action_get_widget ("/main-window-menu/torrent-menu/torrent-reannounce");
654  g_signal_connect (w, "query-tooltip", G_CALLBACK (onAskTrackerQueryTooltip), p);
655
656  /* toolbar */
657  toolbar = p->toolbar = gtr_action_get_widget ("/main-window-toolbar");
658  gtk_style_context_add_class (gtk_widget_get_style_context (toolbar),
659                               GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
660  gtr_action_set_important ("open-torrent-toolbar", TRUE);
661  gtr_action_set_important ("show-torrent-properties", TRUE);
662
663  /* filter */
664  w = filter = p->filter = gtr_filter_bar_new (gtr_core_session (core),
665                                               gtr_core_model (core),
666                                               &p->filter_model);
667  gtk_container_set_border_width (GTK_CONTAINER (w), GUI_PAD_SMALL);
668
669  /* status menu */
670  menu = p->status_menu = gtk_menu_new ();
671  l = NULL;
672  pch = gtr_pref_string_get (TR_KEY_statusbar_stats);
673  for (i=0, n=G_N_ELEMENTS (stats_modes); i<n; ++i)
674    {
675      const char * val = stats_modes[i].val;
676      w = gtk_radio_menu_item_new_with_label (l, _ (stats_modes[i].i18n));
677      l = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
678      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), !strcmp (val, pch));
679      g_object_set_data (G_OBJECT (w), STATS_MODE, (gpointer)stats_modes[i].val);
680      g_signal_connect (w, "toggled", G_CALLBACK (status_menu_toggled_cb), p);
681      gtk_menu_shell_append (GTK_MENU_SHELL (menu), w);
682      gtk_widget_show (w);
683    }
684
685  /**
686  *** Statusbar
687  **/
688
689  grid_w = status = p->status = gtk_grid_new ();
690  grid = GTK_GRID (grid_w);
691  gtk_container_set_border_width (GTK_CONTAINER (grid), GUI_PAD_SMALL);
692
693  /* gear */
694  w = gtk_button_new ();
695  gtk_container_add (GTK_CONTAINER (w), gtk_image_new_from_stock ("utilities", -1));
696  gtk_widget_set_tooltip_text (w, _("Options"));
697  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
698  gtk_button_set_relief (GTK_BUTTON (w), GTK_RELIEF_NONE);
699  p->options_menu = createOptionsMenu (p);
700  g_signal_connect (w, "clicked", G_CALLBACK (onOptionsClicked), p);
701  sibling = w;
702
703  /* turtle */
704  p->alt_speed_image = gtk_image_new ();
705  w = p->alt_speed_button = gtk_toggle_button_new ();
706  gtk_button_set_image (GTK_BUTTON (w), p->alt_speed_image);
707  gtk_button_set_relief (GTK_BUTTON (w), GTK_RELIEF_NONE);
708  g_signal_connect (w, "toggled", G_CALLBACK (alt_speed_toggled_cb), p);
709  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
710  sibling = w;
711
712  /* spacer */
713  w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
714  gtk_widget_set_hexpand (w, TRUE);
715  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
716  sibling = w;
717
718  /* freespace */
719  w = gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU);
720  p->freespace_icon = w;
721  g_object_set (G_OBJECT(w), "margin-left", GUI_PAD, NULL);
722  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
723  sibling = w;
724  w = gtk_label_new (NULL);
725  g_object_set (G_OBJECT(w), "margin-left", GUI_PAD_BIG*2, NULL);
726  p->freespace_lb = GTK_LABEL (w);
727  gtk_label_set_single_line_mode (p->freespace_lb, TRUE);
728  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
729  sibling = w;
730
731  /* spacer */
732  w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
733  gtk_widget_set_hexpand (w, TRUE);
734  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
735  sibling = w;
736
737  /* download */
738  w = dl_lb = gtk_label_new (NULL);
739  p->dl_lb = GTK_LABEL (w);
740  gtk_label_set_single_line_mode (p->dl_lb, TRUE);
741  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
742  sibling = w;
743  w = gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU);
744  g_object_set (G_OBJECT(w), "margin-left", GUI_PAD, NULL);
745  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
746  sibling = w;
747
748  /* upload */
749  w = ul_lb = gtk_label_new (NULL);
750  g_object_set (G_OBJECT(w), "margin-left", GUI_PAD, NULL);
751  p->ul_lb = GTK_LABEL (w);
752  gtk_label_set_single_line_mode (p->ul_lb, TRUE);
753  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
754  sibling = w;
755  w = gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU);
756  g_object_set (G_OBJECT(w), "margin-left", GUI_PAD, NULL);
757  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
758  sibling = w;
759
760  /* ratio */
761  w = gtk_label_new (NULL);
762  g_object_set (G_OBJECT(w), "margin-left", GUI_PAD_BIG, NULL);
763  p->stats_lb = GTK_LABEL (w);
764  gtk_label_set_single_line_mode (p->stats_lb, TRUE);
765  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
766  sibling = w;
767  w = gtk_button_new ();
768  gtk_widget_set_tooltip_text (w, _("Statistics"));
769  gtk_container_add (GTK_CONTAINER (w), gtk_image_new_from_stock ("ratio", -1));
770  gtk_button_set_relief (GTK_BUTTON (w), GTK_RELIEF_NONE);
771  g_signal_connect (w, "clicked", G_CALLBACK (onYinYangReleased), p);
772  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
773  sibling = w;
774
775  /* workarea */
776  p->view = makeview (p);
777  w = list = p->scroll = gtk_scrolled_window_new (NULL, NULL);
778  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
779                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
780  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
781                                       GTK_SHADOW_OUT);
782  gtk_style_context_add_class (gtk_widget_get_style_context (w), "tr-workarea");
783  gtk_container_add (GTK_CONTAINER (w), p->view);
784
785  /* lay out the widgets */
786  gtk_box_pack_start (GTK_BOX (vbox), mainmenu, FALSE, FALSE, 0);
787  gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
788  gtk_box_pack_start (GTK_BOX (vbox), filter, FALSE, FALSE, 0);
789  gtk_box_pack_start (GTK_BOX (vbox), list, TRUE, TRUE, 0);
790  gtk_box_pack_start (GTK_BOX (vbox), status, FALSE, FALSE, 0);
791
792  {
793    /* this is to determine the maximum width/height for the label */
794    int w=0, h=0;
795    PangoLayout * pango_layout;
796    pango_layout = gtk_widget_create_pango_layout (ul_lb, "999.99 kB/s");
797    pango_layout_get_pixel_size (pango_layout, &w, &h);
798    gtk_widget_set_size_request (ul_lb, w, h);
799    gtk_widget_set_size_request (dl_lb, w, h);
800    gtk_misc_set_alignment (GTK_MISC (ul_lb), 1.0, 0.5);
801    gtk_misc_set_alignment (GTK_MISC (dl_lb), 1.0, 0.5);
802    g_object_unref (G_OBJECT (pango_layout));
803  }
804
805  /* show all but the window */
806  gtk_widget_show_all (vbox);
807
808  /* listen for prefs changes that affect the window */
809  p->core = core;
810  prefsChanged (core, TR_KEY_compact_view, self);
811  prefsChanged (core, TR_KEY_show_filterbar, self);
812  prefsChanged (core, TR_KEY_show_statusbar, self);
813  prefsChanged (core, TR_KEY_statusbar_stats, self);
814  prefsChanged (core, TR_KEY_show_toolbar, self);
815  prefsChanged (core, TR_KEY_alt_speed_enabled, self);
816  p->pref_handler_id = g_signal_connect (core, "prefs-changed",
817                                         G_CALLBACK (prefsChanged), self);
818
819  tr_sessionSetAltSpeedFunc (gtr_core_session (core), onAltSpeedToggled, p);
820
821  return self;
822}
823
824static void
825updateFreeSpace (PrivateData * p)
826{
827  GtkWidget * w;
828  bool visible = false;
829
830  g_return_if_fail (p != NULL);
831
832  w = GTK_WIDGET (p->freespace_lb);
833
834  if (p->core != NULL)
835    {
836      tr_session * session = gtr_core_session (p->core);
837      const int64_t n = tr_sessionGetDownloadDirFreeSpace (session);
838      const char * downloadDir = tr_sessionGetDownloadDir (session);
839
840      visible = n >= 0;
841
842      if (visible)
843        {
844          char * tip;
845          char sizeStr[32];
846
847          tr_strlsize (sizeStr, n, sizeof(sizeStr));
848          gtk_label_set_text (p->freespace_lb, sizeStr);
849
850          tip = tr_strdup_printf (_("Download folder \"%1$s\" has %2$s free"), downloadDir, sizeStr);
851          gtk_widget_set_tooltip_text (w, tip);
852          g_free (tip);
853        }
854    }
855
856  gtk_widget_set_visible (w, visible);
857  gtk_widget_set_visible (p->freespace_icon, visible);
858}
859
860static void
861updateStats (PrivateData * p)
862{
863  const char * pch;
864  char up[32];
865  char down[32];
866  char ratio[32];
867  char buf[512];
868  struct tr_session_stats stats;
869  tr_session * session = gtr_core_session (p->core);
870
871  /* update the stats */
872  pch = gtr_pref_string_get (TR_KEY_statusbar_stats);
873  if (!strcmp (pch, "session-ratio"))
874    {
875      tr_sessionGetStats (session, &stats);
876      tr_strlratio (ratio, stats.ratio, sizeof (ratio));
877      g_snprintf (buf, sizeof (buf), _("Ratio: %s"), ratio);
878    }
879  else if (!strcmp (pch, "session-transfer"))
880    {
881      tr_sessionGetStats (session, &stats);
882      tr_strlsize (up, stats.uploadedBytes, sizeof (up));
883      tr_strlsize (down, stats.downloadedBytes, sizeof (down));
884      /* Translators: "size|" is here for disambiguation. Please remove it from your translation.
885         %1$s is the size of the data we've downloaded
886         %2$s is the size of the data we've uploaded */
887      g_snprintf (buf, sizeof (buf),
888                  Q_("Down: %1$s, Up: %2$s"), down, up);
889    }
890  else if (!strcmp (pch, "total-transfer"))
891    {
892      tr_sessionGetCumulativeStats (session, &stats);
893      tr_strlsize (up, stats.uploadedBytes, sizeof (up));
894      tr_strlsize (down, stats.downloadedBytes, sizeof (down));
895      /* Translators: "size|" is here for disambiguation. Please remove it from your translation.
896         %1$s is the size of the data we've downloaded
897         %2$s is the size of the data we've uploaded */
898      g_snprintf (buf, sizeof (buf),
899                  Q_( "size|Down: %1$s, Up: %2$s"), down, up);
900    }
901  else /* default is total-ratio */
902    {
903      tr_sessionGetCumulativeStats (session, &stats);
904      tr_strlratio (ratio, stats.ratio, sizeof (ratio));
905      g_snprintf (buf, sizeof (buf), _("Ratio: %s"), ratio);
906    }
907
908  gtr_label_set_text (p->stats_lb, buf);
909}
910
911static void
912updateSpeeds (PrivateData * p)
913{
914  tr_session * session = gtr_core_session (p->core);
915
916  if (session != NULL)
917    {
918      char buf[128];
919      double up=0, down=0;
920      GtkTreeIter iter;
921      GtkTreeModel * model = gtr_core_model (p->core);
922
923      if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) do
924        {
925          double u, d;
926          gtk_tree_model_get (model, &iter, MC_SPEED_UP, &u,
927                                            MC_SPEED_DOWN, &d,
928                                            -1);
929          up += u;
930          down += d;
931        }
932      while (gtk_tree_model_iter_next (model, &iter));
933
934      tr_formatter_speed_KBps (buf, down, sizeof (buf));
935      gtr_label_set_text (p->dl_lb, buf);
936
937      tr_formatter_speed_KBps (buf, up, sizeof (buf));
938      gtr_label_set_text (p->ul_lb, buf);
939    }
940}
941
942void
943gtr_window_refresh (GtkWindow * self)
944{
945  PrivateData * p = get_private_data (self);
946
947  if (p && p->core && gtr_core_session (p->core))
948    {
949      updateSpeeds (p);
950      updateStats (p);
951      updateFreeSpace (p);
952    }
953}
954
955GtkTreeSelection*
956gtr_window_get_selection (GtkWindow * w)
957{
958    return get_private_data (w)->selection;
959}
960
961void
962gtr_window_set_busy (GtkWindow * win, gboolean isBusy)
963{
964  GtkWidget * w = GTK_WIDGET (win);
965
966  if (w && gtk_widget_get_realized (w))
967    {
968      GdkDisplay * display = gtk_widget_get_display (w);
969      GdkCursor * cursor = isBusy ? gdk_cursor_new_for_display (display, GDK_WATCH) : NULL;
970
971      gdk_window_set_cursor (gtk_widget_get_window (w), cursor);
972      gdk_display_flush (display);
973
974      g_clear_object (&cursor);
975    }
976}
Note: See TracBrowser for help on using the repository browser.