ephy-header-bar.c 31.9 KB
Newer Older
1 2
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
3 4
 *  Copyright © 2012 Igalia S.L
 *  Copyright © 2013 Yosef Or Boczko <yoseforb@gmail.com>
Iulian Radu's avatar
Iulian Radu committed
5
 *  Copyright © 2016 Iulian-Gabriel Radu <iulian.radu67@gmail.com>
6
 *
Michael Catanzaro's avatar
Michael Catanzaro committed
7 8 9
 *  This file is part of Epiphany.
 *
 *  Epiphany is free software: you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
Michael Catanzaro's avatar
Michael Catanzaro committed
11 12
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
13
 *
Michael Catanzaro's avatar
Michael Catanzaro committed
14
 *  Epiphany is distributed in the hope that it will be useful,
15 16 17 18 19
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
Michael Catanzaro's avatar
Michael Catanzaro committed
20
 *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
21 22 23
 */

#include "config.h"
24
#include "ephy-header-bar.h"
25

26
#include "ephy-action-helper.h"
27
#include "ephy-add-bookmark-popover.h"
28
#include "ephy-bookmarks-popover.h"
29
#include "ephy-bookmark-properties-grid.h"
30 31
#include "ephy-downloads-popover.h"
#include "ephy-downloads-progress-icon.h"
32 33 34 35 36 37 38
#include "ephy-embed.h"
#include "ephy-embed-container.h"
#include "ephy-embed-prefs.h"
#include "ephy-embed-utils.h"
#include "ephy-favicon-helpers.h"
#include "ephy-gui.h"
#include "ephy-history-service.h"
39
#include "ephy-location-entry.h"
40
#include "ephy-middle-clickable-button.h"
41 42
#include "ephy-prefs.h"
#include "ephy-settings.h"
43
#include "ephy-shell.h"
44 45
#include "ephy-title-box.h"
#include "ephy-title-widget.h"
46
#include "ephy-type-builtins.h"
47

48
#include <glib/gi18n.h>
49 50
#include <webkit2/webkit2.h>

51 52 53 54 55 56 57 58
enum {
  PROP_0,
  PROP_WINDOW,
  N_PROPERTIES
};

static GParamSpec *object_properties[N_PROPERTIES] = { NULL, };

59 60 61
#define MAX_LABEL_LENGTH 48
#define HISTORY_ITEM_DATA_KEY "history-item-data-key"

62
struct _EphyHeaderBar {
63 64
  GtkHeaderBar parent_instance;

65
  EphyWindow *window;
66
  EphyTitleWidget *title_widget;
67
  GtkWidget *navigation_box;
68
  GtkWidget *new_tab_revealer;
69
  GtkWidget *new_tab_button;
70
  GtkWidget *combined_stop_reload_button;
71
  GtkWidget *bookmarks_button;
72
  GtkWidget *page_menu_button;
73 74 75
  GtkWidget *downloads_revealer;
  GtkWidget *downloads_button;
  GtkWidget *downloads_popover;
76
  GtkWidget *zoom_level_button;
77

78
  guint navigation_buttons_menu_timeout;
79 80
};

81
G_DEFINE_TYPE (EphyHeaderBar, ephy_header_bar, GTK_TYPE_HEADER_BAR)
82

Michael Catanzaro's avatar
Michael Catanzaro committed
83 84 85
/* Translators: tooltip for the refresh button */
static const char *REFRESH_BUTTON_TOOLTIP = N_("Reload the current page");

86
static gboolean
87
header_bar_is_for_active_window (EphyHeaderBar *header_bar)
88 89 90 91 92 93
{
  EphyShell *shell = ephy_shell_get_default ();
  GtkWindow *active_window;

  active_window = gtk_application_get_active_window (GTK_APPLICATION (shell));

94
  return active_window == GTK_WINDOW (header_bar->window);
95 96
}

97 98
static void
download_added_cb (EphyDownloadsManager *manager,
Michael Catanzaro's avatar
Michael Catanzaro committed
99
                   EphyDownload         *download,
100
                   EphyHeaderBar        *header_bar)
101
{
102 103 104 105
  if (!header_bar->downloads_popover) {
    header_bar->downloads_popover = ephy_downloads_popover_new (header_bar->downloads_button);
    gtk_menu_button_set_popover (GTK_MENU_BUTTON (header_bar->downloads_button),
                                 header_bar->downloads_popover);
106 107
  }

108
  gtk_revealer_set_reveal_child (GTK_REVEALER (header_bar->downloads_revealer), TRUE);
109

110 111
  if (header_bar_is_for_active_window (header_bar))
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (header_bar->downloads_button), TRUE);
112 113
}

