示例#1
0
  void renameTrack() {
    if (interfaceDisabled) return;
    Playlist p = getSelectedPlaylist();
    if (p == null) return;
    Track t = getSelectedTrack();
    if (t == null) return;

    TextInputDialog dialog = new TextInputDialog(t.getTitle());
    dialog.setTitle(res.getString("rename_track"));
    dialog.setHeaderText(res.getString("rename_track"));
    dialog.setContentText(res.getString("enter_new_title"));
    dialog.getDialogPane().getStylesheets().add("/styles/dialogs.css");
    ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);
    Optional<String> result = dialog.showAndWait();
    result.ifPresent(
        title -> {
          if (StringUtils.isEmpty(title)) {
            return;
          }
          Cache.renameTrack(t, p, title);
          Platform.runLater(
              () -> {
                loadSelectedPlaylist();
              });
        });
  }
示例#2
0
 private ChoiceDialog<Playlist> playlistSelectionDialog(List<Playlist> items) {
   ChoiceDialog<Playlist> dialog = new ChoiceDialog<>(null, items);
   dialog.setTitle(res.getString("add_to"));
   dialog.setGraphic(null);
   ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);
   dialog.setHeaderText(res.getString("choose_playlist"));
   dialog.getDialogPane().getStylesheets().add("/styles/dialogs.css");
   if (rememberedPlaylist != null) dialog.setSelectedItem(rememberedPlaylist);
   return dialog;
 }
示例#3
0
 void deleteDeadFromOfflinePlaylist(Playlist p) {
   if (interfaceDisabled) return;
   infoLabel.setText(res.getString("deleting_dead_items"));
   log.info("Deleting dead items from " + p.getTitle());
   Integer deleted = Cache.deleteDead(p);
   infoLabel.setText(String.format(res.getString("deleted_dead_items"), deleted));
   log.info("Deleted dead items from " + p.getTitle() + ": " + deleted);
   Platform.runLater(
       () -> {
         loadSelectedPlaylist();
       });
 }
示例#4
0
 void addToRememberedPlaylist(Track t) {
   if (interfaceDisabled) return;
   if (t == null) return;
   if (rememberedPlaylist != null) {
     Cache.pushToPlaylist(t, rememberedPlaylist);
     infoAndAnnounce(
         String.format(
             res.getString("track_added_to_playlist"),
             rememberedPlaylist.getTitle(),
             t.getTitle()));
     if (rememberedPlaylist == getSelectedPlaylist()) loadSelectedPlaylist();
   } else {
     infoAndAnnounce(res.getString("remembered_not_yet"));
   }
 }
示例#5
0
 void addToPlaylist(List<File> files, Playlist p) {
   if (interfaceDisabled) return;
   new Thread(
           () -> {
             setInterfaceDisabled(true);
             int total = files.size();
             AtomicInteger current = new AtomicInteger(0);
             Cache.pushToPlaylist(
                 files,
                 p,
                 (Track t) -> {
                   Platform.runLater(
                       () -> {
                         infoLabel.setText(
                             String.format(
                                 res.getString("processed"),
                                 current.incrementAndGet(),
                                 total,
                                 t.getTitle()));
                         if (p == getSelectedPlaylist()) {
                           tracksView.getItems().remove(t);
                           tracksView.getItems().add(0, t);
                         }
                       });
                 });
             setInterfaceDisabled(false);
           })
       .start();
   if (p == getSelectedPlaylist()) {
     Platform.runLater(
         () -> {
           loadSelectedPlaylist();
         });
   }
 }
示例#6
0
 void setupPlaylistsContextMenu() {
   ContextMenu cm = playlistsContextMenu;
   MenuItem createOffline =
       menuItem(
           res.getString("create_offline_playlist"),
           () -> {
             createOfflinePlaylist();
           });
   MenuItem setAsFeatured =
       menuItem(
           res.getString("set_as_featured"),
           () -> {
             Playlist selectedPlaylist =
                 (Playlist) playlistsView.getSelectionModel().getSelectedItem();
             if (selectedPlaylist != null) {
               rememberedPlaylist = selectedPlaylist;
               Settings.rememberedPlaylistId = selectedPlaylist.getId();
               infoLabel.setText(
                   String.format(res.getString("featured_set"), selectedPlaylist.getTitle()));
             }
           });
   MenuItem rename =
       menuItem(
           "(...) " + res.getString("rename_playlist"),
           () -> {
             renamePlaylist();
           });
   MenuItem delete =
       menuItem(
           "(-) " + res.getString("delete_playlist"),
           () -> {
             deletePlaylist();
           });
   cm.getItems()
       .addAll(
           createOffline,
           new SeparatorMenuItem(),
           setAsFeatured,
           new SeparatorMenuItem(),
           rename,
           delete);
 }
示例#7
0
  @FXML
  void refreshPlaylists() {
    infoLabel.setText(res.getString("refreshing"));
    log.info("Refreshing playlists");

    ObservableList<Playlist> items = playlistsView.getItems();
    items.clear();
    items.addAll(Cache.playlists());

    if (Settings.rememberedPlaylistId != null)
      for (Playlist p : items) {
        if (p.getId().equals(Settings.rememberedPlaylistId)) {
          rememberedPlaylist = p;
          break;
        }
      }

    infoLabel.setText(res.getString("playlists_refreshed"));
    log.info("Playlists refreshed");
  }
示例#8
0
 void createOfflinePlaylist() {
   if (interfaceDisabled) return;
   TextInputDialog dialog = new TextInputDialog(res.getString("new_offline_playlist"));
   dialog.setTitle(res.getString("create_new_offline_playlist"));
   dialog.setHeaderText(res.getString("create_new_playlist"));
   dialog.setContentText(res.getString("enter_playlist_name"));
   dialog.getDialogPane().getStylesheets().add("/styles/dialogs.css");
   ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);
   Optional<String> result = dialog.showAndWait();
   result.ifPresent(
       title -> {
         Platform.runLater(
             () -> {
               Playlist newPlaylist = Cache.createPlaylist(title);
               Platform.runLater(
                   () -> {
                     playlistsView.getItems().add(newPlaylist);
                     playlistsView.getSelectionModel().select(newPlaylist);
                   });
             });
       });
 }
示例#9
0
  void deletePlaylist() {
    if (interfaceDisabled) return;
    Playlist p = getSelectedPlaylist();
    if (p == null) return;
    Alert alert = new Alert(AlertType.CONFIRMATION);
    ButtonType btYes = new ButtonType(res.getString("yes"), ButtonBar.ButtonData.YES);
    ButtonType btCancel =
        new ButtonType(res.getString("cancel"), ButtonBar.ButtonData.CANCEL_CLOSE);
    alert.getButtonTypes().setAll(btYes, btCancel);
    alert.setTitle(res.getString("delete_playlist"));
    alert.setHeaderText(res.getString("delete_playlist"));
    alert.setContentText(String.format(res.getString("delete_confirm"), p.getTitle()));
    alert.getDialogPane().getStylesheets().add("/styles/dialogs.css");
    ((Stage) alert.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);

    Optional<ButtonType> result = alert.showAndWait();
    if (result.get() == btYes) {
      Cache.deletePlaylist(p);
      Platform.runLater(
          () -> {
            updatePlaylists();
          });
    }
  }
