« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--playlist-utils.js2
-rw-r--r--ui.js76
2 files changed, 56 insertions, 22 deletions
diff --git a/playlist-utils.js b/playlist-utils.js
index 654d6ad..92e62b1 100644
--- a/playlist-utils.js
+++ b/playlist-utils.js
@@ -467,7 +467,7 @@ function isTrack(obj) {
 
 module.exports = {
   parentSymbol,
-  updatePlaylistFormat, updateTrackFormat,
+  updatePlaylistFormat, updateGroupFormat, updateTrackFormat,
   filterTracks,
   flattenGrouplike,
   partiallyFlattenGrouplike, collapseGrouplike,
diff --git a/ui.js b/ui.js
index 7ce5fdd..a18cd4d 100644
--- a/ui.js
+++ b/ui.js
@@ -1,7 +1,7 @@
 const { getAllCrawlersForArg } = require('./crawlers')
 const { getDownloaderFor } = require('./downloaders')
 const { getPlayer } = require('./players')
-const { parentSymbol, isGroup, isTrack, getItemPath, getItemPathString, flattenGrouplike } = require('./playlist-utils')
+const { parentSymbol, isGroup, isTrack, getItemPath, getItemPathString, flattenGrouplike, updateGroupFormat } = require('./playlist-utils')
 const { shuffleArray } = require('./general-util')
 const processSmartPlaylist = require('./smart-playlist')
 const UndoManager = require('./undo-manager')
@@ -34,7 +34,7 @@ class AppElement extends FocusElement {
     this.undoManager = new UndoManager()
     this.queueGrouplike = {name: 'Queue', isTheQueue: true, items: []}
     this.markGrouplike = {name: 'Marked', items: []}
-    this.editMode = false
+    this.editMode = true
 
     // Crude hack...
     this.recordStore.app = this
@@ -117,31 +117,31 @@ class AppElement extends FocusElement {
     grouplikeListing.on('queue (shuffled)', item => this.shuffleQueueGrouplikeItem(item))
     grouplikeListing.on('queue (play next)', item => this.queueGrouplikeItem(item, true, this.playingTrack))
 
+    const updateListingsFor = item => {
+      for (const grouplikeListing of this.tabber.tabberElements) {
+        if (grouplikeListing.grouplike === item) {
+          grouplikeListing.loadGrouplike(item, false)
+        }
+      }
+    }
+
     grouplikeListing.on('remove (x)', item => {
       if (this.editMode) {
         const parent = item[parentSymbol]
         const index = parent.items.indexOf(item)
 
-        const updateDisplay = () => {
-          for (const grouplikeListing of this.tabber.tabberElements) {
-            if (grouplikeListing.grouplike === parent) {
-              grouplikeListing.loadGrouplike(parent)
-            } else if (grouplikeListing.grouplike === item) {
-              grouplikeListing.loadGrouplike(item)
-            }
-          }
-        }
-
         this.undoManager.pushAction({
           activate: () => {
             parent.items.splice(index, 1)
             delete item[parentSymbol]
-            updateDisplay()
+            updateListingsFor(item)
+            updateListingsFor(parent)
           },
           undo: () => {
             parent.items.splice(index, 0, item)
             item[parentSymbol] = parent
-            updateDisplay()
+            updateListingsFor(item)
+            updateListingsFor(parent)
           }
         })
       }
@@ -172,6 +172,34 @@ class AppElement extends FocusElement {
       }
     })
 
+    const handlePaste = offset => item => {
+      if (this.editMode) {
+        if (this.markGrouplike.items.length) {
+          const parent = item[parentSymbol]
+          const index = parent.items.indexOf(item) + offset
+          this.undoManager.pushAction({
+            activate: () => {
+              // TODO: More "proper" way of cloning a grouplike. (The purpose of updateGroupFormat
+              // here is to make the parentSymbols be properly set, as well as to create a set of
+              // totally new objects, so none of the pasted groups already appear somewhere else;
+              // or rather, not as the same objects.)
+              parent.items.splice(index, 0, ...updateGroupFormat(this.markGrouplike).items.map(
+                item => Object.assign({}, item, {[parentSymbol]: parent})
+              ))
+              updateListingsFor(parent)
+            },
+            undo: () => {
+              parent.items.splice(index, this.markGrouplike.items.length)
+              updateListingsFor(parent)
+            }
+          })
+        }
+      }
+    }
+
+    grouplikeListing.on('paste (below)', handlePaste(+1))
+    grouplikeListing.on('paste (above)', handlePaste(0))
+
     const handleSelectFromPathElement = item => {
       this.form.selectInput(grouplikeListing)
       if (isGroup(item)) {
@@ -674,9 +702,9 @@ class GrouplikeListingElement extends FocusElement {
     }
   }
 
-  loadGrouplike(grouplike) {
+  loadGrouplike(grouplike, resetIndex = true) {
     this.grouplike = grouplike
-    this.buildItems(true)
+    this.buildItems(resetIndex)
   }
 
   buildItems(resetIndex = false) {
@@ -703,7 +731,7 @@ class GrouplikeListingElement extends FocusElement {
     if (this.grouplike.items.length) {
       for (const item of this.grouplike.items) {
         const itemElement = new GrouplikeItemElement(item, this.recordStore)
-        for (const evtName of ['download', 'remove (backspace)', 'remove (x)', 'mark', 'select (space)', 'select (enter)', 'queue', 'queue (shuffled)', 'queue (play next)']) {
+        for (const evtName of ['download', 'remove (backspace)', 'remove (x)', 'mark', 'paste (above)', 'paste (below)', 'select (space)', 'select (enter)', 'queue', 'queue (shuffled)', 'queue (play next)']) {
           itemElement.on(evtName, () => this.emit(evtName, item))
         }
         form.addInput(itemElement)
@@ -870,16 +898,22 @@ class GrouplikeItemElement extends Button {
           this.emit('queue (play next)')
         }
       }
+    } else if (telc.isSpace(keyBuf)) {
+      this.emit('select (space)')
+    } else if (telc.isEnter(keyBuf)) {
+      this.emit('select (enter)')
     } else if (telc.isBackspace(keyBuf)) {
       this.emit('remove (backspace)')
     } else if (telc.isCaselessLetter(keyBuf, 'x')) {
       this.emit('remove (x)')
+
+      // Edit-mode things
     } else if (telc.isCaselessLetter(keyBuf, 'm')) {
       this.emit('mark')
-    } else if (telc.isSpace(keyBuf)) {
-      this.emit('select (space)')
-    } else if (telc.isEnter(keyBuf)) {
-      this.emit('select (enter)')
+    } else if (telc.isCharacter(keyBuf, 'p')) {
+      this.emit('paste (below)')
+    } else if (telc.isCharacter(keyBuf, 'P')) {
+      this.emit('paste (above)')
     }
   }
 }