114 115
static void
download_completed_cb (EphyDownloadsManager *manager,
Michael Catanzaro's avatar
Michael Catanzaro committed
116
                       EphyDownload         *download,
117
                       EphyHeaderBar        *header_bar)
118
{
119 120
  if (header_bar_is_for_active_window (header_bar))
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (header_bar->downloads_button), TRUE);
121 122
}

123 124
static void
download_removed_cb (EphyDownloadsManager *manager,
Michael Catanzaro's avatar
Michael Catanzaro committed
125
                     EphyDownload         *download,
126
                     EphyHeaderBar        *header_bar)
127 128
{
  if (!ephy_downloads_manager_get_downloads (manager))
129
    gtk_revealer_set_reveal_child (GTK_REVEALER (header_bar->downloads_revealer), FALSE);
130 131
}

132 133
static void
downloads_estimated_progress_cb (EphyDownloadsManager *manager,
134
                                 EphyHeaderBar        *header_bar)
135
{
136
  gtk_widget_queue_draw (gtk_button_get_image (GTK_BUTTON (header_bar->downloads_button)));
137 138
}

139
static void
140 141 142 143
ephy_header_bar_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
144
{
145
  EphyHeaderBar *header_bar = EPHY_HEADER_BAR (object);
146 147

  switch (property_id) {
Michael Catanzaro's avatar
Michael Catanzaro committed
148
    case PROP_WINDOW:
149
      header_bar->window = EPHY_WINDOW (g_value_get_object (value));
Michael Catanzaro's avatar
Michael Catanzaro committed
150 151 152 153
      g_object_notify_by_pspec (object, object_properties[PROP_WINDOW]);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
154 155 156 157
  }
}

static void
158 159 160 161
ephy_header_bar_get_property (GObject    *object,
                              guint       property_id,
                              GValue     *value,
                              GParamSpec *pspec)
162
{
163
  EphyHeaderBar *header_bar = EPHY_HEADER_BAR (object);
164 165

  switch (property_id) {
Michael Catanzaro's avatar
Michael Catanzaro committed
166
    case PROP_WINDOW:
167
      g_value_set_object (value, header_bar->window);
Michael Catanzaro's avatar
Michael Catanzaro committed
168 169 170
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
171 172 173
  }
}

174
static void
175
sync_chromes_visibility (EphyHeaderBar *header_bar)
176 177 178
{
  EphyWindowChrome chrome;

179
  chrome = ephy_window_get_chrome (header_bar->window);
180

181
  gtk_widget_set_visible (header_bar->navigation_box, chrome & EPHY_WINDOW_CHROME_HEADER_BAR);
182
  gtk_widget_set_visible (header_bar->bookmarks_button, chrome & EPHY_WINDOW_CHROME_BOOKMARKS);
183 184
  gtk_widget_set_visible (header_bar->page_menu_button, chrome & EPHY_WINDOW_CHROME_MENU);
  gtk_widget_set_visible (header_bar->new_tab_button, chrome & EPHY_WINDOW_CHROME_TABSBAR);
185 186
}

187 188 189 190 191
static void
homepage_url_changed (GSettings  *settings,
                      const char *key,
                      GtkWidget  *button)
{
192
  char *setting;
193 194 195

  setting = g_settings_get_string (settings, key);
  gtk_widget_set_visible (button, setting && setting[0]);
196
  g_free (setting);
197 198
}

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
typedef enum {
  EPHY_NAVIGATION_HISTORY_DIRECTION_BACK,
  EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD
} EphyNavigationHistoryDirection;

typedef enum {
  WEBKIT_HISTORY_BACKWARD,
  WEBKIT_HISTORY_FORWARD
} WebKitHistoryType;

static gboolean
item_enter_notify_event_cb (GtkWidget   *widget,
                            GdkEvent    *event,
                            EphyWebView *view)
{
  char *text;

  text = g_object_get_data (G_OBJECT (widget), "link-message");
  ephy_web_view_set_link_message (view, text);

  return FALSE;
}

static gboolean
item_leave_notify_event_cb (GtkWidget   *widget,
                            GdkEvent    *event,
                            EphyWebView *view)
{
  ephy_web_view_set_link_message (view, NULL);
  return FALSE;
}

static void
232 233 234
icon_loaded_cb (GObject      *source,
                GAsyncResult *result,
                GtkWidget    *image)
235 236 237 238 239 240 241 242 243 244 245
{
  WebKitFaviconDatabase *database = WEBKIT_FAVICON_DATABASE (source);
  GdkPixbuf *favicon = NULL;
  cairo_surface_t *icon_surface = webkit_favicon_database_get_favicon_finish (database, result, NULL);

  if (icon_surface) {
    favicon = ephy_pixbuf_get_from_surface_scaled (icon_surface, FAVICON_SIZE, FAVICON_SIZE);
    cairo_surface_destroy (icon_surface);
  }

  if (favicon) {
246 247
    gtk_image_set_from_pixbuf (GTK_IMAGE (image), favicon);
    gtk_widget_show (image);
248 249 250 251

    g_object_unref (favicon);
  }

252
  g_object_unref (image);
253 254 255 256 257 258 259
}