示例#10
0
  void setupTracksContextMenu() {
    ContextMenu cm = tracksContextMenu;
    List<MenuItem> items = cm.getItems();
    items.clear();

    Playlist pl = getSelectedPlaylist();
    if (pl == null) {
      items.add(
          menuItem(
              "-> " + res.getString("add_to"),
              () -> {
                menuAddToPlaylist();
              }));
      if (rememberedPlaylist != null) {
        items.add(
            menuItem(
                "-> "
                    + String.format(
                        res.getString("add_to_featured"), rememberedPlaylist.getTitle()),
                () -> {
                  Track t = getSelectedTrack();
                  if (t != null) {
                    addToPlaylist(t, rememberedPlaylist);
                  }
                }));
      }
    } else {
      items.add(
          menuItem(
              "(+) " + res.getString("add_files"),
              () -> {
                FileChooser fileChooser = new FileChooser();
                fileChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
                fileChooser.setTitle(String.format(res.getString("add_files_to"), pl.getTitle()));
                fileChooser
                    .getExtensionFilters()
                    .addAll(new FileChooser.ExtensionFilter("MP3", "*.mp3"));
                List<File> files = fileChooser.showOpenMultipleDialog(null);
                if (files != null) {
                  addToPlaylist(files, pl);
                }
              }));
      items.add(
          menuItem(
              "(+) " + res.getString("add_folder_recursively"),
              () -> {
                DirectoryChooser dirChooser = new DirectoryChooser();
                dirChooser.setTitle(String.format(res.getString("add_folder_to"), pl.getTitle()));
                dirChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
                File dir = dirChooser.showDialog(null);
                if (dir == null) return;
                Collection<File> files = FileUtils.listFiles(dir, new String[] {"mp3"}, true);
                addToPlaylist(new ArrayList(files), pl);
              }));
      items.add(new SeparatorMenuItem());
      if (rememberedPlaylist != null)
        items.add(
            menuItem(
                "-> "
                    + String.format(
                        res.getString("add_to_featured"), rememberedPlaylist.getTitle()),
                () -> {
                  Track t = getSelectedTrack();
                  if (t != null) addToPlaylist(t, rememberedPlaylist);
                }));
      items.add(
          menuItem(
              "-> " + res.getString("add_to"),
              () -> {
                menuAddToPlaylist();
              }));
      items.add(new SeparatorMenuItem());
      items.add(
          menuItem(
              "(...) " + res.getString("rename_track"),
              () -> {
                renameTrack();
              }));
      items.add(new SeparatorMenuItem());
      items.add(
          menuItem(
              "(-) " + res.getString("delete_track") + pl.getTitle(),
              () -> {
                deleteTrackFromPlaylist();
              }));
      items.add(
          menuItem(
              "(-) " + res.getString("delete_dead_items"),
              () -> {
                Track t = getSelectedTrack();
                if (t == null) {
                  return;
                }
                deleteDeadFromOfflinePlaylist(pl);
              }));
    }
  }
示例#11
0
 void setupShortcuts() {
   Platform.runLater(
       () -> {
         stateButton
             .getScene()
             .getAccelerators()
             .put(
                 new KeyCodeCombination(KeyCode.F3),
                 (Runnable)
                     () -> {
                       if (!interfaceDisabled) SwingAppenderUI.getInstance().show();
                     });
         stateButton
             .getScene()
             .getAccelerators()
             .put(
                 new KeyCodeCombination(KeyCode.F),
                 (Runnable)
                     () -> {
                       if (!interfaceDisabled) findPlayingTrack();
                     });
         stateButton
             .getScene()
             .getAccelerators()
             .put(
                 new KeyCodeCombination(KeyCode.F5),
                 (Runnable)
                     () -> {
                       if (!interfaceDisabled) Platform.runLater(() -> refreshPlaylists());
                     });
         stateButton
             .getScene()
             .getAccelerators()
             .put(
                 new KeyCodeCombination(KeyCode.F4),
                 (Runnable)
                     () -> {
                       if (numpadOff) {
                         setupGlobalKeys();
                         Platform.runLater(
                             () -> {
                               infoLabel.setText(res.getString("global_hotkeys_on"));
                               log.info("Global hotkeys ON");
                             });
                       } else {
                         hotkeysProvider.reset();
                         Platform.runLater(
                             () -> {
                               infoLabel.setText(res.getString("global_hotkeys_off"));
                               log.info("Global hotkeys OFF");
                             });
                       }
                       numpadOff = !numpadOff;
                     });
         tracksView.setOnKeyPressed(
             (evt) -> {
               if (interfaceDisabled) return;
               if (evt.getCode().equals(KeyCode.DELETE)) {
                 deleteTrackFromPlaylist();
                 evt.consume();
               } else if (evt.getCode().equals(KeyCode.ENTER)) {
                 playSelectedTrack();
                 evt.consume();
               } else if (evt.getCode().equals(KeyCode.F2)) {
                 renameTrack();
                 evt.consume();
               }
             });
         playlistsView.setOnKeyPressed(
             (evt) -> {
               if (interfaceDisabled) return;
               if (evt.getCode().equals(KeyCode.F2)) {
                 renamePlaylist();
                 evt.consume();
               }
             });
       });
   setupGlobalKeys();
 }
