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();
258 MPD
.Player
.play(server
);
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();});
269 if(view_mode
== ViewType
.PLAY_QUEUE
) {
270 item
= new Gtk
.MenuItem
.with_label(_("Dequeue"));
271 item
.activate
.connect((source
)=>{ selected_songs_remove_priority();});
276 if(selected_rows
> 0)
279 item
= new Gtk
.SeparatorMenuItem();
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();});
287 item
= new Gtk
.ImageMenuItem
.from_stock(Gtk
.Stock
.COPY
, null);
288 item
.activate
.connect((source
)=>{ selected_songs_paste_queue_copy();});
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();});
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();});
305 // Add to playlist integration.
308 if(selected_rows
== 1)
311 var list
= get_selection().get_selected_rows(out model
);
312 var path
= list
.first().data
;
314 if(model
.get_iter(out iter
, path
))
316 unowned MPD
.Song? song
;
317 model
.get(iter
, MpdData
.ColumnTypes
.COL_MPDSONG
, out song
);
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");
337 menu
.append(new Gtk
.SeparatorMenuItem());
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
);
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();
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.
373 config
.set_int(uid
+"-colsize",gmpc_data_view_col_names
[col_index
], width
);
378 if(this
.model is Gtk
.TreeSortable
) {
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
]));
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();
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
)
432 var renderer
= new Gtk
.CellRendererPixbuf();
433 renderer
.xalign
= 0.0f
;
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;
444 col
.set_title(FixGtk
.gettext(gmpc_data_view_col_names
[i
]));
445 var renderer
= new Gtk
.CellRendererText();
446 renderer
.ellipsize
= Pango
.EllipsizeMode
.END
;
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);
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
]);
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
;
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
])){
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();
506 MPD
.Data
.Type row_type
;
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. */
514 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_ID
, out song_id
);
515 MPD
.Player
.play_id(Gmpc
.server
, song_id
);
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(
535 Gtk
.CellRenderer renderer
,
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
;
544 (renderer as Gtk
.CellRendererText
).weight
= Pango
.Weight
.NORMAL
;
547 // A prioritized song we make italic.
549 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_PRIORITY
, out prio
);
551 (renderer as Gtk
.CellRendererText
).style
= Pango
.Style
.ITALIC
;
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();
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
))
604 return selected_songs_paste_before();
606 else if (event
.keyval
== Gdk
.Key_p
&& !__key_mod_pressed(event
))
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
);
620 else if (event
.keyval
== Gdk
.Key_D
&& !__key_mod_pressed(event
))
622 MPD
.Database
.playlist_clear(server
, playlist_name
);
627 private bool __key_press_event_callback_play_queue(Gdk
.EventKey event
)
629 if (event
.keyval
== Gdk
.Key_Q
&& !__key_mod_pressed(event
))
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();
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();
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
))
661 return selected_songs_paste_before();
663 else if (event
.keyval
== Gdk
.Key_p
&& !__key_mod_pressed(event
))
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)
675 var model
= get_model();
678 MPD
.PlayQueue
.clear(server
);
684 else if (event
.keyval
== Gdk
.Key_D
&& !__key_mod_pressed(event
))
686 var model
= get_model();
689 MPD
.PlayQueue
.clear(server
);
696 private bool __key_press_event_callback(Gdk
.EventKey event
)
698 if(event
.keyval
== Gdk
.Key_h
&& !__key_mod_pressed(event
))
702 else if (event
.keyval
== Gdk
.Key_l
&& !__key_mod_pressed(event
))
706 else if(event
.keyval
== Gdk
.Key_j
&& !__key_mod_pressed(event
))
711 else if (event
.keyval
== Gdk
.Key_k
&& !__key_mod_pressed(event
))
716 else if (event
.keyval
== Gdk
.Key_g
&& !__key_mod_pressed(event
))
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
))
736 column_show_selection_menu();
739 else if (event
.keyval
== Gdk
.Key_q
&& !__key_mod_pressed(event
))
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());
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;
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
);
792 private bool __button_release_event_callback(Gdk
.EventButton event
)
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
);
814 foreach(var path
in selection
.get_selected_rows(out model
))
817 if(model
.get_iter(out iter
, path
))
819 MPD
.Data
.Type row_type
;
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;
828 MPD
.PlayQueue
.set_priority(server
, song_id
, priority
--);
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
--);
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();
846 foreach(var path
in selection
.get_selected_rows(out model
))
849 if(model
.get_iter(out iter
, path
))
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);
860 // Add the selected song
861 private bool selected_songs_add()
864 var selection
= this
.get_selection();
867 foreach(var path
in selection
.get_selected_rows(out model
))
870 if(model
.get_iter(out iter
, path
))
873 MPD
.Data
.Type row_type
;
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
);
880 }else if (row_type
== MPD
.Data
.Type
.PLAYLIST
) {
881 MPD
.PlayQueue
.queue_load_playlist(server
, song_path
);
886 MPD
.PlayQueue
.queue_commit(server
);
892 // Play the selected song
893 private bool selected_songs_play()
895 var selection
= this
.get_selection();
897 foreach(var path
in selection
.get_selected_rows(out model
))
900 if(model
.get_iter(out iter
, path
))
902 if(view_mode
== ViewType
.PLAY_QUEUE
) {
904 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_ID
, out song_id
);
906 MPD
.Player
.play_id(server
, song_id
);
911 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_PATH
, out song_path
);
912 MpdInteraction
.play_path(song_path
);
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>();
929 if(model
.get_iter_first(out iter
))
932 if(!this
.get_selection().iter_is_selected(iter
))
934 if(view_mode
== ViewType
.PLAY_QUEUE
) {
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();
966 var sel_rows
= selection
.get_selected_rows(out model
);
968 foreach(var path
in sel_rows
)
971 if(model
.get_iter(out iter
, path
))
973 if(view_mode
== ViewType
.PLAY_QUEUE
) {
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
);
982 error("Invalid state, cannot call this here.");
987 MPD
.PlayQueue
.queue_commit(server
);
988 if(deleted_rows
> 0) {
989 selection
.unselect_all();
994 // Show the Information of the first selected song.
995 private bool selected_songs_info()
997 var selection
= this
.get_selection();
999 foreach(var path
in selection
.get_selected_rows(out model
))
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
);
1007 Browser
.Metadata
.show();
1008 Browser
.Metadata
.show_song(song
);
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();
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
1032 Gtk
.TreeModel model
;
1033 foreach(var path
in selection
.get_selected_rows(out model
))
1036 if(model
.get_iter(out iter
, path
))
1039 model
.get(iter
,Gmpc
.MpdData
.ColumnTypes
.COL_PATH
, out insert_path
);
1040 paste_queue
.prepend(insert_path
);
1043 paste_queue
.reverse();
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)
1056 Gtk
.TreeModel model
;
1058 Gtk
.TreePath path
= selection
.get_selected_rows(out model
).last().data
;
1059 if(model
.get_iter(out iter
, path
))
1062 if( view_mode
== ViewType
.PLAY_QUEUE
) {
1063 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_POS
, out 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();
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
);
1085 paste_queue
.reverse();
1088 error("We should be able to be here.");
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)
1101 Gtk
.TreeModel model
;
1103 Gtk
.TreePath path
= selection
.get_selected_rows(out model
).last().data
;
1104 if(model
.get_iter(out iter
, path
))
1107 if(view_mode
== ViewType
.PLAY_QUEUE
) {
1108 model
.get(iter
, Gmpc
.MpdData
.ColumnTypes
.COL_SONG_POS
, out songpos
);
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
);
1130 paste_queue
.reverse();
1132 error("We should be able to be here.");
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)
1148 Gtk
.TreeModel model
;
1149 foreach (var path
in selection
.get_selected_rows(out model
))
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
);
1162 return (songs_added
> 0);
1168 private void move_cursor_down()
1171 Gtk
.TreeViewColumn? col
;
1172 this
.get_cursor(out path
, out col
);
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);
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);
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()
1208 Gtk
.TreeViewColumn? col
;
1209 this
.get_cursor(out path
, out col
);
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
);