static GtkWidget *
new_history_menu_item (EphyWebView *view,
                       const char  *origtext,
                       const char  *address)
{
260 261 262 263
  GtkWidget *menu_item;
  GtkWidget *box;
  GtkWidget *image;
  GtkWidget *label;
264 265 266 267 268
  WebKitFaviconDatabase *database;
  EphyEmbedShell *shell = ephy_embed_shell_get_default ();

  g_return_val_if_fail (address != NULL && origtext != NULL, NULL);

269 270 271 272 273 274 275 276 277 278
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);

  image = gtk_image_new ();
  gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);

  label = gtk_label_new (origtext);
  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
  gtk_label_set_max_width_chars (GTK_LABEL (label), MAX_LABEL_LENGTH);
  gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
  gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 6);
279

280 281
  menu_item = gtk_menu_item_new ();
  gtk_container_add (GTK_CONTAINER (menu_item), box);
282 283 284 285 286

  database = webkit_web_context_get_favicon_database (ephy_embed_shell_get_web_context (shell));
  webkit_favicon_database_get_favicon (database, address,
                                       NULL,
                                       (GAsyncReadyCallback)icon_loaded_cb,
287
                                       g_object_ref (image));
288

289
  g_object_set_data_full (G_OBJECT (menu_item), "link-message", g_strdup (address), (GDestroyNotify)g_free);
290

291
  g_signal_connect (menu_item, "enter-notify-event",
292
                    G_CALLBACK (item_enter_notify_event_cb), view);
293
  g_signal_connect (menu_item, "leave-notify-event",
294 295
                    G_CALLBACK (item_leave_notify_event_cb), view);

296
  gtk_widget_show_all (menu_item);
297

298
  return menu_item;
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
}

static void
middle_click_handle_on_history_menu_item (EphyEmbed                 *embed,
                                          WebKitBackForwardListItem *item)
{
  EphyEmbed *new_embed = NULL;
  const gchar *url;

  new_embed = ephy_shell_new_tab (ephy_shell_get_default (),
                                  EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed))),
                                  embed,
                                  0);
  g_return_if_fail (new_embed != NULL);

  /* Load the new URL */
  url = webkit_back_forward_list_item_get_original_uri (item);
  ephy_web_view_load_url (ephy_embed_get_web_view (new_embed), url);
}

static gboolean
navigation_menu_item_pressed_cb (GtkWidget *menuitem,
                                 GdkEvent  *event,
                                 gpointer   user_data)
{
  EphyWindow *window = EPHY_WINDOW (user_data);
  WebKitBackForwardListItem *item;
  EphyEmbed *embed;

  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));

  item = (WebKitBackForwardListItem *)g_object_get_data (G_OBJECT (menuitem), HISTORY_ITEM_DATA_KEY);

Iulian Radu's avatar
Iulian Radu committed
332
  if (((GdkEventButton *)event)->button == GDK_BUTTON_MIDDLE) {
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
    middle_click_handle_on_history_menu_item (embed, item);
  } else {
    WebKitWebView *web_view;

    web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);
    webkit_web_view_go_to_back_forward_list_item (web_view, item);
  }

  return G_SOURCE_REMOVE;
}

static GList *
construct_webkit_history_list (WebKitWebView    *web_view,
                               WebKitHistoryType hist_type,
                               int               limit)
{
  WebKitBackForwardList *back_forward_list;

  back_forward_list = webkit_web_view_get_back_forward_list (web_view);
  return hist_type == WEBKIT_HISTORY_FORWARD ?
         g_list_reverse (webkit_back_forward_list_get_forward_list_with_limit (back_forward_list, limit)) :
         webkit_back_forward_list_get_back_list_with_limit (back_forward_list, limit);
}

