~ubuntu-branches/debian/stretch/gnome-builder/stretch

« back to all changes in this revision

Viewing changes to src/search/gb-search-display.c

  • Committer: Package Import Robot
  • Author(s): Andreas Henriksson
  • Date: 2015-10-11 12:38:45 UTC
  • Revision ID: package-import@ubuntu.com-20151011123845-a0hvkz01se0p1p5a
Tags: upstream-3.16.3
ImportĀ upstreamĀ versionĀ 3.16.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* gb-search-display.c
 
2
 *
 
3
 * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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/>.
 
17
 */
 
18
 
 
19
#define G_LOG_DOMAIN "gb-search-display"
 
20
 
 
21
#include <glib/gi18n.h>
 
22
#include <ide.h>
 
23
 
 
24
#include "gb-search-display.h"
 
25
#include "gb-search-display-group.h"
 
26
 
 
27
struct _GbSearchDisplay
 
28
{
 
29
  GtkBox                parent_instance;
 
30
 
 
31
  IdeSearchContext     *context;
 
32
  GPtrArray            *providers;
 
33
  GtkSizeGroup         *size_group;
 
34
  GbSearchDisplayGroup *last_group;
 
35
};
 
36
 
 
37
typedef struct
 
38
{
 
39
  IdeSearchProvider    *provider;
 
40
  GbSearchDisplayGroup *group;
 
41
} ProviderEntry;
 
42
 
 
43
G_DEFINE_TYPE (GbSearchDisplay, gb_search_display, GTK_TYPE_BOX)
 
44
 
 
45
enum {
 
46
  PROP_0,
 
47
  PROP_CONTEXT,
 
48
  LAST_PROP
 
49
};
 
50
 
 
51
enum {
 
52
  RESULT_ACTIVATED,
 
53
  LAST_SIGNAL
 
54
};
 
55
 
 
56
static GParamSpec *gParamSpecs [LAST_PROP];
 
57
static guint       gSignals [LAST_SIGNAL];
 
58
 
 
59
static void
 
60
provider_entry_destroy (gpointer data)
 
61
{
 
62
  ProviderEntry *entry = data;
 
63
 
 
64
  IDE_ENTRY;
 
65
 
 
66
  IDE_TRACE_MSG ("releasing %p", data);
 
67
 
 
68
  ide_clear_weak_pointer (&entry->group);
 
69
  g_clear_object (&entry->provider);
 
70
  g_free (entry);
 
71
 
 
72
  IDE_EXIT;
 
73
}
 
74
 
 
75
static gint
 
76
provider_entry_sort (gconstpointer ptra,
 
77
                     gconstpointer ptrb)
 
78
{
 
79
  ProviderEntry **entrya = (ProviderEntry **)ptra;
 
80
  ProviderEntry **entryb = (ProviderEntry **)ptrb;
 
81
  gint a;
 
82
  gint b;
 
83
 
 
84
  a = ide_search_provider_get_priority ((IDE_SEARCH_PROVIDER ((*entrya)->provider)));
 
85
  b = ide_search_provider_get_priority ((IDE_SEARCH_PROVIDER ((*entryb)->provider)));
 
86
 
 
87
  return a - b;
 
88
}
 
89
 
 
90
GtkWidget *
 
91
gb_search_display_new (void)
 
92
{
 
93
  return g_object_new (GB_TYPE_SEARCH_DISPLAY, NULL);
 
94
}
 
95
 
 
96
static void
 
97
gb_search_display_real_result_activated (GbSearchDisplay *self,
 
98
                                         IdeSearchResult *result)
 
99
{
 
100
  g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
 
101
  g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
 
102
 
 
103
}
 
104
 
 
105
static void
 
106
gb_search_display_result_activated (GbSearchDisplay      *self,
 
107
                                    IdeSearchResult      *result,
 
108
                                    GbSearchDisplayGroup *group)
 
109
{
 
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));
 
113
 
 
114
  g_signal_emit (self, gSignals [RESULT_ACTIVATED], 0, result);
 
115
}
 
