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;
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
= {
63 const bool[] gmpc_data_view_col_enabled
= {
87 const int[] gmpc_data_view_col_position
= {
110 public class Gmpc
.DataView
: Gtk
.TreeView
112 public enum ViewType
{
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
{
132 return __playlist_name
;
135 if(view_mode
== ViewType
.PLAYLIST
) {
136 stdout
.printf("Set playlist name: %s\n", value
);
137 __playlist_name
= value
;
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
;
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);
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
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) {
212 var path
= this
.get_selection().get_selected_rows(out model
).first().data
;
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();
225 item
= new Gtk
.ImageMenuItem
.from_stock(Gtk
.Stock
.INFO
, null);
226 item
.activate
.connect((source
)=>{
227 selected_songs_info();
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();
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(); });
249 var item
= new Gtk
.ImageMenuItem
.from_stock(Gtk
.Stock
.ADD
,null);
250 item
.activate
.connect((source
)=>{
251 selected_songs_add();
254 item
= new Gtk
.ImageMenuItem
.with_label(_("Replace"));
255 item
.activate
.connect((source
)=>{
256 MPD
.PlayQueue
.clear(server
);
257 selected_songs_add();
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();});
268 if(view_mode
== ViewType
.PLAY_QUEUE
) {
269 item
= new Gtk
.MenuItem
.with_label(_("Dequeue"));
270 item
.activate
.connect((source
)=>{ selected_songs_remove_priority();});
275 if(selected_rows
> 0)
278 item
= new Gtk
.SeparatorMenuItem();
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();});
286 item
= new Gtk
.ImageMenuItem
.from_stock(Gtk
.Stock
.COPY
, null);
287 item
.activate
.connect((source
)=>{ selected_songs_paste_queue_copy();});
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();});
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();});
304 // Add to playlist integration.
307 if(selected_rows
== 1)
310 var list
= get_selection().get_selected_rows(out model
);
311 var path
= list
.first().data
;
313 if(model
.get_iter(out iter
, path
))
315 unowned MPD
.Song? song
;
316 model
.get(iter
, MpdData
.ColumnTypes
.COL_MPDSONG
, out song
);
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");
336 menu
.append(new Gtk
.SeparatorMenuItem());
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
);
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();
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.
372 config
.set_int(uid
+"-colsize",gmpc_data_view_col_names
[col_index
], width
);
377 if(this
.model is Gtk
.TreeSortable
) {
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
]));
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();
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
)
431 var renderer
= new Gtk
.CellRendererPixbuf();
432 renderer
.xalign
= 0.0f
;
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;
443 col
.set_title(FixGtk
.gettext(gmpc_data_view_col_names
[i
]));
444 var renderer
= new Gtk
.CellRendererText();
445 renderer
.ellipsize
= Pango
.EllipsizeMode
.END
;
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);
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
]);
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();
497 MPD
.Data
.Type row_type
;
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. */
505 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_ID
, out song_id
);
506 MPD
.Player
.play_id(Gmpc
.server
, song_id
);
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(
526 Gtk
.CellRenderer renderer
,
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
;
535 (renderer as Gtk
.CellRendererText
).weight
= Pango
.Weight
.NORMAL
;
538 // A prioritized song we make italic.
540 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_PRIORITY
, out prio
);
542 (renderer as Gtk
.CellRendererText
).style
= Pango
.Style
.ITALIC
;
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();
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
))
595 return selected_songs_paste_before();
597 else if (event
.keyval
== Gdk
.Key_p
&& !__key_mod_pressed(event
))
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
);
611 else if (event
.keyval
== Gdk
.Key_D
&& !__key_mod_pressed(event
))
613 MPD
.Database
.playlist_clear(server
, playlist_name
);
618 private bool __key_press_event_callback_play_queue(Gdk
.EventKey event
)
620 if (event
.keyval
== Gdk
.Key_Q
&& !__key_mod_pressed(event
))
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();
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();
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
))
652 return selected_songs_paste_before();
654 else if (event
.keyval
== Gdk
.Key_p
&& !__key_mod_pressed(event
))
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)
666 var model
= get_model();
669 MPD
.PlayQueue
.clear(server
);
675 else if (event
.keyval
== Gdk
.Key_D
&& !__key_mod_pressed(event
))
677 var model
= get_model();
680 MPD
.PlayQueue
.clear(server
);
687 private bool __key_press_event_callback(Gdk
.EventKey event
)
689 if(event
.keyval
== Gdk
.Key_h
&& !__key_mod_pressed(event
))
693 else if (event
.keyval
== Gdk
.Key_l
&& !__key_mod_pressed(event
))
697 else if(event
.keyval
== Gdk
.Key_j
&& !__key_mod_pressed(event
))
702 else if (event
.keyval
== Gdk
.Key_k
&& !__key_mod_pressed(event
))
707 else if (event
.keyval
== Gdk
.Key_g
&& !__key_mod_pressed(event
))
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
))
727 column_show_selection_menu();
730 else if (event
.keyval
== Gdk
.Key_q
&& !__key_mod_pressed(event
))
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());
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;
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
);
783 private bool __button_release_event_callback(Gdk
.EventButton event
)
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
);
805 foreach(var path
in selection
.get_selected_rows(out model
))
808 if(model
.get_iter(out iter
, path
))
810 MPD
.Data
.Type row_type
;
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;
819 MPD
.PlayQueue
.set_priority(server
, song_id
, priority
--);
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
--);
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();
837 foreach(var path
in selection
.get_selected_rows(out model
))
840 if(model
.get_iter(out iter
, path
))
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);
851 // Add the selected song
852 private bool selected_songs_add()
855 var selection
= this
.get_selection();
858 foreach(var path
in selection
.get_selected_rows(out model
))
861 if(model
.get_iter(out iter
, path
))
864 MPD
.Data
.Type row_type
;
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
);
871 }else if (row_type
== MPD
.Data
.Type
.PLAYLIST
) {
872 MPD
.PlayQueue
.queue_load_playlist(server
, song_path
);
877 MPD
.PlayQueue
.queue_commit(server
);
883 // Play the selected song
884 private bool selected_songs_play()
886 var selection
= this
.get_selection();
888 foreach(var path
in selection
.get_selected_rows(out model
))
891 if(model
.get_iter(out iter
, path
))
893 if(view_mode
== ViewType
.PLAY_QUEUE
) {
895 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_ID
, out song_id
);
897 MPD
.Player
.play_id(server
, song_id
);
902 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_PATH
, out song_path
);
903 MpdInteraction
.play_path(song_path
);
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>();
920 if(model
.get_iter_first(out iter
))
923 if(!this
.get_selection().iter_is_selected(iter
))
925 if(view_mode
== ViewType
.PLAY_QUEUE
) {
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();
957 var sel_rows
= selection
.get_selected_rows(out model
);
959 foreach(var path
in sel_rows
)
962 if(model
.get_iter(out iter
, path
))
964 if(view_mode
== ViewType
.PLAY_QUEUE
) {
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
);
973 error("Invalid state, cannot call this here.");
978 MPD
.PlayQueue
.queue_commit(server
);
979 if(deleted_rows
> 0) {
980 selection
.unselect_all();
985 // Show the Information of the first selected song.
986 private bool selected_songs_info()
988 var selection
= this
.get_selection();
990 foreach(var path
in selection
.get_selected_rows(out model
))
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
);
998 Browser
.Metadata
.show();
999 Browser
.Metadata
.show_song(song
);
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();
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
1023 Gtk
.TreeModel model
;
1024 foreach(var path
in selection
.get_selected_rows(out model
))
1027 if(model
.get_iter(out iter
, path
))
1030 model
.get(iter
,Gmpc
.MpdData
.ColumnTypes
.COL_PATH
, out insert_path
);
1031 paste_queue
.prepend(insert_path
);
1034 paste_queue
.reverse();
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)
1047 Gtk
.TreeModel model
;
1049 Gtk
.TreePath path
= selection
.get_selected_rows(out model
).last().data
;
1050 if(model
.get_iter(out iter
, path
))
1053 if( view_mode
== ViewType
.PLAY_QUEUE
) {
1054 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_POS
, out 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();
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
);
1076 paste_queue
.reverse();
1079 error("We should be able to be here.");
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)
1092 Gtk
.TreeModel model
;
1094 Gtk
.TreePath path
= selection
.get_selected_rows(out model
).last().data
;
1095 if(model
.get_iter(out iter
, path
))
1098 if(view_mode
== ViewType
.PLAY_QUEUE
) {
1099 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_POS
, out songpos
);
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
);
1121 paste_queue
.reverse();
1123 error("We should be able to be here.");
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)
1139 Gtk
.TreeModel model
;
1140 foreach (var path
in selection
.get_selected_rows(out model
))
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
);
1153 return (songs_added
> 0);
1159 private void move_cursor_down()
1162 Gtk
.TreeViewColumn? col
;
1163 this
.get_cursor(out path
, out col
);
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);
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);
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()
1199 Gtk
.TreeViewColumn? col
;
1200 this
.get_cursor(out path
, out col
);
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
);