Filter mod key, when mapping keybindings
[gmpc.git] / src / Widgets / gmpc-data-view.vala
blobcba387d445c2bd506ca4410628d9d0ce25808e47
1 using Gmpc;
3 using Gtk;
5 public List<string> paste_queue;
6 const string log_domain = "Gmpc.DataView";
8 /** The Default column width. */
9 const int default_column_width = 200;
11 /**
12 * List of columns.
13 * * List of column ids to show.
14 * * Name of each column
15 * * Default set of enabled columns.
17 const int NUM_COLS = 20;
18 const int[] gmpc_data_view_col_ids = {
19 Gmpc.MpdData.ColumnTypes.COL_MARKUP,
20 Gmpc.MpdData.ColumnTypes.COL_SONG_ARTIST, /* album name */
21 Gmpc.MpdData.ColumnTypes.COL_SONG_ALBUM, /* album name */
22 Gmpc.MpdData.ColumnTypes.COL_SONG_TITLE, /* song title */
23 Gmpc.MpdData.ColumnTypes.COL_SONG_TITLEFILE, /* song title */
24 Gmpc.MpdData.ColumnTypes.COL_SONG_GENRE, /* song genre */
25 Gmpc.MpdData.ColumnTypes.COL_SONG_TRACK, /* song track */
26 Gmpc.MpdData.ColumnTypes.COL_SONG_NAME, /* stream name */
27 Gmpc.MpdData.ColumnTypes.COL_SONG_COMPOSER, /* composer name */
28 Gmpc.MpdData.ColumnTypes.COL_SONG_PERFORMER, /* performer */
29 Gmpc.MpdData.ColumnTypes.COL_SONG_DATE, /* date */
30 Gmpc.MpdData.ColumnTypes.COL_SONG_LENGTH_FORMAT, /* length formatted */
31 Gmpc.MpdData.ColumnTypes.COL_SONG_DISC, /* disc */
32 Gmpc.MpdData.ColumnTypes.COL_SONG_COMMENT, /* comment */
33 Gmpc.MpdData.ColumnTypes.COL_ICON_ID, /* icon id */
34 Gmpc.MpdData.ColumnTypes.COL_SONG_POS,
35 Gmpc.MpdData.ColumnTypes.COL_SONG_ALBUMARTIST,
36 Gmpc.MpdData.ColumnTypes.COL_PATH_EXTENSION, /* Extension */
37 Gmpc.MpdData.ColumnTypes.COL_PATH_DIRECTORY, /* Directory */
38 Gmpc.MpdData.ColumnTypes.COL_SONG_PRIORITY
40 const string[] gmpc_data_view_col_names = {
41 N_("Markup"),
42 N_("Artist"),
43 N_("Album"),
44 N_("Title"),
45 N_("File"),
46 N_("Genre"),
47 N_("Track"),
48 N_("Name"),
49 N_("Composer"),
50 N_("Performer"),
51 N_("Date"),
52 N_("Duration"),
53 N_("Disc"),
54 N_("Comment"),
55 N_("Icon Id"),
56 N_("Position"),
57 N_("AlbumArtist"),
58 N_("Extension"),
59 N_("Directory"),
60 N_("Priority")
63 const bool[] gmpc_data_view_col_enabled = {
64 false,//"Markup",
65 true, //"Artist",
66 true,//"Album",
67 true,//"Title",
68 false,//"File",
69 false,//"Genre",
70 false,//"Track",
71 false,//"Name",
72 false,//"Composer",
73 false,//"Performer",
74 false,//"Date",
75 false,//"Duration",
76 false,//"Disc",
77 false,//"Comment",
78 true,//"Icon Id"
79 false,//"Position"
80 false,//"AlbumArtist"
81 false,//Extension
82 false,//Directory
83 false//Priority
87 const int[] gmpc_data_view_col_position = {
88 14,//"Markup",
89 3, //"Artist",
90 2,//"Album",
91 1,//"Title",
92 4,//"File",
93 5,//"Genre",
94 6,//"Track",
95 7,//"Name",
96 8,//"Composer",
97 9,//"Performer",
98 10,//"Date",
99 11,//"Duration",
100 12,//"Disc",
101 13,//"Comment",
102 0,//"Icon Id"
103 15, // "Position"
104 18, // "AlbumArtist"
105 16,// Extension
106 17, // Directory
110 public class Gmpc.DataView : Gtk.TreeView
112 public enum ViewType {
113 SONG_LIST,
114 PLAY_QUEUE,
115 PLAYLIST
118 private Gtk.TreeViewColumn[] tree_columns = new Gtk.TreeViewColumn[NUM_COLS];
120 * If we are a play-queue we should treat the content.
121 * slightly different.
122 * e.g. add-replace will be play-crop
124 public ViewType view_mode {get; set;default=ViewType.SONG_LIST;}
126 * If we 'view' a playlist, we need to know the name of the play queue.
127 * This can be set here.
129 private string __playlist_name = null;
130 public string playlist_name {
131 get{
132 return __playlist_name;
134 set{
135 if(view_mode == ViewType.PLAYLIST) {
136 stdout.printf("Set playlist name: %s\n", value);
137 __playlist_name = value;
138 }else {
139 error("Cannot set playlist name on non-playlist view.");
145 * The name of the treeview.
146 * This is used to store the column layout.
148 public string uid {get; set; default="default";}
152 * Construction function.
154 public DataView(string name, ViewType mode = ViewType.SONG_LIST)
156 this.can_focus = true;
157 log(log_domain, LogLevelFlags.LEVEL_INFO, "Constructing dataview: "+name);
158 this.view_mode = mode;
159 this.uid = name;
160 // Connect row activated signal.
161 this.row_activated.connect(__row_activated);
162 this.key_press_event.connect(__key_press_event_callback);
163 this.button_press_event.connect(__button_press_event_callback);
164 this.button_release_event.connect(__button_release_event_callback);
165 // When it getst he destroy signal.
166 this.destroy.connect(column_store_state);
168 this.set_rules_hint(true);
169 this.set_enable_search(false);
171 this.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
172 this.set_fixed_height_mode(true);
174 // Create the view.
175 column_populate();
177 // Update the sorting when it changed.
178 this.notify["model"].connect((source) => {
179 // Only the SONG_LIST is sortable.
180 if(this.model is Gtk.TreeSortable && view_mode == ViewType.SONG_LIST) {
181 int sort_column = config.get_int_with_default(uid,
182 "sort-column", Gmpc.MpdData.ColumnTypes.COL_ICON_ID);
183 Gtk.SortType sort_order = (Gtk.SortType)config.get_int_with_default(uid,
184 "sort-order",(int)Gtk.SortType.ASCENDING);
186 (this.model as Gtk.TreeSortable).set_sort_column_id(
187 sort_column, sort_order
195 * Deconstructor.
197 ~DataView()
200 /** small wrapper.. */
201 private void playlist_editor_add_new(Gtk.Widget menu)
203 string playlist = menu.get_data("playlist");
204 selected_songs_add_to_playlist(playlist);
207 public void right_mouse_menu(Gtk.Menu menu)
209 int selected_rows = this.get_selection().count_selected_rows();
210 if(selected_rows == 1) {
211 Gtk.TreeModel model;
212 var path = this.get_selection().get_selected_rows(out model).first().data;
213 Gtk.TreeIter iter;
214 if(model.get_iter(out iter, path))
216 MPD.Data.Type row_type;
217 model.get(iter, Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
218 if(row_type == MPD.Data.Type.SONG) {
219 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.MEDIA_PLAY,null);
220 item.activate.connect((source)=>{
221 selected_songs_play();
223 menu.append(item);
225 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.INFO, null);
226 item.activate.connect((source)=>{
227 selected_songs_info();
229 menu.append(item);
234 if(view_mode == ViewType.PLAY_QUEUE || view_mode == ViewType.PLAYLIST)
236 if(selected_rows > 0) {
237 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.REMOVE,null);
238 item.activate.connect((source)=>{
239 selected_songs_remove();
241 menu.append(item);
243 item = new Gtk.ImageMenuItem.with_label(_("Crop"));
244 item.set_image(new Gtk.Image.from_stock(Gtk.Stock.CUT, Gtk.IconSize.MENU));
245 item.activate.connect((source)=>{ selected_songs_crop(); });
246 menu.append(item);
248 }else{
249 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.ADD,null);
250 item.activate.connect((source)=>{
251 selected_songs_add();
253 menu.append(item);
254 item = new Gtk.ImageMenuItem.with_label(_("Replace"));
255 item.activate.connect((source)=>{
256 MPD.PlayQueue.clear(server);
257 selected_songs_add();
259 menu.append(item);
261 if(server.check_command_allowed("prioid") == MPD.Server.Command.ALLOWED)
263 menu.append( new Gtk.SeparatorMenuItem());
264 var item = new Gtk.MenuItem.with_label(_("Queue"));
265 item.activate.connect((source)=>{ selected_songs_raise_priority();});
266 menu.append(item);
268 if(view_mode == ViewType.PLAY_QUEUE) {
269 item = new Gtk.MenuItem.with_label(_("Dequeue"));
270 item.activate.connect((source)=>{ selected_songs_remove_priority();});
271 menu.append(item);
274 // Copy paste.
275 if(selected_rows > 0)
277 Gtk.MenuItem item;
278 item = new Gtk.SeparatorMenuItem();
279 menu.append(item);
280 if(view_mode == ViewType.PLAY_QUEUE || view_mode == ViewType.PLAYLIST) {
281 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.CUT, null);
282 item.activate.connect((source)=>{ selected_songs_paste_queue_cut();});
283 menu.append(item);
286 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.COPY, null);
287 item.activate.connect((source)=>{ selected_songs_paste_queue_copy();});
288 menu.append(item);
290 if( (view_mode == ViewType.PLAY_QUEUE ||
291 view_mode == ViewType.PLAYLIST )
292 && paste_queue != null) {
293 item = new Gtk.ImageMenuItem.with_label(_("Paste before"));
294 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
295 item.activate.connect((source)=>{ selected_songs_paste_before();});
296 menu.append(item);
298 item = new Gtk.ImageMenuItem.with_label(_("Paste after"));
299 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
300 item.activate.connect((source)=>{ selected_songs_paste_after();});
301 menu.append(item);
304 // Add to playlist integration.
307 if(selected_rows == 1)
309 Gtk.TreeModel model;
310 var list = get_selection().get_selected_rows(out model);
311 var path = list.first().data;
312 Gtk.TreeIter iter;
313 if(model.get_iter(out iter, path))
315 unowned MPD.Song? song;
316 model.get(iter, MpdData.ColumnTypes.COL_MPDSONG, out song);
317 if(song != null){
318 MpdInteraction.submenu_for_song(menu, song);
323 // Tool menu integration.
324 bool initial = false;
325 var item = new Gtk.MenuItem.with_label(_("Tools"));
326 var smenu = new Gtk.Menu();
327 item.set_submenu(smenu);
328 for(int i = 0; i < Gmpc.num_plugins; i++){
329 if(plugins[i].get_enabled() &&
330 plugins[i].browser_song_list_option_menu(this,smenu) > 0){
331 stdout.printf("adding entry\n");
332 initial = true;
335 if(initial) {
336 menu.append(new Gtk.SeparatorMenuItem());
337 menu.append(item);
340 Gmpc.Browser.PlaylistEditor.right_mouse(menu, playlist_editor_add_new);
344 item = new Gtk.MenuItem.with_label(_("Edit Columns"));
345 smenu = new Gtk.Menu();
346 item.set_submenu(smenu);
347 generate_column_selection_menu(smenu);
349 menu.append(item);
353 * Internal functions.
357 * Store the position, visibility and width of the columns
359 private void column_store_state()
361 // Save the position of the columns
362 var columns = get_columns();
363 int index = 0;
364 foreach(var column in columns)
366 int col_index = column.get_data("index");
367 int width = column.get_width();
368 config.set_int(uid+"-colpos", gmpc_data_view_col_names[col_index], index);
369 config.set_bool(uid+"-colshow", gmpc_data_view_col_names[col_index], column.visible);
370 // Only store width if bigger then 0.
371 if(width > 0 ) {
372 config.set_int(uid+"-colsize",gmpc_data_view_col_names[col_index], width);
374 index++;
377 if(this.model is Gtk.TreeSortable) {
378 int sort_column;
379 int sort_order;
380 if((this.model as Gtk.TreeSortable).get_sort_column_id(out sort_column, out sort_order)) {
381 config.set_int(uid, "sort-column", sort_column);
382 config.set_int(uid, "sort-order", sort_order);
386 // Hack to make vala not destroy the menu directly.
388 private void generate_column_selection_menu(Gtk.Menu menu)
390 foreach(var col in tree_columns)
392 int index = col.get_data("index");
393 // Do not show the icon id in the selection list.
394 if(gmpc_data_view_col_ids[index] == MpdData.ColumnTypes.COL_ICON_ID) continue;
395 var item = new Gtk.CheckMenuItem.with_label(FixGtk.gettext(gmpc_data_view_col_names[index]));
396 if(col.visible) {
397 item.set_active(true);
399 // On activation toggle the state.
400 item.activate.connect((source) => {
401 col.visible = (source as Gtk.CheckMenuItem).get_active();
403 menu.append(item);
408 private Gtk.Menu column_selection_menu = null;
409 private void column_show_selection_menu()
411 column_selection_menu = new Gtk.Menu();
412 generate_column_selection_menu(column_selection_menu);
413 column_selection_menu.show_all();
414 column_selection_menu.popup(null, null, null, 0, Gtk.get_current_event_time());
417 * Populate the treeview with the right columns.
418 * The treeview should have a name now.
420 private void column_populate()
422 for(int i = 0; i < NUM_COLS; i++)
424 Gtk.TreeViewColumn col = new Gtk.TreeViewColumn();
425 col.set_data("index", i);
426 if(gmpc_data_view_col_ids[i] == Gmpc.MpdData.ColumnTypes.COL_ICON_ID)
429 * Picture.
431 var renderer = new Gtk.CellRendererPixbuf();
432 renderer.xalign = 0.0f;
433 // Set up column
434 col.pack_start(renderer, true);
435 col.set_attributes(renderer, "icon-name", Gmpc.MpdData.ColumnTypes.COL_ICON_ID);
436 col.set_resizable(false);
437 col.set_fixed_width(20);
438 col.clickable = true;
439 } else {
441 * Text column
443 col.set_title(FixGtk.gettext(gmpc_data_view_col_names[i]));
444 var renderer = new Gtk.CellRendererText();
445 renderer.ellipsize = Pango.EllipsizeMode.END;
446 // Set up column
447 if(view_mode == ViewType.PLAY_QUEUE) {
448 renderer.weight_set = true;
449 renderer.style_set = true;
450 col.set_cell_data_func(renderer, highlight_row_current_song_playing);
452 col.pack_start(renderer, true);
453 col.set_attributes(renderer, "text", gmpc_data_view_col_ids[i]);
454 col.set_resizable(true);
456 int width = config.get_int_with_default(uid+"-colsize",
457 gmpc_data_view_col_names[i], default_column_width);
458 // Do not set to size 0, then revert back to 200.
459 col.set_fixed_width(width>0?width:default_column_width);
461 col.set_sizing(Gtk.TreeViewColumnSizing.FIXED);
462 col.set_reorderable(true);
464 // Sorting.
465 if(view_mode == ViewType.SONG_LIST) {
466 col.set_sort_indicator(true);
467 col.set_sort_column_id(gmpc_data_view_col_ids[i]);
470 // Fixed width.
471 int pos = config.get_int_with_default(uid+"-colpos",
472 gmpc_data_view_col_names[i], gmpc_data_view_col_position[i]);
473 this.tree_columns[pos] = col;
476 // Add the columns (in right order)
477 for(int i = 0; i < NUM_COLS; i++) {
478 int index = this.tree_columns[i].get_data("index");
479 this.insert_column(this.tree_columns[i], i);
480 this.tree_columns[i].set_visible(config.get_bool_with_default(uid+"-colshow",
481 gmpc_data_view_col_names[index], gmpc_data_view_col_enabled[index]));
490 * Function handles the row-activate signal.
492 private void __row_activated (Gtk.TreePath path, Gtk.TreeViewColumn col)
494 Gtk.TreeModel? model = this.get_model();
495 if(model != null)
497 MPD.Data.Type row_type;
498 Gtk.TreeIter iter;
499 if(!model.get_iter(out iter, path)) return;
501 model.get(iter, Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
502 if(view_mode == ViewType.PLAY_QUEUE) {
503 /* If we are play-queue, play the selected song. */
504 int song_id;
505 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
506 MPD.Player.play_id(Gmpc.server, song_id);
507 } else {
508 string song_path;
509 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
510 if(row_type == MPD.Data.Type.SONG) {
511 /* If we are a song browser, add the path and play it. */
512 MpdInteraction.play_path(song_path);
513 } else if (row_type == MPD.Data.Type.PLAYLIST) {
514 MPD.PlayQueue.queue_load_playlist(server, song_path);
515 MPD.PlayQueue.queue_commit(server);
522 * Check if current row is playing.
524 private void highlight_row_current_song_playing(
525 Gtk.CellLayout col,
526 Gtk.CellRenderer renderer,
527 Gtk.TreeModel model,
528 Gtk.TreeIter iter)
530 // The current song we make bold.
531 if(model is Gmpc.MpdData.ModelPlaylist &&
532 (model as Gmpc.MpdData.ModelPlaylist).is_current_song(iter)){
533 (renderer as Gtk.CellRendererText).weight = Pango.Weight.BOLD;
534 }else{
535 (renderer as Gtk.CellRendererText).weight = Pango.Weight.NORMAL;
538 // A prioritized song we make italic.
539 int prio = 0;
540 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_PRIORITY, out prio);
541 if(prio > 0) {
542 (renderer as Gtk.CellRendererText).style = Pango.Style.ITALIC;
543 }else {
544 (renderer as Gtk.CellRendererText).style = Pango.Style.NORMAL;
550 * Check if modifier is pressed.
552 private bool __key_mod_pressed(Gdk.EventKey event)
554 return (event.state&Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK;
558 * Handle keyboard input.
560 private bool __key_press_event_callback_song_list(Gdk.EventKey event)
562 if(event.keyval == Gdk.Key_i && !__key_mod_pressed(event))
564 return selected_songs_add();
566 else if (event.keyval == Gdk.Key_r && !__key_mod_pressed(event))
568 // If there are songs selected, clear the play queue,
569 // and add selection.
570 if(this.get_selection().count_selected_rows() > 0) {
571 MPD.PlayQueue.clear(server);
572 return selected_songs_add();
575 return false;
578 * Keybinding for playlists.
580 private bool __key_press_event_callback_playlist(Gdk.EventKey event)
582 if (event.keyval == Gdk.Key_x && !__key_mod_pressed(event))
584 // Cut (if available) into clipboard
585 selected_songs_paste_queue_cut();
587 else if (event.keyval == Gdk.Key_X && !__key_mod_pressed(event))
589 // Crop selected songs.
590 selected_songs_crop();
592 else if (event.keyval == Gdk.Key_P && !__key_mod_pressed(event))
594 // Paste before
595 return selected_songs_paste_before();
597 else if (event.keyval == Gdk.Key_p && !__key_mod_pressed(event))
599 // Paste after
600 return selected_songs_paste_after();
602 else if (event.keyval == Gdk.Key_d && !__key_mod_pressed(event))
604 if(!selected_songs_remove())
606 // Clear the playlist
607 MPD.Database.playlist_clear(server, playlist_name);
608 return true;
611 else if (event.keyval == Gdk.Key_D && !__key_mod_pressed(event))
613 MPD.Database.playlist_clear(server, playlist_name);
614 return true;
616 return false;
618 private bool __key_press_event_callback_play_queue(Gdk.EventKey event)
620 if (event.keyval == Gdk.Key_Q && !__key_mod_pressed(event))
622 // remove priority.
623 return selected_songs_remove_priority();
625 else if (__key_mod_pressed(event) &&
626 event.keyval == Gdk.Key_X)
628 stdout.printf("Clear everything but playing song\n");
629 // Select the playing song:
630 unowned MPD.Song? song = server.playlist_get_current_song();
631 if(song != null) {
632 var path = new Gtk.TreePath.from_indices(song.pos);
633 this.get_selection().unselect_all();
634 this.get_selection().select_path(path);
635 selected_songs_crop();
637 return false;
639 else if (event.keyval == Gdk.Key_x && !__key_mod_pressed(event))
641 // Cut (if available) into clipboard
642 selected_songs_paste_queue_cut();
644 else if (event.keyval == Gdk.Key_X && !__key_mod_pressed(event))
646 // Crop selected songs.
647 selected_songs_crop();
649 else if (event.keyval == Gdk.Key_P && !__key_mod_pressed(event))
651 // Paste before
652 return selected_songs_paste_before();
654 else if (event.keyval == Gdk.Key_p && !__key_mod_pressed(event))
656 // Paste after
657 return selected_songs_paste_after();
659 else if (event.keyval == Gdk.Key_d && !__key_mod_pressed(event))
661 if(!selected_songs_remove())
663 // Detach model (for some reason keeping it attached
664 // Makes thing break, work-around for now)
665 // TODO: fixme
666 var model = get_model();
667 this.model = null;
668 // Clear
669 MPD.PlayQueue.clear(server);
670 // Re-add model
671 this.model = model;
672 return true;
675 else if (event.keyval == Gdk.Key_D && !__key_mod_pressed(event))
677 var model = get_model();
678 this.model = null;
679 // Clear
680 MPD.PlayQueue.clear(server);
681 // Re-add model
682 this.model = model;
683 return true;
685 return false;
687 private bool __key_press_event_callback(Gdk.EventKey event)
689 if(event.keyval == Gdk.Key_h && !__key_mod_pressed(event))
691 move_cursor_left();
693 else if (event.keyval == Gdk.Key_l && !__key_mod_pressed(event))
695 move_cursor_right();
697 else if(event.keyval == Gdk.Key_j && !__key_mod_pressed(event))
699 // Move cursor down.
700 move_cursor_down();
702 else if (event.keyval == Gdk.Key_k && !__key_mod_pressed(event))
704 // Move cursor up
705 move_cursor_up();
707 else if (event.keyval == Gdk.Key_g && !__key_mod_pressed(event))
709 move_cursor_top();
711 else if (event.keyval == Gdk.Key_G && !__key_mod_pressed(event))
713 move_cursor_bottom();
715 else if(event.keyval == Gdk.Key_y && !__key_mod_pressed(event))
717 // Copy data to clipboard
718 return selected_songs_paste_queue_copy();
720 else if (event.keyval == Gdk.Key_o && !__key_mod_pressed(event))
722 return selected_songs_info();
724 else if (event.keyval == Gdk.Key_m && !__key_mod_pressed(event))
726 // Configure columns
727 column_show_selection_menu();
728 return true;
730 else if (event.keyval == Gdk.Key_q && !__key_mod_pressed(event))
732 // Raise priority.
733 return selected_songs_raise_priority();
735 else if (event.keyval == Gdk.Key_Menu && !__key_mod_pressed(event))
737 __button_press_menu = new Gtk.Menu();
739 right_mouse_menu(__button_press_menu);
744 __button_press_menu.show_all();
745 __button_press_menu.popup(null, null,null,0, Gtk.get_current_event_time());
746 return true;
749 // Commands specific to play_queue
750 if(view_mode == ViewType.PLAY_QUEUE)
752 if(__key_press_event_callback_play_queue(event)) return true;
754 else if (view_mode == ViewType.PLAYLIST)
756 if(__key_press_event_callback_playlist(event)) return true;
758 else if (view_mode == ViewType.SONG_LIST)
760 if(__key_press_event_callback_song_list(event)) return true;
762 return false;
766 * Right mouse popup.
768 // Hack to stop vala from destroying my menu.
769 private Gtk.Menu __button_press_menu = null;
770 private bool __button_press_event_callback(Gdk.EventButton event)
772 if(event.button == 3)
774 __button_press_menu = new Gtk.Menu();
775 right_mouse_menu(__button_press_menu);
777 __button_press_menu.show_all();
778 __button_press_menu.popup(null, null,null, event.button, event.time);
779 return true;
781 return false;
783 private bool __button_release_event_callback(Gdk.EventButton event)
785 return false;
790 * Interaction on selected songs.
792 // Set priority on the selected songs.
793 private bool selected_songs_raise_priority()
795 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
796 var selection = this.get_selection();
798 if(selection.count_selected_rows() > 254) {
799 Gmpc.Messages.show(_("You can only queue 254 songs at the time."), Gmpc.Messages.Level.WARNING);
800 return false;
803 int priority = 255;
804 Gtk.TreeModel model;
805 foreach(var path in selection.get_selected_rows(out model))
807 Gtk.TreeIter iter;
808 if(model.get_iter(out iter, path))
810 MPD.Data.Type row_type;
811 int song_id;
812 model.get(iter,
813 Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id,
814 Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
815 // Only act on songs.
816 if(row_type != MPD.Data.Type.SONG) continue;
818 if(song_id >= 0) {
819 MPD.PlayQueue.set_priority(server, song_id, priority--);
820 } else {
821 string song_path;
822 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
823 song_id = MPD.PlayQueue.add_song_get_id(server,song_path);
824 MPD.PlayQueue.set_priority(server, song_id, priority--);
828 return true;
830 // Remove the set priority from the selected songs.
831 private bool selected_songs_remove_priority()
833 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
834 var selection = this.get_selection();
836 Gtk.TreeModel model;
837 foreach(var path in selection.get_selected_rows(out model))
839 Gtk.TreeIter iter;
840 if(model.get_iter(out iter, path))
842 int song_id;
843 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
844 if ( song_id >= 0 ) {
845 MPD.PlayQueue.set_priority(server, song_id, 0);
849 return true;
851 // Add the selected song
852 private bool selected_songs_add()
854 int added_rows = 0;
855 var selection = this.get_selection();
857 Gtk.TreeModel model;
858 foreach(var path in selection.get_selected_rows(out model))
860 Gtk.TreeIter iter;
861 if(model.get_iter(out iter, path))
863 string song_path;
864 MPD.Data.Type row_type;
865 model.get(iter,
866 Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type,
867 Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
868 if ( row_type == MPD.Data.Type.SONG || row_type == MPD.Data.Type.DIRECTORY) {
869 MPD.PlayQueue.queue_add_song(server, song_path);
870 added_rows++;
871 }else if (row_type == MPD.Data.Type.PLAYLIST) {
872 MPD.PlayQueue.queue_load_playlist(server, song_path);
873 added_rows++;
877 MPD.PlayQueue.queue_commit(server);
878 if(added_rows > 0) {
879 return true;
881 return false;
883 // Play the selected song
884 private bool selected_songs_play()
886 var selection = this.get_selection();
887 Gtk.TreeModel model;
888 foreach(var path in selection.get_selected_rows(out model))
890 Gtk.TreeIter iter;
891 if(model.get_iter(out iter, path))
893 if(view_mode == ViewType.PLAY_QUEUE) {
894 int song_id;
895 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
896 if(song_id >= 0){
897 MPD.Player.play_id(server, song_id);
898 return true;
900 }else{
901 string song_path;
902 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
903 MpdInteraction.play_path(song_path);
904 return true;
908 return selection.count_selected_rows() > 0;
910 // Crop the selected songs.
911 private void selected_songs_crop()
913 int deleted_rows = 0;
914 var selection = this.get_selection();
916 model = this.get_model();
917 List<int> items_to_delete = new List<int>();
919 TreeIter iter;
920 if(model.get_iter_first(out iter))
923 if(!this.get_selection().iter_is_selected(iter))
925 if(view_mode == ViewType.PLAY_QUEUE) {
926 int song_id;
927 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
928 items_to_delete.prepend(song_id);
929 }else if ( view_mode == ViewType.PLAYLIST) {
930 var path = model.get_path(iter);
931 var indc = path.get_indices();
932 int song_pos = indc[0];
933 items_to_delete.prepend(song_pos);
936 }while(model.iter_next(ref iter));
938 foreach(int id in items_to_delete)
940 if(view_mode == ViewType.PLAY_QUEUE)
942 MPD.PlayQueue.queue_delete_id(server, id);
943 } else if (view_mode == ViewType.PLAYLIST)
945 MPD.Database.playlist_list_delete(server, playlist_name, id);
948 MPD.PlayQueue.queue_commit(server);
950 // Remove the selected songs from the play queue.
951 private bool selected_songs_remove()
953 int deleted_rows = 0;
954 var selection = this.get_selection();
956 Gtk.TreeModel model;
957 var sel_rows = selection.get_selected_rows(out model);
958 sel_rows.reverse();
959 foreach(var path in sel_rows)
961 Gtk.TreeIter iter;
962 if(model.get_iter(out iter, path))
964 if(view_mode == ViewType.PLAY_QUEUE) {
965 int song_id;
966 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
967 MPD.PlayQueue.queue_delete_id(server, song_id);
968 }else if ( view_mode == ViewType.PLAYLIST) {
969 var indc = path.get_indices();
970 int song_pos = indc[0];
971 MPD.Database.playlist_list_delete(server, playlist_name, song_pos);
972 }else {
973 error("Invalid state, cannot call this here.");
975 deleted_rows++;
978 MPD.PlayQueue.queue_commit(server);
979 if(deleted_rows > 0) {
980 selection.unselect_all();
981 return true;
983 return false;
985 // Show the Information of the first selected song.
986 private bool selected_songs_info()
988 var selection = this.get_selection();
989 Gtk.TreeModel model;
990 foreach(var path in selection.get_selected_rows(out model))
992 Gtk.TreeIter iter;
993 if(model.get_iter(out iter, path))
995 unowned MPD.Song? song = null;
996 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_MPDSONG, out song);
997 if(song != null) {
998 Browser.Metadata.show();
999 Browser.Metadata.show_song(song);
1000 return true;
1004 return selection.count_selected_rows() > 0;
1007 // Cut the selected songs in the play queue
1008 private bool selected_songs_paste_queue_cut()
1010 if(selected_songs_paste_queue_copy()){
1011 selected_songs_remove();
1013 return false;
1015 // Copy the selected songs in the play queue
1016 private bool selected_songs_paste_queue_copy()
1018 var selection = this.get_selection();
1019 // If nothing to copy , do nothing.
1020 if(selection.count_selected_rows() == 0) return false;
1021 // Clear paste_queue
1022 paste_queue = null;
1023 Gtk.TreeModel model;
1024 foreach(var path in selection.get_selected_rows(out model))
1026 Gtk.TreeIter iter;
1027 if(model.get_iter(out iter, path))
1029 string insert_path;
1030 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_PATH, out insert_path);
1031 paste_queue.prepend(insert_path);
1034 paste_queue.reverse();
1035 return true;
1039 private bool selected_songs_paste_before()
1041 var selection = this.get_selection();
1042 // If nothing selected.
1043 if(selection.count_selected_rows() == 0|| paste_queue == null)
1045 return false;
1047 Gtk.TreeModel model;
1048 Gtk.TreeIter iter;
1049 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
1050 if(model.get_iter(out iter, path))
1052 int songpos;
1053 if( view_mode == ViewType.PLAY_QUEUE) {
1054 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_POS, out songpos);
1055 if(songpos> 0) {
1056 songpos--;
1057 paste_queue.reverse();
1058 foreach(var fpath in paste_queue)
1060 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
1061 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
1063 paste_queue.reverse();
1065 }else if (view_mode == ViewType.PLAYLIST) {
1066 int length = this.model.iter_n_children(null);
1067 var indc = path.get_indices();
1068 songpos = indc[0];
1069 paste_queue.reverse();
1070 foreach(var fpath in paste_queue)
1072 MPD.Database.playlist_list_add(server, playlist_name, fpath);
1073 MPD.Database.playlist_move(server, playlist_name, length,songpos);
1074 length++;
1076 paste_queue.reverse();
1078 }else{
1079 error("We should be able to be here.");
1082 return true;
1084 private bool selected_songs_paste_after()
1086 var selection = this.get_selection();
1087 // If nothing selected.
1088 if(selection.count_selected_rows() == 0|| paste_queue == null)
1090 return false;
1092 Gtk.TreeModel model;
1093 Gtk.TreeIter iter;
1094 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
1095 if(model.get_iter(out iter, path))
1097 int songpos;
1098 if(view_mode == ViewType.PLAY_QUEUE) {
1099 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_POS, out songpos);
1100 if(songpos> 0) {
1101 paste_queue.reverse();
1102 foreach(var fpath in paste_queue)
1104 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
1105 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
1107 paste_queue.reverse();
1110 else if (view_mode == ViewType.PLAYLIST) {
1111 int length = this.model.iter_n_children(null);
1112 var indc = path.get_indices();
1113 songpos = indc[0]+1;
1114 paste_queue.reverse();
1115 foreach(var fpath in paste_queue)
1117 MPD.Database.playlist_list_add(server, playlist_name, fpath);
1118 MPD.Database.playlist_move(server, playlist_name, length,songpos);
1119 length++;
1121 paste_queue.reverse();
1122 }else{
1123 error("We should be able to be here.");
1127 return true;
1130 private bool selected_songs_add_to_playlist(string playlist)
1132 int songs_added = 0;
1133 var selection = this.get_selection();
1134 // If nothing selected.
1135 if(selection.count_selected_rows() == 0)
1137 return false;
1139 Gtk.TreeModel model;
1140 foreach (var path in selection.get_selected_rows(out model))
1142 Gtk.TreeIter iter;
1143 if(model.get_iter(out iter, path))
1145 string? song_path = null;
1146 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
1147 if(song_path != null) {
1148 MPD.Database.playlist_list_add(server, playlist, song_path);
1149 songs_added++;
1153 return (songs_added > 0);
1157 * Move
1159 private void move_cursor_down()
1161 Gtk.TreePath? path;
1162 Gtk.TreeViewColumn? col;
1163 this.get_cursor(out path, out col);
1164 if(path != null)
1166 Gtk.TreeIter iter;
1167 path.next();
1168 if(this.model.get_iter(out iter, path))
1170 this.set_cursor(path, col, false);
1174 private void move_cursor_top()
1176 Gtk.TreeViewColumn? col;
1177 var rows = this.model.iter_n_children(null);
1178 if(rows > 0){
1179 Gtk.TreePath? path;
1180 this.get_cursor(null, out col);
1181 path = new Gtk.TreePath.from_indices(0);
1182 this.set_cursor(path, col, false);
1185 private void move_cursor_bottom()
1187 Gtk.TreeViewColumn? col;
1188 var rows = this.model.iter_n_children(null);
1189 if(rows > 0){
1190 Gtk.TreePath? path;
1191 this.get_cursor(null, out col);
1192 path = new Gtk.TreePath.from_indices(rows-1);
1193 this.set_cursor(path, col, false);
1196 private void move_cursor_up()
1198 Gtk.TreePath? path;
1199 Gtk.TreeViewColumn? col;
1200 this.get_cursor(out path, out col);
1201 if(path != null)
1203 if(path.prev())
1205 this.set_cursor(path, col, false);
1209 private void move_cursor_left()
1211 if(this is Gtk.Scrollable)
1213 var hadj = this.get_hadjustment();
1214 hadj.set_value(hadj.value-hadj.step_increment);
1217 private void move_cursor_right()
1219 if(this is Gtk.Scrollable)
1221 var hadj = this.get_hadjustment();
1222 hadj.set_value(hadj.value+hadj.step_increment);