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

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

(gtk) #5190 'Add borders instead of shadow on scrolled window': fixed, patch by nagisa

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