1 /* ide-terminal-popover.c
2  *
3  * Copyright 2019 Christian Hergert <chergert@redhat.com>
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  * SPDX-License-Identifier: GPL-3.0-or-later
19  */
20 
21 #define G_LOG_DOMAIN "ide-terminal-popover"
22 
23 #include "config.h"
24 
25 #include <dazzle.h>
26 #include <libide-foundry.h>
27 #include <libide-gui.h>
28 
29 #include "ide-terminal-popover.h"
30 #include "ide-terminal-popover-row.h"
31 
32 struct _IdeTerminalPopover
33 {
34   GtkPopover          parent_instance;
35 
36   DzlListModelFilter *filter;
37   gchar              *selected;
38 
39   /* Template widgets */
40   GtkSearchEntry     *search_entry;
41   GtkListBox         *list_box;
42 };
43 
44 G_DEFINE_FINAL_TYPE (IdeTerminalPopover, ide_terminal_popover, GTK_TYPE_POPOVER)
45 
46 static void
47 ide_terminal_popover_update_selected_cb (GtkWidget *widget,
48                                          gpointer   user_data)
49 {
50   IdeTerminalPopoverRow *row = (IdeTerminalPopoverRow *)widget;
51   IdeRuntime *runtime = user_data;
52   gboolean selected;
53 
54   g_assert (IDE_IS_TERMINAL_POPOVER_ROW (row));
55   g_assert (IDE_IS_RUNTIME (runtime));
56 
57   selected = runtime == ide_terminal_popover_row_get_runtime (row);
58   ide_terminal_popover_row_set_selected (row, selected);
59 }
60 
61 static void
62 ide_terminal_popover_row_activated_cb (IdeTerminalPopover    *self,
63                                        IdeTerminalPopoverRow *row,
64                                        GtkListBox            *list_box)
65 {
66   IdeRuntime *runtime;
67 
68   g_assert (IDE_IS_TERMINAL_POPOVER (self));
69   g_assert (IDE_IS_TERMINAL_POPOVER_ROW (row));
70   g_assert (GTK_IS_LIST_BOX (list_box));
71 
72   runtime = ide_terminal_popover_row_get_runtime (row);
73 
74   g_free (self->selected);
75   self->selected = g_strdup (ide_runtime_get_id (runtime));
76 
77   gtk_container_foreach (GTK_CONTAINER (self->list_box),
78                          ide_terminal_popover_update_selected_cb,
79                          runtime);
80 }
81 
82 static GtkWidget *
83 ide_terminal_popover_create_row_cb (gpointer item,
84                                     gpointer user_data)
85 {
86   IdeRuntime *runtime = item;
87   IdeTerminalPopover *self = user_data;
88   GtkWidget *row = ide_terminal_popover_row_new (runtime);
89 
90   if (ide_str_equal0 (ide_runtime_get_id (runtime), self->selected))
91     ide_terminal_popover_row_set_selected (IDE_TERMINAL_POPOVER_ROW (row), TRUE);
92   else
93     ide_terminal_popover_row_set_selected (IDE_TERMINAL_POPOVER_ROW (row), FALSE);
94 
95   return row;
96 }
97 
98 static gboolean
99 ide_terminal_popover_filter_func (GObject  *item,
100                                   gpointer  user_data)
101 {
102   DzlPatternSpec *spec = user_data;
103   IdeRuntime *runtime = IDE_RUNTIME (item);
104   const gchar *str;
105 
106   str = ide_runtime_get_id (runtime);
107   if (dzl_pattern_spec_match (spec, str))
108     return TRUE;
109 
110   str = ide_runtime_get_category (runtime);
111   if (dzl_pattern_spec_match (spec, str))
112     return TRUE;
113 
114   str = ide_runtime_get_display_name (runtime);
115   if (dzl_pattern_spec_match (spec, str))
116     return TRUE;
117 
118   return FALSE;
119 }
120 
121 static void
122 ide_terminal_popover_search_changed_cb (IdeTerminalPopover *self,
123                                         GtkSearchEntry     *entry)
124 {
125   const gchar *text;
126 
127   g_assert (IDE_IS_TERMINAL_POPOVER (self));
128   g_assert (GTK_IS_SEARCH_ENTRY (entry));
129 
130   if (self->filter == NULL)
131     return;
132 
133   text = gtk_entry_get_text (GTK_ENTRY (entry));
134   if (ide_str_empty0 (text))
135     text = NULL;
136 
137   dzl_list_model_filter_set_filter_func (self->filter,
138                                          ide_terminal_popover_filter_func,
139                                          dzl_pattern_spec_new (text),
140                                          (GDestroyNotify) dzl_pattern_spec_unref);
141 }
142 
143 static void
144 ide_terminal_popover_context_set_cb (GtkWidget  *widget,
145                                      IdeContext *context)
146 {
147   IdeTerminalPopover *self = (IdeTerminalPopover *)widget;
148   IdeRuntimeManager *runtime_manager;
149 
150   g_assert (IDE_IS_TERMINAL_POPOVER (self));
151   g_assert (!context || IDE_IS_CONTEXT (context));
152 
153   if (context == NULL)
154     return;
155 
156   runtime_manager = ide_runtime_manager_from_context (context);
157 
158   if (ide_context_has_project (context))
159     {
160       IdeConfigManager *config_manager;
161       IdeConfig *config;
162 
163       config_manager = ide_config_manager_from_context (context);
164       config = ide_config_manager_get_current (config_manager);
165 
166       if (config != NULL)
167         {
168           g_free (self->selected);
169           self->selected = g_strdup (ide_config_get_runtime_id (config));
170         }
171     }
172 
173   g_clear_object (&self->filter);
174   self->filter = dzl_list_model_filter_new (G_LIST_MODEL (runtime_manager));
175 
176   gtk_list_box_bind_model (self->list_box,
177                            G_LIST_MODEL (self->filter),
178                            ide_terminal_popover_create_row_cb,
179                            self, NULL);
180 }
181 
182 static void
183 ide_terminal_popover_class_init (IdeTerminalPopoverClass *klass)
184 {
185   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
186 
187   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-terminal/ui/ide-terminal-popover.ui");
188   gtk_widget_class_bind_template_child (widget_class, IdeTerminalPopover, list_box);
189   gtk_widget_class_bind_template_child (widget_class, IdeTerminalPopover, search_entry);
190 }
191 
192 static void
193 ide_terminal_popover_init (IdeTerminalPopover *self)
194 {
195   gtk_widget_init_template (GTK_WIDGET (self));
196 
197   self->selected = g_strdup ("host");
198 
199   ide_widget_set_context_handler (self, ide_terminal_popover_context_set_cb);
200 
201   g_signal_connect_object (self->search_entry,
202                            "changed",
203                            G_CALLBACK (ide_terminal_popover_search_changed_cb),
204                            self,
205                            G_CONNECT_SWAPPED);
206 
207   g_signal_connect_object (self->list_box,
208                            "row-activated",
209                            G_CALLBACK (ide_terminal_popover_row_activated_cb),
210                            self,
211                            G_CONNECT_SWAPPED);
212 }
213 
214 GtkWidget *
215 ide_terminal_popover_new (void)
216 {
217   return g_object_new (IDE_TYPE_TERMINAL_POPOVER, NULL);
218 }
219 
220 /**
221  * ide_terminal_popover_get_runtime:
222  * @self: a #IdeTerminalPopover
223  *
224  * Returns: (transfer none): an #IdeRuntime or %NULL
225  *
226  * Since: 3.32
227  */
228 IdeRuntime *
229 ide_terminal_popover_get_runtime (IdeTerminalPopover *self)
230 {
231   IdeRuntimeManager *runtime_manager;
232   IdeContext *context;
233 
234   g_return_val_if_fail (IDE_IS_TERMINAL_POPOVER (self), NULL);
235 
236   if (self->selected != NULL &&
237       (context = ide_widget_get_context (GTK_WIDGET (self))) &&
238       (runtime_manager = ide_runtime_manager_from_context (context)))
239     return ide_runtime_manager_get_runtime (runtime_manager, self->selected);
240 
241   return NULL;
242 }
243