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