# Standard library imports from typing import Optional # PyQt imports from PyQt6.QtCore import Qt from PyQt6.QtWidgets import ( QDialog, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QMainWindow, QPushButton, QVBoxLayout, ) # Third party imports # App imports from classes import ( ApplicationError, InsertTrack, MusicMusterSignals, TrackAndPlaylist, ) from helpers import ( get_relative_date, ms_to_mmss, ) import ds class TrackInsertDialog(QDialog): def __init__( self, parent: QMainWindow, playlist_id: int, add_to_header: Optional[bool] = False, ) -> None: """ Subclassed QDialog to manage track selection """ super().__init__(parent) self.playlist_id = playlist_id self.add_to_header = add_to_header self.setWindowTitle("Insert Track") # Title input on one line self.title_label = QLabel("Title:") self.title_edit = QLineEdit() self.title_edit.textChanged.connect(self.update_list) title_layout = QHBoxLayout() title_layout.addWidget(self.title_label) title_layout.addWidget(self.title_edit) # Track list self.track_list = QListWidget() self.track_list.itemDoubleClicked.connect(self.add_clicked) self.track_list.itemSelectionChanged.connect(self.selection_changed) # Note input on one line self.note_label = QLabel("Note:") self.note_edit = QLineEdit() note_layout = QHBoxLayout() note_layout.addWidget(self.note_label) note_layout.addWidget(self.note_edit) # Track path self.path = QLabel() path_layout = QHBoxLayout() path_layout.addWidget(self.path) # Buttons self.add_btn = QPushButton("Add") self.add_close_btn = QPushButton("Add and close") self.close_btn = QPushButton("Close") self.add_btn.clicked.connect(self.add_clicked) self.add_close_btn.clicked.connect(self.add_and_close_clicked) self.close_btn.clicked.connect(self.close) btn_layout = QHBoxLayout() btn_layout.addWidget(self.add_btn) btn_layout.addWidget(self.add_close_btn) btn_layout.addWidget(self.close_btn) # Main layout layout = QVBoxLayout() layout.addLayout(title_layout) layout.addWidget(self.track_list) layout.addLayout(note_layout) layout.addLayout(path_layout) layout.addLayout(btn_layout) self.setLayout(layout) self.resize(800, 600) width = ds.setting_get("dbdialog_width") or 800 height = ds.setting_get("dbdialog_height") or 800 self.resize(width, height) self.signals = MusicMusterSignals() def update_list(self, text: str) -> None: self.track_list.clear() if text.strip() == "": # Do not search or populate list if input is empty return if text.startswith("a/") and len(text) > 2: self.tracks = ds.tracks_by_artist(text[2:]) else: self.tracks = ds.tracks_by_title(text) for track in self.tracks: duration_str = ms_to_mmss(track.duration) last_played_str = get_relative_date(track.lastplayed) item_str = ( f"{track.title} - {track.artist} [{duration_str}] {last_played_str}" ) item = QListWidgetItem(item_str) item.setData(Qt.ItemDataRole.UserRole, track.track_id) self.track_list.addItem(item) def get_selected_track_id(self) -> int | None: selected_items = self.track_list.selectedItems() if selected_items: return selected_items[0].data(Qt.ItemDataRole.UserRole) return None def add_clicked(self): track_id = self.get_selected_track_id() note_text = self.note_edit.text() if track_id is None and not note_text: return insert_track_data = InsertTrack(self.playlist_id, track_id, note_text) self.title_edit.selectAll() self.title_edit.setFocus() self.note_edit.clear() self.title_edit.setFocus() if self.add_to_header: # The model will have the right-clicked row marked as a # selected_row so we only need to pass the playlist_id and # track_id. self.signals.signal_add_track_to_header.emit( TrackAndPlaylist(playlist_id=self.playlist_id, track_id=track_id) ) self.accept() else: self.signals.signal_insert_track.emit(insert_track_data) def add_and_close_clicked(self): self.add_clicked() self.accept() def selection_changed(self) -> None: """Display selected track path in dialog box""" self.path.setText("") track_id = self.get_selected_track_id() if track_id is None: return tracklist = [t for t in self.tracks if t.track_id == track_id] if not tracklist: return if len(tracklist) > 1: raise ApplicationError("More than one track returned") track = tracklist[0] self.path.setText(track.path)