static GtkWidget *
build_dropdown_menu (EphyWindow                    *window,
                     EphyNavigationHistoryDirection direction)
{
  GtkMenuShell *menu;
  EphyEmbed *embed;
  GList *list, *l;
  WebKitWebView *web_view;

  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
  g_return_val_if_fail (embed != NULL, NULL);

  menu = GTK_MENU_SHELL (gtk_menu_new ());

  web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);

  if (direction == EPHY_NAVIGATION_HISTORY_DIRECTION_BACK)
    list = construct_webkit_history_list (web_view,
                                          WEBKIT_HISTORY_BACKWARD, 10);
  else
    list = construct_webkit_history_list (web_view,
                                          WEBKIT_HISTORY_FORWARD, 10);

  for (l = list; l != NULL; l = l->next) {
    GtkWidget *item;
    WebKitBackForwardListItem *hitem;
    const char *uri;
    char *title;

    hitem = (WebKitBackForwardListItem *)l->data;
    uri = webkit_back_forward_list_item_get_uri (hitem);
    title = g_strdup (webkit_back_forward_list_item_get_title (hitem));

    if (title == NULL || g_strstrip (title)[0] == '\0')
      item = new_history_menu_item (EPHY_WEB_VIEW (web_view), uri, uri);
    else
      item = new_history_menu_item (EPHY_WEB_VIEW (web_view), title, uri);

    g_free (title);

    g_object_set_data_full (G_OBJECT (item), HISTORY_ITEM_DATA_KEY,
                            g_object_ref (hitem), g_object_unref);

    g_signal_connect (item, "button-release-event",
                      G_CALLBACK (navigation_menu_item_pressed_cb), window);

    gtk_menu_shell_append (menu, item);
    gtk_widget_show_all (item);
  }

  g_list_free (list);

  return GTK_WIDGET (menu);
}

static void
popup_history_menu (GtkWidget                     *widget,
                    EphyWindow                    *window,
                    EphyNavigationHistoryDirection direction,
                    GdkEventButton                *event)
{
  GtkWidget *menu;

  menu = build_dropdown_menu (window, direction);
421 422 423 424 425
  gtk_menu_popup_at_widget (GTK_MENU (menu),
                            widget,
                            GDK_GRAVITY_SOUTH_WEST,
                            GDK_GRAVITY_NORTH_WEST,
                            (GdkEvent *)event);
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
}

typedef struct {
  GtkWidget *button;
  EphyWindow *window;
  EphyNavigationHistoryDirection direction;
  GdkEventButton *event;
} PopupData;

static gboolean
menu_timeout_cb (PopupData *data)
{
  if (data != NULL && data->window)
    popup_history_menu (data->button, data->window, data->direction, data->event);

  return G_SOURCE_REMOVE;
}

static gboolean
navigation_button_press_event_cb (GtkButton *button,
                                  GdkEvent  *event,
                                  gpointer   user_data)
{
449
  EphyHeaderBar *header_bar = EPHY_HEADER_BAR (user_data);
450
  EphyNavigationHistoryDirection direction;
451
  const gchar *action_name;
452

453
  action_name = gtk_actionable_get_action_name (GTK_ACTIONABLE (button));
454

455 456
  direction = strstr (action_name, "back") ? EPHY_NAVIGATION_HISTORY_DIRECTION_BACK
                                           : EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD;
457

Iulian Radu's avatar
Iulian Radu committed
458
  if (((GdkEventButton *)event)->button == GDK_BUTTON_SECONDARY) {
459
    popup_history_menu (GTK_WIDGET (button), header_bar->window,
460 461 462 463 464 465
                        direction, (GdkEventButton *)event);
  } else {
    PopupData *data;

    data = g_new (PopupData, 1);
    data->button = GTK_WIDGET (button);
466
    data->window = header_bar->window;
467 468 469
    data->direction = direction;
    data->event = (GdkEventButton *)event;

470 471 472 473 474
    header_bar->navigation_buttons_menu_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 500,
                                                                      (GSourceFunc)menu_timeout_cb,
                                                                      data,
                                                                      (GDestroyNotify)g_free);
    g_source_set_name_by_id (header_bar->navigation_buttons_menu_timeout, "[epiphany] menu_timeout_cb");
475 476 477 478 479 480 481 482 483 484
  }

  return FALSE;
}

static gboolean
navigation_button_release_event_cb (GtkButton *button,
                                    GdkEvent  *event,
                                    gpointer   user_data)
{
485
  EphyHeaderBar *header_bar = EPHY_HEADER_BAR (user_data);
486 487 488
  GActionGroup *action_group;
  GAction *action;
  EphyNavigationHistoryDirection direction;
489
  const gchar *action_name;
490

491 492 493
  if (header_bar->navigation_buttons_menu_timeout > 0) {
    g_source_remove (header_bar->navigation_buttons_menu_timeout);
    header_bar->navigation_buttons_menu_timeout = 0;
494
  }
495

496
  action_name = gtk_actionable_get_action_name (GTK_ACTIONABLE (button));
497
  action_group = gtk_widget_get_action_group (GTK_WIDGET (header_bar->window), "toolbar");
498

499 500
  direction = strstr (action_name, "back") == 0 ? EPHY_NAVIGATION_HISTORY_DIRECTION_BACK
                                                : EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD;
501 502

  switch (((GdkEventButton *)event)->button) {
503
    case GDK_BUTTON_MIDDLE:
504 505 506
      if (direction == EPHY_NAVIGATION_HISTORY_DIRECTION_BACK) {
        action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                             "navigation-back-new-tab");
507
        g_action_activate (action, NULL);
508 509 510
      } else if (direction == EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD) {
        action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                             "navigation-forward-new-tab");
511
        g_action_activate (action, NULL);
512 513
      }
      break;
