1 /* LASH Control Panel
2  * Copyright (C) 2006 Dave Robillard <drobilla@connect.carleton.ca>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include <string.h>
20 #include <errno.h>
21 #include <unistd.h>
22 
23 #include <lash/lash.h>
24 #include <gtk/gtk.h>
25 
26 #include "panel.h"
27 #include "project.h"
28 #include "config.h"
29 
30 #define WINDOW_TITLE "LASH Control Panel"
31 
32 void quit_cb(GtkButton * button, void *data);
33 
34 project_t *
get_project(panel_t * panel,const char * const name)35 get_project(panel_t * panel, const char *const name)
36 {
37 	project_t *project = NULL;
38 	GtkTreeModel *tree_model = GTK_TREE_MODEL(panel->projects);
39 	GtkTreeIter iter;
40 
41 	if (gtk_tree_model_get_iter_first(tree_model, &iter))
42 		do {
43 			gtk_tree_model_get(tree_model, &iter, PROJECT_PROJECT_COLUMN,
44 							   &project, -1);
45 
46 			if (!strcmp(name, project->name))
47 				return project;
48 
49 		} while (gtk_tree_model_iter_next(tree_model, &iter));
50 
51 	fprintf(stderr, "Error: Unable to find project '%s'!", name);
52 	return NULL;
53 }
54 
55 void
error_dialog(panel_t * client,const char * message)56 error_dialog(panel_t * client, const char *message)
57 {
58 	GtkWidget *dialog;
59 	gint response;
60 
61 	dialog =
62 		gtk_message_dialog_new(GTK_WINDOW(client->window),
63 							   GTK_DIALOG_DESTROY_WITH_PARENT |
64 							   GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
65 							   GTK_BUTTONS_OK, "%s", message);
66 
67 	response = gtk_dialog_run(GTK_DIALOG(dialog));
68 	gtk_widget_destroy(dialog);
69 }
70 
71 
72 void
about_cb(GtkButton * button,void * client)73 about_cb(GtkButton* button, void* client)
74 {
75 	/*panel_t* panel = (panel_t*)client;*/
76 	GtkWidget *dialog;
77 	gint response;
78 
79 	dialog = gtk_about_dialog_new();
80 	gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), "LASH Panel");
81 	gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), PACKAGE_VERSION);
82 	gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog),
83 		"Copyright (C) 2006 Dave Robillard");
84 	gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog),
85 		"http://www.nongnu.org/lash");
86 
87 
88 
89 	response = gtk_dialog_run(GTK_DIALOG(dialog));
90 	gtk_widget_destroy(dialog);
91 }
92 
93 
94 void
server_disconnect(panel_t * panel)95 server_disconnect(panel_t * panel)
96 {
97 	error_dialog(panel, "The server disconnected!");
98 
99 	exit(0);
100 }
101 
102 void
event_project_add(panel_t * panel,lash_event_t * event)103 event_project_add(panel_t * panel, lash_event_t * event)
104 {
105 	project_t *project = NULL;
106 	const char *name = lash_event_get_string(event);
107 	GtkTreeIter iter;
108 
109 	printf("Add project: %s\n", name);
110 
111 	if (panel->num_projects == 0)
112 		gtk_label_set_text(GTK_LABEL(panel->no_projects_label), "");
113 
114 	gtk_list_store_append(panel->projects, &iter);
115 
116 	project = project_create(panel->lash_client, name);
117 
118 	gtk_list_store_set(panel->projects, &iter,
119 					   PROJECT_PROJECT_COLUMN, project, -1);
120 
121 	project->page_number =
122 		gtk_notebook_append_page(GTK_NOTEBOOK(panel->project_notebook),
123 								 project->box, project->tab_label);
124 
125 	panel->num_projects++;
126 }
127 
128 void
event_project_remove(panel_t * panel,lash_event_t * event)129 event_project_remove(panel_t * panel, lash_event_t * event)
130 {
131 	const char *name = lash_event_get_string(event);
132 	project_t *project = get_project(panel, name);
133 	GtkTreeModel *tree_model = GTK_TREE_MODEL(panel->projects);
134 	GtkTreeIter iter;
135 
136 	printf("Remove project: %s\n", name);
137 
138 	if (panel->num_projects == 1)
139 		gtk_label_set_text(GTK_LABEL(panel->no_projects_label),
140 			"(No projects open.  Start a LASH client, or load a project from the File menu)");
141 
142 	if (project != NULL)
143 		if (gtk_tree_model_get_iter_first(tree_model, &iter))
144 			do {
145 				gtk_tree_model_get(tree_model, &iter, PROJECT_PROJECT_COLUMN,
146 								   &project, -1);
147 
148 				if (!strcmp(name, project->name)) {
149 					gtk_list_store_remove(panel->projects, &iter);
150 					gtk_notebook_remove_page(GTK_NOTEBOOK
151 											 (panel->project_notebook),
152 											 project->page_number);
153 					project_destroy(project);
154 					break;
155 				}
156 			} while (gtk_tree_model_iter_next(tree_model, &iter));
157 
158 	panel->num_projects--;
159 }
160 
161 void
event_project_dir(panel_t * panel,lash_event_t * event)162 event_project_dir(panel_t * panel, lash_event_t * event)
163 {
164 	const char *name = lash_event_get_project(event);
165 	const char *dir = lash_event_get_string(event);
166 	project_t *project = get_project(panel, name);
167 
168 	printf("Change project dir: %s = %s\n", name, dir);
169 
170 	if (project != NULL) {
171 		project_set_dir(project, dir);
172 	}
173 }
174 
175 void
event_project_name(panel_t * panel,lash_event_t * event)176 event_project_name(panel_t * panel, lash_event_t * event)
177 {
178 	const char *old_name = lash_event_get_project(event);
179 	const char *new_name = lash_event_get_string(event);
180 	project_t *project = get_project(panel, old_name);
181 
182 	printf("Change project name: %s = %s\n", old_name, new_name);
183 
184 	if (project != NULL)
185 		project_set_name(project, new_name);
186 }
187 
188 void
event_client_add(panel_t * panel,lash_event_t * event)189 event_client_add(panel_t * panel, lash_event_t * event)
190 {
191 	const char *project_name = lash_event_get_project(event);
192 	project_t *project = get_project(panel, project_name);
193 	char *client_id_str = malloc(37);
194 	char *search_id_str = NULL;
195 	uuid_t client_id;
196 	GtkTreeIter iter;
197 	GtkTreeModel *tree_model;
198 	int client_exists = FALSE;
199 
200 	lash_event_get_client_id(event, client_id);
201 	uuid_unparse(client_id, client_id_str);
202 
203 	printf("Add client (%s): %s\n", project_name, client_id_str);
204 
205 	if (client_id != NULL && project != NULL) {
206 		tree_model = GTK_TREE_MODEL(project->clients);
207 
208 		if (gtk_tree_model_get_iter_first(tree_model, &iter)) {
209 			do {
210 				gtk_tree_model_get(tree_model, &iter, CLIENT_ID_COLUMN, &search_id_str, -1);
211 
212 				if (!strcmp(search_id_str, client_id_str)) {
213 					client_exists = TRUE;
214 					break;
215 				}
216 			} while (gtk_tree_model_iter_next(tree_model, &iter));
217 		}
218 		if (!client_exists) {
219 			gtk_list_store_append(project->clients, &iter);
220 			gtk_list_store_set(project->clients, &iter,
221 							   CLIENT_ID_COLUMN, client_id_str, -1);
222 		}
223 	}
224 }
225 
226 void
event_client_name(panel_t * panel,lash_event_t * event)227 event_client_name(panel_t * panel, lash_event_t * event)
228 {
229 	const char *project_name = lash_event_get_project(event);
230 	const char *client_name = lash_event_get_string(event);
231 	project_t *project = get_project(panel, project_name);
232 	GtkTreeModel *tree_model = NULL;
233 	GtkTreeIter iter;
234 	uuid_t client_id;
235 	char *search_id_str = NULL;
236 	char *client_id_str = malloc(37);
237 
238 	lash_event_get_client_id(event, client_id);
239 	uuid_unparse(client_id, client_id_str);
240 
241 	printf("Change client name (%s): %s = %s\n", project_name, client_id_str,
242 		   client_name);
243 
244 	if (project != NULL) {
245 		tree_model = GTK_TREE_MODEL(project->clients);
246 
247 		if (gtk_tree_model_get_iter_first(tree_model, &iter))
248 			do {
249 				gtk_tree_model_get(tree_model, &iter, CLIENT_ID_COLUMN,
250 								   &search_id_str, -1);
251 
252 				if (!strcmp(search_id_str, client_id_str)) {
253 					gtk_list_store_set(project->clients, &iter,
254 									   CLIENT_NAME_COLUMN, client_name, -1);
255 					break;
256 				}
257 			} while (gtk_tree_model_iter_next(tree_model, &iter));
258 	}
259 }
260 
261 void
deal_with_event(panel_t * panel,lash_event_t * event)262 deal_with_event(panel_t * panel, lash_event_t * event)
263 {
264 	switch (lash_event_get_type(event)) {
265 	case LASH_Project_Add:
266 		event_project_add(panel, event);
267 		break;
268 	case LASH_Project_Remove:
269 		event_project_remove(panel, event);
270 		break;
271 	case LASH_Project_Dir:
272 		event_project_dir(panel, event);
273 		break;
274 	case LASH_Project_Name:
275 		event_project_name(panel, event);
276 		break;
277 	case LASH_Client_Add:
278 		event_client_add(panel, event);
279 		break;
280 	case LASH_Client_Name:
281 		event_client_name(panel, event);
282 		break;
283 	case LASH_Jack_Client_Name:
284 		break;
285 	case LASH_Alsa_Client_ID:
286 		break;
287 	case LASH_Percentage:
288 		break;
289 	default:
290 		break;
291 	}
292 }
293 
294 gboolean
idle_cb(gpointer data)295 idle_cb(gpointer data)
296 {
297 	panel_t *panel;
298 	lash_event_t *event;
299 
300 	/*lash_config_t * config; */
301 
302 	panel = (panel_t *) data;
303 
304 	while ((event = lash_get_event(panel->lash_client))) {
305 		deal_with_event(panel, event);
306 	}
307 
308 	/*while ((config = lash_get_config(panel->lash_client)) ) {
309 	 * add_config(panel, config);
310 	 * } */
311 
312 	return TRUE;
313 }
314 
315 void
quit_cb(GtkButton * button,void * data)316 quit_cb(GtkButton * button, void *data)
317 {
318 	gtk_main_quit();
319 }
320 
321 void
open_cb(GtkButton * button,void * data)322 open_cb(GtkButton * button, void *data)
323 {
324 	panel_t *panel = (panel_t *) data;
325 	int response = GTK_RESPONSE_NONE;
326 	char *filename = NULL;
327 	lash_event_t *event = NULL;
328 
329 	GtkWidget *open_dialog =
330 		gtk_file_chooser_dialog_new("Open Project", GTK_WINDOW(panel->window),
331 									GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
332 									GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
333 									GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
334 
335 	char default_dir[256];
336 	snprintf(default_dir, 256, "%s/%s", getenv("HOME"), DEFAULT_PROJECT_DIR);
337 	gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(open_dialog),
338 		default_dir);
339 
340 	response = gtk_dialog_run(GTK_DIALOG(open_dialog));
341 
342 	if (response == GTK_RESPONSE_OK) {
343 		filename =
344 			gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(open_dialog));
345 		event = lash_event_new_with_type(LASH_Project_Add);
346 		lash_event_set_string(event, filename);
347 		lash_send_event(panel->lash_client, event);
348 	}
349 
350 	gtk_widget_destroy(open_dialog);
351 }
352 
353 panel_t *
panel_create(lash_client_t * lash_client)354 panel_create(lash_client_t * lash_client)
355 {
356 	panel_t *panel = NULL;
357 	GtkWidget *main_box = NULL;
358 
359 	GtkWidget *menu_bar = NULL;
360 	GtkWidget *file_menu = NULL;
361 	GtkWidget *file_menuitem = NULL;
362 	GtkWidget *help_menu = NULL;
363 	GtkWidget *help_menuitem = NULL;
364 	GtkWidget *open_menu_item = NULL;
365 	GtkWidget *menu_separator = NULL;
366 	GtkWidget *quit_menu_item = NULL;
367 	GtkWidget *about_menu_item = NULL;
368 
369 	guint status_context;
370 
371 	panel = malloc(sizeof(panel_t));
372 	panel->lash_client = lash_client;
373 
374 	panel->projects = gtk_list_store_new(PROJECT_NUM_COLUMNS, G_TYPE_POINTER);
375 	panel->num_projects = 0;
376 
377 	panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
378 	gtk_window_set_title(GTK_WINDOW(panel->window), WINDOW_TITLE);
379 	gtk_window_set_default_size(GTK_WINDOW(panel->window), 450, 282);
380 	g_signal_connect(G_OBJECT(panel->window), "delete_event",
381 					 G_CALLBACK(quit_cb), NULL);
382 
383 	main_box = gtk_vbox_new(FALSE, 0);
384 	gtk_widget_show(main_box);
385 	gtk_container_add(GTK_CONTAINER(panel->window), main_box);
386 
387 	/*
388 	 * menu bar
389 	 */
390 	file_menu = gtk_menu_new();
391 
392 	open_menu_item = gtk_image_menu_item_new_with_label("Open Project...");
393 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(open_menu_item),
394 								  gtk_image_new_from_stock(GTK_STOCK_OPEN,
395 														   GTK_ICON_SIZE_MENU));
396 	gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), open_menu_item);
397 	g_signal_connect(G_OBJECT(open_menu_item), "activate",
398 					 G_CALLBACK(open_cb), panel);
399 	gtk_widget_show(open_menu_item);
400 
401 	menu_separator = gtk_separator_menu_item_new();
402 	gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), menu_separator);
403 	gtk_widget_show(menu_separator);
404 
405 	quit_menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
406 	gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit_menu_item);
407 	g_signal_connect(G_OBJECT(quit_menu_item), "activate",
408 					 G_CALLBACK(quit_cb), panel);
409 	gtk_widget_show(quit_menu_item);
410 
411 	file_menuitem = gtk_menu_item_new_with_label("File");
412 	gtk_widget_show(file_menuitem);
413 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_menuitem), file_menu);
414 
415 	help_menu = gtk_menu_new();
416 	help_menuitem = gtk_menu_item_new_with_label("Help");
417 	gtk_widget_show(help_menuitem);
418 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_menuitem), help_menu);
419 
420 	about_menu_item = gtk_image_menu_item_new_with_label("About...");
421 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(about_menu_item),
422 								  gtk_image_new_from_stock(GTK_STOCK_ABOUT,
423 														   GTK_ICON_SIZE_MENU));
424 	gtk_menu_shell_append(GTK_MENU_SHELL(help_menu), about_menu_item);
425 	g_signal_connect(G_OBJECT(about_menu_item), "activate",
426 					 G_CALLBACK(about_cb), panel);
427 
428 	gtk_widget_show(about_menu_item);
429 
430 	menu_bar = gtk_menu_bar_new();
431 	gtk_widget_show(menu_bar);
432 	gtk_box_pack_start(GTK_BOX(main_box), menu_bar, FALSE, TRUE, 0);
433 
434 	gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file_menuitem);
435 	gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), help_menuitem);
436 
437 	/*
438 	 * projects notebook
439 	 */
440 	panel->project_notebook = gtk_notebook_new();
441 	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(panel->project_notebook),
442 							 GTK_POS_TOP);
443 	gtk_widget_show(panel->project_notebook);
444 	gtk_box_pack_start(GTK_BOX(main_box), panel->project_notebook, TRUE, TRUE, 0);
445 
446 	/*
447 	 * "no project" label (shown instead of a notebook tab when no projects are present)
448 	 */
449 	panel->no_projects_label = gtk_label_new("No projects open.  Start a LASH client, or load a project from the File menu.");
450 	gtk_widget_show(panel->no_projects_label);
451 	gtk_box_pack_start(GTK_BOX(main_box), panel->no_projects_label, FALSE, TRUE, 10);
452 
453 	/*
454 	 * status bar
455 	 */
456 	panel->status_bar = gtk_statusbar_new();
457 	gtk_widget_show(panel->status_bar);
458 	status_context =
459 		gtk_statusbar_get_context_id(GTK_STATUSBAR(panel->status_bar),
460 									 "connected");
461 	gtk_statusbar_push(GTK_STATUSBAR(panel->status_bar), status_context,
462 					   g_strdup_printf("Connected to server %s",
463 									   lash_get_server_name(lash_client)));
464 	gtk_box_pack_start(GTK_BOX(main_box), panel->status_bar, FALSE, TRUE, 0);
465 
466 	gtk_timeout_add(1000, idle_cb, panel);
467 	gtk_widget_show(panel->window);
468 
469 	return panel;
470 }
471