diff --git a/README.md b/README.md
index 5150e50f..05def9a9 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+[](https://classroom.github.com/a/_uV8Mn8f)
# đ Projektarbete: JPA + Hibernate med GitHub-flöde
Projektet genomförs som antingen en Java CLI-applikation eller med hjÀlp av JavaFX om ni vill ha ett grafiskt grÀnssnitt.
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..fcd736ac
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,15 @@
+services:
+ myPod:
+ image: mysql:9.5.0
+ environment:
+ MYSQL_ROOT_PASSWORD: root
+ MYSQL_ROOT_HOST: "%"
+ MYSQL_DATABASE: myPodDB
+ MYSQL_USER: user
+ MYSQL_PASSWORD: pass
+ ports:
+ - "3306:3306"
+ volumes:
+ - mypod_data:/var/lib/mysql
+volumes:
+ mypod_data:
diff --git a/pom.xml b/pom.xml
index 909503d0..d68424a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,10 +45,64 @@
9.5.0
runtime
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.17.1
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ 2.17.1
+
io.github.classgraph
classgraph
4.8.184
+
+ org.openjfx
+ javafx-controls
+ 25
+
+
+ org.openjfx
+ javafx-fxml
+ 25
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+ 25
+ 25
+
+
+
+ org.openjfx
+ javafx-maven-plugin
+ 0.0.8
+
+
+
+ default-cli
+
+ org.example.MyPod
+ app
+ app
+ app
+ true
+ true
+ true
+
+
+
+
+
+
diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java
index 165e5cd5..b9d5de88 100644
--- a/src/main/java/org/example/App.java
+++ b/src/main/java/org/example/App.java
@@ -1,7 +1,12 @@
package org.example;
+import javafx.application.Application;
+
+
public class App {
public static void main(String[] args) {
- System.out.println("Hello There!");
+
+ Application.launch(MyPod.class, args);
+
}
}
diff --git a/src/main/java/org/example/DatabaseInitializer.java b/src/main/java/org/example/DatabaseInitializer.java
new file mode 100644
index 00000000..92eda0f4
--- /dev/null
+++ b/src/main/java/org/example/DatabaseInitializer.java
@@ -0,0 +1,67 @@
+package org.example;
+
+import org.example.entity.Album;
+import org.example.entity.Artist;
+import org.example.entity.Song;
+import org.example.repo.AlbumRepository;
+import org.example.repo.ArtistRepository;
+import org.example.repo.SongRepository;
+
+import java.util.List;
+
+public class DatabaseInitializer {
+
+ private final ItunesApiClient apiClient;
+
+ private final SongRepository songRepo;
+ private final AlbumRepository albumRepo;
+ private final ArtistRepository artistRepo;
+
+ public DatabaseInitializer(ItunesApiClient apiClient, SongRepository songRepo , AlbumRepository albumRepo, ArtistRepository artistRepo) {
+ this.apiClient = apiClient;
+ this.songRepo = songRepo;
+ this.albumRepo = albumRepo;
+ this.artistRepo = artistRepo;
+ }
+
+ public void init() {
+ if (songRepo.count() > 0) { //check if there is data already
+ return;
+ }
+
+ List searches = List.of("the+war+on+drugs",
+ "refused",
+ "thrice",
+ "16+horsepower",
+ "viagra+boys",
+ "geese",
+ "ghost",
+ "run+the+jewels",
+ "rammstein",
+ "salvatore+ganacci",
+ "baroness"
+ );
+ for (String term : searches) {
+ try {
+ apiClient.searchSongs(term).forEach(dto -> {
+ Artist ar = Artist.fromDTO(dto);
+ if (!artistRepo.existsByUniqueId(ar)) {
+ artistRepo.save(ar);
+ }
+
+ Album al = Album.fromDTO(dto, ar);
+ if (!albumRepo.existsByUniqueId(al)) {
+ albumRepo.save(al);
+ }
+
+ Song s = Song.fromDTO(dto, al);
+ if (!songRepo.existsByUniqueId(s)) {
+ songRepo.save(s);
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to fetch or persist data for search term: " + term, e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/example/ItunesApiClient.java b/src/main/java/org/example/ItunesApiClient.java
new file mode 100644
index 00000000..b0184f0f
--- /dev/null
+++ b/src/main/java/org/example/ItunesApiClient.java
@@ -0,0 +1,63 @@
+package org.example;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.net.URI;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ItunesApiClient {
+
+ private final HttpClient client;
+ private final ObjectMapper mapper;
+
+ public ItunesApiClient() {
+ this.client = HttpClient.newHttpClient();
+ this.mapper = new ObjectMapper();
+ mapper.registerModule(new JavaTimeModule());
+ }
+
+ public List searchSongs(String term) throws Exception {
+
+ String encodedTerm = URLEncoder.encode(term, StandardCharsets.UTF_8);
+ String url = "https://itunes.apple.com/search?term=" + encodedTerm + "&entity=song&attribute=artistTerm&limit=8";
+
+ HttpRequest request = HttpRequest.newBuilder()
+ .GET()
+ .uri(URI.create(url))
+ .timeout(Duration.ofSeconds(10))
+ .build();
+
+ HttpResponse response =
+ client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ // Kontrollera status
+ if (response.statusCode() != 200) {
+ throw new RuntimeException("API-fel: " + response.statusCode());
+ }
+
+ // Parse JSON
+ JsonNode root = mapper.readTree(response.body());
+ JsonNode results = root.get("results");
+ if (results == null || !results.isArray()) {
+ return List.of();
+ }
+
+ List songs = new ArrayList<>();
+ for (JsonNode node : results) {
+ ItunesDTO song = mapper.treeToValue(node, ItunesDTO.class);
+ songs.add(song);
+ }
+
+ return songs;
+ }
+}
+
diff --git a/src/main/java/org/example/ItunesDTO.java b/src/main/java/org/example/ItunesDTO.java
new file mode 100644
index 00000000..c85e89ac
--- /dev/null
+++ b/src/main/java/org/example/ItunesDTO.java
@@ -0,0 +1,23 @@
+package org.example;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import java.time.LocalDate;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record ItunesDTO(Long artistId,
+ Long collectionId,
+ Long trackId,
+ String trackName,
+ String artistName,
+ String collectionName,
+ String country,
+ String primaryGenreName,
+ LocalDate releaseDate,
+ Long trackCount,
+ Long trackTimeMillis) {
+
+ public int releaseYear() {
+ return releaseDate != null ? releaseDate.getYear() : 0;
+ }
+}
diff --git a/src/main/java/org/example/ItunesPlayList.java b/src/main/java/org/example/ItunesPlayList.java
new file mode 100644
index 00000000..d637f428
--- /dev/null
+++ b/src/main/java/org/example/ItunesPlayList.java
@@ -0,0 +1,345 @@
+package org.example;
+
+import javafx.beans.property.SimpleStringProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.*;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Huvudklass för GUI:t. Hanterar visning av bibliotek, spellistor och sökning.
+ */
+public class ItunesPlayList {
+
+ // --- DATAMODELLER ---
+
+ // En Map som lagrar alla spellistor. Nyckeln Àr namnet (t.ex. "Musik") och vÀrdet Àr listan med lÄtar.
+ private Map> allPlaylists = new HashMap<>();
+
+ // Listan med namn pÄ spellistor som visas i vÀnstermenyn (Sidebar).
+ // "ObservableList" gör att GUI:t uppdateras automatiskt om vi lÀgger till/tar bort namn hÀr.
+ private ObservableList playlistNames = FXCollections.observableArrayList();
+
+ // --- GUI KOMPONENTER ---
+
+ // Tabellen i mitten som visar lÄtarna
+ private TableView songTable = new TableView<>();
+
+ // Listan till vÀnster dÀr man vÀljer spellista
+ private ListView sourceList = new ListView<>();
+
+ // TextfÀlt för den "digitala displayen" högst upp
+ private Text lcdTitle = new Text("myTunes");
+ private Text lcdArtist = new Text("VĂ€lj bibliotek eller spellista");
+
+ /**
+ * Bygger upp hela grÀnssnittet och visar fönstret.
+ * @param dbSongs En lista med lÄtar hÀmtade frÄn databasen/backend.
+ */
+ public void showLibrary(List dbSongs) {
+ Stage stage = new Stage();
+
+ // Konvertera databas-objekten till vÄr interna DisplaySong-klass och skapa grundlistorna
+ initData(dbSongs);
+
+ // BorderPane Àr huvudlayouten: Top, Left, Center, Bottom
+ BorderPane root = new BorderPane();
+
+ // ---------------------------------------------------------
+ // 1. TOPPEN (Knappar, LCD-display, SökfÀlt)
+ // ---------------------------------------------------------
+ HBox topPanel = new HBox(15); // HBox lÀgger saker pÄ rad horisontellt
+ topPanel.getStyleClass().add("top-panel"); // CSS-klass för styling
+ topPanel.setPadding(new Insets(10, 15, 10, 15));
+ topPanel.setAlignment(Pos.CENTER_LEFT);
+
+ // Skapa LCD-displayen (den blÄ rutan med text)
+ StackPane lcdDisplay = createLCDDisplay();
+ // SÀg Ät displayen att vÀxa och ta upp ledig plats i bredd
+ HBox.setHgrow(lcdDisplay, Priority.ALWAYS);
+
+ // SökfÀltet
+ TextField searchField = new TextField();
+ searchField.setPromptText("Sök...");
+ searchField.getStyleClass().add("itunes-search");
+
+ // Lyssnare: NÀr texten Àndras i sökfÀltet, kör metoden filterSongs()
+ searchField.textProperty().addListener((obs, old, newVal) -> filterSongs(newVal));
+
+ // LĂ€gg till allt i toppen
+ topPanel.getChildren().addAll(
+ createRoundButton("âź"), createRoundButton("â¶"), createRoundButton("â"),
+ lcdDisplay, searchField
+ );
+
+ // ---------------------------------------------------------
+ // 2. VĂNSTER (Spellistorna)
+ // ---------------------------------------------------------
+ sourceList.setItems(playlistNames); // Koppla data till listan
+ sourceList.getStyleClass().add("source-list");
+ sourceList.setPrefWidth(200);
+
+ // Lyssnare: Vad hÀnder nÀr man klickar pÄ en spellista i menyn?
+ sourceList.getSelectionModel().selectedItemProperty().addListener((obs, old, newVal) -> {
+ if (newVal != null) {
+ searchField.clear(); // Rensa gammal sökning
+ // HÀmta lÄtlistan frÄn vÄr Map baserat pÄ namnet och visa i tabellen
+ songTable.setItems(allPlaylists.get(newVal));
+ }
+ });
+ sourceList.getSelectionModel().selectFirst(); // VÀlj första listan ("Musik") som startvÀrde
+
+ // ---------------------------------------------------------
+ // 3. MITTEN (LÄttabellen)
+ // ---------------------------------------------------------
+ setupTable(); // Konfigurerar kolumner och beteende för tabellen
+
+ // ---------------------------------------------------------
+ // 4. BOTTEN (Knappar för att hantera listor)
+ // ---------------------------------------------------------
+ HBox bottomPanel = new HBox(10);
+ bottomPanel.setPadding(new Insets(10));
+ bottomPanel.getStyleClass().add("bottom-panel");
+
+ Button btnAddList = new Button("+");
+ btnAddList.getStyleClass().add("list-control-button");
+ Button btnDeleteList = new Button("-");
+ btnDeleteList.getStyleClass().add("list-control-button");
+ Button btnMoveToPlaylist = new Button("LÀgg till LÄt i spellista");
+ Button btnRemoveSong = new Button("Ta bort lÄt frÄn lista");
+
+ // Koppla knapparna till metoder
+ btnAddList.setOnAction(e -> createNewPlaylist());
+ btnDeleteList.setOnAction(e -> deleteSelectedPlaylist());
+ btnRemoveSong.setOnAction(e -> removeSelectedSong());
+ btnMoveToPlaylist.setOnAction(e -> addSelectedSong(btnMoveToPlaylist));
+
+ bottomPanel.getChildren().addAll(btnAddList, btnDeleteList, new Separator(), btnMoveToPlaylist, btnRemoveSong);
+
+ // ---------------------------------------------------------
+ // SLUTMONTERING
+ // ---------------------------------------------------------
+
+ // SplitPane gör att anvÀndaren kan dra i grÀnsen mellan vÀnstermeny och tabell
+ SplitPane splitPane = new SplitPane(sourceList, songTable);
+ splitPane.setDividerPositions(0.25); // SÀtt startposition för avdelaren
+
+ root.setTop(topPanel);
+ root.setCenter(splitPane);
+ root.setBottom(bottomPanel);
+
+ Scene scene = new Scene(root, 950, 600);
+ // Ladda CSS-filen (mÄste ligga i resources-mappen)
+ scene.getStylesheets().add(getClass().getResource("/ipod_style.css").toExternalForm());
+
+ stage.setScene(scene);
+ stage.setTitle("myTunes");
+ stage.show();
+ }
+
+ /**
+ * HjÀlpmetod för att skapa LCD-displayen (bakgrund + text).
+ */
+ private StackPane createLCDDisplay() {
+ StackPane stack = new StackPane();
+ Rectangle bg = new Rectangle(350, 45);
+ bg.getStyleClass().add("lcd-background");
+
+ VBox textStack = new VBox(2);
+ textStack.setAlignment(Pos.CENTER);
+ lcdTitle.getStyleClass().add("lcd-title");
+ lcdArtist.getStyleClass().add("lcd-artist");
+
+ textStack.getChildren().addAll(lcdTitle, lcdArtist);
+ stack.getChildren().addAll(bg, textStack);
+ return stack;
+ }
+
+ /**
+ * HjÀlpmetod för att skapa en standardiserad knapp.
+ */
+ private Button createRoundButton(String icon) {
+ Button b = new Button(icon);
+ b.getStyleClass().add("itunes-button");
+ return b;
+ }
+
+ /**
+ * Konfigurerar kolumnerna i tabellen och hur data ska visas.
+ */
+ private void setupTable() {
+ // Skapa kolumner
+ TableColumn titleCol = new TableColumn<>("Namn");
+ // BerÀtta för kolumnen vilket fÀlt i DisplaySong den ska lÀsa frÄn (name)
+ titleCol.setCellValueFactory(d -> new SimpleStringProperty(d.getValue().name));
+
+ TableColumn artistCol = new TableColumn<>("Artist");
+ artistCol.setCellValueFactory(d -> new SimpleStringProperty(d.getValue().artist));
+
+ TableColumn albumCol = new TableColumn<>("Album");
+ albumCol.setCellValueFactory(d -> new SimpleStringProperty(d.getValue().album));
+
+ TableColumn timeCol = new TableColumn<>("LĂ€ngd");
+ timeCol.setCellValueFactory(d -> new SimpleStringProperty(d.getValue().time));
+
+ songTable.getColumns().setAll(titleCol, artistCol, albumCol, timeCol);
+ songTable.getStyleClass().add("song-table");
+
+ // Gör sÄ att kolumnerna fyller ut hela bredden
+ songTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
+
+ // Lyssnare: NÀr man klickar pÄ en rad i tabellen -> Uppdatera LCD-displayen
+ songTable.getSelectionModel().selectedItemProperty().addListener((obs, old, newVal) -> {
+ if (newVal != null) {
+ lcdTitle.setText(newVal.name);
+ lcdArtist.setText(newVal.artist);
+ }
+ });
+ }
+
+ /**
+ * Filtrerar lÄtarna i den aktiva listan baserat pÄ söktexten.
+ */
+ private void filterSongs(String searchText) {
+ String currentList = sourceList.getSelectionModel().getSelectedItem();
+ if (currentList == null) return;
+
+ // HÀmta originaldatan för den valda spellistan
+ ObservableList masterData = allPlaylists.get(currentList);
+
+ // Om sökfÀltet Àr tomt, visa allt
+ if (searchText == null || searchText.isEmpty()) {
+ songTable.setItems(masterData);
+ return;
+ }
+
+ // Skapa en filtrerad lista som omsluter masterData
+ FilteredList filteredData = new FilteredList<>(masterData, song -> {
+ String filter = searchText.toLowerCase();
+ // Returnera true om sökordet finns i namn, artist eller album
+ return song.name.toLowerCase().contains(filter) ||
+ song.artist.toLowerCase().contains(filter) ||
+ song.album.toLowerCase().contains(filter);
+ });
+
+ songTable.setItems(filteredData);
+ }
+
+ /**
+ * Omvandlar databas-objekten till GUI-objekt och skapar standardlistor.
+ */
+ private void initData(List dbSongs) {
+ ObservableList library = FXCollections.observableArrayList();
+
+ // Loopa igenom datan frÄn databasen
+ if (dbSongs != null) {
+ for (org.example.entity.Song s : dbSongs) {
+ // Hantera null-vÀrden snyggt (om artist eller album saknas)
+ String art = (s.getAlbum() != null && s.getAlbum().getArtist() != null) ? s.getAlbum().getArtist().getName() : "OkÀnd";
+ String alb = (s.getAlbum() != null) ? s.getAlbum().getName() : "OkÀnt";
+
+ // Skapa ett nytt DisplaySong-objekt
+ library.add(new DisplaySong(s.getTitle(), art, alb, s.getLength()));
+ }
+ }
+
+ // LĂ€gg in huvudbiblioteket "Musik"
+ allPlaylists.put("Musik", library);
+ playlistNames.add("Musik");
+
+ // Skapa en tom lista för "Favoriter"
+ allPlaylists.put("Favoriter", FXCollections.observableArrayList());
+ playlistNames.add("Favoriter");
+ }
+
+ /**
+ * Visar en dialogruta för att skapa en ny spellista.
+ */
+ private void createNewPlaylist() {
+ TextInputDialog d = new TextInputDialog("Ny lista");
+
+ // HÀr Àndrar du fönstrets titel och text
+ d.setTitle("Skapa ny spellista"); // ErsÀtter "BekrÀftelse"
+ d.setHeaderText("Ange namn pÄ din nya lista"); // Rubriken inuti rutan
+ d.setContentText("Namn:"); // Texten bredvid inmatningsfÀltet
+
+ d.showAndWait().ifPresent(name -> {
+ // Kontrollera att namnet inte Àr tomt och inte redan finns
+ if (!name.trim().isEmpty() && !allPlaylists.containsKey(name)) {
+ allPlaylists.put(name, FXCollections.observableArrayList());
+ playlistNames.add(name);
+ }
+ });
+ }
+
+ /**
+ * Tar bort vald spellista (men tillÄter inte att man tar bort "Musik").
+ */
+ private void deleteSelectedPlaylist() {
+ String sel = sourceList.getSelectionModel().getSelectedItem();
+ if (sel != null && !sel.equals("Musik")) {
+ allPlaylists.remove(sel);
+ playlistNames.remove(sel);
+ }
+ }
+
+ /**
+ * Tar bort vald lÄt frÄn den aktiva spellistan (ej frÄn huvudbiblioteket "Musik").
+ */
+ private void removeSelectedSong() {
+ DisplaySong sel = songTable.getSelectionModel().getSelectedItem();
+ String list = sourceList.getSelectionModel().getSelectedItem();
+ // Skydd: Man fÄr inte ta bort lÄtar direkt frÄn "Musik"-biblioteket i denna vy
+ if (sel != null && list != null && !list.equals("Musik")) {
+ allPlaylists.get(list).remove(sel);
+ }
+ }
+
+ /**
+ * Visar en popup-meny för att lÀgga till vald lÄt i en annan spellista.
+ */
+ private void addSelectedSong(Button anchor) {
+ DisplaySong sel = songTable.getSelectionModel().getSelectedItem();
+ if (sel == null) return; // Ingen lÄt vald
+
+ ContextMenu menu = new ContextMenu();
+ for (String n : playlistNames) {
+ if (n.equals("Musik")) continue; // Man kan inte lÀgga till i "Musik" (det Àr kÀllan)
+
+ MenuItem itm = new MenuItem(n);
+ itm.setOnAction(e -> {
+ // Om lÄten inte redan finns i listan, lÀgg till den
+ if (!allPlaylists.get(n).contains(sel)) {
+ allPlaylists.get(n).add(sel);
+ }
+ });
+ menu.getItems().add(itm);
+ }
+ // Visa menyn vid knappen
+ menu.show(anchor, anchor.getScene().getWindow().getX() + anchor.getLayoutX(), anchor.getScene().getWindow().getY() + anchor.getLayoutY());
+ }
+
+ /**
+ * En inre klass (DTO - Data Transfer Object) enbart för visning i tabellen.
+ * Detta skiljer GUI-logiken frÄn databas-entiteterna.
+ */
+ public static class DisplaySong {
+ String name, artist, album, time;
+ public DisplaySong(String n, String a, String al, Long t) {
+ this.name = n;
+ this.artist = a;
+ this.album = al;
+ this.time = String.valueOf(t);
+ }
+ }
+}
diff --git a/src/main/java/org/example/MyPod.java b/src/main/java/org/example/MyPod.java
new file mode 100644
index 00000000..5500942e
--- /dev/null
+++ b/src/main/java/org/example/MyPod.java
@@ -0,0 +1,374 @@
+package org.example;
+
+import jakarta.persistence.EntityManagerFactory;
+import javafx.concurrent.Task;
+import javafx.application.Platform;
+import javafx.application.Application;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.input.KeyCode;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.StackPane;
+import javafx.scene.layout.VBox;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+import org.example.entity.Album;
+import org.example.entity.Artist;
+import org.example.entity.Song;
+import org.example.repo.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Huvudklassen för applikationen "MyPod".
+ * Denna klass bygger upp GUI:t (simulerar en iPod) och hanterar navigering.
+ */
+public class MyPod extends Application {
+
+ // --- DATA-LAGER ---
+ // Repositories anvÀnds för att hÀmta data frÄn databasen istÀllet för att hÄrdkoda den.
+ private final SongRepository songRepo = new SongRepositoryImpl();
+ private final ArtistRepository artistRepo = new ArtistRepositoryImpl();
+ private final AlbumRepository albumRepo = new AlbumRepositoryImpl();
+ private final ItunesApiClient apiClient = new ItunesApiClient();
+
+ // Listor som hÄller datan vi hÀmtat frÄn databasen
+ private List songs;
+ private List artists;
+ private List albums;
+
+ // --- MENY-DATA ---
+ // Huvudmenyns alternativ. "ObservableList" Àr en speciell lista i JavaFX
+ // som GUI:t kan "lyssna" pÄ, Àven om vi hÀr mest anvÀnder den som en vanlig lista.
+ private final ObservableList mainMenu = FXCollections.observableArrayList(
+ "Songs", "Artists", "Albums", "Playlists");
+
+ // En lista med sjÀlva Label-objekten som visas pÄ skÀrmen (för att kunna markera/avmarkera dem)
+ private final List