1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  *  Copyright © 2019 Purism SPC
4  *
5  *  This file is part of Epiphany.
6  *
7  *  Epiphany is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  Epiphany is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 #include "ephy-data-view.h"
23 
24 #include <ctype.h>
25 
26 typedef struct {
27   GtkWidget *box;
28   GtkWidget *child;
29   GtkWidget *header_bar;
30   GtkWidget *back_button;
31   GtkWidget *clear_button;
32   GtkWidget *search_bar;
33   GtkWidget *search_entry;
34   GtkWidget *search_button;
35   GtkWidget *stack;
36   GtkWidget *empty_page;
37   GtkWidget *spinner;
38 
39   gboolean is_loading : 1;
40   gboolean has_data : 1;
41   gboolean has_search_results : 1;
42   gboolean can_clear : 1;
43   char *search_text;
44 } EphyDataViewPrivate;
45 
46 G_DEFINE_TYPE_WITH_PRIVATE (EphyDataView, ephy_data_view, GTK_TYPE_BIN)
47 
48 enum {
49   PROP_0,
50   PROP_TITLE,
51   PROP_CLEAR_ACTION_NAME,
52   PROP_CLEAR_ACTION_TARGET,
53   PROP_CLEAR_BUTTON_LABEL,
54   PROP_CLEAR_BUTTON_TOOLTIP,
55   PROP_SEARCH_DESCRIPTION,
56   PROP_EMPTY_TITLE,
57   PROP_EMPTY_DESCRIPTION,
58   PROP_SEARCH_TEXT,
59   PROP_IS_LOADING,
60   PROP_HAS_DATA,
61   PROP_HAS_SEARCH_RESULTS,
62   PROP_CAN_CLEAR,
63   LAST_PROP,
64 };
65 
66 static GParamSpec *obj_properties[LAST_PROP];
67 
68 enum {
69   BACK_BUTTON_CLICKED,
70   CLEAR_BUTTON_CLICKED,
71   LAST_SIGNAL,
72 };
73 
74 static gint signals[LAST_SIGNAL] = { 0 };
75 
76 static void
update(EphyDataView * self)77 update (EphyDataView *self)
78 {
79   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
80   gboolean has_data = priv->has_data && priv->child && gtk_widget_get_visible (priv->child);
81 
82   if (priv->is_loading) {
83     gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "loading");
84     gtk_spinner_start (GTK_SPINNER (priv->spinner));
85   } else {
86     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->search_button))) {
87       if (has_data && priv->has_search_results)
88         gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->child);
89       else
90         gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "no-results");
91     } else {
92       if (has_data)
93         gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->child);
94       else
95         gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "empty");
96     }
97     gtk_spinner_stop (GTK_SPINNER (priv->spinner));
98   }
99 
100   gtk_widget_set_sensitive (priv->clear_button, has_data && priv->can_clear);
101   gtk_widget_set_sensitive (priv->search_button, has_data);
102 }
103 
104 static void
on_back_button_clicked(GtkWidget * button,EphyDataView * self)105 on_back_button_clicked (GtkWidget    *button,
106                         EphyDataView *self)
107 {
108   g_signal_emit (self, signals[BACK_BUTTON_CLICKED], 0);
109 }
110 
111 static void
on_clear_button_clicked(EphyDataView * self)112 on_clear_button_clicked (EphyDataView *self)
113 {
114   g_signal_emit (self, signals[CLEAR_BUTTON_CLICKED], 0);
115 }
116 
117 static void
on_search_entry_changed(GtkSearchEntry * entry,EphyDataView * self)118 on_search_entry_changed (GtkSearchEntry *entry,
119                          EphyDataView   *self)
120 {
121   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
122   const char *text;
123 
124   text = gtk_entry_get_text (GTK_ENTRY (entry));
125   g_free (priv->search_text);
126   priv->search_text = g_strdup (text);
127 
128   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_SEARCH_TEXT]);
129 }
130 
131 gboolean
ephy_data_view_handle_event(EphyDataView * self,GdkEvent * event)132 ephy_data_view_handle_event (EphyDataView *self,
133                              GdkEvent     *event)
134 {
135   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
136   GdkEventKey *key = (GdkEventKey *)event;
137   gint result;
138 
139   /* Firstly, we check if the HdySearchBar can handle the event */
140   result = hdy_search_bar_handle_event (HDY_SEARCH_BAR (priv->search_bar), event);
141 
142   if (result == GDK_EVENT_STOP)
143     return result;
144 
145   /* Secondly, we check for the shortcuts implemented by the data views */
146   /* Ctrl + F */
147   if ((key->state & GDK_CONTROL_MASK) && key->keyval == GDK_KEY_f) {
148     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->search_button), TRUE);
149     return GDK_EVENT_STOP;
150   }
151 
152   /* Shift + Delete */
153   if ((key->state & GDK_SHIFT_MASK) && key->keyval == GDK_KEY_Delete) {
154     gtk_button_clicked (GTK_BUTTON (priv->clear_button));
155     return GDK_EVENT_STOP;
156   }
157 
158   /* Finally, we check for the Escape key */
159   if (key->keyval == GDK_KEY_Escape) {
160     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->search_button)))
161       /* If the user presses the Escape key and the search bar is hidden,
162        * we want the event to have the same effect as clicking the back button*/
163       gtk_button_clicked (GTK_BUTTON (priv->back_button));
164     else
165       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->search_button), FALSE);
166 
167     return GDK_EVENT_STOP;
168   }
169 
170   return GDK_EVENT_PROPAGATE;
171 }
172 
173 static void
ephy_data_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)174 ephy_data_view_set_property (GObject      *object,
175                              guint         prop_id,
176                              const GValue *value,
177                              GParamSpec   *pspec)
178 {
179   EphyDataView *self = EPHY_DATA_VIEW (object);
180   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
181 
182   switch (prop_id) {
183     case PROP_TITLE:
184       gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header_bar), g_value_get_string (value));
185       break;
186     case PROP_CLEAR_ACTION_NAME:
187       gtk_actionable_set_action_name (GTK_ACTIONABLE (priv->clear_button), g_value_get_string (value));
188       break;
189     case PROP_CLEAR_ACTION_TARGET:
190       gtk_actionable_set_action_target_value (GTK_ACTIONABLE (priv->clear_button), g_value_get_variant (value));
191       break;
192     case PROP_CLEAR_BUTTON_LABEL:
193       ephy_data_view_set_clear_button_label (self, g_value_get_string (value));
194       break;
195     case PROP_CLEAR_BUTTON_TOOLTIP:
196       ephy_data_view_set_clear_button_tooltip (self, g_value_get_string (value));
197       break;
198     case PROP_SEARCH_DESCRIPTION:
199       gtk_entry_set_placeholder_text (GTK_ENTRY (priv->search_entry), g_value_get_string (value));
200       atk_object_set_description (gtk_widget_get_accessible (GTK_WIDGET (self)), g_value_get_string (value));
201       break;
202     case PROP_EMPTY_TITLE:
203       hdy_status_page_set_title (HDY_STATUS_PAGE (priv->empty_page), g_value_get_string (value));
204       break;
205     case PROP_EMPTY_DESCRIPTION:
206       hdy_status_page_set_description (HDY_STATUS_PAGE (priv->empty_page), g_value_get_string (value));
207       break;
208     case PROP_IS_LOADING:
209       ephy_data_view_set_is_loading (self, g_value_get_boolean (value));
210       break;
211     case PROP_HAS_DATA:
212       ephy_data_view_set_has_data (self, g_value_get_boolean (value));
213       break;
214     case PROP_HAS_SEARCH_RESULTS:
215       ephy_data_view_set_has_search_results (self, g_value_get_boolean (value));
216       break;
217     case PROP_CAN_CLEAR:
218       ephy_data_view_set_can_clear (self, g_value_get_boolean (value));
219       break;
220     default:
221       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222       break;
223   }
224 }
225 
226 static void
ephy_data_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)227 ephy_data_view_get_property (GObject    *object,
228                              guint       prop_id,
229                              GValue     *value,
230                              GParamSpec *pspec)
231 {
232   EphyDataView *self = EPHY_DATA_VIEW (object);
233   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
234 
235   switch (prop_id) {
236     case PROP_TITLE:
237       g_value_set_string (value, gtk_header_bar_get_title (GTK_HEADER_BAR (priv->header_bar)));
238       break;
239     case PROP_CLEAR_ACTION_NAME:
240       g_value_set_string (value, gtk_actionable_get_action_name (GTK_ACTIONABLE (priv->clear_button)));
241       break;
242     case PROP_CLEAR_ACTION_TARGET:
243       g_value_set_variant (value, gtk_actionable_get_action_target_value (GTK_ACTIONABLE (priv->clear_button)));
244       break;
245     case PROP_CLEAR_BUTTON_LABEL:
246       g_value_set_string (value, ephy_data_view_get_clear_button_label (self));
247       break;
248     case PROP_CLEAR_BUTTON_TOOLTIP:
249       g_value_set_string (value, ephy_data_view_get_clear_button_tooltip (self));
250       break;
251     case PROP_SEARCH_DESCRIPTION:
252       g_value_set_string (value, gtk_entry_get_placeholder_text (GTK_ENTRY (priv->search_entry)));
253       break;
254     case PROP_EMPTY_TITLE:
255       g_value_set_string (value, hdy_status_page_get_title (HDY_STATUS_PAGE (priv->empty_page)));
256       break;
257     case PROP_EMPTY_DESCRIPTION:
258       g_value_set_string (value, hdy_status_page_get_description (HDY_STATUS_PAGE (priv->empty_page)));
259       break;
260     case PROP_SEARCH_TEXT:
261       g_value_set_string (value, ephy_data_view_get_search_text (self));
262       break;
263     case PROP_IS_LOADING:
264       g_value_set_boolean (value, ephy_data_view_get_is_loading (self));
265       break;
266     case PROP_HAS_DATA:
267       g_value_set_boolean (value, ephy_data_view_get_has_data (self));
268       break;
269     case PROP_HAS_SEARCH_RESULTS:
270       g_value_set_boolean (value, ephy_data_view_get_has_search_results (self));
271       break;
272     case PROP_CAN_CLEAR:
273       g_value_set_boolean (value, ephy_data_view_get_can_clear (self));
274       break;
275     default:
276       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
277       break;
278   }
279 }
280 
281 static void
ephy_data_view_finalize(GObject * object)282 ephy_data_view_finalize (GObject *object)
283 {
284   EphyDataView *self = EPHY_DATA_VIEW (object);
285   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
286 
287   g_free (priv->search_text);
288 
289   G_OBJECT_CLASS (ephy_data_view_parent_class)->finalize (object);
290 }
291 
292 static void
ephy_data_view_add(GtkContainer * container,GtkWidget * child)293 ephy_data_view_add (GtkContainer *container,
294                     GtkWidget    *child)
295 {
296   EphyDataView *self = EPHY_DATA_VIEW (container);
297   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
298 
299   if (!priv->box) {
300     GTK_CONTAINER_CLASS (ephy_data_view_parent_class)->add (container, child);
301     return;
302   }
303 
304   g_assert (!priv->child);
305 
306   priv->child = child;
307   gtk_container_add (GTK_CONTAINER (priv->stack), child);
308 
309   update (self);
310 }
311 
312 static void
ephy_data_view_class_init(EphyDataViewClass * klass)313 ephy_data_view_class_init (EphyDataViewClass *klass)
314 {
315   GObjectClass *object_class = G_OBJECT_CLASS (klass);
316   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
317   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
318 
319   object_class->set_property = ephy_data_view_set_property;
320   object_class->get_property = ephy_data_view_get_property;
321   object_class->finalize = ephy_data_view_finalize;
322 
323   container_class->add = ephy_data_view_add;
324 
325   obj_properties[PROP_TITLE] =
326     g_param_spec_string ("title",
327                          "Title",
328                          "The title used for the top HdyHeaderBar",
329                          NULL,
330                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
331 
332   obj_properties[PROP_CLEAR_ACTION_NAME] =
333     g_param_spec_string ("clear-action-name",
334                          "'Clear' action name",
335                          "The name of the action associated to the 'Clear' button",
336                          NULL,
337                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
338 
339   obj_properties[PROP_CLEAR_ACTION_TARGET] =
340     g_param_spec_variant ("clear-action-target",
341                           "'Clear' action target value",
342                           "The parameter for 'Clear' action invocations",
343                           G_VARIANT_TYPE_ANY, NULL,
344                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
345 
346   obj_properties[PROP_CLEAR_BUTTON_LABEL] =
347     g_param_spec_string ("clear-button-label",
348                          "'Clear' button label",
349                          "The label of the 'Clear' button",
350                          NULL,
351                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
352 
353   obj_properties[PROP_CLEAR_BUTTON_TOOLTIP] =
354     g_param_spec_string ("clear-button-tooltip",
355                          "'Clear' button tooltip",
356                          "The description of the 'Clear' action",
357                          NULL,
358                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
359 
360   obj_properties[PROP_SEARCH_DESCRIPTION] =
361     g_param_spec_string ("search-description",
362                          "'Search' description",
363                          "The description of the 'Search' action",
364                          NULL,
365                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
366 
367   obj_properties[PROP_EMPTY_TITLE] =
368     g_param_spec_string ("empty-title",
369                          "'Empty' title",
370                          "The title of the 'Empty' state page",
371                          NULL,
372                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
373 
374   obj_properties[PROP_EMPTY_DESCRIPTION] =
375     g_param_spec_string ("empty-description",
376                          "'Empty' description",
377                          "The description of the 'Empty' state page",
378                          NULL,
379                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
380 
381   obj_properties[PROP_SEARCH_TEXT] =
382     g_param_spec_string ("search-text",
383                          "Search text",
384                          "The text of the search entry",
385                          NULL,
386                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
387 
388   obj_properties[PROP_IS_LOADING] =
389     g_param_spec_boolean ("is-loading",
390                           "Is loading",
391                           "Whether the dialog is loading its data",
392                           FALSE,
393                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
394 
395   obj_properties[PROP_HAS_DATA] =
396     g_param_spec_boolean ("has-data",
397                           "Has data",
398                           "Whether the dialog has data",
399                           FALSE,
400                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
401 
402   obj_properties[PROP_HAS_SEARCH_RESULTS] =
403     g_param_spec_boolean ("has-search-results",
404                           "Has search results",
405                           "Whether the dialog has search results",
406                           FALSE,
407                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
408 
409   obj_properties[PROP_CAN_CLEAR] =
410     g_param_spec_boolean ("can-clear",
411                           "Can clear",
412                           "Whether the data can be cleared",
413                           FALSE,
414                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
415 
416   g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
417 
418   signals[BACK_BUTTON_CLICKED] =
419     g_signal_new ("back-button-clicked",
420                   EPHY_TYPE_DATA_VIEW,
421                   G_SIGNAL_RUN_LAST,
422                   0, NULL, NULL, NULL,
423                   G_TYPE_NONE, 0);
424 
425   /**
426    * EphyLocationEntry::user-changed:
427    * @entry: the object on which the signal is emitted
428    *
429    * Emitted when the user changes the contents of the internal #GtkEntry
430    *
431    */
432   signals[CLEAR_BUTTON_CLICKED] =
433     g_signal_new ("clear-button-clicked",
434                   G_OBJECT_CLASS_TYPE (klass),
435                   G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
436                   0, NULL, NULL, NULL,
437                   G_TYPE_NONE,
438                   0,
439                   G_TYPE_NONE);
440 
441   gtk_widget_class_set_template_from_resource (widget_class,
442                                                "/org/gnome/epiphany/gtk/data-view.ui");
443 
444   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, box);
445   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, header_bar);
446   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, back_button);
447   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, clear_button);
448   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, empty_page);
449   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, search_bar);
450   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, search_button);
451   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, search_entry);
452   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, spinner);
453   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, stack);
454 
455   gtk_widget_class_bind_template_callback (widget_class, on_back_button_clicked);
456   gtk_widget_class_bind_template_callback (widget_class, on_clear_button_clicked);
457   gtk_widget_class_bind_template_callback (widget_class, on_search_entry_changed);
458 }
459 
460 static void
ephy_data_view_init(EphyDataView * self)461 ephy_data_view_init (EphyDataView *self)
462 {
463   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
464 
465   gtk_widget_init_template (GTK_WIDGET (self));
466 
467   hdy_search_bar_connect_entry (HDY_SEARCH_BAR (priv->search_bar), GTK_ENTRY (priv->search_entry));
468 
469   hdy_status_page_set_icon_name (HDY_STATUS_PAGE (priv->empty_page),
470                                  APPLICATION_ID "-symbolic");
471 
472   update (self);
473 }
474 
475 const gchar *
ephy_data_view_get_clear_button_label(EphyDataView * self)476 ephy_data_view_get_clear_button_label (EphyDataView *self)
477 {
478   EphyDataViewPrivate *priv;
479 
480   g_assert (EPHY_IS_DATA_VIEW (self));
481 
482   priv = ephy_data_view_get_instance_private (self);
483 
484   return gtk_button_get_label (GTK_BUTTON (priv->clear_button));
485 }
486 
487 void
ephy_data_view_set_clear_button_label(EphyDataView * self,const gchar * label)488 ephy_data_view_set_clear_button_label (EphyDataView *self,
489                                        const gchar  *label)
490 {
491   EphyDataViewPrivate *priv;
492 
493   g_assert (EPHY_IS_DATA_VIEW (self));
494 
495   priv = ephy_data_view_get_instance_private (self);
496 
497   if (g_strcmp0 (gtk_button_get_label (GTK_BUTTON (priv->clear_button)), label) == 0)
498     return;
499 
500   gtk_button_set_label (GTK_BUTTON (priv->clear_button), label);
501 
502   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_CLEAR_BUTTON_LABEL]);
503 }
504 
505 const gchar *
ephy_data_view_get_clear_button_tooltip(EphyDataView * self)506 ephy_data_view_get_clear_button_tooltip (EphyDataView *self)
507 {
508   EphyDataViewPrivate *priv;
509 
510   g_assert (EPHY_IS_DATA_VIEW (self));
511 
512   priv = ephy_data_view_get_instance_private (self);
513 
514   return gtk_widget_get_tooltip_text (GTK_WIDGET (priv->clear_button));
515 }
516 
517 void
ephy_data_view_set_clear_button_tooltip(EphyDataView * self,const gchar * tooltip)518 ephy_data_view_set_clear_button_tooltip (EphyDataView *self,
519                                          const gchar  *tooltip)
520 {
521   EphyDataViewPrivate *priv;
522 
523   g_assert (EPHY_IS_DATA_VIEW (self));
524 
525   priv = ephy_data_view_get_instance_private (self);
526 
527   if (g_strcmp0 (gtk_widget_get_tooltip_text (GTK_WIDGET (priv->clear_button)), tooltip) == 0)
528     return;
529 
530   gtk_widget_set_tooltip_text (GTK_WIDGET (priv->clear_button), tooltip);
531 
532   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_CLEAR_BUTTON_TOOLTIP]);
533 }
534 
535 gboolean
ephy_data_view_get_is_loading(EphyDataView * self)536 ephy_data_view_get_is_loading (EphyDataView *self)
537 {
538   EphyDataViewPrivate *priv;
539 
540   g_assert (EPHY_IS_DATA_VIEW (self));
541 
542   priv = ephy_data_view_get_instance_private (self);
543 
544   return priv->is_loading;
545 }
546 
547 void
ephy_data_view_set_is_loading(EphyDataView * self,gboolean is_loading)548 ephy_data_view_set_is_loading (EphyDataView *self,
549                                gboolean      is_loading)
550 {
551   EphyDataViewPrivate *priv;
552 
553   g_assert (EPHY_IS_DATA_VIEW (self));
554 
555   priv = ephy_data_view_get_instance_private (self);
556   is_loading = !!is_loading;
557 
558   if (priv->is_loading == is_loading)
559     return;
560 
561   priv->is_loading = is_loading;
562 
563   update (self);
564 
565   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_IS_LOADING]);
566 }
567 
568 gboolean
ephy_data_view_get_has_data(EphyDataView * self)569 ephy_data_view_get_has_data (EphyDataView *self)
570 {
571   EphyDataViewPrivate *priv;
572 
573   g_assert (EPHY_IS_DATA_VIEW (self));
574 
575   priv = ephy_data_view_get_instance_private (self);
576 
577   return priv->has_data;
578 }
579 
580 void
ephy_data_view_set_has_data(EphyDataView * self,gboolean has_data)581 ephy_data_view_set_has_data (EphyDataView *self,
582                              gboolean      has_data)
583 {
584   EphyDataViewPrivate *priv;
585 
586   g_assert (EPHY_IS_DATA_VIEW (self));
587 
588   priv = ephy_data_view_get_instance_private (self);
589   has_data = !!has_data;
590 
591   if (priv->has_data == has_data)
592     return;
593 
594   priv->has_data = has_data;
595 
596   update (self);
597 
598   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_HAS_DATA]);
599 }
600 
601 gboolean
ephy_data_view_get_has_search_results(EphyDataView * self)602 ephy_data_view_get_has_search_results (EphyDataView *self)
603 {
604   EphyDataViewPrivate *priv;
605 
606   g_assert (EPHY_IS_DATA_VIEW (self));
607 
608   priv = ephy_data_view_get_instance_private (self);
609 
610   return priv->has_search_results;
611 }
612 
613 void
ephy_data_view_set_has_search_results(EphyDataView * self,gboolean has_search_results)614 ephy_data_view_set_has_search_results (EphyDataView *self,
615                                        gboolean      has_search_results)
616 {
617   EphyDataViewPrivate *priv;
618 
619   g_assert (EPHY_IS_DATA_VIEW (self));
620 
621   priv = ephy_data_view_get_instance_private (self);
622   has_search_results = !!has_search_results;
623 
624   if (priv->has_search_results == has_search_results)
625     return;
626 
627   priv->has_search_results = has_search_results;
628 
629   update (self);
630 
631   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_HAS_SEARCH_RESULTS]);
632 }
633 
634 gboolean
ephy_data_view_get_can_clear(EphyDataView * self)635 ephy_data_view_get_can_clear (EphyDataView *self)
636 {
637   EphyDataViewPrivate *priv;
638 
639   g_assert (EPHY_IS_DATA_VIEW (self));
640 
641   priv = ephy_data_view_get_instance_private (self);
642 
643   return priv->can_clear;
644 }
645 
646 void
ephy_data_view_set_can_clear(EphyDataView * self,gboolean can_clear)647 ephy_data_view_set_can_clear (EphyDataView *self,
648                               gboolean      can_clear)
649 {
650   EphyDataViewPrivate *priv;
651 
652   g_assert (EPHY_IS_DATA_VIEW (self));
653 
654   priv = ephy_data_view_get_instance_private (self);
655   can_clear = !!can_clear;
656 
657   if (priv->can_clear == can_clear)
658     return;
659 
660   priv->can_clear = can_clear;
661 
662   update (self);
663 
664   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_CAN_CLEAR]);
665 }
666 
667 const gchar *
ephy_data_view_get_search_text(EphyDataView * self)668 ephy_data_view_get_search_text (EphyDataView *self)
669 {
670   EphyDataViewPrivate *priv;
671 
672   g_assert (EPHY_IS_DATA_VIEW (self));
673 
674   priv = ephy_data_view_get_instance_private (self);
675 
676   return priv->search_text;
677 }
678