示例#12
0
  @Override
  public void initialize(URL location, ResourceBundle resources) {
    AppController.instance = this;
    this.res = resources;

    ObservableList<String> modeItems =
        FXCollections.observableArrayList(
            Settings.MODE_NEXT, Settings.MODE_RANDOM, Settings.MODE_SAME);
    modeList.setItems(modeItems);
    modeList.getSelectionModel().select(Settings.currentMode);
    modeList
        .valueProperty()
        .addListener(
            (ObservableValue ov, Object oldVal, Object newVal) -> {
              Settings.currentMode = (String) newVal;
            });

    setupPlaylistsView();
    setupPlaylistsContextMenu();

    setupTracksView();

    tracksView.setContextMenu(tracksContextMenu);
    tracksView.setOnContextMenuRequested(
        (ContextMenuEvent evt) -> {
          setupTracksContextMenu();
          evt.consume();
        });
    tracksView
        .getSelectionModel()
        .selectedItemProperty()
        .addListener(
            (ObservableValue observable, Object oldValue, Object newValue) -> {
              Settings.lastTrackId = newValue != null ? ((Track) newValue).getId() : null;
            });

    playlistsView
        .getSelectionModel()
        .selectedItemProperty()
        .addListener(
            (ObservableValue observable, Object oldValue, Object newValue) -> {
              loadSelectedPlaylist();
              Settings.lastPlaylistId = newValue != null ? ((Playlist) newValue).getId() : null;
              searching = false;
              searchText.setText(StringUtils.EMPTY);
            });

    volumeSlider.setCursor(Cursor.HAND);
    volumeSlider
        .valueProperty()
        .addListener(
            (ObservableValue<? extends Number> ov, Number oldVal, Number newVal) -> {
              if (player != null) {
                player.setVolume(newVal.doubleValue());
              }
              Settings.currentVolume = newVal.doubleValue();
              volumeLabel.setText(
                  String.valueOf((int) Math.ceil(newVal.doubleValue() * 100)) + "%");
            });
    volumeSlider.setValue(Settings.currentVolume);

    imagePlay = new ImageView(new Image(getClass().getResourceAsStream("/images/button_play.png")));
    imagePlay.setScaleX(0.40);
    imagePlay.setScaleY(0.40);
    imagePause =
        new ImageView(new Image(getClass().getResourceAsStream("/images/button_pause.png")));
    imagePause.setScaleX(0.5);
    imagePause.setScaleY(0.5);
    imageSettings =
        new ImageView(new Image(getClass().getResourceAsStream("/images/settings.png")));
    imageSettings.setScaleX(0.5);
    imageSettings.setScaleY(0.5);
    imageUpdate = new ImageView(new Image(getClass().getResourceAsStream("/images/refresh.png")));
    imageUpdate.setScaleX(0.5);
    imageUpdate.setScaleY(0.5);
    imageAdd = new ImageView(new Image(getClass().getResourceAsStream("/images/add.png")));
    imageAdd.setScaleX(0.5);
    imageAdd.setScaleY(0.5);
    imageRename = new ImageView(new Image(getClass().getResourceAsStream("/images/rename.png")));
    imageRename.setScaleX(0.5);
    imageRename.setScaleY(0.5);
    imageDelete = new ImageView(new Image(getClass().getResourceAsStream("/images/delete.png")));
    imageDelete.setScaleX(0.5);
    imageDelete.setScaleY(0.5);

    stateButton.setGraphic(imagePlay);
    settingsButton.setGraphic(imageSettings);
    refreshButton.setGraphic(imageUpdate);
    refreshButton.setTooltip(new Tooltip(res.getString("tooltip_sync")));

    addButton.setGraphic(imageAdd);
    addButton.setOnAction((evt) -> createOfflinePlaylist());
    addButton.setTooltip(new Tooltip(res.getString("tooltip_add_offline")));
    renameButton.setGraphic(imageRename);
    renameButton.setOnAction(evt -> renamePlaylist());
    renameButton.setTooltip(new Tooltip(res.getString("rename_playlist")));
    deleteButton.setGraphic(imageDelete);
    deleteButton.setOnAction(evt -> deletePlaylist());
    deleteButton.setTooltip(new Tooltip(res.getString("delete_playlist")));

    loadProgressBar.setCursor(Cursor.HAND);
    volumeSlider.setCursor(Cursor.HAND);
    updatePlaylists();
    if (Settings.lastPlaylistId != null) {
      currentPlaylist =
          ((ObservableList<Playlist>) playlistsView.getItems())
              .stream()
              .filter((playlist) -> playlist.getId().equals(Settings.lastPlaylistId))
              .findFirst()
              .orElse(null);
      playlistsView.getSelectionModel().select(currentPlaylist);
      playlistsView.scrollTo(currentPlaylist);
      Platform.runLater(
          () -> {
            if (Settings.lastTrackId != null) {
              currentTrack =
                  ((ObservableList<Track>) tracksView.getItems())
                      .stream()
                      .filter((track) -> track.getId().equals(Settings.lastTrackId))
                      .findFirst()
                      .orElse(null);
              tracksView.getSelectionModel().select(currentTrack);
              tracksView.scrollTo(currentTrack);
            }
          });
    }
    new Timer()
        .scheduleAtFixedRate(
            new TimerTask() {
              @Override
              public void run() {
                updatePlayProgress();
              }
            },
            100,
            100);
    setupTracksContextMenu();

    setupShortcuts();

    Platform.runLater(
        () -> {
          tracksView.requestFocus();
        });
  }
示例#13
0
public class AppController implements Initializable {

  public static final Image[] logoImages =
      new Image[] {
        new Image(AppController.class.getResourceAsStream("/images/logo_tray_16.png")),
        new Image(AppController.class.getResourceAsStream("/images/logo_tray_32.png")),
        new Image(AppController.class.getResourceAsStream("/images/logo_tray_48.png")),
        new Image(AppController.class.getResourceAsStream("/images/logo_tray_64.png")),
        new Image(AppController.class.getResourceAsStream("/images/logo_tray_96.png")),
        new Image(AppController.class.getResourceAsStream("/images/logo_tray_128.png"))
      };
  private static final Logger log = Logger.getLogger(AppController.class);
  private static AppController instance;

  private static MediaPlayer player;
  private final Provider hotkeysProvider = Provider.getCurrentProvider(true);
  private final ContextMenu playlistsContextMenu = new ContextMenu();
  private final ContextMenu tracksContextMenu = new ContextMenu();
  InputStream currentAudioIn;
  int timeUpdate = 0;
  private ResourceBundle res = App.res;
  private final Label offlinePlaceHolder =
      new Label(res.getString("empty_playlist_placeholder_offline"));
  private final Label searchPlaceholder = new Label(res.getString("no_results_found"));
  private final Label playlistPlaceholder = new Label(res.getString("playlist_placeholder"));
  private boolean isPlaying = false;
  private boolean interfaceDisabled = false;
  private boolean searching = false;
  private boolean tweenBlock = false;
  private Playlist currentPlaylist;
  private Playlist rememberedPlaylist;
  private Track currentTrack;
  private Deque<PlaylistAndTrack> playStack = new LinkedList<>();
  private boolean numpadOff = false;
  private Node imagePlay;
  private Node imagePause;
  private Node imageSettings;
  private Node imageUpdate;
  private Node imageAdd;
  private Node imageRename;
  private Node imageDelete;
  @FXML private TableView playlistsView;
  @FXML private TableView tracksView;
  @FXML private Button stateButton;
  @FXML private Label titleLabel;
  @FXML private Label timeLabel;
  @FXML private ProgressBar loadProgressBar;
  @FXML private Label volumeLabel;
  @FXML private Slider volumeSlider;
  @FXML private ChoiceBox modeList;
  @FXML private Label infoLabel;
  @FXML private TextField searchText;
  @FXML private Button searchButton;
  @FXML private Button settingsButton;
  @FXML private Button refreshButton;
  @FXML private Button addButton;
  @FXML private Button renameButton;
  @FXML private Button deleteButton;
  private Stage stage;
  private boolean findTrack = false;

  public static void afterSettings() {
    setupGlobalHotkeys();
  }

  public static void setupGlobalHotkeys() {
    AppController.instance.setupGlobalKeys();
  }

  void fillTracks(List<Track> tracks) {
    ObservableList<Track> items = FXCollections.observableArrayList(tracks);
    tracksView.setItems(items);
  }

  @FXML
  void toggleState() {
    if (tweenBlock) return;
    tweenBlock = true;
    if (isPlaying) {
      stateButton.setGraphic(imagePlay);
      pause(
          () -> {
            tweenBlock = false;
          });
    } else {
      if (currentTrack != null) {
        stateButton.setGraphic(imagePause);
        resume(
            () -> {
              tweenBlock = false;
            });
      }
    }
  }

  @FXML
  void seek(MouseEvent evt) {
    if (player != null) {
      Duration d = player.getMedia().getDuration();
      double millis = (evt.getX() / loadProgressBar.getWidth()) * d.toMillis();
      Duration s = new Duration(millis);
      player.seek(s);
    }
  }

