playlist basic operations
[melted_gui] / src / instance.c
1 /*
2 * instance.c -- GTK+ 2 melted gui
3 * Copyright (C) 2012 Maksym Veremeyenko <verem@m1stereo.tv>
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 2 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, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #ifndef _GNU_SOURCE
21 #define _GNU_SOURCE
22 #endif
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <pthread.h>
34
35 #include "instance.h"
36 #include "ui.h"
37 #include "opts.h"
38 #include "timecode.h"
39 #include "player.h"
40 #include "library.h"
41 #include "playlist.h"
42
43 GtkTargetEntry drag_targets[] = { { (char*) "application/playlist_item_t", 0, 0 } };
44
45 static gboolean library_tree_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
46 {
47 instance_t* app = (instance_t*)data;
48
49 switch(event->keyval)
50 {
51 case GDK_C:
52 case GDK_c:
53 if(event->state & GDK_CONTROL_MASK)
54 {
55 int count;
56 playlist_item_t* items;
57
58 items = library_get_selected_items(app, &count);
59
60 if(items)
61 {
62 int i;
63
64 for(i = 0; i < count; i++)
65 app->clipboard.item[i] = items[i];
66
67 app->clipboard.count = count;
68 };
69
70 return TRUE;
71 };
72 break;
73 case GDK_V:
74 case GDK_v:
75 if(event->state & GDK_CONTROL_MASK)
76 {
77 g_warning("CTRL+v\n");
78 return TRUE;
79 };
80 break;
81 case GDK_X:
82 case GDK_x:
83 if(event->state & GDK_CONTROL_MASK)
84 {
85 g_warning("CTRL+x\n");
86 return TRUE;
87 };
88 break;
89 case GDK_KEY_BackSpace:
90 // omnplay_library_add(app, 0);
91 return TRUE;
92 case GDK_KEY_F5:
93 // omnplay_library_refresh(app);
94 return TRUE;
95 };
96
97 return FALSE;
98 };
99
100 static gboolean playlist_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
101 {
102 instance_t* app = (instance_t*)data;
103
104 switch(event->keyval)
105 {
106 case GDK_C:
107 case GDK_c:
108 if(event->state & GDK_CONTROL_MASK)
109 {
110 playlist_item_copy(app);
111 return TRUE;
112 };
113 break;
114 case GDK_V:
115 case GDK_v:
116 if(event->state & GDK_CONTROL_MASK)
117 {
118 playlist_item_paste(app, 0);
119 return TRUE;
120 };
121 break;
122 case GDK_X:
123 case GDK_x:
124 if(event->state & GDK_CONTROL_MASK)
125 {
126 playlist_item_copy(app);
127 playlist_delete_selected_items(app);
128 return TRUE;
129 };
130 break;
131 case GDK_S:
132 case GDK_s:
133 if(event->state & GDK_CONTROL_MASK)
134 {
135 // omnplay_playlist_save(app);
136 return TRUE;
137 };
138 break;
139 case GDK_O:
140 case GDK_o:
141 if(event->state & GDK_CONTROL_MASK)
142 {
143 // omnplay_playlist_load(app);
144 return TRUE;
145 };
146 break;
147 case GDK_KEY_uparrow:
148 if(event->state & GDK_CONTROL_MASK)
149 {
150 playlist_item_swap(app, -1);
151 return TRUE;
152 };
153 break;
154 case GDK_KEY_downarrow:
155 if(event->state & GDK_CONTROL_MASK)
156 {
157 playlist_item_swap(app, -1);
158 return TRUE;
159 };
160 break;
161 case GDK_KEY_space:
162 // omnplay_ctl(app, BUTTON_PLAYER_PLAY);
163 return TRUE;
164 case GDK_KEY_Return:
165 // omnplay_ctl(app, BUTTON_PLAYER_CUE);
166 return TRUE;
167 case GDK_KEY_Insert:
168 // omnplay_playlist_item_add(app, 0);
169 return TRUE;
170 case GDK_KEY_Delete:
171 playlist_delete_selected_items(app);
172 return TRUE;
173 case GDK_E:
174 case GDK_e:
175 // omnplay_playlist_item_edit(app);
176 return TRUE;
177 };
178
179 return FALSE;
180 };
181
182 static gboolean library_tree_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
183 {
184 // g_warning("on_library_grid_button: event->button=%d, event->type=%d", event->button, event->type);
185
186 if(event->button==1 && event->type==GDK_2BUTTON_PRESS)
187 {
188 // omnplay_library_add((omnplay_instance_t* )data, 0);
189 return TRUE;
190 };
191
192 return FALSE;
193 };
194
195 static gboolean playlist_grid_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
196 {
197 instance_t* app = (instance_t*)data;
198
199 // g_warning("on_playlist_grid_button");
200
201 if(event->button==1 && event->type==GDK_2BUTTON_PRESS)
202 {
203 // omnplay_playlist_item_edit(app);
204 return TRUE;
205 };
206
207 return FALSE;
208 };
209
210 static gboolean instance_button_click(instance_t* app, control_buttons_t button)
211 {
212 switch(button)
213 {
214 case BUTTON_PLAYLIST_ITEM_ADD:
215 playlist_item_add(app, 0);
216 break;
217 case BUTTON_PLAYLIST_ITEM_DEL:
218 playlist_delete_selected_items(app);
219 break;
220 case BUTTON_PLAYLIST_ITEM_EDIT:
221 playlist_item_edit(app);
222 break;
223 case BUTTON_PLAYLIST_LOAD:
224 playlist_load(app);
225 break;
226 case BUTTON_PLAYLIST_SAVE:
227 playlist_save(app);
228 break;
229 case BUTTON_PLAYLIST_BLOCK_SINGLE:
230 case BUTTON_PLAYLIST_BLOCK_LOOP:
231 playlist_block(app, (BUTTON_PLAYLIST_BLOCK_LOOP == button)?1:0);
232 break;
233 case BUTTON_PLAYLIST_ITEM_UP:
234 playlist_item_swap(app, -1);
235 break;
236 case BUTTON_PLAYLIST_ITEM_DOWN:
237 playlist_item_swap(app, +1);
238 break;
239 case BUTTON_PLAYER_CUE:
240 case BUTTON_PLAYER_PLAY:
241 case BUTTON_PLAYER_PAUSE:
242 case BUTTON_PLAYER_STOP:
243 // omnplay_ctl(app, button);
244 break;
245 case BUTTON_LIBRARY_ADD:
246 // omnplay_library_add(app, 0);
247 playlist_item_add_from_library(app, 0);
248 break;
249 case BUTTON_LIBRARY_REFRESH:
250 // omnplay_library_refresh(app);
251 break;
252 case BUTTON_LIBRARY_FIND:
253 // omnplay_library_search(app, 0);
254 break;
255 case BUTTON_LIBRARY_FIND_NEXT:
256 // omnplay_library_search(app, 1);
257 break;
258 case BUTTON_PLAYLIST_RELINK:
259 // omnplay_playlist_relink(app);
260 break;
261 default:
262 g_warning("instance_button_click: unknow button clicked");
263 };
264 return TRUE;
265 };
266
267 static gboolean on_button_click(GtkWidget *button, gpointer user_data)
268 {
269 int i;
270 instance_t* app = (instance_t*)user_data;
271
272 for(i = 1; i < BUTTON_LAST; i++)
273 if(app->buttons[i] == button)
274 return instance_button_click(app, (control_buttons_t)i);
275
276 return FALSE;
277 };
278
279 static gboolean on_main_window_delete_event( GtkWidget *widget, GdkEvent *event, gpointer user_data )
280 {
281 g_print ("delete event occurred [start]\n");
282 gdk_threads_leave();
283 instance_release((instance_t*)user_data);
284 gdk_threads_enter();
285 g_print ("delete event occurred [finish]\n");
286
287 return FALSE;
288 }
289
290 static void on_main_window_destroy( GtkWidget *widget, gpointer user_data )
291 {
292 g_print ("destroy occurred\n");
293 gtk_main_quit();
294 }
295
296 instance_t* instance_create(int argc, char** argv)
297 {
298 int i, c;
299 instance_t* app;
300
301 /* prepare application instance */
302 app = (instance_t*)malloc(sizeof(instance_t));
303 memset(app, 0, sizeof(instance_t));
304
305 /* load parameters from command line */
306 if(!instance_opt(argc, argv, app) && app->players.count)
307 app->window = ui_create(app);
308 else
309 instance_usage();
310
311 return app;
312 };
313
314 void instance_destroy(instance_t* app)
315 {
316 free(app);
317 };
318
319 void instance_init(instance_t* app)
320 {
321 int i;
322 pthread_mutexattr_t attr;
323
324 gtk_signal_connect( GTK_OBJECT( app->window ), "delete-event",
325 GTK_SIGNAL_FUNC(on_main_window_delete_event), app);
326
327 gtk_signal_connect( GTK_OBJECT( app->window ), "destroy",
328 GTK_SIGNAL_FUNC(on_main_window_destroy), app);
329
330 gtk_widget_add_events(app->playlist_grid, GDK_BUTTON_PRESS_MASK);
331 gtk_widget_add_events(app->playlist_grid, GDK_KEY_PRESS_MASK);
332 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "key-press-event",
333 GTK_SIGNAL_FUNC(playlist_grid_key), app);
334
335 gtk_widget_add_events(app->library_tree, GDK_BUTTON_PRESS_MASK);
336 gtk_widget_add_events(app->library_tree, GDK_KEY_PRESS_MASK);
337 gtk_signal_connect(GTK_OBJECT(app->library_tree), "key-press-event",
338 GTK_SIGNAL_FUNC(library_tree_key), app);
339
340 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "button-press-event",
341 GTK_SIGNAL_FUNC(playlist_grid_button), app);
342
343 gtk_signal_connect(GTK_OBJECT(app->library_tree), "button-press-event",
344 GTK_SIGNAL_FUNC(library_tree_button), app);
345
346 /* create lock */
347 pthread_mutexattr_init(&attr);
348 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
349 pthread_mutex_init(&app->players.lock, &attr);
350 pthread_mutex_init(&app->playlist.lock, &attr);
351 pthread_mutex_init(&app->library.lock, &attr);
352 pthread_mutexattr_destroy(&attr);
353
354 /* run unit monitoring threads */
355 for(i = 0; i < app->players.count; i++)
356 player_run(app, i);
357
358 /* attach buttons click */
359 for(i = 1; i < BUTTON_LAST; i++)
360 gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked",
361 GTK_SIGNAL_FUNC( on_button_click), app );
362
363 /* init library */
364 library_init(app);
365
366 /* init playlist */
367 playlist_init(app);
368 };
369
370 void instance_release(instance_t* app)
371 {
372 int i;
373
374 app->f_exit = 1;
375
376 /* stop unit monitoring threads */
377 for(i = 0; i < app->players.count; i++)
378 player_stop(app, i);
379
380 /* release laylist */
381 playlist_release(app);
382
383 /* release library */
384 library_release(app);
385
386 /* destroy lock */
387 pthread_mutex_destroy(&app->players.lock);
388
389 /* destroy lock */
390 pthread_mutex_destroy(&app->playlist.lock);
391
392 /* destroy library lock */
393 pthread_mutex_destroy(&app->library.lock);
394 };