1 /* gbp-buildui-config-view-addin.c
2 *
3 * Copyright 2018-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 "gbp-buildui-config-view-addin"
22
23 #include "config.h"
24
25 #include <glib/gi18n.h>
26 #include <libide-foundry.h>
27 #include <libide-gui.h>
28
29 #include "gbp-buildui-config-view-addin.h"
30 #include "gbp-buildui-runtime-categories.h"
31 #include "gbp-buildui-runtime-row.h"
32
33 struct _GbpBuilduiConfigViewAddin
34 {
35 GObject parent_instance;
36 };
37
38 static gboolean
treat_null_as_empty(GBinding * binding,const GValue * from_value,GValue * to_value,gpointer user_data)39 treat_null_as_empty (GBinding *binding,
40 const GValue *from_value,
41 GValue *to_value,
42 gpointer user_data)
43 {
44 const gchar *str = g_value_get_string (from_value);
45 g_value_set_string (to_value, str ?: "");
46 return TRUE;
47 }
48
49 static void
add_description_row(DzlPreferences * preferences,const gchar * page,const gchar * group,const gchar * title,const gchar * value,GtkWidget * value_widget)50 add_description_row (DzlPreferences *preferences,
51 const gchar *page,
52 const gchar *group,
53 const gchar *title,
54 const gchar *value,
55 GtkWidget *value_widget)
56 {
57 GtkWidget *widget;
58
59 g_assert (IDE_IS_MAIN_THREAD ());
60 g_assert (DZL_IS_PREFERENCES (preferences));
61
62 widget = g_object_new (GTK_TYPE_LABEL,
63 "xalign", 0.0f,
64 "label", title,
65 "visible", TRUE,
66 "margin-right", 12,
67 NULL);
68 dzl_gtk_widget_add_style_class (widget, "dim-label");
69
70 if (value_widget == NULL)
71 value_widget = g_object_new (GTK_TYPE_LABEL,
72 "hexpand", TRUE,
73 "label", value,
74 "xalign", 0.0f,
75 "visible", TRUE,
76 NULL);
77
78 dzl_preferences_add_table_row (preferences, page, group, widget, value_widget, NULL);
79 }
80
81 static GtkWidget *
create_stack_list_row(gpointer item,gpointer user_data)82 create_stack_list_row (gpointer item,
83 gpointer user_data)
84 {
85 IdeConfig *config = user_data;
86 GtkWidget *row;
87
88 g_assert (IDE_IS_MAIN_THREAD ());
89 g_assert (IDE_IS_CONFIG (config));
90
91 if (IDE_IS_RUNTIME (item))
92 return gbp_buildui_runtime_row_new (item, config);
93
94 row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
95 "visible", TRUE,
96 NULL);
97 g_object_set_data_full (G_OBJECT (row),
98 "ITEM",
99 g_object_ref (item),
100 g_object_unref);
101
102 if (GBP_IS_BUILDUI_RUNTIME_CATEGORIES (item))
103 {
104 const gchar *category = gbp_buildui_runtime_categories_get_name (item);
105 GtkWidget *label;
106
107 label = g_object_new (GTK_TYPE_LABEL,
108 "label", category,
109 "margin", 10,
110 "use-markup", TRUE,
111 "visible", TRUE,
112 "xalign", 0.0f,
113 NULL);
114 gtk_container_add (GTK_CONTAINER (row), label);
115 }
116 else if (DZL_IS_LIST_MODEL_FILTER (item))
117 {
118 const gchar *category = g_object_get_data (item, "CATEGORY");
119 GtkWidget *label;
120
121 label = g_object_new (GTK_TYPE_LABEL,
122 "label", category,
123 "margin", 10,
124 "use-markup", TRUE,
125 "visible", TRUE,
126 "xalign", 0.0f,
127 NULL);
128 gtk_container_add (GTK_CONTAINER (row), label);
129 }
130
131 return row;
132 }
133
134 static void
on_runtime_row_activated_cb(DzlStackList * stack_list,GtkListBoxRow * row,gpointer user_data)135 on_runtime_row_activated_cb (DzlStackList *stack_list,
136 GtkListBoxRow *row,
137 gpointer user_data)
138 {
139 IdeConfig *config = user_data;
140 gpointer item;
141
142 g_assert (IDE_IS_MAIN_THREAD ());
143 g_assert (DZL_IS_STACK_LIST (stack_list));
144 g_assert (GTK_IS_LIST_BOX_ROW (row));
145 g_assert (IDE_IS_CONFIG (config));
146
147 if (GBP_IS_BUILDUI_RUNTIME_ROW (row))
148 {
149 const gchar *id;
150
151 id = gbp_buildui_runtime_row_get_id (GBP_BUILDUI_RUNTIME_ROW (row));
152 ide_config_set_runtime_id (config, id);
153
154 {
155 GtkWidget *box;
156
157 if ((box = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_LIST_BOX)))
158 gtk_list_box_unselect_all (GTK_LIST_BOX (box));
159 }
160
161 return;
162 }
163
164 item = g_object_get_data (G_OBJECT (row), "ITEM");
165 g_assert (G_IS_LIST_MODEL (item) || IDE_IS_RUNTIME (item));
166
167 if (GBP_IS_BUILDUI_RUNTIME_CATEGORIES (item))
168 {
169 dzl_stack_list_push (stack_list,
170 create_stack_list_row (item, config),
171 G_LIST_MODEL (item),
172 create_stack_list_row,
173 g_object_ref (config),
174 g_object_unref);
175 }
176 else if (G_IS_LIST_MODEL (item))
177 {
178 dzl_stack_list_push (stack_list,
179 create_stack_list_row (item, config),
180 G_LIST_MODEL (item),
181 create_stack_list_row,
182 g_object_ref (config),
183 g_object_unref);
184 }
185 }
186
187 static GtkWidget *
create_runtime_box(IdeConfig * config,IdeRuntimeManager * runtime_manager)188 create_runtime_box (IdeConfig *config,
189 IdeRuntimeManager *runtime_manager)
190 {
191 g_autoptr(GbpBuilduiRuntimeCategories) filter = NULL;
192 DzlStackList *stack;
193 const gchar *category;
194 IdeRuntime *runtime;
195 GtkWidget *header;
196 GtkWidget *frame;
197
198 g_assert (IDE_IS_MAIN_THREAD ());
199 g_assert (IDE_IS_CONFIG (config));
200 g_assert (IDE_IS_RUNTIME_MANAGER (runtime_manager));
201
202 filter = gbp_buildui_runtime_categories_new (runtime_manager, NULL);
203
204 frame = g_object_new (GTK_TYPE_FRAME,
205 "visible", TRUE,
206 NULL);
207
208 header = g_object_new (GTK_TYPE_LABEL,
209 "label", _("All Runtimes"),
210 "margin", 10,
211 "visible", TRUE,
212 "xalign", 0.0f,
213 NULL);
214
215 stack = g_object_new (DZL_TYPE_STACK_LIST,
216 "visible", TRUE,
217 NULL);
218 dzl_stack_list_push (stack,
219 header,
220 G_LIST_MODEL (filter),
221 create_stack_list_row,
222 g_object_ref (config),
223 g_object_unref);
224 gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (stack));
225
226 g_signal_connect_object (stack,
227 "row-activated",
228 G_CALLBACK (on_runtime_row_activated_cb),
229 config,
230 0);
231
232 if ((runtime = ide_config_get_runtime (config)) &&
233 (category = ide_runtime_get_category (runtime)))
234 {
235 g_autoptr(GString) prefix = g_string_new (NULL);
236 g_auto(GStrv) parts = g_strsplit (category, "/", 0);
237
238 for (guint i = 0; parts[i]; i++)
239 {
240 g_autoptr(GListModel) model = NULL;
241
242 g_string_append (prefix, parts[i]);
243 if (parts[i+1])
244 g_string_append_c (prefix, '/');
245
246 model = gbp_buildui_runtime_categories_create_child_model (filter, prefix->str);
247
248 dzl_stack_list_push (stack,
249 create_stack_list_row (model, config),
250 model,
251 create_stack_list_row,
252 g_object_ref (config),
253 g_object_unref);
254 }
255 }
256
257 return GTK_WIDGET (frame);
258 }
259
260 static void
notify_toolchain_id(IdeConfig * config,GParamSpec * pspec,GtkImage * image)261 notify_toolchain_id (IdeConfig *config,
262 GParamSpec *pspec,
263 GtkImage *image)
264 {
265 const gchar *toolchain_id;
266 const gchar *current;
267
268 g_assert (IDE_IS_MAIN_THREAD ());
269 g_assert (IDE_IS_CONFIG (config));
270 g_assert (GTK_IS_IMAGE (image));
271
272 toolchain_id = ide_config_get_toolchain_id (config);
273 current = g_object_get_data (G_OBJECT (image), "TOOLCHAIN_ID");
274
275 gtk_widget_set_visible (GTK_WIDGET (image), ide_str_equal0 (toolchain_id, current));
276 }
277
278 static GtkWidget *
create_toolchain_row(gpointer item,gpointer user_data)279 create_toolchain_row (gpointer item,
280 gpointer user_data)
281 {
282 IdeToolchain *toolchain = item;
283 IdeConfig *config = user_data;
284 const gchar *toolchain_id;
285 GtkWidget *label;
286 GtkWidget *row;
287 GtkWidget *box;
288 GtkImage *image;
289
290 g_assert (IDE_IS_MAIN_THREAD ());
291 g_assert (IDE_IS_TOOLCHAIN (toolchain));
292 g_assert (IDE_IS_CONFIG (config));
293
294 toolchain_id = ide_toolchain_get_id (toolchain);
295
296 row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
297 "visible", TRUE,
298 NULL);
299 g_object_set_data_full (G_OBJECT (row), "TOOLCHAIN_ID", g_strdup (toolchain_id), g_free);
300
301 box = g_object_new (GTK_TYPE_BOX,
302 "spacing", 6,
303 "visible", TRUE,
304 NULL);
305 gtk_container_add (GTK_CONTAINER (row), box);
306
307 label = g_object_new (GTK_TYPE_LABEL,
308 "label", ide_toolchain_get_display_name (toolchain),
309 "visible", TRUE,
310 "xalign", 0.0f,
311 NULL);
312 gtk_container_add (GTK_CONTAINER (box), label);
313
314 image = g_object_new (GTK_TYPE_IMAGE,
315 "icon-name", "object-select-symbolic",
316 "halign", GTK_ALIGN_START,
317 "hexpand", TRUE,
318 NULL);
319 g_object_set_data_full (G_OBJECT (image), "TOOLCHAIN_ID", g_strdup (toolchain_id), g_free);
320 gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (image));
321
322 g_signal_connect_object (config,
323 "notify::toolchain-id",
324 G_CALLBACK (notify_toolchain_id),
325 image,
326 0);
327 notify_toolchain_id (config, NULL, image);
328
329 return row;
330 }
331
332 static void
on_toolchain_row_activated_cb(GtkListBox * list_box,GtkListBoxRow * row,IdeConfig * config)333 on_toolchain_row_activated_cb (GtkListBox *list_box,
334 GtkListBoxRow *row,
335 IdeConfig *config)
336 {
337 const gchar *toolchain_id;
338
339 g_assert (IDE_IS_MAIN_THREAD ());
340 g_assert (GTK_IS_LIST_BOX (list_box));
341 g_assert (GTK_IS_LIST_BOX_ROW (row));
342 g_assert (IDE_IS_CONFIG (config));
343
344 if ((toolchain_id = g_object_get_data (G_OBJECT (row), "TOOLCHAIN_ID")))
345 ide_config_set_toolchain_id (config, toolchain_id);
346
347 gtk_list_box_unselect_all (list_box);
348 }
349
350 static GtkWidget *
create_toolchain_box(IdeConfig * config,IdeToolchainManager * toolchain_manager)351 create_toolchain_box (IdeConfig *config,
352 IdeToolchainManager *toolchain_manager)
353 {
354 GtkScrolledWindow *scroller;
355 GtkListBox *list_box;
356
357 g_assert (IDE_IS_MAIN_THREAD ());
358 g_assert (IDE_IS_CONFIG (config));
359 g_assert (IDE_IS_TOOLCHAIN_MANAGER (toolchain_manager));
360
361 scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
362 "propagate-natural-height", TRUE,
363 "shadow-type", GTK_SHADOW_IN,
364 "visible", TRUE,
365 NULL);
366
367 list_box = g_object_new (GTK_TYPE_LIST_BOX,
368 "visible", TRUE,
369 NULL);
370 g_signal_connect_object (list_box,
371 "row-activated",
372 G_CALLBACK (on_toolchain_row_activated_cb),
373 config,
374 0);
375 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (list_box));
376
377 gtk_list_box_bind_model (list_box,
378 G_LIST_MODEL (toolchain_manager),
379 create_toolchain_row,
380 g_object_ref (config),
381 g_object_unref);
382
383 return GTK_WIDGET (scroller);
384 }
385
386 static void
gbp_buildui_config_view_addin_load(IdeConfigViewAddin * addin,DzlPreferences * preferences,IdeConfig * config)387 gbp_buildui_config_view_addin_load (IdeConfigViewAddin *addin,
388 DzlPreferences *preferences,
389 IdeConfig *config)
390 {
391 IdeToolchainManager *toolchain_manager;
392 IdeRuntimeManager *runtime_manager;
393 g_autoptr(GFile) workdir = NULL;
394 IdeBuildSystem *build_system;
395 IdeEnvironment *environ_;
396 IdeContext *context;
397 GtkWidget *box;
398 GtkWidget *entry;
399 static const struct {
400 const gchar *label;
401 const gchar *action;
402 const gchar *tooltip;
403 const gchar *style_class;
404 } actions[] = {
405 { N_("Make _Active"), "config-manager.current", N_("Select this configuration as the active configuration.") },
406 { N_("_Duplicate"), "config-manager.duplicate", N_("Duplicating the configuration allows making changes without modifying this configuration.") },
407 { N_("_Remove"), "config-manager.delete", N_("Removes the configuration and cannot be undone."), "destructive-action" },
408 };
409
410 g_assert (IDE_IS_MAIN_THREAD ());
411 g_assert (GBP_IS_BUILDUI_CONFIG_VIEW_ADDIN (addin));
412 g_assert (DZL_IS_PREFERENCES (preferences));
413 g_assert (IDE_IS_CONFIG (config));
414
415 /* Get manager objects */
416 context = ide_object_get_context (IDE_OBJECT (config));
417 runtime_manager = ide_runtime_manager_from_context (context);
418 toolchain_manager = ide_toolchain_manager_from_context (context);
419 build_system = ide_build_system_from_context (context);
420 workdir = ide_context_ref_workdir (context);
421
422 /* Add our pages */
423 dzl_preferences_add_page (preferences, "general", _("General"), 0);
424 dzl_preferences_add_page (preferences, "environ", _("Environment"), 10);
425
426 /* Add groups to pages */
427 dzl_preferences_add_list_group (preferences, "general", "general", _("Overview"), GTK_SELECTION_NONE, 0);
428 dzl_preferences_add_group (preferences, "general", "buttons", NULL, 0);
429 dzl_preferences_add_group (preferences, "environ", "build", _("Build Environment"), 0);
430 dzl_preferences_add_group (preferences, "environ", "runtime", _("Runtime Environment"), 0);
431
432 /* actions button box */
433 box = g_object_new (GTK_TYPE_BOX,
434 "homogeneous", TRUE,
435 "spacing", 12,
436 "visible", TRUE,
437 NULL);
438 for (guint i = 0; i < G_N_ELEMENTS (actions); i++)
439 {
440 GtkWidget *button;
441
442 button = g_object_new (GTK_TYPE_BUTTON,
443 "visible", TRUE,
444 "action-name", actions[i].action,
445 "action-target", g_variant_new_string (ide_config_get_id (config)),
446 "label", g_dgettext (GETTEXT_PACKAGE, actions[i].label),
447 "tooltip-text", g_dgettext (GETTEXT_PACKAGE, actions[i].tooltip),
448 "use-underline", TRUE,
449 NULL);
450 if (actions[i].style_class)
451 dzl_gtk_widget_add_style_class (button, actions[i].style_class);
452 gtk_container_add (GTK_CONTAINER (box), button);
453 }
454
455 /* Add description info */
456 add_description_row (preferences, "general", "general", _("Name"), ide_config_get_display_name (config), NULL);
457 add_description_row (preferences, "general", "general", _("Source Directory"), g_file_peek_path (workdir), NULL);
458 add_description_row (preferences, "general", "general", _("Build System"), ide_build_system_get_display_name (build_system), NULL);
459
460 entry = g_object_new (GTK_TYPE_ENTRY,
461 "visible", TRUE,
462 "hexpand", TRUE,
463 NULL);
464 g_object_bind_property_full (config, "prefix", entry, "text",
465 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
466 treat_null_as_empty, NULL, NULL, NULL);
467 add_description_row (preferences, "general", "general", _("Install Prefix"), NULL, entry);
468
469 entry = g_object_new (GTK_TYPE_ENTRY,
470 "visible", TRUE,
471 "hexpand", TRUE,
472 NULL);
473 g_object_bind_property_full (config, "config-opts", entry, "text",
474 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
475 treat_null_as_empty, NULL, NULL, NULL);
476 add_description_row (preferences, "general", "general", _("Configure Options"), NULL, entry);
477
478 dzl_preferences_add_custom (preferences, "general", "buttons", box, NULL, 5);
479
480 /* Setup runtime selection */
481 dzl_preferences_add_group (preferences, "general", "runtime", _("Application Runtime"), 10);
482 dzl_preferences_add_custom (preferences, "general", "runtime", create_runtime_box (config, runtime_manager), NULL, 10);
483
484 /* Setup toolchain selection */
485 dzl_preferences_add_group (preferences, "general", "toolchain", _("Build Toolchain"), 20);
486 dzl_preferences_add_custom (preferences, "general", "toolchain", create_toolchain_box (config, toolchain_manager), NULL, 10);
487
488 /* Add environment selector */
489 environ_ = ide_config_get_environment (config);
490 dzl_preferences_add_custom (preferences, "environ", "build",
491 g_object_new (GTK_TYPE_FRAME,
492 "visible", TRUE,
493 "child", g_object_new (IDE_TYPE_ENVIRONMENT_EDITOR,
494 "environment", environ_,
495 "visible", TRUE,
496 NULL),
497 NULL),
498 NULL, 0);
499
500 /* Add runtime environment selector */
501 environ_ = ide_config_get_runtime_environment (config);
502 dzl_preferences_add_custom (preferences, "environ", "runtime",
503 g_object_new (GTK_TYPE_FRAME,
504 "visible", TRUE,
505 "child", g_object_new (IDE_TYPE_ENVIRONMENT_EDITOR,
506 "environment", environ_,
507 "visible", TRUE,
508 NULL),
509 NULL),
510 NULL, 0);
511
512 }
513
514 static void
config_view_addin_iface_init(IdeConfigViewAddinInterface * iface)515 config_view_addin_iface_init (IdeConfigViewAddinInterface *iface)
516 {
517 iface->load = gbp_buildui_config_view_addin_load;
518 }
519
G_DEFINE_FINAL_TYPE_WITH_CODE(GbpBuilduiConfigViewAddin,gbp_buildui_config_view_addin,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (IDE_TYPE_CONFIG_VIEW_ADDIN,config_view_addin_iface_init))520 G_DEFINE_FINAL_TYPE_WITH_CODE (GbpBuilduiConfigViewAddin, gbp_buildui_config_view_addin, G_TYPE_OBJECT,
521 G_IMPLEMENT_INTERFACE (IDE_TYPE_CONFIG_VIEW_ADDIN, config_view_addin_iface_init))
522
523 static void
524 gbp_buildui_config_view_addin_class_init (GbpBuilduiConfigViewAddinClass *klass)
525 {
526 }
527
528 static void
gbp_buildui_config_view_addin_init(GbpBuilduiConfigViewAddin * self)529 gbp_buildui_config_view_addin_init (GbpBuilduiConfigViewAddin *self)
530 {
531 }
532