  void stop(Runnable callback) {
    if (player != null) {
      new Tween(player.getVolume())
          .tweenToZero(
              0.06,
              10L,
              (curVolume) -> {
                player.setVolume(curVolume);
              },
              (zeroVolume) -> {
                isPlaying = false;
                try {
                  if (currentAudioIn != null) {
                    currentAudioIn.close();
                  }
                  player.stop();
                  callback.run();
                } catch (IOException | IllegalStateException | NullPointerException ex) {
                  log.log(Level.ERROR, null, ex);
                }
              });
    } else {
      callback.run();
    }
  }

  void pause(Runnable callback) {
    if (player != null) {
      new Tween(player.getVolume())
          .tweenToZero(
              0.06,
              10L,
              (curVolume) -> {
                player.setVolume(curVolume);
              },
              (zeroVolume) -> {
                player.pause();
                callback.run();
              });
      isPlaying = false;
    } else {
      callback.run();
    }
  }

  void resume(Runnable callback) {
    if (player != null) {
      player.setVolume(0d);
      player.play();
      new Tween(player.getVolume())
          .tweenToValue(
              Settings.currentVolume,
              0.06,
              10L,
              (currentVolume) -> {
                player.setVolume(currentVolume);
              },
              (valueVolume) -> {
                player.setVolume(valueVolume);
                callback.run();
              });
      isPlaying = true;
    } else {
      callback.run();
    }
  }

  void playNext() {
    List<Track> tracks = tracksView.getItems();
    int selected = tracksView.getSelectionModel().getSelectedIndex();
    int next = selected + 1;
    if (next >= tracks.size()) {
      next = 0;
    }
    tracksView.getSelectionModel().select(next);
    tracksView.scrollTo(tracksView.getSelectionModel().getSelectedIndex());
    Track nextTrack = (Track) tracksView.getSelectionModel().getSelectedItem();
    playTrack(nextTrack);
  }

  void pushToPlayStack(Playlist p, Track t) {
    if (playStack.size() > 10000) {
      playStack.removeLast();
    }
    playStack.push(new PlaylistAndTrack(p, t));
  }

  void playSame() {
    player.seek(Duration.ZERO);
    player.play();
  }

  void playRandom() {
    List<Track> tracks = tracksView.getItems();
    Random random = new Random();
    int size = tracks.size();
    int r = random.nextInt(size - 1);
    tracksView.getSelectionModel().select(r);
    tracksView.scrollTo(tracksView.getSelectionModel().getSelectedIndex());
    Track nextTrack = (Track) tracksView.getSelectionModel().getSelectedItem();
    playTrack(nextTrack);
  }

  void playPrevious() {
    if (playStack.size() < 2) return;
    System.out.println("------------");
    for (PlaylistAndTrack pnt : playStack) {
      System.out.println(pnt.track.getTitle());
    }
    playStack.removeFirst();
    PlaylistAndTrack prevPlaylistAndTrack = playStack.removeFirst();
    playlistsView.getSelectionModel().select(prevPlaylistAndTrack.playlist);
    tracksView.getSelectionModel().select(prevPlaylistAndTrack.track);
    tracksView.scrollTo(tracksView.getSelectionModel().getSelectedIndex());
    playTrack(prevPlaylistAndTrack.track);
  }

  void updatePlayProgress() {
    if (player != null && Tray.stage.isShowing()) {
      Duration d = player.getMedia().getDuration();
      Duration t = player.getCurrentTime();
      loadProgressBar.setProgress(t.toMillis() / d.toMillis());
      timeUpdate++;
      if (timeUpdate == 10) {
        timeUpdate = 0;
        Platform.runLater(
            () -> {
              timeLabel.setText(formatTime(d, t));
            });
      }
    }
  }

  void updatePlaylists() {
    if (interfaceDisabled) return;
    final ObservableList<Playlist> items = playlistsView.getItems();
    items.clear();
    items.addAll(Cache.playlists());
    if (Settings.rememberedPlaylistId != null)
      for (Playlist p : items) {
        if (p.getId().equals(Settings.rememberedPlaylistId)) {
          rememberedPlaylist = p;
          break;
        }
      }
  }

  @FXML
  void loadSelectedPlaylist() {
    Playlist p = (Playlist) playlistsView.getSelectionModel().getSelectedItem();
    if (p == null) return;
    tracksView.setPlaceholder(offlinePlaceHolder);
    Platform.runLater(
        () -> {
          List<Track> tracks = p.getTracks();
          tracksView.getItems().clear();
          for (int i = tracks.size() - 1; i >= 0; i--) {
            tracksView.getItems().add(tracks.get(i));
          }
          if (findTrack) {
            Platform.runLater(
                () -> {
                  tracksView.getSelectionModel().select(currentTrack);
                  tracksView.scrollTo(currentTrack);
                  tracksView.requestFocus();
                  findTrack = false;
                });
          } else {
            if (tracksView.getItems().size() > 0) {
              tracksView.scrollTo(0);
            }
          }
        });
  }

  @FXML
  void playTrackFromKeyboard(KeyEvent e) {
    if (e.getCode() == KeyCode.ENTER) {
      playSelectedTrack();
      e.consume();
    }
  }

  @FXML
  void playTrack(Track t) {
    if (tweenBlock) return;
    tweenBlock = true;
    stop(
        () -> {
          currentPlaylist = (Playlist) playlistsView.getSelectionModel().getSelectedItem();
          currentTrack = t;
          if (player != null) {
            player.dispose();
          }
          if (t == null) {
            return;
          }
          timeUpdate = 9;
          if (currentPlaylist != null) {
            Tray.announce("[" + currentPlaylist.getTitle() + "] " + t.getTitle());
          }

          Platform.runLater(
              () -> {
                infoLabel.setText(res.getString("playing_offline") + t.getTitle());
                log.info("Playing offline: " + t.getTitle());
              });
          try {
            Media media = new Media(Cache.getContent(t).toURI().toString());
            player = new MediaPlayer(media);
            player.setVolume(Settings.currentVolume);
            player.play();
          } catch (MediaException ex) {
            Platform.runLater(
                () -> {
                  infoLabel.setText(
                      String.format(res.getString("cant_play_offline_not_exist"), t.getTitle()));
                  log.error("Can't play: seems that track does not exist - " + t.getTitle());
                });
          }

          if (player != null) {
            pushToPlayStack(getSelectedPlaylist(), t);
            player.setOnEndOfMedia(
                () -> {
                  String mode = Settings.currentMode;
                  if (mode.equals(Settings.MODE_NEXT)) {
                    playNext();
                  } else if (mode.equals(Settings.MODE_RANDOM)) {
                    playRandom();
                  } else if (mode.equals(Settings.MODE_SAME)) {
                    playSame();
                  }
                });
          }

          Platform.runLater(
              () -> {
                isPlaying = true;
                tweenBlock = false;
                titleLabel.setText(t.getTitle());
                stateButton.setGraphic(imagePause);
              });
        });
  }

  @FXML
  void search() {
    searching = true;
    new Thread(
            () -> {
              List<Track> tracks = Cache.search(searchText.getText());
              tracksView.setPlaceholder(searchPlaceholder);
              Platform.runLater(
                  () -> {
                    if (tracksView.getItems().size() > 0) {
                      tracksView.scrollTo(0);
                    }
                    tracksView.getItems().clear();
                    tracksView.getItems().addAll(tracks.toArray());
                  });
            })
        .start();
  }

