3
* Copyright (C) 2015 Christian Hergert <christian@hergert.me>
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#define G_LOG_DOMAIN "gb-search-display"
21
#include <glib/gi18n.h>
24
#include "gb-search-display.h"
25
#include "gb-search-display-group.h"
27
struct _GbSearchDisplay
29
GtkBox parent_instance;
31
IdeSearchContext *context;
33
GtkSizeGroup *size_group;
34
GbSearchDisplayGroup *last_group;
39
IdeSearchProvider *provider;
40
GbSearchDisplayGroup *group;
43
G_DEFINE_TYPE (GbSearchDisplay, gb_search_display, GTK_TYPE_BOX)
56
static GParamSpec *gParamSpecs [LAST_PROP];
57
static guint gSignals [LAST_SIGNAL];
60
provider_entry_destroy (gpointer data)
62
ProviderEntry *entry = data;
66
IDE_TRACE_MSG ("releasing %p", data);
68
ide_clear_weak_pointer (&entry->group);
69
g_clear_object (&entry->provider);
76
provider_entry_sort (gconstpointer ptra,
79
ProviderEntry **entrya = (ProviderEntry **)ptra;
80
ProviderEntry **entryb = (ProviderEntry **)ptrb;
84
a = ide_search_provider_get_priority ((IDE_SEARCH_PROVIDER ((*entrya)->provider)));
85
b = ide_search_provider_get_priority ((IDE_SEARCH_PROVIDER ((*entryb)->provider)));
91
gb_search_display_new (void)
93
return g_object_new (GB_TYPE_SEARCH_DISPLAY, NULL);
97
gb_search_display_real_result_activated (GbSearchDisplay *self,
98
IdeSearchResult *result)
100
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
101
g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
106
gb_search_display_result_activated (GbSearchDisplay *self,
107
IdeSearchResult *result,
108
GbSearchDisplayGroup *group)
110
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
111
g_return_if_fail (!result || IDE_IS_SEARCH_RESULT (result));
112
g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
114
g_signal_emit (self, gSignals [RESULT_ACTIVATED], 0, result);
118
gb_search_display_result_selected (GbSearchDisplay *self,
119
IdeSearchResult *result,
120
GbSearchDisplayGroup *group)
124
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
125
g_return_if_fail (!result || IDE_IS_SEARCH_RESULT (result));
126
g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
128
for (i = 0; i < self->providers->len; i++)
132
ptr = g_ptr_array_index (self->providers, i);
133
if ((ptr->group != NULL) && (ptr->group != group))
134
gb_search_display_group_unselect (ptr->group);
139
gb_search_display_keynav_failed (GbSearchDisplay *self,
140
GtkDirectionType dir,
141
GbSearchDisplayGroup *group)
147
g_return_val_if_fail (GB_IS_SEARCH_DISPLAY (self), FALSE);
148
g_return_val_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group), FALSE);
150
gtk_container_child_get (GTK_CONTAINER (self), GTK_WIDGET (group),
151
"position", &position,
154
if (dir == GTK_DIR_DOWN)
156
list = gtk_container_get_children (GTK_CONTAINER (self));
157
iter = g_list_nth (list, position + 1);
158
if (iter && (iter->data != self->last_group))
160
gb_search_display_group_unselect (group);
161
gb_search_display_group_focus_first (iter->data);
165
else if (dir == GTK_DIR_UP && position > 0)
167
list = gtk_container_get_children (GTK_CONTAINER (self));
168
iter = g_list_nth (list, position - 1);
171
gb_search_display_group_unselect (group);
172
gb_search_display_group_focus_last (iter->data);
181
gb_search_display_activate (GbSearchDisplay *self)
183
IdeSearchResult *result = NULL;
186
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
188
for (i = 0; !result && i < self->providers->len; i++)
192
ptr = g_ptr_array_index (self->providers, i);
193
if (ptr->group != NULL)
194
result = gb_search_display_group_get_first (ptr->group);
198
g_signal_emit (self, gSignals [RESULT_ACTIVATED], 0, result);
202
gb_search_display_add_provider (GbSearchDisplay *self,
203
IdeSearchProvider *provider)
205
ProviderEntry *entry;
208
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
209
g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
212
* Make sure we don't add an item twice. Probably can assert here, but
213
* warning will do for now.
215
for (i = 0; i < self->providers->len; i++)
219
ptr = g_ptr_array_index (self->providers, i);
221
if (ptr->provider == provider)
223
g_warning (_("Cannot add provider more than once."));
229
* Add the entry to our array and sort the array to determine our target
230
* widget packing position.
232
entry = g_new0 (ProviderEntry, 1);
233
entry->provider = g_object_ref (provider);
234
entry->group = g_object_new (GB_TYPE_SEARCH_DISPLAY_GROUP,
235
"size-group", self->size_group,
236
"provider", provider,
239
g_object_add_weak_pointer (G_OBJECT (entry->group), (gpointer *)&entry->group);
240
g_signal_connect_object (entry->group,
242
G_CALLBACK (gb_search_display_result_activated),
245
g_signal_connect_object (entry->group,
247
G_CALLBACK (gb_search_display_result_selected),
250
g_signal_connect_object (entry->group,
252
G_CALLBACK (gb_search_display_keynav_failed),
255
g_ptr_array_add (self->providers, entry);
256
g_ptr_array_sort (self->providers, provider_entry_sort);
259
* Find the location of the entry and use the index to pack the display
262
for (i = 0; i < self->providers->len; i++)
266
ptr = g_ptr_array_index (self->providers, i);
268
if (ptr->provider == provider)
270
gtk_container_add_with_properties (GTK_CONTAINER (self),
271
GTK_WIDGET (entry->group),
280
gb_search_display_remove_provider (GbSearchDisplay *self,
281
IdeSearchProvider *provider)
285
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
286
g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
288
for (i = 0; i < self->providers->len; i++)
292
ptr = g_ptr_array_index (self->providers, i);
294
if (ptr->provider == provider)
296
GbSearchDisplayGroup *group = ptr->group;
299
gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (group));
300
g_ptr_array_remove_index (self->providers, i);
305
g_warning (_("The provider could not be found."));
309
gb_search_display_result_added (GbSearchDisplay *self,
310
IdeSearchProvider *provider,
311
IdeSearchResult *result,
312
IdeSearchContext *context)
316
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
317
g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
318
g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
319
g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
321
for (i = 0; i < self->providers->len; i++)
325
ptr = g_ptr_array_index (self->providers, i);
327
if (ptr->provider == provider)
329
if (ptr->group != NULL)
331
gb_search_display_group_add_result (ptr->group, result);
332
gtk_widget_show (GTK_WIDGET (ptr->group));
340
gb_search_display_result_removed (GbSearchDisplay *self,
341
IdeSearchProvider *provider,
342
IdeSearchResult *result,
343
IdeSearchContext *context)
347
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
348
g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
349
g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
350
g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
352
for (i = 0; i < self->providers->len; i++)
356
ptr = g_ptr_array_index (self->providers, i);
358
if (ptr->provider == provider)
360
if (ptr->group != NULL)
361
gb_search_display_group_remove_result (ptr->group, result);
368
gb_search_display_count_set (GbSearchDisplay *self,
369
IdeSearchProvider *provider,
371
IdeSearchContext *context)
375
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
376
g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
377
g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
379
for (i = 0; i < self->providers->len; i++)
383
ptr = g_ptr_array_index (self->providers, i);
385
if (ptr->provider == provider)
387
if (ptr->group != NULL)
388
gb_search_display_group_set_count (ptr->group, count);
395
gb_search_display_connect_context (GbSearchDisplay *self,
396
IdeSearchContext *context)
398
const GList *providers;
401
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
402
g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
404
providers = ide_search_context_get_providers (context);
406
for (iter = providers; iter; iter = iter->next)
407
gb_search_display_add_provider (self, iter->data);
409
g_signal_connect_object (context,
411
G_CALLBACK (gb_search_display_result_added),
414
g_signal_connect_object (context,
416
G_CALLBACK (gb_search_display_result_removed),
419
g_signal_connect_object (context,
421
G_CALLBACK (gb_search_display_count_set),
427
gb_search_display_disconnect_context (GbSearchDisplay *self,
428
IdeSearchContext *context)
430
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
431
g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
433
g_signal_handlers_disconnect_by_func (context,
434
G_CALLBACK (gb_search_display_result_added),
437
while (self->providers->len)
441
ptr = g_ptr_array_index (self->providers,
442
self->providers->len - 1);
443
gb_search_display_remove_provider (self, ptr->provider);
448
gb_search_display_get_context (GbSearchDisplay *self)
450
g_return_val_if_fail (GB_IS_SEARCH_DISPLAY (self), NULL);
452
return self->context;
456
gb_search_display_set_context (GbSearchDisplay *self,
457
IdeSearchContext *context)
459
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
460
g_return_if_fail (!context || IDE_IS_SEARCH_CONTEXT (context));
462
if (self->context != context)
466
gb_search_display_disconnect_context (self, self->context);
467
g_clear_object (&self->context);
472
self->context = g_object_ref (context);
473
gb_search_display_connect_context (self, self->context);
476
g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_CONTEXT]);
481
gb_search_display_grab_focus (GtkWidget *widget)
483
GbSearchDisplay *self = (GbSearchDisplay *)widget;
485
g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
487
if (self->providers->len)
491
ptr = g_ptr_array_index (self->providers, 0);
492
gtk_widget_child_focus (GTK_WIDGET (ptr->group), GTK_DIR_DOWN);
497
gb_search_display_dispose (GObject *object)
499
GbSearchDisplay *self = (GbSearchDisplay *)object;
501
g_clear_pointer (&self->providers, g_ptr_array_unref);
502
g_clear_object (&self->context);
503
g_clear_object (&self->size_group);
505
G_OBJECT_CLASS (gb_search_display_parent_class)->dispose (object);
509
gb_search_display_get_property (GObject *object,
514
GbSearchDisplay *self = GB_SEARCH_DISPLAY (object);
519
g_value_set_object (value, gb_search_display_get_context (self));
523
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
528
gb_search_display_set_property (GObject *object,
533
GbSearchDisplay *self = GB_SEARCH_DISPLAY (object);
538
gb_search_display_set_context (self, g_value_get_object (value));
542
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
547
gb_search_display_class_init (GbSearchDisplayClass *klass)
549
GObjectClass *object_class = G_OBJECT_CLASS (klass);
550
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
552
widget_class->grab_focus = gb_search_display_grab_focus;
554
object_class->dispose = gb_search_display_dispose;
555
object_class->get_property = gb_search_display_get_property;
556
object_class->set_property = gb_search_display_set_property;
558
gParamSpecs [PROP_CONTEXT] =
559
g_param_spec_object ("context",
561
_("The active search context."),
562
IDE_TYPE_SEARCH_CONTEXT,
563
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
565
g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
567
gSignals [RESULT_ACTIVATED] =
568
g_signal_new_class_handler ("result-activated",
569
G_TYPE_FROM_CLASS (klass),
571
G_CALLBACK (gb_search_display_real_result_activated),
575
IDE_TYPE_SEARCH_RESULT);
579
gb_search_display_init (GbSearchDisplay *self)
581
self->providers = g_ptr_array_new_with_free_func (provider_entry_destroy);
583
self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
585
gtk_orientable_set_orientation (GTK_ORIENTABLE (self),
586
GTK_ORIENTATION_VERTICAL);
588
self->last_group = g_object_new (GB_TYPE_SEARCH_DISPLAY_GROUP,
589
"size-group", self->size_group,
593
gtk_container_add (GTK_CONTAINER (self),
594
GTK_WIDGET (self->last_group));