116
 
 
117
static void
 
118
gb_search_display_result_selected (GbSearchDisplay      *self,
 
119
                                   IdeSearchResult      *result,
 
120
                                   GbSearchDisplayGroup *group)
 
121
{
 
122
  guint i;
 
123
 
 
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));
 
127
 
 
128
  for (i = 0; i < self->providers->len; i++)
 
129
    {
 
130
      ProviderEntry *ptr;
 
131
 
 
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);
 
135
    }
 
136
}
 
137
 
 
138
static gboolean
 
139
gb_search_display_keynav_failed (GbSearchDisplay      *self,
 
140
                                 GtkDirectionType      dir,
 
141
                                 GbSearchDisplayGroup *group)
 
142
{
 
143
  GList *list;
 
144
  GList *iter;
 
145
  gint position = -1;
 
146
 
 
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);
 
149
 
 
150
  gtk_container_child_get (GTK_CONTAINER (self), GTK_WIDGET (group),
 
151
                           "position", &position,
 
152
                           NULL);
 
153
 
 
154
  if (dir == GTK_DIR_DOWN)
 
155
    {
 
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))
 
159
        {
 
160
          gb_search_display_group_unselect (group);
 
161
          gb_search_display_group_focus_first (iter->data);
 
162
          return TRUE;
 
163
        }
 
164
    }
 
165
  else if (dir == GTK_DIR_UP && position > 0)
 
166
    {
 
167
      list = gtk_container_get_children (GTK_CONTAINER (self));
 
168
      iter = g_list_nth (list, position - 1);
 
169
      if (iter)
 
170
        {
 
171
          gb_search_display_group_unselect (group);
 
172
          gb_search_display_group_focus_last (iter->data);
 
173
          return TRUE;
 
174
        }
 
175
    }
 
176
 
 
177
  return FALSE;
 
178
}
 
179
 
 
180
void
 
181
gb_search_display_activate (GbSearchDisplay *self)
 
182
{
 
183
  IdeSearchResult *result = NULL;
 
184
  guint i;
 
185
 
 
186
  g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
 
187
 
 
188
  for (i = 0; !result && i < self->providers->len; i++)
 
189
    {
 
190
      ProviderEntry *ptr;
 
191
 
 
192
      ptr = g_ptr_array_index (self->providers, i);
 
193
      if (ptr->group != NULL)
 
194
        result = gb_search_display_group_get_first (ptr->group);
 
195
    }
 
196
 
 
197
  if (result)
 
198
    g_signal_emit (self, gSignals [RESULT_ACTIVATED], 0, result);
 
199
}
 
200
 
 
201
static void
 
202
gb_search_display_add_provider (GbSearchDisplay   *self,
 
203
                                IdeSearchProvider *provider)
 
204
{
 
205
  ProviderEntry *entry;
 
206
  guint i;
 
207
 
 
208
  g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
 
209
  g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
 
210
 
 
211
  /*
 
212
   * Make sure we don't add an item twice. Probably can assert here, but
 
213
   * warning will do for now.
 
214
   */
 
215
  for (i = 0; i < self->providers->len; i++)
 
216
    {
 
217
      ProviderEntry *ptr;
 
218
 
 
219
      ptr = g_ptr_array_index (self->providers, i);
 
220
 
 
221
      if (ptr->provider == provider)
 
222
        {
 
223
          g_warning (_("Cannot add provider more than once."));
 
224
          return;
 
225
        }
 
226
    }
 
227
 
 
228
  /*
 
229
   * Add the entry to our array and sort the array to determine our target
 
230
   * widget packing position.
 
231
   */
 
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,
 
237
                               "visible", FALSE,
 
238
                               NULL);
 
239
  g_object_add_weak_pointer (G_OBJECT (entry->group), (gpointer *)&entry->group);
 
240
  g_signal_connect_object (entry->group,
 
241
                           "result-activated",
 
242
                           G_CALLBACK (gb_search_display_result_activated),
 
243
                           self,
 
244
                           G_CONNECT_SWAPPED);
 