  @FXML
  void refreshPlaylists() {
    infoLabel.setText(res.getString("refreshing"));
    log.info("Refreshing playlists");

    ObservableList<Playlist> items = playlistsView.getItems();
    items.clear();
    items.addAll(Cache.playlists());

    if (Settings.rememberedPlaylistId != null)
      for (Playlist p : items) {
        if (p.getId().equals(Settings.rememberedPlaylistId)) {
          rememberedPlaylist = p;
          break;
        }
      }

    infoLabel.setText(res.getString("playlists_refreshed"));
    log.info("Playlists refreshed");
  }

  private void setupPlaylistsView() {
    TableColumn titleColumn = new TableColumn("Title");
    titleColumn.setMinWidth(USE_COMPUTED_SIZE);
    titleColumn.setSortable(false);
    titleColumn.setCellValueFactory(new PropertyValueFactory<>("title"));

    TableColumn countColumn = new TableColumn("Tracks");
    countColumn.setMinWidth(60);
    countColumn.setMaxWidth(60);
    countColumn.setSortable(false);
    countColumn.setCellValueFactory(new PropertyValueFactory<>("count"));
    countColumn.getStyleClass().add("text-layout-right");

    playlistsView.getColumns().clear();
    playlistsView.getColumns().addAll(titleColumn, countColumn);
    playlistsView.setContextMenu(playlistsContextMenu);
    playlistsView.setPlaceholder(playlistPlaceholder);
    playlistsView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
  }

  private void setupTracksView() {
    TableColumn titleColumn = new TableColumn("Title");
    titleColumn.setSortable(false);
    titleColumn.setMinWidth(USE_COMPUTED_SIZE);
    titleColumn.setCellValueFactory(new PropertyValueFactory<>("title"));

    TableColumn durationColumn = new TableColumn("Duration");
    durationColumn.setMinWidth(60);
    durationColumn.setMaxWidth(60);
    durationColumn.setSortable(false);
    durationColumn.setCellValueFactory(new PropertyValueFactory<>("durationFormatted"));
    durationColumn.getStyleClass().add("text-layout-center");

    tracksView.getColumns().clear();
    tracksView.getColumns().addAll(titleColumn, durationColumn);
    tracksView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
  }

  public void onShown() {
    hideTablesHeaders();
  }

  private void hideTablesHeaders() {
    List<Pane> headers =
        Arrays.asList(
            (Pane) tracksView.lookup("TableHeaderRow"),
            (Pane) playlistsView.lookup("TableHeaderRow"));
    headers.forEach(
        header -> {
          header.setMaxHeight(0);
          header.setMinHeight(0);
          header.setPrefHeight(0);
          header.setVisible(false);
        });
  }

  @Override
  public void initialize(URL location, ResourceBundle resources) {
    AppController.instance = this;
    this.res = resources;

    ObservableList<String> modeItems =
        FXCollections.observableArrayList(
            Settings.MODE_NEXT, Settings.MODE_RANDOM, Settings.MODE_SAME);
    modeList.setItems(modeItems);
    modeList.getSelectionModel().select(Settings.currentMode);
    modeList
        .valueProperty()
        .addListener(
            (ObservableValue ov, Object oldVal, Object newVal) -> {
              Settings.currentMode = (String) newVal;
            });

    setupPlaylistsView();
    setupPlaylistsContextMenu();

    setupTracksView();

    tracksView.setContextMenu(tracksContextMenu);
    tracksView.setOnContextMenuRequested(
        (ContextMenuEvent evt) -> {
          setupTracksContextMenu();
          evt.consume();
        });
    tracksView
        .getSelectionModel()
        .selectedItemProperty()
        .addListener(
            (ObservableValue observable, Object oldValue, Object newValue) -> {
              Settings.lastTrackId = newValue != null ? ((Track) newValue).getId() : null;
            });

    playlistsView
        .getSelectionModel()
        .selectedItemProperty()
        .addListener(
            (ObservableValue observable, Object oldValue, Object newValue) -> {
              loadSelectedPlaylist();
              Settings.lastPlaylistId = newValue != null ? ((Playlist) newValue).getId() : null;
              searching = false;
              searchText.setText(StringUtils.EMPTY);
            });

    volumeSlider.setCursor(Cursor.HAND);
    volumeSlider
        .valueProperty()
        .addListener(
            (ObservableValue<? extends Number> ov, Number oldVal, Number newVal) -> {
              if (player != null) {
                player.setVolume(newVal.doubleValue());
              }
              Settings.currentVolume = newVal.doubleValue();
              volumeLabel.setText(
                  String.valueOf((int) Math.ceil(newVal.doubleValue() * 100)) + "%");
            });
    volumeSlider.setValue(Settings.currentVolume);

    imagePlay = new ImageView(new Image(getClass().getResourceAsStream("/images/button_play.png")));
    imagePlay.setScaleX(0.40);
    imagePlay.setScaleY(0.40);
    imagePause =
        new ImageView(new Image(getClass().getResourceAsStream("/images/button_pause.png")));
    imagePause.setScaleX(0.5);
    imagePause.setScaleY(0.5);
    imageSettings =
        new ImageView(new Image(getClass().getResourceAsStream("/images/settings.png")));
    imageSettings.setScaleX(0.5);
    imageSettings.setScaleY(0.5);
    imageUpdate = new ImageView(new Image(getClass().getResourceAsStream("/images/refresh.png")));
    imageUpdate.setScaleX(0.5);
    imageUpdate.setScaleY(0.5);
    imageAdd = new ImageView(new Image(getClass().getResourceAsStream("/images/add.png")));
    imageAdd.setScaleX(0.5);
    imageAdd.setScaleY(0.5);
    imageRename = new ImageView(new Image(getClass().getResourceAsStream("/images/rename.png")));
    imageRename.setScaleX(0.5);
    imageRename.setScaleY(0.5);
    imageDelete = new ImageView(new Image(getClass().getResourceAsStream("/images/delete.png")));
    imageDelete.setScaleX(0.5);
    imageDelete.setScaleY(0.5);

    stateButton.setGraphic(imagePlay);
    settingsButton.setGraphic(imageSettings);
    refreshButton.setGraphic(imageUpdate);
    refreshButton.setTooltip(new Tooltip(res.getString("tooltip_sync")));

    addButton.setGraphic(imageAdd);
    addButton.setOnAction((evt) -> createOfflinePlaylist());
    addButton.setTooltip(new Tooltip(res.getString("tooltip_add_offline")));
    renameButton.setGraphic(imageRename);
    renameButton.setOnAction(evt -> renamePlaylist());
    renameButton.setTooltip(new Tooltip(res.getString("rename_playlist")));
    deleteButton.setGraphic(imageDelete);
    deleteButton.setOnAction(evt -> deletePlaylist());
    deleteButton.setTooltip(new Tooltip(res.getString("delete_playlist")));

    loadProgressBar.setCursor(Cursor.HAND);
    volumeSlider.setCursor(Cursor.HAND);
    updatePlaylists();
    if (Settings.lastPlaylistId != null) {
      currentPlaylist =
          ((ObservableList<Playlist>) playlistsView.getItems())
              .stream()
              .filter((playlist) -> playlist.getId().equals(Settings.lastPlaylistId))
              .findFirst()
              .orElse(null);
      playlistsView.getSelectionModel().select(currentPlaylist);
      playlistsView.scrollTo(currentPlaylist);
      Platform.runLater(
          () -> {
            if (Settings.lastTrackId != null) {
              currentTrack =
                  ((ObservableList<Track>) tracksView.getItems())
                      .stream()
                      .filter((track) -> track.getId().equals(Settings.lastTrackId))
                      .findFirst()
                      .orElse(null);
              tracksView.getSelectionModel().select(currentTrack);
              tracksView.scrollTo(currentTrack);
            }
          });
    }
    new Timer()
        .scheduleAtFixedRate(
            new TimerTask() {
              @Override
              public void run() {
                updatePlayProgress();
              }
            },
            100,
            100);
    setupTracksContextMenu();

    setupShortcuts();

    Platform.runLater(
        () -> {
          tracksView.requestFocus();
        });
  }

