Do not restore last-column size. Should fix expanding now-playing.
[gmpc.git] / src / Widgets / gmpc-data-view.vala
blob81de26bf8b091e78bae6b545b8b9959882224e8e
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();
258 MPD.Player.play(server);
260 menu.append(item);
262 if(server.check_command_allowed("prioid") == MPD.Server.Command.ALLOWED)
264 menu.append( new Gtk.SeparatorMenuItem());
265 var item = new Gtk.MenuItem.with_label(_("Queue"));
266 item.activate.connect((source)=>{ selected_songs_raise_priority();});
267 menu.append(item);
269 if(view_mode == ViewType.PLAY_QUEUE) {
270 item = new Gtk.MenuItem.with_label(_("Dequeue"));
271 item.activate.connect((source)=>{ selected_songs_remove_priority();});
272 menu.append(item);
275 // Copy paste.
276 if(selected_rows > 0)
278 Gtk.MenuItem item;
279 item = new Gtk.SeparatorMenuItem();
280 menu.append(item);
281 if(view_mode == ViewType.PLAY_QUEUE || view_mode == ViewType.PLAYLIST) {
282 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.CUT, null);
283 item.activate.connect((source)=>{ selected_songs_paste_queue_cut();});
284 menu.append(item);
287 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.COPY, null);
288 item.activate.connect((source)=>{ selected_songs_paste_queue_copy();});
289 menu.append(item);
291 if( (view_mode == ViewType.PLAY_QUEUE ||
292 view_mode == ViewType.PLAYLIST )
293 && paste_queue != null) {
294 item = new Gtk.ImageMenuItem.with_label(_("Paste before"));
295 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
296 item.activate.connect((source)=>{ selected_songs_paste_before();});
297 menu.append(item);
299 item = new Gtk.ImageMenuItem.with_label(_("Paste after"));
300 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
301 item.activate.connect((source)=>{ selected_songs_paste_after();});
302 menu.append(item);
305 // Add to playlist integration.
308 if(selected_rows == 1)
310 Gtk.TreeModel model;
311 var list = get_selection().get_selected_rows(out model);
312 var path = list.first().data;
313 Gtk.TreeIter iter;
314 if(model.get_iter(out iter, path))
316 unowned MPD.Song? song;
317 model.get(iter, MpdData.ColumnTypes.COL_MPDSONG, out song);
318 if(song != null){
319 MpdInteraction.submenu_for_song(menu, song);
324 // Tool menu integration.
325 bool initial = false;
326 var item = new Gtk.MenuItem.with_label(_("Tools"));
327 var smenu = new Gtk.Menu();
328 item.set_submenu(smenu);
329 for(int i = 0; i < Gmpc.num_plugins; i++){
330 if(plugins[i].get_enabled() &&
331 plugins[i].browser_song_list_option_menu(this,smenu) > 0){
332 stdout.printf("adding entry\n");
333 initial = true;
336 if(initial) {
337 menu.append(new Gtk.SeparatorMenuItem());
338 menu.append(item);
341 Gmpc.Browser.PlaylistEditor.right_mouse(menu, playlist_editor_add_new);
345 item = new Gtk.MenuItem.with_label(_("Edit Columns"));
346 smenu = new Gtk.Menu();
347 item.set_submenu(smenu);
348 generate_column_selection_menu(smenu);
350 menu.append(item);
354 * Internal functions.
358 * Store the position, visibility and width of the columns
360 private void column_store_state()
362 // Save the position of the columns
363 var columns = get_columns();
364 int index = 0;
365 foreach(var column in columns)
367 int col_index = column.get_data("index");
368 int width = column.get_width();
369 config.set_int(uid+"-colpos", gmpc_data_view_col_names[col_index], index);
370 config.set_bool(uid+"-colshow", gmpc_data_view_col_names[col_index], column.visible);
371 // Only store width if bigger then 0.
372 if(width > 0 ) {
373 config.set_int(uid+"-colsize",gmpc_data_view_col_names[col_index], width);
375 index++;
378 if(this.model is Gtk.TreeSortable) {
379 int sort_column;
380 int sort_order;
381 if((this.model as Gtk.TreeSortable).get_sort_column_id(out sort_column, out sort_order)) {
382 config.set_int(uid, "sort-column", sort_column);
383 config.set_int(uid, "sort-order", sort_order);
387 // Hack to make vala not destroy the menu directly.
389 private void generate_column_selection_menu(Gtk.Menu menu)
391 foreach(var col in tree_columns)
393 int index = col.get_data("index");
394 // Do not show the icon id in the selection list.
395 if(gmpc_data_view_col_ids[index] == MpdData.ColumnTypes.COL_ICON_ID) continue;
396 var item = new Gtk.CheckMenuItem.with_label(FixGtk.gettext(gmpc_data_view_col_names[index]));
397 if(col.visible) {
398 item.set_active(true);
400 // On activation toggle the state.
401 item.activate.connect((source) => {
402 col.visible = (source as Gtk.CheckMenuItem).get_active();
404 menu.append(item);
409 private Gtk.Menu column_selection_menu = null;
410 private void column_show_selection_menu()
412 column_selection_menu = new Gtk.Menu();
413 generate_column_selection_menu(column_selection_menu);
414 column_selection_menu.show_all();
415 column_selection_menu.popup(null, null, null, 0, Gtk.get_current_event_time());
418 * Populate the treeview with the right columns.
419 * The treeview should have a name now.
421 private void column_populate()
423 for(int i = 0; i < NUM_COLS; i++)
425 Gtk.TreeViewColumn col = new Gtk.TreeViewColumn();
426 col.set_data("index", i);
427 if(gmpc_data_view_col_ids[i] == Gmpc.MpdData.ColumnTypes.COL_ICON_ID)
430 * Picture.
432 var renderer = new Gtk.CellRendererPixbuf();
433 renderer.xalign = 0.0f;
434 // Set up column
435 col.pack_start(renderer, true);
436 col.set_attributes(renderer, "icon-name", Gmpc.MpdData.ColumnTypes.COL_ICON_ID);
437 col.set_resizable(false);
438 col.set_fixed_width(20);
439 col.clickable = true;
440 } else {
442 * Text column
444 col.set_title(FixGtk.gettext(gmpc_data_view_col_names[i]));
445 var renderer = new Gtk.CellRendererText();
446 renderer.ellipsize = Pango.EllipsizeMode.END;
447 // Set up column
448 if(view_mode == ViewType.PLAY_QUEUE) {
449 renderer.weight_set = true;
450 renderer.style_set = true;
451 col.set_cell_data_func(renderer, highlight_row_current_song_playing);
453 col.pack_start(renderer, true);
454 col.set_attributes(renderer, "text", gmpc_data_view_col_ids[i]);
455 col.set_resizable(true);
457 int width = config.get_int_with_default(uid+"-colsize",
458 gmpc_data_view_col_names[i], default_column_width);
459 // Do not set to size 0, then revert back to 200.
460 col.set_fixed_width(width>0?width:default_column_width);
462 col.set_sizing(Gtk.TreeViewColumnSizing.FIXED);
463 col.set_reorderable(true);
465 // Sorting.
466 if(view_mode == ViewType.SONG_LIST) {
467 col.set_sort_indicator(true);
468 col.set_sort_column_id(gmpc_data_view_col_ids[i]);
471 // Fixed width.
472 int pos = config.get_int_with_default(uid+"-colpos",
473 gmpc_data_view_col_names[i], gmpc_data_view_col_position[i]);
474 this.tree_columns[pos] = col;
477 int last_column = 0;
478 // Add the columns (in right order)
479 for(int i = 0; i < NUM_COLS; i++) {
480 int index = this.tree_columns[i].get_data("index");
481 this.insert_column(this.tree_columns[i], i);
482 this.tree_columns[i].set_visible(config.get_bool_with_default(uid+"-colshow",
483 gmpc_data_view_col_names[index], gmpc_data_view_col_enabled[index]));
484 if(config.get_bool_with_default(uid+"-colshow",
485 gmpc_data_view_col_names[index], gmpc_data_view_col_enabled[index])){
486 last_column = i;
489 if(last_column >= 0) {
490 this.tree_columns[last_column].set_fixed_width(20);
499 * Function handles the row-activate signal.
501 private void __row_activated (Gtk.TreePath path, Gtk.TreeViewColumn col)
503 Gtk.TreeModel? model = this.get_model();
504 if(model != null)
506 MPD.Data.Type row_type;
507 Gtk.TreeIter iter;
508 if(!model.get_iter(out iter, path)) return;
510 model.get(iter, Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
511 if(view_mode == ViewType.PLAY_QUEUE) {
512 /* If we are play-queue, play the selected song. */
513 int song_id;
514 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
515 MPD.Player.play_id(Gmpc.server, song_id);
516 } else {
517 string song_path;
518 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
519 if(row_type == MPD.Data.Type.SONG) {
520 /* If we are a song browser, add the path and play it. */
521 MpdInteraction.play_path(song_path);
522 } else if (row_type == MPD.Data.Type.PLAYLIST) {
523 MPD.PlayQueue.queue_load_playlist(server, song_path);
524 MPD.PlayQueue.queue_commit(server);
531 * Check if current row is playing.
533 private void highlight_row_current_song_playing(
534 Gtk.CellLayout col,
535 Gtk.CellRenderer renderer,
536 Gtk.TreeModel model,
537 Gtk.TreeIter iter)
539 // The current song we make bold.
540 if(model is Gmpc.MpdData.ModelPlaylist &&
541 (model as Gmpc.MpdData.ModelPlaylist).is_current_song(iter)){
542 (renderer as Gtk.CellRendererText).weight = Pango.Weight.BOLD;
543 }else{
544 (renderer as Gtk.CellRendererText).weight = Pango.Weight.NORMAL;
547 // A prioritized song we make italic.
548 int prio = 0;
549 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_PRIORITY, out prio);
550 if(prio > 0) {
551 (renderer as Gtk.CellRendererText).style = Pango.Style.ITALIC;
552 }else {
553 (renderer as Gtk.CellRendererText).style = Pango.Style.NORMAL;
559 * Check if modifier is pressed.
561 private bool __key_mod_pressed(Gdk.EventKey event)
563 return (event.state&Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK;
567 * Handle keyboard input.
569 private bool __key_press_event_callback_song_list(Gdk.EventKey event)
571 if(event.keyval == Gdk.Key_i && !__key_mod_pressed(event))
573 return selected_songs_add();
575 else if (event.keyval == Gdk.Key_r && !__key_mod_pressed(event))
577 // If there are songs selected, clear the play queue,
578 // and add selection.
579 if(this.get_selection().count_selected_rows() > 0) {
580 MPD.PlayQueue.clear(server);
581 return selected_songs_add();
584 return false;
587 * Keybinding for playlists.
589 private bool __key_press_event_callback_playlist(Gdk.EventKey event)
591 if (event.keyval == Gdk.Key_x && !__key_mod_pressed(event))
593 // Cut (if available) into clipboard
594 selected_songs_paste_queue_cut();
596 else if (event.keyval == Gdk.Key_X && !__key_mod_pressed(event))
598 // Crop selected songs.
599 selected_songs_crop();
601 else if (event.keyval == Gdk.Key_P && !__key_mod_pressed(event))
603 // Paste before
604 return selected_songs_paste_before();
606 else if (event.keyval == Gdk.Key_p && !__key_mod_pressed(event))
608 // Paste after
609 return selected_songs_paste_after();
611 else if (event.keyval == Gdk.Key_d && !__key_mod_pressed(event))
613 if(!selected_songs_remove())
615 // Clear the playlist
616 MPD.Database.playlist_clear(server, playlist_name);
617 return true;
620 else if (event.keyval == Gdk.Key_D && !__key_mod_pressed(event))
622 MPD.Database.playlist_clear(server, playlist_name);
623 return true;
625 return false;
627 private bool __key_press_event_callback_play_queue(Gdk.EventKey event)
629 if (event.keyval == Gdk.Key_Q && !__key_mod_pressed(event))
631 // remove priority.
632 return selected_songs_remove_priority();
634 else if (__key_mod_pressed(event) &&
635 event.keyval == Gdk.Key_X)
637 stdout.printf("Clear everything but playing song\n");
638 // Select the playing song:
639 unowned MPD.Song? song = server.playlist_get_current_song();
640 if(song != null) {
641 var path = new Gtk.TreePath.from_indices(song.pos);
642 this.get_selection().unselect_all();
643 this.get_selection().select_path(path);
644 selected_songs_crop();
646 return false;
648 else if (event.keyval == Gdk.Key_x && !__key_mod_pressed(event))
650 // Cut (if available) into clipboard
651 selected_songs_paste_queue_cut();
653 else if (event.keyval == Gdk.Key_X && !__key_mod_pressed(event))
655 // Crop selected songs.
656 selected_songs_crop();
658 else if (event.keyval == Gdk.Key_P && !__key_mod_pressed(event))
660 // Paste before
661 return selected_songs_paste_before();
663 else if (event.keyval == Gdk.Key_p && !__key_mod_pressed(event))
665 // Paste after
666 return selected_songs_paste_after();
668 else if (event.keyval == Gdk.Key_d && !__key_mod_pressed(event))
670 if(!selected_songs_remove())
672 // Detach model (for some reason keeping it attached
673 // Makes thing break, work-around for now)
674 // TODO: fixme
675 var model = get_model();
676 this.model = null;
677 // Clear
678 MPD.PlayQueue.clear(server);
679 // Re-add model
680 this.model = model;
681 return true;
684 else if (event.keyval == Gdk.Key_D && !__key_mod_pressed(event))
686 var model = get_model();
687 this.model = null;
688 // Clear
689 MPD.PlayQueue.clear(server);
690 // Re-add model
691 this.model = model;
692 return true;
694 return false;
696 private bool __key_press_event_callback(Gdk.EventKey event)
698 if(event.keyval == Gdk.Key_h && !__key_mod_pressed(event))
700 move_cursor_left();
702 else if (event.keyval == Gdk.Key_l && !__key_mod_pressed(event))
704 move_cursor_right();
706 else if(event.keyval == Gdk.Key_j && !__key_mod_pressed(event))
708 // Move cursor down.
709 move_cursor_down();
711 else if (event.keyval == Gdk.Key_k && !__key_mod_pressed(event))
713 // Move cursor up
714 move_cursor_up();
716 else if (event.keyval == Gdk.Key_g && !__key_mod_pressed(event))
718 move_cursor_top();
720 else if (event.keyval == Gdk.Key_G && !__key_mod_pressed(event))
722 move_cursor_bottom();
724 else if(event.keyval == Gdk.Key_y && !__key_mod_pressed(event))
726 // Copy data to clipboard
727 return selected_songs_paste_queue_copy();
729 else if (event.keyval == Gdk.Key_o && !__key_mod_pressed(event))
731 return selected_songs_info();
733 else if (event.keyval == Gdk.Key_m && !__key_mod_pressed(event))
735 // Configure columns
736 column_show_selection_menu();
737 return true;
739 else if (event.keyval == Gdk.Key_q && !__key_mod_pressed(event))
741 // Raise priority.
742 return selected_songs_raise_priority();
744 else if (event.keyval == Gdk.Key_Menu && !__key_mod_pressed(event))
746 __button_press_menu = new Gtk.Menu();
748 right_mouse_menu(__button_press_menu);
753 __button_press_menu.show_all();
754 __button_press_menu.popup(null, null,null,0, Gtk.get_current_event_time());
755 return true;
758 // Commands specific to play_queue
759 if(view_mode == ViewType.PLAY_QUEUE)
761 if(__key_press_event_callback_play_queue(event)) return true;
763 else if (view_mode == ViewType.PLAYLIST)
765 if(__key_press_event_callback_playlist(event)) return true;
767 else if (view_mode == ViewType.SONG_LIST)
769 if(__key_press_event_callback_song_list(event)) return true;
771 return false;
775 * Right mouse popup.
777 // Hack to stop vala from destroying my menu.
778 private Gtk.Menu __button_press_menu = null;
779 private bool __button_press_event_callback(Gdk.EventButton event)
781 if(event.button == 3)
783 __button_press_menu = new Gtk.Menu();
784 right_mouse_menu(__button_press_menu);
786 __button_press_menu.show_all();
787 __button_press_menu.popup(null, null,null, event.button, event.time);
788 return true;
790 return false;
792 private bool __button_release_event_callback(Gdk.EventButton event)
794 return false;
799 * Interaction on selected songs.
801 // Set priority on the selected songs.
802 private bool selected_songs_raise_priority()
804 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
805 var selection = this.get_selection();
807 if(selection.count_selected_rows() > 254) {
808 Gmpc.Messages.show(_("You can only queue 254 songs at the time."), Gmpc.Messages.Level.WARNING);
809 return false;
812 int priority = 255;
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 MPD.Data.Type row_type;
820 int song_id;
821 model.get(iter,
822 Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id,
823 Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type);
824 // Only act on songs.
825 if(row_type != MPD.Data.Type.SONG) continue;
827 if(song_id >= 0) {
828 MPD.PlayQueue.set_priority(server, song_id, priority--);
829 } else {
830 string song_path;
831 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
832 song_id = MPD.PlayQueue.add_song_get_id(server,song_path);
833 MPD.PlayQueue.set_priority(server, song_id, priority--);
837 return true;
839 // Remove the set priority from the selected songs.
840 private bool selected_songs_remove_priority()
842 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
843 var selection = this.get_selection();
845 Gtk.TreeModel model;
846 foreach(var path in selection.get_selected_rows(out model))
848 Gtk.TreeIter iter;
849 if(model.get_iter(out iter, path))
851 int song_id;
852 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
853 if ( song_id >= 0 ) {
854 MPD.PlayQueue.set_priority(server, song_id, 0);
858 return true;
860 // Add the selected song
861 private bool selected_songs_add()
863 int added_rows = 0;
864 var selection = this.get_selection();
866 Gtk.TreeModel model;
867 foreach(var path in selection.get_selected_rows(out model))
869 Gtk.TreeIter iter;
870 if(model.get_iter(out iter, path))
872 string song_path;
873 MPD.Data.Type row_type;
874 model.get(iter,
875 Gmpc.MpdData.ColumnTypes.ROW_TYPE, out row_type,
876 Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
877 if ( row_type == MPD.Data.Type.SONG || row_type == MPD.Data.Type.DIRECTORY) {
878 MPD.PlayQueue.queue_add_song(server, song_path);
879 added_rows++;
880 }else if (row_type == MPD.Data.Type.PLAYLIST) {
881 MPD.PlayQueue.queue_load_playlist(server, song_path);
882 added_rows++;
886 MPD.PlayQueue.queue_commit(server);
887 if(added_rows > 0) {
888 return true;
890 return false;
892 // Play the selected song
893 private bool selected_songs_play()
895 var selection = this.get_selection();
896 Gtk.TreeModel model;
897 foreach(var path in selection.get_selected_rows(out model))
899 Gtk.TreeIter iter;
900 if(model.get_iter(out iter, path))
902 if(view_mode == ViewType.PLAY_QUEUE) {
903 int song_id;
904 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
905 if(song_id >= 0){
906 MPD.Player.play_id(server, song_id);
907 return true;
909 }else{
910 string song_path;
911 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
912 MpdInteraction.play_path(song_path);
913 return true;
917 return selection.count_selected_rows() > 0;
919 // Crop the selected songs.
920 private void selected_songs_crop()
922 int deleted_rows = 0;
923 var selection = this.get_selection();
925 model = this.get_model();
926 List<int> items_to_delete = new List<int>();
928 TreeIter iter;
929 if(model.get_iter_first(out iter))
932 if(!this.get_selection().iter_is_selected(iter))
934 if(view_mode == ViewType.PLAY_QUEUE) {
935 int song_id;
936 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
937 items_to_delete.prepend(song_id);
938 }else if ( view_mode == ViewType.PLAYLIST) {
939 var path = model.get_path(iter);
940 var indc = path.get_indices();
941 int song_pos = indc[0];
942 items_to_delete.prepend(song_pos);
945 }while(model.iter_next(ref iter));
947 foreach(int id in items_to_delete)
949 if(view_mode == ViewType.PLAY_QUEUE)
951 MPD.PlayQueue.queue_delete_id(server, id);
952 } else if (view_mode == ViewType.PLAYLIST)
954 MPD.Database.playlist_list_delete(server, playlist_name, id);
957 MPD.PlayQueue.queue_commit(server);
959 // Remove the selected songs from the play queue.
960 private bool selected_songs_remove()
962 int deleted_rows = 0;
963 var selection = this.get_selection();
965 Gtk.TreeModel model;
966 var sel_rows = selection.get_selected_rows(out model);
967 sel_rows.reverse();
968 foreach(var path in sel_rows)
970 Gtk.TreeIter iter;
971 if(model.get_iter(out iter, path))
973 if(view_mode == ViewType.PLAY_QUEUE) {
974 int song_id;
975 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_SONG_ID, out song_id);
976 MPD.PlayQueue.queue_delete_id(server, song_id);
977 }else if ( view_mode == ViewType.PLAYLIST) {
978 var indc = path.get_indices();
979 int song_pos = indc[0];
980 MPD.Database.playlist_list_delete(server, playlist_name, song_pos);
981 }else {
982 error("Invalid state, cannot call this here.");
984 deleted_rows++;
987 MPD.PlayQueue.queue_commit(server);
988 if(deleted_rows > 0) {
989 selection.unselect_all();
990 return true;
992 return false;
994 // Show the Information of the first selected song.
995 private bool selected_songs_info()
997 var selection = this.get_selection();
998 Gtk.TreeModel model;
999 foreach(var path in selection.get_selected_rows(out model))
1001 Gtk.TreeIter iter;
1002 if(model.get_iter(out iter, path))
1004 unowned MPD.Song? song = null;
1005 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_MPDSONG, out song);
1006 if(song != null) {
1007 Browser.Metadata.show();
1008 Browser.Metadata.show_song(song);
1009 return true;
1013 return selection.count_selected_rows() > 0;
1016 // Cut the selected songs in the play queue
1017 private bool selected_songs_paste_queue_cut()
1019 if(selected_songs_paste_queue_copy()){
1020 selected_songs_remove();
1022 return false;
1024 // Copy the selected songs in the play queue
1025 private bool selected_songs_paste_queue_copy()
1027 var selection = this.get_selection();
1028 // If nothing to copy , do nothing.
1029 if(selection.count_selected_rows() == 0) return false;
1030 // Clear paste_queue
1031 paste_queue = null;
1032 Gtk.TreeModel model;
1033 foreach(var path in selection.get_selected_rows(out model))
1035 Gtk.TreeIter iter;
1036 if(model.get_iter(out iter, path))
1038 string insert_path;
1039 model.get(iter,Gmpc.MpdData.ColumnTypes.COL_PATH, out insert_path);
1040 paste_queue.prepend(insert_path);
1043 paste_queue.reverse();
1044 return true;
1048 private bool selected_songs_paste_before()
1050 var selection = this.get_selection();
1051 // If nothing selected.
1052 if(selection.count_selected_rows() == 0|| paste_queue == null)
1054 return false;
1056 Gtk.TreeModel model;
1057 Gtk.TreeIter iter;
1058 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
1059 if(model.get_iter(out iter, path))
1061 int songpos;
1062 if( view_mode == ViewType.PLAY_QUEUE) {
1063 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_POS, out songpos);
1064 if(songpos> 0) {
1065 songpos--;
1066 paste_queue.reverse();
1067 foreach(var fpath in paste_queue)
1069 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
1070 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
1072 paste_queue.reverse();
1074 }else if (view_mode == ViewType.PLAYLIST) {
1075 int length = this.model.iter_n_children(null);
1076 var indc = path.get_indices();
1077 songpos = indc[0];
1078 paste_queue.reverse();
1079 foreach(var fpath in paste_queue)
1081 MPD.Database.playlist_list_add(server, playlist_name, fpath);
1082 MPD.Database.playlist_move(server, playlist_name, length,songpos);
1083 length++;
1085 paste_queue.reverse();
1087 }else{
1088 error("We should be able to be here.");
1091 return true;
1093 private bool selected_songs_paste_after()
1095 var selection = this.get_selection();
1096 // If nothing selected.
1097 if(selection.count_selected_rows() == 0|| paste_queue == null)
1099 return false;
1101 Gtk.TreeModel model;
1102 Gtk.TreeIter iter;
1103 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
1104 if(model.get_iter(out iter, path))
1106 int songpos;
1107 if(view_mode == ViewType.PLAY_QUEUE) {
1108 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_SONG_POS, out songpos);
1109 if(songpos> 0) {
1110 paste_queue.reverse();
1111 foreach(var fpath in paste_queue)
1113 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
1114 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
1116 paste_queue.reverse();
1119 else if (view_mode == ViewType.PLAYLIST) {
1120 int length = this.model.iter_n_children(null);
1121 var indc = path.get_indices();
1122 songpos = indc[0]+1;
1123 paste_queue.reverse();
1124 foreach(var fpath in paste_queue)
1126 MPD.Database.playlist_list_add(server, playlist_name, fpath);
1127 MPD.Database.playlist_move(server, playlist_name, length,songpos);
1128 length++;
1130 paste_queue.reverse();
1131 }else{
1132 error("We should be able to be here.");
1136 return true;
1139 private bool selected_songs_add_to_playlist(string playlist)
1141 int songs_added = 0;
1142 var selection = this.get_selection();
1143 // If nothing selected.
1144 if(selection.count_selected_rows() == 0)
1146 return false;
1148 Gtk.TreeModel model;
1149 foreach (var path in selection.get_selected_rows(out model))
1151 Gtk.TreeIter iter;
1152 if(model.get_iter(out iter, path))
1154 string? song_path = null;
1155 model.get(iter, Gmpc.MpdData.ColumnTypes.COL_PATH, out song_path);
1156 if(song_path != null) {
1157 MPD.Database.playlist_list_add(server, playlist, song_path);
1158 songs_added++;
1162 return (songs_added > 0);
1166 * Move
1168 private void move_cursor_down()
1170 Gtk.TreePath? path;
1171 Gtk.TreeViewColumn? col;
1172 this.get_cursor(out path, out col);
1173 if(path != null)
1175 Gtk.TreeIter iter;
1176 path.next();
1177 if(this.model.get_iter(out iter, path))
1179 this.set_cursor(path, col, false);
1183 private void move_cursor_top()
1185 Gtk.TreeViewColumn? col;
1186 var rows = this.model.iter_n_children(null);
1187 if(rows > 0){
1188 Gtk.TreePath? path;
1189 this.get_cursor(null, out col);
1190 path = new Gtk.TreePath.from_indices(0);
1191 this.set_cursor(path, col, false);
1194 private void move_cursor_bottom()
1196 Gtk.TreeViewColumn? col;
1197 var rows = this.model.iter_n_children(null);
1198 if(rows > 0){
1199 Gtk.TreePath? path;
1200 this.get_cursor(null, out col);
1201 path = new Gtk.TreePath.from_indices(rows-1);
1202 this.set_cursor(path, col, false);
1205 private void move_cursor_up()
1207 Gtk.TreePath? path;
1208 Gtk.TreeViewColumn? col;
1209 this.get_cursor(out path, out col);
1210 if(path != null)
1212 if(path.prev())
1214 this.set_cursor(path, col, false);
1218 private void move_cursor_left()
1220 if(this is Gtk.Scrollable)
1222 var hadj = this.get_hadjustment();
1223 hadj.set_value(hadj.value-hadj.step_increment);
1226 private void move_cursor_right()
1228 if(this is Gtk.Scrollable)
1230 var hadj = this.get_hadjustment();
1231 hadj.set_value(hadj.value+hadj.step_increment);