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

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

since Transmission supports older versions of glib, add a portability wrapper for G_DEFINE_QUARK

  • Property svn:keywords set to Date Rev Author Id
File size: 30.2 KB
RevLine 
[1475]1/******************************************************************************
2 * $Id: tr-window.c 14142 2013-07-24 00:13:31Z jordan $
3 *
[11709]4 * Copyright (c) Transmission authors and contributors
[1475]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
[14018]25#include <string.h> /* strlen() */
[1475]26
27#include <gtk/gtk.h>
28#include <glib/gi18n.h>
29
[2400]30#include <libtransmission/transmission.h>
[13625]31#include <libtransmission/utils.h> /* tr_formatter_speed_KBps () */
[1475]32
[2149]33#include "actions.h"
[4222]34#include "conf.h"
[10374]35#include "filter.h"
[2149]36#include "hig.h"
[4210]37#include "torrent-cell-renderer.h"
[5122]38#include "tr-prefs.h"
39#include "tr-window.h"
[1475]40#include "util.h"
41
[2149]42typedef struct
[1475]43{
[13657]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;
[1475]66}
[2149]67PrivateData;
[1475]68
[14142]69static TR_DEFINE_QUARK (private_data, private_data)
[13657]70
[6490]71static PrivateData*
[13625]72get_private_data (GtkWindow * w)
[1475]73{
[14115]74  return g_object_get_qdata (G_OBJECT (w), private_data_quark ());
[1475]75}
76
[2149]77/***
78****
79***/
[1475]80
81static void
[13625]82on_popup_menu (GtkWidget * self UNUSED,
83               GdkEventButton * event)
[1475]84{
[13657]85  GtkWidget * menu = gtr_action_get_widget ("/main-window-popup");
[6795]86
[13657]87  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
[13625]88                  (event ? event->button : 0),
89                  (event ? event->time : 0));
[1475]90}
91
[2149]92static void
[13625]93view_row_activated (GtkTreeView       * tree_view UNUSED,
[6795]94                    GtkTreePath       * path      UNUSED,
95                    GtkTreeViewColumn * column    UNUSED,
[13625]96                    gpointer            user_data UNUSED)
[1475]97{
[13657]98  gtr_action_activate ("show-torrent-properties");
[1475]99}
100
[12856]101static gboolean
[13625]102tree_view_search_equal_func (GtkTreeModel * model,
[12856]103                             gint           column UNUSED,
104                             const gchar  * key,
105                             GtkTreeIter  * iter,
[13625]106                             gpointer       search_data UNUSED)
[12856]107{
108    gboolean match;
109    char * lower;
110    const char * name = NULL;
111
[13625]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);
[12856]116
117    return !match;
118}
119
[2149]120static GtkWidget*
[13625]121makeview (PrivateData * p)
[1475]122{
[13657]123  GtkWidget * view;
124  GtkTreeViewColumn * col;
125  GtkTreeSelection * sel;
126  GtkCellRenderer * r;
127  GtkTreeView * tree_view;
[1475]128
[13657]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);
[1475]137
[13657]138  p->selection = gtk_tree_view_get_selection (tree_view);
[1475]139
[13657]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));
[7995]145
[13657]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);
[8889]151
[13657]152  gtk_tree_view_append_column (tree_view, col);
153  g_object_set (r, "xpad", GUI_PAD_SMALL, "ypad", GUI_PAD_SMALL, NULL);
[1475]154
[13657]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);
[1475]159
[13657]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),
[13625]164                    (void *) on_popup_menu);
[13657]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);
[1475]169
[4304]170
[13657]171  gtk_tree_view_set_model (tree_view, p->filter_model);
172  g_object_unref (p->filter_model);
[4304]173
[13657]174  return view;
[1475]175}
176
[13625]177static void syncAltSpeedButton (PrivateData * p);
[8097]178
[1475]179static void
[13683]180prefsChanged (TrCore           * core UNUSED,
181              const tr_quark     key,
182              gpointer           wind)
[4222]183{
[13683]184  gboolean isEnabled;
[13657]185  PrivateData * p = get_private_data (GTK_WINDOW (wind));
[4240]186
[13683]187  switch (key)
[4222]188    {
[13683]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;
[4222]224    }
225}
226
227static void
[13625]228privateFree (gpointer vprivate)
[4222]229{
[13657]230  PrivateData * p = vprivate;
231  g_signal_handler_disconnect (p->core, p->pref_handler_id);
232  g_free (p);
[4222]233}
234
[4248]235static void
[13625]236onYinYangReleased (GtkWidget * w UNUSED, gpointer vprivate)
[4248]237{
[13657]238  PrivateData * p = vprivate;
[6795]239
[13657]240  gtk_menu_popup (GTK_MENU (p->status_menu),
241                  NULL, NULL, NULL, NULL, 0,
242                  gtk_get_current_event_time ());
[4248]243}
244
245#define STATS_MODE "stats-mode"
246
[6795]247static struct
248{
249    const char *  val, *i18n;
[4248]250} stats_modes[] = {
[13625]251    { "total-ratio",      N_("Total Ratio")                },
252    { "session-ratio",    N_("Session Ratio")              },
253    { "total-transfer",   N_("Total Transfer")             },
254    { "session-transfer", N_("Session Transfer")           }
[4248]255};
256
257static void
[13625]258status_menu_toggled_cb (GtkCheckMenuItem * menu_item,
259                        gpointer           vprivate)
[4248]260{
[13657]261  if (gtk_check_menu_item_get_active (menu_item))
[4248]262    {
[13657]263      PrivateData * p = vprivate;
264      const char *  val = g_object_get_data (G_OBJECT (menu_item), STATS_MODE);
[13683]265      gtr_core_set_pref (p->core, TR_KEY_statusbar_stats, val);
[4248]266    }
267}
268
[8097]269static void
[13625]270syncAltSpeedButton (PrivateData * p)
[8097]271{
[13657]272  char u[32];
273  char d[32];
274  char * str;
275  const char * fmt;
[13683]276  const gboolean b = gtr_pref_flag_get (TR_KEY_alt_speed_enabled);
[13657]277  const char * stock = b ? "alt-speed-on" : "alt-speed-off";
278  GtkWidget * w = p->alt_speed_button;
[8097]279
[13683]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));
[13657]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);
[8751]285
[13657]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);
[9099]290
[13657]291  g_free (str);
[8097]292}
293
294static void
[13625]295alt_speed_toggled_cb (GtkToggleButton * button, gpointer vprivate)
[8097]296{
[13657]297  PrivateData * p = vprivate;
298  const gboolean b = gtk_toggle_button_get_active (button);
[13683]299  gtr_core_set_pref_bool (p->core, TR_KEY_alt_speed_enabled,  b);
[8097]300}
[8889]301
[2149]302/***
[4304]303****  FILTER
304***/
305
[6829]306static void
[13657]307findMaxAnnounceTime (GtkTreeModel * model,
[6829]308                     GtkTreePath  * path UNUSED,
[13657]309                     GtkTreeIter  * iter,
310                     gpointer       gmaxTime)
[6829]311{
[13657]312  tr_torrent * tor;
313  const tr_stat * torStat;
314  time_t * maxTime = gmaxTime;
[6829]315
[13657]316  gtk_tree_model_get (model, iter, MC_TORRENT, &tor, -1);
317  torStat = tr_torrentStatCached (tor);
318  *maxTime = MAX (*maxTime, torStat->manualAnnounceTime);
[6829]319}
320
321static gboolean
[13657]322onAskTrackerQueryTooltip (GtkWidget   * widget UNUSED,
323                          gint          x UNUSED,
324                          gint          y UNUSED,
325                          gboolean      keyboard_tip UNUSED,
326                          GtkTooltip  * tooltip,
327                          gpointer      gdata)
[6829]328{
[13657]329  gboolean handled;
330  time_t maxTime = 0;
331  PrivateData * p = gdata;
332  const time_t now = time (NULL);
[6829]333
[13657]334  gtk_tree_selection_selected_foreach (p->selection,
335                                       findMaxAnnounceTime,
336                                       &maxTime);
337  if (maxTime <= now)
[6829]338    {
[13657]339      handled = FALSE;
[6829]340    }
[13657]341  else
[6829]342    {
[13657]343      char buf[512];
344      char timebuf[64];
345      const int seconds = maxTime - now;
[6829]346
[13657]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;
[6829]352    }
[13657]353
354  return handled;
[6829]355}
356
[8097]357static gboolean
[13625]358onAltSpeedToggledIdle (gpointer vp)
[8097]359{
[13657]360  PrivateData * p = vp;
361  gboolean b = tr_sessionUsesAltSpeed (gtr_core_session (p->core));
[13683]362  gtr_core_set_pref_bool (p->core, TR_KEY_alt_speed_enabled, b);
[8097]363
[14133]364  return G_SOURCE_REMOVE;
[8097]365}
366
367static void
[13625]368onAltSpeedToggled (tr_session * s UNUSED, bool isEnabled UNUSED, bool byUser UNUSED, void * p)
[8097]369{
[13657]370  gdk_threads_add_idle (onAltSpeedToggledIdle, p);
[8097]371}
372
[8239]373/***
374****  Speed limit menu
375***/
376
377#define DIRECTION_KEY "direction-key"
[8238]378#define ENABLED_KEY "enabled-key"
379#define SPEED_KEY "speed-key"
380
381static void
[13625]382onSpeedToggled (GtkCheckMenuItem * check, gpointer vp)
[8238]383{
[13657]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));
[13683]388  const tr_quark key = dir == TR_UP ? TR_KEY_speed_limit_up_enabled
389                                    : TR_KEY_speed_limit_down_enabled;
[8239]390
[13657]391  if (gtk_check_menu_item_get_active (check))
392    gtr_core_set_pref_bool (p->core, key, isEnabled);
[8238]393}
[8239]394
[8238]395static void
[13625]396onSpeedSet (GtkCheckMenuItem * check, gpointer vp)
[8238]397{
[13683]398  tr_quark key;
[13657]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));
[8239]403
[13683]404  key = dir==TR_UP ? TR_KEY_speed_limit_up : TR_KEY_speed_limit_down;
[13657]405  gtr_core_set_pref_int (p->core, key, KBps);
[8239]406
[13683]407  key = dir==TR_UP ? TR_KEY_speed_limit_up_enabled : TR_KEY_speed_limit_down_enabled;
[13657]408  gtr_core_set_pref_bool (p->core, key, TRUE);
[8238]409}
[8239]410
411static GtkWidget*
[13625]412createSpeedMenu (PrivateData * p, tr_direction dir)
[8238]413{
[13657]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 };
[8239]420
[13657]421  m = gtk_menu_new ();
422  menu_shell = GTK_MENU_SHELL (m);
[8239]423
[13657]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);
[8239]431
[13657]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);
[8239]439
[13657]440  w = gtk_separator_menu_item_new ();
441  gtk_menu_shell_append (menu_shell, w);
[8239]442
[13657]443  for (i=0, n=G_N_ELEMENTS (speeds_KBps); i<n; ++i)
[8239]444    {
[13657]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);
[8239]453    }
454
[13657]455  return m;
[8238]456}
[8239]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
[8238]466static void
[13625]467onRatioToggled (GtkCheckMenuItem * check, gpointer vp)
[8238]468{
[13657]469  PrivateData * p = vp;
470
471  if (gtk_check_menu_item_get_active (check))
[8238]472    {
[13657]473      gboolean f = g_object_get_data (G_OBJECT (check), ENABLED_KEY) != 0;
[13683]474      gtr_core_set_pref_bool (p->core, TR_KEY_ratio_limit_enabled, f);
[8238]475    }
476}
477static void
[13625]478onRatioSet (GtkCheckMenuItem * check, gpointer vp)
[8238]479{
[13657]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];
[13683]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);
[8238]485}
486
487static GtkWidget*
[13625]488createRatioMenu (PrivateData * p)
[8238]489{
[13657]490  int i, n;
[13822]491  GtkWidget *m, *w;
[13657]492  GtkMenuShell * menu_shell;
[8238]493
[13657]494  m = gtk_menu_new ();
495  menu_shell = GTK_MENU_SHELL (m);
[8238]496
[13657]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);
[8238]502
[13657]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);
[8238]508
[13657]509  w = gtk_separator_menu_item_new ();
510  gtk_menu_shell_append (menu_shell, w);
[8238]511
[13657]512  for (i=0, n=G_N_ELEMENTS (stockRatios); i<n; ++i)
[8239]513    {
[13657]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);
[8238]520    }
521
[8239]522    return m;
523}
[8238]524
[8239]525/***
526****  Option menu
527***/
[8238]528
[8239]529static GtkWidget*
[13625]530createOptionsMenu (PrivateData * p)
[8239]531{
[13657]532  GtkWidget * m;
533  GtkWidget * top = gtk_menu_new ();
534  GtkMenuShell * menu_shell = GTK_MENU_SHELL (top);
[8238]535
[13657]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);
[8238]539
[13657]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);
[8238]543
[13657]544  m = gtk_separator_menu_item_new ();
545  gtk_menu_shell_append (menu_shell, m);
[8238]546
[13657]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);
[8238]550
[13657]551  gtk_widget_show_all (top);
552  return top;
[8238]553}
554
555static void
[13625]556onOptionsClicked (GtkButton * button UNUSED, gpointer vp)
[8238]557{
[13657]558  char buf1[512];
559  char buf2[512];
560  gboolean b;
561  GtkWidget * w;
562  PrivateData * p = vp;
[8238]563
[13657]564  w = p->speedlimit_on_item[TR_DOWN];
[13683]565  tr_formatter_speed_KBps (buf1, gtr_pref_int_get (TR_KEY_speed_limit_down), sizeof (buf1));
[13657]566  gtr_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (w))), buf1);
[8238]567
[13683]568  b = gtr_pref_flag_get (TR_KEY_speed_limit_down_enabled);
[13657]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);
[8238]571
[13657]572  w = p->speedlimit_on_item[TR_UP];
[13683]573  tr_formatter_speed_KBps (buf1, gtr_pref_int_get (TR_KEY_speed_limit_up), sizeof (buf1));
[13657]574  gtr_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (w))), buf1);
[8238]575
[13683]576  b = gtr_pref_flag_get (TR_KEY_speed_limit_up_enabled);
[13657]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);
[8238]579
[13683]580  tr_strlratio (buf1, gtr_pref_double_get (TR_KEY_ratio_limit), sizeof (buf1));
[13657]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);
[8238]583
[13683]584  b = gtr_pref_flag_get (TR_KEY_ratio_limit_enabled);
[13657]585  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (b ? p->ratio_on_item : p->ratio_off_item), TRUE);
[8238]586
[13657]587  gtk_menu_popup (GTK_MENU (p->options_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
[8238]588}
589
[4304]590/***
[2149]591****  PUBLIC
592***/
[1515]593
[2149]594GtkWidget *
[13625]595gtr_window_new (GtkApplication * app, GtkUIManager * ui_mgr, TrCore * core)
[1475]596{
[13822]597  int              i, n;
598  const char     * pch, * style;
599  PrivateData    * p;
[13891]600  GtkWidget      * sibling = NULL;
[13822]601  GtkWidget      * ul_lb, * dl_lb;
602  GtkWidget      * mainmenu, *toolbar, *filter, *list, *status;
[13891]603  GtkWidget      * vbox, *w, *self, *menu;
604  GtkWidget      * grid_w;
[13822]605  GtkWindow      * win;
606  GtkCssProvider * css_provider;
607  GSList         * l;
[13891]608  GtkGrid        * grid;
[7502]609
[13657]610  p = g_new0 (PrivateData, 1);
[4317]611
[13657]612  /* make the window */
613  self = gtk_application_window_new (app);
[14115]614  g_object_set_qdata_full (G_OBJECT (self), private_data_quark (), p, privateFree);
[13657]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,
[13683]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))
[13657]624    gtk_window_maximize (win);
625  gtk_window_add_accel_group (win, gtk_ui_manager_get_accel_group (ui_mgr));
[13822]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 {border-width: 1px 0; border-style: solid; 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);
[1475]635
[13657]636  /* window's main container */
637  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
638  gtk_container_add (GTK_CONTAINER (self), vbox);
[1475]639
[13657]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);
[1475]644
[13657]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);
[1475]651
[13657]652  /* filter */
[13891]653  w = filter = p->filter = gtr_filter_bar_new (gtr_core_session (core),
[13657]654                                               gtr_core_model (core),
655                                               &p->filter_model);
[13891]656  gtk_container_set_border_width (GTK_CONTAINER (w), GUI_PAD_SMALL);
[8195]657
[13657]658  /* status menu */
659  menu = p->status_menu = gtk_menu_new ();
660  l = NULL;
[13683]661  pch = gtr_pref_string_get (TR_KEY_statusbar_stats);
[13657]662  for (i=0, n=G_N_ELEMENTS (stats_modes); i<n; ++i)
[4248]663    {
[13657]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));
[14018]667      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), !g_strcmp0 (val, pch));
[13657]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);
[4248]672    }
673
[13891]674  /**
675  *** Statusbar
676  **/
[4240]677
[13891]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 */
[13657]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"));
[13891]686  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
[13657]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);
[13891]690  sibling = w;
[8238]691
[13891]692  /* turtle */
[13657]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);
[13891]698  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
699  sibling = w;
[8238]700
[13891]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
[13897]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);
[13891]711  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
712  sibling = w;
[8238]713
[13891]714  /* upload */
715  w = ul_lb = gtk_label_new (NULL);
[13897]716  g_object_set (G_OBJECT(w), "margin-left", GUI_PAD, NULL);
[13891]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;
[8238]721
[13897]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);
[13891]727  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
728  sibling = w;
[13897]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);
[13891]734  gtk_grid_attach_next_to (grid, w, sibling, GTK_POS_RIGHT, 1, 1);
735  sibling = w;
736
[13657]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),
[13822]743                                       GTK_SHADOW_OUT);
744  gtk_style_context_add_class (gtk_widget_get_style_context (w), "tr-workarea");
[13657]745  gtk_container_add (GTK_CONTAINER (w), p->view);
[1475]746
[13657]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);
[7502]753
[13657]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  }
[9900]766
[13657]767  /* show all but the window */
768  gtk_widget_show_all (vbox);
[2149]769
[13657]770  /* listen for prefs changes that affect the window */
771  p->core = core;
[13683]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);
[13657]778  p->pref_handler_id = g_signal_connect (core, "prefs-changed",
779                                         G_CALLBACK (prefsChanged), self);
[4222]780
[13657]781  tr_sessionSetAltSpeedFunc (gtr_core_session (core), onAltSpeedToggled, p);
[8097]782
[13983]783  gtr_window_refresh (GTK_WINDOW(self));
[13657]784  return self;
[1475]785}
786
[4317]787static void
[13625]788updateStats (PrivateData * p)
[4322]789{
[13657]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);
[4317]797
[13657]798  /* update the stats */
[13683]799  pch = gtr_pref_string_get (TR_KEY_statusbar_stats);
[14018]800  if (!g_strcmp0 (pch, "session-ratio"))
[6795]801    {
[13657]802      tr_sessionGetStats (session, &stats);
803      tr_strlratio (ratio, stats.ratio, sizeof (ratio));
804      g_snprintf (buf, sizeof (buf), _("Ratio: %s"), ratio);
[6795]805    }
[14018]806  else if (!g_strcmp0 (pch, "session-transfer"))
[6795]807    {
[13657]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);
[6795]816    }
[14018]817  else if (!g_strcmp0 (pch, "total-transfer"))
[6795]818    {
[13657]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);
[6795]827    }
[13657]828  else /* default is total-ratio */
[6795]829    {
[13657]830      tr_sessionGetCumulativeStats (session, &stats);
831      tr_strlratio (ratio, stats.ratio, sizeof (ratio));
832      g_snprintf (buf, sizeof (buf), _("Ratio: %s"), ratio);
[4322]833    }
[13657]834
835  gtr_label_set_text (p->stats_lb, buf);
[4322]836}
837
838static void
[13625]839updateSpeeds (PrivateData * p)
[4322]840{
[13657]841  tr_session * session = gtr_core_session (p->core);
[4322]842
[13657]843  if (session != NULL)
[7069]844    {
[13983]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;
[13657]851      GtkTreeIter iter;
852      GtkTreeModel * model = gtr_core_model (p->core);
[7069]853
[13983]854
[13657]855      if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) do
[8882]856        {
[13983]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,
[13657]863                                            -1);
[13983]864          upSpeed += us;
865          upCount += uc;
866          downSpeed += ds;
867          downCount += dc;
[8882]868        }
[13657]869      while (gtk_tree_model_iter_next (model, &iter));
[8882]870
[13983]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));
[7069]875
[13983]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)));
[7069]880    }
[4322]881}
882
[2149]883void
[13625]884gtr_window_refresh (GtkWindow * self)
[1475]885{
[13657]886  PrivateData * p = get_private_data (self);
[6795]887
[13657]888  if (p && p->core && gtr_core_session (p->core))
[4248]889    {
[13657]890      updateSpeeds (p);
891      updateStats (p);
[4248]892    }
[1475]893}
894
[2149]895GtkTreeSelection*
[13625]896gtr_window_get_selection (GtkWindow * w)
[1475]897{
[13625]898    return get_private_data (w)->selection;
[1475]899}
[6795]900
[10863]901void
[13657]902gtr_window_set_busy (GtkWindow * win, gboolean isBusy)
[10863]903{
[13657]904  GtkWidget * w = GTK_WIDGET (win);
905
906  if (w && gtk_widget_get_realized (w))
[11522]907    {
[13657]908      GdkDisplay * display = gtk_widget_get_display (w);
909      GdkCursor * cursor = isBusy ? gdk_cursor_new_for_display (display, GDK_WATCH) : NULL;
[10863]910
[13657]911      gdk_window_set_cursor (gtk_widget_get_window (w), cursor);
912      gdk_display_flush (display);
[10863]913
[13657]914      g_clear_object (&cursor);
[10863]915    }
916}
Note: See TracBrowser for help on using the repository browser.