  void findPlayingTrack() {
    if (currentPlaylist == playlistsView.getSelectionModel().getSelectedItem()) {
      tracksView.getSelectionModel().select(currentTrack);
      tracksView.scrollTo(currentTrack);
      tracksView.requestFocus();
    } else {
      findTrack = true;
      Platform.runLater(
          () -> {
            playlistsView.getSelectionModel().select(currentPlaylist);
            playlistsView.scrollTo(currentPlaylist);
          });
    }
  }

  void increaseVolume() {
    if (player == null) return;
    double newVolume = Settings.currentVolume + 0.05;

    if (newVolume > 1.0) newVolume = 1.0;
    Settings.currentVolume = newVolume;
    player.setVolume(newVolume);
    volumeSlider.setValue(newVolume);
  }

  void decreaseVolume() {
    if (player == null) return;
    double newVolume = Settings.currentVolume - 0.05;

    if (newVolume < 0.0) newVolume = 0.0;
    Settings.currentVolume = newVolume;
    player.setVolume(newVolume);
    volumeSlider.setValue(newVolume);
  }

  void setupGlobalKeys() {
    hotkeysProvider.reset();
    if (StringUtils.isNotBlank(Settings.Hotkeys.playPause))
      hotkeysProvider.register(
          KeyStroke.getKeyStroke(Settings.Hotkeys.playPause),
          (HotKey hotkey) -> {
            Platform.runLater(
                () -> {
                  stateButton.fire();
                });
          });
    if (StringUtils.isNotBlank(Settings.Hotkeys.playRandom))
      hotkeysProvider.register(
          KeyStroke.getKeyStroke(Settings.Hotkeys.playRandom),
          (HotKey hotkey) -> {
            Platform.runLater(
                () -> {
                  playRandom();
                });
          });
    if (StringUtils.isNotBlank(Settings.Hotkeys.playPrev))
      hotkeysProvider.register(
          KeyStroke.getKeyStroke(Settings.Hotkeys.playPrev),
          (HotKey hotkey) -> {
            Platform.runLater(
                () -> {
                  if (!interfaceDisabled) playPrevious();
                });
          });
    if (StringUtils.isNotBlank(Settings.Hotkeys.deleteAndPlayNext))
      hotkeysProvider.register(
          KeyStroke.getKeyStroke(Settings.Hotkeys.deleteAndPlayNext),
          (HotKey hotkey) -> {
            Platform.runLater(
                () -> {
                  deleteTrackFromPlaylist();
                  playNext();
                });
          });
    if (StringUtils.isNotBlank(Settings.Hotkeys.addToRemembered))
      hotkeysProvider.register(
          KeyStroke.getKeyStroke(Settings.Hotkeys.addToRemembered),
          (HotKey hotkey) -> {
            Platform.runLater(
                () -> {
                  addToRememberedPlaylist(currentTrack);
                });
          });
    if (StringUtils.isNotBlank(Settings.Hotkeys.incVolume))
      hotkeysProvider.register(
          KeyStroke.getKeyStroke(Settings.Hotkeys.incVolume),
          (HotKey hotkey) -> {
            Platform.runLater(
                () -> {
                  increaseVolume();
                });
          });
    if (StringUtils.isNotBlank(Settings.Hotkeys.decVolume))
      hotkeysProvider.register(
          KeyStroke.getKeyStroke(Settings.Hotkeys.decVolume),
          (HotKey hotkey) -> {
            Platform.runLater(
                () -> {
                  decreaseVolume();
                });
          });
  }

  void setupShortcuts() {
    Platform.runLater(
        () -> {
          stateButton
              .getScene()
              .getAccelerators()
              .put(
                  new KeyCodeCombination(KeyCode.F3),
                  (Runnable)
                      () -> {
                        if (!interfaceDisabled) SwingAppenderUI.getInstance().show();
                      });
          stateButton
              .getScene()
              .getAccelerators()
              .put(
                  new KeyCodeCombination(KeyCode.F),
                  (Runnable)
                      () -> {
                        if (!interfaceDisabled) findPlayingTrack();
                      });
          stateButton
              .getScene()
              .getAccelerators()
              .put(
                  new KeyCodeCombination(KeyCode.F5),
                  (Runnable)
                      () -> {
                        if (!interfaceDisabled) Platform.runLater(() -> refreshPlaylists());
                      });
          stateButton
              .getScene()
              .getAccelerators()
              .put(
                  new KeyCodeCombination(KeyCode.F4),
                  (Runnable)
                      () -> {
                        if (numpadOff) {
                          setupGlobalKeys();
                          Platform.runLater(
                              () -> {
                                infoLabel.setText(res.getString("global_hotkeys_on"));
                                log.info("Global hotkeys ON");
                              });
                        } else {
                          hotkeysProvider.reset();
                          Platform.runLater(
                              () -> {
                                infoLabel.setText(res.getString("global_hotkeys_off"));
                                log.info("Global hotkeys OFF");
                              });
                        }
                        numpadOff = !numpadOff;
                      });
          tracksView.setOnKeyPressed(
              (evt) -> {
                if (interfaceDisabled) return;
                if (evt.getCode().equals(KeyCode.DELETE)) {
                  deleteTrackFromPlaylist();
                  evt.consume();
                } else if (evt.getCode().equals(KeyCode.ENTER)) {
                  playSelectedTrack();
                  evt.consume();
                } else if (evt.getCode().equals(KeyCode.F2)) {
                  renameTrack();
                  evt.consume();
                }
              });
          playlistsView.setOnKeyPressed(
              (evt) -> {
                if (interfaceDisabled) return;
                if (evt.getCode().equals(KeyCode.F2)) {
                  renamePlaylist();
                  evt.consume();
                }
              });
        });
    setupGlobalKeys();
  }

  void setupPlaylistsContextMenu() {
    ContextMenu cm = playlistsContextMenu;
    MenuItem createOffline =
        menuItem(
            res.getString("create_offline_playlist"),
            () -> {
              createOfflinePlaylist();
            });
    MenuItem setAsFeatured =
        menuItem(
            res.getString("set_as_featured"),
            () -> {
              Playlist selectedPlaylist =
                  (Playlist) playlistsView.getSelectionModel().getSelectedItem();
              if (selectedPlaylist != null) {
                rememberedPlaylist = selectedPlaylist;
                Settings.rememberedPlaylistId = selectedPlaylist.getId();
                infoLabel.setText(
                    String.format(res.getString("featured_set"), selectedPlaylist.getTitle()));
              }
            });
    MenuItem rename =
        menuItem(
            "(...) " + res.getString("rename_playlist"),
            () -> {
              renamePlaylist();
            });
    MenuItem delete =
        menuItem(
            "(-) " + res.getString("delete_playlist"),
            () -> {
              deletePlaylist();
            });
    cm.getItems()
        .addAll(
            createOffline,
            new SeparatorMenuItem(),
            setAsFeatured,
            new SeparatorMenuItem(),
            rename,
            delete);
  }

