1 /*-
2  * Copyright (c) 2008 Jannis Pohlmann <jannis@xfce.org>
3  * Copyright (c) 2012 Guido Berhoerster <guido+xfce@berhoerster.name>
4  * Copyright (c) 2020 - 2021 Rozhuk Ivan <rozhuk.im@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 
23 #include <sys/param.h>
24 #include <sys/types.h>
25 #include <inttypes.h>
26 
27 #include "gtk-mixer.h"
28 
29 
30 static void
gtk_mixer_container_destroy(GtkWidget * container __unused,gpointer user_data)31 gtk_mixer_container_destroy(GtkWidget *container __unused, gpointer user_data) {
32 	GHashTable *widgets = user_data;
33 
34 	g_hash_table_remove_all(widgets);
35 	g_hash_table_unref(widgets);
36 }
37 
38 GtkWidget *
gtk_mixer_container_create(void)39 gtk_mixer_container_create(void) {
40 	GtkWidget *container;
41 	GHashTable *widgets;
42 
43 	container = gtk_notebook_new();
44 	gtk_notebook_set_show_border(GTK_NOTEBOOK(container), TRUE);
45 	widgets = g_hash_table_new_full(g_str_hash,
46 	    g_str_equal, g_free, NULL);
47 	g_object_set_data(G_OBJECT(container),
48 	    "__gtk_mixer_container_widgets", widgets);
49 	g_signal_connect(container, "destroy",
50 	    G_CALLBACK(gtk_mixer_container_destroy), widgets);
51 
52 	return (container);
53 }
54 
55 static void
gtk_mixer_container_create_contents(GtkWidget * container,GHashTable * widgets,gmp_dev_p dev)56 gtk_mixer_container_create_contents(GtkWidget *container,
57     GHashTable *widgets, gmp_dev_p dev) {
58 	gmp_dev_line_p line;
59 	const gchar *titles[4] = { N_("_Playback"), N_("C_apture"),
60 		N_("S_witches"), N_("_Options") };
61 	GtkWidget *line_widget, *line_label_widget;
62 	GtkWidget *labels[4], *scrollwins[4], *views[4];
63 	GtkWidget *last_separator[4] = { NULL, NULL, NULL, NULL };
64 	GtkWidget *vbox, *label1, *label2, *label3;
65 	const char *line_label;
66 	gint num_children[4] = { 0, 0, 0, 0 };
67 	gboolean no_controls_visible = TRUE;
68 
69 	/* Create widgets for all four tabs. */
70 	for (size_t i = 0; i < 4; i ++) {
71 		labels[i] = gtk_label_new_with_mnemonic(_(titles[i]));
72 		scrollwins[i] = gtk_scrolled_window_new(NULL, NULL);
73 		gtk_scrolled_window_set_shadow_type(
74 		    GTK_SCROLLED_WINDOW(scrollwins[i]), GTK_SHADOW_IN);
75 		gtk_container_set_border_width(GTK_CONTAINER(scrollwins[i]),
76 		    BORDER_WIDTH);
77 		gtk_scrolled_window_set_policy(
78 		    GTK_SCROLLED_WINDOW(scrollwins[i]),
79 		    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
80 
81 		views[i] = gtk_grid_new();
82 		g_object_set(G_OBJECT(views[i]),
83 		    "row-spacing", BORDER_WIDTH,
84 		    "column-spacing", (BORDER_WIDTH * 2),
85 		    "border-width", BORDER_WIDTH, NULL);
86 		gtk_container_add(GTK_CONTAINER(scrollwins[i]), views[i]);
87 		gtk_viewport_set_shadow_type(GTK_VIEWPORT(
88 		    gtk_bin_get_child(GTK_BIN(scrollwins[i]))),
89 		    GTK_SHADOW_NONE);
90 		gtk_widget_show(views[i]);
91 		gtk_widget_show(scrollwins[i]);
92 	}
93 
94 	/* Create controls for all mixer lines. */
95 	//preferences = gtk_mixer_preferences_get();
96 	for (size_t i = 0; NULL != dev && i < dev->lines_count; i ++) {
97 		line = &dev->lines[i];
98 		line_label = line->display_name;
99 
100 		//if (!gtk_mixer_preferences_get_control_visible(
101 		//	preferences, line_label))
102 		//	continue;
103 
104 		size_t idx = ((0 == line->is_capture) ? 0 : 1);
105 		/* Create a regular volume control for this line. */
106 		line_label_widget = gtk_label_new(line_label);
107 		gtk_grid_attach(GTK_GRID(views[idx]),
108 		    line_label_widget, num_children[idx], 0, 1, 1);
109 		gtk_widget_show(line_label_widget);
110 		line_widget = gtk_mixer_line_create(dev, line);
111 		g_object_set(G_OBJECT(line_widget),
112 		    "valign", GTK_ALIGN_FILL,
113 		    "vexpand", TRUE, NULL);
114 		gtk_grid_attach(GTK_GRID(views[idx]),
115 		    line_widget, num_children[idx], 1, 1, 1);
116 		gtk_widget_show(line_widget);
117 		num_children[idx]++;
118 
119 		/* Append a separator. The last one will be
120 		 * destroyed later. */
121 		last_separator[idx] = gtk_separator_new(
122 		    GTK_ORIENTATION_VERTICAL);
123 		gtk_grid_attach(GTK_GRID(views[idx]),
124 		    last_separator[idx], num_children[idx], 0, 1, 2);
125 		gtk_widget_show(last_separator[idx]);
126 		num_children[idx]++;
127 
128 		/* Add the line to the hash table. */
129 		g_hash_table_insert(widgets,
130 		    g_strdup(line_label), line_widget);
131 
132 #if 0
133 		case XFCE_MIXER_TRACK_TYPE_SWITCH:
134 			line_widget = gtk_mixer_switch_new(
135 			    container->card, line);
136 			g_object_set(G_OBJECT(line_widget),
137 			    "halign", GTK_ALIGN_FILL,
138 			    "hexpand", TRUE, NULL);
139 			gtk_grid_attach(GTK_GRID(views[2]),
140 			    line_widget, 0, num_children[2], 1, 1);
141 			gtk_widget_show(line_widget);
142 
143 			num_children[2]++;
144 
145 			/* Add the line to the hash table. */
146 			g_hash_table_insert(container->widgets,
147 			    g_strdup(line_label), line_widget);
148 			break;
149 
150 		case XFCE_MIXER_TRACK_TYPE_OPTIONS:
151 			snprintf(option_line_label, sizeof(option_line_label),
152 			    "%s:", line_label);
153 			line_label_widget = gtk_label_new(
154 			    option_line_label);
155 			g_object_set(G_OBJECT(line_label_widget),
156 			    "halign", GTK_ALIGN_FILL, NULL);
157 			gtk_grid_attach(GTK_GRID(views[3]),
158 			    line_label_widget, 0, num_children[3], 1, 1);
159 			gtk_widget_show(line_label_widget);
160 
161 			line_widget = gtk_mixer_option_new(
162 			    container->card, line);
163 			g_object_set(G_OBJECT(line_widget),
164 			    "halign", GTK_ALIGN_FILL,
165 			    "hexpand", TRUE, NULL);
166 			gtk_grid_attach(GTK_GRID(views[3]),
167 			    line_widget, 1, num_children[3], 1, 1);
168 			gtk_widget_show(line_widget);
169 
170 			num_children[3]++;
171 
172 			/* Add the line to the hash table. */
173 			g_hash_table_insert(container->widgets,
174 			    g_strdup(line_label), line_widget);
175 			break;
176 #endif
177 	}
178 
179 	/* Append tab or destroy all its widgets - depending on the
180 	 * contents of each tab. */
181 	for (gint i = 0; i < 4; i ++) {
182 		/* Destroy the last separator in the tab. */
183 		if (last_separator[i] != NULL) {
184 			gtk_widget_destroy(last_separator[i]);
185 		}
186 
187 		gtk_notebook_append_page(GTK_NOTEBOOK(container),
188 		    scrollwins[i], labels[i]);
189 
190 		/* Hide tabs with no visible controls. */
191 		if (num_children[i] > 0) {
192 			no_controls_visible = FALSE;
193 		} else {
194 			gtk_widget_hide(gtk_notebook_get_nth_page(
195 			    GTK_NOTEBOOK(container), i));
196 		}
197 	}
198 	/* Show informational message if no controls are visible. */
199 	if (no_controls_visible) {
200 		label1 = gtk_label_new(_("No controls visible"));
201 		gtk_widget_show(label1);
202 
203 		vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
204 		g_object_set(G_OBJECT(vbox),
205 		    "halign", GTK_ALIGN_CENTER,
206 		    "hexpand", TRUE,
207 		    "valign", GTK_ALIGN_CENTER,
208 		    "vexpand", TRUE,
209 		    "border-width", BORDER_WIDTH, NULL);
210 		gtk_widget_show(vbox);
211 
212 		label2 = gtk_label_new(NULL);
213 		gtk_label_set_markup(GTK_LABEL(label2),
214 		    _("<span weight=\"bold\" size=\"larger\">No controls visible</span>"));
215 		g_object_set(G_OBJECT(label2),
216 		    "max-width-chars", 80,
217 		    "xalign", 0.0,
218 		    "wrap", TRUE, NULL);
219 		gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, TRUE, 0);
220 		gtk_widget_show(label2);
221 
222 		label3 = gtk_label_new(NULL);
223 		gtk_label_set_markup(GTK_LABEL(label3),
224 		    _("In order to toggle the visibility of mixer controls, open the <b>\"Select Controls\"</b> dialog."));
225 		g_object_set(G_OBJECT(label3),
226 		    "max-width-chars", 80,
227 		    "xalign", 0.0,
228 		    "wrap", TRUE, NULL);
229 		gtk_box_pack_start(GTK_BOX(vbox), label3, FALSE, TRUE, 0);
230 		gtk_widget_show(label3);
231 
232 		gtk_notebook_append_page(GTK_NOTEBOOK(container), vbox,
233 		    label1);
234 	}
235 }
236 
237 void
gtk_mixer_container_dev_set(GtkWidget * container,gmp_dev_p dev)238 gtk_mixer_container_dev_set(GtkWidget *container, gmp_dev_p dev) {
239 	gint current_tab, i;
240 	GHashTable *widgets = g_object_get_data(G_OBJECT(container),
241 	    "__gtk_mixer_container_widgets");
242 
243 	g_hash_table_remove_all(widgets);
244 
245 	/* Remember active tab */
246 	current_tab = gtk_notebook_get_current_page(GTK_NOTEBOOK(container));
247 
248 	/* Destroy all tabs */
249 	for (i = gtk_notebook_get_n_pages(GTK_NOTEBOOK(container)); i >= 0; i --) {
250 		gtk_notebook_remove_page(GTK_NOTEBOOK(container), i);
251 	}
252 
253 	/* Re-create contents */
254 	gtk_mixer_container_create_contents(container, widgets, dev);
255 
256 	/* Restore previously active tab if possible */
257 	if (current_tab > 0 && current_tab < 4) {
258 		gtk_notebook_set_current_page(GTK_NOTEBOOK(container),
259 		    current_tab);
260 	}
261 }
262 
263 void
gtk_mixer_container_update(GtkWidget * container)264 gtk_mixer_container_update(GtkWidget *container) {
265 	GtkWidget *line_widget;
266 	GHashTable *widgets = g_object_get_data(G_OBJECT(container),
267 	    "__gtk_mixer_container_widgets");
268 	GHashTableIter iter;
269 
270 	if (NULL == container || NULL == widgets)
271 		return;
272 
273 	g_hash_table_iter_init(&iter, widgets);
274 	while (g_hash_table_iter_next(&iter, NULL, (void**)&line_widget)) {
275 		gtk_mixer_line_update(line_widget);
276 	}
277 }
278