diff --git a/internal/config/model.go b/internal/config/model.go index e4a20bb..3f8efe4 100644 --- a/internal/config/model.go +++ b/internal/config/model.go @@ -46,6 +46,7 @@ const ( MTAttrGrouping MTAttrComment MTAttrLabel + MTAttrPos // List store's "artificial" columns used for rendering QueueColumnIcon QueueColumnFontWeight @@ -91,6 +92,7 @@ var MpdTrackAttributes = map[int]MpdTrackAttribute{ MTAttrGrouping: {"Grouping", "Grouping", "Grouping", false, false, 200, 0, nil, nil}, MTAttrComment: {"Comment", "Comment", "Comment", false, true, 200, 0, nil, nil}, MTAttrLabel: {"Label", "Label", "Label", false, true, 200, 0, nil, nil}, + MTAttrPos: {"Pos", "Position", "Pos", true, false, 0, 1, nil, nil}, } // MpdTrackAttributeIds stores attribute IDs sorted in desired display order diff --git a/internal/player/glade/player.glade b/internal/player/glade/player.glade index edd6735..d1e6d25 100644 --- a/internal/player/glade/player.glade +++ b/internal/player/glade/player.glade @@ -1,5 +1,5 @@ - + @@ -126,6 +126,8 @@ + + @@ -135,6 +137,7 @@ + QueueListStore @@ -681,11 +684,11 @@ True True True - QueueTreeModelFilter + QueueListStore + True False True False - True diff --git a/internal/player/main-window.go b/internal/player/main-window.go index 7564c38..2cb22fa 100644 --- a/internal/player/main-window.go +++ b/internal/player/main-window.go @@ -253,6 +253,7 @@ func NewMainWindow(application *gtk.Application) (*MainWindow, error) { "on_StreamsReplaceMenuItem_activate": func() { w.applyStreamSelection(tbTrue) }, "on_StreamsEditMenuItem_activate": w.onStreamEdit, "on_StreamsDeleteMenuItem_activate": w.onStreamDelete, + "on_QueueListStore_row_changed": w.onQueueReorder, }) // Register the main window with the app @@ -510,6 +511,42 @@ func (w *MainWindow) onPlayPositionButtonEvent(_ interface{}, event *gdk.Event) } } +func (w *MainWindow) onQueueReorder(self *gtk.ListStore, path *gtk.TreePath, iter *gtk.TreeIter) { + cancelReorder := func(err error) { + log.Errorf("Failed to reorder queue: %v", err) + w.updateQueue() + } + + val, err := self.GetValue(iter, config.MTAttrPos) + if err != nil { + cancelReorder(err) + return + } + posStr, err := val.GetString() + if err != nil { + cancelReorder(err) + return + } + oldPos, err := strconv.Atoi(posStr) + if err != nil { + cancelReorder(err) + return + } + newPos := path.GetIndices()[0] + if newPos == oldPos { + return + } + err = fmt.Errorf("not connected") + w.connector.IfConnected(func(client *mpd.Client) { + log.Debugf("move %d -> %d", oldPos, newPos) + err = client.Move(oldPos, oldPos+1, newPos) + }) + if err == nil { + return + } + cancelReorder(err) +} + func (w *MainWindow) onQueueSavePopoverValidate() { // Only show new playlist widgets if (new playlist) is selected in the combo box selectedID := w.QueueSavePlaylistComboBox.GetActiveID() @@ -526,8 +563,15 @@ func (w *MainWindow) onQueueSavePopoverValidate() { func (w *MainWindow) onQueueSearchMode() { w.queueFilter() - // Return focus to the queue on deactivating search - if !w.QueueSearchBar.GetSearchMode() { + // Only use (non-reorderable) QueueTreeModelFilter when searching + if w.QueueSearchBar.GetSearchMode() { + w.QueueTreeView.SetModel(w.QueueTreeModelFilter) + w.QueueTreeView.SetReorderable(false) + } else { + w.QueueTreeView.SetModel(w.QueueListStore) + w.QueueTreeView.SetReorderable(true) + + // Return focus to the queue on deactivating search w.focusMainList() } } @@ -1898,6 +1942,7 @@ func (w *MainWindow) updateAll() { w.aMPDDisconnect.SetEnabled(connected || connecting) w.aMPDInfo.SetEnabled(connected) w.aMPDOutputs.SetEnabled(connected) + w.QueueTreeView.SetReorderable(connected) // Update other widgets w.updateQueue() @@ -2384,7 +2429,9 @@ func (w *MainWindow) updateQueue() { w.QueueTreeView.FreezeChildNotify() defer w.QueueTreeView.ThawChildNotify() - // Detach the tree view from the list model to speed up processing + // Detach the tree view from the list model to speed up processing. + // Model may either be QueueTreeModelFilter (when searching) or QueueListStore (otherwise). + model, _ := w.QueueTreeView.GetModel() w.QueueTreeView.SetModel(nil) // Clear the queue list store @@ -2491,7 +2538,7 @@ func (w *MainWindow) updateQueue() { w.updateQueueActions() // Restore the tree view model - w.QueueTreeView.SetModel(w.QueueTreeModelFilter) + w.QueueTreeView.SetModel(model) // Highlight and scroll the tree to the currently played item w.updateQueueNowPlaying()