  void setupTracksContextMenu() {
    ContextMenu cm = tracksContextMenu;
    List<MenuItem> items = cm.getItems();
    items.clear();

    Playlist pl = getSelectedPlaylist();
    if (pl == null) {
      items.add(
          menuItem(
              "-> " + res.getString("add_to"),
              () -> {
                menuAddToPlaylist();
              }));
      if (rememberedPlaylist != null) {
        items.add(
            menuItem(
                "-> "
                    + String.format(
                        res.getString("add_to_featured"), rememberedPlaylist.getTitle()),
                () -> {
                  Track t = getSelectedTrack();
                  if (t != null) {
                    addToPlaylist(t, rememberedPlaylist);
                  }
                }));
      }
    } else {
      items.add(
          menuItem(
              "(+) " + res.getString("add_files"),
              () -> {
                FileChooser fileChooser = new FileChooser();
                fileChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
                fileChooser.setTitle(String.format(res.getString("add_files_to"), pl.getTitle()));
                fileChooser
                    .getExtensionFilters()
                    .addAll(new FileChooser.ExtensionFilter("MP3", "*.mp3"));
                List<File> files = fileChooser.showOpenMultipleDialog(null);
                if (files != null) {
                  addToPlaylist(files, pl);
                }
              }));
      items.add(
          menuItem(
              "(+) " + res.getString("add_folder_recursively"),
              () -> {
                DirectoryChooser dirChooser = new DirectoryChooser();
                dirChooser.setTitle(String.format(res.getString("add_folder_to"), pl.getTitle()));
                dirChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
                File dir = dirChooser.showDialog(null);
                if (dir == null) return;
                Collection<File> files = FileUtils.listFiles(dir, new String[] {"mp3"}, true);
                addToPlaylist(new ArrayList(files), pl);
              }));
      items.add(new SeparatorMenuItem());
      if (rememberedPlaylist != null)
        items.add(
            menuItem(
                "-> "
                    + String.format(
                        res.getString("add_to_featured"), rememberedPlaylist.getTitle()),
                () -> {
                  Track t = getSelectedTrack();
                  if (t != null) addToPlaylist(t, rememberedPlaylist);
                }));
      items.add(
          menuItem(
              "-> " + res.getString("add_to"),
              () -> {
                menuAddToPlaylist();
              }));
      items.add(new SeparatorMenuItem());
      items.add(
          menuItem(
              "(...) " + res.getString("rename_track"),
              () -> {
                renameTrack();
              }));
      items.add(new SeparatorMenuItem());
      items.add(
          menuItem(
              "(-) " + res.getString("delete_track") + pl.getTitle(),
              () -> {
                deleteTrackFromPlaylist();
              }));
      items.add(
          menuItem(
              "(-) " + res.getString("delete_dead_items"),
              () -> {
                Track t = getSelectedTrack();
                if (t == null) {
                  return;
                }
                deleteDeadFromOfflinePlaylist(pl);
              }));
    }
  }

  void addToPlaylist(List<File> files, Playlist p) {
    if (interfaceDisabled) return;
    new Thread(
            () -> {
              setInterfaceDisabled(true);
              int total = files.size();
              AtomicInteger current = new AtomicInteger(0);
              Cache.pushToPlaylist(
                  files,
                  p,
                  (Track t) -> {
                    Platform.runLater(
                        () -> {
                          infoLabel.setText(
                              String.format(
                                  res.getString("processed"),
                                  current.incrementAndGet(),
                                  total,
                                  t.getTitle()));
                          if (p == getSelectedPlaylist()) {
                            tracksView.getItems().remove(t);
                            tracksView.getItems().add(0, t);
                          }
                        });
                  });
              setInterfaceDisabled(false);
            })
        .start();
    if (p == getSelectedPlaylist()) {
      Platform.runLater(
          () -> {
            loadSelectedPlaylist();
          });
    }
  }

  private void setInterfaceDisabled(boolean disabled) {
    playlistsView.setDisable(disabled);
    searchButton.setDisable(disabled);
    searchText.setDisable(disabled);
    addButton.setDisable(disabled);
    renameButton.setDisable(disabled);
    deleteButton.setDisable(disabled);
    refreshButton.setDisable(disabled);
    interfaceDisabled = disabled;
  }

  void addToPlaylist(Track t, Playlist p) {
    if (interfaceDisabled) return;
    Cache.pushToPlaylist(t, p);
    if (p == getSelectedPlaylist()) {
      Platform.runLater(
          () -> {
            loadSelectedPlaylist();
          });
    }
  }

  void deleteFromOfflinePlaylist(Track t, Playlist p) {
    if (interfaceDisabled) return;
    tracksView.getItems().remove(t);
    Cache.deleteFromPlaylist(t, p);
  }

  void deleteDeadFromOfflinePlaylist(Playlist p) {
    if (interfaceDisabled) return;
    infoLabel.setText(res.getString("deleting_dead_items"));
    log.info("Deleting dead items from " + p.getTitle());
    Integer deleted = Cache.deleteDead(p);
    infoLabel.setText(String.format(res.getString("deleted_dead_items"), deleted));
    log.info("Deleted dead items from " + p.getTitle() + ": " + deleted);
    Platform.runLater(
        () -> {
          loadSelectedPlaylist();
        });
  }

  private MenuItem menuItem(String title, Runnable r) {
    MenuItem mi = new MenuItem(title);
    mi.setOnAction(
        event -> {
          r.run();
        });
    return mi;
  }

