RSS

(root)/calliope : /src/ui/calliope.c (revision 451)

Line Revision Contents
1 17 /*  Calliope Music Player
2 185  *  Copyright 2005-09 Sam Thursfield <ssssam gmail.com>
3 17  *
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 3 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 451  *  but WITHOUT ANYdu WARRANTY; without even the implied warranty of
11 17  *  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 267  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17  */
17 24
18 #include <string.h>
19 17 #include <glib/gstdio.h>
20 #include <gdk/gdkkeysyms.h>
21 #include "format.h"
22 #include "process.h"
23 #include "main.h"
24 #include "conftool.h"
25 #include "files.h"
26 #include "filesearch.h"
27 191 #include "gstreamer.h"
28 17 #include "library.h"
29 #include "cdsource.h"
30 #include "filesource.h"
31 24 #include "conftool-ui.h"
32 17 #include "calliope.h"
33 #include "uimanager.h"
34 #include "browser.h"
35 #include "info.h"
36 #include "player.h"
37 #include "addmusicdialog.h"
38 450 #include "status-dialog.h"
39 17
40 // FIXME: should be in conftool!
41 static int run_dialog_with_panel(GtkWidget *dialog, GtkWidget *notebook, GtkWidget *panel, const char *title) {
42         int page_num=-1;
43         if (panel!=NULL) {
44                 gtk_widget_show(panel);
45                 // FIXME: should be possible to get the panel title from its dialog title
46                 page_num=gtk_notebook_append_page(GTK_NOTEBOOK(notebook), panel, gtk_label_new(title));
47                 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
48 267         };
49
50 17         int response=gtk_dialog_run(GTK_DIALOG(dialog));
51 267
52 17         if (page_num!=-1) {
53                 gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), page_num);
54                 gtk_widget_hide(panel);
55         };
56 267         gtk_widget_hide(dialog);
57
58 17         return response;
59 };
60
61 // FIXME
62 //      - update ui based on signals from gst - not signals sent or user actions
63
64 267 //FIXME: Does calliope even need to be an object ?? maybe a singleton ??
65 17 //  singletons are horrible and a hack used in languages that for some reason
66 //  can't have global functions.
67
68 24 G_DEFINE_TYPE(Calliope, calliope, GTK_TYPE_WINDOW);
69 17
70 // FIXME: should this really be a class ?? Name one scenario we would want a
71 // calliope process with two discrete calliope objects.
72
73 // FIXME: remember at the moment THIS IS DUPLICATED IN ADDMUSICDIALOG !! because it can't librarylist without it :(
74 struct CalliopePrivate {
75 189         gboolean disposed;
76 267
77 17         AddMusicDialog *add_music_dialog;
78
79 267         // To keep a dynamic menu of drives and disks, we need to store the
80 17         // GtkActions and the ui entry ID's for each device.
81         //
82 28         //GnomeVFSVolumeMonitor *vmon;
83         //GtkActionGroup *cd_actions;
84         //GHashTable *drive_ui_entry_table, *volume_ui_entry_table;
85 267
86 17         // FIXME: take out, conftool should deal
87         //GConfChangeSet *changeset;
88 267
89 402         char *default_view_config_string;
90
91 17         MusicSource *selected_source;
92 267         int selected_source_status_changed_handler_id;
93
94 24         MusicSourceView *info_closure_view; GtkTreePath *info_closure_path;
95         int info_closure_idle_id;
96 17
97 267         GtkWidget *player, *browser, *info;
98 17         GtkWidget *appbar;
99         GtkWidget *open_dir_file_chooser;
100 267
101 193         GtkWidget *master_vbox;
102 17 };
103
104 187 static void dispose(GObject *self);
105 static void finalize(GObject *self);
106 17
107 350 //static void init_device_menu(Calliope *self);
108 17
109 static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event, Calliope *self);
110 24 static void browser_selection_changed(Browser *browser, MusicSourceView *selected_view, GtkTreePath *selected_path, Calliope *self);
111 17 static void selected_source_notify(Browser *browser, GParamSpec *pspec, Calliope *self);
112 static void selected_source_status_changed(MusicSource *source, const char *status, Calliope *self);
113 28 /*static void drive_connected(GnomeVFSVolumeMonitor *vmon, GnomeVFSDrive *drive, Calliope *self);
114 17 static void drive_disconnected(GnomeVFSVolumeMonitor *vmon, GnomeVFSDrive *drive, Calliope *self);
115 static void volume_mounted(GnomeVFSVolumeMonitor *vmon, GnomeVFSVolume *volume, Calliope *self);
116 static void volume_unmounted(GnomeVFSVolumeMonitor *vmon, GnomeVFSVolume *volume, Calliope *self);
117 28 */
118 356 #ifndef LIBRARY_DISABLE
119 446 static void new_action            (GtkAction *action, Calliope *self);
120 static void open_action           (GtkAction *action, Calliope *self);
121 static void add_music_action      (GtkAction *action, Calliope *self);
122 356 #endif
123 350 //static void play_cd_action(GtkAction *action, Calliope *self);
124 //static void import_cd_action(GtkAction *action, Calliope *self);
125 446 static void open_directory_action (GtkAction *action, Calliope *self);
126 static void close_action          (GtkAction *action, Calliope *self);
127 static void settings_action       (GtkAction *action, Calliope *self);
128 static void status_action         (GtkAction *action, Calliope *self);
129 static void exit_action           (GtkAction *action, Calliope *self);
130 17
131 static const char *ui_descr=
132         "<menubar name='MainMenu'>"
133         "<menu action='CalliopeMenu'>"
134 356 #ifndef LIBRARY_DISABLE
135 17         "       <menuitem action='New'/>"
136         "       <menuitem action='Open'/>"
137         "       <menuitem action='AddMusic'/>"
138 356 #endif
139 17         "       <separator/>"
140         "       <placeholder name='CDList'/>"
141         "       <menuitem action='OpenDir'/>"
142 446         "       <menuitem action='Close'/>"
143 17         "       <separator/>"
144         "       <menuitem action='Settings'/>"
145 446         "       <menuitem action='Status'/>"
146 17         "       <menuitem action='Exit'/>"
147         "</menu></menubar>";
148 267
149 17 static GtkActionEntry actions[] = {
150 446     { "CalliopeMenu", NULL, "_Calliope" },
151 356 #ifndef LIBRARY_DISABLE
152 446     { "New",      GTK_STOCK_NEW,        "_Create New Database", "",           NULL, G_CALLBACK(new_action)            },
153     { "Open",     GTK_STOCK_OPEN,       "_Open Database",       "",           NULL, G_CALLBACK(open_action)           },
154     { "AddMusic", GTK_STOCK_ADD,        "_Add Music...",        "",           NULL, G_CALLBACK(add_music_action)      },
155 356 #endif
156 446     { "OpenDir",  GTK_STOCK_DIRECTORY,  "_Open Directory...",   "",           NULL, G_CALLBACK(open_directory_action) },
157     { "Close",    GTK_STOCK_CLOSE,      "_Close Source",        "<ctrl>W",    NULL, G_CALLBACK(close_action)          },
158     { "Settings", GTK_STOCK_PROPERTIES, "Se_ttings",            "",           NULL, G_CALLBACK(settings_action)       },
159     { "Status",   GTK_STOCK_INFO,       "_Status",              "",           NULL, G_CALLBACK(status_action)         },
160     { "Exit",     GTK_STOCK_QUIT,       "E_xit",                "<control>X", NULL, G_CALLBACK(exit_action)           }
161 17 };
162
163 static void calliope_class_init(CalliopeClass *cl) {
164 189         G_OBJECT_CLASS(cl)->dispose = dispose;
165 187         G_OBJECT_CLASS(cl)->finalize = finalize;
166         g_type_class_add_private (cl, sizeof(CalliopePrivate));
167 17 }
168
169 static void calliope_init(Calliope *self) {
170 140         SP = G_TYPE_INSTANCE_GET_PRIVATE(self, CALLIOPE_TYPE, CalliopePrivate);
171 267
172 189         SP->disposed = FALSE;
173 267
174 402         SP->default_view_config_string = NULL;
175
176 267         ui_manager_add_actions (ui, actions, G_N_ELEMENTS(actions), self, "actions-main");
177 188         ui_manager_add_ui_from_string (ui, ui_descr, "ui-main");
178 17
179 267         SP->selected_source = NULL;
180         SP->selected_source_status_changed_handler_id=0;
181 24         SP->info_closure_idle_id=0;
182 17
183 28         //init_device_menu(self);
184 267
185 187         // FIXME: our implementation of saving geometry is of course imperfect. Things like, what if the
186 267         // user has us on a 2nd monitor which isn't there on next run? I would love to leave window
187 187         // management up to the OS or a parent class, but this seems to be beyond them.
188 267         // I think the best hope is a GtkAppWindow class, which links to gconf? Or something, and can
189 187         // save and restore geometry info etc. How could we merge this with GtkUnique as well? I guess
190         // the GtkUnique could contain the GtkAppWindow. Yeah do that!
191 267         //
192 187         GSList *geometry = gconf_client_get_list (gconf, KEY_GEOMETRY, GCONF_VALUE_INT, NULL);
193 267         if (geometry!=NULL && g_slist_length(geometry) == 4) {
194 187                 GSList *node;
195 267                 int x = GPOINTER_TO_INT((node=geometry)->data),
196 187                     y = GPOINTER_TO_INT((node=node->next)->data),
197                         w = GPOINTER_TO_INT((node=node->next)->data),
198                         h = GPOINTER_TO_INT((node=node->next)->data);
199 267                 //GdkWindowState state = GPOINTER_TO_INT((node=node->next)->data);
200 187                 //printf ("Setting geometry: (%i, %i) %ix%i\n", x, y, w, h); fflush (stdout);
201 267
202 187                 gtk_window_move (GTK_WINDOW(self), x, y);
203                 gtk_window_set_default_size (GTK_WINDOW(self), w, h);
204                 //gdk_window_set_state (
205                 if (w < 0 || h < 0)
206                         gtk_window_maximize (GTK_WINDOW(self));
207         }
208         g_slist_free (geometry);
209
210 24         char *icon_file_path=g_build_filename("data", "calliope-icon.png", NULL);
211         if (!g_file_test(icon_file_path, G_FILE_TEST_EXISTS)) {
212                 g_free(icon_file_path);
213                 icon_file_path=g_build_filename(PACKAGE_DATA_DIR, "pixmaps", "calliope-icon.png", NULL);
214         };
215 267
216 24         if (g_file_test(icon_file_path, G_FILE_TEST_EXISTS)) {
217                 gtk_window_set_default_icon_from_file(icon_file_path, NULL);
218 17                 g_free(icon_file_path);
219         }
220 267
221         gtk_window_set_title(GTK_WINDOW(self), "Calliope");
222 24         gtk_window_add_accel_group(GTK_WINDOW(self), gtk_ui_manager_get_accel_group(GTK_UI_MANAGER(ui)));
223 267         g_signal_connect(self, "key-press-event", G_CALLBACK(key_press_event), self);
224 17         g_signal_connect(self, "destroy", gtk_main_quit, NULL);
225
226 24         SP->appbar=gtk_statusbar_new();
227 267
228 17         // FIXME: info needs a better name.
229 24         SP->info=info_new();
230 17
231         SP->browser=browser_new();
232 267         g_signal_connect(SP->browser, "selection-changed", G_CALLBACK(browser_selection_changed), self);
233 17         g_signal_connect(SP->browser, "notify::selected-source", G_CALLBACK(selected_source_notify), self);
234 267         selected_source_notify(BROWSER(SP->browser), NULL, self);
235 17
236 189         SP->player = player_new(BROWSER(SP->browser));
237 267
238 17         GtkWidget *hpaned=gtk_hpaned_new();
239         gtk_paned_add1(GTK_PANED(hpaned), SP->browser);
240 24         gtk_paned_add2(GTK_PANED(hpaned), SP->info);
241 267
242 193         SP->master_vbox = gtk_vbox_new(0, 0);
243         GtkWidget *menu = gtk_ui_manager_get_widget(GTK_UI_MANAGER(ui), "/MainMenu");
244         gtk_box_pack_start (GTK_BOX(SP->master_vbox), menu, 0, 1, 0);
245 189         if (SP->player!=NULL)
246 193                 gtk_box_pack_start (GTK_BOX(SP->master_vbox), SP->player, 0, 0, 0);
247         gtk_box_pack_start(GTK_BOX(SP->master_vbox), hpaned, 1, 1, 0);
248         gtk_box_pack_start(GTK_BOX(SP->master_vbox), SP->appbar, 0, 1, 0);
249         gtk_container_add (GTK_CONTAINER(self), SP->master_vbox);
250 17
251         // FIXME: I would like to receive drags from nautilus, azereus etc.
252         /*const GtkTargetEntry target[]={
253                 {       "dogs", 0, 1    }
254 267         };
255 17         gtk_drag_dest_set(GTK_WIDGET(self), GTK_DEST_DEFAULT_ALL, , GDK_ACTION_LINK);*/
256
257         //SP->changeset=gconf_change_set_new();
258 120         //GObject *peditor;
259 267
260 24         SP->add_music_dialog=NULL;
261 17         SP->add_music_dialog=add_music_dialog_new(BROWSER(SP->browser)->library_liststore);
262 267
263 17         /// play-dir dialog
264 267         //
265 120         //GtkWidget *dialog=WID("play_dir_dialog");
266 267         // FIXME: now this doesn't work !!
267 17         //peditor=gconf_peditor_new_boolean(SP->changeset, KEY_FILE_SOURCE_RECURSE, WID("play_dir_recurse_opt"), NULL);
268 267
269 17         /// play dir dialog
270 267         // FIXME: this isn't a folder chooser any more :(
271 40         SP->open_dir_file_chooser=WID("play_dir_location");
272 17 }
273
274 189 // Never forget dispose can get called multiple times!!!
275 static void dispose(GObject *object) {
276         Calliope *self = CALLIOPE(object);
277         if (SP->disposed==TRUE)
278                 return;
279
280 187         // Save window position.
281         int x, y, w, h;
282 267
283 187         // FIXME:
284 267         /* If you are saving and restoring your application's window positions, you should know that
285 187          * it's impossible for applications to do this without getting it somewhat wrong because
286          *  applications do not have sufficient knowledge of window manager state. The Correct Mechanism
287 267          * is to support the session management protocol (see the "GnomeClient" object in the GNOME
288          * libraries for example) and allow the window manager to save your window sizes and positions.
289 187          * */
290         // However, this is still valid on Windows.
291 267
292         GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(self));
293 187         if (gdk_window!=NULL) {
294                 // Window can be NULL if not yet realized.
295 267
296 187                 gtk_window_get_position (GTK_WINDOW(self), &x, &y);
297                 if (gdk_window_get_state(gdk_window) & GDK_WINDOW_STATE_MAXIMIZED)
298                         w = h = -1;
299                 else gtk_window_get_size (GTK_WINDOW(self), &w, &h);
300                 GSList *list = g_slist_build (4, x, y, w, h);
301                 //printf ("Saving geometry: (%i, %i) %ix%i.\n", x, y, w, h); fflush (stdout);
302 267
303 187                 GError *error = NULL;
304                 gconf_client_set_list (gconf, KEY_GEOMETRY, GCONF_VALUE_INT, list, &error);
305                 g_slist_free (list);
306 267
307 187                 if (error!=NULL)
308                         printf ("Unable to save window geometry - %s.\n", error->message);
309 267         };
310
311         ui_manager_remove_ui (ui, "ui-main");
312         ui_manager_remove_actions (ui, "actions-main", self);
313
314 193         // Destroy player before we are disposed. Some crazy bug happens otherwise where removing the UI
315         // actions causes a crash inside GTK+, even if the action group is empty .. but only when adding
316 267         // the entries again, or something. To reproduce it, move this next line of code to after
317         // parent_class->dispose is called, and run main-test. Should give you a GTK_IS_CONTAINER fail
318 193         // for some bizarre reason.
319         // FIXME: should really try and find out what causes this, but I have already wasted days of my
320         // life trying.
321         gtk_container_remove (GTK_CONTAINER(SP->master_vbox), SP->player);
322 267
323 189         SP->disposed = TRUE;
324 193
325 267         G_OBJECT_CLASS(calliope_parent_class)->dispose (object);
326 187 };
327
328 17 static void finalize(GObject *object) {
329         Calliope *self=CALLIOPE(object);
330 188
331 24         if (SP->add_music_dialog!=NULL)
332                 add_music_dialog_free(SP->add_music_dialog);
333 267
334         //g_hash_table_unref(SP->volume_ui_entry_table);
335         //g_hash_table_unref(SP->drive_ui_entry_table);
336 17
337 402         if (SP->default_view_config_string != NULL) g_free (SP->default_view_config_string);
338
339 17         G_OBJECT_CLASS(calliope_parent_class)->finalize(object);
340 193
341         //g_object_ref_sink (SP->player);
342         //g_object_unref (SP->player);
343 17 }
344
345 267 // FIXME: somehow can we make the device menu code shorter ?? or spin it off into
346 17 // lib ?
347
348 350 //static void init_device_menu(Calliope *self) {
349 267         /*SP->vmon=gnome_vfs_get_volume_monitor();
350 17         SP->drive_ui_entry_table=g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)g_free);
351         SP->volume_ui_entry_table=g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)g_free);
352 267
353 17         SP->cd_actions=gtk_action_group_new("CD actions");
354 267         gtk_ui_manager_insert_action_group(GTK_UI_MANAGER(ui), SP->cd_actions, 0);
355
356 17         g_signal_connect(SP->vmon, "volume-mounted", G_CALLBACK(volume_mounted), self);
357         g_signal_connect(SP->vmon, "volume-unmounted", G_CALLBACK(volume_unmounted), self);
358         g_signal_connect(SP->vmon, "drive-connected", G_CALLBACK(drive_connected), self);
359         g_signal_connect(SP->vmon, "drive-disconnected", G_CALLBACK(drive_disconnected), self);
360 267
361 17         GList *drives=gnome_vfs_volume_monitor_get_connected_drives(SP->vmon), *drive_node=drives;
362         while(drive_node!=NULL) {
363                 GnomeVFSDrive *drive=drive_node->data;
364                 GnomeVFSDeviceType drive_type=gnome_vfs_drive_get_device_type(drive);
365 28                 if (drive_type==GNOME_VFS_DEVICE_TYPE_CDROM ||
366                  drive_type==GNOME_VFS_DEVICE_TYPE_AUDIO_CD // FIXME: does this ever happen ??
367                 ) {
368 17                         drive_connected(SP->vmon, drive, self);
369 267
370 17                         GList *volumes=gnome_vfs_drive_get_mounted_volumes(drive), *volume_node=volumes;
371                         while (volume_node!=NULL) {
372 267                                 volume_mounted(SP->vmon, volume_node->data, self);
373 17                                 g_object_unref(volume_node->data);
374                                 volume_node=volume_node->next;
375 267                         };
376                         g_list_free(volumes);
377 17                 };
378                 g_object_unref(drive);
379                 drive_node=drive_node->next;
380         };
381 28         g_list_free(drives);*/
382 350 //};
383 17
384 // Normally ctrl+pagedn/up is taken by treeview to move around somehow, however in
385 // a notebook it makes more sense to have these keys move tabs so we pass them to
386 // the notebook.
387 267 //
388 17 static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event, Calliope  *self) {
389         // FIXME: I think we could use key binding sets for this instead. I have tried
390         // before though and I can't make them work.
391 267
392 17         if (event->state & GDK_CONTROL_MASK) {
393 267
394 17                 if (event->keyval==GDK_Page_Up) {
395                         browser_move_page(BROWSER(SP->browser), GTK_DIR_LEFT);
396                         return TRUE;
397                 } else if (event->keyval==GDK_Page_Down) {
398                         browser_move_page(BROWSER(SP->browser), GTK_DIR_RIGHT);
399                         return TRUE;
400                 };
401 267
402 17                 // Volume change requires alt as well as ctrl simply because the treeview
403                 // takes ctrl+up/down as cursor control.
404                 // FIXME: Actually at the moment it doesn't.
405                 // FIXME: completely redesign pc keyboards to be more logical.
406                 //
407 //              if (event->state & GDK_ALT_MASK) {
408                         if (event->keyval==GDK_Up) {
409                                 player_change_volume(PLAYER(SP->player), GTK_DIR_UP);
410                                 return TRUE;
411                         } else if (event->keyval==GDK_Down) {
412                                 player_change_volume(PLAYER(SP->player), GTK_DIR_DOWN);
413                                 return TRUE;
414                         };
415 267         //      };
416 17         };
417
418         return FALSE;
419 };
420
421 24 static gboolean update_info_idle(Calliope *self) {
422 79         info_set_path(INFO(SP->info), SP->info_closure_view, SP->info_closure_path);
423 24         g_object_unref(SP->info_closure_view);
424 267         gtk_tree_path_free(SP->info_closure_path);
425 24         SP->info_closure_idle_id=0;
426         return FALSE;
427 };
428
429 static void browser_selection_changed(Browser *browser, MusicSourceView *selected_view, GtkTreePath *selected_path, Calliope *self) {
430         if (SP->info_closure_idle_id!=0) {
431                 g_object_unref(SP->info_closure_view);
432                 gtk_tree_path_free(SP->info_closure_path);
433         };
434 267
435 24         //printf("Got path: %s\n", gtk_tree_path_to_string(selected_path));fflush(stdout);
436 267
437 24         // We reference the view, so that source and view are guaranteed to be kept alive until the
438         // idle executes
439         // FIXME: surely a weak ref would be more appropriate .. ?
440         if (selected_view!=NULL && selected_path!=NULL) {
441                 SP->info_closure_view=selected_view; g_object_ref(selected_view);
442                 SP->info_closure_path=gtk_tree_path_copy(selected_path);
443 267
444 24                 if (SP->info_closure_idle_id==0) {
445                         SP->info_closure_idle_id=g_idle_add((GSourceFunc)update_info_idle, self);
446                 };
447         } else {
448                 if (SP->info_closure_idle_id!=0) {
449 267                         g_source_remove(SP->info_closure_idle_id);
450 24                         SP->info_closure_idle_id=0;
451                 }
452         }
453 };
454
455
456 17 // FIXME: this totally doesn't work especially on my laptop. Another problem is
457 // when I am on the laptop and I remove the drive the entire system crashes.
458
459 28 /*static void drive_connected(GnomeVFSVolumeMonitor *vmon, GnomeVFSDrive *drive, Calliope *self) {
460 17 //printf("drive connected\n");
461         GnomeVFSDeviceType drive_type=gnome_vfs_drive_get_device_type(drive);
462 28         if (drive_type!=GNOME_VFS_DEVICE_TYPE_CDROM && drive_type!=GNOME_VFS_DEVICE_TYPE_AUDIO_CD) return;
463 17
464         char *label=g_strdup_printf("Play '%s'", gnome_vfs_drive_get_display_name(drive)),
465                 *icon_name=gnome_vfs_drive_get_icon(drive);
466 267         UIEntry *ui_entry=ui_manager_add_menu_item(ui, SP->cd_actions,
467           "/MainMenu/CalliopeMenu/CDList", label, icon_name, GTK_STOCK_CDROM);
468 17         g_free(label); g_free(icon_name);
469         gtk_action_set_sensitive(ui_entry->action, TRUE);
470 267
471         g_object_set_data_full(G_OBJECT(ui_entry->action), "device-path",
472 17           gnome_vfs_drive_get_device_path(drive), (GDestroyNotify)g_free);
473         g_signal_connect(ui_entry->action, "activate", G_CALLBACK(play_cd_action), self);
474         gtk_action_set_sensitive(ui_entry->action, TRUE);
475         int *key=g_new(int, 1); *key=gnome_vfs_drive_get_id(drive);
476         g_hash_table_insert(SP->drive_ui_entry_table, key, ui_entry);
477 };
478
479 static void drive_disconnected(GnomeVFSVolumeMonitor *vmon, GnomeVFSDrive *drive, Calliope *self) {
480         printf("drive-disconnected: drive %X\n", drive);
481         int drive_id=gnome_vfs_drive_get_id(drive);
482         ui_manager_remove_entry(ui, g_hash_table_lookup(SP->drive_ui_entry_table, &drive_id));
483         g_hash_table_remove(SP->drive_ui_entry_table, &drive_id);
484 };
485
486 static void volume_mounted(GnomeVFSVolumeMonitor *vmon, GnomeVFSVolume *volume, Calliope *self) {
487         printf("Calliope::volume-mounted: volume %X, ", volume);
488         GnomeVFSDeviceType drive_type=gnome_vfs_volume_get_device_type(volume);
489 28         if (drive_type!=GNOME_VFS_DEVICE_TYPE_CDROM && drive_type!=GNOME_VFS_DEVICE_TYPE_AUDIO_CD) return;
490 17
491         int drive_id=gnome_vfs_drive_get_id(gnome_vfs_volume_get_drive(volume));
492         printf("drive_id %i\n", drive_id);
493
494         // Hide drive action
495         UIEntry *ui_entry=g_hash_table_lookup(SP->drive_ui_entry_table, &drive_id);
496         gtk_action_set_visible(ui_entry->action, FALSE);
497
498         char *label=g_strdup_printf("Play '%s'", gnome_vfs_volume_get_display_name(volume)),
499                 *icon_name=gnome_vfs_volume_get_icon(volume);
500 267         ui_entry=ui_manager_add_menu_item(ui, SP->cd_actions,
501 17           "/MainMenu/CalliopeMenu/CDList", label, icon_name, GTK_STOCK_CDROM);
502         g_free(label); g_free(icon_name);
503 267
504         g_object_set_data_full(G_OBJECT(ui_entry->action), "device-path",
505 17           gnome_vfs_volume_get_device_path(volume), (GDestroyNotify)g_free);
506         g_signal_connect(ui_entry->action, "activate", G_CALLBACK(play_cd_action), self);
507         gtk_action_set_sensitive(ui_entry->action, TRUE);
508         int *key=g_new(int, 1); *key=gnome_vfs_volume_get_id(volume);
509         g_hash_table_insert(SP->volume_ui_entry_table, key, ui_entry);
510 };
511
512 // FIXME: in theory here we need to check if the drive is now empty and if so,
513 // make the drive entry visible. In practice gnome-vfs seems to remove the
514 // drive too and connect a new one with a different id. I don't have any idea
515 267 // why.
516 17 static void volume_unmounted(GnomeVFSVolumeMonitor *vmon, GnomeVFSVolume *volume, Calliope *self) {
517 //printf("volume unmounted\n");
518         int volume_id=gnome_vfs_volume_get_id(volume);
519 267         ui_manager_remove_entry(ui, g_hash_table_lookup(SP->volume_ui_entry_table, &volume_id));
520 17         g_hash_table_remove(SP->volume_ui_entry_table, &volume_id);
521 };
522
523 28 */
524 172
525 // FIXME: it seems like tracking selected source ourselves is more difficult than just
526 // asking browser for it a lot.
527 static void selected_source_destroyed (Calliope *self, MusicSource *ex_source) {
528         if (SP->selected_source==ex_source)
529                 SP->selected_source = NULL;
530 };
531
532 17 static void selected_source_notify(Browser *browser, GParamSpec *pspec, Calliope *self) {
533 24         MusicSource *source;
534 172         browser_get_selected (browser, &source, NULL);
535 267
536         if (SP->selected_source!=NULL)
537 172                 g_signal_handler_disconnect (SP->selected_source, SP->selected_source_status_changed_handler_id);
538 267
539 172         SP->selected_source = source;
540 267
541 24         int cxt=gtk_statusbar_get_context_id(GTK_STATUSBAR(SP->appbar), "Database state"); // FIXME: wtf is this for
542 17         if (source==NULL)
543 24                 gtk_statusbar_push(GTK_STATUSBAR(SP->appbar), cxt, "No database loaded.");
544 17         else {
545 267                 char *msg=music_source_get_summary(source);
546 24                 gtk_statusbar_push(GTK_STATUSBAR(SP->appbar), cxt, msg);
547 267                 g_free(msg);
548 17
549                 SP->selected_source_status_changed_handler_id=g_signal_connect(source, "status-changed", G_CALLBACK(selected_source_status_changed), self);
550 172                 g_object_weak_ref (G_OBJECT(SP->selected_source), (GWeakNotify)selected_source_destroyed, self);
551 17         };
552 };
553
554 static void selected_source_status_changed(MusicSource *source, const char *status, Calliope *self) {
555 24         // FIXME: need to pop old msgs each time- just use 1 context I guess. I think.
556         int cxt=gtk_statusbar_get_context_id(GTK_STATUSBAR(SP->appbar), "Source info"); // FIXME: wtf is this for
557         gtk_statusbar_push(GTK_STATUSBAR(SP->appbar), cxt, status);
558 17 };
559
560 Calliope *calliope_new() {
561 24         Calliope *self=g_object_new(CALLIOPE_TYPE, NULL);
562         GTK_WINDOW(self)->type=GTK_WINDOW_TOPLEVEL;     // Very important!
563 267         gtk_widget_show_all(GTK_WIDGET(self));
564 178         //printf ("%X, %X\n", self, GTK_OBJECT(self));fflush(stdout);
565 17         return self;
566 };
567
568 356 #ifndef LIBRARY_DISABLE
569 142 MusicView *calliope_open_library (Calliope *self, const char *file) {
570         MusicSource *library = library_new(file);
571 158         GtkWidget *view = NULL;
572 142         if (library!=NULL) {
573 402                 view = browser_add_source(BROWSER(SP->browser), MUSIC_SOURCE(library),
574                                           SP->default_view_config_string);
575 267                 g_object_unref(library);
576 142         };
577 158         return MUSIC_VIEW(view);
578 17 };
579 356 #endif
580 17
581 void calliope_play_cd(Calliope *self, const char *device_path) {
582         // FIXME: make sure this isn't leaked
583         if (device_path==NULL) device_path="/dev/cdrom";
584         CdSource *cd=cd_source_new(device_path);
585         if (cd!=NULL)
586 402                 browser_add_source (BROWSER(SP->browser), MUSIC_SOURCE(cd), SP->default_view_config_string);
587 267         g_object_unref(cd);
588 17
589         // FIXME: lookup disk id already and change name
590         //static void cd_source_name_changed(MusicSource *source, char *name, GtkAction *action) {
591 267         //char *label=g_strdup_printf("Play '%s'",
592 17
593 };
594
595 267 MusicView *calliope_open_directory (Calliope *self, const char *path,
596 421                                     const char *view_config, GConfChangeSet *changeset) {
597 140         FileSource *source = file_source_new(path, changeset);
598 158         GtkWidget *view = NULL;
599 267
600 142         if (source!=NULL) {
601 421                 if (view_config == NULL) view_config = SP->default_view_config_string;
602
603                 view = browser_add_source (BROWSER(SP->browser), MUSIC_SOURCE(source), view_config);
604 267                 g_object_unref (source);
605 421
606                 return MUSIC_VIEW(view);
607         };
608
609         return NULL;
610 17 };
611
612 356 #ifndef LIBRARY_DISABLE
613 17 void calliope_import_directory(Calliope *self, const char *path, GConfChangeSet *changeset) {
614         Library *dest;
615         // FIXME: this won't work with no libraries open
616         GtkTreeModel *library_model=GTK_TREE_MODEL(BROWSER(SP->browser)->library_liststore);
617         //if (!gtk_tree_model_iter_has_child(library_model, NULL)) return;
618 267
619 17         GtkTreeIter iter; gtk_tree_model_iter_nth_child(library_model, &iter, NULL, 0);
620         gtk_tree_model_get(library_model, &iter, BROWSER_LIBRARY_LISTSTORE_COLUMN_SOURCE, &dest, -1);
621 267
622         Process *import_process = file_import_process_new(MUSIC_SOURCE(dest), changeset, NULL);
623 191         music_search_process_new (path, audio_extensions_list, NULL, import_process, NULL);
624 17 };
625 356 #endif
626 17
627 void calliope_add_music(Calliope *self, gboolean search) {
628         add_music_dialog_show(SP->add_music_dialog);
629 267
630 17         if (search)
631 267                 add_music_dialog_search_path(SP->add_music_dialog, "/");
632 17 };
633
634 319 void calliope_close_source (Calliope *self, MusicView *view) {
635 350         browser_close_source (BROWSER(SP->browser), music_view_get_source(view));
636 319 };
637
638 402
639 void calliope_set_default_view_config (Calliope *self, const char *config_string) {
640         if (SP->default_view_config_string != NULL) g_free (SP->default_view_config_string);
641         SP->default_view_config_string = g_strdup(config_string);
642 };
643
644 17 /////////// Actions
645 ///
646
647 356 #ifndef LIBRARY_DISABLE
648 17 static void change_library(Calliope *self, gboolean create) {
649         // FIXME: would it be more efficient to create this at the start of the
650         // program and reuse it? Who knows  ??
651         GtkWidget *file_dlg=gtk_file_chooser_dialog_new(
652           create?"Create new database file":"Select database file",
653 267           GTK_WINDOW(self), create?GTK_FILE_CHOOSER_ACTION_SAVE:GTK_FILE_CHOOSER_ACTION_OPEN,
654 17           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, create?GTK_STOCK_SAVE:GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
655         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(file_dlg), TRUE);
656 267
657 17         GtkWidget *store=gtk_check_button_new_with_label("Set as the default database");
658         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(file_dlg), store);
659 267
660 17         if (gtk_dialog_run(GTK_DIALOG(file_dlg))==GTK_RESPONSE_OK) {
661                 // FIXME: check if file is writable before blindly accepting, or test
662                 // & disable modification ??
663                 // FIXME: Also give errors (visible ones) if the file does not load
664 267                 char *file_name=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_dlg));
665                 if (create && g_file_test(file_name, G_FILE_TEST_EXISTS))
666                         g_unlink(file_name);
667                 calliope_open_library(self, file_name);
668                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(store))) {
669                         gconf_client_set_bool (gconf, KEY_DEFAULT_LIBRARY_ENABLE, TRUE, NULL);
670                         gconf_client_set_string (gconf, KEY_DEFAULT_LIBRARY_FILE_NAME, file_name, NULL);
671                 };
672 17                 g_free(file_name);
673         };
674
675 267         gtk_widget_destroy(file_dlg);
676 17 };
677
678 static void new_action(GtkAction *action, Calliope *self) {
679 267         change_library(self, 1);                };
680
681 17 static void open_action(GtkAction *action, Calliope *self) {
682 267         change_library(self, 0);                };
683
684 17 static void add_music_action(GtkAction *action, Calliope *self) {
685 267         calliope_add_music(self, FALSE);                };
686 356 #endif
687 267
688 17 static void settings_action(GtkAction *action, Calliope *self) {
689         run_global_settings_dialog();   };
690 267
691 446 static void status_action (GtkAction *action, Calliope *self) {
692         status_dialog_run ();
693 };
694
695 17
696 350 //static void play_cd_action(GtkAction *action, Calliope *self) {
697 //      calliope_play_cd(self, g_object_get_data(G_OBJECT(action), "device-path"));     };
698 17
699 static void open_directory_action(GtkAction *action, Calliope *self) {
700 199         GConfChangeSet *changeset = gconf_change_set_new();
701 267         const char *recent_path = gconf_client_get_string(gconf, KEY_FILE_SOURCE_RECENT_DIRECTORY,
702 199                                                           NULL);
703 17         if (recent_path!=NULL)
704 267                 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(SP->open_dir_file_chooser), recent_path);
705
706         if (run_dialog_with_panel(WID("play_dir_dialog"), WID("play_dir_notebook"),
707 199                                   get_import_settings_panel(changeset), import_settings_panel_title)
708 267                   == GTK_RESPONSE_OK) {
709 17                 // FIXME: this gives an error as engine has a client - but what to do !!
710                 if (!gconf_engine_commit_change_set(gconf_engine_get_default(), changeset,
711 267                                                     TRUE, NULL))
712 199                         g_warning("Error while committing change set");
713                 char *path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(SP->open_dir_file_chooser));
714 421                 calliope_open_directory (self, path, NULL, changeset);
715 199                 gconf_client_set_string (gconf, KEY_FILE_SOURCE_RECENT_DIRECTORY, path, NULL);
716                 g_free (path);
717 17         };
718 199         gconf_change_set_unref (changeset);
719 17 };
720
721 static void close_action(GtkAction *action, Calliope *self) {
722 267         browser_close_current_source (BROWSER(SP->browser));
723 172 };
724 17
725 static void exit_action(GtkAction *action, Calliope *self) {
726 267         gtk_object_destroy(GTK_OBJECT(self));
727 17 };
728 24

Loggerhead is a web-based interface for Bazaar branches