514
    case GDK_BUTTON_SECONDARY:
515
      popup_history_menu (GTK_WIDGET (button), header_bar->window,
516 517 518 519 520 521 522 523 524 525 526 527 528 529
                          direction, (GdkEventButton *)event);
      break;
    default:
      break;
  }

  return G_SOURCE_REMOVE;
}

static gboolean
navigation_leave_notify_event_cb (GtkButton *button,
                                  GdkEvent  *event,
                                  gpointer   user_data)
{
530
  EphyHeaderBar *header_bar = EPHY_HEADER_BAR (user_data);
531

532 533 534
  if (header_bar->navigation_buttons_menu_timeout > 0) {
    g_source_remove (header_bar->navigation_buttons_menu_timeout);
    header_bar->navigation_buttons_menu_timeout = 0;
535
  }
536 537 538 539

  return G_SOURCE_REMOVE;
}

540
void
541 542 543
ephy_header_bar_change_combined_stop_reload_state (GSimpleAction *action,
                                                   GVariant      *loading,
                                                   gpointer       user_data)
544 545
{
  EphyWindow *window = EPHY_WINDOW (user_data);
546
  EphyHeaderBar *header_bar;
547 548
  GtkWidget *image;

Michael Catanzaro's avatar
Michael Catanzaro committed
549 550 551
  header_bar = EPHY_HEADER_BAR (ephy_window_get_header_bar (window));

  if (g_variant_get_boolean (loading)) {
552 553
    image = gtk_image_new_from_icon_name ("process-stop-symbolic",
                                          GTK_ICON_SIZE_BUTTON);
Michael Catanzaro's avatar
Michael Catanzaro committed
554 555 556
    /* Translators: tooltip for the stop button */
    gtk_widget_set_tooltip_text (header_bar->combined_stop_reload_button, _("Stop loading the current page"));
  } else {
557 558
    image = gtk_image_new_from_icon_name ("view-refresh-symbolic",
                                          GTK_ICON_SIZE_BUTTON);
Michael Catanzaro's avatar
Michael Catanzaro committed
559 560
    gtk_widget_set_tooltip_text (header_bar->combined_stop_reload_button, _(REFRESH_BUTTON_TOOLTIP));
  }
561

562 563 564
  gtk_button_set_image (GTK_BUTTON (header_bar->combined_stop_reload_button), image);

  g_simple_action_set_state (action, loading);
565 566
}

567 568 569 570 571
static void
add_bookmark_button_clicked_cb (EphyLocationEntry *entry,
                                gpointer          *user_data)
{
  EphyHeaderBar *header_bar = EPHY_HEADER_BAR (user_data);
572 573
  GActionGroup *action_group;
  GAction *action;
574

575 576
  action_group = gtk_widget_get_action_group (GTK_WIDGET (header_bar->window), "win");
  action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "bookmark-page");
577

578
  g_action_activate (action, NULL);
579 580
}

581 582 583 584 585
static void
notebook_show_tabs_changed_cb (GtkNotebook   *notebook,
                               GParamSpec    *pspec,
                               EphyHeaderBar *header_bar)
{
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
  gboolean visible = !gtk_notebook_get_show_tabs (notebook);

  if (visible) {
    gtk_widget_show (header_bar->new_tab_revealer);
    gtk_revealer_set_reveal_child (GTK_REVEALER (header_bar->new_tab_revealer), TRUE);
  } else {
    /* Note the animation here doesn't actually work, since we hide the revealer
     * right away. That's not ideal, but not much we can do about it, since
     * hiding the revealer results in the location entry expanding, and that
     * needs to happen immediately or it looks pretty bad, so we can't wait
     * until the animation completes. Using the revealer is still worthwhile
     * because the new tab button reveal animation is more important.
     */
    gtk_revealer_set_reveal_child (GTK_REVEALER (header_bar->new_tab_revealer), FALSE);
    gtk_widget_hide (header_bar->new_tab_revealer);
  }
602 603
}