245
  g_signal_connect_object (entry->group,
 
246
                           "result-selected",
 
247
                           G_CALLBACK (gb_search_display_result_selected),
 
248
                           self,
 
249
                           G_CONNECT_SWAPPED);
 
250
  g_signal_connect_object (entry->group,
 
251
                           "keynav-failed",
 
252
                           G_CALLBACK (gb_search_display_keynav_failed),
 
253
                           self,
 
254
                           G_CONNECT_SWAPPED);
 
255
  g_ptr_array_add (self->providers, entry);
 
256
  g_ptr_array_sort (self->providers, provider_entry_sort);
 
257
 
 
258
  /*
 
259
   * Find the location of the entry and use the index to pack the display
 
260
   * group widget.
 
261
   */
 
262
  for (i = 0; i < self->providers->len; i++)
 
263
    {
 
264
      ProviderEntry *ptr;
 
265
 
 
266
      ptr = g_ptr_array_index (self->providers, i);
 
267
 
 
268
      if (ptr->provider == provider)
 
269
        {
 
270
          gtk_container_add_with_properties (GTK_CONTAINER (self),
 
271
                                             GTK_WIDGET (entry->group),
 
272
                                             "position", i,
 
273
                                             NULL);
 
274
          break;
 
275
        }
 
276
    }
 
277
}
 
278
 
 
279
static void
 
280
gb_search_display_remove_provider (GbSearchDisplay   *self,
 
281
                                   IdeSearchProvider *provider)
 
282
{
 
283
  guint i;
 
284
 
 
285
  g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
 
286
  g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
 
287
 
 
288
  for (i = 0; i < self->providers->len; i++)
 
289
    {
 
290
      ProviderEntry *ptr;
 
291
 
 
292
      ptr = g_ptr_array_index (self->providers, i);
 
293
 
 
294
      if (ptr->provider == provider)
 
295
        {
 
296
          GbSearchDisplayGroup *group = ptr->group;
 
297
 
 
298
          if (group)
 
299
            gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (group));
 
300
          g_ptr_array_remove_index (self->providers, i);
 
301
          return;
 
302
        }
 
303
    }
 
304
 
 
305
  g_warning (_("The provider could not be found."));
 
306
}
 
307
 
 
308
static void
 
309
gb_search_display_result_added (GbSearchDisplay   *self,
 
310
                                IdeSearchProvider *provider,
 
311
                                IdeSearchResult   *result,
 
312
                                IdeSearchContext  *context)
 
313
{
 
314
  guint i;
 
315
 
 
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));
 
320
 
 
321
  for (i = 0; i < self->providers->len; i++)
 
322
    {
 
323
      ProviderEntry *ptr;
 
324
 
 
325
      ptr = g_ptr_array_index (self->providers, i);
 
326
 
 
327
      if (ptr->provider == provider)
 
328
        {
 
329
          if (ptr->group != NULL)
 
330
            {
 
331
              gb_search_display_group_add_result (ptr->group, result);
 
332
              gtk_widget_show (GTK_WIDGET (ptr->group));
 
333
            }
 
334
          break;
 
335
        }
 
336
    }
 
337
}
 
338
 
 
339
static void
 
340
gb_search_display_result_removed (GbSearchDisplay   *self,
 
341
                                  IdeSearchProvider *provider,
 
342
                                  IdeSearchResult   *result,
 
343
                                  IdeSearchContext  *context)
 
344
{
 
345
  guint i;
 
346
 
 
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));
 
351
 
 
352
  for (i = 0; i < self->providers->len; i++)
 
353
    {
 
354
      ProviderEntry *ptr;
 
355
 
 
356
      ptr = g_ptr_array_index (self->providers, i);
 
357
 
 
358
      if (ptr->provider == provider)
 
359
        {
 
360
          if (ptr->group != NULL)
 
361
            gb_search_display_group_remove_result (ptr->group, result);
 
362
          break;
 
363
        }
 
364
    }
 
365
}
 
366
 
 
367
static void
 
