PlayQueue: Close the search entry on escape
[gmpc.git] / src / Widgets / gmpc-data-view.vala
blobf99f4bff05baa002411d3261a714b0d300c70c21
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 log(log_domain, LogLevelFlags.LEVEL_INFO, "Constructing dataview: "+name);
157 this.view_mode = mode;
158 this.uid = name;
159 // Connect row activated signal.
160 this.row_activated.connect(__row_activated);
161 this.key_press_event.connect(__key_press_event_callback);
162 this.button_press_event.connect(__button_press_event_callback);
163 this.button_release_event.connect(__button_release_event_callback);
164 // When it getst he destroy signal.
165 this.destroy.connect(column_store_state);
167 this.set_rules_hint(true);
168 this.set_enable_search(false);
170 this.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
171 this.set_fixed_height_mode(true);
173 // Create the view.
174 column_populate();
176 // Update the sorting when it changed.
177 this.notify["model"].connect((source) => {
178 // Only the SONG_LIST is sortable.
179 if(this.model is Gtk.TreeSortable && view_mode == ViewType.SONG_LIST) {
180 int sort_column = config.get_int_with_default(uid,
181 "sort-column", Gmpc.MpdData.ColumnTypes.COL_ICON_ID);
182 Gtk.SortType sort_order = (Gtk.SortType)config.get_int_with_default(uid,
183 "sort-order",(int)Gtk.SortType.ASCENDING);
185 (this.model as Gtk.TreeSortable).set_sort_column_id(
186 sort_column, sort_order
194 * Deconstructor.
196 ~DataView()
199 /** small wrapper.. */
200 private void playlist_editor_add_new(Gtk.Widget menu)
202 string playlist = menu.get_data("playlist");
203 selected_songs_add_to_playlist(playlist);
206 public void right_mouse_menu(Gtk.Menu menu)
208 int selected_rows = this.get_selection().count_selected_rows();
209 if(selected_rows == 1) {
210 Gtk.TreeModel model;
211 var path = this.get_selection().get_selected_rows(out model).first().data;
212 Gtk.TreeIter iter;
213 if(model.get_iter(out iter, path))
215 MPD.Data.Type row_type;
216 model.get(iter, Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
217 if(row_type == MPD.Data.Type.SONG) {
218 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.MEDIA_PLAY,null);
219 item.activate.connect((source)=>{
220 selected_songs_play();
222 menu.append(item);
224 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.INFO, null);
225 item.activate.connect((source)=>{
226 selected_songs_info();
228 menu.append(item);
233 if(view_mode == ViewType.PLAY_QUEUE || view_mode == ViewType.PLAYLIST)
235 if(selected_rows > 0) {
236 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.REMOVE,null);
237 item.activate.connect((source)=>{
238 selected_songs_remove();
240 menu.append(item);
242 }else{
243 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.ADD,null);
244 item.activate.connect((source)=>{
245 selected_songs_add();
247 menu.append(item);
248 item = new Gtk.ImageMenuItem.with_label(_("Replace"));
249 item.activate.connect((source)=>{
250 MPD.PlayQueue.clear(server);
251 selected_songs_add();
253 menu.append(item);
255 if(server.check_command_allowed("prioid") == MPD.Server.Command.ALLOWED)
257 menu.append( new Gtk.SeparatorMenuItem());
258 var item = new Gtk.MenuItem.with_label(_("Queue"));
259 item.activate.connect((source)=>{ selected_songs_raise_priority();});
260 menu.append(item);
262 if(view_mode == ViewType.PLAY_QUEUE) {
263 item = new Gtk.MenuItem.with_label(_("Dequeue"));
264 item.activate.connect((source)=>{ selected_songs_remove_priority();});
265 menu.append(item);
268 // Copy paste.
269 if(selected_rows > 0)
271 Gtk.MenuItem item;
272 item = new Gtk.SeparatorMenuItem();
273 menu.append(item);
274 if(view_mode == ViewType.PLAY_QUEUE || view_mode == ViewType.PLAYLIST) {
275 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.CUT, null);
276 item.activate.connect((source)=>{ selected_songs_paste_queue_cut();});
277 menu.append(item);
280 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.COPY, null);
281 item.activate.connect((source)=>{ selected_songs_paste_queue_copy();});
282 menu.append(item);
284 if( (view_mode == ViewType.PLAY_QUEUE ||
285 view_mode == ViewType.PLAYLIST )
286 && paste_queue != null) {
287 item = new Gtk.ImageMenuItem.with_label(_("Paste before"));
288 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
289 item.activate.connect((source)=>{ selected_songs_paste_before();});
290 menu.append(item);
292 item = new Gtk.ImageMenuItem.with_label(_("Paste after"));
293 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
294 item.activate.connect((source)=>{ selected_songs_paste_after();});
295 menu.append(item);
298 // Add to playlist integration.
301 if(selected_rows == 1)
303 Gtk.TreeModel model;
304 var list = get_selection().get_selected_rows(out model);
305 var path = list.first().data;
306 Gtk.TreeIter iter;
307 if(model.get_iter(out iter, path))
309 unowned MPD.Song? song;
310 model.get(iter, MpdData.ColumnTypes.COL_MPDSONG, out song);
311 if(song != null){
312 MpdInteraction.submenu_for_song(menu, song);
317 // Tool menu integration.
318 bool initial = false;
319 var item = new Gtk.MenuItem.with_label(_("Tools"));
320 var smenu = new Gtk.Menu();
321 item.set_submenu(smenu);
322 for(int i = 0; i < Gmpc.num_plugins; i++){
323 if(plugins[i].get_enabled() &&
324 plugins[i].browser_song_list_option_menu(this,smenu) > 0){
325 stdout.printf("adding entry\n");
326 initial = true;
329 if(initial) {
330 menu.append(new Gtk.SeparatorMenuItem());
331 menu.append(item);
334 Gmpc.Browser.PlaylistEditor.right_mouse(menu, playlist_editor_add_new);
338 item = new Gtk.MenuItem.with_label(_("Edit Columns"));
339 smenu = new Gtk.Menu();
340 item.set_submenu(smenu);
341 generate_column_selection_menu(smenu);
343 menu.append(item);
347 * Internal functions.
351 * Store the position, visibility and width of the columns
353 private void column_store_state()
355 // Save the position of the columns
356 var columns = get_columns();
357 int index = 0;
358 foreach(var column in columns)
360 int col_index = column.get_data("index");
361 int width = column.get_width();
362 config.set_int(uid+"-colpos", gmpc_data_view_col_names[col_index], index);
363 config.set_bool(uid+"-colshow", gmpc_data_view_col_names[col_index], column.visible);
364 // Only store width if bigger then 0.
365 if(width > 0 ) {
366 config.set_int(uid+"-colsize",gmpc_data_view_col_names[col_index], width);
368 index++;
371 if(this.model is Gtk.TreeSortable) {
372 int sort_column;
373 int sort_order;
374 if((this.model as Gtk.TreeSortable).get_sort_column_id(out sort_column, out sort_order)) {
375 config.set_int(uid, "sort-column", sort_column);
376 config.set_int(uid, "sort-order", sort_order);
380 // Hack to make vala not destroy the menu directly.
382 private void generate_column_selection_menu(Gtk.Menu menu)
384 foreach(var col in tree_columns)
386 int index = col.get_data("index");
387 // Do not show the icon id in the selection list.
388 if(gmpc_data_view_col_ids[index] == MpdData.ColumnTypes.COL_ICON_ID) continue;
389 var item = new Gtk.CheckMenuItem.with_label(FixGtk.gettext(gmpc_data_view_col_names[index]));
390 if(col.visible) {
391 item.set_active(true);
393 // On activation toggle the state.
394 item.activate.connect((source) => {
395 col.visible = (source as Gtk.CheckMenuItem).get_active();
397 menu.append(item);
402 private Gtk.Menu column_selection_menu = null;
403 private void column_show_selection_menu()
405 column_selection_menu = new Gtk.Menu();
406 generate_column_selection_menu(column_selection_menu);
407 column_selection_menu.show_all();
408 column_selection_menu.popup(null, null, null, 0, Gtk.get_current_event_time());
411 * Populate the treeview with the right columns.
412 * The treeview should have a name now.
414 private void column_populate()
416 for(int i = 0; i < NUM_COLS; i++)
418 Gtk.TreeViewColumn col = new Gtk.TreeViewColumn();
419 col.set_data("index", i);
420 if(gmpc_data_view_col_ids[i] == Gmpc.MpdData.ColumnTypes.COL_ICON_ID)
423 * Picture.
425 var renderer = new Gtk.CellRendererPixbuf();
426 renderer.xalign = 0.0f;
427 // Set up column
428 col.pack_start(renderer, true);
429 col.set_attributes(renderer, "icon-name", Gmpc.MpdData.ColumnTypes.COL_ICON_ID);
430 col.set_resizable(false);
431 col.set_fixed_width(20);
432 col.clickable = true;
433 } else {
435 * Text column
437 col.set_title(FixGtk.gettext(gmpc_data_view_col_names[i]));
438 var renderer = new Gtk.CellRendererText();
439 renderer.ellipsize = Pango.EllipsizeMode.END;
440 // Set up column
441 if(view_mode == ViewType.PLAY_QUEUE) {
442 renderer.weight_set = true;
443 renderer.style_set = true;
444 col.set_cell_data_func(renderer, highlight_row_current_song_playing);
446 col.pack_start(renderer, true);
447 col.set_attributes(renderer, "text", gmpc_data_view_col_ids[i]);
448 col.set_resizable(true);
450 int width = config.get_int_with_default(uid+"-colsize",
451 gmpc_data_view_col_names[i], default_column_width);
452 // Do not set to size 0, then revert back to 200.
453 col.set_fixed_width(width>0?width:default_column_width);
455 col.set_sizing(Gtk.TreeViewColumnSizing.FIXED);
456 col.set_reorderable(true);
458 // Sorting.
459 if(view_mode == ViewType.SONG_LIST) {
460 col.set_sort_indicator(true);
461 col.set_sort_column_id(gmpc_data_view_col_ids[i]);
464 // Fixed width.
465 int pos = config.get_int_with_default(uid+"-colpos",
466 gmpc_data_view_col_names[i], gmpc_data_view_col_position[i]);
467 this.tree_columns[pos] = col;
470 // Add the columns (in right order)
471 for(int i = 0; i < NUM_COLS; i++) {
472 int index = this.tree_columns[i].get_data("index");
473 this.insert_column(this.tree_columns[i], i);
474 this.tree_columns[i].set_visible(config.get_bool_with_default(uid+"-colshow",
475 gmpc_data_view_col_names[index], gmpc_data_view_col_enabled[index]));
484 * Function handles the row-activate signal.
486 private void __row_activated (Gtk.TreePath path, Gtk.TreeViewColumn col)
488 Gtk.TreeModel? model = this.get_model();
489 if(model != null)
491 MPD.Data.Type row_type;
492 Gtk.TreeIter iter;
493 if(!model.get_iter(out iter, path)) return;
495 model.get(iter, Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
496 if(view_mode == ViewType.PLAY_QUEUE) {
497 /* If we are play-queue, play the selected song. */
498 int song_id;
499 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
500 MPD.Player.play_id(Gmpc.server, song_id);
501 } else {
502 string song_path;
503 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
504 if(row_type == MPD.Data.Type.SONG) {
505 /* If we are a song browser, add the path and play it. */
506 MpdInteraction.play_path(song_path);
507 } else if (row_type == MPD.Data.Type.PLAYLIST) {
508 MPD.PlayQueue.queue_load_playlist(server, song_path);
509 MPD.PlayQueue.queue_commit(server);
515 * Check if current row is playing.
517 private void highlight_row_current_song_playing(
518 Gtk.CellLayout col,
519 Gtk.CellRenderer renderer,
520 Gtk.TreeModel model,
521 Gtk.TreeIter iter)
523 // The current song we make bold.
524 if(model is Gmpc.MpdData.ModelPlaylist &&
525 (model as Gmpc.MpdData.ModelPlaylist).is_current_song(iter)){
526 (renderer as Gtk.CellRendererText).weight = Pango.Weight.BOLD;
527 }else{
528 (renderer as Gtk.CellRendererText).weight = Pango.Weight.NORMAL;
531 // A prioritized song we make italic.
532 int prio = 0;
533 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_PRIORITY, out prio);
534 if(prio > 0) {
535 (renderer as Gtk.CellRendererText).style = Pango.Style.ITALIC;
536 }else {
537 (renderer as Gtk.CellRendererText).style = Pango.Style.NORMAL;
543 * Handle keyboard input.
545 private bool __key_press_event_callback_song_list(Gdk.EventKey event)
547 if(event.keyval == Gdk.Key_i)
549 return selected_songs_add();
551 else if (event.keyval == Gdk.Key_r)
553 // If there are songs selected, clear the play queue,
554 // and add selection.
555 if(this.get_selection().count_selected_rows() > 0) {
556 MPD.PlayQueue.clear(server);
557 return selected_songs_add();
560 return false;
563 * Keybinding for playlists.
565 private bool __key_press_event_callback_playlist(Gdk.EventKey event)
567 if (event.keyval == Gdk.Key_x)
569 // Cut (if available) into clipboard
570 selected_songs_paste_queue_cut();
572 else if (event.keyval == Gdk.Key_P)
574 // Paste before
575 return selected_songs_paste_before();
577 else if (event.keyval == Gdk.Key_p)
579 // Paste after
580 return selected_songs_paste_after();
582 else if (event.keyval == Gdk.Key_d)
584 if(!selected_songs_remove())
586 // Clear the playlist
587 MPD.Database.playlist_clear(server, playlist_name);
588 return true;
591 else if (event.keyval == Gdk.Key_D)
593 MPD.Database.playlist_clear(server, playlist_name);
594 return true;
596 return false;
598 private bool __key_press_event_callback_play_queue(Gdk.EventKey event)
600 if (event.keyval == Gdk.Key_Q)
602 // remove priority.
603 return selected_songs_remove_priority();
605 else if (event.keyval == Gdk.Key_x)
607 // Cut (if available) into clipboard
608 selected_songs_paste_queue_cut();
610 else if (event.keyval == Gdk.Key_P)
612 // Paste before
613 return selected_songs_paste_before();
615 else if (event.keyval == Gdk.Key_p)
617 // Paste after
618 return selected_songs_paste_after();
620 else if (event.keyval == Gdk.Key_d)
622 if(!selected_songs_remove())
624 // Detach model (for some reason keeping it attached
625 // Makes thing break, work-around for now)
626 // TODO: fixme
627 var model = get_model();
628 this.model = null;
629 // Clear
630 MPD.PlayQueue.clear(server);
631 // Re-add model
632 this.model = model;
633 return true;
636 else if (event.keyval == Gdk.Key_D)
638 var model = get_model();
639 this.model = null;
640 // Clear
641 MPD.PlayQueue.clear(server);
642 // Re-add model
643 this.model = model;
644 return true;
646 return false;
648 private bool __key_press_event_callback(Gdk.EventKey event)
650 if(event.keyval == Gdk.Key_j)
652 // Move cursor down.
653 move_cursor_down();
655 else if (event.keyval == Gdk.Key_k)
657 // Move cursor up
658 move_cursor_up();
660 else if (event.keyval == Gdk.Key_g)
662 move_cursor_top();
664 else if (event.keyval == Gdk.Key_G)
666 move_cursor_bottom();
668 else if(event.keyval == Gdk.Key_y)
670 // Copy data to clipboard
671 return selected_songs_paste_queue_copy();
673 else if (event.keyval == Gdk.Key_o)
675 return selected_songs_info();
677 else if (event.keyval == Gdk.Key_m)
679 // Configure columns
680 column_show_selection_menu();
681 return true;
683 else if (event.keyval == Gdk.Key_q)
685 // Raise priority.
686 return selected_songs_raise_priority();
688 else if (event.keyval == Gdk.Key_Menu)
690 __button_press_menu = new Gtk.Menu();
692 right_mouse_menu(__button_press_menu);
697 __button_press_menu.show_all();
698 __button_press_menu.popup(null, null,null,0, Gtk.get_current_event_time());
699 return true;
702 // Commands specific to play_queue
703 if(view_mode == ViewType.PLAY_QUEUE)
705 if(__key_press_event_callback_play_queue(event)) return true;
707 else if (view_mode == ViewType.PLAYLIST)
709 if(__key_press_event_callback_playlist(event)) return true;
711 else if (view_mode == ViewType.SONG_LIST)
713 if(__key_press_event_callback_song_list(event)) return true;
715 return false;
719 * Right mouse popup.
721 // Hack to stop vala from destroying my menu.
722 private Gtk.Menu __button_press_menu = null;
723 private bool __button_press_event_callback(Gdk.EventButton event)
725 if(event.button == 3)
727 __button_press_menu = new Gtk.Menu();
728 right_mouse_menu(__button_press_menu);
730 __button_press_menu.show_all();
731 __button_press_menu.popup(null, null,null, event.button, event.time);
732 return true;
734 return false;
736 private bool __button_release_event_callback(Gdk.EventButton event)
738 return false;
743 * Interaction on selected songs.
745 // Set priority on the selected songs.
746 private bool selected_songs_raise_priority()
748 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
749 var selection = this.get_selection();
751 if(selection.count_selected_rows() > 254) {
752 Gmpc.Messages.show(_("You can only queue 254 songs at the time."), Gmpc.Messages.Level.WARNING);
753 return false;
756 int priority = 255;
757 Gtk.TreeModel model;
758 foreach(var path in selection.get_selected_rows(out model))
760 Gtk.TreeIter iter;
761 if(model.get_iter(out iter, path))
763 MPD.Data.Type row_type;
764 int song_id;
765 model.get(iter,
766 Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id,
767 Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
768 // Only act on songs.
769 if(row_type != MPD.Data.Type.SONG) continue;
771 if(song_id >= 0) {
772 MPD.PlayQueue.set_priority(server, song_id, priority--);
773 } else {
774 string song_path;
775 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
776 song_id = MPD.PlayQueue.add_song_get_id(server,song_path);
777 MPD.PlayQueue.set_priority(server, song_id, priority--);
781 return true;
783 // Remove the set priority from the selected songs.
784 private bool selected_songs_remove_priority()
786 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
787 var selection = this.get_selection();
789 Gtk.TreeModel model;
790 foreach(var path in selection.get_selected_rows(out model))
792 Gtk.TreeIter iter;
793 if(model.get_iter(out iter, path))
795 int song_id;
796 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
797 if ( song_id >= 0 ) {
798 MPD.PlayQueue.set_priority(server, song_id, 0);
802 return true;
804 // Add the selected song
805 private bool selected_songs_add()
807 int added_rows = 0;
808 var selection = this.get_selection();
810 Gtk.TreeModel model;
811 foreach(var path in selection.get_selected_rows(out model))
813 Gtk.TreeIter iter;
814 if(model.get_iter(out iter, path))
816 string song_path;
817 MPD.Data.Type row_type;
818 model.get(iter,
819 Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type,
820 Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
821 if ( row_type == MPD.Data.Type.SONG || row_type == MPD.Data.Type.DIRECTORY) {
822 MPD.PlayQueue.queue_add_song(server, song_path);
823 added_rows++;
824 }else if (row_type == MPD.Data.Type.PLAYLIST) {
825 MPD.PlayQueue.queue_load_playlist(server, song_path);
826 added_rows++;
830 MPD.PlayQueue.queue_commit(server);
831 if(added_rows > 0) {
832 return true;
834 return false;
836 // Play the selected song
837 private bool selected_songs_play()
839 var selection = this.get_selection();
840 Gtk.TreeModel model;
841 foreach(var path in selection.get_selected_rows(out model))
843 Gtk.TreeIter iter;
844 if(model.get_iter(out iter, path))
846 if(view_mode == ViewType.PLAY_QUEUE) {
847 int song_id;
848 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
849 if(song_id >= 0){
850 MPD.Player.play_id(server, song_id);
851 return true;
853 }else{
854 string song_path;
855 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
856 MpdInteraction.play_path(song_path);
857 return true;
861 return selection.count_selected_rows() > 0;
863 // Remove the selected songs from the play queue.
864 private bool selected_songs_remove()
866 int deleted_rows = 0;
867 var selection = this.get_selection();
869 Gtk.TreeModel model;
870 var sel_rows = selection.get_selected_rows(out model);
871 sel_rows.reverse();
872 foreach(var path in sel_rows)
874 Gtk.TreeIter iter;
875 if(model.get_iter(out iter, path))
877 if(view_mode == ViewType.PLAY_QUEUE) {
878 int song_id;
879 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
880 MPD.PlayQueue.queue_delete_id(server, song_id);
881 }else if ( view_mode == ViewType.PLAYLIST) {
882 var indc = path.get_indices();
883 int song_pos = indc[0];
884 MPD.Database.playlist_list_delete(server, playlist_name, song_pos);
885 }else {
886 error("Invalid state, cannot call this here.");
888 deleted_rows++;
891 MPD.PlayQueue.queue_commit(server);
892 if(deleted_rows > 0) {
893 selection.unselect_all();
894 return true;
896 return false;
898 // Show the Information of the first selected song.
899 private bool selected_songs_info()
901 var selection = this.get_selection();
902 Gtk.TreeModel model;
903 foreach(var path in selection.get_selected_rows(out model))
905 Gtk.TreeIter iter;
906 if(model.get_iter(out iter, path))
908 unowned MPD.Song? song = null;
909 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_MPDSONG, out song);
910 if(song != null) {
911 Browser.Metadata.show();
912 Browser.Metadata.show_song(song);
913 return true;
917 return selection.count_selected_rows() > 0;
920 // Cut the selected songs in the play queue
921 private bool selected_songs_paste_queue_cut()
923 if(selected_songs_paste_queue_copy()){
924 selected_songs_remove();
926 return false;
928 // Copy the selected songs in the play queue
929 private bool selected_songs_paste_queue_copy()
931 var selection = this.get_selection();
932 // If nothing to copy , do nothing.
933 if(selection.count_selected_rows() == 0) return false;
934 // Clear paste_queue
935 paste_queue = null;
936 Gtk.TreeModel model;
937 foreach(var path in selection.get_selected_rows(out model))
939 Gtk.TreeIter iter;
940 if(model.get_iter(out iter, path))
942 string insert_path;
943 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_PATH, out insert_path);
944 paste_queue.prepend(insert_path);
947 paste_queue.reverse();
948 return true;
952 private bool selected_songs_paste_before()
954 var selection = this.get_selection();
955 // If nothing selected.
956 if(selection.count_selected_rows() == 0|| paste_queue == null)
958 return false;
960 Gtk.TreeModel model;
961 Gtk.TreeIter iter;
962 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
963 if(model.get_iter(out iter, path))
965 int songpos;
966 if( view_mode == ViewType.PLAY_QUEUE) {
967 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_POS, out songpos);
968 if(songpos> 0) {
969 songpos--;
970 paste_queue.reverse();
971 foreach(var fpath in paste_queue)
973 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
974 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
976 paste_queue.reverse();
978 }else if (view_mode == ViewType.PLAYLIST) {
979 int length = this.model.iter_n_children(null);
980 var indc = path.get_indices();
981 songpos = indc[0];
982 paste_queue.reverse();
983 foreach(var fpath in paste_queue)
985 MPD.Database.playlist_list_add(server, playlist_name, fpath);
986 MPD.Database.playlist_move(server, playlist_name, length,songpos);
987 length++;
989 paste_queue.reverse();
991 }else{
992 error("We should be able to be here.");
995 return true;
997 private bool selected_songs_paste_after()
999 var selection = this.get_selection();
1000 // If nothing selected.
1001 if(selection.count_selected_rows() == 0|| paste_queue == null)
1003 return false;
1005 Gtk.TreeModel model;
1006 Gtk.TreeIter iter;
1007 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
1008 if(model.get_iter(out iter, path))
1010 int songpos;
1011 if(view_mode == ViewType.PLAY_QUEUE) {
1012 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_POS, out songpos);
1013 if(songpos> 0) {
1014 paste_queue.reverse();
1015 foreach(var fpath in paste_queue)
1017 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
1018 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
1020 paste_queue.reverse();
1023 else if (view_mode == ViewType.PLAYLIST) {
1024 int length = this.model.iter_n_children(null);
1025 var indc = path.get_indices();
1026 songpos = indc[0]+1;
1027 paste_queue.reverse();
1028 foreach(var fpath in paste_queue)
1030 MPD.Database.playlist_list_add(server, playlist_name, fpath);
1031 MPD.Database.playlist_move(server, playlist_name, length,songpos);
1032 length++;
1034 paste_queue.reverse();
1035 }else{
1036 error("We should be able to be here.");
1040 return true;
1043 private bool selected_songs_add_to_playlist(string playlist)
1045 int songs_added = 0;
1046 var selection = this.get_selection();
1047 // If nothing selected.
1048 if(selection.count_selected_rows() == 0)
1050 return false;
1052 Gtk.TreeModel model;
1053 foreach (var path in selection.get_selected_rows(out model))
1055 Gtk.TreeIter iter;
1056 if(model.get_iter(out iter, path))
1058 string? song_path = null;
1059 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
1060 if(song_path != null) {
1061 MPD.Database.playlist_list_add(server, playlist, song_path);
1062 songs_added++;
1066 return (songs_added > 0);
1070 * Move
1072 private void move_cursor_down()
1074 Gtk.TreePath? path;
1075 Gtk.TreeViewColumn? col;
1076 this.get_cursor(out path, out col);
1077 if(path != null)
1079 Gtk.TreeIter iter;
1080 path.next();
1081 if(this.model.get_iter(out iter, path))
1083 this.set_cursor(path, col, false);
1087 private void move_cursor_top()
1089 Gtk.TreeViewColumn? col;
1090 var rows = this.model.iter_n_children(null);
1091 if(rows > 0){
1092 Gtk.TreePath? path;
1093 this.get_cursor(null, out col);
1094 path = new Gtk.TreePath.from_indices(0);
1095 this.set_cursor(path, col, false);
1098 private void move_cursor_bottom()
1100 Gtk.TreeViewColumn? col;
1101 var rows = this.model.iter_n_children(null);
1102 if(rows > 0){
1103 Gtk.TreePath? path;
1104 this.get_cursor(null, out col);
1105 path = new Gtk.TreePath.from_indices(rows-1);
1106 this.set_cursor(path, col, false);
1109 private void move_cursor_up()
1111 Gtk.TreePath? path;
1112 Gtk.TreeViewColumn? col;
1113 this.get_cursor(out path, out col);
1114 if(path != null)
1116 if(path.prev())
1118 this.set_cursor(path, col, false);