604
static void
605
ephy_header_bar_constructed (GObject *object)
606
{
607
  EphyHeaderBar *header_bar = EPHY_HEADER_BAR (object);
608
  GtkWidget *box, *button, *notebook;
609
  GtkWidget *page_menu_popover;
610
  EphyDownloadsManager *downloads_manager;
611
  GtkBuilder *builder;
612
  EphyEmbedShell *embed_shell;
613

614
  G_OBJECT_CLASS (ephy_header_bar_parent_class)->constructed (object);
615

616 617
  g_signal_connect_swapped (header_bar->window, "notify::chrome",
                            G_CALLBACK (sync_chromes_visibility), header_bar);
618

619 620
  /* Back and Forward */
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
621
  header_bar->navigation_box = box;
622 623

  /* Back */
624 625 626 627
  button = gtk_button_new ();
  gtk_button_set_image (GTK_BUTTON (button),
                        gtk_image_new_from_icon_name ("go-previous-symbolic",
                        GTK_ICON_SIZE_BUTTON));
628
  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
Michael Catanzaro's avatar
Michael Catanzaro committed
629 630
  /* Translators: tooltip for the back button */
  gtk_widget_set_tooltip_text (button, _("Go back to the previous page"));
631 632 633
  gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
                                  "toolbar.navigation-back");
  g_signal_connect (button, "button-press-event",
634
                    G_CALLBACK (navigation_button_press_event_cb), header_bar);
635
  g_signal_connect (button, "button-release-event",
636
                    G_CALLBACK (navigation_button_release_event_cb), header_bar);
637
  g_signal_connect (button, "leave-notify-event",
638
                    G_CALLBACK (navigation_leave_notify_event_cb), header_bar);
639
  gtk_container_add (GTK_CONTAINER (box), button);
640
  gtk_widget_show (GTK_WIDGET (button));
641 642

  /* Forward */
643 644 645 646
  button = gtk_button_new ();
  gtk_button_set_image (GTK_BUTTON (button),
                        gtk_image_new_from_icon_name ("go-next-symbolic",
                        GTK_ICON_SIZE_BUTTON));
647
  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
Michael Catanzaro's avatar
Michael Catanzaro committed
648 649
  /* Translators: tooltip for the forward button */
  gtk_widget_set_tooltip_text (button, _("Go forward to the next page"));
650 651 652
  gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
                                  "toolbar.navigation-forward");
  g_signal_connect (button, "button-press-event",
653
                    G_CALLBACK (navigation_button_press_event_cb), header_bar);
654
  g_signal_connect (button, "button-release-event",
655
                    G_CALLBACK (navigation_button_release_event_cb), header_bar);
656
  g_signal_connect (button, "leave-notify-event",
657
                    G_CALLBACK (navigation_leave_notify_event_cb), header_bar);
658
  gtk_container_add (GTK_CONTAINER (box), button);
659
  gtk_widget_show (GTK_WIDGET (button));
660 661 662 663 664 665

  gtk_style_context_add_class (gtk_widget_get_style_context (box),
                               "raised");
  gtk_style_context_add_class (gtk_widget_get_style_context (box),
                               "linked");

666
  gtk_header_bar_pack_start (GTK_HEADER_BAR (header_bar), box);
667

668 669
  /* Reload/Stop */
  button = gtk_button_new ();
670
  header_bar->combined_stop_reload_button = button;
671 672
  gtk_button_set_image (GTK_BUTTON (button),
                        gtk_image_new_from_icon_name ("view-refresh-symbolic", GTK_ICON_SIZE_BUTTON));
673
  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
Michael Catanzaro's avatar
Michael Catanzaro committed
674
  gtk_widget_set_tooltip_text (button, _(REFRESH_BUTTON_TOOLTIP));
675 676
  gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
                                  "toolbar.combined-stop-reload");
677 678
  gtk_style_context_add_class (gtk_widget_get_style_context (button),
                               "image-button");
679
  gtk_widget_show (GTK_WIDGET (button));
680
  gtk_header_bar_pack_start (GTK_HEADER_BAR (header_bar), button);
681

682 683 684 685 686
  /* Homepage */
  button = gtk_button_new ();
  gtk_button_set_image (GTK_BUTTON (button),
                        gtk_image_new_from_icon_name ("go-home-symbolic", GTK_ICON_SIZE_BUTTON));
  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
Michael Catanzaro's avatar
Michael Catanzaro committed
687 688
  /* Translators: tooltip for the secret homepage button */
  gtk_widget_set_tooltip_text (button, _("Go to your homepage"));
689 690 691 692 693 694 695 696 697 698 699 700
  gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.home");
  gtk_header_bar_pack_start (GTK_HEADER_BAR (header_bar), button);

  embed_shell = ephy_embed_shell_get_default ();
  if (ephy_embed_shell_get_mode (embed_shell) != EPHY_EMBED_SHELL_MODE_APPLICATION) {
    homepage_url_changed (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL, button);
    g_signal_connect (EPHY_SETTINGS_MAIN,
                      "changed::" EPHY_PREFS_HOMEPAGE_URL,
                      G_CALLBACK (homepage_url_changed),
                      button);
  }