368
gb_search_display_count_set (GbSearchDisplay   *self,
 
369
                             IdeSearchProvider *provider,
 
370
                             guint64            count,
 
371
                             IdeSearchContext  *context)
 
372
{
 
373
  guint i;
 
374
 
 
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));
 
378
 
 
379
  for (i = 0; i < self->providers->len; i++)
 
380
    {
 
381
      ProviderEntry *ptr;
 
382
 
 
383
      ptr = g_ptr_array_index (self->providers, i);
 
384
 
 
385
      if (ptr->provider == provider)
 
386
        {
 
387
          if (ptr->group != NULL)
 
388
            gb_search_display_group_set_count (ptr->group, count);
 
389
          break;
 
390
        }
 
391
    }
 
392
}
 
393
 
 
394
static void
 
395
gb_search_display_connect_context (GbSearchDisplay  *self,
 
396
                                   IdeSearchContext *context)
 
397
{
 
398
  const GList *providers;
 
399
  const GList *iter;
 
400
 
 
401
  g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
 
402
  g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
 
403
 
 
404
  providers = ide_search_context_get_providers (context);
 
405
 
 
406
  for (iter = providers; iter; iter = iter->next)
 
407
    gb_search_display_add_provider (self, iter->data);
 
408
 
 
409
  g_signal_connect_object (context,
 
410
                           "result-added",
 
411
                           G_CALLBACK (gb_search_display_result_added),
 
412
                           self,
 
413
                           G_CONNECT_SWAPPED);
 
414
  g_signal_connect_object (context,
 
415
                           "result-removed",
 
416
                           G_CALLBACK (gb_search_display_result_removed),
 
417
                           self,
 
418
                           G_CONNECT_SWAPPED);
 
419
  g_signal_connect_object (context,
 
420
                           "count-set",
 
421
                           G_CALLBACK (gb_search_display_count_set),
 
422
                           self,
 
423
                           G_CONNECT_SWAPPED);
 
424
}
 
425
 
 
426
static void
 
427
gb_search_display_disconnect_context (GbSearchDisplay  *self,
 
428
                                      IdeSearchContext *context)
 
429
{
 
430
  g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
 
431
  g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
 
432
 
 
433
  g_signal_handlers_disconnect_by_func (context,
 
434
                                        G_CALLBACK (gb_search_display_result_added),
 
435
                                        self);
 
436
 
 
437
  while (self->providers->len)
 
438
    {
 
439
      ProviderEntry *ptr;
 
440
 
 
441
      ptr = g_ptr_array_index (self->providers,
 
442
                               self->providers->len - 1);
 
443
      gb_search_display_remove_provider (self, ptr->provider);
 
444
    }
 
445
}
 
446
 
 
447
IdeSearchContext *
 
448
gb_search_display_get_context (GbSearchDisplay *self)
 
449
{
 
450
  g_return_val_if_fail (GB_IS_SEARCH_DISPLAY (self), NULL);
 
451
 
 
452
  return self->context;
 
453
}
 
454
 
 
455
void
 
456
gb_search_display_set_context (GbSearchDisplay  *self,
 
457
                               IdeSearchContext *context)
 
458
{
 
459
  g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
 
460
  g_return_if_fail (!context || IDE_IS_SEARCH_CONTEXT (context));
 
461
 
 
462
  if (self->context != context)
 
463
    {
 
464
      if (self->context)
 
465
        {
 
466
          gb_search_display_disconnect_context (self, self->context);
 
467
          g_clear_object (&self->context);
 
468
        }
 
469
 
 
470
      if (context)
 
471
        {
 
472
          self->context = g_object_ref (context);
 
473
          gb_search_display_connect_context (self, self->context);
 
474
        }
 
475
 
 
476
      g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_CONTEXT]);
 
477
    }
 
478
}
 
479
 
 
480
static void
 
481
gb_search_display_grab_focus (GtkWidget *widget)
 
