]> git.sesse.net Git - nageru/commitdiff
Add some playlist manipulation controls.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 18 Jun 2018 22:37:31 +0000 (00:37 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 18 Jun 2018 22:37:31 +0000 (00:37 +0200)
clip_list.cpp
clip_list.h
mainwindow.cpp
mainwindow.h
ui_mainwindow.ui

index e15fbfe79aae0d712448930e5dd951cb11894765..373ef0fc1f6d6be3e682cab7ed7a6b3851c2bbf3 100644 (file)
@@ -304,6 +304,34 @@ void PlayList::add_clip(const Clip &clip)
        endInsertRows();
 }
 
+void PlayList::duplicate_clips(size_t first, size_t last)
+{
+       beginInsertRows(QModelIndex(), first, last);
+       clips.insert(clips.begin() + first, clips.begin() + first, clips.begin() + last + 1);
+       endInsertRows();
+}
+
+void PlayList::erase_clips(size_t first, size_t last)
+{
+       beginRemoveRows(QModelIndex(), first, last);
+       clips.erase(clips.begin() + first, clips.begin() + last + 1);
+       endRemoveRows();
+}
+
+void PlayList::move_clips(size_t first, size_t last, int delta)
+{
+       if (delta == -1) {
+               beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
+               rotate(clips.begin() + first - 1, clips.begin() + first, clips.begin() + last + 1);
+       } else {
+               beginMoveRows(QModelIndex(), first, last, QModelIndex(), first + (last-first+1) + 1);
+               first = clips.size() - first - 1;
+               last = clips.size() - last - 1;
+               rotate(clips.rbegin() + last - 1, clips.rbegin() + last, clips.rbegin() + first + 1);
+       }
+       endMoveRows();
+}
+
 void ClipList::emit_data_changed(size_t row)
 {
        emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
index 64d06cd29f19633da0b82c1f9355442c49bcb44f..126d1d5bc340e2fb1b04b85ee26361ab5f328acb 100644 (file)
@@ -104,6 +104,13 @@ public:
        bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
 
        void add_clip(const Clip &clip);
+
+       // <last> is inclusive in all of these.
+       void duplicate_clips(size_t first, size_t last);
+       void erase_clips(size_t first, size_t last);
+       // <delta> is -1 to move upwards, +1 to move downwards.
+       void move_clips(size_t first, size_t last, int delta);
+
        size_t size() const { return clips.size(); }
        bool empty() const { return clips.empty(); }
 
index 04b4e56275d9e93bcdc37aa7c8e8e3fe6fc3e9c5..6b100ac358160244fceafda29882c61bd860fd51 100644 (file)
@@ -74,6 +74,19 @@ MainWindow::MainWindow()
        connect(preview_4, &QShortcut::activated, ui->preview_4_btn, &QPushButton::click);
        connect(ui->preview_4_btn, &QPushButton::clicked, [this]{ preview_angle_clicked(3); });
 
+       connect(ui->playlist_duplicate_btn, &QPushButton::clicked, this, &MainWindow::playlist_duplicate);
+
+       // TODO: support the delete key iff the widget has focus?
+       connect(ui->playlist_remove_btn, &QPushButton::clicked, this, &MainWindow::playlist_remove);
+
+       // TODO: support drag-and-drop.
+       connect(ui->playlist_move_up_btn, &QPushButton::clicked, [this]{ playlist_move(-1); });
+       connect(ui->playlist_move_down_btn, &QPushButton::clicked, [this]{ playlist_move(1); });
+
+       connect(ui->playlist->selectionModel(), &QItemSelectionModel::selectionChanged,
+               this, &MainWindow::playlist_selection_changed);
+       playlist_selection_changed();  // First time set-up.
+
        preview_player = new Player(ui->preview_display);
        live_player = new Player(ui->live_display);
        live_player->set_done_callback([this]{
@@ -92,6 +105,7 @@ void MainWindow::cue_in_clicked()
        Clip clip;
        clip.pts_in = current_pts;
        cliplist_clips->add_clip(clip);
+       playlist_selection_changed();
 }
 
 void MainWindow::cue_out_clicked()
@@ -122,6 +136,7 @@ void MainWindow::queue_clicked()
                Clip clip = *cliplist_clips->clip(index.row());
                clip.stream_idx = index.column() - int(ClipList::Column::CAMERA_1);
                playlist_clips->add_clip(clip);
+               playlist_selection_changed();
        }
 }
 
@@ -158,6 +173,55 @@ void MainWindow::preview_angle_clicked(unsigned stream_idx)
        }
 }
 