701
  /* Title widget (location entry or title box) */
702
  if (ephy_embed_shell_get_mode (embed_shell) == EPHY_EMBED_SHELL_MODE_APPLICATION)
703
    header_bar->title_widget = EPHY_TITLE_WIDGET (ephy_title_box_new ());
704
  else
705
    header_bar->title_widget = EPHY_TITLE_WIDGET (ephy_location_entry_new ());
706 707
  gtk_widget_set_margin_start (GTK_WIDGET (header_bar->title_widget), 54);
  gtk_widget_set_margin_end (GTK_WIDGET (header_bar->title_widget), 54);
708 709
  gtk_header_bar_set_custom_title (GTK_HEADER_BAR (header_bar), GTK_WIDGET (header_bar->title_widget));
  gtk_widget_show (GTK_WIDGET (header_bar->title_widget));
710

711
  if (EPHY_IS_LOCATION_ENTRY (header_bar->title_widget)) {
712 713 714
    ephy_location_entry_set_add_bookmark_popover (EPHY_LOCATION_ENTRY (header_bar->title_widget),
                                                  GTK_POPOVER (ephy_add_bookmark_popover_new (header_bar)));

715 716 717 718 719 720 721
    g_signal_connect_object (header_bar->title_widget,
                             "bookmark-clicked",
                             G_CALLBACK (add_bookmark_button_clicked_cb),
                             header_bar,
                             0);
  }

722
  /* Page Menu */
723
  button = gtk_menu_button_new ();
724
  header_bar->page_menu_button = button;
725 726 727
  gtk_button_set_image (GTK_BUTTON (button),
                        gtk_image_new_from_icon_name ("open-menu-symbolic", GTK_ICON_SIZE_BUTTON));
  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
728 729 730
  g_type_ensure (G_TYPE_THEMED_ICON);
  builder = gtk_builder_new_from_resource ("/org/gnome/epiphany/gtk/page-menu-popover.ui");
  page_menu_popover = GTK_WIDGET (gtk_builder_get_object (builder, "page-menu-popover"));
731
  header_bar->zoom_level_button = GTK_WIDGET (gtk_builder_get_object (builder, "zoom-level"));
732
  gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), page_menu_popover);
733
  g_object_unref (builder);
Iulian Radu's avatar
Iulian Radu committed
734

735
  gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar), button);
736

737 738 739 740
  /* Bookmarks button */
  button = gtk_menu_button_new ();
  header_bar->bookmarks_button = button;
  gtk_button_set_image (GTK_BUTTON (button),
Iulian Radu's avatar
Iulian Radu committed
741
                        gtk_image_new_from_icon_name ("ephy-bookmarks-symbolic", GTK_ICON_SIZE_BUTTON));
742
  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
Michael Catanzaro's avatar
Michael Catanzaro committed
743 744
  /* Translators: tooltip for the bookmarks popover button */
  gtk_widget_set_tooltip_text (button, _("View and manage your bookmarks"));
745
  gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), GTK_WIDGET (ephy_bookmarks_popover_new (header_bar->window)));
746 747
  gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar), button);

748 749 750
  /* Downloads */
  downloads_manager = ephy_embed_shell_get_downloads_manager (ephy_embed_shell_get_default ());

751 752 753
  header_bar->downloads_revealer = gtk_revealer_new ();
  gtk_revealer_set_transition_type (GTK_REVEALER (header_bar->downloads_revealer), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE);
  gtk_revealer_set_reveal_child (GTK_REVEALER (header_bar->downloads_revealer),
754 755
                                 ephy_downloads_manager_get_downloads (downloads_manager) != NULL);

Michael Catanzaro's avatar
Michael Catanzaro committed
756 757 758 759 760 761 762 763
  button = gtk_menu_button_new ();
  header_bar->downloads_button = button;
  gtk_button_set_image (GTK_BUTTON (button), ephy_downloads_progress_icon_new ());
  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
  /* Translators: tooltip for the downloads button */
  gtk_widget_set_tooltip_text (button, _("View downloads"));
  gtk_container_add (GTK_CONTAINER (header_bar->downloads_revealer), button);
  gtk_widget_show (button);
764 765

  if (ephy_downloads_manager_get_downloads (downloads_manager)) {
Michael Catanzaro's avatar
Michael Catanzaro committed
766 767
    header_bar->downloads_popover = ephy_downloads_popover_new (button);
    gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), header_bar->downloads_popover);
768 769
  }

770 771 772 773 774 775 776 777 778
  /* New Tab */
  header_bar->new_tab_revealer = gtk_revealer_new ();
  gtk_revealer_set_transition_type (GTK_REVEALER (header_bar->new_tab_revealer), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE);
  gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar), header_bar->new_tab_revealer);

  button = gtk_button_new ();
  header_bar->new_tab_button = button;
  gtk_button_set_image (GTK_BUTTON (button),
                        gtk_image_new_from_icon_name ("tab-new-symbolic", GTK_ICON_SIZE_BUTTON));