482
{
 
483
  GbSearchDisplay *self = (GbSearchDisplay *)widget;
 
484
 
 
485
  g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
 
486
 
 
487
  if (self->providers->len)
 
488
    {
 
489
      ProviderEntry *ptr;
 
490
 
 
491
      ptr = g_ptr_array_index (self->providers, 0);
 
492
      gtk_widget_child_focus (GTK_WIDGET (ptr->group), GTK_DIR_DOWN);
 
493
    }
 
494
}
 
495
 
 
496
static void
 
497
gb_search_display_dispose (GObject *object)
 
498
{
 
499
  GbSearchDisplay *self = (GbSearchDisplay *)object;
 
500
 
 
501
  g_clear_pointer (&self->providers, g_ptr_array_unref);
 
502
  g_clear_object (&self->context);
 
503
  g_clear_object (&self->size_group);
 
504
 
 
505
  G_OBJECT_CLASS (gb_search_display_parent_class)->dispose (object);
 
506
}
 
507
 
 
508
static void
 
509
gb_search_display_get_property (GObject    *object,
 
510
                                guint       prop_id,
 
511
                                GValue     *value,
 
512
                                GParamSpec *pspec)
 
513
{
 
514
  GbSearchDisplay *self = GB_SEARCH_DISPLAY (object);
 
515
 
 
516
  switch (prop_id)
 
517
    {
 
518
    case PROP_CONTEXT:
 
519
      g_value_set_object (value, gb_search_display_get_context (self));
 
520
      break;
 
521
 
 
522
    default:
 
523
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
524
    }
 
525
}
 
526
 
 
527
static void
 
528
gb_search_display_set_property (GObject      *object,
 
529
                                guint         prop_id,
 
530
                                const GValue *value,
 
531
                                GParamSpec   *pspec)
 
532
{
 
533
  GbSearchDisplay *self = GB_SEARCH_DISPLAY (object);
 
534
 
 
535
  switch (prop_id)
 
536
    {
 
537
    case PROP_CONTEXT:
 
538
      gb_search_display_set_context (self, g_value_get_object (value));
 
539
      break;
 
540
 
 
541
    default:
 
542
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
543
    }
 
544
}
 
545
 
 
546
static void
 
547
gb_search_display_class_init (GbSearchDisplayClass *klass)
 
548
{
 
549
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
550
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
551
 
 
552
  widget_class->grab_focus = gb_search_display_grab_focus;
 
553
 
 
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;
 
557
 
 
558
  gParamSpecs [PROP_CONTEXT] =
 
559
    g_param_spec_object ("context",
 
560
                         _("Context"),
 
561
                         _("The active search context."),
 
562
                         IDE_TYPE_SEARCH_CONTEXT,
 
563
                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
564
 
 
565
  g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
 
566
 
 
567
  gSignals [RESULT_ACTIVATED] =
 
568
    g_signal_new_class_handler ("result-activated",
 
569
                                G_TYPE_FROM_CLASS (klass),
 
570
                                G_SIGNAL_RUN_LAST,
 
571
                                G_CALLBACK (gb_search_display_real_result_activated),
 
572
                                NULL, NULL, NULL,
 
573
                                G_TYPE_NONE,
 
574
                                1,
 
575
                                IDE_TYPE_SEARCH_RESULT);
 
576
}
 
577
 
 
578
static void
 
579
gb_search_display_init (GbSearchDisplay *self)
 
580
{
 
581
  self->providers = g_ptr_array_new_with_free_func (provider_entry_destroy);
 
582
 
 
583
  self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
 
584
 
 
585
  gtk_orientable_set_orientation (GTK_ORIENTABLE (self),
 
586
                                  GTK_ORIENTATION_VERTICAL);
 
587
 
 
588
  self->last_group = g_object_new (GB_TYPE_SEARCH_DISPLAY_GROUP,
 
589
                                         "size-group", self->size_group,
 
590
                                         "visible", TRUE,
 
591
                                         "vexpand", TRUE,
 
592
                                         NULL);
 
593
  gtk_container_add (GTK_CONTAINER (self),
 
594
                     GTK_WIDGET (self->last_group));
 
595
}