Remove Gmpc.MpdData.Treeview,
[gmpc.git] / src / Widgets / gmpc-data-view.vala
blob8004cd3b7475a629509aa02ae1025d86f27556f5
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 __playlist_name = value;
137 }else {
138 error("Cannot set playlist name on non-playlist view.");
144 * The name of the treeview.
145 * This is used to store the column layout.
147 public string uid {get; set; default="default";}
151 * Construction function.
153 public DataView(string name, ViewType mode = ViewType.SONG_LIST)
155 log(log_domain, LogLevelFlags.LEVEL_INFO, "Constructing dataview: "+name);
156 this.view_mode = mode;
157 this.uid = name;
158 // Connect row activated signal.
159 this.row_activated.connect(__row_activated);
160 this.key_press_event.connect(__key_press_event_callback);
161 this.button_press_event.connect(__button_press_event_callback);
162 this.button_release_event.connect(__button_release_event_callback);
163 // When it getst he destroy signal.
164 this.destroy.connect(column_store_state);
166 this.set_rules_hint(true);
167 this.set_enable_search(false);
169 this.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
170 this.set_fixed_height_mode(true);
172 // Create the view.
173 column_populate();
175 // Update the sorting when it changed.
176 this.notify["model"].connect((source) => {
177 // Only the SONG_LIST is sortable.
178 if(this.model is Gtk.TreeSortable && view_mode == ViewType.SONG_LIST) {
179 int sort_column = config.get_int_with_default(uid,
180 "sort-column", Gmpc.MpdData.ColumnTypes.COL_ICON_ID);
181 Gtk.SortType sort_order = (Gtk.SortType)config.get_int_with_default(uid,
182 "sort-order",(int)Gtk.SortType.ASCENDING);
184 (this.model as Gtk.TreeSortable).set_sort_column_id(
185 sort_column, sort_order
193 * Deconstructor.
195 ~DataView()
198 /** small wrapper.. */
199 private void playlist_editor_add_new(Gtk.Widget menu)
201 string playlist = menu.get_data("playlist");
202 selected_songs_add_to_playlist(playlist);
205 public void right_mouse_menu(Gtk.Menu menu)
207 int selected_rows = this.get_selection().count_selected_rows();
208 if(selected_rows == 1) {
209 Gtk.TreeModel model;
210 var path = this.get_selection().get_selected_rows(out model).first().data;
211 Gtk.TreeIter iter;
212 if(model.get_iter(out iter, path))
214 MPD.Data.Type row_type;
215 model.get(iter, Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
216 if(row_type == MPD.Data.Type.SONG) {
217 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.MEDIA_PLAY,null);
218 item.activate.connect((source)=>{
219 selected_songs_play();
221 menu.append(item);
223 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.INFO, null);
224 item.activate.connect((source)=>{
225 selected_songs_info();
227 menu.append(item);
232 if(view_mode == ViewType.PLAY_QUEUE || view_mode == ViewType.PLAYLIST)
234 if(selected_rows > 0) {
235 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.REMOVE,null);
236 item.activate.connect((source)=>{
237 selected_songs_remove();
239 menu.append(item);
241 }else{
242 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.ADD,null);
243 item.activate.connect((source)=>{
244 selected_songs_add();
246 menu.append(item);
247 item = new Gtk.ImageMenuItem.with_label(_("Replace"));
248 item.activate.connect((source)=>{
249 MPD.PlayQueue.clear(server);
250 selected_songs_add();
252 menu.append(item);
254 if(server.check_command_allowed("prioid") == MPD.Server.Command.ALLOWED)
256 menu.append( new Gtk.SeparatorMenuItem());
257 var item = new Gtk.MenuItem.with_label(_("Queue"));
258 item.activate.connect((source)=>{ selected_songs_raise_priority();});
259 menu.append(item);
261 if(view_mode == ViewType.PLAY_QUEUE) {
262 item = new Gtk.MenuItem.with_label(_("Dequeue"));
263 item.activate.connect((source)=>{ selected_songs_remove_priority();});
264 menu.append(item);
267 // Copy paste.
268 if(selected_rows > 0)
270 Gtk.MenuItem item;
271 item = new Gtk.SeparatorMenuItem();
272 menu.append(item);
273 if(view_mode == ViewType.PLAY_QUEUE) {
274 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.CUT, null);
275 item.activate.connect((source)=>{ selected_songs_paste_queue_cut();});
276 menu.append(item);
279 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.COPY, null);
280 item.activate.connect((source)=>{ selected_songs_paste_queue_copy();});
281 menu.append(item);
283 if(view_mode == ViewType.PLAY_QUEUE && paste_queue != null) {
284 item = new Gtk.ImageMenuItem.with_label(_("Paste before"));
285 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
286 item.activate.connect((source)=>{ selected_songs_paste_before();});
287 menu.append(item);
289 item = new Gtk.ImageMenuItem.with_label(_("Paste after"));
290 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
291 item.activate.connect((source)=>{ selected_songs_paste_after();});
292 menu.append(item);
295 // Add to playlist integration.
298 if(selected_rows == 1)
300 Gtk.TreeModel model;
301 var list = get_selection().get_selected_rows(out model);
302 var path = list.first().data;
303 Gtk.TreeIter iter;
304 if(model.get_iter(out iter, path))
306 unowned MPD.Song? song;
307 model.get(iter, MpdData.ColumnTypes.COL_MPDSONG, out song);
308 if(song != null){
309 MpdInteraction.submenu_for_song(menu, song);
314 // Tool menu integration.
315 bool initial = false;
316 var item = new Gtk.MenuItem.with_label(_("Tools"));
317 var smenu = new Gtk.Menu();
318 item.set_submenu(smenu);
319 for(int i = 0; i < Gmpc.num_plugins; i++){
320 if(plugins[i].get_enabled() &&
321 plugins[i].browser_song_list_option_menu(this,smenu) > 0){
322 stdout.printf("adding entry\n");
323 initial = true;
326 if(initial) {
327 menu.append(new Gtk.SeparatorMenuItem());
328 menu.append(item);
331 Gmpc.Browser.PlaylistEditor.right_mouse(menu, playlist_editor_add_new);
335 item = new Gtk.MenuItem.with_label(_("Edit Columns"));
336 smenu = new Gtk.Menu();
337 item.set_submenu(smenu);
338 generate_column_selection_menu(smenu);
340 // item.activate.connect((source)=>{ column_show_selection_menu();});
341 menu.append(item);
345 * Internal functions.
349 * Store the position, visibility and width of the columns
351 private void column_store_state()
353 // Save the position of the columns
354 var columns = get_columns();
355 int index = 0;
356 foreach(var column in columns)
358 int col_index = column.get_data("index");
359 int width = column.get_width();
360 config.set_int(uid+"-colpos", gmpc_data_view_col_names[col_index], index);
361 config.set_bool(uid+"-colshow", gmpc_data_view_col_names[col_index], column.visible);
362 // Only store width if bigger then 0.
363 if(width > 0 ) {
364 config.set_int(uid+"-colsize",gmpc_data_view_col_names[col_index], width);
366 index++;
369 if(this.model is Gtk.TreeSortable) {
370 int sort_column;
371 int sort_order;
372 if((this.model as Gtk.TreeSortable).get_sort_column_id(out sort_column, out sort_order)) {
373 config.set_int(uid, "sort-column", sort_column);
374 config.set_int(uid, "sort-order", sort_order);
378 // Hack to make vala not destroy the menu directly.
380 private void generate_column_selection_menu(Gtk.Menu menu)
382 foreach(var col in tree_columns)
384 int index = col.get_data("index");
385 // Do not show the icon id in the selection list.
386 if(gmpc_data_view_col_ids[index] == MpdData.ColumnTypes.COL_ICON_ID) continue;
387 var item = new Gtk.CheckMenuItem.with_label(FixGtk.gettext(gmpc_data_view_col_names[index]));
388 if(col.visible) {
389 item.set_active(true);
391 // On activation toggle the state.
392 item.activate.connect((source) => {
393 col.visible = (source as Gtk.CheckMenuItem).get_active();
395 menu.append(item);
400 private Gtk.Menu column_selection_menu = null;
401 private void column_show_selection_menu()
403 column_selection_menu = new Gtk.Menu();
404 generate_column_selection_menu(column_selection_menu);
405 column_selection_menu.show_all();
406 column_selection_menu.popup(null, null, null, 0, Gtk.get_current_event_time());
409 * Populate the treeview with the right columns.
410 * The treeview should have a name now.
412 private void column_populate()
414 for(int i = 0; i < NUM_COLS; i++)
416 Gtk.TreeViewColumn col = new Gtk.TreeViewColumn();
417 col.set_data("index", i);
418 if(gmpc_data_view_col_ids[i] == Gmpc.MpdData.ColumnTypes.COL_ICON_ID)
421 * Picture.
423 var renderer = new Gtk.CellRendererPixbuf();
424 renderer.xalign = 0.0f;
425 // Set up column
426 col.pack_start(renderer, true);
427 col.set_attributes(renderer, "icon-name", Gmpc.MpdData.ColumnTypes.COL_ICON_ID);
428 col.set_resizable(false);
429 col.set_fixed_width(20);
430 col.clickable = true;
431 } else {
433 * Text column
435 col.set_title(FixGtk.gettext(gmpc_data_view_col_names[i]));
436 var renderer = new Gtk.CellRendererText();
437 renderer.ellipsize = Pango.EllipsizeMode.END;
438 // Set up column
439 if(view_mode == ViewType.PLAY_QUEUE) {
440 renderer.weight_set = true;
441 renderer.style_set = true;
442 col.set_cell_data_func(renderer, highlight_row_current_song_playing);
444 col.pack_start(renderer, true);
445 col.set_attributes(renderer, "text", gmpc_data_view_col_ids[i]);
446 col.set_resizable(true);
448 int width = config.get_int_with_default(uid+"-colsize",
449 gmpc_data_view_col_names[i], default_column_width);
450 // Do not set to size 0, then revert back to 200.
451 col.set_fixed_width(width>0?width:default_column_width);
453 col.set_sizing(Gtk.TreeViewColumnSizing.FIXED);
454 col.set_reorderable(true);
456 // Sorting.
457 if(view_mode == ViewType.SONG_LIST) {
458 col.set_sort_indicator(true);
459 col.set_sort_column_id(gmpc_data_view_col_ids[i]);
462 // Fixed width.
463 int pos = config.get_int_with_default(uid+"-colpos",
464 gmpc_data_view_col_names[i], gmpc_data_view_col_position[i]);
465 this.tree_columns[pos] = col;
468 // Add the columns (in right order)
469 for(int i = 0; i < NUM_COLS; i++) {
470 int index = this.tree_columns[i].get_data("index");
471 this.insert_column(this.tree_columns[i], i);
472 this.tree_columns[i].set_visible(config.get_bool_with_default(uid+"-colshow",
473 gmpc_data_view_col_names[index], gmpc_data_view_col_enabled[index]));
482 * Function handles the row-activate signal.
484 private void __row_activated (Gtk.TreePath path, Gtk.TreeViewColumn col)
486 Gtk.TreeModel? model = this.get_model();
487 if(model != null)
489 MPD.Data.Type row_type;
490 Gtk.TreeIter iter;
491 if(!model.get_iter(out iter, path)) return;
493 model.get(iter, Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
494 if(view_mode == ViewType.PLAY_QUEUE) {
495 /* If we are play-queue, play the selected song. */
496 int song_id;
497 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
498 MPD.Player.play_id(Gmpc.server, song_id);
499 } else {
500 string song_path;
501 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
502 if(row_type == MPD.Data.Type.SONG) {
503 /* If we are a song browser, add the path and play it. */
504 MpdInteraction.play_path(song_path);
505 } else if (row_type == MPD.Data.Type.PLAYLIST) {
506 MPD.PlayQueue.queue_load_playlist(server, song_path);
507 MPD.PlayQueue.queue_commit(server);
513 * Check if current row is playing.
515 private void highlight_row_current_song_playing(
516 Gtk.CellLayout col,
517 Gtk.CellRenderer renderer,
518 Gtk.TreeModel model,
519 Gtk.TreeIter iter)
521 // The current song we make bold.
522 if(model is Gmpc.MpdData.ModelPlaylist &&
523 (model as Gmpc.MpdData.ModelPlaylist).is_current_song(iter)){
524 (renderer as Gtk.CellRendererText).weight = Pango.Weight.BOLD;
525 }else{
526 (renderer as Gtk.CellRendererText).weight = Pango.Weight.NORMAL;
529 // A prioritized song we make italic.
530 int prio = 0;
531 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_PRIORITY, out prio);
532 if(prio > 0) {
533 (renderer as Gtk.CellRendererText).style = Pango.Style.ITALIC;
534 }else {
535 (renderer as Gtk.CellRendererText).style = Pango.Style.NORMAL;
541 * Handle keyboard input.
543 private bool __key_press_event_callback_song_list(Gdk.EventKey event)
545 if(event.keyval == Gdk.Key_i)
547 return selected_songs_add();
549 else if (event.keyval == Gdk.Key_r)
551 // If there are songs selected, clear the play queue,
552 // and add selection.
553 if(this.get_selection().count_selected_rows() > 0) {
554 MPD.PlayQueue.clear(server);
555 return selected_songs_add();
558 return false;
560 private bool __key_press_event_callback_play_queue(Gdk.EventKey event)
562 if (event.keyval == Gdk.Key_Q)
564 // remove priority.
565 return selected_songs_remove_priority();
567 else 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 // Detach model (for some reason keeping it attached
587 // Makes thing break, work-around for now)
588 // TODO: fixme
589 var model = get_model();
590 this.model = null;
591 // Clear
592 MPD.PlayQueue.clear(server);
593 // Re-add model
594 this.model = model;
595 return true;
598 return false;
600 private bool __key_press_event_callback(Gdk.EventKey event)
602 if(event.keyval == Gdk.Key_j)
604 // Move cursor down.
605 move_cursor_down();
607 else if (event.keyval == Gdk.Key_k)
609 // Move cursor up
610 move_cursor_up();
612 else if(event.keyval == Gdk.Key_y)
614 // Copy data to clipboard
615 return selected_songs_paste_queue_copy();
617 else if (event.keyval == Gdk.Key_o)
619 return selected_songs_info();
621 else if (event.keyval == Gdk.Key_Escape)
625 else if (event.keyval == Gdk.Key_m)
627 // Configure columns
628 column_show_selection_menu();
629 return true;
631 else if (event.keyval == Gdk.Key_q)
633 // Raise priority.
634 return selected_songs_raise_priority();
636 else if (event.keyval == Gdk.Key_Menu)
638 __button_press_menu = new Gtk.Menu();
640 right_mouse_menu(__button_press_menu);
645 __button_press_menu.show_all();
646 __button_press_menu.popup(null, null,null,0, Gtk.get_current_event_time());
647 return true;
650 // Commands specific to play_queue
651 if(view_mode == ViewType.PLAY_QUEUE)
653 if(__key_press_event_callback_play_queue(event)) return true;
655 else
657 if(__key_press_event_callback_song_list(event)) return true;
659 return false;
663 * Right mouse popup.
665 // Hack to stop vala from destroying my menu.
666 private Gtk.Menu __button_press_menu = null;
667 private bool __button_press_event_callback(Gdk.EventButton event)
669 if(event.button == 3)
671 __button_press_menu = new Gtk.Menu();
672 right_mouse_menu(__button_press_menu);
674 __button_press_menu.show_all();
675 __button_press_menu.popup(null, null,null, event.button, event.time);
676 return true;
678 return false;
680 private bool __button_release_event_callback(Gdk.EventButton event)
682 return false;
687 * Interaction on selected songs.
689 // Set priority on the selected songs.
690 private bool selected_songs_raise_priority()
692 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
693 var selection = this.get_selection();
695 if(selection.count_selected_rows() > 254) {
696 Gmpc.Messages.show(_("You can only queue 254 songs at the time."), Gmpc.Messages.Level.WARNING);
697 return false;
700 int priority = 255;
701 Gtk.TreeModel model;
702 foreach(var path in selection.get_selected_rows(out model))
704 Gtk.TreeIter iter;
705 if(model.get_iter(out iter, path))
707 MPD.Data.Type row_type;
708 int song_id;
709 model.get(iter,
710 Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id,
711 Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
712 // Only act on songs.
713 if(row_type != MPD.Data.Type.SONG) continue;
715 if(song_id >= 0) {
716 MPD.PlayQueue.set_priority(server, song_id, priority--);
717 } else {
718 string song_path;
719 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
720 song_id = MPD.PlayQueue.add_song_get_id(server,song_path);
721 MPD.PlayQueue.set_priority(server, song_id, priority--);
725 return true;
727 // Remove the set priority from the selected songs.
728 private bool selected_songs_remove_priority()
730 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
731 var selection = this.get_selection();
733 Gtk.TreeModel model;
734 foreach(var path in selection.get_selected_rows(out model))
736 Gtk.TreeIter iter;
737 if(model.get_iter(out iter, path))
739 int song_id;
740 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
741 if ( song_id >= 0 ) {
742 MPD.PlayQueue.set_priority(server, song_id, 0);
746 return true;
748 // Add the selected song
749 private bool selected_songs_add()
751 int added_rows = 0;
752 var selection = this.get_selection();
754 Gtk.TreeModel model;
755 foreach(var path in selection.get_selected_rows(out model))
757 Gtk.TreeIter iter;
758 if(model.get_iter(out iter, path))
760 string song_path;
761 MPD.Data.Type row_type;
762 model.get(iter,
763 Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type,
764 Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
765 if ( row_type == MPD.Data.Type.SONG || row_type == MPD.Data.Type.DIRECTORY) {
766 MPD.PlayQueue.queue_add_song(server, song_path);
767 added_rows++;
768 }else if (row_type == MPD.Data.Type.PLAYLIST) {
769 MPD.PlayQueue.queue_load_playlist(server, song_path);
770 added_rows++;
774 MPD.PlayQueue.queue_commit(server);
775 if(added_rows > 0) {
776 return true;
778 return false;
780 // Play the selected song
781 private bool selected_songs_play()
783 var selection = this.get_selection();
784 Gtk.TreeModel model;
785 foreach(var path in selection.get_selected_rows(out model))
787 Gtk.TreeIter iter;
788 if(model.get_iter(out iter, path))
790 if(view_mode == ViewType.PLAY_QUEUE) {
791 int song_id;
792 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
793 if(song_id >= 0){
794 MPD.Player.play_id(server, song_id);
795 return true;
797 }else{
798 string song_path;
799 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
800 MpdInteraction.play_path(song_path);
801 return true;
805 return selection.count_selected_rows() > 0;
807 // Remove the selected songs from the play queue.
808 private bool selected_songs_remove()
810 int deleted_rows = 0;
811 var selection = this.get_selection();
813 Gtk.TreeModel model;
814 foreach(var path in selection.get_selected_rows(out model))
816 Gtk.TreeIter iter;
817 if(model.get_iter(out iter, path))
819 int song_id;
820 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
821 MPD.PlayQueue.queue_delete_id(server, song_id);
822 deleted_rows++;
825 MPD.PlayQueue.queue_commit(server);
826 if(deleted_rows > 0) {
827 selection.unselect_all();
828 return true;
830 return false;
832 // Show the Information of the first selected song.
833 private bool selected_songs_info()
835 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 unowned MPD.Song? song = null;
843 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_MPDSONG, out song);
844 if(song != null) {
845 Browser.Metadata.show();
846 Browser.Metadata.show_song(song);
847 return true;
851 return selection.count_selected_rows() > 0;
854 // Cut the selected songs in the play queue
855 private bool selected_songs_paste_queue_cut()
857 if(selected_songs_paste_queue_copy()){
858 selected_songs_remove();
860 return false;
862 // Copy the selected songs in the play queue
863 private bool selected_songs_paste_queue_copy()
865 var selection = this.get_selection();
866 // If nothing to copy , do nothing.
867 if(selection.count_selected_rows() == 0) return false;
868 // Clear paste_queue
869 paste_queue = null;
870 Gtk.TreeModel model;
871 foreach(var path in selection.get_selected_rows(out model))
873 Gtk.TreeIter iter;
874 if(model.get_iter(out iter, path))
876 string insert_path;
877 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_PATH, out insert_path);
878 paste_queue.prepend(insert_path);
881 paste_queue.reverse();
882 return true;
886 private bool selected_songs_paste_before()
888 var selection = this.get_selection();
889 // If nothing selected.
890 if(selection.count_selected_rows() == 0|| paste_queue == null)
892 return false;
894 Gtk.TreeModel model;
895 Gtk.TreeIter iter;
896 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
897 if(model.get_iter(out iter, path))
899 int songpos;
900 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_POS, out songpos);
901 if(songpos> 0) {
902 songpos--;
903 paste_queue.reverse();
904 foreach(var fpath in paste_queue)
906 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
907 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
909 paste_queue.reverse();
912 return true;
914 private bool selected_songs_paste_after()
916 var selection = this.get_selection();
917 // If nothing selected.
918 if(selection.count_selected_rows() == 0|| paste_queue == null)
920 return false;
922 Gtk.TreeModel model;
923 Gtk.TreeIter iter;
924 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
925 if(model.get_iter(out iter, path))
927 int songpos;
928 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_POS, out songpos);
929 if(songpos> 0) {
930 paste_queue.reverse();
931 foreach(var fpath in paste_queue)
933 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
934 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
936 paste_queue.reverse();
939 return true;
942 private bool selected_songs_add_to_playlist(string playlist)
944 int songs_added = 0;
945 var selection = this.get_selection();
946 // If nothing selected.
947 if(selection.count_selected_rows() == 0)
949 return false;
951 Gtk.TreeModel model;
952 foreach (var path in selection.get_selected_rows(out model))
954 Gtk.TreeIter iter;
955 if(model.get_iter(out iter, path))
957 string? song_path = null;
958 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
959 if(song_path != null) {
960 MPD.Database.playlist_list_add(server, playlist, song_path);
961 songs_added++;
965 return (songs_added > 0);
969 * Move
971 private void move_cursor_down()
973 Gtk.TreePath? path;
974 Gtk.TreeViewColumn? col;
975 this.get_cursor(out path, out col);
976 if(path != null)
978 Gtk.TreeIter iter;
979 path.next();
980 if(this.model.get_iter(out iter, path))
982 this.set_cursor(path, col, false);
986 private void move_cursor_up()
988 Gtk.TreePath? path;
989 Gtk.TreeViewColumn? col;
990 this.get_cursor(out path, out col);
991 if(path != null)
993 if(path.prev())
995 this.set_cursor(path, col, false);