+void MainWindow::playlist_duplicate()
+{
+       QItemSelectionModel *selected = ui->playlist->selectionModel();
+       if (!selected->hasSelection()) {
+               // Should have been grayed out, but OK.
+               return;
+       }
+       QModelIndexList rows = selected->selectedRows();
+       int first = rows.front().row(), last = rows.back().row();
+       playlist_clips->duplicate_clips(first, last);
+       playlist_selection_changed();
+}
+
+void MainWindow::playlist_remove()
+{
+       QItemSelectionModel *selected = ui->playlist->selectionModel();
+       if (!selected->hasSelection()) {
+               // Should have been grayed out, but OK.
+               return;
+       }
+       QModelIndexList rows = selected->selectedRows();
+       int first = rows.front().row(), last = rows.back().row();
+       playlist_clips->erase_clips(first, last);
+
+       // TODO: select the next one in the list?
+
+       playlist_selection_changed();
+}
+
+void MainWindow::playlist_move(int delta)
+{
+       QItemSelectionModel *selected = ui->playlist->selectionModel();
+       if (!selected->hasSelection()) {
+               // Should have been grayed out, but OK.
+               return;
+       }
+
+       QModelIndexList rows = selected->selectedRows();
+       int first = rows.front().row(), last = rows.back().row();
+       if ((delta == -1 && first == 0) ||
+           (delta == 1 && size_t(last) == playlist_clips->size() - 1)) {
+               // Should have been grayed out, but OK.
+               return;
+       }
+
+       playlist_clips->move_clips(first, last, delta);
+       playlist_selection_changed();
+}
+
 void MainWindow::play_clicked()
 {
        if (playlist_clips->empty()) return;
@@ -173,6 +237,7 @@ void MainWindow::play_clicked()
        const Clip &clip = *playlist_clips->clip(row);
        live_player->play_clip(clip, clip.stream_idx);
        playlist_clips->set_currently_playing(row);
+       playlist_selection_changed();
 }
 
 void MainWindow::live_player_clip_done()
@@ -365,3 +430,16 @@ void MainWindow::preview_single_frame(int64_t pts, unsigned stream_idx, MainWind
        fake_clip.pts_out = pts + 1;
        preview_player->play_clip(fake_clip, stream_idx);
 }
+
+void MainWindow::playlist_selection_changed()
+{
+       QItemSelectionModel *selected = ui->playlist->selectionModel();
+       bool any_selected = selected->hasSelection();
+       ui->playlist_duplicate_btn->setEnabled(any_selected);
+       ui->playlist_remove_btn->setEnabled(any_selected);
+       ui->playlist_move_up_btn->setEnabled(
+               any_selected && selected->selectedRows().front().row() > 0);
+       ui->playlist_move_down_btn->setEnabled(
+               any_selected && selected->selectedRows().back().row() < int(playlist_clips->size()) - 1);
+       ui->play_btn->setEnabled(!playlist_clips->empty());
+}
index 76124ae63fbb1e6518986dd1e7bc54fff00b3c30..8deca18b0712cc9ffd2e1ea8e9898070bdd33068 100644 (file)
@@ -43,10 +43,16 @@ private:
        void preview_angle_clicked(unsigned stream_idx);
        void play_clicked();
        void live_player_clip_done();
+       void playlist_duplicate();
+       void playlist_remove();
+       void playlist_move(int delta);
 
        enum Rounding { FIRST_AT_OR_AFTER, LAST_BEFORE };
        void preview_single_frame(int64_t pts, unsigned stream_idx, Rounding rounding);
 
+       // Also covers when the playlist itself changes.
+       void playlist_selection_changed();
+
        void resizeEvent(QResizeEvent *event) override;
        bool eventFilter(QObject *watched, QEvent *event) override;
 
index 2dd07d7ff306b11411f9363bbbb3a4f6837d7929..183fcea121446e026b0d44ae6030c940386e63a3 100644 (file)
       <widget class="QWidget" name="verticalLayoutWidget_4">
        <layout class="QVBoxLayout" name="verticalLayout_4">
         <item>
-         <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <widget class="QTableView" name="clip_list"/>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="clip_list_buttons">
           <item>
            <widget class="QPushButton" name="queue_btn">
             <property name="text">
              <string>Queue (&amp;Q)</string>
             </property>
+            <property name="icon">
+             <iconset theme="list-add"/>
+            </property>
            </widget>
           </item>
           <item>
             <property name="text">
              <string>Preview (&amp;W)</string>
             </property>
+            <property name="icon">
+             <iconset theme="media-playback-start"/>
+            </property>
            </widget>
           </item>
           <item>
            </widget>
           </item>
           <item>
-           <widget class="QPushButton" name="play_btn">
-            <property name="text">
-             <string>Play (space)</string>
+           <spacer name="horizontalSpacer_2">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
             </property>
-           </widget>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
           </item>
          </layout>
         </item>
-        <item>
-         <widget class="QTableView" name="clip_list"/>
-        </item>
         <item>
          <widget class="QTableView" name="playlist">
           <property name="selectionMode">
-           <enum>QAbstractItemView::SingleSelection</enum>
+           <enum>QAbstractItemView::ContiguousSelection</enum>
           </property>
           <property name="selectionBehavior">
            <enum>QAbstractItemView::SelectRows</enum>
           </property>
          </widget>
         </item>
+        <item>
+         <layout class="QHBoxLayout" name="playlist_buttons">
+          <item>
+           <widget class="QPushButton" name="playlist_duplicate_btn">
+            <property name="text">
+             <string>Duplicate</string>
+            </property>
+            <property name="icon">
+             <iconset theme="list-add"/>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="playlist_remove_btn">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="text">
+             <string>Remove</string>
+            </property>
+            <property name="icon">
+             <iconset theme="list-remove"/>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="playlist_move_up_btn">
+            <property name="text">
+             <string>Move up</string>
+            </property>
+            <property name="icon">
+             <iconset theme="go-up"/>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="playlist_move_down_btn">
+            <property name="text">
+             <string>Move down</string>
+            </property>
+            <property name="icon">
+             <iconset theme="go-down"/>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item>
+           <widget class="QPushButton" name="play_btn">
+            <property name="text">
+             <string>Play (space)</string>
+            </property>
+            <property name="icon">
+             <iconset theme="media-playback-start"/>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
        </layout>
       </widget>
      </widget>