Michael Catanzaro's avatar
Michael Catanzaro committed
779 780
  /* Translators: tooltip for the new tab button */
  gtk_widget_set_tooltip_text (button, _("Open a new tab"));
781 782 783 784 785 786 787 788 789 790 791 792 793
  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
  gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.new-tab");
  gtk_container_add (GTK_CONTAINER (header_bar->new_tab_revealer), button);
  gtk_widget_show (button);

  notebook = ephy_window_get_notebook (header_bar->window);
  gtk_revealer_set_reveal_child (GTK_REVEALER (header_bar->new_tab_revealer),
                                 !gtk_notebook_get_show_tabs (GTK_NOTEBOOK (notebook)));
  gtk_widget_set_visible (header_bar->new_tab_revealer,
                          !gtk_notebook_get_show_tabs (GTK_NOTEBOOK (notebook)));
  g_signal_connect_object (notebook, "notify::show-tabs",
                           G_CALLBACK (notebook_show_tabs_changed_cb), header_bar, 0);

794 795 796
  g_signal_connect_object (downloads_manager, "download-added",
                           G_CALLBACK (download_added_cb),
                           object, 0);
797 798 799
  g_signal_connect_object (downloads_manager, "download-completed",
                           G_CALLBACK (download_completed_cb),
                           object, 0);
800 801 802
  g_signal_connect_object (downloads_manager, "download-removed",
                           G_CALLBACK (download_removed_cb),
                           object, 0);
803 804 805
  g_signal_connect_object (downloads_manager, "estimated-progress-changed",
                           G_CALLBACK (downloads_estimated_progress_cb),
                           object, 0);
806

807 808
  gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar), header_bar->downloads_revealer);
  gtk_widget_show (header_bar->downloads_revealer);
809 810 811
}

static void
812
ephy_header_bar_init (EphyHeaderBar *header_bar)
813 814 815
{
}

816
static void
817
ephy_header_bar_dispose (GObject *object)
818
{
819
  EphyHeaderBar *header_bar = EPHY_HEADER_BAR (object);
820

821 822 823
  if (header_bar->navigation_buttons_menu_timeout > 0) {
    g_source_remove (header_bar->navigation_buttons_menu_timeout);
    header_bar->navigation_buttons_menu_timeout = 0;
824
  }
825

826
  G_OBJECT_CLASS (ephy_header_bar_parent_class)->dispose (object);
827 828 829
}

static void
830
ephy_header_bar_class_init (EphyHeaderBarClass *klass)
831
{
Michael Catanzaro's avatar
Michael Catanzaro committed
832 833
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

834 835 836 837
  gobject_class->dispose = ephy_header_bar_dispose;
  gobject_class->set_property = ephy_header_bar_set_property;
  gobject_class->get_property = ephy_header_bar_get_property;
  gobject_class->constructed = ephy_header_bar_constructed;
Michael Catanzaro's avatar
Michael Catanzaro committed
838 839 840 841

  object_properties[PROP_WINDOW] =
    g_param_spec_object ("window",
                         "Window",
842
                         "The header_bar's EphyWindow",
Michael Catanzaro's avatar
Michael Catanzaro committed
843 844 845 846 847 848
                         EPHY_TYPE_WINDOW,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

  g_object_class_install_properties (gobject_class,
                                     N_PROPERTIES,
                                     object_properties);
849 850
}

Michael Catanzaro's avatar
Michael Catanzaro committed
851
GtkWidget *
852
ephy_header_bar_new (EphyWindow *window)
853
{
Michael Catanzaro's avatar
Michael Catanzaro committed
854
  g_return_val_if_fail (EPHY_IS_WINDOW (window), NULL);
855

856
  return GTK_WIDGET (g_object_new (EPHY_TYPE_HEADER_BAR,
Michael Catanzaro's avatar
Michael Catanzaro committed
857 858 859
                                   "show-close-button", TRUE,
                                   "window", window,
                                   NULL));
860
}
861

862 863
EphyTitleWidget *
ephy_header_bar_get_title_widget (EphyHeaderBar *header_bar)
864
{
865
  return header_bar->title_widget;
866
}
867

868
GtkWidget *
869
ephy_header_bar_get_zoom_level_button (EphyHeaderBar *header_bar)
870
{
871
  return header_bar->zoom_level_button;
872 873
}

874 875 876 877 878
EphyWindow *
ephy_header_bar_get_window (EphyHeaderBar *header_bar)
{
  return header_bar->window;
}