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

Last change on this file since 14519 was 14519, checked in by mikedld, 6 years ago

#5930: Fix style of Gtk+3.16 overshoot and undershoot (patch by nagisa)

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