  void createOfflinePlaylist() {
    if (interfaceDisabled) return;
    TextInputDialog dialog = new TextInputDialog(res.getString("new_offline_playlist"));
    dialog.setTitle(res.getString("create_new_offline_playlist"));
    dialog.setHeaderText(res.getString("create_new_playlist"));
    dialog.setContentText(res.getString("enter_playlist_name"));
    dialog.getDialogPane().getStylesheets().add("/styles/dialogs.css");
    ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);
    Optional<String> result = dialog.showAndWait();
    result.ifPresent(
        title -> {
          Platform.runLater(
              () -> {
                Playlist newPlaylist = Cache.createPlaylist(title);
                Platform.runLater(
                    () -> {
                      playlistsView.getItems().add(newPlaylist);
                      playlistsView.getSelectionModel().select(newPlaylist);
                    });
              });
        });
  }

  void renamePlaylist() {
    if (interfaceDisabled) return;
    Playlist p = getSelectedPlaylist();
    if (p == null) return;

    TextInputDialog dialog = new TextInputDialog(p.getTitle());
    dialog.setTitle(res.getString("rename_playlist"));
    dialog.setHeaderText(res.getString("rename_playlist"));
    dialog.setContentText(res.getString("enter_new_title"));
    dialog.getDialogPane().getStylesheets().add("/styles/dialogs.css");
    ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);
    Optional<String> result = dialog.showAndWait();
    result.ifPresent(
        title -> {
          if (StringUtils.isEmpty(title)) {
            return;
          }
          Cache.renamePlaylist(p, title);
          Platform.runLater(
              () -> {
                updatePlaylists();
              });
        });
  }

  void renameTrack() {
    if (interfaceDisabled) return;
    Playlist p = getSelectedPlaylist();
    if (p == null) return;
    Track t = getSelectedTrack();
    if (t == null) return;

    TextInputDialog dialog = new TextInputDialog(t.getTitle());
    dialog.setTitle(res.getString("rename_track"));
    dialog.setHeaderText(res.getString("rename_track"));
    dialog.setContentText(res.getString("enter_new_title"));
    dialog.getDialogPane().getStylesheets().add("/styles/dialogs.css");
    ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);
    Optional<String> result = dialog.showAndWait();
    result.ifPresent(
        title -> {
          if (StringUtils.isEmpty(title)) {
            return;
          }
          Cache.renameTrack(t, p, title);
          Platform.runLater(
              () -> {
                loadSelectedPlaylist();
              });
        });
  }

  void deletePlaylist() {
    if (interfaceDisabled) return;
    Playlist p = getSelectedPlaylist();
    if (p == null) return;
    Alert alert = new Alert(AlertType.CONFIRMATION);
    ButtonType btYes = new ButtonType(res.getString("yes"), ButtonBar.ButtonData.YES);
    ButtonType btCancel =
        new ButtonType(res.getString("cancel"), ButtonBar.ButtonData.CANCEL_CLOSE);
    alert.getButtonTypes().setAll(btYes, btCancel);
    alert.setTitle(res.getString("delete_playlist"));
    alert.setHeaderText(res.getString("delete_playlist"));
    alert.setContentText(String.format(res.getString("delete_confirm"), p.getTitle()));
    alert.getDialogPane().getStylesheets().add("/styles/dialogs.css");
    ((Stage) alert.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);

    Optional<ButtonType> result = alert.showAndWait();
    if (result.get() == btYes) {
      Cache.deletePlaylist(p);
      Platform.runLater(
          () -> {
            updatePlaylists();
          });
    }
  }

  Playlist getSelectedPlaylist() {
    if (searching) return null;
    return (Playlist) playlistsView.getSelectionModel().getSelectedItem();
  }

  Track getSelectedTrack() {
    return (Track) tracksView.getSelectionModel().getSelectedItem();
  }

  @FXML
  void showSettings() {
    SettingsWindow.show();
  }

  @FXML
  public void tracksViewMouseClicked(MouseEvent evt) {
    if (evt.getClickCount() == 2) {
      playSelectedTrack();
    }
  }

  void playSelectedTrack() {
    Track t = (Track) tracksView.getSelectionModel().getSelectedItem();
    if (t != null) Settings.lastTrackId = t.getId();
    playTrack(t);
  }

  void deleteTrackFromPlaylist() {
    Track t = getSelectedTrack();
    if (t == null) {
      return;
    }
    Playlist pl = getSelectedPlaylist();
    if (pl != null) {
      deleteFromOfflinePlaylist(t, pl);
    }
  }

  private ChoiceDialog<Playlist> playlistSelectionDialog(List<Playlist> items) {
    ChoiceDialog<Playlist> dialog = new ChoiceDialog<>(null, items);
    dialog.setTitle(res.getString("add_to"));
    dialog.setGraphic(null);
    ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().addAll(logoImages);
    dialog.setHeaderText(res.getString("choose_playlist"));
    dialog.getDialogPane().getStylesheets().add("/styles/dialogs.css");
    if (rememberedPlaylist != null) dialog.setSelectedItem(rememberedPlaylist);
    return dialog;
  }

  void menuAddToPlaylist() {
    Track t = getSelectedTrack();
    if (t == null) {
      return;
    }
    ChoiceDialog<Playlist> dialog = playlistSelectionDialog(playlistsView.getItems());
    Optional<Playlist> result = dialog.showAndWait();
    result.ifPresent(playlist -> addToPlaylist(t, playlist));
  }

  void addToRememberedPlaylist(Track t) {
    if (interfaceDisabled) return;
    if (t == null) return;
    if (rememberedPlaylist != null) {
      Cache.pushToPlaylist(t, rememberedPlaylist);
      infoAndAnnounce(
          String.format(
              res.getString("track_added_to_playlist"),
              rememberedPlaylist.getTitle(),
              t.getTitle()));
      if (rememberedPlaylist == getSelectedPlaylist()) loadSelectedPlaylist();
    } else {
      infoAndAnnounce(res.getString("remembered_not_yet"));
    }
  }

  private void infoAndAnnounce(String message) {
    infoLabel.setText(message);
    Tray.announce(message);
  }

  String formatTime(Duration total, Duration current) {
    return String.format(
        "%01d:%02d / %01d:%02d",
        (int) current.toMinutes(),
        (int) current.toSeconds() % 60,
        (int) total.toMinutes(),
        (int) total.toSeconds() % 60);
  }

  public void setStage(Stage stage) {
    this.stage = stage;
  }

  private static class PlaylistAndTrack {
    private Playlist playlist;
    private Track track;

    PlaylistAndTrack(Playlist playlist, Track track) {
      this.playlist = playlist;
      this.track = track;
    }

    public void setPlaylist(Playlist playlist) {
      this.playlist = playlist;
    }

    public void setTrack(Track track) {
      this.track = track;
    }
  }
}
示例#14
0
  @FXML
  void playTrack(Track t) {
    if (tweenBlock) return;
    tweenBlock = true;
    stop(
        () -> {
          currentPlaylist = (Playlist) playlistsView.getSelectionModel().getSelectedItem();
          currentTrack = t;
          if (player != null) {
            player.dispose();
          }
          if (t == null) {
            return;
          }
          timeUpdate = 9;
          if (currentPlaylist != null) {
            Tray.announce("[" + currentPlaylist.getTitle() + "] " + t.getTitle());
          }

          Platform.runLater(
              () -> {
                infoLabel.setText(res.getString("playing_offline") + t.getTitle());
                log.info("Playing offline: " + t.getTitle());
              });
          try {
            Media media = new Media(Cache.getContent(t).toURI().toString());
            player = new MediaPlayer(media);
            player.setVolume(Settings.currentVolume);
            player.play();
          } catch (MediaException ex) {
            Platform.runLater(
                () -> {
                  infoLabel.setText(
                      String.format(res.getString("cant_play_offline_not_exist"), t.getTitle()));
                  log.error("Can't play: seems that track does not exist - " + t.getTitle());
                });
          }

          if (player != null) {
            pushToPlayStack(getSelectedPlaylist(), t);
            player.setOnEndOfMedia(
                () -> {
                  String mode = Settings.currentMode;
                  if (mode.equals(Settings.MODE_NEXT)) {
                    playNext();
                  } else if (mode.equals(Settings.MODE_RANDOM)) {
                    playRandom();
                  } else if (mode.equals(Settings.MODE_SAME)) {
                    playSame();
                  }
                });
          }

          Platform.runLater(
              () -> {
                isPlaying = true;
                tweenBlock = false;
                titleLabel.setText(t.getTitle());
                stateButton.setGraphic(imagePause);
              });
        });
  }