content: package net.doge.ui;
  
  import cn.hutool.core.io.IORuntimeException;
  import cn.hutool.http.HttpException;
  import cn.hutool.http.HttpRequest;
  import com.alibaba.fastjson2.JSONArray;
  import com.alibaba.fastjson2.JSONException;
  import com.alibaba.fastjson2.JSONObject;
  import javafx.animation.KeyFrame;
  import javafx.animation.KeyValue;
  import javafx.animation.Timeline;
  import javafx.application.Platform;
  import javafx.collections.ObservableList;
  import javafx.embed.swing.JFXPanel;
  import javafx.scene.media.AudioSpectrumListener;
  import javafx.scene.media.MediaException;
  import javafx.scene.media.MediaPlayer;
  import javafx.stage.DirectoryChooser;
  import javafx.stage.FileChooser;
  import javafx.util.Duration;
  import net.doge.constant.async.GlobalExecutors;
  import net.doge.constant.config.ConfigConstants;
  import net.doge.constant.lang.I18n;
  import net.doge.constant.lyric.LyricType;
  import net.doge.constant.lyric.NextLrc;
  import net.doge.constant.meta.SoftInfo;
  import net.doge.constant.model.NetMusicSource;
  import net.doge.constant.model.UIStyleConstants;
  import net.doge.constant.player.EqualizerData;
  import net.doge.constant.player.PlayMode;
  import net.doge.constant.player.PlayerStatus;
  import net.doge.constant.system.*;
  import net.doge.constant.tab.CollectionTabIndex;
  import net.doge.constant.tab.PersonalMusicTabIndex;
  import net.doge.constant.tab.RecommendTabIndex;
  import net.doge.constant.tab.TabIndex;
  import net.doge.constant.task.TaskType;
  import net.doge.constant.ui.*;
  import net.doge.constant.window.CloseWindowOptions;
  import net.doge.constant.window.WindowSize;
  import net.doge.constant.window.WindowState;
  import net.doge.exception.IllegalMediaException;
  import net.doge.exception.NoCopyrightException;
  import net.doge.model.entity.*;
  import net.doge.model.entity.base.MusicResource;
  import net.doge.model.entity.base.NetResource;
  import net.doge.model.entity.base.Resource;
  import net.doge.model.lyric.LrcData;
  import net.doge.model.lyric.Statement;
  import net.doge.model.player.MetaMusicInfo;
  import net.doge.model.player.MusicPlayer;
  import net.doge.model.task.Task;
  import net.doge.model.ui.UIStyle;
  import net.doge.sdk.common.CommonResult;
  import net.doge.sdk.common.Tags;
  import net.doge.sdk.util.MusicServerUtil;
  import net.doge.ui.widget.button.ChangePaneButton;
  import net.doge.ui.widget.button.CustomButton;
  import net.doge.ui.widget.button.DialogButton;
  import net.doge.ui.widget.button.TabButton;
  import net.doge.ui.widget.button.listener.ButtonMouseListener;
  import net.doge.ui.widget.button.listener.ChangePaneButtonMouseListener;
  import net.doge.ui.widget.button.ui.ChangePaneButtonUI;
  import net.doge.ui.widget.checkbox.CustomCheckBox;
  import net.doge.ui.widget.combobox.CustomComboBox;
  import net.doge.ui.widget.combobox.ui.ComboBoxUI;
  import net.doge.ui.widget.dialog.*;
  import net.doge.ui.widget.dialog.factory.AbstractShadowDialog;
  import net.doge.ui.widget.label.CustomLabel;
  import net.doge.ui.widget.list.CustomList;
  import net.doge.ui.widget.list.renderer.entity.*;
  import net.doge.ui.widget.list.renderer.system.DownloadListRenderer;
  import net.doge.ui.widget.list.renderer.system.LrcListRenderer;
  import net.doge.ui.widget.list.ui.ListUI;
  import net.doge.ui.widget.lyric.StringTwoColor;
  import net.doge.ui.widget.menu.*;
  import net.doge.ui.widget.menu.ui.CheckMenuItemUI;
  import net.doge.ui.widget.menu.ui.MenuItemUI;
  import net.doge.ui.widget.menu.ui.MenuUI;
  import net.doge.ui.widget.menu.ui.RadioButtonMenuItemUI;
  import net.doge.ui.widget.panel.CustomPanel;
  import net.doge.ui.widget.panel.GlobalPanel;
  import net.doge.ui.widget.panel.LoadingPanel;
  import net.doge.ui.widget.panel.SpectrumPanel;
  import net.doge.ui.widget.scrollpane.CustomScrollPane;
  import net.doge.ui.widget.scrollpane.listener.ScrollPaneListener;
  import net.doge.ui.widget.scrollpane.ui.ScrollBarUI;
  import net.doge.ui.widget.slider.CustomSlider;
  import net.doge.ui.widget.slider.ui.SliderUI;
  import net.doge.ui.widget.tabbedpane.CustomTabbedPane;
  import net.doge.ui.widget.tabbedpane.ui.TabbedPaneUI;
  import net.doge.ui.widget.textfield.CustomTextField;
  import net.doge.ui.widget.textfield.SafeDocument;
  import net.doge.ui.widget.textfield.listener.TextFieldHintListener;
  import net.doge.ui.widget.toolbar.CustomToolBar;
  import net.doge.util.collection.ListUtil;
  import net.doge.util.common.*;
  import net.doge.util.lmdata.LMDataUtil;
  import net.doge.util.lmdata.LMIconManager;
  import net.doge.util.media.MediaUtil;
  import net.doge.util.system.DesktopUtil;
  import net.doge.util.system.FileUtil;
  import net.doge.util.system.KeyUtil;
  import net.doge.util.system.TerminalUtil;
  import net.doge.util.ui.ColorUtil;
  import net.doge.util.ui.ImageUtil;
  import net.doge.util.ui.SpectrumUtil;
  import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
  import org.jsoup.Jsoup;
  import org.jsoup.nodes.Document;
  import org.jsoup.select.Elements;
  
  import javax.swing.*;
  import javax.swing.Timer;
  import javax.swing.border.Border;
  import javax.swing.event.DocumentEvent;
  import javax.swing.event.DocumentListener;
  import javax.swing.event.ListDataEvent;
  import javax.swing.event.ListDataListener;
  import java.awt.*;
  import java.awt.datatransfer.Clipboard;
  import java.awt.datatransfer.StringSelection;
  import java.awt.event.*;
  import java.awt.geom.Rectangle2D;
  import java.awt.geom.RoundRectangle2D;
  import java.awt.image.BufferedImage;
  import java.io.File;
  import java.util.*;
  import java.util.List;
  import java.util.concurrent.*;
  
  public class MainFrame extends JFrame {
      private MainFrame() {
      }
  
      private final MainFrame THIS = this;
      public final String TITLE = I18n.getText("title");
      // 窗口宽高
      public int windowWidth;
      public int windowHeight;
      private final String SONG_NAME_LABEL = I18n.getText("songName");
      private final String ARTIST_LABEL = I18n.getText("artist");
      private final String ALBUM_NAME_LABEL = I18n.getText("albumName");
      // 进度条最大值
      private final int TIME_BAR_MAX = 0x3f3f3f3f;
      // 默认音量
      private final int DEFAULT_VOLUME = 90;
      // 最大音量
      private final int MAX_VOLUME = 100;
      // 默认快进/快退时间
      private final int DEFAULT_FORWARD_OR_BACKWARD_TIME = 10;
      // 默认倍速
      private final double DEFAULT_RATE = 1;
      // 默认均衡
      private final double DEFAULT_BALANCE = 0;
      // 历史记录最大条数
      public int maxHistoryCount;
      // 搜索历史最大条数
      public int maxSearchHistoryCount;
      // 最大尝试播放次数
      private final int MAX_RETRY = 3;
      // 图片圆角半径弧度
      private final double TINY_ARC = 0.2;
      private final double MIDDLE_ARC = 0.1;
      private final double LARGE_ARC = 0.03;
      //    private final double WIN_ARC = 0.01;
      private final String PAGINATION_MSG = I18n.getText("paginationMsg");
      private final String TOTAL_MSG = I18n.getText("totalMsg");
      private final String LRC_LOADING_MSG = I18n.getText("lrcLoadingMsg");
      private final Statement LRC_LOADING_STMT = new Statement(LRC_LOADING_MSG);
      private final String NO_LRC_MSG = I18n.getText("noLrcMsg");
      private final Statement NO_LRC_STMT = new Statement(NO_LRC_MSG);
      private final String BAD_FORMAT_LRC_MSG = I18n.getText("badFormatLrcMsg");
      private final Statement BAD_FORMAT_LRC_STMT = new Statement(BAD_FORMAT_LRC_MSG);
      private final String LOADING_MSG = I18n.getText("loadingMsg");
      private final String LOAD_FAILED = I18n.getText("loadFailed");
      private final String ASK_DISPOSE_MSG = I18n.getText("askDisposeMsg");
      private final String REMEMBER_CHOICE_MSG = I18n.getText("rememberChoiceMsg");
      private final String[] EXIT_OPTIONS = {I18n.getText("exitOption1"), I18n.getText("exitOption2"), I18n.getText("exitOption3")};
      private final String ASK_REIMPORT_MSG = I18n.getText("askReimportMsg");
      private final String ASK_RETAIN_MUSIC_LIST_MSG = I18n.getText("askRetainMusicListMsg");
      private final String ASK_CLEAR_CACHE_MSG = I18n.getText("askClearCacheMsg");
      private final String ASK_REMOVE_FIFE_NOT_FOUND_MSG = I18n.getText("askRemoveFileNotFoundMsg");
      private final String ASK_REMOVE_ITEMS_MSG = I18n.getText("askRemoveItemsMsg");
      private final String ASK_CLEAR_LIST_MSG = I18n.getText("askClearListMsg");
      private final String ASK_DUPLICATE_MSG = I18n.getText("askDuplicateMsg");
      private final String ASK_REVERSE_MSG = I18n.getText("askReverseMsg");
      private final String UPDATE_CHECKING_MSG = I18n.getText("updateCheckingMsg");
      private final String UPDATE_CHECK_FAILED_MSG = I18n.getText("updateCheckFailedMsg");
      private final String UPDATE_MSG = I18n.getText("updateMsg");
      private final String IGNORE_UPDATE_MSG = I18n.getText("ignoreUpdateMsg");
      private final String UPDATE_INFO_MSG = I18n.getText("updateInfoMsg");
      private final String UPDATE_READY_MSG = I18n.getText("updateReadyMsg");
      private final String LATEST_MSG = I18n.getText("latestMsg");
      private final String TO_RELEASE = I18n.getText("toRelease");
      private final String RESTART_NOW = I18n.getText("restartNow");
      private final String LATER = I18n.getText("later");
      private final String OK = I18n.getText("ok");
      private final String YES = I18n.getText("yes");
      private final String NO = I18n.getText("no");
      private final String CANCEL = I18n.getText("cancel");
      private final String READY = I18n.getText("ready");
      private final String PLAYING = I18n.getText("playing");
      private final String PAUSING = I18n.getText("pausing");
      private final String STOPPED = I18n.getText("stopped");
      private final String LOAD_TRACK = I18n.getText("loadTrack");
      private final String LOAD_TRACK_FAILED = I18n.getText("loadTrackFailed");
      private final String REFRESH_URL = I18n.getText("refreshUrl");
      private final String CHOOSE_TRACK_FILE = I18n.getText("chooseTrackFile");
      private final String CHOOSE_TRACK_FOLDER = I18n.getText("chooseTrackFolder");
      private final String SAVE_IMG = I18n.getText("saveImg");
      private final String AUDIO_FILE = I18n.getText("audioFile");
      private final String TRACK_ADDED = I18n.getText("trackAdded");
      private final String USER_COMMENTED = I18n.getText("userCommented");
      private final String HELP_MSG = String.format("Hi，欢迎使用%s~\n\n" +
              "下面是一些常见问题解答，请仔细阅读。祝你使用愉快~\n\n" +
              "Q1：如何导入我的歌单？\nA1：无需登录，在“用户”选项卡搜索自己的用户名，右键选择“查看用户歌单”，收藏即可\n" +
              "注：\n1.此操作要求你的歌单权限是公开的，不能是私密，否则看不到\n" +
              "2.如果你确实不想公开你的歌单，也可以在“歌单”选项卡勾上“歌单 ID”，搜索你的歌单 ID\n\n" +
              "Q2：如何下载无损 FLAC 音质？\nA2：“设置”->“播放与历史”->“优先音质”可以设置播放和下载时的音质\n" +
              "注：选择无损音质时，不支持 FLAC 直接播放，需调用 FFmpeg 转为 MP3 进行播放，但不影响下载 FLAC 文件\n\n" +
              "Q3：如何进行收藏、下载等其他操作？\nA3：通过右键菜单操作，除此之外还有很多功能都在右键菜单里，等你探索~\n\n" +
              "Q4：如何批量操作？\nA4：列表支持 Ctrl Shift 多选，Ctrl + A 全选\n\n" +
              "Q5：为什么有些歌曲名字和音频不一致？\nA5：付费或无版权歌曲采用自动换源机制，不能100%%保证一致，可以尝试手动换源搜索\n\n" +
              "Q6：资源是如何获取的？\nA6：调用各大平台的 API 获取，免费使用，不可商用！\n\n" +
              "Q7：软件更新后原来的数据如何继承？\nA7：建议覆盖更新，数据文件 %s 保存在程序所在目录，保证与程序在同一目录即可\n\n" +
              "如果还有其他的问题，欢迎在我的 Github 主页发布 issue ~\n" +
              "最后，开源不易，低调使用，请勿推广！有条件还请多多支持正版！", TITLE, ConfigConstants.CONFIG_FILE_NAME);
      private final String FIRST_PAGE_MSG = I18n.getText("firstPageMsg");
      private final String LAST_PAGE_MSG = I18n.getText("lastPageMsg");
      private final String ILLEGAL_PAGE_MSG = I18n.getText("illegalPageMsg");
      private final String FILE_NOT_FOUND_MSG = I18n.getText("fileNotFoundMsg");
      private final String UNSUPPORTED_AUDIO_FILE_MSG = I18n.getText("unsupportedAudioFileMsg");
      private final String INVALID_AUDIO_FILE_MSG = I18n.getText("invalidAudioFileMsg");
      private final String NO_CATALOG_MSG = I18n.getText("noCatalogMsg");
      private final String NO_MUSIC_MSG = I18n.getText("noMusicMsg");
      private final String ALREADY_PLAYING_MSG = I18n.getText("alreadyPlayingMsg");
      private final String NO_IMG_MSG = I18n.getText("noImgMsg");
      private final String GET_RESOURCE_FAILED_MSG = I18n.getText("getResourceFailedMsg");
      private final String NO_NET_MSG = I18n.getText("noNetMsg");
      private final String TIME_OUT_MSG = I18n.getText("timeOutMsg");
      private final String API_ERROR_MSG = I18n.getText("apiErrorMsg");
      private final String CLEAR_CACHE_SUCCESS_MSG = I18n.getText("clearCacheSuccessMsg");
      private final String NEXT_PLAY_SUCCESS_MSG = I18n.getText("nextPlaySuccessMsg");
      private final String COLLECT_SUCCESS_MSG = I18n.getText("collectSuccessMsg");
      private final String CANCEL_COLLECTION_SUCCESS_MSG = I18n.getText("cancelCollectionSuccessMsg");
      private final String REMOVE_SUCCESS_MSG = I18n.getText("removeSuccessMsg");
      private final String CLEAR_SUCCESS_MSG = I18n.getText("clearSuccessMsg");
      private final String DUPLICATE_SUCCESS_MSG = I18n.getText("duplicateSuccessMsg");
      private final String REVERSE_SUCCESS_MSG = I18n.getText("reverseSuccessMsg");
      private final String CHANGE_DISABLED_MSG = I18n.getText("changeDisabledMsg");
      private final String CHANGE_SINGLE_MSG = I18n.getText("changeSingleMsg");
      private final String CHANGE_LIST_CYCLE_MSG = I18n.getText("changeListCycleMsg");
      private final String CHANGE_SEQUENCE_MSG = I18n.getText("changeSequenceMsg");
      private final String CHANGE_SHUFFLE_MSG = I18n.getText("changeShuffleMsg");
      private final String DOWNLOAD_COMPLETED_MSG = I18n.getText("downloadCompletedMsg");
      private final String TASK_ADDED_MSG = I18n.getText("taskAddedMsg");
      private final String WAIT_FOR_TASK_COMPLETED_MSG = I18n.getText("waitForTaskCompletedMsg");
      private final String ASK_REMOVE_SELECTED_TASKS_MSG = I18n.getText("askRemoveSelectedTasksMsg");
      private final String ASK_REMOVE_FILE_MSG = I18n.getText("askRemoveFileMsg");
      private final String ASK_RESTART_ALL_TASKS_MSG = I18n.getText("askRestartAllTasksMsg");
      private final String ASK_CANCEL_ALL_TASKS_MSG = I18n.getText("askCancelAllTasksMsg");
      private final String ASK_REMOVE_ALL_TASKS_MSG = I18n.getText("askRemoveAllTasksMsg");
      private final String ASK_REMOVE_SONGS_FROM_PLAY_QUEUE_MSG = I18n.getText("askRemoveSongsFromPlayQueueMsg");
      private final String ASK_CLEAR_PLAY_QUEUE_MSG = I18n.getText("askClearPlayQueueMsg");
      private final String LOADING_MV_MSG = I18n.getText("loadingMvMsg");
      private final String COPY_SUCCESS_MSG = I18n.getText("copySuccessMsg");
  
      private final String DEFAULT_TIME = DurationUtil.format(0);
      private final String PLAY_MENU_ITEM_TEXT = I18n.getText("playMenuItem");
      private final String NEXT_PLAY_MENU_ITEM_TEXT = I18n.getText("nextPlayMenuItem");
      private final String OPEN_MENU_ITEM_TEXT = I18n.getText("openMenuItem");
      private final String PLAY_ALL_MENU_ITEM_TEXT = I18n.getText("playAllMenuItem");
      private final String BROWSE_ALBUM_MENU_ITEM_TEXT = I18n.getText("browseAlbumMenuItem");
      private final String BROWSE_MV_MENU_ITEM_TEXT = I18n.getText("browseMvMenuItem");
      private final String SIMILAR_ARTIST_MENU_ITEM_TEXT = I18n.getText("similarArtistMenuItem");
      private final String ARTIST_FANS_MENU_ITEM_TEXT = I18n.getText("artistFansMenuItem");
      private final String ARTIST_BUDDY_MENU_ITEM_TEXT = I18n.getText("artistBuddyMenuItem");
      private final String ARTIST_RADIO_MENU_ITEM_TEXT = I18n.getText("artistRadioMenuItem");
      private final String ARTIST_PHOTOS_MENU_ITEM_TEXT = I18n.getText("artistPhotosMenuItem");
      private final String USER_PLAYLIST_MENU_ITEM_TEXT = I18n.getText("userPlaylistMenuItem");
      private final String USER_ALBUM_MENU_ITEM_TEXT = I18n.getText("userAlbumMenuItem");
      private final String USER_RADIO_MENU_ITEM_TEXT = I18n.getText("userRadioMenuItem");
      private final String USER_VIDEO_MENU_ITEM_TEXT = I18n.getText("userVideoMenuItem");
      private final String USER_FOLLOW_MENU_ITEM_TEXT = I18n.getText("userFollowMenuItem");
      private final String USER_FAN_MENU_ITEM_TEXT = I18n.getText("userFanMenuItem");
      private final String RADIO_DJ_MENU_ITEM_TEXT = I18n.getText("radioDjMenuItem");
      private final String EDIT_INFO_MENU_ITEM_TEXT = I18n.getText("editInfoMenuItem");
      private final String LOCATE_FILE_MENU_ITEM_TEXT = I18n.getText("locateFileMenuItem");
      private final String REMOVE_MENU_ITEM_TEXT = I18n.getText("removeMenuItem");
      private final String COLLECT_MENU_ITEM_TEXT = I18n.getText("collectMenuItem");
      private final String CANCEL_COLLECTION_MENU_ITEM_TEXT = I18n.getText("cancelCollectionMenuItem");
      private final String PLAY_MV_MENU_ITEM_TEXT = I18n.getText("playMvMenuItem");
      private final String DOWNLOAD_MENU_ITEM_TEXT = I18n.getText("downloadMenuItem");
      private final String COMMENT_MENU_ITEM_TEXT = I18n.getText("commentMenuItem");
      private final String ALBUM_ARTIST_MENU_ITEM_TEXT = I18n.getText("albumArtistMenuItem");
      private final String SIMILAR_ALBUM_MENU_ITEM_TEXT = I18n.getText("similarAlbumMenuItem");
      private final String ALBUM_PHOTOS_MENU_ITEM_TEXT = I18n.getText("albumPhotosMenuItem");
      private final String SHEET_MENU_ITEM_TEXT = I18n.getText("sheetMenuItem");
      private final String SEARCH_SONG_MENU_ITEM_TEXT = I18n.getText("searchSongMenuItem");
      private final String SIMILAR_SONG_MENU_ITEM_TEXT = I18n.getText("similarSongMenuItem");
      private final String RELATED_PLAYLIST_MENU_ITEM_TEXT = I18n.getText("relatedPlaylistMenuItem");
      private final String AUTHOR_MENU_ITEM_TEXT = I18n.getText("authorMenuItem");
      private final String ALBUM_MENU_ITEM_TEXT = I18n.getText("albumMenuItem");
      private final String SIMILAR_PLAYLIST_MENU_ITEM_TEXT = I18n.getText("similarPlaylistMenuItem");
      private final String PLAYLIST_CREATOR_MENU_ITEM_TEXT = I18n.getText("playlistCreatorMenuItem");
      private final String PLAYLIST_SUBSCRIBER_MENU_ITEM_TEXT = I18n.getText("playlistSubscriberMenuItem");
      private final String RADIO_SUBSCRIBER_MENU_ITEM_TEXT = I18n.getText("radioSubscriberMenuItem");
      private final String SIMILAR_RADIO_MENU_ITEM_TEXT = I18n.getText("similarRadioMenuItem");
      private final String RADIO_ARTISTS_MENU_ITEM_TEXT = I18n.getText("radioArtistsMenuItem");
      private final String RADIO_PHOTOS_MENU_ITEM_TEXT = I18n.getText("radioPhotosMenuItem");
      private final String RADIO_POSTERS_MENU_ITEM_TEXT = I18n.getText("radioPostersMenuItem");
      private final String REC_RADIO_MENU_ITEM_TEXT = I18n.getText("recRadioMenuItem");
      private final String RELATED_MV_MENU_ITEM_TEXT = I18n.getText("relatedMvMenuItem");
      private final String SIMILAR_MV_MENU_ITEM_TEXT = I18n.getText("similarMvMenuItem");
      private final String VIDEO_EPISODE_MENU_ITEM_TEXT = I18n.getText("videoEpisodeMenuItem");
      private final String MV_CREATOR_MENU_ITEM_TEXT = I18n.getText("mvCreatorMenuItem");
      private final String COPY_NAME_MENU_ITEM_TEXT = I18n.getText("copyNameMenuItem");
      private final String SAVE_ALBUM_IMAGE_TEXT = I18n.getText("saveAlbumImage");
  
      // 托盘图标
      private ImageIcon trayIcon = LMIconManager.getIcon("tray");
      // 打开主界面图标
      private ImageIcon openMainFrameIcon = LMIconManager.getIcon("menu.openMainFrame");
      // 退出图标
      private ImageIcon exitIcon = LMIconManager.getIcon("menu.exit");
      // 主界面标题图标
      private ImageIcon titleIcon = LMIconManager.getIcon("title.title");
      // 隐藏歌曲详情图标
      private ImageIcon hideDetailIcon = LMIconManager.getIcon("title.hideDetail");
      // 分割线图标
      private ImageIcon splitIcon = LMIconManager.getIcon("title.split");
      // 迷你模式图标
      private ImageIcon miniIcon = LMIconManager.getIcon("title.miniWindow");
      // 最小化图标
      private ImageIcon minimizeIcon = LMIconManager.getIcon("title.minWindow");
      // 最大化图标
      private ImageIcon maximizeIcon = LMIconManager.getIcon("title.maxWindow");
      // 恢复图标
      private ImageIcon restoreIcon = LMIconManager.getIcon("title.restoreWindow");
      // 关闭窗口图标
      public ImageIcon closeWindowIcon = LMIconManager.getIcon("title.closeWindow");
      // 菜单图标
      private ImageIcon menuIcon = LMIconManager.getIcon("title.menu");
      // 更换主题图标
      private ImageIcon changeStyleIcon = LMIconManager.getIcon("menu.changeStyle");
      // 添加自定义主题图标
      private ImageIcon addCustomStyleIcon = LMIconManager.getIcon("menu.addCustomStyle");
      // 关闭歌曲图标
      private ImageIcon closeSongIcon = LMIconManager.getIcon("menu.closeSong");
      // 清空缓存图标
      private ImageIcon clearCacheIcon = LMIconManager.getIcon("menu.clearCache");
      // 设置图标
      private ImageIcon settingsIcon = LMIconManager.getIcon("menu.settings");
      // 捐赠图标
      private ImageIcon donateIcon = LMIconManager.getIcon("menu.donate");
      // 发布页图标
      private ImageIcon releaseIcon = LMIconManager.getIcon("menu.release");
      // 检查更新图标
      private ImageIcon updateIcon = LMIconManager.getIcon("menu.update");
      // 指南图标
      private ImageIcon helpIcon = LMIconManager.getIcon("menu.help");
      // 关于图标
      private ImageIcon aboutIcon = LMIconManager.getIcon("menu.about");
      // 列表为空提示图标
      private ImageIcon emptyHintIcon = LMIconManager.getIcon("emptyHint");
      // 选定点图标(单选时)
      private ImageIcon dotIcon = LMIconManager.getIcon("menu.dot");
      // 选定勾图标
      private ImageIcon tickIcon = LMIconManager.getIcon("menu.tick");
      // 复选框图标
      public ImageIcon uncheckedIcon = LMIconManager.getIcon("dialog.unchecked");
      public ImageIcon checkedIcon = LMIconManager.getIcon("dialog.checked");
      // 添加歌曲文件图标
      private ImageIcon fileIcon = LMIconManager.getIcon("menu.file");
      // 添加文件夹图标
      private ImageIcon folderIcon = LMIconManager.getIcon("menu.folder");
      // 未收藏图标
      private ImageIcon collectIcon = LMIconManager.getIcon("control.collect");
      // 已收藏图标
      private ImageIcon hasCollectedIcon = LMIconManager.getIcon("control.collected");
      // 未收藏图标
      private ImageIcon collectItemIcon = LMIconManager.getIcon("collectItem");
      // 已收藏图标
      private ImageIcon collectedItemIcon = LMIconManager.getIcon("collectedItem");
      // MV 图标
      private ImageIcon mvIcon = LMIconManager.getIcon("control.mv");
      // 下载图标
      private ImageIcon downloadIcon = LMIconManager.getIcon("control.download");
      // 评论图标
      private ImageIcon commentIcon = LMIconManager.getIcon("control.comment");
      // 乐谱图标
      private ImageIcon sheetIcon = LMIconManager.getIcon("control.sheet");
      // 播放图标
      private ImageIcon playIcon = LMIconManager.getIcon("control.play");
      // 暂停图标
      private ImageIcon pauseIcon = LMIconManager.getIcon("control.pause");
      // 上一曲图标
      private ImageIcon lastIcon = LMIconManager.getIcon("control.last");
      // 下一曲图标
      private ImageIcon nextIcon = LMIconManager.getIcon("control.next");
      // 快退图标
      private ImageIcon backwIcon = LMIconManager.getIcon("control.backw");
      // 快进图标
      private ImageIcon forwIcon = LMIconManager.getIcon("control.forw");
      // 播完暂停图标
      private ImageIcon playModeDisabledIcon = LMIconManager.getIcon("menu.playModeDisabled");
      // 单曲循环图标
      private ImageIcon singleIcon = LMIconManager.getIcon("menu.single");
      // 顺序播放图标
      private ImageIcon sequenceIcon = LMIconManager.getIcon("menu.sequence");
      // 列表循环图标
      private ImageIcon listCycleIcon = LMIconManager.getIcon("menu.listCycle");
      // 随机播放图标
      private ImageIcon shuffleIcon = LMIconManager.getIcon("menu.shuffle");
      // 声音图标
      private ImageIcon soundIcon = LMIconManager.getIcon("control.sound");
      // 静音图标
      private ImageIcon muteIcon = LMIconManager.getIcon("control.mute");
      // 倍速图标
      private ImageIcon rateIcon = LMIconManager.getIcon("control.rate");
      // 频谱开启图标
      private ImageIcon spectrumOnIcon = LMIconManager.getIcon("control.spectrumOn");
      // 频谱关闭图标
      private ImageIcon spectrumOffIcon = LMIconManager.getIcon("control.spectrumOff");
      // 歌曲封面图标
      private ImageIcon cvBlurIcon = LMIconManager.getIcon("menu.cvBlur");
      // 纯主色调图标
      private ImageIcon mcBlurIcon = LMIconManager.getIcon("menu.pureColorBlur");
      // 线性渐变图标
      private ImageIcon lgBlurIcon = LMIconManager.getIcon("menu.linearGradient");
      // 迷幻纹理图标
      private ImageIcon fbmBlurIcon = LMIconManager.getIcon("menu.fbm");
      // 模糊关闭图标
      private ImageIcon blurOffIcon = LMIconManager.getIcon("menu.blurOff");
      // 音效图标
      private ImageIcon soundEffectIcon = LMIconManager.getIcon("control.soundEffect");
      // 转到播放队列图标
      private ImageIcon goToPlayQueueIcon = LMIconManager.getIcon("control.goToPlayQueue");
      // 开启桌面歌词图标
      private ImageIcon desktopLyricOnIcon = LMIconManager.getIcon("control.lrcOn");
      // 关闭桌面歌词图标
      private ImageIcon desktopLyricOffIcon = LMIconManager.getIcon("control.lrcOff");
      // 繁体中文图标
      private ImageIcon tradChineseIcon = LMIconManager.getIcon("control.traditionalChinese");
      // 罗马音图标
      private ImageIcon romajiIcon = LMIconManager.getIcon("control.romaji");
      // 原歌词图标
      private ImageIcon originalIcon = LMIconManager.getIcon("control.original");
      // 歌词翻译图标
      private ImageIcon translationIcon = LMIconManager.getIcon("control.translation");
      // 添加歌曲图标
      private ImageIcon addIcon = LMIconManager.getIcon("toolbar.add");
      // 重新导入图标
      private ImageIcon reimportIcon = LMIconManager.getIcon("toolbar.reimport");
      // 管理歌曲目录图标
      private ImageIcon manageCatalogIcon = LMIconManager.getIcon("toolbar.manageCatalog");
      // 删除歌曲图标
      private ImageIcon removeIcon = LMIconManager.getIcon("toolbar.remove");
      // 去重图标
      private ImageIcon duplicateIcon = LMIconManager.getIcon("toolbar.duplicate");
      // 倒序图标
      private ImageIcon reverseIcon = LMIconManager.getIcon("toolbar.reverse");
      // 排序图标
      private ImageIcon sortIcon = LMIconManager.getIcon("toolbar.sort");
      // 上移图标
      private ImageIcon moveUpIcon = LMIconManager.getIcon("toolbar.moveUp");
      // 下移图标
      private ImageIcon moveDownIcon = LMIconManager.getIcon("toolbar.moveDown");
      // 清除输入图标
      private ImageIcon clearInputIcon = LMIconManager.getIcon("toolbar.clearInput");
      // 换肤图标
      private ImageIcon styleIcon = LMIconManager.getIcon("title.style");
      // 搜索图标
      private ImageIcon searchIcon = LMIconManager.getIcon("toolbar.search");
      // 清空搜索历史图标
      private ImageIcon clearHistorySearchIcon = LMIconManager.getIcon("clearHistorySearch");
      // 返回图标
      private ImageIcon backwardIcon = LMIconManager.getIcon("toolbar.backward");
      // 刷新图标
      private ImageIcon refreshIcon = LMIconManager.getIcon("toolbar.refresh");
      // 跳页图标
      private ImageIcon goIcon = LMIconManager.getIcon("toolbar.go");
      // 播放全部图标
      private ImageIcon playAllIcon = LMIconManager.getIcon("toolbar.playAll");
      // 第一页图标
      private ImageIcon startPageIcon = LMIconManager.getIcon("toolbar.startPage");
      // 上一页图标
      private ImageIcon lastPageIcon = LMIconManager.getIcon("toolbar.lastPage");
      // 下一页图标
      private ImageIcon nextPageIcon = LMIconManager.getIcon("toolbar.nextPage");
      // 最后一页图标
      private ImageIcon endPageIcon = LMIconManager.getIcon("toolbar.endPage");
      // 个人音乐图标
      private ImageIcon personalMusicIcon = LMIconManager.getIcon("tab.personalMusic");
      // 本地音乐图标
      private ImageIcon localMusicIcon = LMIconManager.getIcon("tab.local");
      // 播放历史图标
      private ImageIcon historyIcon = LMIconManager.getIcon("tab.history");
      // 收藏图标
      private ImageIcon collectionIcon = LMIconManager.getIcon("tab.collection");
      // 音乐馆图标
      private ImageIcon netMusicIcon = LMIconManager.getIcon("tab.netMusic");
      // 歌单图标
      private ImageIcon playlistIcon = LMIconManager.getIcon("tab.netPlaylist");
      // 专辑图标
      private ImageIcon netAlbumIcon = LMIconManager.getIcon("tab.netAlbum");
      // 歌手图标
      private ImageIcon netArtistIcon = LMIconManager.getIcon("tab.netArtist");
      // 电台图标
      private ImageIcon netRadioIcon = LMIconManager.getIcon("tab.netRadio");
      // 榜单图标
      private ImageIcon netRankingIcon = LMIconManager.getIcon("tab.netRanking");
      // 用户图标
      private ImageIcon netUserIcon = LMIconManager.getIcon("tab.netUser");
      // MV 标签页图标
      private ImageIcon netMvIcon = LMIconManager.getIcon("tab.netMv");
      // 推荐图标
      private ImageIcon recommendIcon = LMIconManager.getIcon("tab.recommend");
      // 下载管理图标
      private ImageIcon downloadManagementIcon = LMIconManager.getIcon("tab.downloadManagement");
      // 播放队列图标
      private ImageIcon playQueueIcon = LMIconManager.getIcon("tab.playQueue");
      // 清空图标
      private ImageIcon clearIcon = LMIconManager.getIcon("toolbar.clear");
      // 推荐歌单图标
      private ImageIcon playlistRecommendIcon = LMIconManager.getIcon("tab.playlistRecommend");
      // 精品歌单图标
      private ImageIcon highQualityPlaylistIcon = LMIconManager.getIcon("tab.highQualityPlaylistRecommend");
      // 飙升歌曲图标
      private ImageIcon hotMusicIcon = LMIconManager.getIcon("tab.hotMusicRecommend");
      // 推荐单曲图标
      private ImageIcon musicRecommendIcon = LMIconManager.getIcon("tab.musicRecommend");
      // 新碟上架图标
      private ImageIcon newAlbumRecommendIcon = LMIconManager.getIcon("tab.newAlbumRecommend");
      // 歌手排行图标
      private ImageIcon artistListRecommendIcon = LMIconManager.getIcon("tab.artistListRecommend");
      // 新晋电台图标
      private ImageIcon newRadioRecommendIcon = LMIconManager.getIcon("tab.newRadioRecommend");
      // 热门电台图标
      private ImageIcon hotRadioRecommendIcon = LMIconManager.getIcon("tab.hotRadioRecommend");
      // 推荐节目图标
      private ImageIcon programRecommendIcon = LMIconManager.getIcon("tab.programRecommend");
      // 推荐 MV 图标
      private ImageIcon mvRecommendIcon = LMIconManager.getIcon("tab.mvRecommend");
      // 重新开始全部任务图标
      private ImageIcon restartAllTasksIcon = LMIconManager.getIcon("toolbar.restartAllTasks");
      // 取消全部任务图标
      private ImageIcon cancelAllTasksIcon = LMIconManager.getIcon("toolbar.cancelAllTasks");
      // 删除全部任务图标
      private ImageIcon removeAllTasksIcon = LMIconManager.getIcon("toolbar.removeAllTasks");
  
      // 下一条格言图标
      private ImageIcon nextMottoIcon = LMIconManager.getIcon("menu.nextMotto");
      // 播放菜单项图标
      private ImageIcon playMenuItemIcon = LMIconManager.getIcon("menu.play");
      // 下一首播放菜单项图标
      private ImageIcon nextPlayMenuItemIcon = LMIconManager.getIcon("menu.nextPlay");
      // 打开菜单项图标
      private ImageIcon openMenuItemIcon = LMIconManager.getIcon("menu.open");
      // 查看专辑菜单项图标
      private ImageIcon browseAlbumMenuItemIcon = LMIconManager.getIcon("menu.browseAlbum");
      // 播放 MV 菜单项图标
      private ImageIcon playMvMenuItemIcon = LMIconManager.getIcon("menu.playMv");
      // 收藏菜单项图标
      private ImageIcon collectMenuItemIcon = LMIconManager.getIcon("menu.collect");
      // 取消收藏菜单项图标
      private ImageIcon cancelCollectionMenuItemIcon = LMIconManager.getIcon("menu.cancelCollection");
      // 下载菜单项图标
      private ImageIcon downloadMenuItemIcon = LMIconManager.getIcon("menu.download");
      // 查看评论菜单项图标
      private ImageIcon commentMenuItemIcon = LMIconManager.getIcon("menu.comment");
      // 查看乐谱菜单项图标
      private ImageIcon sheetMenuItemIcon = LMIconManager.getIcon("menu.sheet");
      // 搜索这首歌曲菜单项图标
      private ImageIcon searchSongItemIcon = LMIconManager.getIcon("menu.searchSong");
      // 查看相似歌曲菜单项图标
      private ImageIcon similarMenuItemIcon = LMIconManager.getIcon("menu.similar");
      // 查看相关歌单菜单项图标
      private ImageIcon relatedPlaylistMenuItemIcon = LMIconManager.getIcon("menu.similarPlaylist");
      // 查看电台菜单项图标
      private ImageIcon radioMenuItemIcon = LMIconManager.getIcon("menu.radio");
      // 查看用户图标
      private ImageIcon userMenuItemIcon = LMIconManager.getIcon("menu.user");
      // 查看相似 MV 菜单项图标
      private ImageIcon similarMvMenuItemIcon = LMIconManager.getIcon("menu.similarMv");
      // 查看视频视频分集菜单项图标
      private ImageIcon videoEpisodeMenuItemIcon = LMIconManager.getIcon("menu.episode");
      // 查看相似歌手菜单项图标
      private ImageIcon similarArtistMenuItemIcon = LMIconManager.getIcon("menu.similarArtist");
      // 查看歌手合作人菜单项图标
      private ImageIcon buddyMenuItemIcon = LMIconManager.getIcon("menu.buddy");
      // 查看用户关注菜单项图标
      private ImageIcon userFollowMenuItemIcon = LMIconManager.getIcon("menu.follow");
      // 查看用户粉丝菜单项图标
      private ImageIcon userFanMenuItemIcon = LMIconManager.getIcon("menu.fan");
      // 复制名称菜单项图标
      private ImageIcon copyNameMenuItemIcon = LMIconManager.getIcon("menu.copy");
      // 查看乐谱菜单项图标
      private ImageIcon browseSheetMenuItemIcon = LMIconManager.getIcon("menu.browseSheet");
      // 删除菜单项图标
      private ImageIcon removeMenuItemIcon = LMIconManager.getIcon("menu.remove");
      // 定位歌词时间菜单项图标
      private ImageIcon locateLrcMenuItemIcon = LMIconManager.getIcon("menu.locateLrc");
      // 查看歌词文件菜单项图标
      private ImageIcon browseLrcMenuItemIcon = LMIconManager.getIcon("menu.browseLrc");
      // 频谱透明度菜单项图标
      private ImageIcon spectrumOpacityMenuItemIcon = LMIconManager.getIcon("menu.spectrumOpacity");
      // 歌词偏移菜单项图标
      private ImageIcon lrcOffsetMenuItemIcon = LMIconManager.getIcon("menu.lrcOffset");
      // 导出专辑图片菜单项图标
      private ImageIcon saveAlbumImgMenuItemIcon = LMIconManager.getIcon("menu.saveAlbumImg");
      // 查看海报菜单项图标
      private ImageIcon posterMenuItemIcon = LMIconManager.getIcon("menu.poster");
      // 取消任务菜单项图标
      private ImageIcon cancelTaskMenuItemIcon = LMIconManager.getIcon("menu.cancelTask");
      // 重新开始任务菜单项图标
      private ImageIcon restartTaskMenuItemIcon = LMIconManager.getIcon("menu.restartTask");
      // 打开文件所在位置图标
      private ImageIcon locateFileMenuItemIcon = LMIconManager.getIcon("menu.locateFile");
      // 编辑歌曲信息图标
      private ImageIcon editInfoMenuItemIcon = LMIconManager.getIcon("menu.editInfo");
  
      // 托盘图标
      private TrayIcon trayIconImg = new TrayIcon(trayIcon.getImage(), TITLE);
  
      // 悬浮帮助提示
      private final String MINI_WINDOW_TIP = I18n.getText("miniWindowTip");
      private final String MINIMIZE_WINDOW_TIP = I18n.getText("minimizeWindowTip");
      private final String MAXIMIZE_WINDOW_TIP = I18n.getText("maximizeWindowTip");
      private final String CLOSE_WINDOW_TIP = I18n.getText("closeWindowTip");
      private final String CHANGE_TO_LYRIC_PANE_TIP = I18n.getText("changeToLyricPaneTip");
      private final String CHANGE_TO_MUSIC_PANE_TIP = I18n.getText("changeToMusicPaneTip");
      private final String COLLECT_TIP = I18n.getText("collectTip");
      private final String COLLECTED_TIP = I18n.getText("collectedTip");
      private final String DOWNLOAD_TIP = I18n.getText("downloadTip");
      private final String COMMENT_TIP = I18n.getText("commentTip");
      private final String MV_TIP = I18n.getText("mvTip");
      private final String PLAY_TIP = I18n.getText("playTip");
      private final String PAUSE_TIP = I18n.getText("pauseTip");
      private final String LAST_TIP = I18n.getText("lastTip");
      private final String NEXT_TIP = I18n.getText("nextTip");
      private final String BACKW_TIP = I18n.getText("backwTip");
      private final String FORW_TIP = I18n.getText("forwTip");
      private final String PLAY_MODE_DISABLED_TIP = I18n.getText("playModeDisabledTip");
      private final String SINGLE_TIP = I18n.getText("singleTip");
      private final String SEQUENCE_TIP = I18n.getText("sequenceTip");
      private final String LIST_CYCLE_TIP = I18n.getText("listCycleTip");
      private final String SHUFFLE_TIP = I18n.getText("shuffleTip");
      private final String SOUND_TIP = I18n.getText("soundTip");
      private final String MUTE_TIP = I18n.getText("muteTip");
      private final String RATE_TIP = I18n.getText("rateTip");
      private final String SWITCH_SPECTRUM_TIP = I18n.getText("switchSpectrumTip");
      private final String SWITCH_BLUR_TIP = I18n.getText("switchBlurTip");
      private final String SOUND_EFFECT_TIP = I18n.getText("soundEffectTip");
      private final String SHEET_TIP = I18n.getText("sheetTip");
      private final String ORIGINAL_LRC_TIP = I18n.getText("originalLrcTip");
      private final String TRANSLATION_TIP = I18n.getText("translationTip");
      private final String ROMA_TIP = I18n.getText("romaTip");
      private final String TRAD_CHINESE_TIP = I18n.getText("tradChineseTip");
      private final String MENU_TIP = I18n.getText("menuTip");
      private final String GO_TO_PLAY_QUEUE_TIP = I18n.getText("goToPlayQueueTip");
      private final String DESKTOP_LRC_TIP = I18n.getText("desktopLrcTip");
      private final String ADD_TIP = I18n.getText("addTip");
      private final String REIMPORT_TIP = I18n.getText("reimportTip");
      private final String MANAGE_CATALOG_TIP = I18n.getText("manageCatalogTip");
      private final String REMOVE_TIP = I18n.getText("removeTip");
      private final String CLEAR_TIP = I18n.getText("clearTip");
      private final String DUPLICATE_TIP = I18n.getText("duplicateTip");
      private final String REVERSE_TIP = I18n.getText("reverseTip");
      private final String SORT_TIP = I18n.getText("sortTip");
      private final String MOVE_UP_TIP = I18n.getText("moveUpTip");
      private final String MOVE_DOWN_TIP = I18n.getText("moveDownTip");
      private final String CLEAR_INPUT_TIP = I18n.getText("clearInputTip");
      private final String STYLE_TIP = I18n.getText("styleTip");
      private final String HIDE_DETAIL_TIP = I18n.getText("hideDetailTip");
      private final String SHOW_KEYWORD_PANEL_TIP = I18n.getText("showKeywordPanelTip");
      private final String SEARCH_TIP = I18n.getText("searchTip");
      private final String BACKWARD_TIP = I18n.getText("backwardTip");
      private final String PLAY_ALL_TIP = I18n.getText("playAllTip");
      private final String REFRESH_TIP = I18n.getText("refreshTip");
      private final String START_PAGE_TIP = I18n.getText("startPageTip");
      private final String LAST_PAGE_TIP = I18n.getText("lastPageTip");
      private final String GO_TIP = I18n.getText("goTip");
      private final String NEXT_PAGE_TIP = I18n.getText("nextPageTip");
      private final String END_PAGE_TIP = I18n.getText("endPageTip");
      private final String RESTART_SELECTED_TASKS_TIP = I18n.getText("restartSelectedTasksTip");
      private final String CANCEL_SELECTED_TASKS_TIP = I18n.getText("cancelSelectedTasksTip");
      private final String REMOVE_SELECTED_TASKS_TIP = I18n.getText("removeSelectedTasksTip");
      private final String RESTART_ALL_TASKS_TIP = I18n.getText("restartAllTasksTip");
      private final String CANCEL_ALL_TASKS_TIP = I18n.getText("cancelAllTasksTip");
      private final String REMOVE_ALL_TASKS_TIP = I18n.getText("removeAllTasksTip");
      private final String REMOVE_FROM_PLAY_QUEUE_TIP = I18n.getText("removeFromPlayQueueTip");
      private final String CLEAR_PLAY_QUEUE_TIP = I18n.getText("clearPlayQueueTip");
      private final String CLEAR_HISTORY_SEARCH_TIP = I18n.getText("clearHistorySearchTip");
      private final String REMOVE_HISTORY_KEYWORD_TIP = I18n.getText("removeHistoryKeywordTip");
  
      // 当前包含的所有歌曲目录
      public List<File> catalogs = new LinkedList<>();
      // 所有界面主题
      public List<UIStyle> styles = new LinkedList<>();
  
      // 添加界面主题
      {
          styles.addAll(Arrays.asList(PreDefinedUIStyle.STYLES));
      }
  
      public boolean autoUpdate;
      public int windowState = WindowState.NORMAL;
      // 关闭窗口操作
      public int currCloseWindowOption;
      public int windowSize;
      public int forwardOrBackwardTime;
      public int videoForwardOrBackwardTime;
      // 是否显示已播放时间
      private boolean timeElapsedMode;
      public boolean videoTimeElapsedMode;
      // 播放视频时是否关闭主界面
      public boolean videoOnly;
      // 显示侧边栏文字
      public boolean showTabText;
      // 当前播放速率
      public double currRate;
      public double currVideoRate;
      // 当前频谱样式
      public int currSpecStyle;
      // 当前均衡
      public double currBalance;
      // 最大缓存大小(MB，超出后自动清理)
      public long maxCacheSize;
      // 当前排序方式
      private int currSortMethod = -1;
      // 当前排序顺序
      private int currSortOrder;
      // 当前播放曲目的索引
      private int currSong;
      // 播放失败后重试次数
      private int retry = 0;
      // 当前播放模式
      private int currPlayMode;
      // 随机播放序列
      private List<Integer> shuffleList = new LinkedList<>();
      // 当前随机播放索引
      private int shuffleIndex;
      // 当前歌词类型
      private int currLrcType;
      // 歌词显示比率(源)
      private double originalRatio;
      // 搜索每页大小
      private int limit = 50;
      private int commentLimit = 30;
      // 当前正在搜索的关键词
      private String netMusicCurrKeyword;
      private String netPlaylistCurrKeyword;
      private String netAlbumCurrKeyword;
      private String netArtistCurrKeyword;
      private String netRadioCurrKeyword;
      private String netMvCurrKeyword;
      private String netUserCurrKeyword;
      // 当前页码
      private int netMusicCurrPage = 1;
      private int netPlaylistCurrPage = 1;
      private int netMusicInPlaylistCurrPage = 1;
      private int netAlbumCurrPage = 1;
      private int netMusicInAlbumCurrPage = 1;
      private int netArtistCurrPage = 1;
      private int netMusicInArtistCurrPage = 1;
      private int netRadioCurrPage = 1;
      private int netMusicInRadioCurrPage = 1;
      private int netMvCurrPage = 1;
      private int netMusicInRecommendCurrPage = 1;
      private int netMusicInCollectionCurrPage = 1;
      private int netRankingCurrPage = 1;
      private int netMusicInRankingCurrPage = 1;
      private int netUserCurrPage = 1;
      private int netMusicInUserCurrPage = 1;
      private int netCommentCurrPage = 1;
      private int netSheetCurrPage = 1;
      private int netRecommendCurrPage = 1;
      // 最大页码
      private int netMusicMaxPage;
      private int netPlaylistMaxPage;
      private int netMusicInPlaylistMaxPage;
      private int netAlbumMaxPage;
      private int netMusicInAlbumMaxPage;
      private int netArtistMaxPage;
      private int netMusicInArtistMaxPage;
      private int netRadioMaxPage;
      private int netMusicInRadioMaxPage;
      private int netMvMaxPage;
      private int netRankingMaxPage;
      private int netMusicInRankingMaxPage;
      private int netUserMaxPage;
      private int netMusicInUserMaxPage;
      private int netCommentMaxPage;
      private int netSheetMaxPage;
      private int netRecommendMaxPage;
      private int netMusicInRecommendMaxPage;
      private int netMusicInCollectionMaxPage;
      // 当前个人音乐展示的标签
      private int currPersonalMusicTab = -1;
      // 当前推荐展示的标签
      private int currRecommendTab = -1;
      private int preRecommendTab;
      // 是否静音
      public boolean isMute;
      // 是否高斯模糊
      public boolean gsOn;
      // 是否朦胧遮罩
      public boolean maskOn;
      // 是否暗化
      public boolean darkerOn;
      // 是否律动
      public boolean grooveOn;
      // 模糊类型
      public int blurType;
      // 是否显示频谱
      public boolean showSpectrum;
      // 是否自动下载歌词
      public boolean isAutoDownloadLrc;
      // 是否显示桌面歌词
      public boolean showDesktopLyric;
      // 是否锁定桌面歌词
      public boolean desktopLyricLocked;
      public int desktopLyricX;
      public int desktopLyricY;
      // 桌面歌词是否置顶
      public boolean desktopLyricOnTop;
      // 桌面歌词透明度
      public float desktopLyricAlpha;
      // 桌面歌词字体大小
      public int desktopLyricFontSize;
      // 专辑图片宽/高
      private int albumImageWidth;
      // 封面图片宽/高
      private int coverImageWidth = 200;
      // 切换面板按钮图片宽度
      private int changePaneImageWidth = 50;
      // 当前面板
      private int currPane = MusicPane.MUSIC;
      // 上一个面板(评论跳转要用到)
      private int lastPane;
  
      // 加载中图片
      private BufferedImage loadingImage = ImageUtil.width(LMIconManager.getImage("loadingImage"), coverImageWidth);
  
      // 全局字体
      private Font globalFont = Fonts.NORMAL;
  
      // 当前主界面主题
      public UIStyle currUIStyle = styles.get(0);
  
      public MusicPlayer player = new MusicPlayer(THIS);
  
      // 标题部分
      private CustomPanel topPanel = new CustomPanel();
      private CustomLabel titleLabel = new CustomLabel();
      private CustomPanel windowCtrlPanel = new CustomPanel();
      // 窗口控制按钮
      private CustomButton hideDetailButton = new CustomButton(hideDetailIcon);
      private CustomLabel splitLabel = new CustomLabel(splitIcon);
      private CustomButton miniButton = new CustomButton(miniIcon);
      private CustomButton minimizeButton = new CustomButton(minimizeIcon);
      private CustomButton maximizeButton = new CustomButton(maximizeIcon);
      private CustomButton closeButton = new CustomButton(closeWindowIcon);
  
      // 主菜单
      private CustomButton mainMenuButton = new CustomButton(menuIcon);
      private CustomPopupMenu mainMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem settingMenuItem = new CustomMenuItem(I18n.getText("setting"));
      private CustomMenuItem closeSong = new CustomMenuItem(I18n.getText("closeSong"));
      private CustomMenuItem clearCache = new CustomMenuItem(I18n.getText("clearCache"));
      private CustomMenuItem manageStyleMenuItem = new CustomMenuItem(I18n.getText("manageStyle"));
      private CustomMenuItem styleCustomMenuItem = new CustomMenuItem(I18n.getText("styleCustom"));
      private CustomMenuItem donateMenuItem = new CustomMenuItem(I18n.getText("donate"));
      private CustomMenuItem releaseMenuItem = new CustomMenuItem(I18n.getText("release"));
      private CustomMenuItem updateMenuItem = new CustomMenuItem(I18n.getText("update"));
      private CustomMenuItem helpMenuItem = new CustomMenuItem(I18n.getText("help"));
      private CustomMenuItem aboutMenuItem = new CustomMenuItem(I18n.getText("about"));
  
      // 歌名
      private CustomLabel songNameLabel = new CustomLabel();
      // 艺术家
      private CustomLabel artistLabel = new CustomLabel();
      // 专辑
      private CustomLabel albumLabel = new CustomLabel();
      // 专辑图片
      private CustomLabel albumImageLabel = new CustomLabel();
      // 歌曲信息弹出菜单
      private CustomPopupMenu leftInfoPopupMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem saveAlbumImageMenuItem = new CustomMenuItem(SAVE_ALBUM_IMAGE_TEXT);
      private CustomMenuItem copySongNameMenuItem = new CustomMenuItem(I18n.getText("copySongName"));
      private CustomMenuItem copyArtistMenuItem = new CustomMenuItem(I18n.getText("copyArtist"));
      private CustomMenuItem copyAlbumMenuItem = new CustomMenuItem(I18n.getText("copyAlbum"));
  
      private CustomSlider timeBar = new CustomSlider();
      private CustomLabel currTimeLabel = new CustomLabel(DEFAULT_TIME);
      private CustomLabel durationLabel = new CustomLabel(DEFAULT_TIME);
  
      // 换肤按钮
      private CustomButton styleToolButton = new CustomButton(styleIcon);
      // 换肤按钮弹出菜单
      private CustomPopupMenu stylePopupMenu = new CustomPopupMenu(THIS);
  
      // 歌词列表
      private CustomList<Statement> lrcList = new CustomList<>();
      private CustomScrollPane lrcScrollPane = new CustomScrollPane(lrcList);
      private DefaultListModel<Statement> lrcListModel = new DefaultListModel<>();
      // 频谱面板
      public SpectrumPanel spectrumPanel = new SpectrumPanel(THIS);
      private CustomPopupMenu spectrumPopupMenu = new CustomPopupMenu(THIS);
      private List<CustomRadioButtonMenuItem> spectrumStyleButtonGroup = new LinkedList<>();
      private final String SPEC_OPACITY_MSG = I18n.getText("specOpacityMsg");
      public float specOpacity;
      private CustomMenuItem spectrumOpacityMenuItem = new CustomMenuItem();
      private CustomMenuItem[] calcSpectrumOpacityMenuItems = {
              new CustomMenuItem("+10%"),
              new CustomMenuItem("+20%"),
              new CustomMenuItem("-10%"),
              new CustomMenuItem("-20%")
      };
      // 歌词右键弹出菜单
      private CustomPopupMenu lrcPopupMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem copyMenuItem = new CustomMenuItem(I18n.getText("copy"));
      private CustomMenuItem locateLrcMenuItem = new CustomMenuItem(I18n.getText("locateLrc"));
      private CustomMenuItem browseLrcMenuItem = new CustomMenuItem(I18n.getText("browseLrc"));
      private CustomMenuItem downloadLrcMenuItem = new CustomMenuItem(I18n.getText("downloadLrc"));
      private double lrcOffset;
      private final double lrcOffsetRadius = 5;
      private final String LRC_OFFSET_MSG = I18n.getText("lrcOffsetMsg");
      private final String RESET = I18n.getText("reset");
      private CustomMenuItem currLrcOffsetMenuItem = new CustomMenuItem();
      private CustomMenuItem[] calcLrcOffsetMenuItems = {
              new CustomMenuItem("+0.5 s"),
              new CustomMenuItem("+0.1 s"),
              new CustomMenuItem(RESET),
              new CustomMenuItem("-0.1 s"),
              new CustomMenuItem("-0.5 s")
      };
      // 歌词
      private List<Statement> statements;
      private String lrcStr;
      private int nextLrc = NextLrc.NOT_EXISTS;
  
      public ChangePaneButton changePaneButton = new ChangePaneButton();
      private CustomButton mvButton = new CustomButton(mvIcon);
      private CustomButton collectButton = new CustomButton(collectIcon);
      private CustomButton downloadButton = new CustomButton(downloadIcon);
      private CustomButton commentButton = new CustomButton(commentIcon);
      public CustomButton playOrPauseButton = new CustomButton(playIcon);
      public CustomButton lastButton = new CustomButton(lastIcon);
      public CustomButton nextButton = new CustomButton(nextIcon);
      // 播放模式切换按钮
      private CustomButton playModeButton = new CustomButton(listCycleIcon);
      private CustomButton forwardButton = new CustomButton(forwIcon);
      private CustomButton backwardButton = new CustomButton(backwIcon);
      private CustomPanel volumePanel = new CustomPanel();
      public CustomButton muteButton = new CustomButton(soundIcon);
      public CustomSlider volumeSlider = new CustomSlider();
      private CustomButton rateButton = new CustomButton(rateIcon);
      private CustomButton switchSpectrumButton = new CustomButton(spectrumOnIcon);
      private CustomButton blurButton = new CustomButton(cvBlurIcon);
      private CustomButton soundEffectButton = new CustomButton(soundEffectIcon);
      private CustomButton sheetButton = new CustomButton(sheetIcon);
      private CustomButton goToPlayQueueButton = new CustomButton(goToPlayQueueIcon);
      public CustomButton desktopLyricButton = new CustomButton(desktopLyricOnIcon);
      private CustomButton switchLrcTypeButton = new CustomButton(originalIcon);
      // 歌词类型右键菜单
      private CustomPopupMenu lyricTypePopupMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem originalLrcMenuItem = new CustomMenuItem(I18n.getText("originalLrc"));
      private CustomMenuItem translationMenuItem = new CustomMenuItem(I18n.getText("translation"));
      private CustomMenuItem romaMenuItem = new CustomMenuItem(I18n.getText("roma"));
      private CustomMenuItem tradChineseMenuItem = new CustomMenuItem(I18n.getText("tradChinese"));
  
      // 播放模式右键菜单
      private CustomPopupMenu playModePopupMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem playModeDisabledMenuItem = new CustomMenuItem(I18n.getText("playModeDisabled"));
      private CustomMenuItem singleMenuItem = new CustomMenuItem(I18n.getText("single"));
      private CustomMenuItem sequenceMenuItem = new CustomMenuItem(I18n.getText("sequence"));
      private CustomMenuItem listCycleMenuItem = new CustomMenuItem(I18n.getText("listCycle"));
      private CustomMenuItem shuffleMenuItem = new CustomMenuItem(I18n.getText("shuffle"));
  
      // 模糊模式右键菜单
      private CustomPopupMenu blurPopupMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem gsMenuItem = new CustomMenuItem(I18n.getText("gs"));
      private CustomMenuItem darkerMenuItem = new CustomMenuItem(I18n.getText("darker"));
      private CustomMenuItem maskMenuItem = new CustomMenuItem(I18n.getText("mask"));
      private CustomMenuItem grooveMenuItem = new CustomMenuItem(I18n.getText("groove"));
      private CustomMenuItem blurOffMenuItem = new CustomMenuItem(I18n.getText("blurOff"));
      private CustomMenuItem cvBlurMenuItem = new CustomMenuItem(I18n.getText("cvBlur"));
      private CustomMenuItem mcBlurMenuItem = new CustomMenuItem(I18n.getText("mcBlur"));
      private CustomMenuItem lgBlurMenuItem = new CustomMenuItem(I18n.getText("lgBlur"));
      private CustomMenuItem fbmBlurMenuItem = new CustomMenuItem(I18n.getText("fbmBlur"));
  
      // 标签页
      private CustomTabbedPane tabbedPane = new CustomTabbedPane(CustomTabbedPane.LEFT);
      // 个人音乐 Tab 面板
      private CustomPanel personalMusicPanel = new CustomPanel();
      private CustomLabel personalMusicLabel = new CustomLabel(personalMusicIcon);
      // 音乐馆 Tab 面板
      private CustomPanel netMusicPanel = new CustomPanel();
      private CustomLabel netMusicLabel = new CustomLabel(netMusicIcon);
      // 歌单 Tab 面板
      private CustomPanel netPlaylistPanel = new CustomPanel();
      private CustomLabel netPlaylistLabel = new CustomLabel(playlistIcon);
      // 专辑 Tab 面板
      private CustomPanel netAlbumPanel = new CustomPanel();
      private CustomLabel netAlbumLabel = new CustomLabel(netAlbumIcon);
      // 歌手 Tab 面板
      private CustomPanel netArtistPanel = new CustomPanel();
      private CustomLabel netArtistLabel = new CustomLabel(netArtistIcon);
      // 电台 Tab 面板
      private CustomPanel netRadioPanel = new CustomPanel();
      private CustomLabel netRadioLabel = new CustomLabel(netRadioIcon);
      // MV Tab 面板
      private CustomPanel netMvPanel = new CustomPanel();
      private CustomLabel netMvLabel = new CustomLabel(netMvIcon);
      // 榜单 Tab 面板
      private CustomPanel netRankingPanel = new CustomPanel();
      private CustomLabel netRankingLabel = new CustomLabel(netRankingIcon);
      // 用户 Tab 面板
      private CustomPanel netUserPanel = new CustomPanel();
      private CustomLabel netUserLabel = new CustomLabel(netUserIcon);
      // 推荐 Tab 面板
      private CustomPanel recommendPanel = new CustomPanel();
      private CustomLabel recommendLabel = new CustomLabel(recommendIcon);
      // 下载管理 Tab 面板
      private CustomPanel downloadManagementPanel = new CustomPanel();
      private CustomLabel downloadManagementLabel = new CustomLabel(downloadManagementIcon);
      // 播放队列 Tab 面板
      private CustomPanel playQueuePanel = new CustomPanel();
      private CustomLabel playQueueLabel = new CustomLabel(playQueueIcon);
  
      // 收藏标签页
      private CustomTabbedPane collectionTabbedPane = new CustomTabbedPane(CustomTabbedPane.TOP);
      // 音乐收藏 Tab 面板
      private CustomPanel musicCollectionPanel = new CustomPanel();
      private CustomLabel musicCollectionLabel = new CustomLabel(I18n.getText("musicCollection"), netMusicIcon);
      // 歌单收藏 Tab 面板
      private CustomPanel playlistCollectionPanel = new CustomPanel();
      private CustomLabel playlistCollectionLabel = new CustomLabel(I18n.getText("playlistCollection"), playlistIcon);
      // 专辑收藏 Tab 面板
      private CustomPanel albumCollectionPanel = new CustomPanel();
      private CustomLabel albumCollectionLabel = new CustomLabel(I18n.getText("albumCollection"), netAlbumIcon);
      // 歌手收藏 Tab 面板
      private CustomPanel artistCollectionPanel = new CustomPanel();
      private CustomLabel artistCollectionLabel = new CustomLabel(I18n.getText("artistCollection"), netArtistIcon);
      // 电台收藏 Tab 面板
      private CustomPanel radioCollectionPanel = new CustomPanel();
      private CustomLabel radioCollectionLabel = new CustomLabel(I18n.getText("radioCollection"), netRadioIcon);
      // MV 收藏 Tab 面板
      private CustomPanel mvCollectionPanel = new CustomPanel();
      private CustomLabel mvCollectionLabel = new CustomLabel(I18n.getText("mvCollection"), netMvIcon);
      // 榜单收藏 Tab 面板
      private CustomPanel rankingCollectionPanel = new CustomPanel();
      private CustomLabel rankingCollectionLabel = new CustomLabel(I18n.getText("rankingCollection"), netRankingIcon);
      // 用户收藏 Tab 面板
      private CustomPanel userCollectionPanel = new CustomPanel();
      private CustomLabel userCollectionLabel = new CustomLabel(I18n.getText("userCollection"), netUserIcon);
  
      // 空 ListModel，用于其他 ListModel 的 clear 时暂时显示，防止卡顿
      private DefaultListModel emptyListModel = new DefaultListModel<>();
      private DefaultListModel emptyLrcListModel = new DefaultListModel<>();
  
      // 列表为空提示面板
      private CustomPanel emptyHintPanel = new CustomPanel();
      // 列表为空提示标签
      private CustomLabel emptyHintLabel = new CustomLabel(I18n.getText("emptyHint"));
  
      // 个人音乐歌曲列表
      private CustomList<MusicResource> musicList = new CustomList<>();
      private CustomScrollPane musicScrollPane = new CustomScrollPane(musicList);
      // 本地音乐 ListModel
      private DefaultListModel<MusicResource> musicListModel = new DefaultListModel<>();
      // 播放历史 ListModel
      public DefaultListModel<MusicResource> historyModel = new DefaultListModel<>();
  
      // 收藏列表
      public CustomList<NetResource> collectionList = new CustomList<>();
      private CustomScrollPane collectionScrollPane = new CustomScrollPane(collectionList);
      // 歌曲收藏 ListModel
      private DefaultListModel<MusicResource> collectionModel = new DefaultListModel<>();
      // 歌单收藏 ListModel
      private DefaultListModel<NetResource> playlistCollectionModel = new DefaultListModel<>();
      // 作为收藏歌单单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForPlaylistCollectionModel = new DefaultListModel<>();
      // 专辑收藏 ListModel
      private DefaultListModel<NetResource> albumCollectionModel = new DefaultListModel<>();
      // 作为专辑单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForAlbumCollectionModel = new DefaultListModel<>();
      // 歌手收藏 ListModel
      private DefaultListModel<NetResource> artistCollectionModel = new DefaultListModel<>();
      // 作为收藏歌手单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForArtistCollectionModel = new DefaultListModel<>();
      // 电台收藏 ListModel
      private DefaultListModel<NetResource> radioCollectionModel = new DefaultListModel<>();
      // 作为收藏电台单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForRadioCollectionModel = new DefaultListModel<>();
      // MV 收藏 ListModel
      public DefaultListModel<NetResource> mvCollectionModel = new DefaultListModel<>();
      // 榜单收藏 ListModel
      private DefaultListModel<NetResource> rankingCollectionModel = new DefaultListModel<>();
      // 作为收藏榜单单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForRankingCollectionModel = new DefaultListModel<>();
      // 用户收藏 ListModel
      private DefaultListModel<NetResource> userCollectionModel = new DefaultListModel<>();
      // 作为收藏用户单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForUserCollectionModel = new DefaultListModel<>();
      // 收藏数量面板
      private CustomPanel collectionCountPanel = new CustomPanel();
      // 收藏数量标签
      private CustomLabel collectionCountLabel = new CustomLabel();
      // 收藏音乐工具栏
      private CustomToolBar musicCollectionToolBar = new CustomToolBar();
      // 收藏返回按钮
      private CustomButton collectionBackwardButton = new CustomButton(backwardIcon);
      // 收藏用户听歌记录类型下拉框
      private CustomComboBox<String> collectionRecordTypeComboBox = new CustomComboBox<>();
      private DefaultComboBoxModel<String> collectionRecordTypeComboBoxModel = new DefaultComboBoxModel<>();
      private DefaultComboBoxModel<String> collectionOrderComboBoxModel = new DefaultComboBoxModel<>();
      private DefaultComboBoxModel<String> collectionSortTypeComboBoxModel = new DefaultComboBoxModel<>();
      // 收藏播放全部按钮
      private CustomButton collectionPlayAllButton = new CustomButton(I18n.getText("playAll"), playAllIcon);
      // 收藏刷新按钮
      private CustomButton collectionRefreshButton = new CustomButton(refreshIcon);
      // 收藏页数框
      private CustomTextField collectionPageTextField = new CustomTextField(3);
      // 收藏跳页按钮
      private CustomButton collectionGoButton = new CustomButton(goIcon);
      // 收藏第一页按钮
      private CustomButton collectionStartPageButton = new CustomButton(startPageIcon);
      // 收藏上一页按钮
      private CustomButton collectionLastPageButton = new CustomButton(lastPageIcon);
      // 收藏下一页按钮
      private CustomButton collectionNextPageButton = new CustomButton(nextPageIcon);
      // 收藏最后一页按钮
      private CustomButton collectionEndPageButton = new CustomButton(endPageIcon);
  
      // 筛选 ListModel
      private DefaultListModel filterModel = new DefaultListModel<>();
      // 音乐右键弹出菜单
      private CustomPopupMenu musicPopupMenu = new CustomPopupMenu(THIS);
      // 右键菜单：播放
      private CustomMenuItem playMenuItem = new CustomMenuItem(PLAY_MENU_ITEM_TEXT);
      // 右键菜单：下一首播放
      private CustomMenuItem nextPlayMenuItem = new CustomMenuItem(NEXT_PLAY_MENU_ITEM_TEXT);
      // 右键菜单：播放 MV
      private CustomMenuItem playMvMenuItem = new CustomMenuItem(PLAY_MV_MENU_ITEM_TEXT);
      // 右键菜单：收藏
      private CustomMenuItem collectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 右键菜单：下载
      private CustomMenuItem downloadMenuItem = new CustomMenuItem(DOWNLOAD_MENU_ITEM_TEXT);
      // 右键菜单：查看评论
      private CustomMenuItem commentMenuItem = new CustomMenuItem(COMMENT_MENU_ITEM_TEXT);
      // 右键菜单：查看乐谱
      private CustomMenuItem sheetMenuItem = new CustomMenuItem(SHEET_MENU_ITEM_TEXT);
      // 右键菜单：搜索这首歌曲
      private CustomMenuItem searchSongMenuItem = new CustomMenuItem(SEARCH_SONG_MENU_ITEM_TEXT);
      // 右键菜单：查看相似歌曲
      private CustomMenuItem similarSongMenuItem = new CustomMenuItem(SIMILAR_SONG_MENU_ITEM_TEXT);
      // 右键菜单：查看相关歌单
      private CustomMenuItem relatedPlaylistMenuItem = new CustomMenuItem(RELATED_PLAYLIST_MENU_ITEM_TEXT);
      // 右键菜单：查看歌手/作者
      private CustomMenuItem authorMenuItem = new CustomMenuItem(AUTHOR_MENU_ITEM_TEXT);
      // 右键菜单：查看专辑
      private CustomMenuItem albumMenuItem = new CustomMenuItem(ALBUM_MENU_ITEM_TEXT);
      // 右键菜单：查看推荐电台
      private CustomMenuItem recRadioMenuItem = new CustomMenuItem(REC_RADIO_MENU_ITEM_TEXT);
      // 右键菜单：查看相关 MV
      private CustomMenuItem relatedMvMenuItem = new CustomMenuItem(RELATED_MV_MENU_ITEM_TEXT);
      // 右键菜单：复制名称
      private CustomMenuItem copyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 右键菜单：打开文件所在位置
      private CustomMenuItem locateFileMenuItem = new CustomMenuItem(LOCATE_FILE_MENU_ITEM_TEXT);
      // 右键菜单：编辑歌曲信息
      private CustomMenuItem editInfoMenuItem = new CustomMenuItem(EDIT_INFO_MENU_ITEM_TEXT);
      // 右键菜单：删除
      private CustomMenuItem removeMenuItem = new CustomMenuItem(REMOVE_MENU_ITEM_TEXT);
      // 个人音乐工具栏
      private CustomToolBar personalMusicToolBar = new CustomToolBar();
      // 本地音乐按钮
      private TabButton localMusicButton = new TabButton(I18n.getText("localMusic"), localMusicIcon);
      // 播放历史按钮
      private TabButton historyButton = new TabButton(I18n.getText("history"), historyIcon);
      // 收藏按钮
      private TabButton collectionButton = new TabButton(I18n.getText("collection"), collectionIcon);
  
      // 歌曲列表工具栏
      private CustomToolBar musicToolBar = new CustomToolBar();
      // 添加按钮
      private CustomButton addToolButton = new CustomButton(addIcon);
      // 添加按钮弹出菜单
      private CustomPopupMenu addPopupMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem addFileMenuItem = new CustomMenuItem(I18n.getText("addFile"));
      private CustomMenuItem addDirMenuItem = new CustomMenuItem(I18n.getText("addDir"));
      // 重新导入按钮
      private CustomButton reimportToolButton = new CustomButton(reimportIcon);
      // 管理歌曲目录按钮
      private CustomButton manageCatalogToolButton = new CustomButton(manageCatalogIcon);
      // 删除按钮
      private CustomButton removeToolButton = new CustomButton(removeIcon);
      // 清空按钮
      private CustomButton clearToolButton = new CustomButton(clearIcon);
      // 去重按钮
      private CustomButton duplicateToolButton = new CustomButton(duplicateIcon);
      // 倒序按钮
      private CustomButton reverseToolButton = new CustomButton(reverseIcon);
      // 排序按钮
      private CustomButton sortToolButton = new CustomButton(sortIcon);
      // 排序按钮弹出菜单
      private CustomPopupMenu sortPopupMenu = new CustomPopupMenu(THIS);
      private List<CustomRadioButtonMenuItem> sortOrderButtonGroup = new LinkedList<>();
      private List<CustomRadioButtonMenuItem> sortMethodButtonGroup = new LinkedList<>();
      private CustomRadioButtonMenuItem ascendingMenuItem = new CustomRadioButtonMenuItem(I18n.getText("ascending"));
      private CustomRadioButtonMenuItem descendingMenuItem = new CustomRadioButtonMenuItem(I18n.getText("descending"));
      private CustomRadioButtonMenuItem sortBySongNameAndFileNameMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortBySongNameAndFileName"));
      private CustomRadioButtonMenuItem sortBySongNameMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortBySongName"));
      private CustomRadioButtonMenuItem sortByArtistNameMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortByArtist"));
      private CustomRadioButtonMenuItem sortByAlbumNameMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortByAlbumName"));
      private CustomRadioButtonMenuItem sortByFileNameMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortByFileName"));
      private CustomRadioButtonMenuItem sortByTimeMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortByTime"));
      private CustomRadioButtonMenuItem sortByCreationTimeMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortByCreationTime"));
      private CustomRadioButtonMenuItem sortByLastModifiedTimeMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortByLastModifiedTime"));
      private CustomRadioButtonMenuItem sortByLastAccessTimeMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortByLastAccessTime"));
      private CustomRadioButtonMenuItem sortBySizeMenuItem = new CustomRadioButtonMenuItem(I18n.getText("sortBySize"));
      // 上移按钮
      private CustomButton moveUpToolButton = new CustomButton(moveUpIcon);
      // 下移按钮
      private CustomButton moveDownToolButton = new CustomButton(moveDownIcon);
      // 个人音乐筛选框
      private CustomTextField filterTextField = new CustomTextField(10);
      // 清除输入按钮
      private CustomButton clearInputToolButton = new CustomButton(clearInputIcon);
      // 歌曲数量标签
      private CustomLabel countLabel = new CustomLabel();
  
      // 在线歌曲列表
      private CustomList<NetMusicInfo> netMusicList = new CustomList<>();
      private CustomScrollPane netMusicScrollPane = new CustomScrollPane(netMusicList);
      private DefaultListModel<NetMusicInfo> netMusicListModel = new DefaultListModel<>();
      // 在线音乐右键弹出菜单
      private CustomPopupMenu netMusicPopupMenu = new CustomPopupMenu(THIS);
      // 在线音乐右键菜单：播放
      private CustomMenuItem netMusicPlayMenuItem = new CustomMenuItem(PLAY_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：下一首播放
      private CustomMenuItem netMusicNextPlayMenuItem = new CustomMenuItem(NEXT_PLAY_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：播放 MV
      private CustomMenuItem netMusicPlayMvMenuItem = new CustomMenuItem(PLAY_MV_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：收藏
      private CustomMenuItem netMusicCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：下载
      private CustomMenuItem netMusicDownloadMenuItem = new CustomMenuItem(DOWNLOAD_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：查看评论
      private CustomMenuItem netMusicCommentMenuItem = new CustomMenuItem(COMMENT_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：查看乐谱
      private CustomMenuItem netMusicSheetMenuItem = new CustomMenuItem(SHEET_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：搜索这首歌曲
      private CustomMenuItem netMusicSearchSongMenuItem = new CustomMenuItem(SEARCH_SONG_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：查看相似歌曲
      private CustomMenuItem netMusicSimilarSongMenuItem = new CustomMenuItem(SIMILAR_SONG_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：查看相关歌单
      private CustomMenuItem netMusicRelatedPlaylistMenuItem = new CustomMenuItem(RELATED_PLAYLIST_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：查看歌手/作者
      private CustomMenuItem netMusicAuthorMenuItem = new CustomMenuItem(AUTHOR_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：查看专辑/电台
      private CustomMenuItem netMusicAlbumMenuItem = new CustomMenuItem(ALBUM_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：查看推荐电台
      private CustomMenuItem netMusicRecRadioMenuItem = new CustomMenuItem(REC_RADIO_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：查看相关 MV
      private CustomMenuItem netMusicRelatedMvMenuItem = new CustomMenuItem(RELATED_MV_MENU_ITEM_TEXT);
      // 在线音乐右键菜单：复制名称
      private CustomMenuItem netMusicCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 在线音乐工具栏
      private CustomToolBar netMusicToolBar = new CustomToolBar();
      // 在线音乐搜索框
      private CustomTextField searchTextField = new CustomTextField(8);
      // 在线音乐清除输入按钮
      private CustomButton netMusicClearInputButton = new CustomButton(clearInputIcon);
      // 在线音乐搜索按钮
      private CustomButton searchButton = new CustomButton(searchIcon);
      // 在线音乐标题标签
      private CustomLabel netMusicTitleLabel = new CustomLabel();
      // 歌曲数量面板
      private CustomPanel netMusicCountPanel = new CustomPanel();
      // 歌曲数量标签
      private CustomLabel netMusicCountLabel = new CustomLabel();
      // 在线音乐关键词面板
      public CustomPanel netMusicKeywordsPanel = new CustomPanel();
      private CustomScrollPane netMusicKeywordsPanelScrollPane = new CustomScrollPane(netMusicKeywordsPanel);
      // 在线音乐搜索建议面板
      private CustomPanel netMusicSearchSuggestionPanel = new CustomPanel();
      // 在线音乐搜索建议面板 内部面板1
      private CustomPanel netMusicSearchSuggestionInnerPanel1 = new CustomPanel();
      // 在线音乐搜索建议标签
      private CustomLabel netMusicSearchSuggestionLabel = new CustomLabel(I18n.getText("searchSuggestion"));
      // 刷新搜索建议按钮
      private CustomButton netMusicRefreshSearchSuggestionButton = new CustomButton(refreshIcon);
      // 在线音乐搜索建议面板 内部面板2
      private CustomPanel netMusicSearchSuggestionInnerPanel2 = new CustomPanel();
      // 在线音乐热搜面板
      private CustomPanel netMusicHotSearchPanel = new CustomPanel();
      // 在线音乐热搜面板 内部面板1
      private CustomPanel netMusicHotSearchInnerPanel1 = new CustomPanel();
      // 在线音乐热搜标签
      private CustomLabel netMusicHotSearchLabel = new CustomLabel(I18n.getText("hotSearch"));
      // 刷新热搜按钮
      private CustomButton netMusicRefreshHotSearchButton = new CustomButton(refreshIcon);
      // 在线音乐热搜面板 内部面板2
      private CustomPanel netMusicHotSearchInnerPanel2 = new CustomPanel();
      // 在线音乐搜索历史面板
      public CustomPanel netMusicHistorySearchPanel = new CustomPanel();
      // 在线音乐搜索历史面板 内部面板1
      private CustomPanel netMusicHistorySearchInnerPanel1 = new CustomPanel();
      // 在线音乐搜索历史标签
      private CustomLabel netMusicHistorySearchLabel = new CustomLabel(I18n.getText("historySearch"));
      // 清空搜索历史按钮
      private CustomButton netMusicClearHistorySearchButton = new CustomButton(clearHistorySearchIcon);
      // 在线音乐搜索历史面板 内部面板2
      public CustomPanel netMusicHistorySearchInnerPanel2 = new CustomPanel();
      // 返回关键词面板按钮
      private CustomButton netMusicBackwardButton = new CustomButton(backwardIcon);
      // 在线音乐搜索类型下拉框
      private CustomComboBox<String> netMusicSourceComboBox = new CustomComboBox<>();
      private CustomComboBox<String> netMusicSearchTypeComboBox = new CustomComboBox<>();
      private CustomComboBox<String> netMusicSearchSubTypeComboBox = new CustomComboBox<>();
      // 在线音乐刷新按钮
      private CustomButton netMusicRefreshButton = new CustomButton(refreshIcon);
      // 在线音乐页数框
      private CustomTextField netMusicPageTextField = new CustomTextField(3);
      // 在线音乐跳页按钮
      private CustomButton netMusicGoButton = new CustomButton(goIcon);
      // 在线音乐第一页按钮
      private CustomButton netMusicStartPageButton = new CustomButton(startPageIcon);
      // 在线音乐上一页按钮
      private CustomButton netMusicLastPageButton = new CustomButton(lastPageIcon);
      // 在线音乐下一页按钮
      private CustomButton netMusicNextPageButton = new CustomButton(nextPageIcon);
      // 在线音乐最后一页按钮
      private CustomButton netMusicEndPageButton = new CustomButton(endPageIcon);
  
      // 歌单列表
      private CustomList<NetPlaylistInfo> netPlaylistList = new CustomList<>();
      private CustomScrollPane netPlaylistScrollPane = new CustomScrollPane(netPlaylistList);
      private DefaultListModel netPlaylistListModel = new DefaultListModel<>();
      // 作为歌单单独的 ListModel，与 netMusicListModel 在同一 CustomList 中切换
      private DefaultListModel netMusicListForPlaylistModel = new DefaultListModel<>();
      // 歌单右键弹出菜单
      private CustomPopupMenu netPlaylistPopupMenu = new CustomPopupMenu(THIS);
      // 歌单右键菜单：打开
      private CustomMenuItem netPlaylistOpenMenuItem = new CustomMenuItem(OPEN_MENU_ITEM_TEXT);
      // 歌单右键菜单：播放全部
      private CustomMenuItem netPlaylistPlayAllMenuItem = new CustomMenuItem(PLAY_ALL_MENU_ITEM_TEXT);
      // 歌单右键菜单：收藏
      private CustomMenuItem netPlaylistCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 歌单右键菜单：查看评论
      private CustomMenuItem netPlaylistCommentMenuItem = new CustomMenuItem(COMMENT_MENU_ITEM_TEXT);
      // 歌单右键菜单：查看相似歌单
      private CustomMenuItem netPlaylistSimilarPlaylistMenuItem = new CustomMenuItem(SIMILAR_PLAYLIST_MENU_ITEM_TEXT);
      // 歌单右键菜单：查看创建者
      private CustomMenuItem netPlaylistCreatorMenuItem = new CustomMenuItem(PLAYLIST_CREATOR_MENU_ITEM_TEXT);
      // 歌单右键菜单：查看收藏者
      private CustomMenuItem netPlaylistSubscriberMenuItem = new CustomMenuItem(PLAYLIST_SUBSCRIBER_MENU_ITEM_TEXT);
      // 歌单右键菜单：复制名称
      private CustomMenuItem netPlaylistCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 在线歌单工具栏
      private CustomToolBar netPlaylistToolBar = new CustomToolBar();
      // 歌单标题标签
      private CustomLabel netPlaylistTitleLabel = new CustomLabel();
      // 返回歌单按钮
      private CustomButton netPlaylistBackwardButton = new CustomButton(backwardIcon);
      // 歌单 id
      private CustomCheckBox netPlaylistIdCheckBox = new CustomCheckBox(I18n.getText("playlistId"));
      // 歌单搜索框
      private CustomTextField netPlaylistSearchTextField = new CustomTextField(8);
      // 歌单清除输入按钮
      private CustomButton netPlaylistClearInputButton = new CustomButton(clearInputIcon);
      // 歌单搜索按钮
      private CustomButton netPlaylistSearchButton = new CustomButton(searchIcon);
      // 歌单播放全部按钮
      private CustomButton netPlaylistPlayAllButton = new CustomButton(I18n.getText("playAll"), playAllIcon);
      // 歌单刷新按钮
      private CustomButton netPlaylistRefreshButton = new CustomButton(refreshIcon);
      // 歌单页数框
      private CustomTextField netPlaylistPageTextField = new CustomTextField(3);
      // 歌单跳页按钮
      private CustomButton netPlaylistGoButton = new CustomButton(goIcon);
      // 歌单第一页按钮
      private CustomButton netPlaylistStartPageButton = new CustomButton(startPageIcon);
      // 歌单上一页按钮
      private CustomButton netPlaylistLastPageButton = new CustomButton(lastPageIcon);
      // 歌单下一页按钮
      private CustomButton netPlaylistNextPageButton = new CustomButton(nextPageIcon);
      // 歌单最后一页按钮
      private CustomButton netPlaylistEndPageButton = new CustomButton(endPageIcon);
      // 歌单数量面板
      private CustomPanel netPlaylistCountPanel = new CustomPanel();
      // 歌单源
      private CustomComboBox<String> netPlaylistSourceComboBox = new CustomComboBox<>();
      // 歌单数量标签
      private CustomLabel netPlaylistCountLabel = new CustomLabel();
      // 歌单关键词面板
      public CustomPanel netPlaylistKeywordsPanel = new CustomPanel();
      private CustomScrollPane netPlaylistKeywordsPanelScrollPane = new CustomScrollPane(netPlaylistKeywordsPanel);
      // 歌单搜索历史面板
      public CustomPanel netPlaylistHistorySearchPanel = new CustomPanel();
      // 歌单搜索历史面板 内部面板1
      private CustomPanel netPlaylistHistorySearchInnerPanel1 = new CustomPanel();
      // 歌单搜索历史标签
      private CustomLabel netPlaylistHistorySearchLabel = new CustomLabel(I18n.getText("historySearch"));
      // 清空搜索历史按钮
      private CustomButton netPlaylistClearHistorySearchButton = new CustomButton(clearHistorySearchIcon);
      // 歌单搜索历史面板 内部面板2
      public CustomPanel netPlaylistHistorySearchInnerPanel2 = new CustomPanel();
  
      // 专辑列表
      private CustomList<NetAlbumInfo> netAlbumList = new CustomList<>();
      private CustomScrollPane netAlbumScrollPane = new CustomScrollPane(netAlbumList);
      private DefaultListModel<NetAlbumInfo> netAlbumListModel = new DefaultListModel<>();
      // 作为专辑单独的 ListModel，与 netMusicListModel 在同一 CustomList 中切换
      private DefaultListModel netMusicListForAlbumModel = new DefaultListModel<>();
      // 专辑右键弹出菜单
      private CustomPopupMenu netAlbumPopupMenu = new CustomPopupMenu(THIS);
      // 专辑右键菜单：打开
      private CustomMenuItem netAlbumOpenMenuItem = new CustomMenuItem(OPEN_MENU_ITEM_TEXT);
      // 专辑右键菜单：播放全部
      private CustomMenuItem netAlbumPlayAllMenuItem = new CustomMenuItem(PLAY_ALL_MENU_ITEM_TEXT);
      // 专辑右键菜单：收藏
      private CustomMenuItem netAlbumCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 专辑右键菜单：查看评论
      private CustomMenuItem netAlbumCommentMenuItem = new CustomMenuItem(COMMENT_MENU_ITEM_TEXT);
      // 专辑右键菜单：查看歌手/作者
      private CustomMenuItem netAlbumArtistMenuItem = new CustomMenuItem(ALBUM_ARTIST_MENU_ITEM_TEXT);
      // 专辑右键菜单：查看相似专辑
      private CustomMenuItem netAlbumSimilarMenuItem = new CustomMenuItem(SIMILAR_ALBUM_MENU_ITEM_TEXT);
      // 专辑右键菜单：查看专辑照片
      private CustomMenuItem netAlbumPhotosMenuItem = new CustomMenuItem(ALBUM_PHOTOS_MENU_ITEM_TEXT);
      // 专辑右键菜单：复制名称
      private CustomMenuItem netAlbumCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 在线专辑工具栏
      private CustomToolBar netAlbumToolBar = new CustomToolBar();
      // 返回专辑按钮
      private CustomButton netAlbumBackwardButton = new CustomButton(backwardIcon);
      // 专辑标题标签
      private CustomLabel netAlbumTitleLabel = new CustomLabel();
      // 专辑清除输入按钮
      private CustomButton netAlbumClearInputButton = new CustomButton(clearInputIcon);
      // 专辑搜索框
      private CustomTextField netAlbumSearchTextField = new CustomTextField(8);
      // 专辑搜索按钮
      private CustomButton netAlbumSearchButton = new CustomButton(searchIcon);
      // 专辑播放全部按钮
      private CustomButton netAlbumPlayAllButton = new CustomButton(I18n.getText("playAll"), playAllIcon);
      // 专辑刷新按钮
      private CustomButton netAlbumRefreshButton = new CustomButton(refreshIcon);
      // 专辑页数框
      private CustomTextField netAlbumPageTextField = new CustomTextField(3);
      // 专辑跳页按钮
      private CustomButton netAlbumGoButton = new CustomButton(goIcon);
      // 专辑第一页按钮
      private CustomButton netAlbumStartPageButton = new CustomButton(startPageIcon);
      // 专辑上一页按钮
      private CustomButton netAlbumLastPageButton = new CustomButton(lastPageIcon);
      // 专辑下一页按钮
      private CustomButton netAlbumNextPageButton = new CustomButton(nextPageIcon);
      // 专辑最后一页按钮
      private CustomButton netAlbumEndPageButton = new CustomButton(endPageIcon);
      // 专辑数量面板
      private CustomPanel netAlbumCountPanel = new CustomPanel();
      // 专辑源
      private CustomComboBox<String> netAlbumSourceComboBox = new CustomComboBox<>();
      // 专辑数量标签
      private CustomLabel netAlbumCountLabel = new CustomLabel();
      // 专辑关键词面板
      public CustomPanel netAlbumKeywordsPanel = new CustomPanel();
      private CustomScrollPane netAlbumKeywordsPanelScrollPane = new CustomScrollPane(netAlbumKeywordsPanel);
      // 专辑搜索历史面板
      public CustomPanel netAlbumHistorySearchPanel = new CustomPanel();
      // 专辑搜索历史面板 内部面板1
      private CustomPanel netAlbumHistorySearchInnerPanel1 = new CustomPanel();
      // 专辑搜索历史标签
      private CustomLabel netAlbumHistorySearchLabel = new CustomLabel(I18n.getText("historySearch"));
      // 清空搜索历史按钮
      private CustomButton netAlbumClearHistorySearchButton = new CustomButton(clearHistorySearchIcon);
      // 专辑搜索历史面板 内部面板2
      public CustomPanel netAlbumHistorySearchInnerPanel2 = new CustomPanel();
      // 当前专辑原歌曲
      private NetMusicInfo currAlbumMusicInfo;
      // 当前艺术家
      private NetArtistInfo currAlbumArtistInfo;
      // 当前相似专辑原专辑
      private NetAlbumInfo currAlbumAlbumInfo;
      // 当前专辑原用户
      private NetUserInfo currAlbumUserInfo;
  
      // 歌手列表
      private CustomList<NetArtistInfo> netArtistList = new CustomList<>();
      private CustomScrollPane netArtistScrollPane = new CustomScrollPane(netArtistList);
      private DefaultListModel netArtistListModel = new DefaultListModel<>();
      // 作为歌手单独的 ListModel，与 netMusicListModel 在同一 CustomList 中切换
      private DefaultListModel netMusicListForArtistModel = new DefaultListModel<>();
      // 歌手右键弹出菜单
      private CustomPopupMenu netArtistPopupMenu = new CustomPopupMenu(THIS);
      // 歌手右键菜单：打开
      private CustomMenuItem netArtistOpenMenuItem = new CustomMenuItem(OPEN_MENU_ITEM_TEXT);
      // 歌手右键菜单：播放全部
      private CustomMenuItem netArtistPlayAllMenuItem = new CustomMenuItem(PLAY_ALL_MENU_ITEM_TEXT);
      // 歌手右键菜单：收藏
      private CustomMenuItem netArtistCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 歌手右键菜单：查看歌手专辑
      private CustomMenuItem netArtistBrowseAlbumMenuItem = new CustomMenuItem(BROWSE_ALBUM_MENU_ITEM_TEXT);
      // 歌手右键菜单：查看歌手 MV
      private CustomMenuItem netArtistBrowseMvMenuItem = new CustomMenuItem(BROWSE_MV_MENU_ITEM_TEXT);
      // 歌手右键菜单：查看相似歌手
      private CustomMenuItem netArtistSimilarArtistMenuItem = new CustomMenuItem(SIMILAR_ARTIST_MENU_ITEM_TEXT);
      // 歌手右键菜单：查看歌手粉丝
      private CustomMenuItem netArtistFansMenuItem = new CustomMenuItem(ARTIST_FANS_MENU_ITEM_TEXT);
      // 歌手右键菜单：查看歌手合作人
      private CustomMenuItem netArtistBuddyMenuItem = new CustomMenuItem(ARTIST_BUDDY_MENU_ITEM_TEXT);
      // 歌手右键菜单：查看歌手电台
      private CustomMenuItem netArtistRadiosMenuItem = new CustomMenuItem(ARTIST_RADIO_MENU_ITEM_TEXT);
      // 歌手右键菜单：查看歌手照片
      private CustomMenuItem netArtistPhotosMenuItem = new CustomMenuItem(ARTIST_PHOTOS_MENU_ITEM_TEXT);
      // 歌手右键菜单：复制名称
      private CustomMenuItem netArtistCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 歌手工具栏
      private CustomToolBar netArtistToolBar = new CustomToolBar();
      // 歌手标题标签
      private CustomLabel netArtistTitleLabel = new CustomLabel();
      // 返回歌手按钮
      private CustomButton netArtistBackwardButton = new CustomButton(backwardIcon);
      // 歌手清除输入按钮
      private CustomButton netArtistClearInputButton = new CustomButton(clearInputIcon);
      // 歌手搜索框
      private CustomTextField netArtistSearchTextField = new CustomTextField(8);
      // 歌手搜索按钮
      private CustomButton netArtistSearchButton = new CustomButton(searchIcon);
      // 歌手播放全部按钮
      private CustomButton netArtistPlayAllButton = new CustomButton(I18n.getText("playAll"), playAllIcon);
      // 歌手刷新按钮
      private CustomButton netArtistRefreshButton = new CustomButton(refreshIcon);
      // 歌手页数框
      private CustomTextField netArtistPageTextField = new CustomTextField(3);
      // 歌手跳页按钮
      private CustomButton netArtistGoButton = new CustomButton(goIcon);
      // 歌手第一页按钮
      private CustomButton netArtistStartPageButton = new CustomButton(startPageIcon);
      // 歌手上一页按钮
      private CustomButton netArtistLastPageButton = new CustomButton(lastPageIcon);
      // 歌手下一页按钮
      private CustomButton netArtistNextPageButton = new CustomButton(nextPageIcon);
      // 歌手最后一页按钮
      private CustomButton netArtistEndPageButton = new CustomButton(endPageIcon);
      // 歌手数量面板
      private CustomPanel netArtistCountPanel = new CustomPanel();
      // 歌手源
      private CustomComboBox<String> netArtistSourceComboBox = new CustomComboBox<>();
      // 歌手数量标签
      private CustomLabel netArtistCountLabel = new CustomLabel();
      // 歌手关键词面板
      public CustomPanel netArtistKeywordsPanel = new CustomPanel();
      private CustomScrollPane netArtistKeywordsPanelScrollPane = new CustomScrollPane(netArtistKeywordsPanel);
      // 歌手搜索历史面板
      public CustomPanel netArtistHistorySearchPanel = new CustomPanel();
      // 歌手搜索历史面板 内部面板1
      private CustomPanel netArtistHistorySearchInnerPanel1 = new CustomPanel();
      // 歌手搜索历史标签
      private CustomLabel netArtistHistorySearchLabel = new CustomLabel(I18n.getText("historySearch"));
      // 清空搜索历史按钮
      private CustomButton netArtistClearHistorySearchButton = new CustomButton(clearHistorySearchIcon);
      // 歌手搜索历史面板 内部面板2
      public CustomPanel netArtistHistorySearchInnerPanel2 = new CustomPanel();
  
      // 电台列表
      private CustomList<NetRadioInfo> netRadioList = new CustomList<>();
      private CustomScrollPane netRadioScrollPane = new CustomScrollPane(netRadioList);
      private DefaultListModel netRadioListModel = new DefaultListModel<>();
      // 作为电台单独的 ListModel，与 netMusicListModel 在同一 CustomList 中切换
      private DefaultListModel netMusicListForRadioModel = new DefaultListModel<>();
      // 电台右键弹出菜单
      private CustomPopupMenu netRadioPopupMenu = new CustomPopupMenu(THIS);
      // 电台右键菜单：打开
      private CustomMenuItem netRadioOpenMenuItem = new CustomMenuItem(OPEN_MENU_ITEM_TEXT);
      // 电台右键菜单：播放全部
      private CustomMenuItem netRadioPlayAllMenuItem = new CustomMenuItem(PLAY_ALL_MENU_ITEM_TEXT);
      // 电台右键菜单：收藏
      private CustomMenuItem netRadioCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 电台右键菜单：查看评论
      private CustomMenuItem netRadioCommentMenuItem = new CustomMenuItem(COMMENT_MENU_ITEM_TEXT);
      // 电台右键菜单：查看主播
      private CustomMenuItem netRadioDjMenuItem = new CustomMenuItem(RADIO_DJ_MENU_ITEM_TEXT);
      // 电台右键菜单：查看订阅者
      private CustomMenuItem netRadioSubscriberMenuItem = new CustomMenuItem(RADIO_SUBSCRIBER_MENU_ITEM_TEXT);
      // 电台右键菜单：查看相似电台
      private CustomMenuItem netRadioSimilarMenuItem = new CustomMenuItem(SIMILAR_RADIO_MENU_ITEM_TEXT);
      // 电台右键菜单：查看电台演职员/CV
      private CustomMenuItem netRadioArtistsMenuItem = new CustomMenuItem(RADIO_ARTISTS_MENU_ITEM_TEXT);
      // 电台右键菜单：查看电台照片
      private CustomMenuItem netRadioPhotosMenuItem = new CustomMenuItem(RADIO_PHOTOS_MENU_ITEM_TEXT);
      // 电台右键菜单：查看电台海报
      private CustomMenuItem netRadioPostersMenuItem = new CustomMenuItem(RADIO_POSTERS_MENU_ITEM_TEXT);
      // 电台右键菜单：复制名称
      private CustomMenuItem netRadioCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 电台工具栏
      private CustomToolBar netRadioToolBar = new CustomToolBar();
      // 电台标题标签
      private CustomLabel netRadioTitleLabel = new CustomLabel();
      // 返回电台按钮
      private CustomButton netRadioBackwardButton = new CustomButton(backwardIcon);
      // 电台搜索框
      private CustomTextField netRadioSearchTextField = new CustomTextField(8);
      // 电台清除输入按钮
      private CustomButton netRadioClearInputButton = new CustomButton(clearInputIcon);
      // 电台搜索按钮
      private CustomButton netRadioSearchButton = new CustomButton(searchIcon);
      // 电台排序类型下拉框
      private CustomComboBox<String> netRadioSortTypeComboBox = new CustomComboBox<>();
      // 电台播放全部按钮
      private CustomButton netRadioPlayAllButton = new CustomButton(I18n.getText("playAll"), playAllIcon);
      // 电台刷新按钮
      private CustomButton netRadioRefreshButton = new CustomButton(refreshIcon);
      // 电台页数框
      private CustomTextField netRadioPageTextField = new CustomTextField(3);
      // 电台跳页按钮
      private CustomButton netRadioGoButton = new CustomButton(goIcon);
      // 电台第一页按钮
      private CustomButton netRadioStartPageButton = new CustomButton(startPageIcon);
      // 电台上一页按钮
      private CustomButton netRadioLastPageButton = new CustomButton(lastPageIcon);
      // 电台下一页按钮
      private CustomButton netRadioNextPageButton = new CustomButton(nextPageIcon);
      // 电台最后一页按钮
      private CustomButton netRadioEndPageButton = new CustomButton(endPageIcon);
      // 电台数量面板
      private CustomPanel netRadioCountPanel = new CustomPanel();
      // 电台源
      private CustomComboBox<String> netRadioSourceComboBox = new CustomComboBox<>();
      // 电台数量标签
      private CustomLabel netRadioCountLabel = new CustomLabel();
      // 电台关键词面板
      public CustomPanel netRadioKeywordsPanel = new CustomPanel();
      private CustomScrollPane netRadioKeywordsPanelScrollPane = new CustomScrollPane(netRadioKeywordsPanel);
      // 电台搜索历史面板
      public CustomPanel netRadioHistorySearchPanel = new CustomPanel();
      // 电台搜索历史面板 内部面板1
      private CustomPanel netRadioHistorySearchInnerPanel1 = new CustomPanel();
      // 电台搜索历史标签
      private CustomLabel netRadioHistorySearchLabel = new CustomLabel(I18n.getText("historySearch"));
      // 清空搜索历史按钮
      private CustomButton netRadioClearHistorySearchButton = new CustomButton(clearHistorySearchIcon);
      // 电台搜索历史面板 内部面板2
      public CustomPanel netRadioHistorySearchInnerPanel2 = new CustomPanel();
  
      //  MV 列表
      private CustomList<NetMvInfo> netMvList = new CustomList<>();
      private CustomScrollPane netMvScrollPane = new CustomScrollPane(netMvList);
      private DefaultListModel netMvListModel = new DefaultListModel<>();
      // MV 右键弹出菜单
      private CustomPopupMenu netMvPopupMenu = new CustomPopupMenu(THIS);
      // MV 右键菜单：播放
      private CustomMenuItem netMvPlayMenuItem = new CustomMenuItem(PLAY_MENU_ITEM_TEXT);
      // MV 右键菜单：收藏
      private CustomMenuItem netMvCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // MV 右键菜单：下载
      private CustomMenuItem netMvDownloadMenuItem = new CustomMenuItem(DOWNLOAD_MENU_ITEM_TEXT);
      // MV 右键菜单：查看相似 MV
      private CustomMenuItem netMvSimilarMvMenuItem = new CustomMenuItem(SIMILAR_MV_MENU_ITEM_TEXT);
      // MV 右键菜单：查看视频分集
      private CustomMenuItem netMvVideoEpisodeMenuItem = new CustomMenuItem(VIDEO_EPISODE_MENU_ITEM_TEXT);
      // MV 右键菜单：查看发布者
      private CustomMenuItem netMvCreatorMenuItem = new CustomMenuItem(MV_CREATOR_MENU_ITEM_TEXT);
      // MV 右键菜单：查看评论
      private CustomMenuItem netMvCommentMenuItem = new CustomMenuItem(COMMENT_MENU_ITEM_TEXT);
      // MV 右键菜单：复制名称
      private CustomMenuItem netMvCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // MV 工具栏
      private CustomToolBar netMvToolBar = new CustomToolBar();
      // MV 标题标签
      private CustomLabel netMvTitleLabel = new CustomLabel();
      // MV 搜索框
      private CustomTextField netMvSearchTextField = new CustomTextField(8);
      // MV 清除输入按钮
      private CustomButton netMvClearInputButton = new CustomButton(clearInputIcon);
      // MV 搜索按钮
      private CustomButton netMvSearchButton = new CustomButton(searchIcon);
      // MV 排序类型下拉框
      private CustomComboBox<String> netMvSortTypeComboBox = new CustomComboBox<>();
      // MV 刷新按钮
      private CustomButton netMvRefreshButton = new CustomButton(refreshIcon);
      // MV 页数框
      private CustomTextField netMvPageTextField = new CustomTextField(3);
      // MV 跳页按钮
      private CustomButton netMvGoButton = new CustomButton(goIcon);
      // MV 第一页按钮
      private CustomButton netMvStartPageButton = new CustomButton(startPageIcon);
      // MV 上一页按钮
      private CustomButton netMvLastPageButton = new CustomButton(lastPageIcon);
      // MV 下一页按钮
      private CustomButton netMvNextPageButton = new CustomButton(nextPageIcon);
      // MV 最后一页按钮
      private CustomButton netMvEndPageButton = new CustomButton(endPageIcon);
      // MV 数量面板
      private CustomPanel netMvCountPanel = new CustomPanel();
      // MV 源
      private CustomComboBox<String> netMvSourceComboBox = new CustomComboBox<>();
      //  MV 数量标签
      private CustomLabel netMvCountLabel = new CustomLabel();
      // MV 关键词面板
      public CustomPanel netMvKeywordsPanel = new CustomPanel();
      private CustomScrollPane netMvKeywordsPanelScrollPane = new CustomScrollPane(netMvKeywordsPanel);
      // MV 搜索历史面板
      public CustomPanel netMvHistorySearchPanel = new CustomPanel();
      // MV 搜索历史面板 内部面板1
      private CustomPanel netMvHistorySearchInnerPanel1 = new CustomPanel();
      // MV 搜索历史标签
      private CustomLabel netMvHistorySearchLabel = new CustomLabel(I18n.getText("historySearch"));
      // 清空搜索历史按钮
      private CustomButton netMvClearHistorySearchButton = new CustomButton(clearHistorySearchIcon);
      // MV 搜索历史面板 内部面板2
      public CustomPanel netMvHistorySearchInnerPanel2 = new CustomPanel();
      // 返回关键词面板按钮
      private CustomButton netMvBackwardButton = new CustomButton(backwardIcon);
      // 当前艺术家
      private NetArtistInfo currMvArtistInfo;
  
      // 榜单列表
      private CustomList<NetRankingInfo> netRankingList = new CustomList<>();
      private CustomScrollPane netRankingScrollPane = new CustomScrollPane(netRankingList);
      private DefaultListModel netRankingListModel = new DefaultListModel<>();
      // 作为榜单单独的 ListModel，与 netMusicListModel 在同一 CustomList 中切换
      private DefaultListModel netMusicListForRankingModel = new DefaultListModel<>();
      // 榜单右键弹出菜单
      private CustomPopupMenu netRankingPopupMenu = new CustomPopupMenu(THIS);
      // 榜单右键菜单：打开
      private CustomMenuItem netRankingOpenMenuItem = new CustomMenuItem(OPEN_MENU_ITEM_TEXT);
      // 榜单右键菜单：播放全部
      private CustomMenuItem netRankingPlayAllMenuItem = new CustomMenuItem(PLAY_ALL_MENU_ITEM_TEXT);
      // 榜单右键菜单：收藏
      private CustomMenuItem netRankingCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 榜单右键菜单：查看评论
      private CustomMenuItem netRankingCommentMenuItem = new CustomMenuItem(COMMENT_MENU_ITEM_TEXT);
      // 榜单右键菜单：复制名称
      private CustomMenuItem netRankingCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 榜单工具栏
      private CustomToolBar netRankingToolBar = new CustomToolBar();
      // 返回榜单按钮
      private CustomButton netRankingBackwardButton = new CustomButton(backwardIcon);
      // 榜单播放全部按钮
      private CustomButton netRankingPlayAllButton = new CustomButton(I18n.getText("playAll"), playAllIcon);
      // 榜单刷新按钮
      private CustomButton netRankingRefreshButton = new CustomButton(refreshIcon);
      // 榜单页数框
      private CustomTextField netRankingPageTextField = new CustomTextField(3);
      // 榜单跳页按钮
      private CustomButton netRankingGoButton = new CustomButton(goIcon);
      // 榜单第一页按钮
      private CustomButton netRankingStartPageButton = new CustomButton(startPageIcon);
      // 榜单上一页按钮
      private CustomButton netRankingLastPageButton = new CustomButton(lastPageIcon);
      // 榜单下一页按钮
      private CustomButton netRankingNextPageButton = new CustomButton(nextPageIcon);
      // 榜单最后一页按钮
      private CustomButton netRankingEndPageButton = new CustomButton(endPageIcon);
      // 榜单数量面板
      private CustomPanel netRankingCountPanel = new CustomPanel();
      // 榜单源
      private CustomComboBox<String> netRankingSourceComboBox = new CustomComboBox<>();
      // 榜单数量标签
      private CustomLabel netRankingCountLabel = new CustomLabel();
  
      // 用户列表
      private CustomList<NetUserInfo> netUserList = new CustomList<>();
      private CustomScrollPane netUserScrollPane = new CustomScrollPane(netUserList);
      private DefaultListModel netUserListModel = new DefaultListModel<>();
      // 作为用户单独的 ListModel，与 netMusicListModel 在同一 CustomList 中切换
      private DefaultListModel netMusicListForUserModel = new DefaultListModel<>();
      // 用户右键弹出菜单
      private CustomPopupMenu netUserPopupMenu = new CustomPopupMenu(THIS);
      // 用户右键菜单：打开
      private CustomMenuItem netUserOpenMenuItem = new CustomMenuItem(OPEN_MENU_ITEM_TEXT);
      // 用户右键菜单：播放全部
      private CustomMenuItem netUserPlayAllMenuItem = new CustomMenuItem(PLAY_ALL_MENU_ITEM_TEXT);
      // 用户右键菜单：收藏
      private CustomMenuItem netUserCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 用户右键菜单：查看用户歌单
      private CustomMenuItem netUserPlaylistMenuItem = new CustomMenuItem(USER_PLAYLIST_MENU_ITEM_TEXT);
      // 用户右键菜单：查看用户专辑
      private CustomMenuItem netUserAlbumMenuItem = new CustomMenuItem(USER_ALBUM_MENU_ITEM_TEXT);
      // 用户右键菜单：查看用户电台
      private CustomMenuItem netUserRadioMenuItem = new CustomMenuItem(USER_RADIO_MENU_ITEM_TEXT);
      // 用户右键菜单：查看用户视频
      private CustomMenuItem netUserVideoMenuItem = new CustomMenuItem(USER_VIDEO_MENU_ITEM_TEXT);
      // 用户右键菜单：查看用户关注
      private CustomMenuItem netUserFollowMenuItem = new CustomMenuItem(USER_FOLLOW_MENU_ITEM_TEXT);
      // 用户右键菜单：查看用户粉丝
      private CustomMenuItem netUserFanMenuItem = new CustomMenuItem(USER_FAN_MENU_ITEM_TEXT);
      // 用户右键菜单：复制名称
      private CustomMenuItem netUserCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 用户工具栏
      private CustomToolBar netUserToolBar = new CustomToolBar();
      // 用户标题标签
      private CustomLabel netUserTitleLabel = new CustomLabel();
      // 用户听歌记录类型下拉框
      private CustomComboBox<String> netUserRecordTypeComboBox = new CustomComboBox<>();
      private DefaultComboBoxModel<String> recordTypeComboBoxModel = new DefaultComboBoxModel<>();
      private DefaultComboBoxModel<String> orderComboBoxModel = new DefaultComboBoxModel<>();
      private DefaultComboBoxModel<String> sortTypeComboBoxModel = new DefaultComboBoxModel<>();
      // 返回用户按钮
      private CustomButton netUserBackwardButton = new CustomButton(backwardIcon);
      // 用户搜索框
      private CustomTextField netUserSearchTextField = new CustomTextField(8);
      // 用户清除输入按钮
      private CustomButton netUserClearInputButton = new CustomButton(clearInputIcon);
      // 用户搜索按钮
      private CustomButton netUserSearchButton = new CustomButton(searchIcon);
      // 用户播放全部按钮
      private CustomButton netUserPlayAllButton = new CustomButton(I18n.getText("playAll"), playAllIcon);
      // 用户刷新按钮
      private CustomButton netUserRefreshButton = new CustomButton(refreshIcon);
      // 用户页数框
      private CustomTextField netUserPageTextField = new CustomTextField(3);
      // 用户跳页按钮
      private CustomButton netUserGoButton = new CustomButton(goIcon);
      // 用户第一页按钮
      private CustomButton netUserStartPageButton = new CustomButton(startPageIcon);
      // 用户上一页按钮
      private CustomButton netUserLastPageButton = new CustomButton(lastPageIcon);
      // 用户下一页按钮
      private CustomButton netUserNextPageButton = new CustomButton(nextPageIcon);
      // 用户最后一页按钮
      private CustomButton netUserEndPageButton = new CustomButton(endPageIcon);
      // 用户数量面板
      private CustomPanel netUserCountPanel = new CustomPanel();
      // 用户源
      private CustomComboBox<String> netUserSourceComboBox = new CustomComboBox<>();
      // 用户数量标签
      private CustomLabel netUserCountLabel = new CustomLabel();
      // 用户关键词面板
      public CustomPanel netUserKeywordsPanel = new CustomPanel();
      private CustomScrollPane netUserKeywordsPanelScrollPane = new CustomScrollPane(netUserKeywordsPanel);
      // 用户搜索历史面板
      public CustomPanel netUserHistorySearchPanel = new CustomPanel();
      // 用户搜索历史面板 内部面板1
      private CustomPanel netUserHistorySearchInnerPanel1 = new CustomPanel();
      // 用户搜索历史标签
      private CustomLabel netUserHistorySearchLabel = new CustomLabel(I18n.getText("historySearch"));
      // 清空搜索历史按钮
      private CustomButton netUserClearHistorySearchButton = new CustomButton(clearHistorySearchIcon);
      // 用户搜索历史面板 内部面板2
      public CustomPanel netUserHistorySearchInnerPanel2 = new CustomPanel();
  
      // 推荐歌单/专辑/歌手/电台列表
      private CustomList<NetResource> itemRecommendList = new CustomList<>();
      private CustomScrollPane itemRecommendScrollPane = new CustomScrollPane(itemRecommendList);
      // 推荐歌单 ListModel
      private DefaultListModel<NetResource> playlistRecommendListModel = new DefaultListModel<>();
      // 作为推荐歌单单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForPlaylistRecommendModel = new DefaultListModel<>();
      // 推荐专辑 ListModel
      private DefaultListModel<NetResource> albumRecommendListModel = new DefaultListModel<>();
      // 作为推荐专辑单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForAlbumRecommendModel = new DefaultListModel<>();
      // 推荐歌手 ListModel
      private DefaultListModel<NetResource> artistRecommendListModel = new DefaultListModel<>();
      // 作为推荐歌手单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForArtistRecommendModel = new DefaultListModel<>();
      // 推荐电台 ListModel
      private DefaultListModel<NetResource> radioRecommendListModel = new DefaultListModel<>();
      // 作为推荐电台单独的 ListModel，切换
      private DefaultListModel<NetMusicInfo> netMusicListForRadioRecommendModel = new DefaultListModel<>();
      // 推荐 MV ListModel
      private DefaultListModel<NetResource> mvRecommendListModel = new DefaultListModel<>();
      // 推荐单曲 ListModel
      private DefaultListModel<NetMusicInfo> netMusicRecommendListModel = new DefaultListModel<>();
      // 推荐工具栏
      private CustomToolBar recommendToolBar = new CustomToolBar();
      // 推荐歌单按钮
      private TabButton playlistRecommendButton = new TabButton(I18n.getText("playlistRecommend"), playlistRecommendIcon);
      // 精品歌单按钮
      private TabButton highQualityPlaylistButton = new TabButton(I18n.getText("highQualityPlaylist"), highQualityPlaylistIcon);
      // 飙升歌曲按钮
      private TabButton hotMusicButton = new TabButton(I18n.getText("hotMusic"), hotMusicIcon);
      // 推荐单曲按钮
      private TabButton newMusicButton = new TabButton(I18n.getText("newMusic"), musicRecommendIcon);
      // 新碟上架按钮
      private TabButton newAlbumRecommendButton = new TabButton(I18n.getText("newAlbum"), newAlbumRecommendIcon);
      // 歌手排行按钮
      private TabButton artistListRecommendButton = new TabButton(I18n.getText("artistList"), artistListRecommendIcon);
      // 新晋电台按钮
      private TabButton newRadioRecommendButton = new TabButton(I18n.getText("newRadio"), newRadioRecommendIcon);
      // 热门电台按钮
      private TabButton hotRadioRecommendButton = new TabButton(I18n.getText("hotRadio"), hotRadioRecommendIcon);
      // 推荐节目按钮
      private TabButton programRecommendButton = new TabButton(I18n.getText("programRecommend"), programRecommendIcon);
      // 推荐 MV 按钮
      private TabButton mvRecommendButton = new TabButton(I18n.getText("mvRecommend"), mvRecommendIcon);
      // 推荐数量面板
      private CustomPanel recommendCountPanel = new CustomPanel();
      // 推荐源
      private CustomComboBox<String> netRecommendSourceComboBox = new CustomComboBox<>();
      // 推荐排序
      private CustomComboBox<String> netRecommendSortTypeComboBox = new CustomComboBox<>();
      // 推荐数量标签
      private CustomLabel recommendCountLabel = new CustomLabel();
      // 推荐歌单/专辑/歌手/电台音乐工具栏
      private CustomToolBar musicRecommendToolBar = new CustomToolBar();
      // 返回推荐歌单/专辑/歌手/电台按钮
      private CustomButton recommendBackwardButton = new CustomButton(backwardIcon);
      // 推荐歌单标签下拉框
      private CustomComboBox<String> netRecommendTagComboBox = new CustomComboBox<>();
      // 推荐播放全部按钮
      private CustomButton netRecommendPlayAllButton = new CustomButton(I18n.getText("playAll"), playAllIcon);
      // 推荐刷新按钮
      private CustomButton netRecommendRefreshButton = new CustomButton(refreshIcon);
      // 推荐页数框
      private CustomTextField netRecommendPageTextField = new CustomTextField(3);
      // 推荐跳页按钮
      private CustomButton netRecommendGoButton = new CustomButton(goIcon);
      // 推荐第一页按钮
      private CustomButton netRecommendStartPageButton = new CustomButton(startPageIcon);
      // 推荐上一页按钮
      private CustomButton netRecommendLastPageButton = new CustomButton(lastPageIcon);
      // 推荐下一页按钮
      private CustomButton netRecommendNextPageButton = new CustomButton(nextPageIcon);
      // 推荐最后一页按钮
      private CustomButton netRecommendEndPageButton = new CustomButton(endPageIcon);
  
      // 下载列表
      private CustomList<Task> downloadList = new CustomList<>();
      private CustomScrollPane downloadListScrollPane = new CustomScrollPane(downloadList);
      // 下载列表 ListModel
      private DefaultListModel<Task> downloadListModel = new DefaultListModel<>();
      // 下载右键弹出菜单
      private CustomPopupMenu downloadPopupMenu = new CustomPopupMenu(THIS);
      // 下载右键菜单：播放
      private CustomMenuItem downloadPlayMenuItem = new CustomMenuItem(PLAY_MENU_ITEM_TEXT);
      // 下载右键菜单：下一首播放
      private CustomMenuItem downloadNextPlayMenuItem = new CustomMenuItem(NEXT_PLAY_MENU_ITEM_TEXT);
      // 下载右键菜单：打开文件所在位置
      private CustomMenuItem downloadLocateFileMenuItem = new CustomMenuItem(I18n.getText("locateFileMenuItem"));
      // 下载右键菜单：编辑歌曲信息
      private CustomMenuItem downloadEditInfoMenuItem = new CustomMenuItem(I18n.getText("editInfoMenuItem"));
      // 下载右键菜单：取消任务
      private CustomMenuItem cancelTaskMenuItem = new CustomMenuItem(I18n.getText("cancelTaskMenuItem"));
      // 下载右键菜单：重新开始
      private CustomMenuItem restartTaskMenuItem = new CustomMenuItem(I18n.getText("restartTaskMenuItem"));
      // 下载右键菜单：删除任务
      private CustomMenuItem removeTaskMenuItem = new CustomMenuItem(I18n.getText("removeTaskMenuItem"));
      // 下载工具栏
      private CustomToolBar downloadToolBar = new CustomToolBar();
      // 重新开始选中任务按钮
      private CustomButton restartSelectedTasksButton = new CustomButton(restartTaskMenuItemIcon);
      // 取消选中任务按钮
      private CustomButton cancelSelectedTasksButton = new CustomButton(cancelTaskMenuItemIcon);
      // 删除选中任务按钮
      private CustomButton removeSelectedTasksButton = new CustomButton(removeMenuItemIcon);
      // 重新开始全部任务按钮
      private CustomButton restartAllTasksButton = new CustomButton(restartAllTasksIcon);
      // 取消全部任务按钮
      private CustomButton cancelAllTasksButton = new CustomButton(cancelAllTasksIcon);
      // 删除全部任务按钮
      private CustomButton removeAllTasksButton = new CustomButton(removeAllTasksIcon);
      // 下载任务数量标签
      private CustomLabel taskCountLabel = new CustomLabel(String.format(TOTAL_MSG, 0));
  
      // 播放队列
      private CustomList<MusicResource> playQueue = new CustomList<>();
      private ListCellRenderer playQueueRenderer;
      private CustomScrollPane playQueueScrollPane = new CustomScrollPane(playQueue);
      // 播放队列 ListModel
      private DefaultListModel<MusicResource> playQueueModel = new DefaultListModel<>();
      // 播放队列右键弹出菜单
      private CustomPopupMenu playQueuePopupMenu = new CustomPopupMenu(THIS);
      // 播放队列右键菜单：播放
      private CustomMenuItem playQueuePlayMenuItem = new CustomMenuItem(PLAY_MENU_ITEM_TEXT);
      // 播放队列右键菜单：下一首播放
      private CustomMenuItem playQueueNextPlayMenuItem = new CustomMenuItem(NEXT_PLAY_MENU_ITEM_TEXT);
      // 播放队列右键菜单：播放 MV
      private CustomMenuItem playQueuePlayMvMenuItem = new CustomMenuItem(PLAY_MV_MENU_ITEM_TEXT);
      // 播放队列右键菜单：收藏
      private CustomMenuItem playQueueCollectMenuItem = new CustomMenuItem(COLLECT_MENU_ITEM_TEXT);
      // 播放队列右键菜单：下载
      private CustomMenuItem playQueueDownloadMenuItem = new CustomMenuItem(DOWNLOAD_MENU_ITEM_TEXT);
      // 播放队列右键菜单：查看评论
      private CustomMenuItem playQueueCommentMenuItem = new CustomMenuItem(COMMENT_MENU_ITEM_TEXT);
      // 播放队列右键菜单：查看乐谱
      private CustomMenuItem playQueueSheetMenuItem = new CustomMenuItem(SHEET_MENU_ITEM_TEXT);
      // 播放队列右键菜单：搜索这首歌曲
      private CustomMenuItem playQueueSearchSongMenuItem = new CustomMenuItem(SEARCH_SONG_MENU_ITEM_TEXT);
      // 播放队列右键菜单：查看相似歌曲
      private CustomMenuItem playQueueSimilarSongMenuItem = new CustomMenuItem(SIMILAR_SONG_MENU_ITEM_TEXT);
      // 播放队列右键菜单：查看相关歌单
      private CustomMenuItem playQueueRelatedPlaylistMenuItem = new CustomMenuItem(RELATED_PLAYLIST_MENU_ITEM_TEXT);
      // 播放队列右键菜单：查看歌手/作者
      private CustomMenuItem playQueueAuthorMenuItem = new CustomMenuItem(AUTHOR_MENU_ITEM_TEXT);
      // 播放队列右键菜单：查看专辑/电台
      private CustomMenuItem playQueueAlbumMenuItem = new CustomMenuItem(ALBUM_MENU_ITEM_TEXT);
      // 播放队列右键菜单：查看推荐电台
      private CustomMenuItem playQueueRecRadioMenuItem = new CustomMenuItem(REC_RADIO_MENU_ITEM_TEXT);
      // 播放队列右键菜单：查看相关 MV
      private CustomMenuItem playQueueRelatedMvMenuItem = new CustomMenuItem(RELATED_MV_MENU_ITEM_TEXT);
      // 播放队列右键菜单：复制名称
      private CustomMenuItem playQueueCopyNameMenuItem = new CustomMenuItem(COPY_NAME_MENU_ITEM_TEXT);
      // 播放队列右键菜单：打开文件所在位置
      private CustomMenuItem playQueueLocateFileMenuItem = new CustomMenuItem(LOCATE_FILE_MENU_ITEM_TEXT);
      // 播放队列右键菜单：编辑歌曲信息
      private CustomMenuItem playQueueEditInfoMenuItem = new CustomMenuItem(EDIT_INFO_MENU_ITEM_TEXT);
      // 播放队列右键菜单：删除
      private CustomMenuItem playQueueRemoveMenuItem = new CustomMenuItem(REMOVE_MENU_ITEM_TEXT);
      // 播放队列工具栏
      private CustomToolBar playQueueToolBar = new CustomToolBar();
      // 删除选中歌曲按钮
      private CustomButton playQueueRemoveToolButton = new CustomButton(removeIcon);
      // 清空播放队列按钮
      private CustomButton playQueueClearToolButton = new CustomButton(clearIcon);
      // 去重按钮
      private CustomButton playQueueDuplicateToolButton = new CustomButton(duplicateIcon);
      // 倒序按钮
      private CustomButton playQueueReverseToolButton = new CustomButton(reverseIcon);
      // 上移按钮
      private CustomButton playQueueMoveUpToolButton = new CustomButton(moveUpIcon);
      // 下移按钮
      private CustomButton playQueueMoveDownToolButton = new CustomButton(moveDownIcon);
      // 歌曲数量标签
      private CustomLabel playQueueCountLabel = new CustomLabel(String.format(TOTAL_MSG, 0));
  
      // 描述面板弹出菜单
      private CustomPopupMenu descriptionPanelPopupMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem saveDescCoverImgMenuItem = new CustomMenuItem(I18n.getText("saveDescCover"));
      private CustomMenuItem saveDescBgImgMenuItem = new CustomMenuItem(I18n.getText("saveDescBg"));
      private CustomMenuItem copyDescNameMenuItem = new CustomMenuItem(I18n.getText("copyDescName"));
      private CustomMenuItem copyDescTagMenuItem = new CustomMenuItem(I18n.getText("copyDescTag"));
      private CustomMenuItem copyDescMenuItem = new CustomMenuItem(I18n.getText("copyDesc"));
      private CustomPanel playlistDescriptionPanel = new CustomPanel();
      private CustomPanel albumDescriptionPanel = new CustomPanel();
      private CustomPanel artistDescriptionPanel = new CustomPanel();
      private CustomPanel radioDescriptionPanel = new CustomPanel();
      private CustomPanel rankingDescriptionPanel = new CustomPanel();
      private CustomPanel userDescriptionPanel = new CustomPanel();
      private CustomPanel recommendItemDescriptionPanel = new CustomPanel();
      private CustomPanel collectionItemDescriptionPanel = new CustomPanel();
      // 歌单/专辑/歌手/电台封面图和名称标签
      private CustomLabel playlistCoverAndNameLabel = new CustomLabel();
      private CustomLabel albumCoverAndNameLabel = new CustomLabel();
      private CustomLabel artistCoverAndNameLabel = new CustomLabel();
      private CustomLabel radioCoverAndNameLabel = new CustomLabel();
      private CustomLabel rankingCoverAndNameLabel = new CustomLabel();
      private CustomLabel userCoverAndNameLabel = new CustomLabel();
      private CustomLabel recommendItemCoverAndNameLabel = new CustomLabel();
      private CustomLabel collectionItemCoverAndNameLabel = new CustomLabel();
      // 歌单标签
      private CustomLabel playlistTagLabel = new CustomLabel();
      private CustomLabel artistTagLabel = new CustomLabel();
      private CustomLabel radioTagLabel = new CustomLabel();
      private CustomLabel userTagLabel = new CustomLabel();
      private CustomLabel recommendItemTagLabel = new CustomLabel();
      private CustomLabel collectionItemTagLabel = new CustomLabel();
      // 歌单/专辑/歌手/电台描述标签
      private CustomLabel playlistDescriptionLabel = new CustomLabel();
      private CustomLabel albumDescriptionLabel = new CustomLabel();
      private CustomLabel artistDescriptionLabel = new CustomLabel();
      private CustomLabel radioDescriptionLabel = new CustomLabel();
      private CustomLabel rankingDescriptionLabel = new CustomLabel();
      private CustomLabel userDescriptionLabel = new CustomLabel();
      private CustomLabel recommendItemDescriptionLabel = new CustomLabel();
      private CustomLabel collectionItemDescriptionLabel = new CustomLabel();
      // 描述部分滚动面板
      private CustomScrollPane playlistDescriptionScrollPane = new CustomScrollPane(playlistDescriptionPanel);
      private CustomScrollPane albumDescriptionScrollPane = new CustomScrollPane(albumDescriptionPanel);
      private CustomScrollPane artistDescriptionScrollPane = new CustomScrollPane(artistDescriptionPanel);
      private CustomScrollPane radioDescriptionScrollPane = new CustomScrollPane(radioDescriptionPanel);
      private CustomScrollPane rankingDescriptionScrollPane = new CustomScrollPane(rankingDescriptionPanel);
      private CustomScrollPane userDescriptionScrollPane = new CustomScrollPane(userDescriptionPanel);
      private CustomScrollPane recommendItemDescriptionScrollPane = new CustomScrollPane(recommendItemDescriptionPanel);
      private CustomScrollPane collectionItemDescriptionScrollPane = new CustomScrollPane(collectionItemDescriptionPanel);
      // 描述部分总面板
      private CustomPanel playlistDescriptionOuterPanel = new CustomPanel();
      private CustomPanel albumDescriptionOuterPanel = new CustomPanel();
      private CustomPanel artistDescriptionOuterPanel = new CustomPanel();
      private CustomPanel radioDescriptionOuterPanel = new CustomPanel();
      private CustomPanel rankingDescriptionOuterPanel = new CustomPanel();
      private CustomPanel userDescriptionOuterPanel = new CustomPanel();
      private CustomPanel recommendItemDescriptionOuterPanel = new CustomPanel();
      private CustomPanel collectionItemDescriptionOuterPanel = new CustomPanel();
      // 描述部分收藏面板
      private CustomPanel playlistDescriptionCollectionPanel = new CustomPanel();
      private CustomPanel albumDescriptionCollectionPanel = new CustomPanel();
      private CustomPanel artistDescriptionCollectionPanel = new CustomPanel();
      private CustomPanel radioDescriptionCollectionPanel = new CustomPanel();
      private CustomPanel rankingDescriptionCollectionPanel = new CustomPanel();
      private CustomPanel userDescriptionCollectionPanel = new CustomPanel();
      private CustomPanel recommendItemDescriptionCollectionPanel = new CustomPanel();
      private CustomPanel collectionItemDescriptionCollectionPanel = new CustomPanel();
      // 描述部分收藏按钮
      private CustomButton playlistDescriptionCollectionButton = new CustomButton();
      private CustomButton albumDescriptionCollectionButton = new CustomButton();
      private CustomButton artistDescriptionCollectionButton = new CustomButton();
      private CustomButton radioDescriptionCollectionButton = new CustomButton();
      private CustomButton rankingDescriptionCollectionButton = new CustomButton();
      private CustomButton userDescriptionCollectionButton = new CustomButton();
      private CustomButton recommendItemDescriptionCollectionButton = new CustomButton();
      private CustomButton collectionItemDescriptionCollectionButton = new CustomButton();
  
      // 全局 Panel
      public GlobalPanel globalPanel = new GlobalPanel();
      // 页面切换 Panel
      private CustomPanel changePanePanel = new CustomPanel();
      // 控制面板 Panel
      private CustomPanel controlPanel = new CustomPanel();
      // 歌词控制面板 Panel
      private CustomPanel controlLrcPanel = new CustomPanel();
      // 进度条 Panel
      private CustomPanel progressPanel = new CustomPanel();
  
      // 顶部盒子
      private Box topBox = new Box(BoxLayout.X_AXIS);
      // 左部工具条和歌曲列表盒子
      private Box leftBox = new Box(BoxLayout.Y_AXIS);
      private Box netLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box netPlaylistLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box netAlbumLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box netArtistLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box netRadioLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box netMvLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box netRankingLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box netUserLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box recommendLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box collectionLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box downloadLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box playQueueLeftBox = new Box(BoxLayout.Y_AXIS);
      // 左部收藏列表盒子
      private Box musicCollectionLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box playlistCollectionLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box albumCollectionLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box artistCollectionLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box radioCollectionLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box mvCollectionLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box rankingCollectionLeftBox = new Box(BoxLayout.Y_AXIS);
      private Box userCollectionLeftBox = new Box(BoxLayout.Y_AXIS);
      // 底部进度条和控制面板盒子
      private Box bottomBox = new Box(BoxLayout.Y_AXIS);
      // 进度条水平盒子
      private Box progressBox = new Box(BoxLayout.X_AXIS);
      // 控制面板水平盒子
      private Box controlBox = new Box(BoxLayout.X_AXIS);
      // 歌曲信息和歌词盒子
      private Box infoAndLrcBox = new Box(BoxLayout.X_AXIS);
      // 歌词和频谱盒子
      private Box lrcAndSpecBox = new Box(BoxLayout.Y_AXIS);
      // 左边专辑和标签盒子
      private Box leftInfoBox = new Box(BoxLayout.Y_AXIS);
      // 左下标签纵向排列盒子
      private Box leftBottomBox = new Box(BoxLayout.Y_AXIS);
      // 列表盒子(除搜索工具框，下面所有部分)
      private Box playlistListBox = new Box(BoxLayout.X_AXIS);
      private Box albumListBox = new Box(BoxLayout.X_AXIS);
      private Box artistListBox = new Box(BoxLayout.X_AXIS);
      private Box radioListBox = new Box(BoxLayout.X_AXIS);
      private Box rankingListBox = new Box(BoxLayout.X_AXIS);
      private Box userListBox = new Box(BoxLayout.X_AXIS);
      private Box recommendItemListBox = new Box(BoxLayout.X_AXIS);
      private Box collectionItemListBox = new Box(BoxLayout.X_AXIS);
      // 列表和数量标签盒子
      private Box playlistListCountBox = new Box(BoxLayout.Y_AXIS);
      private Box albumListCountBox = new Box(BoxLayout.Y_AXIS);
      private Box artistListCountBox = new Box(BoxLayout.Y_AXIS);
      private Box radioListCountBox = new Box(BoxLayout.Y_AXIS);
      private Box rankingListCountBox = new Box(BoxLayout.Y_AXIS);
      private Box userListCountBox = new Box(BoxLayout.Y_AXIS);
      private Box recommendItemListCountBox = new Box(BoxLayout.Y_AXIS);
      private Box collectionItemListCountBox = new Box(BoxLayout.Y_AXIS);
  
      // 评论盒子
      private Box netCommentBox = new Box(BoxLayout.Y_AXIS);
      // 评论列表
      private CustomList<NetCommentInfo> netCommentList = new CustomList<>();
      private CustomScrollPane netCommentScrollPane = new CustomScrollPane(netCommentList);
      private DefaultListModel<NetCommentInfo> netCommentListModel = new DefaultListModel<>();
      // 评论右键弹出菜单
      private CustomPopupMenu netCommentPopupMenu = new CustomPopupMenu(THIS);
      // 评论右键菜单：复制评论
      private CustomMenuItem netCommentCopyMenuItem = new CustomMenuItem(I18n.getText("commentCopy"));
      // 评论右键菜单：导出用户头像
      private CustomMenuItem netCommentSaveProfileMenuItem = new CustomMenuItem(I18n.getText("saveProfile"));
      // 评论右键菜单：查看用户
      private CustomMenuItem netCommentUserMenuItem = new CustomMenuItem(I18n.getText("commentUser"));
      // 评论右键菜单：查看用户歌单
      private CustomMenuItem netCommentPlaylistMenuItem = new CustomMenuItem(I18n.getText("commentPlaylist"));
      // 评论右键菜单：查看用户专辑
      private CustomMenuItem netCommentAlbumMenuItem = new CustomMenuItem(I18n.getText("commentAlbum"));
      // 评论工具栏
      private CustomToolBar netCommentToolBar = new CustomToolBar();
      // 评论返回按钮
      private CustomButton netCommentBackwardButton = new CustomButton(backwardIcon);
      // 评论标题标签
      private CustomLabel netCommentTitleLabel = new CustomLabel();
      // 评论类型下拉框
      private CustomComboBox<String> netCommentTypeComboBox = new CustomComboBox<>();
      // 评论刷新按钮
      private CustomButton netCommentRefreshButton = new CustomButton(refreshIcon);
      // 评论页数框
      private CustomTextField netCommentPageTextField = new CustomTextField(3);
      // 评论跳页按钮
      private CustomButton netCommentGoButton = new CustomButton(goIcon);
      // 评论第一页按钮
      private CustomButton netCommentStartPageButton = new CustomButton(startPageIcon);
      // 评论上一页按钮
      private CustomButton netCommentLastPageButton = new CustomButton(lastPageIcon);
      // 评论下一页按钮
      private CustomButton netCommentNextPageButton = new CustomButton(nextPageIcon);
      // 评论最后一页按钮
      private CustomButton netCommentEndPageButton = new CustomButton(endPageIcon);
      // 评论数量面板
      private CustomPanel netCommentCountPanel = new CustomPanel();
      // 评论数量标签
      private CustomLabel netCommentCountLabel = new CustomLabel();
      // 当前显示评论的对象信息
      private NetResource currCommentResource;
  
      // 乐谱盒子
      private Box netSheetBox = new Box(BoxLayout.Y_AXIS);
      // 乐谱列表
      private CustomList<NetSheetInfo> netSheetList = new CustomList<>();
      private CustomScrollPane netSheetScrollPane = new CustomScrollPane(netSheetList);
      private DefaultListModel<NetSheetInfo> netSheetListModel = new DefaultListModel<>();
      // 乐谱右键弹出菜单
      private CustomPopupMenu netSheetPopupMenu = new CustomPopupMenu(THIS);
      // 乐谱右键菜单：查看乐谱
      private CustomMenuItem netSheetBrowseMenuItem = new CustomMenuItem(I18n.getText("sheetBrowse"));
      // 乐谱右键菜单：复制名称
      private CustomMenuItem netSheetCopyNameMenuItem = new CustomMenuItem(I18n.getText("sheetCopy"));
      // 乐谱工具栏
      private CustomToolBar netSheetToolBar = new CustomToolBar();
      // 乐谱返回按钮
      private CustomButton netSheetBackwardButton = new CustomButton(backwardIcon);
      // 乐谱标题标签
      private CustomLabel netSheetTitleLabel = new CustomLabel();
      //    // 乐谱类型下拉框
  //    private CustomComboBox<String> netSheetTypeComboBox = new CustomComboBox<>();
      // 乐谱刷新按钮
      private CustomButton netSheetRefreshButton = new CustomButton(refreshIcon);
      // 乐谱页数框
      private CustomTextField netSheetPageTextField = new CustomTextField(3);
      // 乐谱跳页按钮
      private CustomButton netSheetGoButton = new CustomButton(goIcon);
      // 乐谱第一页按钮
      private CustomButton netSheetStartPageButton = new CustomButton(startPageIcon);
      // 乐谱上一页按钮
      private CustomButton netSheetLastPageButton = new CustomButton(lastPageIcon);
      // 乐谱下一页按钮
      private CustomButton netSheetNextPageButton = new CustomButton(nextPageIcon);
      // 乐谱最后一页按钮
      private CustomButton netSheetEndPageButton = new CustomButton(endPageIcon);
      // 乐谱数量面板
      private CustomPanel netSheetCountPanel = new CustomPanel();
      // 乐谱数量标签
      private CustomLabel netSheetCountLabel = new CustomLabel();
      // 当前显示乐谱的歌曲信息
      private NetMusicInfo currSheetMusicInfo;
  
      // 收藏打开事件
      Runnable openCollectionItemAction;
      // 推荐打开事件
      Runnable openRecommendItemAction;
      // 获取榜单事件
      Runnable getRankingAction;
  
      // 当前音效索引
      public int currSoundEffect;
      // 当前均衡
      public double[] ed;
  
      // 当前相似歌曲原歌曲
      private NetMusicInfo currMusicMusicInfo;
      // 当前相关歌单原歌曲
      private NetMusicInfo currPlaylistMusicInfo;
      // 当前相关 MV 原歌曲
      private NetMusicInfo currMvMusicInfo;
      // 当前相似歌单原歌单
      private NetPlaylistInfo currPlaylistPlaylistInfo;
      // 当前歌单原评论
      private NetCommentInfo currPlaylistCommentInfo;
      // 当前歌单原用户
      private NetUserInfo currPlaylistUserInfo;
      // 当前电台原歌曲
      private NetMusicInfo currRadioMusicInfo;
      // 当前推荐电台原歌曲
      private NetMusicInfo currRecRadioMusicInfo;
      // 当前电台原用户
      private NetUserInfo currRadioUserInfo;
      // 当前专辑原评论
      private NetCommentInfo currAlbumCommentInfo;
      // 当前相似 MV 原 MV
      private NetMvInfo currMvMvInfo;
      // 当前分集原 MV
      private NetMvInfo currEpisodesMvInfo;
      // 当前视频原用户
      private NetUserInfo currMvUserInfo;
      // 当前歌手原歌曲
      private NetMusicInfo currArtistMusicInfo;
      // 当前歌手原专辑
      private NetAlbumInfo currArtistAlbumInfo;
      // 当前相似歌手原歌手
      private NetArtistInfo currArtistArtistInfo;
      // 当前合作人原歌手
      private NetArtistInfo currBuddyArtistInfo;
      // 当前电台原歌手
      private NetArtistInfo currRadioArtistInfo;
      // 当前相似电台原电台
      private NetRadioInfo currRadioRadioInfo;
      // 当前歌手原 MV
      private NetMvInfo currArtistMvInfo;
      // 当前 CV 原电台
      private NetRadioInfo currCVRadioInfo;
      // 当前演职员原电台
      private NetRadioInfo currArtistRadioInfo;
      // 当前关注用户原用户
      private NetUserInfo currFollowUserUserInfo;
      // 当前粉丝用户原用户
      private NetUserInfo currFanUserUserInfo;
      // 当前用户(作者)原歌曲
      private NetMusicInfo currAuthorMusicInfo;
      // 当前用户(作者)原专辑
      private NetAlbumInfo currAuthorAlbumInfo;
      // 当前用户(创建者)原歌单
      private NetPlaylistInfo currUserPlaylistInfo;
      // 当前用户(收藏者)原歌单
      private NetPlaylistInfo currSubscriberPlaylistInfo;
      // 当前用户(粉丝)原歌手
      private NetArtistInfo currUserArtistInfo;
      // 当前用户(发布者)原 MV
      private NetMvInfo currUserMvInfo;
      // 当前用户(主播)原电台
      private NetRadioInfo currUserRadioInfo;
      // 当前用户(订阅者)原电台
      private NetRadioInfo currSubscriberRadioInfo;
      // 当前用户原评论
      private NetCommentInfo currUserCommentInfo;
  
      // 格言
      private String motto;
      // 格言弹出菜单
      private CustomPopupMenu mottoPopupMenu = new CustomPopupMenu(THIS);
      // 格言右键菜单：复制格言
      private CustomMenuItem copyMottoMenuItem = new CustomMenuItem(I18n.getText("copyMotto"));
      // 格言右键菜单：下一条格言
      private CustomMenuItem nextMottoMenuItem = new CustomMenuItem(I18n.getText("nextMotto"));
  
      // loading 面板
      private LoadingPanel loading = new LoadingPanel(THIS);
  
      // 托盘
      private CustomPopupMenu trayPopupMenu = new CustomPopupMenu(THIS);
      private CustomMenuItem openMainFrameMenuItem = new CustomMenuItem(I18n.getText("openMainFrame"));
      private CustomMenuItem exitMenuItem = new CustomMenuItem(I18n.getText("exit") + TITLE);
  
      // 频谱/歌词/背景图切换/滚动条流畅动画 Timer
      private Timer spectrumTimer;
      private Timer lrcTimer;
      private Timer lrcDelayScrollTimer;
      public boolean lrcScrollAnimation;
      public Timer swActionTimer;
      private boolean lrcScrollWaiting;
      private Timer globalPanelTimer;
      private Timer searchSuggestionTimer;
      private boolean searchSuggestionProcessing;
  
      private int row;
      private int currScrollVal;
  
      // 执行多个动画的线程池，是使用多个 Timer 造成卡顿的替代方案！
      private ExecutorService spectrumExecutor = Executors.newSingleThreadExecutor();
      private ExecutorService blurExecutor = Executors.newSingleThreadExecutor();
      private ExecutorService playExecutor = Executors.newSingleThreadExecutor();
      private ExecutorService lrcExecutor = Executors.newSingleThreadExecutor();
      private ExecutorService globalPanelExecutor = Executors.newSingleThreadExecutor();
  
      // 其他需要多线程的操作提交给该线程池
      private ExecutorService globalExecutor = Executors.newFixedThreadPool(5);
  
      // 桌面歌词对话框
      public DesktopLyricDialog desktopLyricDialog = new DesktopLyricDialog(THIS);
      // MV 播放对话框
      private VideoDialog videoDialog;
      // 迷你窗口
      public MiniDialog miniDialog;
      // 迷你窗口坐标
      public int miniX;
      public int miniY;
      // 速率对话框
      private RateDialog rateDialog;
      // 图片预览对话框
      private ImageViewDialog imageViewDialog;
      // 是否仅显示自定义主题
      public boolean customOnly;
  
      // 当前弹出的菜单
      public JPopupMenu currPopup;
      // 当前弹出的对话框
      public List<AbstractShadowDialog> currDialogs = new LinkedList<>();
  
      // 当前窗口坐标
      public int x;
      public int y;
  
      // 更新格言
      private void updateMotto() {
          globalExecutor.execute(() -> {
              try {
                  motto = MusicServerUtil.getMotto();
                  if (player.loadedMusicResource()) return;
                  titleLabel.setVisible(false);
                  titleLabel.setText(StringUtil.textToHtml(TITLE + "\n" + motto));
                  titleLabel.setVisible(true);
              } catch (Exception e) {
                  titleLabel.setText(TITLE);
              }
          });
      }
  
      // 初始化 JavaFX
      private void initJavaFX() {
          Platform.setImplicitExit(false);
          JFXPanel jfxPanel = new JFXPanel();
          jfxPanel.setVisible(false);
          add(jfxPanel);
      }
  
      private void initUI() {
          // 主界面
          setTitle(TITLE);
          globalPanel.setLayout(new BorderLayout());
          setIconImage(titleIcon.getImage());
          setUndecorated(true);
          // 窗口关闭事件
          setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
          addWindowListener(new WindowAdapter() {
              @Override
              public void windowClosing(WindowEvent e) {
                  closeButton.doClick();
              }
          });
          // 窗口焦点事件：调整桌面歌词的显示
          addWindowFocusListener(new WindowAdapter() {
              @Override
              public void windowGainedFocus(WindowEvent e) {
                  // 清除由于失去焦点未出队的按键，避免快捷键失效
                  currKeys.clear();
                  if (windowState == WindowState.MAXIMIZED) desktopLyricDialog.setVisible(false);
                  if (nextLrc != NextLrc.BAD_FORMAT) lrcScrollAnimation = true;
              }
  
              @Override
              public void windowLostFocus(WindowEvent e) {
                  if (windowState == WindowState.MAXIMIZED && (rateDialog == null || !rateDialog.isShowing()))
                      desktopLyricDialog.setVisible(showDesktopLyric);
              }
          });
          // 窗口圆角
          setDefaultLookAndFeelDecorated(true);
          // 窗口透明
          // 存在性能问题，别用
  //        setBackground(Colors.TRANSLUCENT);
          // 窗口大小适应
          addComponentListener(new ComponentAdapter() {
              @Override
              public void componentResized(ComponentEvent e) {
                  if (x == 0x3f3f3f3f && y == 0x3f3f3f3f) setLocationRelativeTo(null);
                  else if (windowState != WindowState.MAXIMIZED) setLocation(x, y);
                  globalExecutor.execute(() -> {
                      int w = getWidth(), h = getHeight();
                      // 窗口圆角
                      SwingUtilities.invokeLater(() -> setShape(windowState == WindowState.MAXIMIZED ? new Rectangle2D.Double(0, 0, w, h)
                              : new RoundRectangle2D.Double(0, 0, w, h, 10, 10)));
                      // 歌词面板
                      Dimension d = new Dimension((int) (w * 0.6), h);
                      Dimension d2 = new Dimension((int) (w * 0.6), SpectrumConstants.barMaxHeight);
                      lrcScrollPane.setPreferredSize(d);
                      spectrumPanel.setPreferredSize(d2);
                      // 时间条
                      currTimeLabel.setVisible(false);
                      currTimeLabel.setVisible(true);
                      timeBar.setPreferredSize(new Dimension(w - currTimeLabel.getPreferredSize().width - durationLabel.getPreferredSize().width - 30 * 2, 20));
                      // 专辑图片
                      albumImageWidth = (int) (w * 0.33);
                      Dimension ad = new Dimension((int) (w * 0.4), Integer.MAX_VALUE);
                      albumImageLabel.setMaximumSize(ad);
                      // 避免标签字太多超宽！！！
                      Dimension ld = new Dimension((int) (w * 0.3), (int) (h * 0.077));
                      songNameLabel.setPreferredSize(ld);
                      artistLabel.setPreferredSize(ld);
                      albumLabel.setPreferredSize(ld);
                      songNameLabel.setVisible(false);
                      songNameLabel.setVisible(true);
                      if (player.loadedMusicResource()) {
                          showAlbumImage();
                      }
                      if (blurType == BlurConstants.OFF || !player.loadedMusicResource()) doBlur();
                  });
              }
          });
          // 解决 setUndecorated(true) 后窗口不能拖动的问题
          Point origin = new Point();
          topBox.addMouseListener(new MouseAdapter() {
              @Override
              public void mousePressed(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || windowState == WindowState.MAXIMIZED) return;
                  origin.x = e.getX();
                  origin.y = e.getY();
              }
          });
          topBox.addMouseMotionListener(new MouseMotionAdapter() {
              @Override
              public void mouseDragged(MouseEvent e) {
                  // mouseDragged 不能正确返回 button 值，需要借助此方法
                  if (!SwingUtilities.isLeftMouseButton(e) || windowState == WindowState.MAXIMIZED) return;
                  Point p = getLocation();
                  setLocation(x = p.x + e.getX() - origin.x, y = p.y + e.getY() - origin.y);
              }
          });
          titleLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mousePressed(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || windowState == WindowState.MAXIMIZED) return;
                  origin.x = e.getX();
                  origin.y = e.getY();
              }
          });
          titleLabel.addMouseMotionListener(new MouseMotionAdapter() {
              @Override
              public void mouseDragged(MouseEvent e) {
                  // mouseDragged 不能正确返回 button 值，需要借助此方法
                  if (!SwingUtilities.isLeftMouseButton(e) || windowState == WindowState.MAXIMIZED) return;
                  Point p = getLocation();
                  setLocation(x = p.x + e.getX() - origin.x, y = p.y + e.getY() - origin.y);
              }
          });
  
          // 主面板
          setContentPane(globalPanel);
  
          // loading 面板
          setGlassPane(loading);
  
          // 初始化 JavaFX 组件
          initJavaFX();
  
          // 初始化标题栏
          initTitleBar();
  
          // 初始化主菜单
          initMainMenu();
  
          // 初始化标签页
          initTabbedPane();
  
          // 初始化收藏标签页
          initCollectionTabbedPane();
  
          // 初始化收藏工具条
          initCollectionToolBar();
  
          // 初始化收藏列表
          initCollectionList();
  
          // 初始化标签
          initLabel();
  
          // 初始化个人音乐工具条
          initPersonalMusicToolBar();
  
          // 初始化本地音乐工具条
          initMusicToolBar();
  
          // 初始化个人音乐列表
          initMusicList();
  
          // 初始化在线音乐工具条
          initNetMusicToolBar();
  
          // 初始化在线音乐列表
          initNetMusicList();
  
          // 初始化描述盒子
          initDescriptionPanel();
  
          // 初始化歌单工具条
          initNetPlaylistToolBar();
  
          // 初始化歌单列表
          initNetPlaylistList();
  
          // 初始化专辑工具条
          initNetAlbumToolBar();
  
          // 初始化专辑列表
          initNetAlbumList();
  
          // 初始化歌手工具条
          initNetArtistToolBar();
  
          // 初始化歌手列表
          initNetArtistList();
  
          // 初始化电台工具条
          initNetRadioToolBar();
  
          // 初始化电台列表
          initNetRadioList();
  
          // 初始化 MV 工具条
          initNetMvToolBar();
  
          // 初始化 MV 列表
          initNetMvList();
  
          // 初始化榜单工具条
          initNetRankingToolBar();
  
          // 初始化榜单列表
          initNetRankingList();
  
          // 初始化用户工具条
          initNetUserToolBar();
  
          // 初始化用户列表
          initNetUserList();
  
          // 初始化评论工具条
          initNetCommentToolBar();
  
          // 初始化评论列表
          initNetCommentList();
  
          // 初始化乐谱工具条
          initNetSheetToolBar();
  
          // 初始化乐谱列表
          initNetSheetList();
  
          // 初始化推荐工具条
          initRecommendToolBar();
  
          // 初始化推荐列表
          initItemRecommendList();
  
          // 初始化下载工具条
          initDownloadToolBar();
  
          // 初始化下载列表
          initDownloadList();
  
          // 初始化播放队列工具条
          initPlayQueueToolBar();
  
          // 初始化播放队列列表
          initPlayQueue();
  
          // 初始化歌词列表
          initLrcList();
  
          // 初始化动画 Timer
          initTimer();
  
          // 初始化进度条
          initTimeBar();
  
          // 初始化控制面板
          initControlPanel();
  
          // 加载全局快捷键监听器
          loadHotKeyListener();
  
          // 初始化托盘
          initTray();
  
          // 格言
          updateMotto();
  
          // 加载配置
          boolean succeed = loadConfig();
  
          setVisible(true);
  
          // 显示托盘
          try {
              SystemTray.getSystemTray().add(trayIconImg);
          } catch (AWTException e) {
              LogUtil.error(e);
          }
  
          // 加载上一次播放的歌曲
          if (currSong > -1) {
              playQueue.setSelectedIndex(currSong);
              playExecutor.execute(() -> playSelected(playQueue, false, false));
          }
  
          // 首次使用显示指南
          if (!succeed) helpMenuItem.doClick();
      }
  
      // 初始化标题栏
      private void initTitleBar() {
          titleLabel.setText(TITLE);
          titleLabel.setIcon(titleIcon);
          titleLabel.setIconTextGap(15);
          titleLabel.setHorizontalTextPosition(SwingConstants.RIGHT);
          titleLabel.setHorizontalAlignment(SwingConstants.LEFT);
          titleLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON3 || !player.isEmpty()) return;
                  mottoPopupMenu.show(titleLabel, e.getX(), e.getY());
              }
          });
  
          // 复制格言
          copyMottoMenuItem.addActionListener(e -> copyToClipboard(motto));
          // 下一条格言
          nextMottoMenuItem.addActionListener(e -> updateMotto());
  
          mottoPopupMenu.add(copyMottoMenuItem);
          mottoPopupMenu.add(nextMottoMenuItem);
  
          // 隐藏歌曲详情
          hideDetailButton.addActionListener(e -> changePaneButton.doClick());
          // 换肤按钮
          styleToolButton.setComponentPopupMenu(stylePopupMenu);
          styleToolButton.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() == MouseEvent.BUTTON1) {
                      stylePopupMenu.show(styleToolButton, e.getX(), e.getY());
                  }
              }
          });
          // 主菜单弹出按钮
          mainMenuButton.setComponentPopupMenu(mainMenu);
          mainMenuButton.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  mainMenu.show(mainMenuButton, e.getX(), e.getY());
              }
          });
          // 迷你模式
          miniButton.addActionListener(e -> {
              // 关闭频谱
              closeSpectrum();
              dispose();
              miniButton.setDrawBg(false);
              miniDialog = new MiniDialog(THIS);
              miniDialog.showDialog();
              miniButton.setIcon(ImageUtil.dye(miniIcon, currUIStyle.getIconColor()));
          });
          // 最小化
          minimizeButton.addActionListener(e -> {
              setExtendedState(ICONIFIED);
  //            windowState = WindowState.MINIMIZED;
          });
          // 最大化
          maximizeButton.addActionListener(e -> {
              if (windowState == NORMAL) {
                  windowState = WindowState.MAXIMIZED;
                  maximizeButton.setIcon(ImageUtil.dye(restoreIcon, currUIStyle.getIconColor()));
                  // 取消桌面歌词显示
                  desktopLyricDialog.setVisible(false);
                  // 不覆盖任务栏
                  Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
                  Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration());
                  setBounds(0, 0, screenSize.width, screenSize.height - insets.bottom);
              } else {
                  windowState = WindowState.NORMAL;
                  maximizeButton.setIcon(ImageUtil.dye(maximizeIcon, currUIStyle.getIconColor()));
                  // 如果开启桌面歌词，恢复桌面歌词显示
                  desktopLyricDialog.setVisible(showDesktopLyric);
                  // 恢复窗口大小
                  setSize(windowWidth, windowHeight);
              }
          });
          // 关闭窗口
          closeButton.addActionListener(e -> {
              // 询问隐藏到托盘还是退出程序
              if (currCloseWindowOption == CloseWindowOptions.ASK) {
                  ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_DISPOSE_MSG, EXIT_OPTIONS[0], EXIT_OPTIONS[1], EXIT_OPTIONS[2], true, REMEMBER_CHOICE_MSG);
                  confirmDialog.showDialog();
                  int response = confirmDialog.getResponse();
                  boolean checked = confirmDialog.isChecked();
                  if (response == JOptionPane.YES_OPTION) {
                      if (checked) currCloseWindowOption = CloseWindowOptions.DISPOSE;
                      hideWindow();
                  } else if (response == JOptionPane.NO_OPTION) {
                      if (checked) currCloseWindowOption = CloseWindowOptions.EXIT;
                      exit();
                  }
              }
              // 隐藏到托盘
              else if (currCloseWindowOption == CloseWindowOptions.DISPOSE) hideWindow();
                  // 退出程序
              else if (currCloseWindowOption == CloseWindowOptions.EXIT) exit();
          });
          // 隐藏详情暂时不可见
          hideDetailButton.setVisible(false);
          // 最佳大小
          Dimension d = new Dimension(hideDetailIcon.getIconWidth() + 10, hideDetailIcon.getIconHeight() + 10);
          hideDetailButton.setPreferredSize(d);
          styleToolButton.setPreferredSize(d);
          mainMenuButton.setPreferredSize(d);
          splitLabel.setPreferredSize(d);
          miniButton.setPreferredSize(d);
          minimizeButton.setPreferredSize(d);
          maximizeButton.setPreferredSize(d);
          closeButton.setPreferredSize(d);
          // 悬浮提示
          hideDetailButton.setToolTipText(HIDE_DETAIL_TIP);
          styleToolButton.setToolTipText(STYLE_TIP);
          mainMenuButton.setToolTipText(MENU_TIP);
          miniButton.setToolTipText(MINI_WINDOW_TIP);
          minimizeButton.setToolTipText(MINIMIZE_WINDOW_TIP);
          maximizeButton.setToolTipText(MAXIMIZE_WINDOW_TIP);
          closeButton.setToolTipText(CLOSE_WINDOW_TIP);
          // 鼠标事件
          hideDetailButton.addMouseListener(new ButtonMouseListener(hideDetailButton, THIS));
          styleToolButton.addMouseListener(new ButtonMouseListener(styleToolButton, THIS));
          mainMenuButton.addMouseListener(new ButtonMouseListener(mainMenuButton, THIS));
          miniButton.addMouseListener(new ButtonMouseListener(miniButton, THIS));
          minimizeButton.addMouseListener(new ButtonMouseListener(minimizeButton, THIS));
          maximizeButton.addMouseListener(new ButtonMouseListener(maximizeButton, THIS));
          closeButton.addMouseListener(new ButtonMouseListener(closeButton, THIS));
  
          FlowLayout fl = new FlowLayout(FlowLayout.RIGHT);
          fl.setHgap(14);
          windowCtrlPanel.setLayout(fl);
          windowCtrlPanel.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
          windowCtrlPanel.setMinimumSize(new Dimension(360, 10));
          windowCtrlPanel.add(hideDetailButton);
          windowCtrlPanel.add(styleToolButton);
          windowCtrlPanel.add(mainMenuButton);
          windowCtrlPanel.add(splitLabel);
          windowCtrlPanel.add(miniButton);
          windowCtrlPanel.add(minimizeButton);
          windowCtrlPanel.add(maximizeButton);
          windowCtrlPanel.add(closeButton);
          topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));
          topPanel.add(titleLabel);
          topPanel.add(Box.createHorizontalGlue());
          topPanel.add(windowCtrlPanel);
          topBox.add(topPanel);
          topBox.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 10));
          globalPanel.add(topBox, BorderLayout.NORTH);
      }
  
      public boolean keyEnabled;
      public List<Integer> playOrPauseKeys;
      public List<Integer> playLastKeys;
      public List<Integer> playNextKeys;
      public List<Integer> backwardKeys;
      public List<Integer> forwardKeys;
      public List<Integer> videoFullScreenKeys;
      public final LinkedList<Integer> currKeys = new LinkedList<>();
  
      // 加载全局快捷键监听器，AWTEventListener
      private void loadHotKeyListener() {
          Toolkit toolkit = Toolkit.getDefaultToolkit();
          toolkit.addAWTEventListener(event -> {
              if (!(event instanceof KeyEvent)) return;
              KeyEvent keyEvent = (KeyEvent) event;
              boolean released = keyEvent.getID() == KeyEvent.KEY_RELEASED, pressed = keyEvent.getID() == KeyEvent.KEY_PRESSED;
              if (!released && !pressed) return;
  
              int code = keyEvent.getKeyCode();
  
              // 图片浏览窗口的监听事件
              if (imageViewDialog != null && released && !imageViewDialog.pageTextField.hasFocus()) {
                  if (code == KeyEvent.VK_LEFT) imageViewDialog.lastImgButton.doClick();
                  else if (code == KeyEvent.VK_RIGHT) imageViewDialog.nextImgButton.doClick();
              }
              // 视频界面快捷键
              else if (videoDialog != null && keyEnabled) {
                  // 记录所有按键
                  if (released && !currKeys.isEmpty()) currKeys.removeLastOccurrence(code);
                  else if (pressed && !currKeys.contains(code)) currKeys.add(code);
                  else return;
                  if (ListUtil.equals(playOrPauseKeys, currKeys)) videoDialog.playOrPause();
                  else if (ListUtil.equals(backwardKeys, currKeys)) videoDialog.backwardButton.doClick();
                  else if (ListUtil.equals(forwardKeys, currKeys)) videoDialog.forwardButton.doClick();
                  else if (ListUtil.equals(videoFullScreenKeys, currKeys)) videoDialog.switchWindow();
              } else {
                  // 搜索框输入时不受影响
                  if (!collectionPageTextField.hasFocus()
                          && !searchTextField.hasFocus() && !netMusicPageTextField.hasFocus()
                          && !netPlaylistSearchTextField.hasFocus() && !netPlaylistPageTextField.hasFocus()
                          && !netAlbumSearchTextField.hasFocus() && !netAlbumPageTextField.hasFocus()
                          && !netArtistSearchTextField.hasFocus() && !netArtistPageTextField.hasFocus()
                          && !netRadioSearchTextField.hasFocus() && !netRadioPageTextField.hasFocus()
                          && !netMvSearchTextField.hasFocus() && !netMvPageTextField.hasFocus()
                          && !netRankingPageTextField.hasFocus()
                          && !netUserSearchTextField.hasFocus() && !netUserPageTextField.hasFocus()
                          && !netCommentPageTextField.hasFocus()
                          && !netSheetPageTextField.hasFocus()
                          && !netRecommendPageTextField.hasFocus()
                          && !filterTextField.hasFocus() && currDialogs.isEmpty()) {
                      // 播放或暂停、上下一首
                      if (keyEnabled) {
                          // 记录所有按键
                          if (released && !currKeys.isEmpty()) currKeys.removeLastOccurrence(code);
                          else if (pressed && !currKeys.contains(code)) currKeys.add(code);
                          else return;
                          if (videoDialog != null) return;
                          if (ListUtil.equals(playOrPauseKeys, currKeys)) playOrPause();
                          else if (ListUtil.equals(playLastKeys, currKeys)) playLast();
                          else if (ListUtil.equals(playNextKeys, currKeys)) playNext();
                          else if (ListUtil.equals(backwardKeys, currKeys)) backwardButton.doClick();
                          else if (ListUtil.equals(forwardKeys, currKeys)) forwardButton.doClick();
                      }
                  } else if (currDialogs.isEmpty() && code == KeyEvent.VK_ENTER && !loading.isShowing() && released) {
                      // 回车跳页
                      if (collectionPageTextField.hasFocus()) collectionGoButton.doClick();
                      else if (netMusicPageTextField.hasFocus()) netMusicGoButton.doClick();
                      else if (netPlaylistPageTextField.hasFocus()) netPlaylistGoButton.doClick();
                      else if (netAlbumPageTextField.hasFocus()) netAlbumGoButton.doClick();
                      else if (netArtistPageTextField.hasFocus()) netArtistGoButton.doClick();
                      else if (netRadioPageTextField.hasFocus()) netRadioGoButton.doClick();
                      else if (netMvPageTextField.hasFocus()) netMvGoButton.doClick();
                      else if (netRankingPageTextField.hasFocus()) netRankingGoButton.doClick();
                      else if (netUserPageTextField.hasFocus()) netUserGoButton.doClick();
                      else if (netCommentPageTextField.hasFocus()) netCommentGoButton.doClick();
                      else if (netSheetPageTextField.hasFocus()) netSheetGoButton.doClick();
                      else if (netRecommendPageTextField.hasFocus()) netRecommendGoButton.doClick();
                      else {
                          // 回车搜索
                          int selectedIndex = tabbedPane.getSelectedIndex();
                          if (selectedIndex == TabIndex.NET_MUSIC) searchButton.doClick();
                          else if (selectedIndex == TabIndex.NET_PLAYLIST) netPlaylistSearchButton.doClick();
                          else if (selectedIndex == TabIndex.NET_ALBUM) netAlbumSearchButton.doClick();
                          else if (selectedIndex == TabIndex.NET_ARTIST) netArtistSearchButton.doClick();
                          else if (selectedIndex == TabIndex.NET_RADIO) netRadioSearchButton.doClick();
                          else if (selectedIndex == TabIndex.NET_MV) netMvSearchButton.doClick();
                          else if (selectedIndex == TabIndex.NET_USER) netUserSearchButton.doClick();
                      }
                  }
              }
          }, AWTEvent.KEY_EVENT_MASK);
      }
  
      // 加载配置
      private boolean loadConfig() {
          JSONObject config = ConfigConstants.CONFIG_DATA;
          // 载入是否自动更新
          autoUpdate = config.getBooleanValue(ConfigConstants.AUTO_UPDATE, true);
          if (autoUpdate) checkUpdate(true);
          // 载入已保存的自定义主题(逆序加载，这样才能保持顺序一致)
          JSONArray styleArray = config.getJSONArray(ConfigConstants.CUSTOM_UI_STYLES);
          if (JsonUtil.notEmpty(styleArray)) {
              for (int i = 0, len = styleArray.size(); i < len; i++) {
                  JSONObject styleObject = styleArray.getJSONObject(i);
                  UIStyle style = new UIStyle(
                          UIStyleConstants.CUSTOM,
                          styleObject.getString("name"),
                          styleObject.getString("imgKey"),
                          ColorUtil.hexToColor(styleObject.getString("bgColor")),
                          ColorUtil.hexToColor(styleObject.getString("foreColor")),
                          ColorUtil.hexToColor(styleObject.getString("selectedColor")),
                          ColorUtil.hexToColor(styleObject.getString("lrcColor")),
                          ColorUtil.hexToColor(styleObject.getString("highlightColor")),
                          ColorUtil.hexToColor(styleObject.getString("textColor")),
                          ColorUtil.hexToColor(styleObject.getString("timeBarColor")),
                          ColorUtil.hexToColor(styleObject.getString("iconColor")),
                          ColorUtil.hexToColor(styleObject.getString("scrollBarColor")),
                          ColorUtil.hexToColor(styleObject.getString("sliderColor")),
                          ColorUtil.hexToColor(styleObject.getString("spectrumColor"))
                  );
                  styles.add(style);
              }
          }
          // 载入是否启用快捷键
          keyEnabled = config.getBooleanValue(ConfigConstants.KEY_ENABLED, true);
          playOrPauseKeys = KeyUtil.jsonArrayToKeys(config.getJSONArray(ConfigConstants.PLAY_OR_PAUSE_KEYS));
          if (playOrPauseKeys.isEmpty()) {
              playOrPauseKeys.add(KeyEvent.VK_CONTROL);
              playOrPauseKeys.add(KeyEvent.VK_F5);
          }
          playLastKeys = KeyUtil.jsonArrayToKeys(config.getJSONArray(ConfigConstants.PLAY_LAST_KEYS));
          if (playLastKeys.isEmpty()) {
              playLastKeys.add(KeyEvent.VK_CONTROL);
              playLastKeys.add(KeyEvent.VK_LEFT);
          }
          playNextKeys = KeyUtil.jsonArrayToKeys(config.getJSONArray(ConfigConstants.PLAY_NEXT_KEYS));
          if (playNextKeys.isEmpty()) {
              playNextKeys.add(KeyEvent.VK_CONTROL);
              playNextKeys.add(KeyEvent.VK_RIGHT);
          }
          backwardKeys = KeyUtil.jsonArrayToKeys(config.getJSONArray(ConfigConstants.BACKWARD_KEYS));
          if (backwardKeys.isEmpty()) {
              backwardKeys.add(KeyEvent.VK_CONTROL);
              backwardKeys.add(KeyEvent.VK_ALT);
              backwardKeys.add(KeyEvent.VK_LEFT);
          }
          forwardKeys = KeyUtil.jsonArrayToKeys(config.getJSONArray(ConfigConstants.FORWARD_KEYS));
          if (forwardKeys.isEmpty()) {
              forwardKeys.add(KeyEvent.VK_CONTROL);
              forwardKeys.add(KeyEvent.VK_ALT);
              forwardKeys.add(KeyEvent.VK_RIGHT);
          }
          videoFullScreenKeys = KeyUtil.jsonArrayToKeys(config.getJSONArray(ConfigConstants.VIDEO_FULL_SCREEN_KEYS));
          if (videoFullScreenKeys.isEmpty()) {
              videoFullScreenKeys.add(KeyEvent.VK_F11);
          }
          // 载入关闭窗口操作
          currCloseWindowOption = config.getIntValue(ConfigConstants.CLOSE_WINDOW_OPTION, CloseWindowOptions.ASK);
          // 载入窗口大小
          windowSize = config.getIntValue(ConfigConstants.WINDOW_SIZE, WindowSize.MIDDLE);
          windowWidth = WindowSize.DIMENSIONS[windowSize][0];
          windowHeight = WindowSize.DIMENSIONS[windowSize][1];
          x = y = 0x3f3f3f3f;
          setSize(windowWidth, windowHeight);
          // 载入是否显示已播放时间
          timeElapsedMode = config.getBooleanValue(ConfigConstants.TIME_ELAPSED_MODE, true);
          videoTimeElapsedMode = config.getBooleanValue(ConfigConstants.VIDEO_TIME_ELAPSED_MODE, true);
          // 载入播放视频是否关闭主界面
          videoOnly = config.getBooleanValue(ConfigConstants.VIDEO_ONLY, true);
          // 是否显示侧边栏文字
          showTabText = config.getBooleanValue(ConfigConstants.SHOW_TAB_TEXT, true);
          updateTabSize();
          // 载入歌词对齐方式
          LyricAlignment.lrcAlignmentIndex = config.getIntValue(ConfigConstants.LRC_ALIGNMENT, LyricAlignment.lrcAlignmentIndex);
          // 载入频谱最大高度
          SpectrumConstants.barMaxHeight = config.getIntValue(ConfigConstants.SPEC_MAX_HEIGHT, SpectrumConstants.barMaxHeight);
          spectrumPanel.setMinimumSize(new Dimension(1, SpectrumConstants.barMaxHeight));
          // 载入高斯模糊因子
          BlurConstants.gsFactorIndex = config.getIntValue(ConfigConstants.GS_FACTOR_INDEX, BlurConstants.gsFactorIndex);
          // 载入暗角滤镜因子
          BlurConstants.darkerFactorIndex = config.getIntValue(ConfigConstants.DARKER_FACTOR_INDEX, BlurConstants.darkerFactorIndex);
  //        // 载入线性渐变色彩风格
  //        BlurConstants.gradientColorStyleIndex = config.getIntValue(ConfigConstants.GRADIENT_COLOR_STYLE_INDEX, BlurConstants.gradientColorStyleIndex);
          // 载入歌曲下载路径
          String musicDownPath = config.getString(ConfigConstants.MUSIC_DOWN_PATH);
          if (StringUtil.notEmpty(musicDownPath)) SimplePath.DOWNLOAD_MUSIC_PATH = musicDownPath;
          FileUtil.mkDir(SimplePath.DOWNLOAD_MUSIC_PATH);
          // 载入 MV 下载路径
          String mvDownPath = config.getString(ConfigConstants.MV_DOWN_PATH);
          if (StringUtil.notEmpty(mvDownPath)) SimplePath.DOWNLOAD_MV_PATH = mvDownPath;
          FileUtil.mkDir(SimplePath.DOWNLOAD_MV_PATH);
          // 载入缓存路径
          String cachePath = config.getString(ConfigConstants.CACHE_PATH);
          if (StringUtil.notEmpty(cachePath)) {
              SimplePath.CACHE_PATH = cachePath;
              SimplePath.IMG_CACHE_PATH = cachePath + (cachePath.endsWith(File.separator) ? "" : File.separator) + "img" + File.separator;
          }
          FileUtil.mkDir(SimplePath.CACHE_PATH);
          FileUtil.mkDir(SimplePath.IMG_CACHE_PATH);
          // 载入最大缓存大小
          maxCacheSize = config.getLongValue(ConfigConstants.MAX_CACHE_SIZE, 1024);
          // 载入最大播放历史数量
          maxHistoryCount = config.getIntValue(ConfigConstants.MAX_HISTORY_COUNT, 300);
          // 载入最大搜索历史数量
          maxSearchHistoryCount = config.getIntValue(ConfigConstants.MAX_SEARCH_HISTORY_COUNT, 50);
          // 载入同时下载的最大任务数
          int maxConcurrentTaskCount = config.getIntValue(ConfigConstants.MAX_CONCURRENT_TASK_COUNT, 3);
          GlobalExecutors.downloadExecutor = Executors.newFixedThreadPool(maxConcurrentTaskCount);
          // 载入是否显示频谱
          showSpectrum = config.getBooleanValue(ConfigConstants.SHOW_SPECTRUM, true);
          switchSpectrumButton.setIcon(ImageUtil.dye(showSpectrum ? spectrumOnIcon : spectrumOffIcon, currUIStyle.getIconColor()));
          // 载入是否高斯模糊
          gsOn = config.getBooleanValue(ConfigConstants.GS_ON, true);
          // 载入是否暗化
          darkerOn = config.getBooleanValue(ConfigConstants.DARKER_ON, true);
          // 载入是否朦胧遮罩
          maskOn = config.getBooleanValue(ConfigConstants.MASK_ON, false);
          // 载入是否律动
          grooveOn = config.getBooleanValue(ConfigConstants.GROOVE_ON, false);
          globalPanel.setGrooveOn(grooveOn);
          // 载入模糊类型
          blurType = config.getIntValue(ConfigConstants.BLUR_TYPE, BlurConstants.CV);
          blurButton.setIcon(ImageUtil.dye(blurType == BlurConstants.CV ? cvBlurIcon
                  : blurType == BlurConstants.MC ? mcBlurIcon
                  : blurType == BlurConstants.LG ? lgBlurIcon
                  : blurType == BlurConstants.FBM ? fbmBlurIcon
                  : blurOffIcon, currUIStyle.getIconColor()));
          // 载入是否自动下载歌词
          isAutoDownloadLrc = config.getBooleanValue(ConfigConstants.AUTO_DOWNLOAD_LYRIC, true);
  //        // 载入是否添加逐字时间轴
  //        LyricType.verbatimTimeline = config.getBooleanValue(ConfigConstants.VERBATIM_TIMELINE, LyricType.verbatimTimeline);
          // 载入歌词偏移
          lrcOffset = config.getDoubleValue(ConfigConstants.LYRIC_OFFSET);
          currLrcOffsetMenuItem.setText(String.format(LRC_OFFSET_MSG, lrcOffset).replace(".0", ""));
          // 载入频谱透明度
          specOpacity = config.containsKey(ConfigConstants.SPEC_OPACITY) ? config.getFloatValue(ConfigConstants.SPEC_OPACITY) : 0.3f;
          spectrumOpacityMenuItem.setText(String.format(SPEC_OPACITY_MSG, (int) (specOpacity * 100)));
          // 载入是否锁定桌面歌词
          desktopLyricLocked = config.getBooleanValue(ConfigConstants.LOCK_DESKTOP_LYRIC, false);
          // 载入桌面歌词坐标
          desktopLyricX = config.getIntValue(ConfigConstants.DESKTOP_LYRIC_X, -1);
          desktopLyricY = config.getIntValue(ConfigConstants.DESKTOP_LYRIC_Y, -1);
          if (desktopLyricX >= 0) desktopLyricDialog.setLocation(desktopLyricX, desktopLyricY);
          // 载入是否桌面歌词置顶
          desktopLyricOnTop = config.getBooleanValue(ConfigConstants.DESKTOP_LYRIC_ON_TOP, true);
          desktopLyricDialog.setAlwaysOnTop(desktopLyricOnTop);
          // 载入桌面歌词透明度
          desktopLyricAlpha = config.containsKey(ConfigConstants.DESKTOP_LYRIC_ALPHA) ? config.getFloatValue(ConfigConstants.DESKTOP_LYRIC_ALPHA) : 1;
          desktopLyricDialog.setAlpha(desktopLyricAlpha);
          // 载入桌面歌词字体大小
          desktopLyricFontSize = config.getIntValue(ConfigConstants.DESKTOP_LYRIC_FONT_SIZE, Fonts.HUGE_SIZE);
          desktopLyricDialog.updateFontSize(desktopLyricFontSize);
          // 初始化桌面歌词
          desktopLyricDialog.updateLyric(NO_LRC_STMT, 0);
          // 载入是否显示桌面歌词
          showDesktopLyric = config.getBooleanValue(ConfigConstants.SHOW_DESKTOP_LYRIC, true);
          if (showDesktopLyric) desktopLyricDialog.setVisible(true);
          else desktopLyricButton.setIcon(ImageUtil.dye(desktopLyricOffIcon, currUIStyle.getIconColor()));
          // 载入播放模式
          switch (config.getIntValue(ConfigConstants.PLAY_MODE, PlayMode.LIST_CYCLE)) {
              case PlayMode.DISABLED:
                  changeToDisabled(false);
                  break;
              case PlayMode.SEQUENCE:
                  changeToSequence(false);
                  break;
              case PlayMode.LIST_CYCLE:
                  changeToListCycle(false);
                  break;
              case PlayMode.SINGLE:
                  changeToSingle(false);
                  break;
              case PlayMode.SHUFFLE:
                  changeToShuffle(false);
                  break;
          }
          // 载入音质
          AudioQuality.quality = config.getIntValue(ConfigConstants.AUDIO_QUALITY, AudioQuality.quality);
          // 载入画质
          VideoQuality.quality = config.getIntValue(ConfigConstants.VIDEO_QUALITY, VideoQuality.quality);
          // 载入快进/快退时间
          forwardOrBackwardTime = config.getIntValue(ConfigConstants.FOB_TIME, DEFAULT_FORWARD_OR_BACKWARD_TIME);
          // 载入视频快进/快退时间
          videoForwardOrBackwardTime = config.getIntValue(ConfigConstants.VIDEO_FOB_TIME, DEFAULT_FORWARD_OR_BACKWARD_TIME);
          // 载入速率
          currRate = config.containsKey(ConfigConstants.RATE) ? config.getDoubleValue(ConfigConstants.RATE) : DEFAULT_RATE;
          // 载入视频速率
          currVideoRate = config.containsKey(ConfigConstants.VIDEO_RATE) ? config.getDoubleValue(ConfigConstants.VIDEO_RATE) : DEFAULT_RATE;
          // 载入频谱样式
          currSpecStyle = config.getIntValue(ConfigConstants.SPECTRUM_STYLE, SpectrumConstants.GROUND);
          updateMenuItemStatus(spectrumStyleButtonGroup, spectrumStyleButtonGroup.get(currSpecStyle));
          // 载入均衡
          currBalance = config.containsKey(ConfigConstants.BALANCE) ? config.getDoubleValue(ConfigConstants.BALANCE) : DEFAULT_BALANCE;
          // 载入音量
          volumeSlider.setValue(config.getIntValue(ConfigConstants.VOLUME, DEFAULT_VOLUME));
          // 载入是否静音
          isMute = config.getBooleanValue(ConfigConstants.MUTE, false);
          if (isMute) muteButton.setIcon(ImageUtil.dye(muteIcon, currUIStyle.getIconColor()));
          // 载入音效
          currSoundEffect = config.getIntValue(ConfigConstants.SOUND_EFFECT, 0);
          // 载入均衡数据
          JSONArray edArray = config.getJSONArray(ConfigConstants.EQUALIZER_DATA);
          ed = new double[EqualizerData.BAND_NUM];
          if (JsonUtil.notEmpty(edArray)) {
              for (int i = 0, s = edArray.size(); i < s; i++) ed[i] = edArray.getDoubleValue(i);
          }
          // 载入迷你窗口位置
          miniX = config.getIntValue(ConfigConstants.MINIX, -0x3f3f3f3f);
          miniY = config.getIntValue(ConfigConstants.MINIY, -0x3f3f3f3f);
          // 载入歌词类型
          currLrcType = config.getIntValue(ConfigConstants.LYRIC_TYPE, LyricType.ORIGINAL);
          switch (currLrcType) {
              case LyricType.ORIGINAL:
                  switchLrcTypeButton.setIcon(ImageUtil.dye(originalIcon, currUIStyle.getIconColor()));
                  switchLrcTypeButton.setToolTipText(ORIGINAL_LRC_TIP);
                  break;
              case LyricType.TRANSLATION:
                  switchLrcTypeButton.setIcon(ImageUtil.dye(translationIcon, currUIStyle.getIconColor()));
                  switchLrcTypeButton.setToolTipText(TRANSLATION_TIP);
                  break;
              case LyricType.ROMA:
                  switchLrcTypeButton.setIcon(ImageUtil.dye(romajiIcon, currUIStyle.getIconColor()));
                  switchLrcTypeButton.setToolTipText(ROMA_TIP);
                  break;
              case LyricType.TRADITIONAL_CN:
                  switchLrcTypeButton.setIcon(ImageUtil.dye(tradChineseIcon, currUIStyle.getIconColor()));
                  switchLrcTypeButton.setToolTipText(TRAD_CHINESE_TIP);
                  break;
          }
  
          // 载入排序顺序
          currSortOrder = config.getIntValue(ConfigConstants.SORT_ORDER, SortMethod.ASCENDING);
          updateMenuItemStatus(sortOrderButtonGroup, sortOrderButtonGroup.get(currSortOrder));
  
          // 载入歌曲目录
          JSONArray catalogJsonArray = config.getJSONArray(ConfigConstants.CATALOGS);
          if (JsonUtil.notEmpty(catalogJsonArray)) {
              for (int i = 0, len = catalogJsonArray.size(); i < len; i++) {
                  String filePath = catalogJsonArray.getString(i);
                  catalogs.add(new File(filePath));
              }
          }
  
          // 载入本地音乐列表
          loadLocalMusicList(config);
  
          // 载入历史列表
          JSONArray historyJsonArray = config.getJSONArray(ConfigConstants.HISTORY);
          if (JsonUtil.notEmpty(historyJsonArray)) {
              for (int i = 0, len = historyJsonArray.size(); i < len; i++) {
                  String s = historyJsonArray.getString(i);
                  // 判断是否为文件路径
                  if (!JsonUtil.isValidObject(s)) {
                      AudioFile audioFile = new AudioFile(s);
                      globalExecutor.execute(() -> {
                          MediaUtil.fillAudioFileInfo(audioFile);
                          musicList.repaint();
                      });
                      historyModel.addElement(audioFile);
                  } else {
                      JSONObject jsonObject = JSONObject.parseObject(s);
                      NetMusicInfo musicInfo = new NetMusicInfo();
                      musicInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_MUSIC_SOURCE));
                      musicInfo.setHash(jsonObject.getString(ConfigConstants.NET_MUSIC_HASH));
                      musicInfo.setId(jsonObject.getString(ConfigConstants.NET_MUSIC_ID));
                      musicInfo.setProgramId(jsonObject.getString(ConfigConstants.NET_MUSIC_PROGRAM_ID));
                      musicInfo.setName(jsonObject.getString(ConfigConstants.NET_MUSIC_NAME));
                      musicInfo.setArtist(jsonObject.getString(ConfigConstants.NET_MUSIC_ARTIST));
                      musicInfo.setArtistId(jsonObject.getString(ConfigConstants.NET_MUSIC_ARTIST_ID));
                      musicInfo.setAlbumName(jsonObject.getString(ConfigConstants.NET_MUSIC_ALBUM_NAME));
                      musicInfo.setAlbumId(jsonObject.getString(ConfigConstants.NET_MUSIC_ALBUM_ID));
                      musicInfo.setDuration(jsonObject.getDoubleValue(ConfigConstants.NET_MUSIC_DURATION));
                      musicInfo.setMvId(jsonObject.getString(ConfigConstants.NET_MUSIC_MV_ID));
                      musicInfo.setQualityType(jsonObject.getIntValue(ConfigConstants.NET_MUSIC_QUALITY_TYPE, AudioQuality.UNKNOWN));
                      historyModel.addElement(musicInfo);
                  }
              }
          }
  
          // 载入所有收藏列表
          loadCollectedMusicList(config);
  
          // 载入下载任务列表
          JSONArray tasksJsonArray = config.getJSONArray(ConfigConstants.TASKS);
          if (JsonUtil.notEmpty(tasksJsonArray)) {
              for (int i = 0, len = tasksJsonArray.size(); i < len; i++) {
                  String s = tasksJsonArray.getString(i);
  
                  JSONObject jsonObject = JSONObject.parseObject(s);
                  int type = jsonObject.getIntValue(ConfigConstants.TASK_TYPE);
                  String name = jsonObject.getString(ConfigConstants.TASK_NAME);
                  String dest = jsonObject.getString(ConfigConstants.TASK_DEST);
                  String format = jsonObject.getString(ConfigConstants.TASK_FORMAT);
                  int status = jsonObject.getIntValue(ConfigConstants.TASK_STATUS);
                  long finished = jsonObject.getLongValue(ConfigConstants.TASK_FINISHED);
                  long total = jsonObject.getLongValue(ConfigConstants.TASK_TOTAL);
  
                  Task task = null;
                  if (type == TaskType.MUSIC) {
                      JSONObject jo = jsonObject.getJSONObject(ConfigConstants.TASK_MUSIC_INFO);
                      NetMusicInfo musicInfo = new NetMusicInfo();
                      musicInfo.setSource(jo.getIntValue(ConfigConstants.NET_MUSIC_SOURCE));
                      musicInfo.setId(jo.getString(ConfigConstants.NET_MUSIC_ID));
                      musicInfo.setName(jo.getString(ConfigConstants.NET_MUSIC_NAME));
                      musicInfo.setArtist(jo.getString(ConfigConstants.NET_MUSIC_ARTIST));
                      task = new Task(downloadList, type, musicInfo);
  
                      task.setInvokeLater(() -> {
                          String destLrcPath = SimplePath.DOWNLOAD_MUSIC_PATH + musicInfo.toSimpleLrcFileName();
                          // 写入歌曲信息
                          MediaUtil.writeAudioFileInfo(dest, musicInfo);
                          // 自动下载歌词
                          if (isAutoDownloadLrc && StringUtil.notEmpty(musicInfo.getLrcFileText()))
                              FileUtil.writeStr(musicInfo.getLrcFileText(), destLrcPath);
                      });
                  } else if (type == TaskType.MV) {
                      JSONObject jo = jsonObject.getJSONObject(ConfigConstants.TASK_MV_INFO);
                      NetMvInfo mvInfo = new NetMvInfo();
                      mvInfo.setSource(jo.getIntValue(ConfigConstants.NET_MV_SOURCE));
                      mvInfo.setType(jo.getIntValue(ConfigConstants.NET_MV_TYPE));
                      mvInfo.setId(jo.getString(ConfigConstants.NET_MV_ID));
                      mvInfo.setBvid(jo.getString(ConfigConstants.NET_MV_BVID));
                      mvInfo.setName(jo.getString(ConfigConstants.NET_MV_NAME));
                      mvInfo.setArtist(jo.getString(ConfigConstants.NET_MV_ARTIST));
                      task = new Task(downloadList, type, mvInfo);
                  }
                  // 考虑到下载路径可能更换，沿用任务原来的路径
                  task.setDest(dest);
                  task.setFormat(format);
                  task.setName(name);
                  task.setStatus(status);
                  task.setFinished(finished);
                  task.setTotal(total);
                  downloadListModel.addElement(task);
              }
          }
  
          // 载入播放队列
          JSONArray playQueueJsonArray = config.getJSONArray(ConfigConstants.PLAY_QUEUE);
          if (JsonUtil.notEmpty(playQueueJsonArray)) {
              for (int i = 0, len = playQueueJsonArray.size(); i < len; i++) {
                  String s = playQueueJsonArray.getString(i);
                  // 判断是否为文件路径
                  if (!JsonUtil.isValidObject(s)) {
                      AudioFile audioFile = new AudioFile(s);
                      globalExecutor.execute(() -> {
                          MediaUtil.fillAudioFileInfo(audioFile);
                          playQueue.repaint();
                      });
                      playQueueModel.addElement(audioFile);
                  } else {
                      JSONObject jsonObject = JSONObject.parseObject(s);
                      NetMusicInfo musicInfo = new NetMusicInfo();
                      musicInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_MUSIC_SOURCE));
                      musicInfo.setHash(jsonObject.getString(ConfigConstants.NET_MUSIC_HASH));
                      musicInfo.setId(jsonObject.getString(ConfigConstants.NET_MUSIC_ID));
                      musicInfo.setProgramId(jsonObject.getString(ConfigConstants.NET_MUSIC_PROGRAM_ID));
                      musicInfo.setName(jsonObject.getString(ConfigConstants.NET_MUSIC_NAME));
                      musicInfo.setArtist(jsonObject.getString(ConfigConstants.NET_MUSIC_ARTIST));
                      musicInfo.setArtistId(jsonObject.getString(ConfigConstants.NET_MUSIC_ARTIST_ID));
                      musicInfo.setAlbumName(jsonObject.getString(ConfigConstants.NET_MUSIC_ALBUM_NAME));
                      musicInfo.setAlbumId(jsonObject.getString(ConfigConstants.NET_MUSIC_ALBUM_ID));
                      musicInfo.setDuration(jsonObject.getDoubleValue(ConfigConstants.NET_MUSIC_DURATION));
                      musicInfo.setMvId(jsonObject.getString(ConfigConstants.NET_MUSIC_MV_ID));
                      musicInfo.setQualityType(jsonObject.getIntValue(ConfigConstants.NET_MUSIC_QUALITY_TYPE, AudioQuality.UNKNOWN));
                      playQueueModel.addElement(musicInfo);
                  }
              }
          }
  
          // 载入上次播放歌曲，选中并加载
          currSong = config.getIntValue(ConfigConstants.CURR_SONG, -1);
          if (currSong >= playQueueModel.size()) currSong = -1;
  
          // 载入收藏选项卡
          collectionTabbedPane.setSelectedIndex(config.getIntValue(ConfigConstants.COLLECTION_TAB_INDEX, CollectionTabIndex.MUSIC));
          // 载入个人音乐选项卡
          int personalMusicTab = config.getIntValue(ConfigConstants.PERSONAL_TAB_INDEX, PersonalMusicTabIndex.LOCAL_MUSIC);
          if (personalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC) localMusicButton.doClick();
          else if (personalMusicTab == PersonalMusicTabIndex.HISTORY) historyButton.doClick();
          else if (personalMusicTab == PersonalMusicTabIndex.COLLECTION) collectionButton.doClick();
          // 载入推荐选项卡
          preRecommendTab = config.getIntValue(ConfigConstants.RECOMMEND_TAB_INDEX, RecommendTabIndex.PLAYLIST_RECOMMEND);
          // 载入选项卡
          tabbedPane.setSelectedIndex(config.getIntValue(ConfigConstants.TAB_INDEX, TabIndex.PERSONAL));
  
          // 载入在线音乐搜索历史关键词
          JSONArray historySearchJsonArray = config.getJSONArray(ConfigConstants.NET_MUSIC_HISTORY_SEARCH);
          if (JsonUtil.notEmpty(historySearchJsonArray)) {
              for (int i = 0, len = historySearchJsonArray.size(); i < len; i++) {
                  String keyword = historySearchJsonArray.getString(i);
                  DialogButton b = new DialogButton(keyword);
                  b.addActionListener(event -> {
                      searchTextField.requestFocus();
                      searchTextField.setText(keyword);
                      netMusicClearInputButton.setVisible(true);
                      searchButton.doClick();
                      netLeftBox.remove(netMusicKeywordsPanelScrollPane);
                      netLeftBox.add(netMusicScrollPane);
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netMusicHistorySearchInnerPanel2.remove(b);
                          netMusicHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  netMusicHistorySearchInnerPanel2.add(b);
              }
              netMusicKeywordsPanel.repaint();
          }
  
          // 载入歌单搜索历史关键词
          historySearchJsonArray = config.getJSONArray(ConfigConstants.NET_PLAYLIST_HISTORY_SEARCH);
          if (JsonUtil.notEmpty(historySearchJsonArray)) {
              for (int i = 0, len = historySearchJsonArray.size(); i < len; i++) {
                  String keyword = historySearchJsonArray.getString(i);
                  DialogButton b = new DialogButton(keyword);
                  b.addActionListener(event -> {
                      netPlaylistSearchTextField.requestFocus();
                      netPlaylistSearchTextField.setText(keyword);
                      netPlaylistClearInputButton.setVisible(true);
                      netPlaylistLeftBox.remove(netPlaylistKeywordsPanelScrollPane);
                      netPlaylistLeftBox.add(netPlaylistScrollPane);
                      netPlaylistSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netPlaylistHistorySearchInnerPanel2.remove(b);
                          netPlaylistHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  netPlaylistHistorySearchInnerPanel2.add(b);
              }
              netPlaylistKeywordsPanel.repaint();
          }
  
          // 载入专辑搜索历史关键词
          historySearchJsonArray = config.getJSONArray(ConfigConstants.NET_ALBUM_HISTORY_SEARCH);
          if (JsonUtil.notEmpty(historySearchJsonArray)) {
              for (int i = 0, len = historySearchJsonArray.size(); i < len; i++) {
                  String keyword = historySearchJsonArray.getString(i);
                  DialogButton b = new DialogButton(keyword);
                  b.addActionListener(event -> {
                      netAlbumSearchTextField.requestFocus();
                      netAlbumSearchTextField.setText(keyword);
                      netAlbumClearInputButton.setVisible(true);
                      netAlbumLeftBox.remove(netAlbumKeywordsPanelScrollPane);
                      netAlbumLeftBox.add(netAlbumScrollPane);
                      netAlbumSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netAlbumHistorySearchInnerPanel2.remove(b);
                          netAlbumHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  netAlbumHistorySearchInnerPanel2.add(b);
              }
              netAlbumKeywordsPanel.repaint();
          }
  
          // 载入歌手搜索历史关键词
          historySearchJsonArray = config.getJSONArray(ConfigConstants.NET_ARTIST_HISTORY_SEARCH);
          if (JsonUtil.notEmpty(historySearchJsonArray)) {
              for (int i = 0, len = historySearchJsonArray.size(); i < len; i++) {
                  String keyword = historySearchJsonArray.getString(i);
                  DialogButton b = new DialogButton(keyword);
                  b.addActionListener(event -> {
                      netArtistSearchTextField.requestFocus();
                      netArtistSearchTextField.setText(keyword);
                      netArtistClearInputButton.setVisible(true);
                      netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                      netArtistLeftBox.add(netArtistScrollPane);
                      netArtistSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netArtistHistorySearchInnerPanel2.remove(b);
                          netArtistHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  netArtistHistorySearchInnerPanel2.add(b);
              }
              netArtistKeywordsPanel.repaint();
          }
  
          // 载入电台搜索历史关键词
          historySearchJsonArray = config.getJSONArray(ConfigConstants.NET_RADIO_HISTORY_SEARCH);
          if (JsonUtil.notEmpty(historySearchJsonArray)) {
              for (int i = 0, len = historySearchJsonArray.size(); i < len; i++) {
                  String keyword = historySearchJsonArray.getString(i);
                  DialogButton b = new DialogButton(keyword);
                  b.addActionListener(event -> {
                      netRadioSearchTextField.requestFocus();
                      netRadioSearchTextField.setText(keyword);
                      netRadioClearInputButton.setVisible(true);
                      netRadioLeftBox.remove(netRadioKeywordsPanelScrollPane);
                      netRadioLeftBox.add(netRadioScrollPane);
                      netRadioSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netRadioHistorySearchInnerPanel2.remove(b);
                          netRadioHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  netRadioHistorySearchInnerPanel2.add(b);
              }
              netRadioKeywordsPanel.repaint();
          }
  
          // 载入 MV 搜索历史关键词
          historySearchJsonArray = config.getJSONArray(ConfigConstants.NET_MV_HISTORY_SEARCH);
          if (JsonUtil.notEmpty(historySearchJsonArray)) {
              for (int i = 0, len = historySearchJsonArray.size(); i < len; i++) {
                  String keyword = historySearchJsonArray.getString(i);
                  DialogButton b = new DialogButton(keyword);
                  b.addActionListener(event -> {
                      netMvSearchTextField.requestFocus();
                      netMvSearchTextField.setText(keyword);
                      netMvClearInputButton.setVisible(true);
                      netMvLeftBox.remove(netMvKeywordsPanelScrollPane);
                      netMvLeftBox.add(netMvScrollPane);
                      netMvSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netMvHistorySearchInnerPanel2.remove(b);
                          netMvHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  netMvHistorySearchInnerPanel2.add(b);
              }
              netMvKeywordsPanel.repaint();
          }
  
          // 载入用户搜索历史关键词
          historySearchJsonArray = config.getJSONArray(ConfigConstants.NET_USER_HISTORY_SEARCH);
          if (JsonUtil.notEmpty(historySearchJsonArray)) {
              for (int i = 0, len = historySearchJsonArray.size(); i < len; i++) {
                  String keyword = historySearchJsonArray.getString(i);
                  DialogButton b = new DialogButton(keyword);
                  b.addActionListener(event -> {
                      netUserSearchTextField.requestFocus();
                      netUserSearchTextField.setText(keyword);
                      netUserClearInputButton.setVisible(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      netUserLeftBox.add(netUserScrollPane);
                      netUserSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netUserHistorySearchInnerPanel2.remove(b);
                          netUserHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  netUserHistorySearchInnerPanel2.add(b);
              }
          }
  
          // 载入上次选的主题
          int styleIndex = config.getIntValue(ConfigConstants.CURR_UI_STYLE, 0);
          changeUIStyle(styles.get(styleIndex));
  
          return JsonUtil.notEmpty(config);
      }
  
      // 载入本地音乐列表
      public void loadLocalMusicList(JSONObject config) {
          JSONArray musicJsonArray = config.getJSONArray(ConfigConstants.MUSIC_LIST);
          if (JsonUtil.notEmpty(musicJsonArray)) {
              for (int i = 0, len = musicJsonArray.size(); i < len; i++) {
                  String filePath = musicJsonArray.getString(i);
                  AudioFile audioFile = new AudioFile(filePath);
                  globalExecutor.execute(() -> {
                      MediaUtil.fillAudioFileInfo(audioFile);
                      musicList.repaint();
                  });
                  musicListModel.addElement(audioFile);
              }
          }
      }
  
      // 载入全部收藏列表
      public void loadCollectedMusicList(JSONObject config) {
          // 载入收藏歌曲列表
          JSONArray collectionJsonArray = config.getJSONArray(ConfigConstants.COLLECTION);
          if (JsonUtil.notEmpty(collectionJsonArray)) {
              for (int i = 0, len = collectionJsonArray.size(); i < len; i++) {
                  String s = collectionJsonArray.getString(i);
                  // 判断是否为文件路径
                  if (!JsonUtil.isValidObject(s)) {
                      AudioFile audioFile = new AudioFile(s);
                      globalExecutor.execute(() -> {
                          MediaUtil.fillAudioFileInfo(audioFile);
                          musicList.repaint();
                      });
                      collectionModel.addElement(audioFile);
                  } else {
                      JSONObject jsonObject = JSONObject.parseObject(s);
                      NetMusicInfo musicInfo = new NetMusicInfo();
                      musicInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_MUSIC_SOURCE));
                      musicInfo.setHash(jsonObject.getString(ConfigConstants.NET_MUSIC_HASH));
                      musicInfo.setId(jsonObject.getString(ConfigConstants.NET_MUSIC_ID));
                      musicInfo.setProgramId(jsonObject.getString(ConfigConstants.NET_MUSIC_PROGRAM_ID));
                      musicInfo.setName(jsonObject.getString(ConfigConstants.NET_MUSIC_NAME));
                      musicInfo.setArtist(jsonObject.getString(ConfigConstants.NET_MUSIC_ARTIST));
                      musicInfo.setArtistId(jsonObject.getString(ConfigConstants.NET_MUSIC_ARTIST_ID));
                      musicInfo.setAlbumName(jsonObject.getString(ConfigConstants.NET_MUSIC_ALBUM_NAME));
                      musicInfo.setAlbumId(jsonObject.getString(ConfigConstants.NET_MUSIC_ALBUM_ID));
                      musicInfo.setDuration(jsonObject.getDoubleValue(ConfigConstants.NET_MUSIC_DURATION));
                      musicInfo.setMvId(jsonObject.getString(ConfigConstants.NET_MUSIC_MV_ID));
                      musicInfo.setQualityType(jsonObject.getIntValue(ConfigConstants.NET_MUSIC_QUALITY_TYPE, AudioQuality.UNKNOWN));
                      collectionModel.addElement(musicInfo);
                  }
              }
          }
  
          // 载入收藏歌单列表
          JSONArray playlistCollectionJsonArray = config.getJSONArray(ConfigConstants.PLAYLIST_COLLECTION);
          if (JsonUtil.notEmpty(playlistCollectionJsonArray)) {
              for (int i = 0, len = playlistCollectionJsonArray.size(); i < len; i++) {
                  JSONObject jsonObject = playlistCollectionJsonArray.getJSONObject(i);
  
                  NetPlaylistInfo playlistInfo = new NetPlaylistInfo();
                  playlistInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_PLAYLIST_SOURCE));
                  playlistInfo.setId(jsonObject.getString(ConfigConstants.NET_PLAYLIST_ID));
                  playlistInfo.setName(jsonObject.getString(ConfigConstants.NET_PLAYLIST_NAME));
                  playlistInfo.setCreator(jsonObject.getString(ConfigConstants.NET_PLAYLIST_CREATOR));
                  playlistInfo.setCreatorId(jsonObject.getString(ConfigConstants.NET_PLAYLIST_CREATOR_ID));
                  playlistInfo.setCoverImgThumbUrl(jsonObject.getString(ConfigConstants.NET_PLAYLIST_COVER_IMG_THUMB_URL));
                  playlistInfo.setTrackCount(jsonObject.getIntValue(ConfigConstants.NET_PLAYLIST_TRACK_COUNT, -1));
                  playlistInfo.setPlayCount(jsonObject.getLongValue(ConfigConstants.NET_PLAYLIST_PLAY_COUNT, -1));
  
                  playlistCollectionModel.addElement(playlistInfo);
              }
          }
  
          // 载入收藏专辑列表
          JSONArray albumCollectionJsonArray = config.getJSONArray(ConfigConstants.ALBUM_COLLECTION);
          if (JsonUtil.notEmpty(albumCollectionJsonArray)) {
              for (int i = 0, len = albumCollectionJsonArray.size(); i < len; i++) {
                  JSONObject jsonObject = albumCollectionJsonArray.getJSONObject(i);
  
                  NetAlbumInfo albumInfo = new NetAlbumInfo();
                  albumInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_ALBUM_SOURCE));
                  albumInfo.setId(jsonObject.getString(ConfigConstants.NET_ALBUM_ID));
                  albumInfo.setName(jsonObject.getString(ConfigConstants.NET_ALBUM_NAME));
                  albumInfo.setArtist(jsonObject.getString(ConfigConstants.NET_ALBUM_ARTIST));
                  albumInfo.setArtistId(jsonObject.getString(ConfigConstants.NET_ALBUM_ARTIST_ID));
                  albumInfo.setCoverImgThumbUrl(jsonObject.getString(ConfigConstants.NET_ALBUM_COVER_IMG_THUMB_URL));
                  albumInfo.setSongNum(jsonObject.getIntValue(ConfigConstants.NET_ALBUM_SONG_NUM, -1));
                  albumInfo.setPublishTime(jsonObject.getString(ConfigConstants.NET_ALBUM_PUBLISH_TIME));
  
                  albumCollectionModel.addElement(albumInfo);
              }
          }
  
          // 载入收藏歌手列表
          JSONArray artistCollectionJsonArray = config.getJSONArray(ConfigConstants.ARTIST_COLLECTION);
          if (JsonUtil.notEmpty(artistCollectionJsonArray)) {
              for (int i = 0, len = artistCollectionJsonArray.size(); i < len; i++) {
                  JSONObject jsonObject = artistCollectionJsonArray.getJSONObject(i);
  
                  NetArtistInfo artistInfo = new NetArtistInfo();
                  artistInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_ARTIST_SOURCE));
                  artistInfo.setOrganization(jsonObject.getBooleanValue(ConfigConstants.NET_ARTIST_IS_ORGANIZATION));
                  artistInfo.setId(jsonObject.getString(ConfigConstants.NET_ARTIST_ID));
                  artistInfo.setName(jsonObject.getString(ConfigConstants.NET_ARTIST_NAME));
                  artistInfo.setCoverImgUrl(jsonObject.getString(ConfigConstants.NET_ARTIST_COVER_IMG_URL));
                  artistInfo.setCoverImgThumbUrl(jsonObject.getString(ConfigConstants.NET_ARTIST_COVER_IMG_THUMB_URL));
                  artistInfo.setSongNum(jsonObject.getIntValue(ConfigConstants.NET_ARTIST_SONG_NUM, -1));
                  artistInfo.setAlbumNum(jsonObject.getIntValue(ConfigConstants.NET_ARTIST_ALBUM_NUM, -1));
                  artistInfo.setMvNum(jsonObject.getIntValue(ConfigConstants.NET_ARTIST_MV_NUM, -1));
  
                  artistCollectionModel.addElement(artistInfo);
              }
          }
  
          // 载入收藏电台列表
          JSONArray radioCollectionJsonArray = config.getJSONArray(ConfigConstants.RADIO_COLLECTION);
          if (JsonUtil.notEmpty(radioCollectionJsonArray)) {
              for (int i = 0, len = radioCollectionJsonArray.size(); i < len; i++) {
                  JSONObject jsonObject = radioCollectionJsonArray.getJSONObject(i);
  
                  NetRadioInfo radioInfo = new NetRadioInfo();
                  radioInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_RADIO_SOURCE));
                  radioInfo.setId(jsonObject.getString(ConfigConstants.NET_RADIO_ID));
                  radioInfo.setName(jsonObject.getString(ConfigConstants.NET_RADIO_NAME));
                  radioInfo.setDj(jsonObject.getString(ConfigConstants.NET_RADIO_DJ));
                  radioInfo.setDjId(jsonObject.getString(ConfigConstants.NET_RADIO_DJ_ID));
                  radioInfo.setCoverImgUrl(jsonObject.getString(ConfigConstants.NET_RADIO_COVER_IMG_URL));
                  radioInfo.setCoverImgThumbUrl(jsonObject.getString(ConfigConstants.NET_RADIO_COVER_IMG_THUMB_URL));
                  radioInfo.setCategory(jsonObject.getString(ConfigConstants.NET_RADIO_CATEGORY));
                  radioInfo.setTrackCount(jsonObject.getIntValue(ConfigConstants.NET_RADIO_TRACK_COUNT, -1));
                  radioInfo.setPlayCount(jsonObject.getLongValue(ConfigConstants.NET_RADIO_PLAY_COUNT, -1));
  
                  radioCollectionModel.addElement(radioInfo);
              }
          }
  
          // 载入收藏 MV 列表
          JSONArray mvCollectionJsonArray = config.getJSONArray(ConfigConstants.MV_COLLECTION);
          if (JsonUtil.notEmpty(mvCollectionJsonArray)) {
              for (int i = 0, len = mvCollectionJsonArray.size(); i < len; i++) {
                  JSONObject jsonObject = mvCollectionJsonArray.getJSONObject(i);
  
                  NetMvInfo mvInfo = new NetMvInfo();
                  mvInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_MV_SOURCE));
                  mvInfo.setType(jsonObject.getIntValue(ConfigConstants.NET_MV_TYPE));
                  mvInfo.setId(jsonObject.getString(ConfigConstants.NET_MV_ID));
                  mvInfo.setBvid(jsonObject.getString(ConfigConstants.NET_MV_BVID));
                  mvInfo.setName(jsonObject.getString(ConfigConstants.NET_MV_NAME));
                  mvInfo.setArtist(jsonObject.getString(ConfigConstants.NET_MV_ARTIST));
                  mvInfo.setCreatorId(jsonObject.getString(ConfigConstants.NET_MV_CREATOR_ID));
                  mvInfo.setDuration(jsonObject.getDoubleValue(ConfigConstants.NET_MV_DURATION));
                  mvInfo.setPubTime(jsonObject.getString(ConfigConstants.NET_MV_PUB_TIME));
                  mvInfo.setCoverImgUrl(jsonObject.getString(ConfigConstants.NET_MV_COVER_IMG_URL));
                  mvInfo.setPlayCount(jsonObject.getLongValue(ConfigConstants.NET_MV_PLAY_COUNT, -1));
  
                  mvCollectionModel.addElement(mvInfo);
              }
          }
  
          // 载入收藏榜单列表
          JSONArray rankingCollectionJsonArray = config.getJSONArray(ConfigConstants.RANKING_COLLECTION);
          if (JsonUtil.notEmpty(rankingCollectionJsonArray)) {
              for (int i = 0, len = rankingCollectionJsonArray.size(); i < len; i++) {
                  JSONObject jsonObject = rankingCollectionJsonArray.getJSONObject(i);
  
                  NetRankingInfo rankingInfo = new NetRankingInfo();
                  rankingInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_RANKING_SOURCE));
                  rankingInfo.setId(jsonObject.getString(ConfigConstants.NET_RANKING_ID));
                  rankingInfo.setName(jsonObject.getString(ConfigConstants.NET_RANKING_NAME));
                  rankingInfo.setPlayCount(jsonObject.getLongValue(ConfigConstants.NET_RANKING_PLAY_COUNT, -1));
                  rankingInfo.setDescription(jsonObject.getString(ConfigConstants.NET_RANKING_DESCRIPTION));
                  rankingInfo.setUpdateFre(jsonObject.getString(ConfigConstants.NET_RANKING_UPDATE_FRE));
                  rankingInfo.setUpdateTime(jsonObject.getString(ConfigConstants.NET_RANKING_UPDATE_TIME));
                  rankingInfo.setCoverImgUrl(jsonObject.getString(ConfigConstants.NET_RANKING_COVER_IMG_URL));
  
                  rankingCollectionModel.addElement(rankingInfo);
              }
          }
  
          // 载入收藏用户列表
          JSONArray userCollectionJsonArray = config.getJSONArray(ConfigConstants.USER_COLLECTION);
          if (JsonUtil.notEmpty(userCollectionJsonArray)) {
              for (int i = 0, len = userCollectionJsonArray.size(); i < len; i++) {
                  JSONObject jsonObject = userCollectionJsonArray.getJSONObject(i);
  
                  NetUserInfo userInfo = new NetUserInfo();
                  userInfo.setSource(jsonObject.getIntValue(ConfigConstants.NET_USER_SOURCE));
                  userInfo.setId(jsonObject.getString(ConfigConstants.NET_USER_ID));
                  userInfo.setName(jsonObject.getString(ConfigConstants.NET_USER_NAME));
                  userInfo.setGender(jsonObject.getString(ConfigConstants.NET_USER_GENDER));
                  userInfo.setAvatarUrl(jsonObject.getString(ConfigConstants.NET_USER_AVATAR_URL));
                  userInfo.setAvatarThumbUrl(jsonObject.getString(ConfigConstants.NET_USER_AVATAR_THUMB_URL));
                  userInfo.setFollow(jsonObject.getIntValue(ConfigConstants.NET_USER_FOLLOW, -1));
                  userInfo.setFan(jsonObject.getIntValue(ConfigConstants.NET_USER_FAN, -1));
                  userInfo.setPlaylistCount(jsonObject.getIntValue(ConfigConstants.NET_USER_PLAYLIST_COUNT, -1));
                  userInfo.setRadioCount(jsonObject.getIntValue(ConfigConstants.NET_USER_RADIO_COUNT, -1));
                  userInfo.setProgramCount(jsonObject.getIntValue(ConfigConstants.NET_USER_PROGRAM_COUNT, -1));
  
                  userCollectionModel.addElement(userInfo);
              }
          }
      }
  
      // 保存配置
      private void saveConfig() {
          JSONObject config = new JSONObject();
          // 存入自定义主题
          JSONArray styleArray = new JSONArray();
          for (UIStyle style : styles) {
              if (!style.isCustom()) continue;
              JSONObject styleObject = new JSONObject();
              styleObject.put("name", style.getName());
              styleObject.put("imgKey", style.getImgKey());
              styleObject.put("bgColor", ColorUtil.colorToHex(style.getBgColor()));
              styleObject.put("foreColor", ColorUtil.colorToHex(style.getForeColor()));
              styleObject.put("selectedColor", ColorUtil.colorToHex(style.getSelectedColor()));
              styleObject.put("lrcColor", ColorUtil.colorToHex(style.getLrcColor()));
              styleObject.put("highlightColor", ColorUtil.colorToHex(style.getHighlightColor()));
              styleObject.put("textColor", ColorUtil.colorToHex(style.getTextColor()));
              styleObject.put("timeBarColor", ColorUtil.colorToHex(style.getTimeBarColor()));
              styleObject.put("iconColor", ColorUtil.colorToHex(style.getIconColor()));
              styleObject.put("scrollBarColor", ColorUtil.colorToHex(style.getScrollBarColor()));
              styleObject.put("sliderColor", ColorUtil.colorToHex(style.getSliderColor()));
              styleObject.put("spectrumColor", ColorUtil.colorToHex(style.getSpectrumColor()));
              styleArray.add(styleObject);
          }
          config.put(ConfigConstants.CUSTOM_UI_STYLES, styleArray);
          // 当前 UI 主题索引
          config.put(ConfigConstants.CURR_UI_STYLE, ListUtil.indexOf(styles, currUIStyle));
          // 存入快捷键
          config.put(ConfigConstants.KEY_ENABLED, keyEnabled);
          config.put(ConfigConstants.PLAY_OR_PAUSE_KEYS, KeyUtil.keysToJsonArray(playOrPauseKeys));
          config.put(ConfigConstants.PLAY_LAST_KEYS, KeyUtil.keysToJsonArray(playLastKeys));
          config.put(ConfigConstants.PLAY_NEXT_KEYS, KeyUtil.keysToJsonArray(playNextKeys));
          config.put(ConfigConstants.BACKWARD_KEYS, KeyUtil.keysToJsonArray(backwardKeys));
          config.put(ConfigConstants.FORWARD_KEYS, KeyUtil.keysToJsonArray(forwardKeys));
          config.put(ConfigConstants.VIDEO_FULL_SCREEN_KEYS, KeyUtil.keysToJsonArray(videoFullScreenKeys));
          // 存入选项卡
          config.put(ConfigConstants.TAB_INDEX, tabbedPane.getSelectedIndex());
          // 存入个人音乐选项卡
          config.put(ConfigConstants.PERSONAL_TAB_INDEX, currPersonalMusicTab);
          // 存入收藏选项卡
          config.put(ConfigConstants.COLLECTION_TAB_INDEX, collectionTabbedPane.getSelectedIndex());
          // 存入推荐选项卡
          config.put(ConfigConstants.RECOMMEND_TAB_INDEX, currRecommendTab == -1 ? preRecommendTab : currRecommendTab);
          // 存入是否自动更新
          config.put(ConfigConstants.AUTO_UPDATE, autoUpdate);
          // 存入界面语言
          config.put(ConfigConstants.UI_LANGUAGE, I18n.currLang);
          // 存入界面字体
          config.put(ConfigConstants.UI_FONT, Fonts.currFont);
          // 存入关闭窗口操作
          config.put(ConfigConstants.CLOSE_WINDOW_OPTION, currCloseWindowOption);
          // 存入窗口大小
          config.put(ConfigConstants.WINDOW_SIZE, windowSize);
          // 存入是否显示已播放时间
          config.put(ConfigConstants.TIME_ELAPSED_MODE, timeElapsedMode);
          config.put(ConfigConstants.VIDEO_TIME_ELAPSED_MODE, videoTimeElapsedMode);
          // 存入播放视频是否隐藏主界面
          config.put(ConfigConstants.VIDEO_ONLY, videoOnly);
          // 存入是否显示侧边栏文字
          config.put(ConfigConstants.SHOW_TAB_TEXT, showTabText);
          // 存入歌词对齐方式
          config.put(ConfigConstants.LRC_ALIGNMENT, LyricAlignment.lrcAlignmentIndex);
          // 存入频谱最大高度
          config.put(ConfigConstants.SPEC_MAX_HEIGHT, SpectrumConstants.barMaxHeight);
          // 存入高斯模糊因子
          config.put(ConfigConstants.GS_FACTOR_INDEX, BlurConstants.gsFactorIndex);
          // 存入暗角滤镜因子
          config.put(ConfigConstants.DARKER_FACTOR_INDEX, BlurConstants.darkerFactorIndex);
  //        // 存入线性渐变色彩风格
  //        config.put(ConfigConstants.GRADIENT_COLOR_STYLE_INDEX, BlurConstants.gradientColorStyleIndex);
          // 存入歌曲下载路径
          config.put(ConfigConstants.MUSIC_DOWN_PATH, SimplePath.DOWNLOAD_MUSIC_PATH);
          // 存入 MV 下载路径
          config.put(ConfigConstants.MV_DOWN_PATH, SimplePath.DOWNLOAD_MV_PATH);
          // 存入缓存路径
          config.put(ConfigConstants.CACHE_PATH, SimplePath.CACHE_PATH);
          // 存入最大缓存大小
          config.put(ConfigConstants.MAX_CACHE_SIZE, maxCacheSize);
          // 存入最大播放历史数量
          config.put(ConfigConstants.MAX_HISTORY_COUNT, maxHistoryCount);
          // 存入最大搜索历史数量
          config.put(ConfigConstants.MAX_SEARCH_HISTORY_COUNT, maxSearchHistoryCount);
          // 存入同时下载的最大任务数
          config.put(ConfigConstants.MAX_CONCURRENT_TASK_COUNT, ((ThreadPoolExecutor) GlobalExecutors.downloadExecutor).getCorePoolSize());
          // 存入当前播放模式
          config.put(ConfigConstants.PLAY_MODE, currPlayMode);
          // 存入是否显示频谱
          config.put(ConfigConstants.SHOW_SPECTRUM, showSpectrum);
          // 存入是否高斯模糊
          config.put(ConfigConstants.GS_ON, gsOn);
          // 存入是否暗化
          config.put(ConfigConstants.DARKER_ON, darkerOn);
          // 存入是否朦胧遮罩
          config.put(ConfigConstants.MASK_ON, maskOn);
          // 存入是否律动
          config.put(ConfigConstants.GROOVE_ON, grooveOn);
          // 存入模糊类型
          config.put(ConfigConstants.BLUR_TYPE, blurType);
          // 存入是否自动下载歌词
          config.put(ConfigConstants.AUTO_DOWNLOAD_LYRIC, isAutoDownloadLrc);
  //        // 存入是否添加逐字时间轴
  //        config.put(ConfigConstants.VERBATIM_TIMELINE, LyricType.verbatimTimeline);
          // 存入歌词偏移
          config.put(ConfigConstants.LYRIC_OFFSET, lrcOffset);
          // 存入频谱透明度
          config.put(ConfigConstants.SPEC_OPACITY, specOpacity);
          // 存入是否显示桌面歌词
          config.put(ConfigConstants.SHOW_DESKTOP_LYRIC, showDesktopLyric);
          // 存入是否锁定桌面歌词
          config.put(ConfigConstants.LOCK_DESKTOP_LYRIC, desktopLyricLocked);
          // 存入桌面歌词坐标
          config.put(ConfigConstants.DESKTOP_LYRIC_X, desktopLyricX);
          config.put(ConfigConstants.DESKTOP_LYRIC_Y, desktopLyricY);
          // 存入是否桌面歌词置顶
          config.put(ConfigConstants.DESKTOP_LYRIC_ON_TOP, desktopLyricOnTop);
          // 存入桌面歌词透明度
          config.put(ConfigConstants.DESKTOP_LYRIC_ALPHA, desktopLyricAlpha);
          // 存入桌面歌词字体大小
          config.put(ConfigConstants.DESKTOP_LYRIC_FONT_SIZE, desktopLyricFontSize);
          // 存入音质
          config.put(ConfigConstants.AUDIO_QUALITY, AudioQuality.quality);
          // 存入画质
          config.put(ConfigConstants.VIDEO_QUALITY, VideoQuality.quality);
          // 存入快进/快退时间
          config.put(ConfigConstants.FOB_TIME, forwardOrBackwardTime);
          // 存入视频快进/快退时间
          config.put(ConfigConstants.VIDEO_FOB_TIME, videoForwardOrBackwardTime);
          // 存入速率
          config.put(ConfigConstants.RATE, currRate);
          // 存入视频速率
          config.put(ConfigConstants.VIDEO_RATE, currVideoRate);
          // 存入频谱样式
          config.put(ConfigConstants.SPECTRUM_STYLE, currSpecStyle);
          // 存入均衡
          config.put(ConfigConstants.BALANCE, currBalance);
          // 存入是否静音
          config.put(ConfigConstants.MUTE, isMute);
          // 存入音量
          config.put(ConfigConstants.VOLUME, volumeSlider.getValue());
          // 存入音效
          config.put(ConfigConstants.SOUND_EFFECT, currSoundEffect);
          // 存入均衡数据
          config.put(ConfigConstants.EQUALIZER_DATA, ed);
          // 存入迷你窗口位置
          config.put(ConfigConstants.MINIX, miniX);
          config.put(ConfigConstants.MINIY, miniY);
          // 存入歌词类型
          config.put(ConfigConstants.LYRIC_TYPE, currLrcType);
          // 存入排序顺序
          config.put(ConfigConstants.SORT_ORDER, currSortOrder);
          // 存入当前所有歌曲目录
          JSONArray catalogJsonArray = new JSONArray();
          for (int i = 0, len = catalogs.size(); i < len; i++) {
              File dir = catalogs.get(i);
              catalogJsonArray.add(dir.getAbsolutePath());
          }
          config.put(ConfigConstants.CATALOGS, catalogJsonArray);
          // 存入当前歌曲列表(文件路径)
          putLocalMusicList(config);
          // 存入当前播放的歌曲索引
          config.put(ConfigConstants.CURR_SONG, currSong);
          // 存入当前历史列表
          JSONArray historyJsonArray = new JSONArray();
          for (int i = 0, len = historyModel.getSize(); i < len; i++) {
              MusicResource resource = historyModel.get(i);
              if (resource instanceof AudioFile) {
                  AudioFile file = (AudioFile) resource;
                  historyJsonArray.add(file.getPath());
              } else if (resource instanceof NetMusicInfo) {
                  NetMusicInfo musicInfo = (NetMusicInfo) resource;
                  JSONObject jsonObject = new JSONObject();
                  jsonObject.put(ConfigConstants.NET_MUSIC_SOURCE, musicInfo.getSource());
                  jsonObject.put(ConfigConstants.NET_MUSIC_HASH, musicInfo.getHash());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ID, musicInfo.getId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_PROGRAM_ID, musicInfo.getProgramId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_NAME, musicInfo.getName());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ARTIST, musicInfo.getArtist());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ARTIST_ID, musicInfo.getArtistId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ALBUM_NAME, musicInfo.getAlbumName());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ALBUM_ID, musicInfo.getAlbumId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_DURATION, musicInfo.getDuration());
                  jsonObject.put(ConfigConstants.NET_MUSIC_MV_ID, musicInfo.getMvId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_QUALITY_TYPE, musicInfo.getQualityType());
                  historyJsonArray.add(jsonObject);
              }
          }
          config.put(ConfigConstants.HISTORY, historyJsonArray);
  
          // 存入全部收藏列表
          putCollectedItemList(config);
  
          // 存入下载任务列表
          JSONArray tasksJsonArray = new JSONArray();
          for (int i = 0, len = downloadListModel.getSize(); i < len; i++) {
              Task task = downloadListModel.get(i);
              // 保存任务时，任务还在运行，先中断
              if (task.isProcessing()) task.stop();
              JSONObject jsonObject = new JSONObject();
              jsonObject.put(ConfigConstants.TASK_URL, task.getUrl());
              jsonObject.put(ConfigConstants.TASK_DEST, task.getDest());
              jsonObject.put(ConfigConstants.TASK_FORMAT, task.getFormat());
              jsonObject.put(ConfigConstants.TASK_TYPE, task.getType());
              jsonObject.put(ConfigConstants.TASK_NAME, task.getName());
              jsonObject.put(ConfigConstants.TASK_STATUS, task.getStatus());
              jsonObject.put(ConfigConstants.TASK_FINISHED, task.getFinished());
              jsonObject.put(ConfigConstants.TASK_TOTAL, task.getTotal());
              // 如果是音乐下载任务，需要额外记录音乐信息
              if (task.isMusic()) {
                  NetMusicInfo musicInfo = (NetMusicInfo) task.getResource();
                  JSONObject jo = new JSONObject();
                  jo.put(ConfigConstants.NET_MUSIC_SOURCE, musicInfo.getSource());
                  jo.put(ConfigConstants.NET_MUSIC_ID, musicInfo.getId());
                  jo.put(ConfigConstants.NET_MUSIC_NAME, musicInfo.getName());
                  jo.put(ConfigConstants.NET_MUSIC_ARTIST, musicInfo.getArtist());
                  jsonObject.put(ConfigConstants.TASK_MUSIC_INFO, jo);
              }
              // 如果是 MV 下载任务，需要额外记录 MV 信息
              else if (task.getType() == TaskType.MV) {
                  NetMvInfo mvInfo = (NetMvInfo) task.getResource();
                  JSONObject jo = new JSONObject();
                  jo.put(ConfigConstants.NET_MV_SOURCE, mvInfo.getSource());
                  jo.put(ConfigConstants.NET_MV_TYPE, mvInfo.getType());
                  jo.put(ConfigConstants.NET_MV_ID, mvInfo.getId());
                  jo.put(ConfigConstants.NET_MV_BVID, mvInfo.getBvid());
                  jo.put(ConfigConstants.NET_MV_NAME, mvInfo.getName());
                  jo.put(ConfigConstants.NET_MV_ARTIST, mvInfo.getArtist());
                  jsonObject.put(ConfigConstants.TASK_MV_INFO, jo);
              }
              tasksJsonArray.add(jsonObject);
          }
          config.put(ConfigConstants.TASKS, tasksJsonArray);
  
          // 存入播放队列
          JSONArray playQueueJsonArray = new JSONArray();
          for (int i = 0, len = playQueueModel.getSize(); i < len; i++) {
              MusicResource resource = playQueueModel.get(i);
              if (resource instanceof AudioFile) {
                  AudioFile file = (AudioFile) resource;
                  playQueueJsonArray.add(file.getPath());
              } else if (resource instanceof NetMusicInfo) {
                  NetMusicInfo musicInfo = (NetMusicInfo) resource;
                  JSONObject jsonObject = new JSONObject();
                  jsonObject.put(ConfigConstants.NET_MUSIC_SOURCE, musicInfo.getSource());
                  jsonObject.put(ConfigConstants.NET_MUSIC_HASH, musicInfo.getHash());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ID, musicInfo.getId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_PROGRAM_ID, musicInfo.getProgramId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_NAME, musicInfo.getName());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ARTIST, musicInfo.getArtist());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ARTIST_ID, musicInfo.getArtistId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ALBUM_NAME, musicInfo.getAlbumName());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ALBUM_ID, musicInfo.getAlbumId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_DURATION, musicInfo.getDuration());
                  jsonObject.put(ConfigConstants.NET_MUSIC_MV_ID, musicInfo.getMvId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_QUALITY_TYPE, musicInfo.getQualityType());
                  playQueueJsonArray.add(jsonObject);
              }
          }
          config.put(ConfigConstants.PLAY_QUEUE, playQueueJsonArray);
  
          // 存入搜索历史关键词
          JSONArray historySearchJsonArray = new JSONArray();
          Component[] components = netMusicHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              DialogButton b = (DialogButton) c;
              historySearchJsonArray.add(b.getPlainText());
          }
          config.put(ConfigConstants.NET_MUSIC_HISTORY_SEARCH, historySearchJsonArray);
  
          // 存入歌单搜索历史关键词
          JSONArray netPlaylistHistorySearchJsonArray = new JSONArray();
          components = netPlaylistHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              DialogButton b = (DialogButton) c;
              netPlaylistHistorySearchJsonArray.add(b.getPlainText());
          }
          config.put(ConfigConstants.NET_PLAYLIST_HISTORY_SEARCH, netPlaylistHistorySearchJsonArray);
  
          // 存入专辑搜索历史关键词
          JSONArray netAlbumHistorySearchJsonArray = new JSONArray();
          components = netAlbumHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              DialogButton b = (DialogButton) c;
              netAlbumHistorySearchJsonArray.add(b.getPlainText());
          }
          config.put(ConfigConstants.NET_ALBUM_HISTORY_SEARCH, netAlbumHistorySearchJsonArray);
  
          // 存入歌手搜索历史关键词
          JSONArray netArtistHistorySearchJsonArray = new JSONArray();
          components = netArtistHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              DialogButton b = (DialogButton) c;
              netArtistHistorySearchJsonArray.add(b.getPlainText());
          }
          config.put(ConfigConstants.NET_ARTIST_HISTORY_SEARCH, netArtistHistorySearchJsonArray);
  
          // 存入电台搜索历史关键词
          JSONArray netRadioHistorySearchJsonArray = new JSONArray();
          components = netRadioHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              DialogButton b = (DialogButton) c;
              netRadioHistorySearchJsonArray.add(b.getPlainText());
          }
          config.put(ConfigConstants.NET_RADIO_HISTORY_SEARCH, netRadioHistorySearchJsonArray);
  
          // 存入 MV 搜索历史关键词
          JSONArray netMvHistorySearchJsonArray = new JSONArray();
          components = netMvHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              DialogButton b = (DialogButton) c;
              netMvHistorySearchJsonArray.add(b.getPlainText());
          }
          config.put(ConfigConstants.NET_MV_HISTORY_SEARCH, netMvHistorySearchJsonArray);
  
          // 存入用户搜索历史关键词
          JSONArray netUserHistorySearchJsonArray = new JSONArray();
          components = netUserHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              DialogButton b = (DialogButton) c;
              netUserHistorySearchJsonArray.add(b.getPlainText());
          }
          config.put(ConfigConstants.NET_USER_HISTORY_SEARCH, netUserHistorySearchJsonArray);
  
          LMDataUtil.toFile(config, ConfigConstants.CONFIG_FILE_NAME);
      }
  
      // 存入本地音乐列表
      public void putLocalMusicList(JSONObject config) {
          JSONArray musicJsonArray = new JSONArray();
          for (int i = 0, len = musicListModel.getSize(); i < len; i++) {
              File file = (File) musicListModel.get(i);
              musicJsonArray.add(file.getPath());
          }
          config.put(ConfigConstants.MUSIC_LIST, musicJsonArray);
      }
  
      // 存入全部收藏列表
      public void putCollectedItemList(JSONObject config) {
          // 存入收藏歌曲列表
          JSONArray collectionJsonArray = new JSONArray();
          for (int i = 0, len = collectionModel.getSize(); i < len; i++) {
              MusicResource resource = collectionModel.get(i);
              if (resource instanceof AudioFile) {
                  AudioFile file = (AudioFile) resource;
                  collectionJsonArray.add(file.getPath());
              } else if (resource instanceof NetMusicInfo) {
                  NetMusicInfo musicInfo = (NetMusicInfo) resource;
                  JSONObject jsonObject = new JSONObject();
                  jsonObject.put(ConfigConstants.NET_MUSIC_SOURCE, musicInfo.getSource());
                  jsonObject.put(ConfigConstants.NET_MUSIC_HASH, musicInfo.getHash());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ID, musicInfo.getId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_PROGRAM_ID, musicInfo.getProgramId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_NAME, musicInfo.getName());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ARTIST, musicInfo.getArtist());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ARTIST_ID, musicInfo.getArtistId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ALBUM_NAME, musicInfo.getAlbumName());
                  jsonObject.put(ConfigConstants.NET_MUSIC_ALBUM_ID, musicInfo.getAlbumId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_DURATION, musicInfo.getDuration());
                  jsonObject.put(ConfigConstants.NET_MUSIC_MV_ID, musicInfo.getMvId());
                  jsonObject.put(ConfigConstants.NET_MUSIC_QUALITY_TYPE, musicInfo.getQualityType());
                  collectionJsonArray.add(jsonObject);
              }
          }
          config.put(ConfigConstants.COLLECTION, collectionJsonArray);
  
          // 存入收藏歌单列表
          JSONArray playlistCollectionJsonArray = new JSONArray();
          for (int i = 0, len = playlistCollectionModel.getSize(); i < len; i++) {
              NetPlaylistInfo playlistInfo = (NetPlaylistInfo) playlistCollectionModel.get(i);
  
              JSONObject jsonObject = new JSONObject();
              jsonObject.put(ConfigConstants.NET_PLAYLIST_SOURCE, playlistInfo.getSource());
              jsonObject.put(ConfigConstants.NET_PLAYLIST_ID, playlistInfo.getId());
              jsonObject.put(ConfigConstants.NET_PLAYLIST_NAME, playlistInfo.getName());
              jsonObject.put(ConfigConstants.NET_PLAYLIST_CREATOR, playlistInfo.getCreator());
              jsonObject.put(ConfigConstants.NET_PLAYLIST_CREATOR_ID, playlistInfo.getCreatorId());
              jsonObject.put(ConfigConstants.NET_PLAYLIST_COVER_IMG_THUMB_URL, playlistInfo.getCoverImgThumbUrl());
              jsonObject.put(ConfigConstants.NET_PLAYLIST_TRACK_COUNT, playlistInfo.getTrackCount());
              jsonObject.put(ConfigConstants.NET_PLAYLIST_PLAY_COUNT, playlistInfo.getPlayCount());
              playlistCollectionJsonArray.add(jsonObject);
          }
          config.put(ConfigConstants.PLAYLIST_COLLECTION, playlistCollectionJsonArray);
  
          // 存入收藏专辑列表
          JSONArray albumCollectionJsonArray = new JSONArray();
          for (int i = 0, len = albumCollectionModel.getSize(); i < len; i++) {
              NetAlbumInfo albumInfo = (NetAlbumInfo) albumCollectionModel.get(i);
  
              JSONObject jsonObject = new JSONObject();
              jsonObject.put(ConfigConstants.NET_ALBUM_SOURCE, albumInfo.getSource());
              jsonObject.put(ConfigConstants.NET_ALBUM_ID, albumInfo.getId());
              jsonObject.put(ConfigConstants.NET_ALBUM_NAME, albumInfo.getName());
              jsonObject.put(ConfigConstants.NET_ALBUM_ARTIST, albumInfo.getArtist());
              jsonObject.put(ConfigConstants.NET_ALBUM_ARTIST_ID, albumInfo.getArtistId());
              jsonObject.put(ConfigConstants.NET_ALBUM_COVER_IMG_THUMB_URL, albumInfo.getCoverImgThumbUrl());
              jsonObject.put(ConfigConstants.NET_ALBUM_SONG_NUM, albumInfo.getSongNum());
              jsonObject.put(ConfigConstants.NET_ALBUM_PUBLISH_TIME, albumInfo.getPublishTime());
              albumCollectionJsonArray.add(jsonObject);
          }
          config.put(ConfigConstants.ALBUM_COLLECTION, albumCollectionJsonArray);
  
          // 存入收藏歌手列表
          JSONArray artistCollectionJsonArray = new JSONArray();
          for (int i = 0, len = artistCollectionModel.getSize(); i < len; i++) {
              NetArtistInfo artistInfo = (NetArtistInfo) artistCollectionModel.get(i);
  
              JSONObject jsonObject = new JSONObject();
              jsonObject.put(ConfigConstants.NET_ARTIST_SOURCE, artistInfo.getSource());
              jsonObject.put(ConfigConstants.NET_ARTIST_IS_ORGANIZATION, artistInfo.isOrganization());
              jsonObject.put(ConfigConstants.NET_ARTIST_ID, artistInfo.getId());
              jsonObject.put(ConfigConstants.NET_ARTIST_NAME, artistInfo.getName());
              jsonObject.put(ConfigConstants.NET_ARTIST_COVER_IMG_URL, artistInfo.getCoverImgUrl());
              jsonObject.put(ConfigConstants.NET_ARTIST_COVER_IMG_THUMB_URL, artistInfo.getCoverImgThumbUrl());
              jsonObject.put(ConfigConstants.NET_ARTIST_SONG_NUM, artistInfo.getSongNum());
              jsonObject.put(ConfigConstants.NET_ARTIST_ALBUM_NUM, artistInfo.getAlbumNum());
              jsonObject.put(ConfigConstants.NET_ARTIST_MV_NUM, artistInfo.getMvNum());
              artistCollectionJsonArray.add(jsonObject);
          }
          config.put(ConfigConstants.ARTIST_COLLECTION, artistCollectionJsonArray);
  
          // 存入收藏电台列表
          JSONArray radioCollectionJsonArray = new JSONArray();
          for (int i = 0, len = radioCollectionModel.getSize(); i < len; i++) {
              NetRadioInfo radioInfo = (NetRadioInfo) radioCollectionModel.get(i);
  
              JSONObject jsonObject = new JSONObject();
              jsonObject.put(ConfigConstants.NET_RADIO_SOURCE, radioInfo.getSource());
              jsonObject.put(ConfigConstants.NET_RADIO_ID, radioInfo.getId());
              jsonObject.put(ConfigConstants.NET_RADIO_NAME, radioInfo.getName());
              jsonObject.put(ConfigConstants.NET_RADIO_DJ, radioInfo.getDj());
              jsonObject.put(ConfigConstants.NET_RADIO_DJ_ID, radioInfo.getDjId());
              jsonObject.put(ConfigConstants.NET_RADIO_COVER_IMG_URL, radioInfo.getCoverImgUrl());
              jsonObject.put(ConfigConstants.NET_RADIO_COVER_IMG_THUMB_URL, radioInfo.getCoverImgThumbUrl());
              jsonObject.put(ConfigConstants.NET_RADIO_CATEGORY, radioInfo.getCategory());
              jsonObject.put(ConfigConstants.NET_RADIO_TRACK_COUNT, radioInfo.getTrackCount());
              jsonObject.put(ConfigConstants.NET_RADIO_PLAY_COUNT, radioInfo.getPlayCount());
              radioCollectionJsonArray.add(jsonObject);
          }
          config.put(ConfigConstants.RADIO_COLLECTION, radioCollectionJsonArray);
  
          // 存入收藏 MV 列表
          JSONArray mvCollectionJsonArray = new JSONArray();
          for (int i = 0, len = mvCollectionModel.getSize(); i < len; i++) {
              NetMvInfo mvInfo = (NetMvInfo) mvCollectionModel.get(i);
  
              JSONObject jsonObject = new JSONObject();
              jsonObject.put(ConfigConstants.NET_MV_SOURCE, mvInfo.getSource());
              jsonObject.put(ConfigConstants.NET_MV_TYPE, mvInfo.getType());
              jsonObject.put(ConfigConstants.NET_MV_ID, mvInfo.getId());
              jsonObject.put(ConfigConstants.NET_MV_BVID, mvInfo.getBvid());
              jsonObject.put(ConfigConstants.NET_MV_NAME, mvInfo.getName());
              jsonObject.put(ConfigConstants.NET_MV_ARTIST, mvInfo.getArtist());
              jsonObject.put(ConfigConstants.NET_MV_CREATOR_ID, mvInfo.getCreatorId());
              jsonObject.put(ConfigConstants.NET_MV_DURATION, mvInfo.getDuration());
              jsonObject.put(ConfigConstants.NET_MV_PUB_TIME, mvInfo.getPubTime());
              jsonObject.put(ConfigConstants.NET_MV_COVER_IMG_URL, mvInfo.getCoverImgUrl());
              jsonObject.put(ConfigConstants.NET_MV_PLAY_COUNT, mvInfo.getPlayCount());
              mvCollectionJsonArray.add(jsonObject);
          }
          config.put(ConfigConstants.MV_COLLECTION, mvCollectionJsonArray);
  
          // 存入收藏榜单列表
          JSONArray rankingCollectionJsonArray = new JSONArray();
          for (int i = 0, len = rankingCollectionModel.getSize(); i < len; i++) {
              NetRankingInfo rankingInfo = (NetRankingInfo) rankingCollectionModel.get(i);
  
              JSONObject jsonObject = new JSONObject();
              jsonObject.put(ConfigConstants.NET_RANKING_SOURCE, rankingInfo.getSource());
              jsonObject.put(ConfigConstants.NET_RANKING_ID, rankingInfo.getId());
              jsonObject.put(ConfigConstants.NET_RANKING_NAME, rankingInfo.getName());
              jsonObject.put(ConfigConstants.NET_RANKING_DESCRIPTION, rankingInfo.getDescription());
              jsonObject.put(ConfigConstants.NET_RANKING_PLAY_COUNT, rankingInfo.getPlayCount());
              jsonObject.put(ConfigConstants.NET_RANKING_UPDATE_FRE, rankingInfo.getUpdateFre());
              jsonObject.put(ConfigConstants.NET_RANKING_UPDATE_TIME, rankingInfo.getUpdateTime());
              jsonObject.put(ConfigConstants.NET_RANKING_COVER_IMG_URL, rankingInfo.getCoverImgUrl());
              rankingCollectionJsonArray.add(jsonObject);
          }
          config.put(ConfigConstants.RANKING_COLLECTION, rankingCollectionJsonArray);
  
          // 存入收藏用户列表
          JSONArray userCollectionJsonArray = new JSONArray();
          for (int i = 0, len = userCollectionModel.getSize(); i < len; i++) {
              NetUserInfo userInfo = (NetUserInfo) userCollectionModel.get(i);
  
              JSONObject jsonObject = new JSONObject();
              jsonObject.put(ConfigConstants.NET_USER_SOURCE, userInfo.getSource());
              jsonObject.put(ConfigConstants.NET_USER_ID, userInfo.getId());
              jsonObject.put(ConfigConstants.NET_USER_NAME, userInfo.getName());
              jsonObject.put(ConfigConstants.NET_USER_GENDER, userInfo.getGender());
              jsonObject.put(ConfigConstants.NET_USER_AVATAR_URL, userInfo.getAvatarUrl());
              jsonObject.put(ConfigConstants.NET_USER_AVATAR_THUMB_URL, userInfo.getAvatarThumbUrl());
              jsonObject.put(ConfigConstants.NET_USER_FOLLOW, userInfo.getFollow());
              jsonObject.put(ConfigConstants.NET_USER_FAN, userInfo.getFan());
              jsonObject.put(ConfigConstants.NET_USER_PLAYLIST_COUNT, userInfo.getPlaylistCount());
              jsonObject.put(ConfigConstants.NET_USER_RADIO_COUNT, userInfo.getRadioCount());
              jsonObject.put(ConfigConstants.NET_USER_PROGRAM_COUNT, userInfo.getProgramCount());
              userCollectionJsonArray.add(jsonObject);
          }
          config.put(ConfigConstants.USER_COLLECTION, userCollectionJsonArray);
      }
  
      // 开始更新
      private void startUpdate(boolean mute, String keyMD5) {
          TerminalUtil.updater(keyMD5);
          if (mute) System.exit(0);
          else exit();
      }
  
      // 检查更新
      private void checkUpdate(boolean mute) {
          globalExecutor.execute(() -> {
              TipDialog td = null;
              if (!mute) td = new TipDialog(THIS, UPDATE_CHECKING_MSG, 0);
              try {
                  if (!mute) td.showDialog();
                  String body = HttpRequest.get(SoftInfo.RELEASE).executeAsync().body();
                  if (!mute) td.close();
                  Document doc = Jsoup.parse(body);
                  String latest = doc.select("h1.d-inline.mr-3").first().text().split(" ")[1], now = SoftInfo.VERSION;
                  String keyMD5 = doc.select("tbody tr td").last().text();
                  Elements li = doc.select(".markdown-body.my-3 ul").first().select("li");
                  StringJoiner sj = new StringJoiner("\n");
                  li.forEach(l -> sj.add("- " + l.text()));
                  String detail = sj.toString();
                  String updateInfo = StringUtil.notEmpty(detail) ? UPDATE_INFO_MSG + detail : "";
                  // 有新版本
                  if (latest.compareTo(now) > 0) {
                      File packageFile = new File(SimplePath.TEMP_PATH + SoftInfo.PACKAGE_FILE_NAME);
                      boolean packageFileValid = packageFile.exists() && keyMD5.equalsIgnoreCase(CryptoUtil.md5(packageFile));
                      // 启动时，若更新包存在且 MD5 值相同，直接更新
                      if (mute && packageFileValid) startUpdate(true, keyMD5);
                      // 弹出更新提示框
                      ConfirmDialog d = new ConfirmDialog(THIS, String.format(UPDATE_MSG, latest, now) + updateInfo,
                              YES, NO, TO_RELEASE, mute, IGNORE_UPDATE_MSG);
                      d.showDialog();
                      int response = d.getResponse();
                      boolean checked = d.isChecked();
                      if (mute && checked) autoUpdate = false;
                      // 在线更新
                      if (response == JOptionPane.YES_OPTION) {
                          UpdateDialog ud = null;
                          // 若更新包不存在或 MD5 值有差异，先开启下载框进行下载
                          if (!packageFileValid) {
                              ud = new UpdateDialog(THIS, keyMD5);
                              ud.showDialog();
                          }
                          // 更新包已存在，或者下载成功，询问是否重启
                          if (ud == null || !ud.isCanceled()) {
                              ConfirmDialog cd = new ConfirmDialog(THIS, UPDATE_READY_MSG, RESTART_NOW, LATER);
                              cd.showDialog();
                              int resp = cd.getResponse();
                              if (resp == JOptionPane.YES_OPTION) startUpdate(false, keyMD5);
                          }
                      } else if (response == JOptionPane.CANCEL_OPTION) releaseMenuItem.doClick();
                  }
                  // 已是最新版
                  else if (!mute) {
                      ConfirmDialog d = new ConfirmDialog(THIS, String.format(LATEST_MSG, latest, now) + updateInfo, OK);
                      d.showDialog();
                  }
              } catch (Exception ex) {
                  if (!mute) new TipDialog(THIS, UPDATE_CHECK_FAILED_MSG).showDialog();
              } finally {
                  if (!mute) td.close();
              }
          });
      }
  
      // 初始化托盘
      private void initTray() {
          // 显示图片必须设置
          trayIconImg.setImageAutoSize(true);
          openMainFrameMenuItem.addActionListener(e -> {
              if (videoDialog != null || miniDialog != null) return;
              // 从托盘还原窗口
              setExtendedState(NORMAL);
              if (showSpectrum) openSpectrum();
              setVisible(true);
          });
          exitMenuItem.addActionListener(e -> {
              // 移除托盘图标、保存数据并退出
              exit();
          });
          trayPopupMenu.add(openMainFrameMenuItem);
          trayPopupMenu.addSeparator();
          trayPopupMenu.add(exitMenuItem);
          searchButton.addFocusListener(new FocusAdapter() {
              @Override
              public void focusLost(FocusEvent e) {
                  trayPopupMenu.setVisible(false);
              }
          });
          trayIconImg.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  int btn = e.getButton();
                  if (btn == MouseEvent.BUTTON1) {
                      if (videoDialog != null || miniDialog != null) return;
                      // 从托盘还原窗口
                      setExtendedState(NORMAL);
                      if (showSpectrum) openSpectrum();
                      setVisible(true);
                  }
                  // 右键弹出菜单
                  else if (btn == MouseEvent.BUTTON3) {
                      if (!e.isPopupTrigger()) return;
                      int h = trayPopupMenu.getHeight();
                      trayPopupMenu.setLocation(e.getX(), e.getY() - (h == 0 ? 89 : h));
                      trayPopupMenu.setInvoker(trayPopupMenu);
                      trayPopupMenu.setVisible(true);
                      searchButton.requestFocus();
                  }
              }
          });
      }
  
      // 初始主化菜单
      private void initMainMenu() {
          initOpenFile();
          initOpenDir();
          initIndividuationMenu();
  
          closeSong.setEnabled(false);
          closeSong.addActionListener(e -> unload());
          clearCache.addActionListener(e -> {
              ConfirmDialog confirmDialog = new ConfirmDialog(THIS,
                      String.format(ASK_CLEAR_CACHE_MSG, FileUtil.getUnitString(FileUtil.size(new File(SimplePath.CACHE_PATH)))), YES, NO);
              confirmDialog.showDialog();
              int response = confirmDialog.getResponse();
              if (response != JOptionPane.YES_OPTION) return;
              clearCache();
              new TipDialog(THIS, CLEAR_CACHE_SUCCESS_MSG).showDialog();
          });
          settingMenuItem.addActionListener(e -> new SettingDialog(THIS).showDialog());
          donateMenuItem.addActionListener(e -> new DonateDialog(THIS).showDialog());
          releaseMenuItem.addActionListener(e -> DesktopUtil.browse(SoftInfo.WEBSITE));
          updateMenuItem.addActionListener(e -> checkUpdate(false));
          helpMenuItem.addActionListener(e -> new ConfirmDialog(THIS, HELP_MSG, OK).showDialog());
          aboutMenuItem.addActionListener(e -> new AboutDialog(THIS).showDialog());
  
          mainMenu.add(closeSong);
          mainMenu.add(clearCache);
          mainMenu.addSeparator();
          mainMenu.add(settingMenuItem);
          mainMenu.addSeparator();
          mainMenu.add(donateMenuItem);
          mainMenu.add(releaseMenuItem);
          mainMenu.add(updateMenuItem);
          mainMenu.add(helpMenuItem);
          mainMenu.add(aboutMenuItem);
      }
  
      // 打开文件
      private void initOpenFile() {
          FileChooser fileChooser = new FileChooser();
          fileChooser.setTitle(CHOOSE_TRACK_FILE);
          ObservableList<FileChooser.ExtensionFilter> filters = fileChooser.getExtensionFilters();
          StringBuilder allSuffix = new StringBuilder();
          for (String suffix : Format.AUDIO_TYPE_SUPPORTED) {
              filters.add(new FileChooser.ExtensionFilter(suffix.toUpperCase(), "*." + suffix));
              allSuffix.append("*.").append(suffix).append(";");
          }
          filters.add(0, new FileChooser.ExtensionFilter(AUDIO_FILE, allSuffix.toString()));
          // 添加歌曲菜单项也是同一个监听器
          addFileMenuItem.addActionListener(e -> {
              Platform.runLater(() -> {
                  List<File> files = fileChooser.showOpenMultipleDialog(null);
                  if (files == null) return;
                  // 添加选中的多个不重复的文件
                  int audioFileCount = 0;
                  for (File file : files) {
                      if (file.exists()) {
                          AudioFile audioFile = new AudioFile(file);
                          globalExecutor.execute(() -> {
                              MediaUtil.fillAudioFileInfo(audioFile);
                              musicList.repaint();
                          });
                          musicListModel.addElement(audioFile);
                          audioFileCount++;
                      }
                  }
                  boolean f = musicList.getModel() == filterModel;
                  filterPersonalMusic();
                  if (!f) {
                      musicList.setModel(musicListModel);
                      countLabel.setText(String.format(TOTAL_MSG, musicList.getModel().getSize()));
                  }
                  String msg = "成功添加 " + audioFileCount + " 首歌曲";
                  new TipDialog(THIS, msg).showDialog();
              });
          });
      }
  
      // 打开歌曲文件夹
      private void initOpenDir() {
          DirectoryChooser dirChooser = new DirectoryChooser();
          dirChooser.setTitle(CHOOSE_TRACK_FOLDER);
          // 添加歌曲文件夹菜单项也是同一个监听器
          addDirMenuItem.addActionListener(e -> {
              Platform.runLater(() -> {
                  File dir = dirChooser.showDialog(null);
                  if (dir == null) return;
                  // 文件夹不存在直接跳出
                  if (!dir.exists()) return;
                  // 歌曲列表不为空时，询问是否保留原歌曲列表
                  ListModel model = musicList.getModel();
                  if (musicListModel.getSize() != 0) {
                      ConfirmDialog confirmDialog = new ConfirmDialog(THIS,
                              ASK_RETAIN_MUSIC_LIST_MSG, YES, NO, CANCEL);
                      confirmDialog.showDialog();
                      int response = confirmDialog.getResponse();
                      if (response == JOptionPane.NO_OPTION) {
                          musicList.setModel(emptyListModel);
                          musicListModel.clear();
                      } else if (response == JOptionPane.CANCEL_OPTION) return;
                  }
                  // 添加到歌曲目录
                  if (!catalogs.contains(dir)) catalogs.add(dir);
  
                  File[] files = dir.listFiles();
                  int audioFileCount = 0;
                  for (File file : files) {
                      // 支持这种文件格式才添加
                      if (!player.support(FileUtil.getSuffix(file))) continue;
                      audioFileCount++;
                      AudioFile audioFile = new AudioFile(file);
                      globalExecutor.execute(() -> {
                          MediaUtil.fillAudioFileInfo(audioFile);
                          musicList.repaint();
                      });
                      musicListModel.addElement(audioFile);
                  }
                  musicList.setModel(model);
                  boolean f = model == filterModel;
                  filterPersonalMusic();
                  if (!f) {
                      musicList.setModel(musicListModel);
                      countLabel.setText(String.format(TOTAL_MSG, musicList.getModel().getSize()));
                  }
                  String msg = String.format(TRACK_ADDED, audioFileCount);
                  new TipDialog(THIS, msg).showDialog();
              });
          });
      }
  
      // 初始化个性化菜单
      private void initIndividuationMenu() {
          // 自定义主题
          styleCustomMenuItem.addActionListener(e -> customStyle());
          manageStyleMenuItem.addActionListener(e -> {
              ManageCustomStyleDialog dialog = new ManageCustomStyleDialog(THIS);
              dialog.showDialog();
          });
      }
  
      // 初始化侧边栏大小
      public void updateTabSize() {
          Dimension d = new Dimension(showTabText ? 125 : 43, 42);
          personalMusicPanel.setPreferredSize(d);
          netMusicPanel.setPreferredSize(d);
          netPlaylistPanel.setPreferredSize(d);
          netAlbumPanel.setPreferredSize(d);
          netArtistPanel.setPreferredSize(d);
          netRadioPanel.setPreferredSize(d);
          netMvPanel.setPreferredSize(d);
          netRankingPanel.setPreferredSize(d);
          netUserPanel.setPreferredSize(d);
          recommendPanel.setPreferredSize(d);
          downloadManagementPanel.setPreferredSize(d);
          playQueuePanel.setPreferredSize(d);
  
          personalMusicLabel.setText(showTabText ? I18n.getText("personalMusic") : "");
          netMusicLabel.setText(showTabText ? I18n.getText("netMusic") : "");
          netPlaylistLabel.setText(showTabText ? I18n.getText("netPlaylist") : "");
          netAlbumLabel.setText(showTabText ? I18n.getText("netAlbum") : "");
          netArtistLabel.setText(showTabText ? I18n.getText("netArtist") : "");
          netRadioLabel.setText(showTabText ? I18n.getText("netRadio") : "");
          netMvLabel.setText(showTabText ? I18n.getText("netMv") : "");
          netRankingLabel.setText(showTabText ? I18n.getText("netRanking") : "");
          netUserLabel.setText(showTabText ? I18n.getText("netUser") : "");
          recommendLabel.setText(showTabText ? I18n.getText("netRecommend") : "");
          downloadManagementLabel.setText(showTabText ? I18n.getText("downloadManagement") : "");
          playQueueLabel.setText(showTabText ? I18n.getText("playQueue") : "");
      }
  
      // 初始化标签页
      private void initTabbedPane() {
          int gap = 10;
          personalMusicLabel.setIconTextGap(gap);
          netMusicLabel.setIconTextGap(gap);
          netPlaylistLabel.setIconTextGap(gap);
          netAlbumLabel.setIconTextGap(gap);
          netArtistLabel.setIconTextGap(gap);
          netRadioLabel.setIconTextGap(gap);
          netMvLabel.setIconTextGap(gap);
          netRankingLabel.setIconTextGap(gap);
          netUserLabel.setIconTextGap(gap);
          recommendLabel.setIconTextGap(gap);
          downloadManagementLabel.setIconTextGap(gap);
          playQueueLabel.setIconTextGap(gap);
  
          personalMusicPanel.add(personalMusicLabel);
          netMusicPanel.add(netMusicLabel);
          netPlaylistPanel.add(netPlaylistLabel);
          netAlbumPanel.add(netAlbumLabel);
          netArtistPanel.add(netArtistLabel);
          netRadioPanel.add(netRadioLabel);
          netMvPanel.add(netMvLabel);
          netRankingPanel.add(netRankingLabel);
          netUserPanel.add(netUserLabel);
          recommendPanel.add(recommendLabel);
          downloadManagementPanel.add(downloadManagementLabel);
          playQueuePanel.add(playQueueLabel);
  
          // 标签布局
          FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
          personalMusicPanel.setLayout(fl);
          netMusicPanel.setLayout(fl);
          netPlaylistPanel.setLayout(fl);
          netAlbumPanel.setLayout(fl);
          netArtistPanel.setLayout(fl);
          netRadioPanel.setLayout(fl);
          netMvPanel.setLayout(fl);
          netRankingPanel.setLayout(fl);
          netUserPanel.setLayout(fl);
          recommendPanel.setLayout(fl);
          downloadManagementPanel.setLayout(fl);
          playQueuePanel.setLayout(fl);
  
          // 添加标签对应的内容
          tabbedPane.addTab(null, null, leftBox);
          tabbedPane.addTab(null, null, recommendLeftBox);
          tabbedPane.addTab(null, null, netRankingLeftBox);
          tabbedPane.addTab(null, null, netLeftBox);
          tabbedPane.addTab(null, null, netPlaylistLeftBox);
          tabbedPane.addTab(null, null, netAlbumLeftBox);
          tabbedPane.addTab(null, null, netArtistLeftBox);
          tabbedPane.addTab(null, null, netRadioLeftBox);
          tabbedPane.addTab(null, null, netMvLeftBox);
          tabbedPane.addTab(null, null, netUserLeftBox);
          tabbedPane.addTab(null, null, downloadLeftBox);
          tabbedPane.addTab(null, null, playQueueLeftBox);
          // 自定义标签面板
          tabbedPane.setTabComponentAt(TabIndex.PERSONAL, personalMusicPanel);
          tabbedPane.setTabComponentAt(TabIndex.RECOMMEND, recommendPanel);
          tabbedPane.setTabComponentAt(TabIndex.NET_RANKING, netRankingPanel);
          tabbedPane.setTabComponentAt(TabIndex.NET_MUSIC, netMusicPanel);
          tabbedPane.setTabComponentAt(TabIndex.NET_PLAYLIST, netPlaylistPanel);
          tabbedPane.setTabComponentAt(TabIndex.NET_ALBUM, netAlbumPanel);
          tabbedPane.setTabComponentAt(TabIndex.NET_ARTIST, netArtistPanel);
          tabbedPane.setTabComponentAt(TabIndex.NET_RADIO, netRadioPanel);
          tabbedPane.setTabComponentAt(TabIndex.NET_MV, netMvPanel);
          tabbedPane.setTabComponentAt(TabIndex.NET_USER, netUserPanel);
          tabbedPane.setTabComponentAt(TabIndex.DOWNLOAD_MANAGEMENT, downloadManagementPanel);
          tabbedPane.setTabComponentAt(TabIndex.PLAY_QUEUE, playQueuePanel);
  
          tabbedPane.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  updateTabUI(tabbedPane, currUIStyle, e.getPoint());
              }
          });
          tabbedPane.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  updateTabUI(tabbedPane, currUIStyle);
              }
          });
          // 改变歌曲列表状态
          tabbedPane.addChangeListener(e -> {
              int selectedIndex = tabbedPane.getSelectedIndex();
              // 根据选项卡选择的情况设置选项卡文字 + 图标颜色
              updateTabUI(tabbedPane, currUIStyle);
              // 个人音乐
              if (selectedIndex == TabIndex.PERSONAL) {
                  int index = collectionTabbedPane.getSelectedIndex();
  
                  DefaultListModel model = null;
                  if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC) model = musicListModel;
                  else if (currPersonalMusicTab == PersonalMusicTabIndex.HISTORY) model = historyModel;
                  else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION) {
                      if (index == CollectionTabIndex.MUSIC) model = collectionModel;
                      else if (index == CollectionTabIndex.PLAYLIST) model = playlistCollectionModel;
                      else if (index == CollectionTabIndex.ALBUM) model = albumCollectionModel;
                      else if (index == CollectionTabIndex.ARTIST) model = artistCollectionModel;
                      else if (index == CollectionTabIndex.RADIO) model = radioCollectionModel;
                      else if (index == CollectionTabIndex.MV) model = mvCollectionModel;
                      else if (index == CollectionTabIndex.RANKING) model = rankingCollectionModel;
                      else if (index == CollectionTabIndex.USER) model = userCollectionModel;
                  }
                  boolean modelEmpty = model.isEmpty();
  
                  if (currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION) {
                      if (modelEmpty) {
                          leftBox.remove(musicScrollPane);
                          leftBox.add(emptyHintPanel);
                      } else {
                          musicList.setModel(model);
                          leftBox.remove(emptyHintPanel);
                          leftBox.add(musicScrollPane);
                      }
                  } else if (index == CollectionTabIndex.MUSIC) {
                      if (modelEmpty) {
                          musicCollectionLeftBox.remove(musicScrollPane);
                          musicCollectionLeftBox.add(emptyHintPanel);
                      } else {
                          musicCollectionLeftBox.remove(emptyHintPanel);
                          musicCollectionLeftBox.add(musicScrollPane);
                      }
                  } else {
                      Box box = null;
                      if (index == CollectionTabIndex.PLAYLIST) box = playlistCollectionLeftBox;
                      else if (index == CollectionTabIndex.ALBUM) box = albumCollectionLeftBox;
                      else if (index == CollectionTabIndex.ARTIST) box = artistCollectionLeftBox;
                      else if (index == CollectionTabIndex.RADIO) box = radioCollectionLeftBox;
                      else if (index == CollectionTabIndex.MV) box = mvCollectionLeftBox;
                      else if (index == CollectionTabIndex.RANKING) box = rankingCollectionLeftBox;
                      else if (index == CollectionTabIndex.USER) box = userCollectionLeftBox;
                      if (modelEmpty) {
                          box.remove(collectionScrollPane);
                          box.add(emptyHintPanel);
                      } else {
                          collectionList.setModel(model);
                          box.remove(emptyHintPanel);
                          box.add(collectionScrollPane);
                      }
                  }
  
                  if (filterTextField.isOccupied()) filterPersonalMusic();
  
                  if (collectionBackwardButton.isShowing()) {
                      if (index == CollectionTabIndex.PLAYLIST) {
                          // 显示收藏歌单的歌曲列表
                          netMusicList.setModel(netMusicListForPlaylistCollectionModel);
                      } else if (index == CollectionTabIndex.ALBUM) {
                          // 显示收藏专辑的歌曲列表
                          netMusicList.setModel(netMusicListForAlbumCollectionModel);
                      } else if (index == CollectionTabIndex.ARTIST) {
                          // 显示收藏歌手的歌曲列表
                          netMusicList.setModel(netMusicListForArtistCollectionModel);
                      } else if (index == CollectionTabIndex.RADIO) {
                          // 显示收藏电台的歌曲列表
                          netMusicList.setModel(netMusicListForRadioCollectionModel);
                      } else if (index == CollectionTabIndex.RANKING) {
                          // 显示收藏榜单的歌曲列表
                          netMusicList.setModel(netMusicListForRankingCollectionModel);
                      } else if (index == CollectionTabIndex.USER) {
                          // 显示收藏用户的歌曲列表
                          netMusicList.setModel(netMusicListForUserCollectionModel);
                      }
                      checkDescriptionCollectionStatus(collectionItemDescriptionCollectionButton, collectionList.getSelectedValue());
                      if (netMusicList.getModel().getSize() == 0) {
                          collectionItemListCountBox.remove(netMusicScrollPane);
                          collectionItemListCountBox.add(emptyHintPanel);
                      } else {
                          collectionItemListCountBox.remove(emptyHintPanel);
                          collectionItemListCountBox.add(netMusicScrollPane);
                      }
                      collectionItemListBox.add(collectionItemListCountBox);
                      collectionLeftBox.add(collectionItemListBox);
                  }
              }
              // 在线音乐
              else if (selectedIndex == TabIndex.NET_MUSIC) {
                  updateRenderer(netMusicList);
                  netMusicList.setModel(netMusicListModel);
                  // 首次切到在线音乐标签页时加载热搜
                  if (netMusicListModel.isEmpty() && netMusicHotSearchInnerPanel2.getComponentCount() == 0) {
                      netLeftBox.remove(netMusicScrollPane);
                      netLeftBox.add(netMusicKeywordsPanelScrollPane);
                      globalExecutor.execute(() -> updateHotSearch());
                  } else if (netMusicHotSearchInnerPanel2.getComponentCount() == 0) {
                      globalExecutor.execute(() -> updateHotSearch());
                  } else if (!netMusicListModel.isEmpty()) {
                      netLeftBox.remove(netMusicKeywordsPanelScrollPane);
                      netLeftBox.add(netMusicScrollPane);
                  }
              }
              // 在线歌单音乐
              else if (selectedIndex == TabIndex.NET_PLAYLIST) {
                  // 显示歌单的歌曲列表
                  if (netPlaylistLeftBox.getComponent(netPlaylistLeftBox.getComponentCount() - 1) == playlistListBox) {
                      updateRenderer(netMusicList);
                      netMusicList.setModel(netMusicListForPlaylistModel);
                      checkDescriptionCollectionStatus(playlistDescriptionCollectionButton, netPlaylistList.getSelectedValue());
                      if (netMusicListForPlaylistModel.isEmpty()) playlistListCountBox.add(emptyHintPanel);
                      else playlistListCountBox.add(netMusicScrollPane);
                      playlistListBox.add(playlistListCountBox);
                      netPlaylistLeftBox.add(playlistListBox);
                  }
                  // 显示歌单列表
                  else if (netPlaylistBackwardButton.isEnabled()) {
                      netPlaylistLeftBox.remove(playlistListBox);
                      if (netPlaylistListModel.isEmpty()) netPlaylistLeftBox.add(emptyHintPanel);
                      else netPlaylistLeftBox.add(netPlaylistScrollPane);
                  }
                  // 显示关键词面板
                  else {
                      netPlaylistLeftBox.remove(netPlaylistScrollPane);
                      netPlaylistLeftBox.add(netPlaylistKeywordsPanelScrollPane);
                  }
              }
              // 在线专辑音乐
              else if (selectedIndex == TabIndex.NET_ALBUM) {
                  // 显示专辑的歌曲列表
                  if (netAlbumLeftBox.getComponent(netAlbumLeftBox.getComponentCount() - 1) == albumListBox) {
                      updateRenderer(netMusicList);
                      netMusicList.setModel(netMusicListForAlbumModel);
                      checkDescriptionCollectionStatus(albumDescriptionCollectionButton, netAlbumList.getSelectedValue());
                      if (netMusicListForAlbumModel.isEmpty()) albumListCountBox.add(emptyHintPanel);
                      else albumListCountBox.add(netMusicScrollPane);
                      albumListBox.add(albumListCountBox);
                      netAlbumLeftBox.add(albumListBox);
                  }
                  // 显示专辑列表
                  else if (netAlbumBackwardButton.isEnabled()) {
                      netAlbumLeftBox.remove(albumListBox);
                      if (netAlbumListModel.isEmpty()) netAlbumLeftBox.add(emptyHintPanel);
                      else netAlbumLeftBox.add(netAlbumScrollPane);
                  }
                  // 显示关键词面板
                  else {
                      netAlbumLeftBox.remove(netAlbumScrollPane);
                      netAlbumLeftBox.add(netAlbumKeywordsPanelScrollPane);
                  }
              }
              // 歌手音乐
              else if (selectedIndex == TabIndex.NET_ARTIST) {
                  // 显示歌手的歌曲列表
                  if (netArtistLeftBox.getComponent(netArtistLeftBox.getComponentCount() - 1) == artistListBox) {
                      updateRenderer(netMusicList);
                      netMusicList.setModel(netMusicListForArtistModel);
                      checkDescriptionCollectionStatus(artistDescriptionCollectionButton, netArtistList.getSelectedValue());
                      if (netMusicListForArtistModel.isEmpty()) artistListCountBox.add(emptyHintPanel);
                      else artistListCountBox.add(netMusicScrollPane);
                      artistListBox.add(artistListCountBox);
                      netArtistLeftBox.add(artistListBox);
                  }
                  // 显示歌手列表
                  else if (netArtistBackwardButton.isEnabled()) {
                      netArtistLeftBox.remove(artistListBox);
                      if (netArtistListModel.isEmpty()) netArtistLeftBox.add(emptyHintPanel);
                      else netArtistLeftBox.add(netArtistScrollPane);
                  }
                  // 显示关键词面板
                  else {
                      netArtistLeftBox.remove(netArtistScrollPane);
                      netArtistLeftBox.add(netArtistKeywordsPanelScrollPane);
                  }
              }
              // 电台音乐
              else if (selectedIndex == TabIndex.NET_RADIO) {
                  // 显示电台的歌曲列表
                  if (netRadioLeftBox.getComponent(netRadioLeftBox.getComponentCount() - 1) == radioListBox) {
                      updateRenderer(netMusicList);
                      netMusicList.setModel(netMusicListForRadioModel);
                      checkDescriptionCollectionStatus(radioDescriptionCollectionButton, netRadioList.getSelectedValue());
                      if (netMusicListForRadioModel.isEmpty()) radioListCountBox.add(emptyHintPanel);
                      else radioListCountBox.add(netMusicScrollPane);
                      radioListBox.add(radioListCountBox);
                      netRadioLeftBox.add(radioListBox);
                  }
                  // 显示电台列表
                  else if (netRadioBackwardButton.isEnabled()) {
                      netRadioLeftBox.remove(radioListBox);
                      if (netRadioListModel.isEmpty()) netRadioLeftBox.add(emptyHintPanel);
                      else netRadioLeftBox.add(netRadioScrollPane);
                  }
                  // 显示关键词面板
                  else {
                      netRadioLeftBox.remove(netRadioScrollPane);
                      netRadioLeftBox.add(netRadioKeywordsPanelScrollPane);
                  }
              }
              // MV
              else if (selectedIndex == TabIndex.NET_MV) {
                  if (netMvBackwardButton.isEnabled()) {
                      if (netMvListModel.isEmpty()) netMvLeftBox.add(emptyHintPanel);
                      else netMvLeftBox.add(netMvScrollPane);
                  } else {
                      netMvLeftBox.remove(netMvScrollPane);
                      netMvLeftBox.add(netMvKeywordsPanelScrollPane);
                  }
              }
              // 榜单音乐
              else if (selectedIndex == TabIndex.NET_RANKING) {
                  if (netRankingListModel.isEmpty()) {
                      getRankingAction.run();
                      return;
                  }
                  // 显示榜单的歌曲列表
                  if (netRankingLeftBox.getComponent(netRankingLeftBox.getComponentCount() - 1) == rankingListBox) {
                      netMusicList.setModel(netMusicListForRankingModel);
                      checkDescriptionCollectionStatus(rankingDescriptionCollectionButton, netRankingList.getSelectedValue());
                      if (netMusicListForRankingModel.isEmpty()) rankingListCountBox.add(emptyHintPanel);
                      else rankingListCountBox.add(netMusicScrollPane);
                      rankingListBox.add(rankingListCountBox);
                      netRankingLeftBox.add(rankingListBox);
                  }
                  // 显示榜单列表
                  else {
                      netRankingLeftBox.remove(rankingListBox);
                      if (netRankingListModel.isEmpty()) netRankingLeftBox.add(emptyHintPanel);
                      else netRankingLeftBox.add(netRankingScrollPane);
                  }
              }
              // 用户音乐
              else if (selectedIndex == TabIndex.NET_USER) {
                  // 显示用户的歌曲列表
                  if (netUserLeftBox.getComponent(netUserLeftBox.getComponentCount() - 1) == userListBox) {
                      updateRenderer(netMusicList);
                      netMusicList.setModel(netMusicListForUserModel);
                      checkDescriptionCollectionStatus(userDescriptionCollectionButton, netUserList.getSelectedValue());
                      if (netMusicListForUserModel.isEmpty()) userListCountBox.add(emptyHintPanel);
                      else userListCountBox.add(netMusicScrollPane);
                      userListBox.add(userListCountBox);
                      netUserLeftBox.add(userListBox);
                  }
                  // 显示用户列表
                  else if (netUserBackwardButton.isEnabled()) {
                      netUserLeftBox.remove(userListBox);
                      if (netUserListModel.isEmpty()) netUserLeftBox.add(emptyHintPanel);
                      else netUserLeftBox.add(netUserScrollPane);
                  }
                  // 显示关键词面板
                  else {
                      netUserLeftBox.remove(netUserScrollPane);
                      netUserLeftBox.add(netUserKeywordsPanelScrollPane);
                  }
              }
              // 推荐
              else if (selectedIndex == TabIndex.RECOMMEND) {
                  if (currRecommendTab == -1) {
                      if (preRecommendTab == RecommendTabIndex.PLAYLIST_RECOMMEND) playlistRecommendButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.HIGH_QUALITY_PLAYLIST_RECOMMEND)
                          highQualityPlaylistButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.HOT_MUSIC_RECOMMEND) hotMusicButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.NEW_MUSIC_RECOMMEND) newMusicButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.NEW_ALBUM_RECOMMEND)
                          newAlbumRecommendButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.ARTIST_LIST_RECOMMEND)
                          artistListRecommendButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.NEW_RADIO_RECOMMEND)
                          newRadioRecommendButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.HOT_RADIO_RECOMMEND)
                          hotRadioRecommendButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.PROGRAM_RECOMMEND) programRecommendButton.doClick();
                      else if (preRecommendTab == RecommendTabIndex.MV_RECOMMEND) mvRecommendButton.doClick();
                      return;
                  }
                  // 加载推荐歌单/专辑/歌手/电台
                  if (currRecommendTab != RecommendTabIndex.NEW_MUSIC_RECOMMEND
                          && currRecommendTab != RecommendTabIndex.HOT_MUSIC_RECOMMEND
                          && currRecommendTab != RecommendTabIndex.PROGRAM_RECOMMEND) {
                      if (recommendBackwardButton.isEnabled()) {
                          // 显示歌单的歌曲列表
                          if (currRecommendTab == RecommendTabIndex.PLAYLIST_RECOMMEND
                                  || currRecommendTab == RecommendTabIndex.HIGH_QUALITY_PLAYLIST_RECOMMEND) {
                              netMusicList.setModel(netMusicListForPlaylistRecommendModel);
                          }
                          // 显示专辑的歌曲列表
                          else if (currRecommendTab == RecommendTabIndex.NEW_ALBUM_RECOMMEND) {
                              netMusicList.setModel(netMusicListForAlbumRecommendModel);
                          }
                          // 显示歌手的歌曲列表
                          else if (currRecommendTab == RecommendTabIndex.ARTIST_LIST_RECOMMEND) {
                              netMusicList.setModel(netMusicListForArtistRecommendModel);
                          }
                          // 显示电台的歌曲列表
                          else if (currRecommendTab == RecommendTabIndex.HOT_RADIO_RECOMMEND
                                  || currRecommendTab == RecommendTabIndex.NEW_RADIO_RECOMMEND) {
                              netMusicList.setModel(netMusicListForRadioRecommendModel);
                          }
                          checkDescriptionCollectionStatus(recommendItemDescriptionCollectionButton, itemRecommendList.getSelectedValue());
                          if (netMusicList.getModel().getSize() == 0) recommendItemListCountBox.add(emptyHintPanel);
                          else recommendItemListCountBox.add(netMusicScrollPane);
                          recommendItemListBox.add(recommendItemListCountBox);
                          recommendLeftBox.add(recommendItemListBox);
                      } else {
                          if (itemRecommendList.getModel().getSize() == 0) recommendLeftBox.add(emptyHintPanel);
                          else recommendLeftBox.add(itemRecommendScrollPane);
                      }
                  }
                  // 加载推荐音乐/节目
                  else {
                      netMusicList.setModel(netMusicRecommendListModel);
                      if (netMusicRecommendListModel.isEmpty()) recommendLeftBox.add(emptyHintPanel);
                      else recommendLeftBox.add(netMusicScrollPane);
                  }
              }
              // 下载管理
              else if (selectedIndex == TabIndex.DOWNLOAD_MANAGEMENT) {
                  if (downloadListModel.isEmpty()) {
                      downloadLeftBox.remove(downloadListScrollPane);
                      downloadLeftBox.add(emptyHintPanel);
                  }
              }
              // 播放队列
              else if (selectedIndex == TabIndex.PLAY_QUEUE) {
                  if (playQueueModel.isEmpty()) {
                      playQueueLeftBox.remove(playQueueScrollPane);
                      playQueueLeftBox.add(emptyHintPanel);
                  }
              }
              playQueue.setCellRenderer(selectedIndex == TabIndex.PLAY_QUEUE ? playQueueRenderer : null);
          });
  
          globalPanel.add(tabbedPane, BorderLayout.CENTER);
      }
  
      // 初始化收藏标签页
      private void initCollectionTabbedPane() {
          // 组装标签面板
          int gap = 10;
          musicCollectionLabel.setIconTextGap(gap);
          musicCollectionPanel.add(musicCollectionLabel);
  
          playlistCollectionLabel.setIconTextGap(gap);
          playlistCollectionPanel.add(playlistCollectionLabel);
  
          albumCollectionLabel.setIconTextGap(gap);
          albumCollectionPanel.add(albumCollectionLabel);
  
          artistCollectionLabel.setIconTextGap(gap);
          artistCollectionPanel.add(artistCollectionLabel);
  
          radioCollectionLabel.setIconTextGap(gap);
          radioCollectionPanel.add(radioCollectionLabel);
  
          mvCollectionLabel.setIconTextGap(gap);
          mvCollectionPanel.add(mvCollectionLabel);
  
          rankingCollectionLabel.setIconTextGap(gap);
          rankingCollectionPanel.add(rankingCollectionLabel);
  
          userCollectionLabel.setIconTextGap(gap);
          userCollectionPanel.add(userCollectionLabel);
  
          // 添加标签对应的内容
          collectionTabbedPane.addTab(null, null, musicCollectionLeftBox);
          collectionTabbedPane.addTab(null, null, playlistCollectionLeftBox);
          collectionTabbedPane.addTab(null, null, rankingCollectionLeftBox);
          collectionTabbedPane.addTab(null, null, albumCollectionLeftBox);
          collectionTabbedPane.addTab(null, null, artistCollectionLeftBox);
          collectionTabbedPane.addTab(null, null, radioCollectionLeftBox);
          collectionTabbedPane.addTab(null, null, mvCollectionLeftBox);
          collectionTabbedPane.addTab(null, null, userCollectionLeftBox);
  
          // 自定义标签面板
          collectionTabbedPane.setTabComponentAt(CollectionTabIndex.MUSIC, musicCollectionPanel);
          collectionTabbedPane.setTabComponentAt(CollectionTabIndex.PLAYLIST, playlistCollectionPanel);
          collectionTabbedPane.setTabComponentAt(CollectionTabIndex.RANKING, rankingCollectionPanel);
          collectionTabbedPane.setTabComponentAt(CollectionTabIndex.ALBUM, albumCollectionPanel);
          collectionTabbedPane.setTabComponentAt(CollectionTabIndex.ARTIST, artistCollectionPanel);
          collectionTabbedPane.setTabComponentAt(CollectionTabIndex.RADIO, radioCollectionPanel);
          collectionTabbedPane.setTabComponentAt(CollectionTabIndex.MV, mvCollectionPanel);
          collectionTabbedPane.setTabComponentAt(CollectionTabIndex.USER, userCollectionPanel);
  
          collectionTabbedPane.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  updateTabUI(collectionTabbedPane, currUIStyle, e.getPoint());
              }
          });
          collectionTabbedPane.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  updateTabUI(collectionTabbedPane, currUIStyle);
              }
          });
          // 改变收藏列表状态
          collectionTabbedPane.addChangeListener(e -> {
              int selectedIndex = collectionTabbedPane.getSelectedIndex();
              // 根据选项卡选择的情况设置选项卡文字 + 图标颜色
              updateTabUI(collectionTabbedPane, currUIStyle);
              if (selectedIndex == CollectionTabIndex.MUSIC) {
                  musicList.setModel(collectionModel);
                  int size = collectionModel.size();
                  if (size > 0) {
                      musicCollectionLeftBox.remove(emptyHintPanel);
                      musicCollectionLeftBox.add(musicScrollPane);
                  } else {
                      musicCollectionLeftBox.remove(musicScrollPane);
                      musicCollectionLeftBox.add(emptyHintPanel);
                  }
                  countLabel.setText(String.format(TOTAL_MSG, collectionModel.size()));
              } else if (selectedIndex == CollectionTabIndex.PLAYLIST) {
                  collectionList.setModel(playlistCollectionModel);
                  int size = playlistCollectionModel.size();
                  if (size > 0) {
                      playlistCollectionLeftBox.remove(emptyHintPanel);
                      playlistCollectionLeftBox.add(collectionScrollPane);
                  } else {
                      playlistCollectionLeftBox.remove(collectionScrollPane);
                      playlistCollectionLeftBox.add(emptyHintPanel);
                  }
                  countLabel.setText(String.format(TOTAL_MSG, size));
  
                  for (int i = 0, s = playlistCollectionModel.size(); i < s; i++) {
                      int finalI = i;
                      globalExecutor.execute(() -> {
                          NetPlaylistInfo playlistInfo = (NetPlaylistInfo) playlistCollectionModel.get(finalI);
                          if (playlistInfo.isIntegrated()) return;
                          playlistInfo.setInvokeLater(() -> updateRenderer(collectionList));
                          MusicServerUtil.preloadPlaylistInfo(playlistInfo);
                      });
                  }
              } else if (selectedIndex == CollectionTabIndex.ALBUM) {
                  collectionList.setModel(albumCollectionModel);
                  int size = albumCollectionModel.size();
                  if (size > 0) {
                      albumCollectionLeftBox.remove(emptyHintPanel);
                      albumCollectionLeftBox.add(collectionScrollPane);
                  } else {
                      albumCollectionLeftBox.remove(collectionScrollPane);
                      albumCollectionLeftBox.add(emptyHintPanel);
                  }
                  countLabel.setText(String.format(TOTAL_MSG, size));
  
                  for (int i = 0, s = albumCollectionModel.size(); i < s; i++) {
                      int finalI = i;
                      globalExecutor.execute(() -> {
                          NetAlbumInfo albumInfo = (NetAlbumInfo) albumCollectionModel.get(finalI);
                          if (albumInfo.isIntegrated()) return;
                          albumInfo.setInvokeLater(() -> updateRenderer(collectionList));
                          MusicServerUtil.preloadAlbumInfo(albumInfo);
                      });
                  }
              } else if (selectedIndex == CollectionTabIndex.ARTIST) {
                  collectionList.setModel(artistCollectionModel);
                  int size = artistCollectionModel.size();
                  if (size > 0) {
                      artistCollectionLeftBox.remove(emptyHintPanel);
                      artistCollectionLeftBox.add(collectionScrollPane);
                  } else {
                      artistCollectionLeftBox.remove(collectionScrollPane);
                      artistCollectionLeftBox.add(emptyHintPanel);
                  }
                  countLabel.setText(String.format(TOTAL_MSG, size));
  
                  for (int i = 0, s = artistCollectionModel.size(); i < s; i++) {
                      int finalI = i;
                      globalExecutor.execute(() -> {
                          NetArtistInfo artistInfo = (NetArtistInfo) artistCollectionModel.get(finalI);
                          if (artistInfo.isIntegrated()) return;
                          artistInfo.setInvokeLater(() -> updateRenderer(collectionList));
                          MusicServerUtil.preloadArtistInfo(artistInfo);
                      });
                  }
              } else if (selectedIndex == CollectionTabIndex.RADIO) {
                  collectionList.setModel(radioCollectionModel);
                  int size = radioCollectionModel.size();
                  if (size > 0) {
                      radioCollectionLeftBox.remove(emptyHintPanel);
                      radioCollectionLeftBox.add(collectionScrollPane);
                  } else {
                      radioCollectionLeftBox.remove(collectionScrollPane);
                      radioCollectionLeftBox.add(emptyHintPanel);
                  }
                  countLabel.setText(String.format(TOTAL_MSG, size));
  
                  for (int i = 0, s = radioCollectionModel.size(); i < s; i++) {
                      int finalI = i;
                      globalExecutor.execute(() -> {
                          NetRadioInfo radioInfo = (NetRadioInfo) radioCollectionModel.get(finalI);
                          if (radioInfo.isIntegrated()) return;
                          radioInfo.setInvokeLater(() -> updateRenderer(collectionList));
                          MusicServerUtil.preloadRadioInfo(radioInfo);
                      });
                  }
              } else if (selectedIndex == CollectionTabIndex.MV) {
                  collectionList.setModel(mvCollectionModel);
                  int size = mvCollectionModel.size();
                  if (size > 0) {
                      mvCollectionLeftBox.remove(emptyHintPanel);
                      mvCollectionLeftBox.add(collectionScrollPane);
                  } else {
                      mvCollectionLeftBox.remove(collectionScrollPane);
                      mvCollectionLeftBox.add(emptyHintPanel);
                  }
                  countLabel.setText(String.format(TOTAL_MSG, size));
  
                  for (int i = 0, s = mvCollectionModel.size(); i < s; i++) {
                      int finalI = i;
                      globalExecutor.execute(() -> {
                          NetMvInfo mvInfo = (NetMvInfo) mvCollectionModel.get(finalI);
                          if (mvInfo.isIntegrated()) return;
                          mvInfo.setInvokeLater(() -> updateRenderer(collectionList));
                          MusicServerUtil.preloadMvInfo(mvInfo);
                      });
                  }
              } else if (selectedIndex == CollectionTabIndex.RANKING) {
                  collectionList.setModel(rankingCollectionModel);
                  int size = rankingCollectionModel.size();
                  if (size > 0) {
                      rankingCollectionLeftBox.remove(emptyHintPanel);
                      rankingCollectionLeftBox.add(collectionScrollPane);
                  } else {
                      rankingCollectionLeftBox.remove(collectionScrollPane);
                      rankingCollectionLeftBox.add(emptyHintPanel);
                  }
                  countLabel.setText(String.format(TOTAL_MSG, size));
  
                  for (int i = 0, s = rankingCollectionModel.size(); i < s; i++) {
                      int finalI = i;
                      globalExecutor.execute(() -> {
                          NetRankingInfo rankingInfo = (NetRankingInfo) rankingCollectionModel.get(finalI);
                          if (rankingInfo.isIntegrated()) return;
                          rankingInfo.setInvokeLater(() -> updateRenderer(collectionList));
                          MusicServerUtil.preloadRankingInfo(rankingInfo);
                      });
                  }
              } else if (selectedIndex == CollectionTabIndex.USER) {
                  collectionList.setModel(userCollectionModel);
                  int size = userCollectionModel.size();
                  if (size > 0) {
                      userCollectionLeftBox.remove(emptyHintPanel);
                      userCollectionLeftBox.add(collectionScrollPane);
                  } else {
                      userCollectionLeftBox.remove(collectionScrollPane);
                      userCollectionLeftBox.add(emptyHintPanel);
                  }
                  countLabel.setText(String.format(TOTAL_MSG, size));
  
                  for (int i = 0, s = userCollectionModel.size(); i < s; i++) {
                      int finalI = i;
                      globalExecutor.execute(() -> {
                          NetUserInfo userInfo = (NetUserInfo) userCollectionModel.get(finalI);
                          if (userInfo.isIntegrated()) return;
                          userInfo.setInvokeLater(() -> updateRenderer(collectionList));
                          MusicServerUtil.preloadUserInfo(userInfo);
                      });
                  }
              }
              // 切换收藏标签页后筛选
              if (filterTextField.isOccupied()) filterPersonalMusic();
          });
      }
  
      // 初始化收藏工具条
      private void initCollectionToolBar() {
  //        collectionPageTextField.addFocusListener(
  //                new JTextFieldHintListener(collectionPageTextField, "", currUIStyle.getForeColor()));
          // 只能输入数字
          collectionPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 收藏后退按钮事件
          collectionBackwardButton.addActionListener(e -> {
              collectionBackwardButton.setDrawBg(false);
              // 返回到歌单/专辑/歌手/电台
              // 先清空在线音乐列表
              ((DefaultListModel) netMusicList.getModel()).clear();
              tabbedPane.setComponentAt(TabIndex.PERSONAL, leftBox);
              collectionRecordTypeComboBox.setVisible(false);
              leftBox.repaint();
          });
          // 收藏模块跳页事件，可复用
          Runnable collectionGoPageAction = () -> {
              // 搜索收藏歌单/专辑/歌手/电台里的歌
              if (collectionBackwardButton.isEnabled()) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          NetResource resource = collectionList.getSelectedValue();
                          // 这是歌单里的歌
                          if (resource instanceof NetPlaylistInfo) {
                              NetPlaylistInfo playlistInfo = (NetPlaylistInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInPlaylist(playlistInfo, netMusicInCollectionCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              // 解决数量标签文字显示不全问题
                              collectionCountPanel.add(collectionCountLabel, collectionCountPanel.getComponentIndex(collectionCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForPlaylistCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForPlaylistCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForPlaylistCollectionModel);
                              collectionLeftBox.repaint();
                          }
                          // 这是专辑里的歌
                          else if (resource instanceof NetAlbumInfo) {
                              NetAlbumInfo albumInfo = (NetAlbumInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInAlbum(albumInfo, netMusicInCollectionCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              // 解决数量标签文字显示不全问题
                              collectionCountPanel.add(collectionCountLabel, collectionCountPanel.getComponentIndex(collectionCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForAlbumCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForAlbumCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForAlbumCollectionModel);
                              collectionLeftBox.repaint();
                          }
                          // 这是歌手里的歌
                          else if (resource instanceof NetArtistInfo) {
                              NetArtistInfo artistInfo = (NetArtistInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInArtist(artistInfo, netMusicInCollectionCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              // 解决数量标签文字显示不全问题
                              collectionCountPanel.add(collectionCountLabel, collectionCountPanel.getComponentIndex(collectionCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForArtistCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForArtistCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForArtistCollectionModel);
                              collectionLeftBox.repaint();
                          }
                          // 这是电台里的歌
                          else if (resource instanceof NetRadioInfo) {
                              NetRadioInfo radioInfo = (NetRadioInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRadio(radioInfo, collectionRecordTypeComboBox.getSelectedIndex(), netMusicInCollectionCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              Integer total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              // 解决数量标签文字显示不全问题
                              collectionCountPanel.add(collectionCountLabel, collectionCountPanel.getComponentIndex(collectionCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForRadioCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForRadioCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForRadioCollectionModel);
                              collectionLeftBox.repaint();
                          }
                          // 这是榜单里的歌
                          else if (resource instanceof NetRankingInfo) {
                              NetRankingInfo RankingInfo = (NetRankingInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRanking(
                                      RankingInfo.getId(), RankingInfo.getSource(), netMusicInCollectionCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              Integer total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              // 解决数量标签文字显示不全问题
                              collectionCountPanel.add(collectionCountLabel, collectionCountPanel.getComponentIndex(collectionCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForRankingCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForRankingCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForRankingCollectionModel);
                              collectionLeftBox.repaint();
                          }
                          // 这是用户里的歌
                          else if (resource instanceof NetUserInfo) {
                              NetUserInfo userInfo = (NetUserInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInUser(
                                      collectionRecordTypeComboBox.getSelectedIndex(), userInfo, netMusicInCollectionCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              Integer total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              // 解决数量标签文字显示不全问题
                              collectionCountPanel.add(collectionCountLabel, collectionCountPanel.getComponentIndex(collectionCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForUserCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForUserCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForUserCollectionModel);
                              if (netMusicListForUserCollectionModel.isEmpty()) {
                                  collectionItemListCountBox.remove(netMusicScrollPane);
                                  collectionItemListCountBox.add(emptyHintPanel);
                              } else {
                                  collectionItemListCountBox.remove(emptyHintPanel);
                                  collectionItemListCountBox.add(netMusicScrollPane);
                              }
                              collectionLeftBox.repaint();
                          }
                          netMusicScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          collectionRecordTypeComboBoxModel.addElement(I18n.getText("recentWeek"));
          collectionRecordTypeComboBoxModel.addElement(I18n.getText("allTime"));
          collectionOrderComboBoxModel.addElement(I18n.getText("latest"));
          collectionOrderComboBoxModel.addElement(I18n.getText("mostPlayed"));
          collectionSortTypeComboBoxModel.addElement(I18n.getText("ascend"));
          collectionSortTypeComboBoxModel.addElement(I18n.getText("descend"));
          collectionRecordTypeComboBox.setModel(collectionRecordTypeComboBoxModel);
          // 用户听歌记录类型
          collectionRecordTypeComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (collectionRecordTypeComboBox.getItemCount() <= 1
                      || collectionRecordTypeComboBox.getSelectedItem() == null
                      || e.getStateChange() != ItemEvent.SELECTED) return;
              netMusicInCollectionCurrPage = 1;
              collectionGoPageAction.run();
          });
          // 播放全部
          collectionPlayAllButton.addActionListener(e -> {
              NetResource resource = collectionList.getSelectedValue();
              if (resource instanceof NetPlaylistInfo) netPlaylistPlayAllButton.doClick();
              else if (resource instanceof NetAlbumInfo) netAlbumPlayAllButton.doClick();
              else if (resource instanceof NetArtistInfo) netArtistPlayAllButton.doClick();
              else if (resource instanceof NetRadioInfo) netRadioPlayAllButton.doClick();
              else if (resource instanceof NetRankingInfo) netRankingPlayAllButton.doClick();
              else if (resource instanceof NetUserInfo) netUserPlayAllButton.doClick();
          });
          // 刷新按钮事件
          collectionRefreshButton.addActionListener(e -> {
              collectionGoPageAction.run();
          });
          // 第一页按钮事件
          collectionStartPageButton.addActionListener(e -> {
              if (netMusicInCollectionCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netMusicInCollectionCurrPage = 1;
              collectionGoPageAction.run();
          });
          // 上一页按钮事件
          collectionLastPageButton.addActionListener(e -> {
              if (netMusicInCollectionCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netMusicInCollectionCurrPage--;
              collectionGoPageAction.run();
          });
          // 下一页按钮事件
          collectionNextPageButton.addActionListener(e -> {
              if (netMusicInCollectionCurrPage >= netMusicInCollectionMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netMusicInCollectionCurrPage++;
              collectionGoPageAction.run();
          });
          // 最后一页按钮事件
          collectionEndPageButton.addActionListener(e -> {
              if (netMusicInCollectionCurrPage >= netMusicInCollectionMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netMusicInCollectionCurrPage = netMusicInCollectionMaxPage;
              collectionGoPageAction.run();
          });
          // 跳页按钮事件
          collectionGoButton.addActionListener(e -> {
              String text = collectionPageTextField.getText();
              collectionPageTextField.setText("");
              if (text.isEmpty()) return;
              int destPage = Integer.parseInt(text);
              if (destPage < 1 || destPage > netMusicInCollectionMaxPage) {
                  new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                  return;
              }
              netMusicInCollectionCurrPage = destPage;
              collectionGoPageAction.run();
          });
          collectionRecordTypeComboBox.setVisible(false);
          // 控制按钮大小
          Dimension dimension = new Dimension(30, 30);
          collectionRefreshButton.setPreferredSize(dimension);
          collectionStartPageButton.setPreferredSize(dimension);
          collectionLastPageButton.setPreferredSize(dimension);
          collectionGoButton.setPreferredSize(dimension);
          collectionNextPageButton.setPreferredSize(dimension);
          collectionEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          collectionBackwardButton.addMouseListener(new ButtonMouseListener(collectionBackwardButton, THIS));
          collectionPlayAllButton.addMouseListener(new ButtonMouseListener(collectionPlayAllButton, THIS));
          collectionRefreshButton.addMouseListener(new ButtonMouseListener(collectionRefreshButton, THIS));
          collectionStartPageButton.addMouseListener(new ButtonMouseListener(collectionStartPageButton, THIS));
          collectionLastPageButton.addMouseListener(new ButtonMouseListener(collectionLastPageButton, THIS));
          collectionGoButton.addMouseListener(new ButtonMouseListener(collectionGoButton, THIS));
          collectionNextPageButton.addMouseListener(new ButtonMouseListener(collectionNextPageButton, THIS));
          collectionEndPageButton.addMouseListener(new ButtonMouseListener(collectionEndPageButton, THIS));
          // 提示语
          collectionBackwardButton.setToolTipText(BACKWARD_TIP);
          collectionPlayAllButton.setToolTipText(PLAY_ALL_TIP);
          collectionRefreshButton.setToolTipText(REFRESH_TIP);
          collectionStartPageButton.setToolTipText(START_PAGE_TIP);
          collectionLastPageButton.setToolTipText(LAST_PAGE_TIP);
          collectionGoButton.setToolTipText(GO_TIP);
          collectionNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          collectionEndPageButton.setToolTipText(END_PAGE_TIP);
  
          collectionPlayAllButton.setIconTextGap(5);
  
          // 收藏歌单/专辑/歌手/电台/榜单中歌曲的工具栏，暂时不放入
          musicCollectionToolBar.add(collectionBackwardButton);
          musicCollectionToolBar.add(Box.createHorizontalGlue());
  
          // 数量标签
          collectionCountPanel.add(collectionRecordTypeComboBox);
          collectionCountPanel.add(collectionPlayAllButton);
          collectionCountPanel.add(collectionStartPageButton);
          collectionCountPanel.add(collectionLastPageButton);
          collectionCountPanel.add(collectionCountLabel);
          collectionCountPanel.add(collectionNextPageButton);
          collectionCountPanel.add(collectionEndPageButton);
          collectionCountPanel.add(collectionRefreshButton);
          collectionCountPanel.add(collectionPageTextField);
          collectionCountPanel.add(collectionGoButton);
          collectionCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          collectionLeftBox.add(collectionCountPanel);
      }
  
      private NetResource collectionOpenResource;
  
      // 初始化收藏列表
      private void initCollectionList() {
          collectionList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          collectionList.setModel(playlistCollectionModel);
          collectionList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      openCollectionItemAction.run();
                  }
              }
          });
          collectionList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = collectionList.locationToIndex(e.getPoint());
                  Rectangle bounds = collectionList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  ItemRecommendListRenderer renderer = (ItemRecommendListRenderer) collectionList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  collectionList.repaint();
              }
          });
          collectionList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  ItemRecommendListRenderer renderer = (ItemRecommendListRenderer) collectionList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  collectionList.repaint();
              }
          });
          openCollectionItemAction = () -> {
              NetResource resource = collectionList.getSelectedValue();
              if (resource == null) return;
              // 打开的是 MV
              if (resource instanceof NetMvInfo) {
                  playMv(MvCompSourceType.COLLECTION);
                  return;
              }
              collectionOpenResource = resource;
              // 检查收藏按钮
              checkDescriptionCollectionStatus(collectionItemDescriptionCollectionButton, resource);
              loadingAndRun(() -> {
                  List<Future<?>> taskList = new LinkedList<>();
                  // 打开的是歌单
                  if (resource instanceof NetPlaylistInfo) {
                      NetPlaylistInfo playlistInfo = (NetPlaylistInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          collectionItemCoverAndNameLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setVisible(true);
                          collectionItemDescriptionLabel.setText(LOADING_MSG);
                          collectionItemDescriptionLabel.setIcon(null);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillPlaylistInfo(playlistInfo);
                                  updateRenderer(collectionList);
                                  collectionItemCoverAndNameLabel.setText(StringUtil.textToHtml(playlistInfo.getName()));
                                  if (playlistInfo.getTag() != null)
                                      collectionItemTagLabel.setText(StringUtil.textToHtml(playlistInfo.getTag().isEmpty() ? "" : "标签：" + playlistInfo.getTag()));
                                  if (playlistInfo.getDescription() != null)
                                      collectionItemDescriptionLabel.setText(StringUtil.textToHtml(playlistInfo.getDescription()));
                                  if (playlistInfo.hasCoverImg()) {
                                      collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(playlistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      playlistInfo.setInvokeLater(() -> {
                                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(playlistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          collectionList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  collectionItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  collectionItemTagLabel.setText(LOAD_FAILED);
                                  collectionItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到歌单的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInPlaylist(playlistInfo, netMusicInCollectionCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新歌单歌曲数量显示
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              collectionItemListCountBox.add(collectionCountPanel);
                              collectionItemListCountBox.add(netMusicScrollPane);
                              collectionItemListBox.add(collectionItemListCountBox);
                              // 歌单列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForPlaylistCollectionModel);
                              collectionLeftBox.add(collectionItemListBox);
                              // 收藏工具栏添加，需要更新 LAF
                              collectionLeftBox.add(musicCollectionToolBar, 0);
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForPlaylistCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForPlaylistCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForPlaylistCollectionModel);
                              collectionItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              if (netMusicListForPlaylistCollectionModel.isEmpty()) {
                                  collectionItemListCountBox.remove(netMusicScrollPane);
                                  collectionItemListCountBox.add(emptyHintPanel);
                              } else {
                                  collectionItemListCountBox.remove(emptyHintPanel);
                                  collectionItemListCountBox.add(netMusicScrollPane);
                              }
                              tabbedPane.setComponentAt(TabIndex.PERSONAL, collectionLeftBox);
  
                              // 切换后一定要刷新！
                              collectionLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
                  // 打开的是专辑
                  else if (resource instanceof NetAlbumInfo) {
                      NetAlbumInfo albumInfo = (NetAlbumInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          collectionItemCoverAndNameLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setText("");
                          collectionItemTagLabel.setVisible(false);
                          collectionItemDescriptionLabel.setText(LOADING_MSG);
                          collectionItemDescriptionLabel.setIcon(null);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillAlbumInfo(albumInfo);
                                  updateRenderer(collectionList);
                                  collectionItemCoverAndNameLabel.setText(StringUtil.textToHtml(albumInfo.getName()));
                                  if (albumInfo.getDescription() != null)
                                      collectionItemDescriptionLabel.setText(StringUtil.textToHtml(albumInfo.getDescription()));
                                  if (albumInfo.hasCoverImg()) {
                                      collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(albumInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      albumInfo.setInvokeLater(() -> {
                                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(albumInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          collectionList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  collectionItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  collectionItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到专辑的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInAlbum(albumInfo, netMusicInCollectionCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新专辑歌曲数量显示
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              collectionItemListCountBox.add(collectionCountPanel);
                              collectionItemListCountBox.add(netMusicScrollPane);
                              collectionItemListBox.add(collectionItemListCountBox);
                              // 专辑列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForAlbumCollectionModel);
                              collectionLeftBox.add(collectionItemListBox);
                              // 收藏工具栏添加，需要更新 LAF
                              collectionLeftBox.add(musicCollectionToolBar, 0);
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForAlbumCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForAlbumCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForAlbumCollectionModel);
                              collectionItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              if (netMusicListForAlbumCollectionModel.isEmpty()) {
                                  collectionItemListCountBox.remove(netMusicScrollPane);
                                  collectionItemListCountBox.add(emptyHintPanel);
                              } else {
                                  collectionItemListCountBox.remove(emptyHintPanel);
                                  collectionItemListCountBox.add(netMusicScrollPane);
                              }
                              tabbedPane.setComponentAt(TabIndex.PERSONAL, collectionLeftBox);
  
                              // 切换后一定要刷新！
                              collectionLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
                  // 打开的是歌手
                  else if (resource instanceof NetArtistInfo) {
                      NetArtistInfo artistInfo = (NetArtistInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          collectionItemCoverAndNameLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setVisible(true);
                          collectionItemDescriptionLabel.setText(LOADING_MSG);
                          collectionItemDescriptionLabel.setIcon(null);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillArtistInfo(artistInfo);
                                  updateRenderer(collectionList);
                                  collectionItemCoverAndNameLabel.setText(StringUtil.textToHtml(artistInfo.getName()));
                                  if (artistInfo.getTag() != null)
                                      collectionItemTagLabel.setText(StringUtil.textToHtml(artistInfo.getTag().isEmpty() ? "" : artistInfo.getTag()));
                                  if (artistInfo.getDescription() != null)
                                      collectionItemDescriptionLabel.setText(StringUtil.textToHtml(artistInfo.getDescription()));
                                  if (artistInfo.hasCoverImg()) {
                                      collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(artistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      artistInfo.setInvokeLater(() -> {
                                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(artistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          collectionList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  collectionItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  collectionItemTagLabel.setText(LOAD_FAILED);
                                  collectionItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到歌手的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInArtist(artistInfo, netMusicInCollectionCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新歌手歌曲数量显示
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              collectionItemListCountBox.add(collectionCountPanel);
                              collectionItemListCountBox.add(netMusicScrollPane);
                              collectionItemListBox.add(collectionItemListCountBox);
                              // 歌手列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForArtistCollectionModel);
                              collectionLeftBox.add(collectionItemListBox);
                              // 收藏工具栏添加，需要更新 LAF
                              collectionLeftBox.add(musicCollectionToolBar, 0);
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForArtistCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForArtistCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForArtistCollectionModel);
                              collectionItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              if (netMusicListForArtistCollectionModel.isEmpty()) {
                                  collectionItemListCountBox.remove(netMusicScrollPane);
                                  collectionItemListCountBox.add(emptyHintPanel);
                              } else {
                                  collectionItemListCountBox.remove(emptyHintPanel);
                                  collectionItemListCountBox.add(netMusicScrollPane);
                              }
                              tabbedPane.setComponentAt(TabIndex.PERSONAL, collectionLeftBox);
  
                              // 切换后一定要刷新！
                              collectionLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
                  // 打开的是电台
                  else if (resource instanceof NetRadioInfo) {
                      NetRadioInfo radioInfo = (NetRadioInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          collectionItemCoverAndNameLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setVisible(true);
                          collectionItemDescriptionLabel.setText(LOADING_MSG);
                          collectionItemDescriptionLabel.setIcon(null);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillRadioInfo(radioInfo);
                                  updateRenderer(collectionList);
                                  collectionItemCoverAndNameLabel.setText(StringUtil.textToHtml(radioInfo.getName()));
                                  if (radioInfo.getTag() != null)
                                      collectionItemTagLabel.setText(StringUtil.textToHtml(radioInfo.getTag().isEmpty() ? "" : "标签：" + radioInfo.getTag()));
                                  if (radioInfo.getDescription() != null)
                                      collectionItemDescriptionLabel.setText(StringUtil.textToHtml(radioInfo.getDescription()));
                                  if (radioInfo.hasCoverImg()) {
                                      collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(radioInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      radioInfo.setInvokeLater(() -> {
                                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(radioInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          collectionList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  collectionItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  collectionItemTagLabel.setText(LOAD_FAILED);
                                  collectionItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到电台的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              collectionRecordTypeComboBox.setModel(collectionSortTypeComboBoxModel);
  
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRadio(radioInfo, collectionRecordTypeComboBox.getSelectedIndex(), netMusicInCollectionCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新电台歌曲数量显示
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              collectionItemListCountBox.add(collectionCountPanel);
                              collectionItemListCountBox.add(netMusicScrollPane);
                              collectionItemListBox.add(collectionItemListCountBox);
                              // 电台列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForRadioCollectionModel);
                              collectionLeftBox.add(collectionItemListBox);
                              // 收藏工具栏添加，需要更新 LAF
                              collectionLeftBox.add(musicCollectionToolBar, 0);
                              collectionRecordTypeComboBox.setVisible(radioInfo.fromXM());
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForRadioCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForRadioCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForRadioCollectionModel);
                              collectionItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              if (netMusicListForRadioCollectionModel.isEmpty()) {
                                  collectionItemListCountBox.remove(netMusicScrollPane);
                                  collectionItemListCountBox.add(emptyHintPanel);
                              } else {
                                  collectionItemListCountBox.remove(emptyHintPanel);
                                  collectionItemListCountBox.add(netMusicScrollPane);
                              }
                              tabbedPane.setComponentAt(TabIndex.PERSONAL, collectionLeftBox);
  
                              // 切换后一定要刷新！
                              collectionLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
                  // 打开的是榜单
                  else if (resource instanceof NetRankingInfo) {
                      NetRankingInfo rankingInfo = (NetRankingInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          collectionItemCoverAndNameLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setText("");
                          collectionItemTagLabel.setVisible(false);
                          collectionItemDescriptionLabel.setText(LOADING_MSG);
                          collectionItemDescriptionLabel.setIcon(null);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillRankingInfo(rankingInfo);
                                  updateRenderer(collectionList);
                                  collectionItemCoverAndNameLabel.setText(StringUtil.textToHtml(rankingInfo.getName()));
                                  if (rankingInfo.getDescription() != null)
                                      collectionItemDescriptionLabel.setText(StringUtil.textToHtml(rankingInfo.getDescription()));
                                  if (rankingInfo.hasCoverImg()) {
                                      collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(rankingInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      rankingInfo.setInvokeLater(() -> {
                                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(rankingInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          collectionList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  collectionItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  collectionItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到榜单的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRanking(
                                      rankingInfo.getId(), rankingInfo.getSource(), netMusicInCollectionCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新榜单歌曲数量显示
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              collectionItemListCountBox.add(collectionCountPanel);
                              collectionItemListCountBox.add(netMusicScrollPane);
                              collectionItemListBox.add(collectionItemListCountBox);
                              // 榜单列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForRankingCollectionModel);
                              collectionLeftBox.add(collectionItemListBox);
                              // 收藏工具栏添加，需要更新 LAF
                              collectionLeftBox.add(musicCollectionToolBar, 0);
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForRankingCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForRankingCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForRankingCollectionModel);
                              collectionItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              if (netMusicListForRankingCollectionModel.isEmpty()) {
                                  collectionItemListCountBox.remove(netMusicScrollPane);
                                  collectionItemListCountBox.add(emptyHintPanel);
                              } else {
                                  collectionItemListCountBox.remove(emptyHintPanel);
                                  collectionItemListCountBox.add(netMusicScrollPane);
                              }
                              tabbedPane.setComponentAt(TabIndex.PERSONAL, collectionLeftBox);
  
                              // 切换后一定要刷新！
                              collectionLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
                  // 打开的是用户
                  else if (resource instanceof NetUserInfo) {
                      NetUserInfo userInfo = (NetUserInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          ImageIcon icon = new ImageIcon(coverImg);
                          collectionItemCoverAndNameLabel.setIcon(icon);
                          collectionItemCoverAndNameLabel.setText(LOADING_MSG);
                          collectionItemTagLabel.setText("");
                          collectionItemTagLabel.setVisible(true);
                          collectionItemDescriptionLabel.setText(LOADING_MSG);
                          collectionItemDescriptionLabel.setIcon(icon);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillUserInfo(userInfo);
                                  updateRenderer(collectionList);
                                  collectionItemCoverAndNameLabel.setText(StringUtil.textToHtml(userInfo.getName()));
                                  collectionItemTagLabel.setText(StringUtil.textToHtml(userInfo.getTag()));
                                  if (userInfo.getSign() != null)
                                      collectionItemDescriptionLabel.setText(StringUtil.textToHtml(userInfo.getSign()));
                                  if (userInfo.hasAvatar()) {
                                      collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(userInfo.getAvatar(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      userInfo.setInvokeLater(() -> {
                                          collectionItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(userInfo.getAvatar(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          collectionList.repaint();
                                      });
                                  }
                                  if (userInfo.hasBgImg()) {
                                      collectionItemDescriptionLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(userInfo.getBgImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      userInfo.setInvokeLater2(() -> {
                                          collectionItemDescriptionLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(userInfo.getBgImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          collectionList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  collectionItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  collectionItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到用户的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              // 网易云/猫耳用户才显示下拉框
                              if (userInfo.fromNetCloud())
                                  collectionRecordTypeComboBox.setModel(collectionRecordTypeComboBoxModel);
                              else if (userInfo.fromME() || userInfo.fromBI())
                                  collectionRecordTypeComboBox.setModel(collectionOrderComboBoxModel);
                              else if (userInfo.fromXM())
                                  collectionRecordTypeComboBox.setModel(collectionSortTypeComboBoxModel);
  
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInUser(
                                      collectionRecordTypeComboBox.getSelectedIndex(), userInfo, netMusicInCollectionCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInCollectionMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新用户歌曲数量显示
                              collectionCountLabel.setText(String.format(PAGINATION_MSG, netMusicInCollectionCurrPage, netMusicInCollectionMaxPage));
                              collectionItemListCountBox.add(collectionCountPanel);
                              collectionItemListCountBox.add(netMusicScrollPane);
                              collectionItemListBox.add(collectionItemListCountBox);
                              // 用户列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForUserCollectionModel);
                              collectionLeftBox.add(collectionItemListBox);
                              // 收藏工具栏添加，需要更新 LAF
                              collectionLeftBox.add(musicCollectionToolBar, 0);
                              collectionRecordTypeComboBox.setVisible(userInfo.fromNetCloud() || userInfo.fromME() || userInfo.fromXM() || userInfo.fromBI());
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForUserCollectionModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForUserCollectionModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForUserCollectionModel);
                              collectionItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              if (netMusicListForUserCollectionModel.isEmpty()) {
                                  collectionItemListCountBox.remove(netMusicScrollPane);
                                  collectionItemListCountBox.add(emptyHintPanel);
                              } else {
                                  collectionItemListCountBox.remove(emptyHintPanel);
                                  collectionItemListCountBox.add(netMusicScrollPane);
                              }
                              tabbedPane.setComponentAt(TabIndex.PERSONAL, collectionLeftBox);
  
                              // 切换后一定要刷新！
                              collectionLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
  
                  taskList.forEach(task -> {
                      try {
                          task.get();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } catch (ExecutionException e) {
                          e.printStackTrace();
                      }
                  });
              });
          };
          collectionList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开歌单/专辑/歌手/电台/MV
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      openCollectionItemAction.run();
                  }
  
                  // 右键收藏列表
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (collectionList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = collectionList.locationToIndex(e.getPoint());
                          if (index != -1 && !collectionList.isSelectedIndex(index)
                                  || collectionList.isSelectedIndex(index) && collectionList.getSelectedIndices().length == 1) {
                              collectionList.setSelectedIndex(index);
                          }
                          int selectedIndex = collectionTabbedPane.getSelectedIndex();
                          if (selectedIndex == CollectionTabIndex.PLAYLIST) {
                              netPlaylistCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netPlaylistCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
  
                              netPlaylistPopupMenu.show(collectionList, e.getX(), e.getY());
                          } else if (selectedIndex == CollectionTabIndex.ALBUM) {
                              netAlbumCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netAlbumCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
  
                              netAlbumPopupMenu.show(collectionList, e.getX(), e.getY());
                          } else if (selectedIndex == CollectionTabIndex.ARTIST) {
                              netArtistCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netArtistCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
  
                              netArtistPopupMenu.show(collectionList, e.getX(), e.getY());
                          } else if (selectedIndex == CollectionTabIndex.RADIO) {
                              netRadioCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netRadioCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
  
                              netRadioPopupMenu.show(collectionList, e.getX(), e.getY());
                          } else if (selectedIndex == CollectionTabIndex.MV) {
                              netMvCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netMvCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
  
                              netMvPopupMenu.show(collectionList, e.getX(), e.getY());
                          } else if (selectedIndex == CollectionTabIndex.RANKING) {
                              netRankingCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netRankingCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
  
                              netRankingPopupMenu.show(collectionList, e.getX(), e.getY());
                          } else if (selectedIndex == CollectionTabIndex.USER) {
                              netUserCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netUserCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
  
                              netUserPopupMenu.show(collectionList, e.getX(), e.getY());
                          }
                      }
                  }
              }
          });
          // 最佳大小
          collectionScrollPane.setPreferredSize(new Dimension(200, 600));
          playlistCollectionLeftBox.add(collectionScrollPane);
      }
  
      // 初始化标签
      private void initLabel() {
          // 导出专辑图片事件
          saveAlbumImageMenuItem.addActionListener(e -> saveImg(player.getMetaMusicInfo().getAlbumImage()));
          copySongNameMenuItem.addActionListener(e -> copyToClipboard(StringUtil.removeHTMLLabel(songNameLabel.getText().replaceFirst(SONG_NAME_LABEL, ""))));
          copyArtistMenuItem.addActionListener(e -> copyToClipboard(StringUtil.removeHTMLLabel(artistLabel.getText().replaceFirst(ARTIST_LABEL, ""))));
          copyAlbumMenuItem.addActionListener(e -> copyToClipboard(StringUtil.removeHTMLLabel(albumLabel.getText().replaceFirst(ALBUM_NAME_LABEL, ""))));
          leftInfoPopupMenu.add(saveAlbumImageMenuItem);
          leftInfoPopupMenu.add(copySongNameMenuItem);
          leftInfoPopupMenu.add(copyArtistMenuItem);
          leftInfoPopupMenu.add(copyAlbumMenuItem);
          leftInfoBox.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON3) return;
                  leftInfoPopupMenu.show(leftInfoBox, e.getX(), e.getY());
              }
          });
  
          final float alpha = 0.5f;
          artistLabel.setAlpha(alpha);
          albumLabel.setAlpha(alpha);
          artistLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  artistLabel.setAlpha(1f);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  artistLabel.setAlpha(alpha);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() == MouseEvent.BUTTON3) {
                      leftInfoPopupMenu.show(artistLabel, e.getX(), e.getY());
                  } else if (e.getButton() == MouseEvent.BUTTON1 && player.loadedNetMusic()) {
                      artistLabel.setAlpha(alpha);
                      netMusicAuthorMenuItem.doClick();
                      changePaneButton.doClick();
                  }
              }
          });
          albumLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  albumLabel.setAlpha(1f);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  albumLabel.setAlpha(alpha);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() == MouseEvent.BUTTON3) {
                      leftInfoPopupMenu.show(albumLabel, e.getX(), e.getY());
                  } else if (e.getButton() == MouseEvent.BUTTON1 && player.loadedNetMusic()) {
                      albumLabel.setAlpha(alpha);
                      netMusicAlbumMenuItem.doClick();
                      changePaneButton.doClick();
                  }
              }
          });
  
          // 添加左下的标签
          leftBottomBox.add(Box.createVerticalGlue());
          leftBottomBox.add(songNameLabel);
          leftBottomBox.add(Box.createVerticalGlue());
          leftBottomBox.add(artistLabel);
          leftBottomBox.add(Box.createVerticalGlue());
          leftBottomBox.add(albumLabel);
          leftBottomBox.add(Box.createVerticalGlue());
          // 专辑图片和下边所有标签整体
          leftInfoBox.add(Box.createVerticalGlue());
          leftInfoBox.add(albumImageLabel);
          leftInfoBox.add(Box.createVerticalGlue());
          leftInfoBox.add(leftBottomBox);
          leftInfoBox.add(Box.createVerticalGlue());
          leftInfoBox.setBorder(BorderFactory.createEmptyBorder(0, 30, 0, 0));
          infoAndLrcBox.add(leftInfoBox);
  
          // 列表为空提示
          emptyHintPanel.setLayout(new BorderLayout());
          emptyHintPanel.add(emptyHintLabel, BorderLayout.CENTER);
          emptyHintPanel.setPreferredSize(new Dimension(260, 1000));
      }
  
      // 初始化个人音乐工具栏
      private void initPersonalMusicToolBar() {
          // 本地音乐事件
          localMusicButton.addActionListener(e -> {
              if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC) return;
              currPersonalMusicTab = PersonalMusicTabIndex.LOCAL_MUSIC;
              updateTabButtonStyle();
              leftBox.remove(collectionTabbedPane);
              if (musicListModel.isEmpty()) {
                  leftBox.remove(musicScrollPane);
                  leftBox.add(emptyHintPanel);
              } else {
                  leftBox.remove(emptyHintPanel);
                  leftBox.add(musicScrollPane);
              }
              addToolButton.setVisible(true);
              reimportToolButton.setVisible(true);
              sortToolButton.setVisible(true);
              leftBox.repaint();
              // 筛选框活跃状态时进行筛选
              if (filterTextField.isOccupied()) filterPersonalMusic();
              else musicList.setModel(musicListModel);
              musicScrollPane.setVValue(0);
              countLabel.setText(String.format(TOTAL_MSG, musicList.getModel().getSize()));
          });
          // 播放历史事件
          historyButton.addActionListener(e -> {
              if (currPersonalMusicTab == PersonalMusicTabIndex.HISTORY) return;
              currPersonalMusicTab = PersonalMusicTabIndex.HISTORY;
              updateTabButtonStyle();
              leftBox.remove(collectionTabbedPane);
              if (historyModel.isEmpty()) {
                  leftBox.remove(musicScrollPane);
                  leftBox.add(emptyHintPanel);
              } else {
                  leftBox.remove(emptyHintPanel);
                  leftBox.add(musicScrollPane);
              }
              addToolButton.setVisible(false);
              reimportToolButton.setVisible(false);
              sortToolButton.setVisible(false);
              leftBox.repaint();
              // 筛选框活跃状态时进行筛选
              if (filterTextField.isOccupied()) filterPersonalMusic();
              else musicList.setModel(historyModel);
              musicScrollPane.setVValue(0);
              countLabel.setText(String.format(TOTAL_MSG, musicList.getModel().getSize()));
          });
          // 收藏事件
          collectionButton.addActionListener(e -> {
              if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION) return;
              currPersonalMusicTab = PersonalMusicTabIndex.COLLECTION;
              updateTabButtonStyle();
  
              // 收藏标签页
              leftBox.remove(emptyHintPanel);
              leftBox.remove(musicScrollPane);
  
              int index = collectionTabbedPane.getSelectedIndex();
              Box box = null;
              DefaultListModel model = null;
              if (index == CollectionTabIndex.MUSIC) {
                  box = musicCollectionLeftBox;
                  model = collectionModel;
              } else if (index == CollectionTabIndex.PLAYLIST) {
                  box = playlistCollectionLeftBox;
                  model = playlistCollectionModel;
              } else if (index == CollectionTabIndex.ALBUM) {
                  box = albumCollectionLeftBox;
                  model = albumCollectionModel;
              } else if (index == CollectionTabIndex.ARTIST) {
                  box = artistCollectionLeftBox;
                  model = artistCollectionModel;
              } else if (index == CollectionTabIndex.RADIO) {
                  box = radioCollectionLeftBox;
                  model = radioCollectionModel;
              } else if (index == CollectionTabIndex.MV) {
                  box = mvCollectionLeftBox;
                  model = mvCollectionModel;
              } else if (index == CollectionTabIndex.RANKING) {
                  box = rankingCollectionLeftBox;
                  model = rankingCollectionModel;
              } else if (index == CollectionTabIndex.USER) {
                  box = userCollectionLeftBox;
                  model = userCollectionModel;
              }
              if (model.isEmpty()) {
                  box.remove(index == CollectionTabIndex.MUSIC ? musicScrollPane : collectionScrollPane);
                  box.add(emptyHintPanel);
              } else {
                  box.remove(emptyHintPanel);
                  box.add(index == CollectionTabIndex.MUSIC ? musicScrollPane : collectionScrollPane);
              }
              leftBox.add(collectionTabbedPane);
  
              addToolButton.setVisible(false);
              reimportToolButton.setVisible(false);
              sortToolButton.setVisible(false);
              leftBox.repaint();
  
              int selectedIndex = collectionTabbedPane.getSelectedIndex();
              // 筛选框活跃状态时进行筛选
              if (filterTextField.isOccupied()) filterPersonalMusic();
              else musicList.setModel(collectionModel);
              musicScrollPane.setVValue(0);
  
              if (selectedIndex == CollectionTabIndex.MUSIC)
                  countLabel.setText(String.format(TOTAL_MSG, musicList.getModel().getSize()));
              else countLabel.setText(String.format(TOTAL_MSG, collectionList.getModel().getSize()));
          });
          // 按钮悬浮和点击效果
          localMusicButton.addMouseListener(new ButtonMouseListener(localMusicButton, THIS));
          historyButton.addMouseListener(new ButtonMouseListener(historyButton, THIS));
          collectionButton.addMouseListener(new ButtonMouseListener(collectionButton, THIS));
  
          // 按钮文字与图标的位置
          int gap = 15;
          localMusicButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          localMusicButton.setIconTextGap(gap);
          historyButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          historyButton.setIconTextGap(gap);
          collectionButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          collectionButton.setIconTextGap(gap);
  
          // 推荐工具栏网格布局
          personalMusicToolBar.setLayout(new GridLayout(1, 3));
          personalMusicToolBar.add(localMusicButton);
          personalMusicToolBar.add(historyButton);
          personalMusicToolBar.add(collectionButton);
  
          personalMusicToolBar.setMaximumSize(new Dimension(Integer.MAX_VALUE, 50));
  
          leftBox.add(personalMusicToolBar);
      }
  
      // 初始化本地音乐工具栏
      private void initMusicToolBar() {
          // 按钮悬浮和点击效果
          addToolButton.addMouseListener(new ButtonMouseListener(addToolButton, THIS));
          reimportToolButton.addMouseListener(new ButtonMouseListener(reimportToolButton, THIS));
          manageCatalogToolButton.addMouseListener(new ButtonMouseListener(manageCatalogToolButton, THIS));
          removeToolButton.addMouseListener(new ButtonMouseListener(removeToolButton, THIS));
          clearToolButton.addMouseListener(new ButtonMouseListener(clearToolButton, THIS));
          duplicateToolButton.addMouseListener(new ButtonMouseListener(duplicateToolButton, THIS));
          reverseToolButton.addMouseListener(new ButtonMouseListener(reverseToolButton, THIS));
          sortToolButton.addMouseListener(new ButtonMouseListener(sortToolButton, THIS));
          moveUpToolButton.addMouseListener(new ButtonMouseListener(moveUpToolButton, THIS));
          moveDownToolButton.addMouseListener(new ButtonMouseListener(moveDownToolButton, THIS));
          clearInputToolButton.addMouseListener(new ButtonMouseListener(clearInputToolButton, THIS));
          // 标签左间距
          countLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
  
          addPopupMenu.add(addFileMenuItem);
          addPopupMenu.add(addDirMenuItem);
          // 按钮绑定右键菜单，不过右键也会弹出
          addToolButton.setComponentPopupMenu(addPopupMenu);
          // 点击添加按钮事件
          addToolButton.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1) return;
                  addPopupMenu.show(addToolButton, e.getX(), e.getY());
              }
          });
          // 点击重新导入按钮事件
          reimportToolButton.addActionListener(e -> {
              ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_REIMPORT_MSG, YES, NO);
              confirmDialog.showDialog();
              int response = confirmDialog.getResponse();
              if (response != JOptionPane.YES_OPTION) return;
              if (catalogs.isEmpty()) {
                  new TipDialog(THIS, NO_CATALOG_MSG).showDialog();
                  return;
              }
              ListModel model = musicList.getModel();
              // 修复重新导入时程序假死问题
              if (model.getSize() > 0) musicList.setSelectedIndex(0);
              musicList.setModel(emptyListModel);
              musicListModel.clear();
              int audioFileCount = 0;
              for (File dir : catalogs) {
                  // 文件夹不存在，跳过
                  if (!dir.exists()) continue;
  
                  File[] files = dir.listFiles();
                  for (File file : files) {
                      // 支持这种文件格式才添加
                      if (!player.support(FileUtil.getSuffix(file))) continue;
                      audioFileCount++;
                      AudioFile audioFile = new AudioFile(file);
                      globalExecutor.execute(() -> {
                          MediaUtil.fillAudioFileInfo(audioFile);
                          musicList.repaint();
                      });
                      musicListModel.addElement(audioFile);
                  }
              }
              musicList.setModel(model);
              boolean f = model == filterModel;
              filterPersonalMusic();
              if (!f) {
                  musicList.setModel(musicListModel);
                  countLabel.setText(String.format(TOTAL_MSG, musicList.getModel().getSize()));
              }
              String msg = "成功添加 " + audioFileCount + " 首歌曲";
              new TipDialog(THIS, msg).showDialog();
          });
          // 点击管理歌曲目录按钮事件
          manageCatalogToolButton.addActionListener(e -> {
              ManageCatalogDialog dialog = new ManageCatalogDialog(THIS);
              dialog.showDialog();
          });
          // 点击删除按钮事件
          removeToolButton.addActionListener(e -> {
              int selectedIndex = collectionTabbedPane.getSelectedIndex();
              if (currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION || selectedIndex == CollectionTabIndex.MUSIC) {
                  List<MusicResource> selectedValues = musicList.getSelectedValuesList();
                  if (selectedValues.isEmpty()) return;
                  ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_REMOVE_ITEMS_MSG, YES, NO);
                  confirmDialog.showDialog();
                  int response = confirmDialog.getResponse();
                  if (response != JOptionPane.YES_OPTION) return;
                  // 删除选中的文件
                  DefaultListModel<MusicResource> model = (DefaultListModel<MusicResource>) musicList.getModel();
                  // 解决删除元素带来的性能问题
                  ListCellRenderer r = musicList.getCellRenderer();
                  musicList.setCellRenderer(null);
                  for (MusicResource resource : selectedValues) {
                      // 改变取消收藏状态
                      if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION && player.loadedMusicResource(resource) && hasBeenCollected(resource))
                          collectButton.setIcon(ImageUtil.dye(collectIcon, currUIStyle.getIconColor()));
                      model.removeElement(resource);
                      if (model == filterModel) {
                          if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC)
                              musicListModel.removeElement(resource);
                          else if (currPersonalMusicTab == PersonalMusicTabIndex.HISTORY)
                              historyModel.removeElement(resource);
                          else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION)
                              collectionModel.removeElement(resource);
                      }
                  }
                  musicList.setCellRenderer(r);
                  new TipDialog(THIS, REMOVE_SUCCESS_MSG).showDialog();
              } else {
                  List<NetResource> selectedValues = collectionList.getSelectedValuesList();
                  if (selectedValues.isEmpty()) return;
                  ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_REMOVE_ITEMS_MSG, YES, NO);
                  confirmDialog.showDialog();
                  int response = confirmDialog.getResponse();
                  if (response != JOptionPane.YES_OPTION) return;
                  // 删除选中的项目
                  for (NetResource resource : selectedValues) {
                      DefaultListModel<NetResource> model = (DefaultListModel<NetResource>) collectionList.getModel();
                      model.removeElement(resource);
                      if (model == filterModel) {
                          if (selectedIndex == CollectionTabIndex.PLAYLIST)
                              playlistCollectionModel.removeElement(resource);
                          else if (selectedIndex == CollectionTabIndex.ALBUM)
                              albumCollectionModel.removeElement(resource);
                          else if (selectedIndex == CollectionTabIndex.ARTIST)
                              artistCollectionModel.removeElement(resource);
                          else if (selectedIndex == CollectionTabIndex.RADIO)
                              radioCollectionModel.removeElement(resource);
                          else if (selectedIndex == CollectionTabIndex.MV)
                              mvCollectionModel.removeElement(resource);
                          else if (selectedIndex == CollectionTabIndex.RANKING)
                              rankingCollectionModel.removeElement(resource);
                          else if (selectedIndex == CollectionTabIndex.USER)
                              userCollectionModel.removeElement(resource);
                      }
                  }
                  new TipDialog(THIS, REMOVE_SUCCESS_MSG).showDialog();
              }
          });
          // 点击清空按钮事件
          clearToolButton.addActionListener(e -> {
              ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_CLEAR_LIST_MSG, YES, NO);
              confirmDialog.showDialog();
              int response = confirmDialog.getResponse();
              if (response != JOptionPane.YES_OPTION) return;
              // 清空列表
              int selectedIndex = collectionTabbedPane.getSelectedIndex();
              if (currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION || selectedIndex == CollectionTabIndex.MUSIC) {
                  DefaultListModel<MusicResource> model = (DefaultListModel<MusicResource>) musicList.getModel();
                  musicList.setModel(emptyListModel);
                  model.clear();
                  if (model == filterModel) {
                      if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC)
                          musicListModel.clear();
                      else if (currPersonalMusicTab == PersonalMusicTabIndex.HISTORY)
                          historyModel.clear();
                      else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION) {
                          collectionModel.clear();
                          collectButton.setIcon(ImageUtil.dye(collectIcon, currUIStyle.getIconColor()));
                      }
                  } else if (model == collectionModel) {
                      collectButton.setIcon(ImageUtil.dye(collectIcon, currUIStyle.getIconColor()));
                  }
              } else {
                  DefaultListModel<NetResource> model = (DefaultListModel<NetResource>) collectionList.getModel();
                  collectionList.setModel(emptyListModel);
                  model.clear();
                  if (model == filterModel) {
                      if (selectedIndex == CollectionTabIndex.PLAYLIST) playlistCollectionModel.clear();
                      else if (selectedIndex == CollectionTabIndex.ALBUM) albumCollectionModel.clear();
                      else if (selectedIndex == CollectionTabIndex.ARTIST) artistCollectionModel.clear();
                      else if (selectedIndex == CollectionTabIndex.RADIO) radioCollectionModel.clear();
                      else if (selectedIndex == CollectionTabIndex.MV) mvCollectionModel.clear();
                      else if (selectedIndex == CollectionTabIndex.RANKING) rankingCollectionModel.clear();
                      else if (selectedIndex == CollectionTabIndex.USER) userCollectionModel.clear();
                  }
              }
              new TipDialog(THIS, CLEAR_SUCCESS_MSG).showDialog();
          });
          // 去重事件
          duplicateToolButton.addActionListener(e -> {
              ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_DUPLICATE_MSG, YES, NO);
              confirmDialog.showDialog();
              int response = confirmDialog.getResponse();
              if (response != JOptionPane.YES_OPTION) return;
              Set<Object> set = new HashSet<>();
              DefaultListModel model = null;
              if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC) model = musicListModel;
              else if (currPersonalMusicTab == PersonalMusicTabIndex.HISTORY) model = historyModel;
              else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION) {
                  int selectedIndex = collectionTabbedPane.getSelectedIndex();
                  if (selectedIndex == CollectionTabIndex.MUSIC) model = collectionModel;
                  else if (selectedIndex == CollectionTabIndex.PLAYLIST) model = playlistCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.ALBUM) model = albumCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.ARTIST) model = artistCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.RADIO) model = radioCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.MV) model = mvCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.RANKING) model = rankingCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.USER) model = userCollectionModel;
              }
              for (int i = 0; i < model.getSize(); i++) {
                  Resource elem = (Resource) model.get(i);
                  if (!set.contains(elem)) set.add(elem);
                  else model.remove(i--);
              }
              set.clear();
              for (int i = 0; i < filterModel.getSize(); i++) {
                  Resource elem = (Resource) filterModel.get(i);
                  if (!set.contains(elem)) set.add(elem);
                  else filterModel.remove(i--);
              }
              new TipDialog(THIS, DUPLICATE_SUCCESS_MSG).showDialog();
          });
          // 倒序事件
          reverseToolButton.addActionListener(e -> {
              ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_REVERSE_MSG, YES, NO);
              confirmDialog.showDialog();
              int response = confirmDialog.getResponse();
              if (response != JOptionPane.YES_OPTION) return;
              DefaultListModel model = null;
              if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC) model = musicListModel;
              else if (currPersonalMusicTab == PersonalMusicTabIndex.HISTORY) model = historyModel;
              else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION) {
                  int selectedIndex = collectionTabbedPane.getSelectedIndex();
                  if (selectedIndex == CollectionTabIndex.MUSIC) model = collectionModel;
                  else if (selectedIndex == CollectionTabIndex.PLAYLIST) model = playlistCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.ALBUM) model = albumCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.ARTIST) model = artistCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.RADIO) model = radioCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.MV) model = mvCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.RANKING) model = rankingCollectionModel;
                  else if (selectedIndex == CollectionTabIndex.USER) model = userCollectionModel;
              }
              for (int i = 0, s = model.size(), half = s / 2; i < half; i++) {
                  Resource t = (Resource) model.get(i);
                  model.set(i, model.get(s - 1 - i));
                  model.set(s - 1 - i, t);
              }
              new TipDialog(THIS, REVERSE_SUCCESS_MSG).showDialog();
          });
          // 升序
          ascendingMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortOrderButtonGroup, ascendingMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod, currSortOrder = SortMethod.ASCENDING);
          });
          // 降序
          descendingMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortOrderButtonGroup, descendingMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod, currSortOrder = SortMethod.DESCENDING);
          });
          // 按曲名/文件名混合排序
          sortBySongNameAndFileNameMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortBySongNameAndFileNameMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_SONG_AND_FILE_NAME, currSortOrder);
          });
          // 按曲名排序
          sortBySongNameMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortBySongNameMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_SONG_NAME, currSortOrder);
          });
          // 按艺术家排序
          sortByArtistNameMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortByArtistNameMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_ARTIST_NAME, currSortOrder);
          });
          // 按专辑排序
          sortByAlbumNameMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortByAlbumNameMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_ALBUM_NAME, currSortOrder);
          });
          // 按文件名排序
          sortByFileNameMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortByFileNameMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_FILE_NAME, currSortOrder);
          });
          // 按时长排序
          sortByTimeMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortByTimeMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_TIME, currSortOrder);
          });
          // 按创建时间排序
          sortByCreationTimeMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortByCreationTimeMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_CREATION_TIME, currSortOrder);
          });
          // 按修改时间排序
          sortByLastModifiedTimeMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortByLastModifiedTimeMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_LAST_MODIFIED_TIME, currSortOrder);
          });
          // 按访问时间排序
          sortByLastAccessTimeMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortByLastAccessTimeMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_LAST_ACCESS_TIME, currSortOrder);
          });
          // 按大小排序
          sortBySizeMenuItem.addActionListener(e -> {
              updateMenuItemStatus(sortMethodButtonGroup, sortBySizeMenuItem);
              updateMenuItemIcon(sortPopupMenu);
              sortFiles(currSortMethod = SortMethod.BY_SIZE, currSortOrder);
          });
  
          sortOrderButtonGroup.add(ascendingMenuItem);
          sortOrderButtonGroup.add(descendingMenuItem);
  
          sortMethodButtonGroup.add(sortBySongNameAndFileNameMenuItem);
          sortMethodButtonGroup.add(sortBySongNameMenuItem);
          sortMethodButtonGroup.add(sortByArtistNameMenuItem);
          sortMethodButtonGroup.add(sortByAlbumNameMenuItem);
          sortMethodButtonGroup.add(sortByFileNameMenuItem);
          sortMethodButtonGroup.add(sortByTimeMenuItem);
          sortMethodButtonGroup.add(sortByCreationTimeMenuItem);
          sortMethodButtonGroup.add(sortByLastModifiedTimeMenuItem);
          sortMethodButtonGroup.add(sortByLastAccessTimeMenuItem);
          sortMethodButtonGroup.add(sortBySizeMenuItem);
  
          sortPopupMenu.add(ascendingMenuItem);
          sortPopupMenu.add(descendingMenuItem);
          sortPopupMenu.addSeparator();
          sortPopupMenu.add(sortBySongNameAndFileNameMenuItem);
          sortPopupMenu.add(sortBySongNameMenuItem);
          sortPopupMenu.add(sortByArtistNameMenuItem);
          sortPopupMenu.add(sortByAlbumNameMenuItem);
          sortPopupMenu.add(sortByFileNameMenuItem);
          sortPopupMenu.add(sortByTimeMenuItem);
          sortPopupMenu.add(sortByCreationTimeMenuItem);
          sortPopupMenu.add(sortByLastModifiedTimeMenuItem);
          sortPopupMenu.add(sortByLastAccessTimeMenuItem);
          sortPopupMenu.add(sortBySizeMenuItem);
          // 按钮绑定右键菜单，不过右键也会弹出
          sortToolButton.setComponentPopupMenu(sortPopupMenu);
          // 点击排序按钮事件
          sortToolButton.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1) return;
                  sortPopupMenu.show(sortToolButton, e.getX(), e.getY());
              }
          });
          // 点击上下移按钮事件
          moveUpToolButton.addActionListener(e -> {
              int index = collectionTabbedPane.getSelectedIndex();
              CustomList list;
              if (currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION || index == CollectionTabIndex.MUSIC) {
                  list = musicList;
              } else list = collectionList;
              int selectedIndex = list.getSelectedIndex();
              if (selectedIndex != -1 && selectedIndex != 0) {
                  DefaultListModel model = (DefaultListModel) list.getModel();
                  Resource r1 = (Resource) model.get(selectedIndex - 1);
                  Resource r2 = (Resource) model.get(selectedIndex);
                  model.set(selectedIndex - 1, r2);
                  model.set(selectedIndex, r1);
                  list.setSelectedIndex(selectedIndex - 1);
              }
          });
          moveDownToolButton.addActionListener(e -> {
              int index = collectionTabbedPane.getSelectedIndex();
              CustomList list;
              if (currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION
                      || index == CollectionTabIndex.MUSIC) {
                  list = musicList;
              } else list = collectionList;
              int selectedIndex = list.getSelectedIndex();
              DefaultListModel model = (DefaultListModel) list.getModel();
              if (selectedIndex != -1 && selectedIndex != model.getSize() - 1) {
                  Resource r1 = (Resource) model.get(selectedIndex);
                  Resource r2 = (Resource) model.get(selectedIndex + 1);
                  model.set(selectedIndex, r2);
                  model.set(selectedIndex + 1, r1);
                  list.setSelectedIndex(selectedIndex + 1);
              }
          });
          // 清除输入事件
          clearInputToolButton.addActionListener(e -> {
              filterTextField.requestFocus();
              filterTextField.setText("");
              removeToolButton.requestFocus();
          });
          stylePopupMenu.add(manageStyleMenuItem);
          stylePopupMenu.add(styleCustomMenuItem);
          // 个人音乐筛选框
          filterTextField.addFocusListener(new TextFieldHintListener(filterTextField, I18n.getText("filterByKeyword"), currUIStyle.getForeColor()));
          filterTextField.getDocument().addDocumentListener(new DocumentListener() {
              @Override
              public void insertUpdate(DocumentEvent e) {
                  if (filterTextField.isOccupied()) {
                      clearInputToolButton.setVisible(true);
                      filterPersonalMusic();
                  }
              }
  
              @Override
              public void removeUpdate(DocumentEvent e) {
                  if (filterTextField.getText().isEmpty()) clearInputToolButton.setVisible(false);
                  filterPersonalMusic();
              }
  
              @Override
              public void changedUpdate(DocumentEvent e) {
  
              }
          });
          // 帮助提示
          addToolButton.setToolTipText(ADD_TIP);
          reimportToolButton.setToolTipText(REIMPORT_TIP);
          manageCatalogToolButton.setToolTipText(MANAGE_CATALOG_TIP);
          removeToolButton.setToolTipText(REMOVE_TIP);
          clearToolButton.setToolTipText(CLEAR_TIP);
          duplicateToolButton.setToolTipText(DUPLICATE_TIP);
          reverseToolButton.setToolTipText(REVERSE_TIP);
          sortToolButton.setToolTipText(SORT_TIP);
          moveUpToolButton.setToolTipText(MOVE_UP_TIP);
          moveDownToolButton.setToolTipText(MOVE_DOWN_TIP);
          clearInputToolButton.setToolTipText(CLEAR_INPUT_TIP);
          // 清除输入按钮初始不可见
          clearInputToolButton.setVisible(false);
  
          musicToolBar.add(addToolButton);
          musicToolBar.add(reimportToolButton);
          musicToolBar.add(manageCatalogToolButton);
          musicToolBar.add(removeToolButton);
          musicToolBar.add(clearToolButton);
          musicToolBar.add(duplicateToolButton);
          musicToolBar.add(reverseToolButton);
          musicToolBar.add(sortToolButton);
          musicToolBar.add(moveUpToolButton);
          musicToolBar.add(moveDownToolButton);
          musicToolBar.add(filterTextField);
          musicToolBar.add(clearInputToolButton);
          musicToolBar.add(countLabel);
          // 加胶水让工具栏左对齐
          musicToolBar.add(Box.createHorizontalGlue());
          leftBox.add(musicToolBar);
      }
  
      // 初始化个人音乐列表
      private void initMusicList() {
          ListDataListener countListener = new ListDataListener() {
              @Override
              public void intervalAdded(ListDataEvent e) {
                  DefaultListModel model = (DefaultListModel) e.getSource();
                  int size = model.getSize();
                  int selectedIndex = collectionTabbedPane.getSelectedIndex();
                  if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC && (model == musicListModel || model == filterModel)
                          || currPersonalMusicTab == PersonalMusicTabIndex.HISTORY && (model == historyModel || model == filterModel)) {
                      countLabel.setText(String.format(TOTAL_MSG, size));
                      musicList.setModel(model);
                      leftBox.remove(emptyHintPanel);
                      leftBox.add(musicScrollPane);
                      leftBox.repaint();
                  } else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION && selectedIndex == CollectionTabIndex.MUSIC
                          && (model == collectionModel || model == filterModel)) {
                      countLabel.setText(String.format(TOTAL_MSG, size));
                      musicList.setModel(model);
                      musicCollectionLeftBox.remove(emptyHintPanel);
                      musicCollectionLeftBox.add(musicScrollPane);
                      musicCollectionLeftBox.repaint();
                  } else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION && selectedIndex != CollectionTabIndex.MUSIC) {
                      if (selectedIndex == CollectionTabIndex.PLAYLIST && (model == playlistCollectionModel || model == filterModel)) {
                          collectionList.setModel(model);
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.ALBUM && (model == albumCollectionModel || model == filterModel)) {
                          collectionList.setModel(model);
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.ARTIST && (model == artistCollectionModel || model == filterModel)) {
                          collectionList.setModel(model);
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.RADIO && (model == radioCollectionModel || model == filterModel)) {
                          collectionList.setModel(model);
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.MV && (model == mvCollectionModel || model == filterModel)) {
                          collectionList.setModel(model);
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.RANKING && (model == rankingCollectionModel || model == filterModel)) {
                          collectionList.setModel(model);
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.USER && (model == userCollectionModel || model == filterModel)) {
                          collectionList.setModel(model);
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      }
  
                      Box box = null;
                      if (selectedIndex == CollectionTabIndex.PLAYLIST) box = playlistCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.ALBUM) box = albumCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.ARTIST) box = artistCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.RADIO) box = radioCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.MV) box = mvCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.RANKING) box = rankingCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.USER) box = userCollectionLeftBox;
                      box.remove(emptyHintPanel);
                      box.add(collectionScrollPane);
                      box.repaint();
                  }
              }
  
              @Override
              public void intervalRemoved(ListDataEvent e) {
                  DefaultListModel model = (DefaultListModel) e.getSource();
                  int size = model.getSize();
                  int selectedIndex = collectionTabbedPane.getSelectedIndex();
                  if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC && (model == musicListModel || model == filterModel)
                          || currPersonalMusicTab == PersonalMusicTabIndex.HISTORY && (model == historyModel || model == filterModel)) {
                      countLabel.setText(String.format(TOTAL_MSG, size));
                      if (size == 0) {
                          leftBox.add(emptyHintPanel);
                          leftBox.remove(musicScrollPane);
                          leftBox.repaint();
                      }
                  } else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION && selectedIndex == CollectionTabIndex.MUSIC
                          && (model == collectionModel || model == filterModel)) {
                      countLabel.setText(String.format(TOTAL_MSG, size));
                      if (size == 0) {
                          musicCollectionLeftBox.remove(musicScrollPane);
                          musicCollectionLeftBox.add(emptyHintPanel);
                          musicCollectionLeftBox.repaint();
                      }
                  } else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION && selectedIndex != CollectionTabIndex.MUSIC) {
                      if (selectedIndex == CollectionTabIndex.PLAYLIST && (model == playlistCollectionModel || model == filterModel)) {
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.ALBUM && (model == albumCollectionModel || model == filterModel)) {
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.ARTIST && (model == artistCollectionModel || model == filterModel)) {
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.RADIO && (model == radioCollectionModel || model == filterModel)) {
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.MV && (model == mvCollectionModel || model == filterModel)) {
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.RANKING && (model == rankingCollectionModel || model == filterModel)) {
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      } else if (selectedIndex == CollectionTabIndex.USER && (model == userCollectionModel || model == filterModel)) {
                          countLabel.setText(String.format(TOTAL_MSG, size));
                      }
                      if (size == 0) {
                          Box box = null;
                          if (selectedIndex == CollectionTabIndex.PLAYLIST) box = playlistCollectionLeftBox;
                          else if (selectedIndex == CollectionTabIndex.ALBUM) box = albumCollectionLeftBox;
                          else if (selectedIndex == CollectionTabIndex.ARTIST) box = artistCollectionLeftBox;
                          else if (selectedIndex == CollectionTabIndex.RADIO) box = radioCollectionLeftBox;
                          else if (selectedIndex == CollectionTabIndex.MV) box = mvCollectionLeftBox;
                          else if (selectedIndex == CollectionTabIndex.RANKING) box = rankingCollectionLeftBox;
                          else if (selectedIndex == CollectionTabIndex.USER) box = userCollectionLeftBox;
                          box.remove(collectionScrollPane);
                          box.add(emptyHintPanel);
                          box.repaint();
                      }
                  }
              }
  
              @Override
              public void contentsChanged(ListDataEvent e) {
  
              }
          };
          musicListModel.addListDataListener(countListener);
          historyModel.addListDataListener(countListener);
          collectionModel.addListDataListener(countListener);
          playlistCollectionModel.addListDataListener(countListener);
          albumCollectionModel.addListDataListener(countListener);
          artistCollectionModel.addListDataListener(countListener);
          radioCollectionModel.addListDataListener(countListener);
          mvCollectionModel.addListDataListener(countListener);
          rankingCollectionModel.addListDataListener(countListener);
          userCollectionModel.addListDataListener(countListener);
          filterModel.addListDataListener(countListener);
          musicList.setModel(musicListModel);
          musicList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      playExecutor.execute(() -> playSelected(musicList, false));
                  }
              }
          });
          musicList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = musicList.locationToIndex(e.getPoint());
                  Rectangle bounds = musicList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  MusicListRenderer renderer = (MusicListRenderer) musicList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  musicList.repaint();
              }
          });
          musicList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  MusicListRenderer renderer = (MusicListRenderer) musicList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  musicList.repaint();
              }
          });
          musicList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击播放
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      playExecutor.execute(() -> playSelected(musicList, false));
                  }
                  // 鼠标右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (musicList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = musicList.locationToIndex(e.getPoint());
                          if (index != -1 && !musicList.isSelectedIndex(index)
                                  || musicList.isSelectedIndex(index) && musicList.getSelectedIndices().length == 1) {
                              musicList.setSelectedIndex(index);
                              MusicResource resource = musicList.getSelectedValue();
                              boolean ins = resource instanceof NetMusicInfo;
                              downloadMenuItem.setEnabled(ins);
                              commentMenuItem.setEnabled(ins);
                              sheetMenuItem.setEnabled(ins);
                              similarSongMenuItem.setEnabled(ins);
                              relatedPlaylistMenuItem.setEnabled(ins);
                              authorMenuItem.setEnabled(ins);
                              albumMenuItem.setEnabled(ins);
                              recRadioMenuItem.setEnabled(ins);
                              relatedMvMenuItem.setEnabled(ins);
                              playMvMenuItem.setEnabled(ins && ((NetMusicInfo) resource).hasMv());
                              if (hasBeenCollected(resource)) {
                                  collectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                                  collectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                              } else {
                                  collectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                                  collectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                              }
                              locateFileMenuItem.setEnabled(!ins);
                              editInfoMenuItem.setEnabled(!ins);
                          } else {
                              List<MusicResource> list = musicList.getSelectedValuesList();
                              for (MusicResource resource : list) {
                                  downloadMenuItem.setEnabled(resource instanceof NetMusicInfo);
                                  if (downloadMenuItem.isEnabled()) break;
                              }
                              MusicResource first = list.get(0);
                              boolean ins = first instanceof NetMusicInfo;
                              commentMenuItem.setEnabled(ins);
                              sheetMenuItem.setEnabled(ins);
                              similarSongMenuItem.setEnabled(ins);
                              relatedPlaylistMenuItem.setEnabled(ins);
                              authorMenuItem.setEnabled(ins);
                              albumMenuItem.setEnabled(ins);
                              recRadioMenuItem.setEnabled(ins);
                              relatedMvMenuItem.setEnabled(ins);
                              playMvMenuItem.setEnabled(ins);
                              if (hasBeenCollected(first)) {
                                  collectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                                  collectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                              } else {
                                  collectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                                  collectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                              }
                              locateFileMenuItem.setEnabled(!ins);
                              editInfoMenuItem.setEnabled(!ins);
                          }
  
                          musicPopupMenu.show(musicList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 右键菜单播放
          playMenuItem.addActionListener(e -> playExecutor.execute(() -> playSelected(musicList, false)));
          // 下一首播放
          nextPlayMenuItem.addActionListener(e -> nextPlay(musicList));
          // 右键菜单打开文件所在位置
          locateFileMenuItem.addActionListener(e -> {
              MusicResource resource = musicList.getSelectedValue();
              if (resource instanceof AudioFile) {
                  File file = ((AudioFile) resource);
                  DesktopUtil.explore(file.exists() ? file : file.getParentFile());
              }
          });
          // 右键菜单编辑歌曲信息
          editInfoMenuItem.addActionListener(e -> {
              MusicResource resource = musicList.getSelectedValue();
              if (resource instanceof AudioFile) {
                  AudioFile file = ((AudioFile) resource);
                  if (file.exists()) {
                      editInfo(file);
                      updateRenderer(musicList);
                  } else new TipDialog(THIS, FILE_NOT_FOUND_MSG).showDialog();
              }
          });
          // 右键菜单删除
          removeMenuItem.addActionListener(e -> removeToolButton.doClick());
          // 右键菜单收藏/取消收藏
          collectMenuItem.addActionListener(e -> {
              ListModel<MusicResource> model = musicList.getModel();
              boolean needRefresh = model == collectionModel;
              if (collectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT)) {
                  List<MusicResource> values = musicList.getSelectedValuesList();
                  if (needRefresh) musicList.setModel(emptyListModel);
                  for (int i = values.size() - 1; i >= 0; i--) {
                      MusicResource resource = values.get(i);
                      if (hasBeenCollected(resource)) continue;
                      collectionModel.add(0, resource);
                      if (player.loadedMusicResource(resource))
                          collectButton.setIcon(ImageUtil.dye(hasCollectedIcon, currUIStyle.getIconColor()));
                  }
                  if (needRefresh) musicList.setModel(model);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List<MusicResource> values = musicList.getSelectedValuesList();
                  if (needRefresh) musicList.setModel(emptyListModel);
                  values.forEach(resource -> {
                      if (hasBeenCollected(resource)) {
                          collectionModel.removeElement(resource);
                          if (player.loadedMusicResource(resource))
                              collectButton.setIcon(ImageUtil.dye(collectIcon, currUIStyle.getIconColor()));
                      }
                  });
                  if (needRefresh) musicList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 右键菜单播放 MV
          playMvMenuItem.addActionListener(e -> playMv(MvCompSourceType.MUSIC_LIST));
          // 右键菜单下载
          downloadMenuItem.addActionListener(e -> {
              List values = musicList.getSelectedValuesList();
              multiDownload(values);
          });
          // 右键菜单查看评论
          commentMenuItem.addActionListener(e -> {
              MusicResource resource = musicList.getSelectedValue();
              if (resource instanceof NetMusicInfo) {
                  getComments((NetMusicInfo) resource, true);
              }
          });
          // 右键菜单查看乐谱
          sheetMenuItem.addActionListener(e -> {
              MusicResource resource = musicList.getSelectedValue();
              if (resource instanceof NetMusicInfo) {
                  getSheets((NetMusicInfo) resource, true);
              }
          });
          // 右键菜单搜索这首歌曲
          searchSongMenuItem.addActionListener(e -> {
              MusicResource resource = musicList.getSelectedValue();
              tabbedPane.setSelectedIndex(TabIndex.NET_MUSIC);
              if (currMusicMusicInfo != null) netMusicBackwardButton.doClick();
              searchTextField.requestFocus();
              if (resource instanceof NetMusicInfo) {
                  searchTextField.setText(((NetMusicInfo) resource).toKeywords());
              } else searchTextField.setText(((AudioFile) resource).toKeywords());
              netMusicClearInputButton.setVisible(true);
              searchButton.doClick();
          });
          // 右键菜单查看相似歌曲
          similarSongMenuItem.addActionListener(e -> netMusicSimilarSongMenuItem.doClick());
          // 右键菜单查看相关歌单
          relatedPlaylistMenuItem.addActionListener(e -> netMusicRelatedPlaylistMenuItem.doClick());
          // 查看歌手/作者
          authorMenuItem.addActionListener(e -> netMusicAuthorMenuItem.doClick());
          // 查看专辑/电台
          albumMenuItem.addActionListener(e -> netMusicAlbumMenuItem.doClick());
          // 查看推荐电台
          recRadioMenuItem.addActionListener(e -> netMusicRecRadioMenuItem.doClick());
          // 右键菜单查看相关 MV
          relatedMvMenuItem.addActionListener(e -> netMusicRelatedMvMenuItem.doClick());
          // 右键菜单复制名称
          copyNameMenuItem.addActionListener(e -> {
              MusicResource resource = musicList.getSelectedValue();
              String s = null;
              if (resource instanceof AudioFile) s = resource.toString();
              else if (resource instanceof NetMusicInfo) s = ((NetMusicInfo) resource).toSimpleString();
              copyToClipboard(s);
          });
  
          musicPopupMenu.add(playMenuItem);
          musicPopupMenu.add(nextPlayMenuItem);
          musicPopupMenu.add(playMvMenuItem);
          musicPopupMenu.add(collectMenuItem);
          musicPopupMenu.add(downloadMenuItem);
          musicPopupMenu.add(commentMenuItem);
          musicPopupMenu.add(sheetMenuItem);
          musicPopupMenu.add(searchSongMenuItem);
          musicPopupMenu.add(similarSongMenuItem);
          musicPopupMenu.add(relatedPlaylistMenuItem);
          musicPopupMenu.add(authorMenuItem);
          musicPopupMenu.add(albumMenuItem);
          musicPopupMenu.add(recRadioMenuItem);
          musicPopupMenu.add(relatedMvMenuItem);
          musicPopupMenu.add(copyNameMenuItem);
          musicPopupMenu.add(locateFileMenuItem);
          musicPopupMenu.add(editInfoMenuItem);
          musicPopupMenu.add(removeMenuItem);
  
          // 最佳大小
          musicScrollPane.setPreferredSize(new Dimension(260, 3000));
          leftBox.add(musicScrollPane);
      }
  
      // 初始化在线音乐工具栏
      private void initNetMusicToolBar() {
          searchTextField.addFocusListener(new TextFieldHintListener(searchTextField, "单曲/歌手/专辑/歌词/节目", currUIStyle.getForeColor()));
          searchTextField.getDocument().addDocumentListener(new DocumentListener() {
              @Override
              public void insertUpdate(DocumentEvent e) {
                  if (searchTextField.isOccupied()) netMusicClearInputButton.setVisible(true);
                  updateSearchSug();
              }
  
              @Override
              public void removeUpdate(DocumentEvent e) {
                  if (searchTextField.getText().isEmpty()) netMusicClearInputButton.setVisible(false);
                  updateSearchSug();
              }
  
              @Override
              public void changedUpdate(DocumentEvent e) {
  
              }
  
              private void updateSearchSug() {
                  if (!searchTextField.getText().isEmpty() && searchSuggestionProcessing) return;
                  searchSuggestionProcessing = true;
                  searchSuggestionTimer.start();
              }
          });
          // 只能输入数字
          netMusicPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 返回关键词面板事件
          netMusicBackwardButton.addActionListener(e -> {
              netMusicListModel.clear();
              netMusicCountPanel.setVisible(false);
              netLeftBox.remove(emptyHintPanel);
              netLeftBox.remove(netMusicScrollPane);
              netLeftBox.add(netMusicKeywordsPanelScrollPane);
              netMusicBackwardButton.setEnabled(false);
  
              // 添加回工具栏的搜索/标题
              netMusicToolBar.removeAll();
              netMusicToolBar.add(netMusicBackwardButton);
              // 删除标题标签
              netMusicToolBar.add(searchTextField);
              netMusicToolBar.add(netMusicClearInputButton);
              netMusicToolBar.add(searchButton);
              currMusicMusicInfo = null;
  
              netLeftBox.repaint();
          });
          // 清除输入事件
          netMusicClearInputButton.addActionListener(e -> {
              searchTextField.requestFocus();
              searchTextField.setText("");
              netMusicBackwardButton.requestFocus();
          });
          // 搜索按钮事件
          searchButton.addActionListener(e -> {
              netMusicCurrKeyword = searchTextField.getText().trim();
              if (!netMusicCurrKeyword.isEmpty()) {
                  // 关键词加入搜索历史
                  DialogButton b = new DialogButton(netMusicCurrKeyword);
                  b.addActionListener(event -> {
                      searchTextField.requestFocus();
                      searchTextField.setText(b.getPlainText());
                      netMusicClearInputButton.setVisible(true);
                      searchButton.doClick();
                      netLeftBox.remove(netMusicKeywordsPanelScrollPane);
                      netLeftBox.add(netMusicScrollPane);
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netMusicHistorySearchInnerPanel2.remove(b);
                          netMusicHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  // 先删除重复的关键词
                  removeKeywordInHistorySearch(netMusicCurrKeyword, HistorySearchType.NET_MUSIC);
                  netMusicHistorySearchInnerPanel2.add(b, 0);
                  // 删除超出上限的最后一个关键词
                  int c = netMusicHistorySearchInnerPanel2.getComponentCount();
                  if (c > maxSearchHistoryCount)
                      netMusicHistorySearchInnerPanel2.remove(c - 1);
                  netMusicHistorySearchPanel.repaint();
  
                  loadingAndRun(() -> {
                      try {
                          // 显示节目搜索分类标签
                          if (netMusicSearchTypeComboBox.getSelectedIndex() == 2 && Tags.programSearchTag.isEmpty()) {
                              MusicServerUtil.initProgramSearchTag();
                              for (String tag : Tags.programSearchTag.keySet())
                                  netMusicSearchSubTypeComboBox.addItem(tag);
                          }
  
                          // 搜索歌曲并显示在在线歌曲列表
                          CommonResult<NetMusicInfo> result = MusicServerUtil.searchMusic(netMusicSourceComboBox.getSelectedIndex(),
                                  netMusicSearchTypeComboBox.getSelectedIndex(), (String) netMusicSearchSubTypeComboBox.getSelectedItem(), netMusicCurrKeyword, netMusicCurrPage = 1, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          Integer total = result.total;
                          netMusicMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netMusicCountLabel.setText(String.format(PAGINATION_MSG, netMusicCurrPage, netMusicMaxPage));
                          netMusicCountPanel.add(netMusicCountLabel, netMusicCountPanel.getComponentIndex(netMusicCountLabel));
                          netMusicCountPanel.setVisible(true);
                          netMusicSearchTypeComboBox.setVisible(true);
                          netMusicSearchSubTypeComboBox.setVisible(netMusicSearchTypeComboBox.getSelectedIndex() == 2);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicListModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListModel);
                          netMusicBackwardButton.setEnabled(true);
                          netLeftBox.remove(netMusicKeywordsPanelScrollPane);
                          if (netMusicListModel.isEmpty()) {
                              netLeftBox.remove(netMusicScrollPane);
                              netLeftBox.add(emptyHintPanel);
                          } else {
                              netLeftBox.remove(emptyHintPanel);
                              netLeftBox.add(netMusicScrollPane);
                          }
                          netMusicScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          });
          // 在线音乐跳页事件
          Runnable netMusicGoPageAction = () -> {
              boolean songRequest = currMusicMusicInfo != null;
              if (songRequest || StringUtil.notEmpty(netMusicCurrKeyword)) {
                  loadingAndRun(() -> {
                      try {
                          // 显示节目搜索分类标签
                          if (netMusicSearchTypeComboBox.getSelectedIndex() == 2 && Tags.programSearchTag.isEmpty()) {
                              MusicServerUtil.initProgramSearchTag();
                              for (String tag : Tags.programSearchTag.keySet())
                                  netMusicSearchSubTypeComboBox.addItem(tag);
                          }
  
                          // 搜索歌曲并显示在在线歌曲列表
                          CommonResult<NetMusicInfo> result = songRequest ? MusicServerUtil.getSimilarSongs(currMusicMusicInfo)
                                  : MusicServerUtil.searchMusic(netMusicSourceComboBox.getSelectedIndex(), netMusicSearchTypeComboBox.getSelectedIndex(),
                                  (String) netMusicSearchSubTypeComboBox.getSelectedItem(), netMusicCurrKeyword, netMusicCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          Integer total = result.total;
                          netMusicMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netMusicCountLabel.setText(String.format(PAGINATION_MSG, netMusicCurrPage, netMusicMaxPage));
                          netMusicCountPanel.add(netMusicCountLabel, netMusicCountPanel.getComponentIndex(netMusicCountLabel));
                          netMusicCountPanel.setVisible(true);
                          netMusicSearchSubTypeComboBox.setVisible(netMusicSearchTypeComboBox.getSelectedIndex() == 2);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicListModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicListModel.isEmpty()) {
                              netLeftBox.remove(netMusicScrollPane);
                              netLeftBox.add(emptyHintPanel);
                          } else {
                              netLeftBox.remove(emptyHintPanel);
                              netLeftBox.add(netMusicScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          for (String name : NetMusicSource.NAMES) netMusicSourceComboBox.addItem(name);
          netMusicSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              searchButton.doClick();
          });
          netMusicSearchTypeComboBox.addItem(I18n.getText("general"));
          netMusicSearchTypeComboBox.addItem(I18n.getText("lyric"));
          netMusicSearchTypeComboBox.addItem(I18n.getText("program"));
          netMusicSearchTypeComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netMusicCurrKeyword = searchTextField.getText().trim();
              searchButton.doClick();
          });
          netMusicSearchSubTypeComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netMusicCurrKeyword = searchTextField.getText().trim();
              searchButton.doClick();
          });
          netMusicSearchSubTypeComboBox.setVisible(false);
          // 刷新按钮事件
          netMusicRefreshButton.addActionListener(e -> {
              netMusicGoPageAction.run();
          });
          // 第一页按钮
          netMusicStartPageButton.addActionListener(e -> {
              if (netMusicCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netMusicCurrPage = 1;
              netMusicGoPageAction.run();
          });
          // 上一页按钮事件
          netMusicLastPageButton.addActionListener(e -> {
              if (netMusicCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netMusicCurrPage--;
              netMusicGoPageAction.run();
          });
          // 下一页按钮事件
          netMusicNextPageButton.addActionListener(e -> {
              if (netMusicCurrPage >= netMusicMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netMusicCurrPage++;
              netMusicGoPageAction.run();
          });
          // 最后一页按钮
          netMusicEndPageButton.addActionListener(e -> {
              if (netMusicCurrPage >= netMusicMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netMusicCurrPage = netMusicMaxPage;
              netMusicGoPageAction.run();
          });
          // 跳页按钮事件
          netMusicGoButton.addActionListener(e -> {
              String text = netMusicPageTextField.getText();
              netMusicPageTextField.setText("");
              if (text.isEmpty()) return;
              int destPage = Integer.parseInt(text);
              if (destPage < 1 || destPage > netMusicMaxPage) {
                  new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                  return;
              }
              netMusicCurrPage = destPage;
              netMusicGoPageAction.run();
          });
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netMusicRefreshButton.setPreferredSize(dimension);
          netMusicStartPageButton.setPreferredSize(dimension);
          netMusicLastPageButton.setPreferredSize(dimension);
          netMusicGoButton.setPreferredSize(dimension);
          netMusicNextPageButton.setPreferredSize(dimension);
          netMusicEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netMusicBackwardButton.addMouseListener(new ButtonMouseListener(netMusicBackwardButton, THIS));
          netMusicClearInputButton.addMouseListener(new ButtonMouseListener(netMusicClearInputButton, THIS));
          searchButton.addMouseListener(new ButtonMouseListener(searchButton, THIS));
          netMusicRefreshButton.addMouseListener(new ButtonMouseListener(netMusicRefreshButton, THIS));
          netMusicStartPageButton.addMouseListener(new ButtonMouseListener(netMusicStartPageButton, THIS));
          netMusicLastPageButton.addMouseListener(new ButtonMouseListener(netMusicLastPageButton, THIS));
          netMusicGoButton.addMouseListener(new ButtonMouseListener(netMusicGoButton, THIS));
          netMusicNextPageButton.addMouseListener(new ButtonMouseListener(netMusicNextPageButton, THIS));
          netMusicEndPageButton.addMouseListener(new ButtonMouseListener(netMusicEndPageButton, THIS));
          // 帮助提示
          netMusicBackwardButton.setToolTipText(SHOW_KEYWORD_PANEL_TIP);
          netMusicClearInputButton.setToolTipText(CLEAR_INPUT_TIP);
          searchButton.setToolTipText(SEARCH_TIP);
          netMusicRefreshButton.setToolTipText(REFRESH_TIP);
          netMusicStartPageButton.setToolTipText(START_PAGE_TIP);
          netMusicLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netMusicGoButton.setToolTipText(GO_TIP);
          netMusicNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netMusicEndPageButton.setToolTipText(END_PAGE_TIP);
  
          // 回到关键词面板起始不可用
          netMusicBackwardButton.setEnabled(false);
          // 清除输入按钮初始不可见
          netMusicClearInputButton.setVisible(false);
  
          netMusicToolBar.add(netMusicBackwardButton);
          netMusicToolBar.add(searchTextField);
          netMusicToolBar.add(netMusicClearInputButton);
          netMusicToolBar.add(searchButton);
          netLeftBox.add(netMusicToolBar);
  
          // 歌曲数量标签
          netMusicCountPanel.add(netMusicSourceComboBox);
          netMusicCountPanel.add(netMusicSearchTypeComboBox);
          netMusicCountPanel.add(netMusicSearchSubTypeComboBox);
          netMusicCountPanel.add(netMusicStartPageButton);
          netMusicCountPanel.add(netMusicLastPageButton);
          netMusicCountPanel.add(netMusicCountLabel);
          netMusicCountPanel.add(netMusicNextPageButton);
          netMusicCountPanel.add(netMusicEndPageButton);
          netMusicCountPanel.add(netMusicRefreshButton);
          netMusicCountPanel.add(netMusicPageTextField);
          netMusicCountPanel.add(netMusicGoButton);
          netMusicCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netMusicCountPanel.setVisible(false);
          netLeftBox.add(netMusicCountPanel);
      }
  
      // 初始化在线音乐列表
      private void initNetMusicList() {
          netMusicList.setModel(netMusicListModel);
          netMusicList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      playExecutor.execute(() -> playSelected(netMusicList, false));
                  }
              }
          });
          netMusicList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netMusicList.locationToIndex(e.getPoint());
                  Rectangle bounds = netMusicList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetMusicListRenderer renderer = (NetMusicListRenderer) netMusicList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netMusicList.repaint();
              }
          });
          netMusicList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetMusicListRenderer renderer = (NetMusicListRenderer) netMusicList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netMusicList.repaint();
              }
          });
          netMusicList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击播放
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      playExecutor.execute(() -> playSelected(netMusicList, false));
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netMusicList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netMusicList.locationToIndex(e.getPoint());
                          if (index == -1) return;
                          if (!netMusicList.isSelectedIndex(index)) netMusicList.setSelectedIndex(index);
                          NetMusicInfo musicInfo = netMusicList.getSelectedValue();
                          netMusicPlayMvMenuItem.setEnabled(musicInfo.hasMv());
                          if (hasBeenCollected(musicInfo)) {
                              netMusicCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netMusicCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                          } else {
                              netMusicCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                              netMusicCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                          }
  
                          netMusicPopupMenu.show(netMusicList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 播放在线音乐
          netMusicPlayMenuItem.addActionListener(e -> {
              playExecutor.execute(() -> playSelected(netMusicList, false));
          });
          // 下一首播放
          netMusicNextPlayMenuItem.addActionListener(e -> nextPlay(netMusicList));
          // 收藏在线音乐
          netMusicCollectMenuItem.addActionListener(e -> {
              ListModel<MusicResource> model = musicList.getModel();
              boolean needRefresh = model == collectionModel;
              if (netMusicCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT)) {
                  List<NetMusicInfo> values = netMusicList.getSelectedValuesList();
                  if (needRefresh) musicList.setModel(emptyListModel);
                  for (int i = values.size() - 1; i >= 0; i--) {
                      NetMusicInfo musicInfo = values.get(i);
                      if (hasBeenCollected(musicInfo)) continue;
                      collectionModel.add(0, musicInfo);
                      if (player.loadedMusicResource(musicInfo))
                          collectButton.setIcon(ImageUtil.dye(hasCollectedIcon, currUIStyle.getIconColor()));
                  }
                  if (needRefresh) musicList.setModel(model);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List<NetMusicInfo> values = netMusicList.getSelectedValuesList();
                  if (needRefresh) musicList.setModel(emptyListModel);
                  values.forEach(musicInfo -> {
                      if (hasBeenCollected(musicInfo)) {
                          collectionModel.removeElement(musicInfo);
                          if (player.loadedMusicResource(musicInfo))
                              collectButton.setIcon(ImageUtil.dye(collectIcon, currUIStyle.getIconColor()));
                      }
                  });
                  if (needRefresh) musicList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 播放在线音乐的 MV
          netMusicPlayMvMenuItem.addActionListener(e -> {
              playMv(MvCompSourceType.NET_MUSIC_LIST);
          });
          // 下载在线音乐
          netMusicDownloadMenuItem.addActionListener(e -> {
              List values = netMusicList.getSelectedValuesList();
              multiDownload(values);
          });
          // 查看评论
          netMusicCommentMenuItem.addActionListener(e -> {
              getComments(netMusicList.getSelectedValue(), true);
          });
          // 查看乐谱
          netMusicSheetMenuItem.addActionListener(e -> {
              getSheets(netMusicList.getSelectedValue(), true);
          });
          // 搜索这首歌曲
          netMusicSearchSongMenuItem.addActionListener(e -> {
              NetMusicInfo musicInfo = netMusicList.getSelectedValue();
              tabbedPane.setSelectedIndex(TabIndex.NET_MUSIC);
              if (currMusicMusicInfo != null) netMusicBackwardButton.doClick();
              searchTextField.requestFocus();
              searchTextField.setText(musicInfo.toKeywords());
              netMusicClearInputButton.setVisible(true);
              searchButton.doClick();
          });
          // 查看相似歌曲
          netMusicSimilarSongMenuItem.addActionListener(e -> {
              NetMusicInfo musicInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION
                      || selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION
                      && collectionTabbedPane.getSelectedIndex() == CollectionTabIndex.MUSIC) {
                  musicInfo = (NetMusicInfo) musicList.getSelectedValue();
              } else if (selectedIndex == TabIndex.PLAY_QUEUE) musicInfo = (NetMusicInfo) playQueue.getSelectedValue();
              else musicInfo = netMusicList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      // 获取相似歌曲
                      CommonResult<NetMusicInfo> result = MusicServerUtil.getSimilarSongs(currMusicMusicInfo = musicInfo);
                      List<NetMusicInfo> musicInfos = result.data;
                      netMusicCurrPage = netMusicMaxPage = 1;
                      // 标题
                      netMusicTitleLabel.setText(StringUtil.textToHtml(musicInfo.toSimpleString() + I18n.getText("similarTrackSuffix")));
                      netMusicToolBar.removeAll();
                      netMusicToolBar.add(netMusicBackwardButton);
                      netMusicToolBar.add(Box.createHorizontalGlue());
                      netMusicToolBar.add(netMusicTitleLabel);
                      netMusicToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netMusicCountLabel.setText(String.format(PAGINATION_MSG, netMusicCurrPage, netMusicMaxPage));
                      netMusicCountPanel.add(netMusicCountLabel, netMusicCountPanel.getComponentIndex(netMusicCountLabel));
                      netLeftBox.add(netMusicCountPanel);
                      netMusicCountPanel.setVisible(true);
                      netMusicSearchTypeComboBox.setVisible(false);
                      netMusicSearchSubTypeComboBox.setVisible(false);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netMusicList.setModel(emptyListModel);
                      netMusicListModel.clear();
                      musicInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              netMusicList.repaint();
                              collectionList.repaint();
                          });
                          netMusicListModel.addElement(info);
                      });
                      netMusicList.setModel(netMusicListModel);
                      netMusicBackwardButton.setEnabled(true);
                      tabbedPane.setSelectedIndex(TabIndex.NET_MUSIC);
                      netLeftBox.remove(netMusicKeywordsPanelScrollPane);
                      if (netMusicListModel.isEmpty()) {
                          netLeftBox.remove(netMusicScrollPane);
                          netLeftBox.add(emptyHintPanel);
                      } else {
                          netLeftBox.remove(emptyHintPanel);
                          netLeftBox.add(netMusicScrollPane);
                      }
                      netMusicScrollPane.setVValue(0);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看相关歌单
          netMusicRelatedPlaylistMenuItem.addActionListener(e -> {
              NetMusicInfo musicInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION
                      || selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION
                      && collectionTabbedPane.getSelectedIndex() == CollectionTabIndex.MUSIC) {
                  musicInfo = (NetMusicInfo) musicList.getSelectedValue();
              } else if (selectedIndex == TabIndex.PLAY_QUEUE) musicInfo = (NetMusicInfo) playQueue.getSelectedValue();
              else musicInfo = netMusicList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForPlaylist();
                      // 搜索相关歌单
                      CommonResult<NetPlaylistInfo> result = MusicServerUtil.getRelatedPlaylists(currPlaylistMusicInfo = musicInfo);
                      List<NetPlaylistInfo> playlistInfos = result.data;
                      netPlaylistCurrPage = netPlaylistMaxPage = 1;
                      // 标题
                      netPlaylistTitleLabel.setText(StringUtil.textToHtml(musicInfo.toSimpleString() + I18n.getText("relatedPlaylistSuffix")));
                      netPlaylistToolBar.removeAll();
                      netPlaylistToolBar.add(netPlaylistBackwardButton);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      netPlaylistToolBar.add(netPlaylistTitleLabel);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netPlaylistCurrPage, netPlaylistMaxPage));
                      netPlaylistCountPanel.add(netPlaylistCountLabel, netPlaylistCountPanel.getComponentIndex(netPlaylistCountLabel));
                      netPlaylistLeftBox.add(netPlaylistCountPanel);
                      netPlaylistSourceComboBox.setVisible(false);
                      netPlaylistPlayAllButton.setVisible(false);
                      netPlaylistCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netPlaylistList.setModel(emptyListModel);
                      netPlaylistListModel.clear();
                      playlistInfos.forEach(playlistInfo -> {
                          globalExecutor.execute(() -> updateCollection(playlistInfo));
                          // 设置图片加载后重绘的事件
                          playlistInfo.setInvokeLater(() -> {
                              updateRenderer(netPlaylistList);
                              updateRenderer(collectionList);
                              netPlaylistList.repaint();
                              collectionList.repaint();
                          });
                          netPlaylistListModel.addElement(playlistInfo);
                      });
                      netPlaylistList.setModel(netPlaylistListModel);
                      netPlaylistBackwardButton.setEnabled(true);
                      netPlaylistLeftBox.remove(netPlaylistKeywordsPanelScrollPane);
                      netPlaylistLeftBox.remove(playlistListBox);
                      if (netPlaylistListModel.isEmpty()) {
                          netPlaylistLeftBox.remove(netPlaylistScrollPane);
                          netPlaylistLeftBox.add(emptyHintPanel);
                      } else {
                          netPlaylistLeftBox.remove(emptyHintPanel);
                          netPlaylistLeftBox.add(netPlaylistScrollPane);
                      }
                      netPlaylistScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_PLAYLIST);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看歌手/作者
          netMusicAuthorMenuItem.addActionListener(e -> {
              NetMusicInfo musicInfo;
              if (currPane == MusicPane.LYRIC) musicInfo = player.getMusicInfo();
              else {
                  int selectedIndex = tabbedPane.getSelectedIndex();
                  if (selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION
                          || selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION
                          && collectionTabbedPane.getSelectedIndex() == CollectionTabIndex.MUSIC) {
                      musicInfo = (NetMusicInfo) musicList.getSelectedValue();
                  } else if (selectedIndex == TabIndex.PLAY_QUEUE)
                      musicInfo = (NetMusicInfo) playQueue.getSelectedValue();
                  else musicInfo = netMusicList.getSelectedValue();
              }
              loadingAndRun(() -> {
                  try {
                      if (musicInfo.isProgram()) {
                          clearRequestForUser();
                          // 获取作者
                          currAuthorMusicInfo = musicInfo;
                          CommonResult<NetUserInfo> result = MusicServerUtil.getUserInfo(musicInfo.getSource(), musicInfo.getArtistId());
                          List<NetUserInfo> userInfos = result.data;
                          netUserCurrPage = netUserMaxPage = 1;
                          // 标题
                          netUserTitleLabel.setText(StringUtil.textToHtml(musicInfo.toSimpleString() + I18n.getText("authorSuffix")));
                          netUserToolBar.removeAll();
                          netUserToolBar.add(netUserBackwardButton);
                          netUserToolBar.add(Box.createHorizontalGlue());
                          netUserToolBar.add(netUserTitleLabel);
                          netUserToolBar.add(Box.createHorizontalGlue());
                          // 更新数量显示
                          netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                          netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                          netUserLeftBox.add(netUserCountPanel);
                          netUserSourceComboBox.setVisible(false);
                          netUserPlayAllButton.setVisible(false);
                          netUserRecordTypeComboBox.setVisible(false);
                          netUserCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netUserList.setModel(emptyListModel);
                          netUserListModel.clear();
                          userInfos.forEach(userInfo -> {
                              globalExecutor.execute(() -> updateCollection(userInfo));
                              // 设置图片加载后重绘的事件
                              userInfo.setInvokeLater(() -> {
                                  updateRenderer(netUserList);
                                  updateRenderer(collectionList);
                                  netUserList.repaint();
                                  collectionList.repaint();
                              });
                              netUserListModel.addElement(userInfo);
                          });
                          netUserList.setModel(netUserListModel);
                          netUserBackwardButton.setEnabled(true);
                          netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                          netUserLeftBox.remove(userListBox);
                          if (netUserListModel.isEmpty()) {
                              netUserLeftBox.remove(netUserScrollPane);
                              netUserLeftBox.add(emptyHintPanel);
                          } else {
                              netUserLeftBox.remove(emptyHintPanel);
                              netUserLeftBox.add(netUserScrollPane);
                          }
                          netUserScrollPane.setVValue(0);
                          tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                      } else {
                          clearRequestForArtist();
                          // 获取歌手
                          currArtistMusicInfo = musicInfo;
                          CommonResult<NetArtistInfo> result = MusicServerUtil.getArtistInfo(musicInfo.getSource(), musicInfo.getArtistId());
                          List<NetArtistInfo> artistInfos = result.data;
                          netArtistCurrPage = netArtistMaxPage = 1;
                          // 标题
                          netArtistTitleLabel.setText(StringUtil.textToHtml(currArtistMusicInfo.toSimpleString() + I18n.getText("artistSuffix")));
                          netArtistToolBar.removeAll();
                          netArtistToolBar.add(netArtistBackwardButton);
                          netArtistToolBar.add(Box.createHorizontalGlue());
                          netArtistToolBar.add(netArtistTitleLabel);
                          netArtistToolBar.add(Box.createHorizontalGlue());
                          // 更新数量显示
                          netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
                          netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                          netArtistLeftBox.add(netArtistCountPanel);
                          netArtistSourceComboBox.setVisible(false);
                          netArtistPlayAllButton.setVisible(false);
                          netArtistCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netArtistList.setModel(emptyListModel);
                          netArtistListModel.clear();
                          artistInfos.forEach(artistInfo -> {
                              globalExecutor.execute(() -> updateCollection(artistInfo));
                              // 设置图片加载后重绘的事件
                              artistInfo.setInvokeLater(() -> {
                                  updateRenderer(netArtistList);
                                  updateRenderer(collectionList);
                                  netArtistList.repaint();
                                  collectionList.repaint();
                              });
                              netArtistListModel.addElement(artistInfo);
                          });
                          netArtistList.setModel(netArtistListModel);
                          netArtistBackwardButton.setEnabled(true);
                          netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                          netArtistLeftBox.remove(artistListBox);
                          if (netArtistListModel.isEmpty()) {
                              netArtistLeftBox.remove(netArtistScrollPane);
                              netArtistLeftBox.add(emptyHintPanel);
                          } else {
                              netArtistLeftBox.remove(emptyHintPanel);
                              netArtistLeftBox.add(netArtistScrollPane);
                          }
                          netArtistScrollPane.setVValue(0);
                          tabbedPane.setSelectedIndex(TabIndex.NET_ARTIST);
                      }
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看专辑/电台
          netMusicAlbumMenuItem.addActionListener(e -> {
              NetMusicInfo musicInfo;
              if (currPane == MusicPane.LYRIC) musicInfo = player.getMusicInfo();
              else {
                  int selectedIndex = tabbedPane.getSelectedIndex();
                  if (selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION
                          || selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION
                          && collectionTabbedPane.getSelectedIndex() == CollectionTabIndex.MUSIC) {
                      musicInfo = (NetMusicInfo) musicList.getSelectedValue();
                  } else if (selectedIndex == TabIndex.PLAY_QUEUE)
                      musicInfo = (NetMusicInfo) playQueue.getSelectedValue();
                  else musicInfo = netMusicList.getSelectedValue();
              }
              loadingAndRun(() -> {
                  try {
                      if (musicInfo.isProgram()) {
                          clearRequestForRadio();
                          // 获取歌曲电台
                          currRadioMusicInfo = musicInfo;
                          CommonResult<NetRadioInfo> result = MusicServerUtil.getRadioInfo(musicInfo.getSource(), musicInfo.getAlbumId());
                          List<NetRadioInfo> radioInfos = result.data;
                          int total = result.total;
                          netRadioMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 标题
                          netRadioTitleLabel.setText(StringUtil.textToHtml(musicInfo.toSimpleString() + I18n.getText("radioSuffix")));
                          netRadioToolBar.removeAll();
                          netRadioToolBar.add(netRadioBackwardButton);
                          netRadioToolBar.add(Box.createHorizontalGlue());
                          netRadioToolBar.add(netRadioTitleLabel);
                          netRadioToolBar.add(Box.createHorizontalGlue());
                          // 更新数量显示
                          netRadioCountLabel.setText(String.format(PAGINATION_MSG, netRadioCurrPage, netRadioMaxPage));
                          netRadioCountPanel.add(netRadioCountLabel, netRadioCountPanel.getComponentIndex(netRadioCountLabel));
                          netRadioLeftBox.add(netRadioCountPanel);
                          netRadioSourceComboBox.setVisible(false);
                          netRadioSortTypeComboBox.setVisible(false);
                          netRadioPlayAllButton.setVisible(false);
                          netRadioCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netRadioList.setModel(emptyListModel);
                          netRadioListModel.clear();
                          radioInfos.forEach(radioInfo -> {
                              globalExecutor.execute(() -> updateCollection(radioInfo));
                              // 设置图片加载后重绘的事件
                              radioInfo.setInvokeLater(() -> {
                                  updateRenderer(netRadioList);
                                  updateRenderer(collectionList);
                                  netRadioList.repaint();
                                  collectionList.repaint();
                              });
                              netRadioListModel.addElement(radioInfo);
                          });
                          netRadioList.setModel(netRadioListModel);
                          netRadioBackwardButton.setEnabled(true);
                          netRadioLeftBox.remove(netRadioKeywordsPanelScrollPane);
                          netRadioLeftBox.remove(radioListBox);
                          if (netRadioListModel.isEmpty()) {
                              netRadioLeftBox.remove(netRadioScrollPane);
                              netRadioLeftBox.add(emptyHintPanel);
                          } else {
                              netRadioLeftBox.remove(emptyHintPanel);
                              netRadioLeftBox.add(netRadioScrollPane);
                          }
                          netRadioScrollPane.setVValue(0);
                          tabbedPane.setSelectedIndex(TabIndex.NET_RADIO);
                      } else {
                          clearRequestForAlbum();
                          // 查看歌曲专辑
                          currAlbumMusicInfo = musicInfo;
                          CommonResult<NetAlbumInfo> result = MusicServerUtil.getAlbumInfo(musicInfo.getSource(), musicInfo.getAlbumId());
                          List<NetAlbumInfo> albumInfos = result.data;
                          Integer total = result.total;
                          netAlbumMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 标题
                          netAlbumTitleLabel.setText(StringUtil.textToHtml(musicInfo.toSimpleString() + I18n.getText("albumSuffix")));
                          netAlbumToolBar.removeAll();
                          netAlbumToolBar.add(netAlbumBackwardButton);
                          netAlbumToolBar.add(Box.createHorizontalGlue());
                          netAlbumToolBar.add(netAlbumTitleLabel);
                          netAlbumToolBar.add(Box.createHorizontalGlue());
                          // 更新数量显示
                          netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netAlbumCurrPage, netAlbumMaxPage));
                          netAlbumCountPanel.add(netAlbumCountLabel, netAlbumCountPanel.getComponentIndex(netAlbumCountLabel));
                          netAlbumLeftBox.add(netAlbumCountPanel);
                          netAlbumSourceComboBox.setVisible(false);
                          netAlbumPlayAllButton.setVisible(false);
                          netAlbumCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netAlbumList.setModel(emptyListModel);
                          netAlbumListModel.clear();
                          albumInfos.forEach(albumInfo -> {
                              globalExecutor.execute(() -> updateCollection(albumInfo));
                              // 设置图片加载后重绘的事件
                              albumInfo.setInvokeLater(() -> {
                                  updateRenderer(netAlbumList);
                                  updateRenderer(collectionList);
                                  netAlbumList.repaint();
                                  collectionList.repaint();
                              });
                              netAlbumListModel.addElement(albumInfo);
                          });
                          netAlbumList.setModel(netAlbumListModel);
                          netAlbumBackwardButton.setEnabled(true);
                          netAlbumLeftBox.remove(netAlbumKeywordsPanelScrollPane);
                          netAlbumLeftBox.remove(albumListBox);
                          if (netAlbumListModel.isEmpty()) {
                              netAlbumLeftBox.remove(netAlbumScrollPane);
                              netAlbumLeftBox.add(emptyHintPanel);
                          } else {
                              netAlbumLeftBox.remove(emptyHintPanel);
                              netAlbumLeftBox.add(netAlbumScrollPane);
                          }
                          netAlbumScrollPane.setVValue(0);
                          tabbedPane.setSelectedIndex(TabIndex.NET_ALBUM);
                      }
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看推荐电台
          netMusicRecRadioMenuItem.addActionListener(e -> {
              NetMusicInfo musicInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION
                      || selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION
                      && collectionTabbedPane.getSelectedIndex() == CollectionTabIndex.MUSIC) {
                  musicInfo = (NetMusicInfo) musicList.getSelectedValue();
              } else if (selectedIndex == TabIndex.PLAY_QUEUE) musicInfo = (NetMusicInfo) playQueue.getSelectedValue();
              else musicInfo = netMusicList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForRadio();
                      // 获取歌曲推荐电台
                      currRecRadioMusicInfo = musicInfo;
                      CommonResult<NetRadioInfo> result = MusicServerUtil.getRecRadios(musicInfo);
                      List<NetRadioInfo> radioInfos = result.data;
                      int total = result.total;
                      netRadioMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netRadioTitleLabel.setText(StringUtil.textToHtml(musicInfo.toSimpleString() + I18n.getText("recRadioSuffix")));
                      netRadioToolBar.removeAll();
                      netRadioToolBar.add(netRadioBackwardButton);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      netRadioToolBar.add(netRadioTitleLabel);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netRadioCountLabel.setText(String.format(PAGINATION_MSG, netRadioCurrPage, netRadioMaxPage));
                      netRadioCountPanel.add(netRadioCountLabel, netRadioCountPanel.getComponentIndex(netRadioCountLabel));
                      netRadioLeftBox.add(netRadioCountPanel);
                      netRadioSourceComboBox.setVisible(false);
                      netRadioSortTypeComboBox.setVisible(false);
                      netRadioPlayAllButton.setVisible(false);
                      netRadioCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netRadioList.setModel(emptyListModel);
                      netRadioListModel.clear();
                      radioInfos.forEach(radioInfo -> {
                          globalExecutor.execute(() -> updateCollection(radioInfo));
                          // 设置图片加载后重绘的事件
                          radioInfo.setInvokeLater(() -> {
                              updateRenderer(netRadioList);
                              updateRenderer(collectionList);
                              netRadioList.repaint();
                              collectionList.repaint();
                          });
                          netRadioListModel.addElement(radioInfo);
                      });
                      netRadioList.setModel(netRadioListModel);
                      netRadioBackwardButton.setEnabled(true);
                      netRadioLeftBox.remove(netRadioKeywordsPanelScrollPane);
                      netRadioLeftBox.remove(radioListBox);
                      if (netRadioListModel.isEmpty()) {
                          netRadioLeftBox.remove(netRadioScrollPane);
                          netRadioLeftBox.add(emptyHintPanel);
                      } else {
                          netRadioLeftBox.remove(emptyHintPanel);
                          netRadioLeftBox.add(netRadioScrollPane);
                      }
                      netRadioScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_RADIO);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看相关 MV
          netMusicRelatedMvMenuItem.addActionListener(e -> {
              NetMusicInfo musicInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION
                      || selectedIndex == TabIndex.PERSONAL && currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION
                      && collectionTabbedPane.getSelectedIndex() == CollectionTabIndex.MUSIC) {
                  musicInfo = (NetMusicInfo) musicList.getSelectedValue();
              } else if (selectedIndex == TabIndex.PLAY_QUEUE) musicInfo = (NetMusicInfo) playQueue.getSelectedValue();
              else musicInfo = netMusicList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForMv();
                      // 获取相关 MV
                      CommonResult<NetMvInfo> result = MusicServerUtil.getRelatedMvs(currMvMusicInfo = musicInfo, netMvCurrPage = 1, limit);
                      List<NetMvInfo> mvInfos = result.data;
                      Integer total = result.total;
                      netMvMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netMvTitleLabel.setText(StringUtil.textToHtml(musicInfo.toSimpleString() + I18n.getText("relatedMvSuffix")));
                      netMvToolBar.removeAll();
                      netMvToolBar.add(netMvBackwardButton);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      netMvToolBar.add(netMvTitleLabel);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netMvCountLabel.setText(String.format(PAGINATION_MSG, netMvCurrPage, netMvMaxPage));
                      netMvCountPanel.add(netMvCountLabel, netMvCountPanel.getComponentIndex(netMvCountLabel));
                      netMvLeftBox.add(netMvCountPanel);
                      netMvCountPanel.setVisible(true);
                      netMvSourceComboBox.setVisible(false);
                      netMvSortTypeComboBox.setVisible(false);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netMvList.setModel(emptyListModel);
                      netMvListModel.clear();
                      mvInfos.forEach(mvInfo -> {
                          globalExecutor.execute(() -> updateCollection(mvInfo));
                          // 设置图片加载后重绘的事件
                          mvInfo.setInvokeLater(() -> {
                              updateRenderer(netMvList);
                              updateRenderer(collectionList);
                              netMvList.repaint();
                              collectionList.repaint();
                          });
                          netMvListModel.addElement(mvInfo);
                      });
                      netMvList.setModel(netMvListModel);
                      netMvBackwardButton.setEnabled(true);
                      netMvLeftBox.remove(netMvKeywordsPanelScrollPane);
                      if (netMvListModel.isEmpty()) {
                          netMvLeftBox.remove(netMvScrollPane);
                          netMvLeftBox.add(emptyHintPanel);
                      } else {
                          netMvLeftBox.remove(emptyHintPanel);
                          netMvLeftBox.add(netMvScrollPane);
                      }
                      netMvScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_MV);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 右键菜单复制名称
          netMusicCopyNameMenuItem.addActionListener(e -> {
              NetMusicInfo musicInfo = netMusicList.getSelectedValue();
              copyToClipboard(musicInfo.toSimpleString());
          });
  
          netMusicPopupMenu.add(netMusicPlayMenuItem);
          netMusicPopupMenu.add(netMusicNextPlayMenuItem);
          netMusicPopupMenu.add(netMusicPlayMvMenuItem);
          netMusicPopupMenu.add(netMusicCollectMenuItem);
          netMusicPopupMenu.add(netMusicDownloadMenuItem);
          netMusicPopupMenu.add(netMusicCommentMenuItem);
          netMusicPopupMenu.add(netMusicSheetMenuItem);
          netMusicPopupMenu.add(netMusicSearchSongMenuItem);
          netMusicPopupMenu.add(netMusicSimilarSongMenuItem);
          netMusicPopupMenu.add(netMusicRelatedPlaylistMenuItem);
          netMusicPopupMenu.add(netMusicAuthorMenuItem);
          netMusicPopupMenu.add(netMusicAlbumMenuItem);
          netMusicPopupMenu.add(netMusicRecRadioMenuItem);
          netMusicPopupMenu.add(netMusicRelatedMvMenuItem);
          netMusicPopupMenu.add(netMusicCopyNameMenuItem);
  
          // 搜索建议面板
          // 刷新搜索建议按钮
          netMusicRefreshSearchSuggestionButton.addActionListener(e -> globalExecutor.execute(() -> updateSearchSuggestion()));
          netMusicRefreshSearchSuggestionButton.setPreferredSize(new Dimension(30, 30));
          netMusicRefreshSearchSuggestionButton.setToolTipText(REFRESH_TIP);
          netMusicRefreshSearchSuggestionButton.addMouseListener(new ButtonMouseListener(netMusicRefreshSearchSuggestionButton, THIS));
  
  //        netMusicSearchSuggestionInnerPanel1.setVisible(false);
          netMusicSearchSuggestionInnerPanel1.add(netMusicSearchSuggestionLabel);
          netMusicSearchSuggestionInnerPanel1.add(netMusicRefreshSearchSuggestionButton);
          netMusicSearchSuggestionInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netMusicSearchSuggestionInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netMusicSearchSuggestionInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netMusicSearchSuggestionInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netMusicSearchSuggestionInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
  //        netMusicSearchSuggestionInnerPanel2.addContainerListener(new ContainerAdapter() {
  //            @Override
  //            public void componentAdded(ContainerEvent e) {
  //                netMusicSearchSuggestionInnerPanel1.setVisible(true);
  //            }
  //
  //            @Override
  //            public void componentRemoved(ContainerEvent e) {
  //                if (netMusicSearchSuggestionInnerPanel2.getComponentCount() == 0) netMusicSearchSuggestionInnerPanel1.setVisible(false);
  //            }
  //        });
          Dimension d = new Dimension(Integer.MAX_VALUE, 500);
          netMusicSearchSuggestionInnerPanel2.setMaximumSize(d);
          netMusicSearchSuggestionInnerPanel2.setPreferredSize(d);
          netMusicSearchSuggestionPanel.setVisible(false);
          netMusicSearchSuggestionPanel.setLayout(new BoxLayout(netMusicSearchSuggestionPanel, BoxLayout.Y_AXIS));
          netMusicSearchSuggestionPanel.add(netMusicSearchSuggestionInnerPanel1);
          netMusicSearchSuggestionPanel.add(netMusicSearchSuggestionInnerPanel2);
  
          // 热搜面板
          // 刷新热门搜索按钮
          netMusicRefreshHotSearchButton.addActionListener(e -> globalExecutor.execute(() -> updateHotSearch()));
          netMusicRefreshHotSearchButton.setPreferredSize(new Dimension(30, 30));
          netMusicRefreshHotSearchButton.setToolTipText(REFRESH_TIP);
          netMusicRefreshHotSearchButton.addMouseListener(new ButtonMouseListener(netMusicRefreshHotSearchButton, THIS));
  
  //        netMusicHotSearchInnerPanel1.setVisible(false);
          netMusicHotSearchInnerPanel1.add(netMusicHotSearchLabel);
          netMusicHotSearchInnerPanel1.add(netMusicRefreshHotSearchButton);
          netMusicHotSearchInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netMusicHotSearchInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netMusicHotSearchInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netMusicHotSearchInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netMusicHotSearchInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
  //        netMusicHotSearchInnerPanel2.addContainerListener(new ContainerAdapter() {
  //            @Override
  //            public void componentAdded(ContainerEvent e) {
  //                netMusicHotSearchInnerPanel1.setVisible(true);
  //            }
  //
  //            @Override
  //            public void componentRemoved(ContainerEvent e) {
  //                if (netMusicHotSearchInnerPanel2.getComponentCount() == 0) netMusicHotSearchInnerPanel1.setVisible(false);
  //            }
  //        });
          d = new Dimension(Integer.MAX_VALUE, 500);
          netMusicHotSearchInnerPanel2.setMaximumSize(d);
          netMusicHotSearchInnerPanel2.setPreferredSize(d);
          netMusicHotSearchPanel.setVisible(false);
          netMusicHotSearchPanel.setLayout(new BoxLayout(netMusicHotSearchPanel, BoxLayout.Y_AXIS));
          netMusicHotSearchPanel.add(netMusicHotSearchInnerPanel1);
          netMusicHotSearchPanel.add(netMusicHotSearchInnerPanel2);
  
          // 搜索历史面板
          // 清空搜索历史按钮
          netMusicClearHistorySearchButton.addActionListener(e -> netMusicHistorySearchInnerPanel2.removeAll());
          netMusicClearHistorySearchButton.setPreferredSize(new Dimension(30, 30));
          netMusicClearHistorySearchButton.setToolTipText(CLEAR_HISTORY_SEARCH_TIP);
          netMusicClearHistorySearchButton.addMouseListener(new ButtonMouseListener(netMusicClearHistorySearchButton, THIS));
  
          netMusicHistorySearchInnerPanel1.setVisible(false);
          netMusicHistorySearchInnerPanel1.add(netMusicHistorySearchLabel);
          netMusicHistorySearchInnerPanel1.add(netMusicClearHistorySearchButton);
          netMusicHistorySearchInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netMusicHistorySearchInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netMusicHistorySearchInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netMusicHistorySearchInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netMusicHistorySearchInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netMusicHistorySearchInnerPanel2.addContainerListener(new ContainerAdapter() {
              @Override
              public void componentAdded(ContainerEvent e) {
                  netMusicHistorySearchInnerPanel1.setVisible(true);
              }
  
              @Override
              public void componentRemoved(ContainerEvent e) {
                  if (netMusicHistorySearchInnerPanel2.getComponentCount() == 0)
                      netMusicHistorySearchInnerPanel1.setVisible(false);
              }
          });
          netMusicHistorySearchPanel.setLayout(new BoxLayout(netMusicHistorySearchPanel, BoxLayout.Y_AXIS));
          netMusicHistorySearchPanel.add(netMusicHistorySearchInnerPanel1);
          netMusicHistorySearchPanel.add(netMusicHistorySearchInnerPanel2);
  
          // 关键词面板
          netMusicKeywordsPanel.setLayout(new BoxLayout(netMusicKeywordsPanel, BoxLayout.Y_AXIS));
          netMusicKeywordsPanel.add(netMusicSearchSuggestionPanel);
          netMusicKeywordsPanel.add(netMusicHotSearchPanel);
          netMusicKeywordsPanel.add(netMusicHistorySearchPanel);
  
          // 同时限制面板与滚动条的大小才能保证按钮不超出窗口！！！
          d = new Dimension(1000, 1000);
          netMusicKeywordsPanel.setPreferredSize(d);
          netMusicKeywordsPanelScrollPane.setPreferredSize(d);
          // 滚动条
          netMusicScrollPane.addComponentListener(new ComponentAdapter() {
              @Override
              public void componentResized(ComponentEvent e) {
                  // 重新设置 renderer 可以避免 netMusicList 宽度不刷新！
  //                updateRenderer(netMusicList);
                  netMusicScrollPane.setVisible(false);
                  netMusicScrollPane.setVisible(true);
              }
          });
          // 在线歌单最佳大小
          netMusicScrollPane.setPreferredSize(new Dimension(200, 600));
          netLeftBox.add(netMusicScrollPane);
      }
  
      // 初始化描述盒子
      private void initDescriptionPanel() {
          // 描述标签垂直对齐方式
          playlistTagLabel.setVerticalAlignment(SwingConstants.TOP);
          playlistDescriptionLabel.setVerticalAlignment(SwingConstants.TOP);
          artistTagLabel.setVerticalAlignment(SwingConstants.TOP);
          albumDescriptionLabel.setVerticalAlignment(SwingConstants.TOP);
          artistDescriptionLabel.setVerticalAlignment(SwingConstants.TOP);
          radioTagLabel.setVerticalAlignment(SwingConstants.TOP);
          radioDescriptionLabel.setVerticalAlignment(SwingConstants.TOP);
          rankingDescriptionLabel.setVerticalAlignment(SwingConstants.TOP);
          userTagLabel.setVerticalAlignment(SwingConstants.TOP);
          userDescriptionLabel.setVerticalAlignment(SwingConstants.TOP);
          recommendItemTagLabel.setVerticalAlignment(SwingConstants.TOP);
          recommendItemDescriptionLabel.setVerticalAlignment(SwingConstants.TOP);
          collectionItemTagLabel.setVerticalAlignment(SwingConstants.TOP);
          collectionItemDescriptionLabel.setVerticalAlignment(SwingConstants.TOP);
          // 文字图标相对位置和间距
          playlistCoverAndNameLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
          playlistCoverAndNameLabel.setHorizontalTextPosition(SwingConstants.CENTER);
          albumCoverAndNameLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
          albumCoverAndNameLabel.setHorizontalTextPosition(SwingConstants.CENTER);
          artistCoverAndNameLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
          artistCoverAndNameLabel.setHorizontalTextPosition(SwingConstants.CENTER);
          radioCoverAndNameLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
          radioCoverAndNameLabel.setHorizontalTextPosition(SwingConstants.CENTER);
          rankingCoverAndNameLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
          rankingCoverAndNameLabel.setHorizontalTextPosition(SwingConstants.CENTER);
          userCoverAndNameLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
          userCoverAndNameLabel.setHorizontalTextPosition(SwingConstants.CENTER);
          recommendItemCoverAndNameLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
          recommendItemCoverAndNameLabel.setHorizontalTextPosition(SwingConstants.CENTER);
          collectionItemCoverAndNameLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
          collectionItemCoverAndNameLabel.setHorizontalTextPosition(SwingConstants.CENTER);
  
          userDescriptionLabel.setVerticalTextPosition(SwingConstants.TOP);
          userDescriptionLabel.setHorizontalTextPosition(SwingConstants.CENTER);
          collectionItemDescriptionLabel.setVerticalTextPosition(SwingConstants.TOP);
          collectionItemDescriptionLabel.setHorizontalTextPosition(SwingConstants.CENTER);
  
          int gap = 30;
          playlistCoverAndNameLabel.setIconTextGap(gap);
          albumCoverAndNameLabel.setIconTextGap(gap);
          artistCoverAndNameLabel.setIconTextGap(gap);
          radioCoverAndNameLabel.setIconTextGap(gap);
          rankingCoverAndNameLabel.setIconTextGap(gap);
          userCoverAndNameLabel.setIconTextGap(gap);
          userDescriptionLabel.setIconTextGap(gap);
          recommendItemCoverAndNameLabel.setIconTextGap(gap);
          collectionItemCoverAndNameLabel.setIconTextGap(gap);
          collectionItemDescriptionLabel.setIconTextGap(gap);
  
          // 显示大图
  //        JScrollBar vs = playlistDescriptionScrollPane.getVerticalScrollBar();
  //        vs.addMouseListener(new MouseAdapter() {
  //            @Override
  //            public void mouseReleased(MouseEvent e) {
  //                if (playlistDescriptionScrollPane.getViewport().contains(e.getPoint())) return;
  //                ((ScrollBarUI) vs.getUI()).setActive(false);
  //            }
  //        });
          playlistCoverAndNameLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) playlistDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
  //                if (vs.getValueIsAdjusting()) return;
                  ((ScrollBarUI) playlistDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetPlaylistInfo playlistInfo = netPlaylistList.getSelectedValue();
                  String url = playlistInfo.hasCoverImgUrl() ? playlistInfo.getCoverImgUrl() : playlistInfo.getCoverImgThumbUrl();
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(url);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          albumCoverAndNameLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) albumDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) albumDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetAlbumInfo albumInfo = netAlbumList.getSelectedValue();
                  String url = albumInfo.hasCoverImgUrl() ? albumInfo.getCoverImgUrl() : albumInfo.getCoverImgThumbUrl();
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(url);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          artistCoverAndNameLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) artistDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) artistDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetArtistInfo artistInfo = netArtistList.getSelectedValue();
                  String url = artistInfo.hasCoverImgUrl() ? artistInfo.getCoverImgUrl() : artistInfo.getCoverImgThumbUrl();
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(url);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          radioCoverAndNameLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) radioDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) radioDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetRadioInfo radioInfo = netRadioList.getSelectedValue();
                  String url = radioInfo.hasCoverImgUrl() ? radioInfo.getCoverImgUrl() : radioInfo.getCoverImgThumbUrl();
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(url);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          rankingCoverAndNameLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) rankingDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) rankingDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetRankingInfo rankingInfo = netRankingList.getSelectedValue();
                  String url = rankingInfo.getCoverImgUrl();
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(url);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          userCoverAndNameLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) userDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) userDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetUserInfo userInfo = netUserList.getSelectedValue();
                  String url = userInfo.hasAvatarUrl() ? userInfo.getAvatarUrl() : userInfo.getAvatarThumbUrl();
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(url);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          userDescriptionLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) userDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) userDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetUserInfo userInfo = netUserList.getSelectedValue();
                  String url = userInfo.getBgImgUrl();
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(url);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          recommendItemCoverAndNameLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) recommendItemDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) recommendItemDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetResource resource = itemRecommendList.getSelectedValue();
                  String url = null;
                  if (resource instanceof NetPlaylistInfo) {
                      NetPlaylistInfo playlistInfo = (NetPlaylistInfo) resource;
                      url = playlistInfo.hasCoverImgUrl() ? playlistInfo.getCoverImgUrl() : playlistInfo.getCoverImgThumbUrl();
                  } else if (resource instanceof NetAlbumInfo) {
                      NetAlbumInfo albumInfo = (NetAlbumInfo) resource;
                      url = albumInfo.hasCoverImgUrl() ? albumInfo.getCoverImgUrl() : albumInfo.getCoverImgThumbUrl();
                  } else if (resource instanceof NetArtistInfo) {
                      NetArtistInfo artistInfo = (NetArtistInfo) resource;
                      url = artistInfo.hasCoverImgUrl() ? artistInfo.getCoverImgUrl() : artistInfo.getCoverImgThumbUrl();
                  } else if (resource instanceof NetRadioInfo) {
                      NetRadioInfo radioInfo = (NetRadioInfo) resource;
                      url = radioInfo.hasCoverImgUrl() ? radioInfo.getCoverImgUrl() : radioInfo.getCoverImgThumbUrl();
                  }
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      final String finalUrl = url;
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(finalUrl);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          collectionItemCoverAndNameLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) collectionItemDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) collectionItemDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetResource resource = collectionList.getSelectedValue();
                  String url = null;
                  if (resource instanceof NetPlaylistInfo) {
                      NetPlaylistInfo playlistInfo = (NetPlaylistInfo) resource;
                      url = playlistInfo.hasCoverImgUrl() ? playlistInfo.getCoverImgUrl() : playlistInfo.getCoverImgThumbUrl();
                  } else if (resource instanceof NetAlbumInfo) {
                      NetAlbumInfo albumInfo = (NetAlbumInfo) resource;
                      url = albumInfo.hasCoverImgUrl() ? albumInfo.getCoverImgUrl() : albumInfo.getCoverImgThumbUrl();
                  } else if (resource instanceof NetArtistInfo) {
                      NetArtistInfo artistInfo = (NetArtistInfo) resource;
                      url = artistInfo.hasCoverImgUrl() ? artistInfo.getCoverImgUrl() : artistInfo.getCoverImgThumbUrl();
                  } else if (resource instanceof NetRadioInfo) {
                      NetRadioInfo radioInfo = (NetRadioInfo) resource;
                      url = radioInfo.hasCoverImgUrl() ? radioInfo.getCoverImgUrl() : radioInfo.getCoverImgThumbUrl();
                  } else if (resource instanceof NetRankingInfo) {
                      NetRankingInfo rankingInfo = (NetRankingInfo) resource;
                      url = rankingInfo.getCoverImgUrl();
                  } else if (resource instanceof NetUserInfo) {
                      NetUserInfo userInfo = (NetUserInfo) resource;
                      url = userInfo.hasAvatarUrl() ? userInfo.getAvatarUrl() : userInfo.getAvatarThumbUrl();
                  }
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      final String finalUrl = url;
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(finalUrl);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
          collectionItemDescriptionLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) collectionItemDescriptionScrollPane.getVUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) collectionItemDescriptionScrollPane.getVUI()).setActive(false);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() != 2) return;
                  NetResource resource = collectionList.getSelectedValue();
                  if (!(resource instanceof NetUserInfo)) return;
                  String url = ((NetUserInfo) resource).getBgImgUrl();
                  if (StringUtil.isEmpty(url)) return;
                  try {
                      imageViewDialog = new ImageViewDialog(THIS, 1) {
                          @Override
                          public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                              List<String> res = new LinkedList<>();
                              res.add(url);
                              return new CommonResult<>(res, 1);
                          }
  
                          @Override
                          public void requestFailed() {
                              new TipDialog(THIS, NO_IMG_MSG).showDialog();
                          }
                      };
                      imageViewDialog.showDialog();
                      imageViewDialog = null;
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              }
          });
  
          // 边框间距
          Border eb = BorderFactory.createEmptyBorder(15, 0, 0, 0);
          playlistTagLabel.setBorder(eb);
          artistTagLabel.setBorder(eb);
          radioTagLabel.setBorder(eb);
          userTagLabel.setBorder(eb);
          recommendItemTagLabel.setBorder(eb);
          collectionItemTagLabel.setBorder(eb);
  
          eb = BorderFactory.createEmptyBorder(gap, 0, 0, 0);
          playlistDescriptionLabel.setBorder(eb);
          albumDescriptionLabel.setBorder(eb);
          artistDescriptionLabel.setBorder(eb);
          radioDescriptionLabel.setBorder(eb);
          rankingDescriptionLabel.setBorder(eb);
          userDescriptionLabel.setBorder(eb);
          recommendItemDescriptionLabel.setBorder(eb);
          collectionItemDescriptionLabel.setBorder(eb);
          // 控制宽度
          int width = coverImageWidth + 20;
          Dimension size = new Dimension(width, Integer.MAX_VALUE);
          playlistCoverAndNameLabel.setMaximumSize(size);
          playlistTagLabel.setMaximumSize(size);
          playlistDescriptionLabel.setMaximumSize(size);
          albumCoverAndNameLabel.setMaximumSize(size);
          albumDescriptionLabel.setMaximumSize(size);
          artistCoverAndNameLabel.setMaximumSize(size);
          artistTagLabel.setMaximumSize(size);
          artistDescriptionLabel.setMaximumSize(size);
          radioCoverAndNameLabel.setMaximumSize(size);
          radioTagLabel.setMaximumSize(size);
          radioDescriptionLabel.setMaximumSize(size);
          rankingCoverAndNameLabel.setMaximumSize(size);
          rankingDescriptionLabel.setMaximumSize(size);
          userCoverAndNameLabel.setMaximumSize(size);
          userTagLabel.setMaximumSize(size);
          userDescriptionLabel.setMaximumSize(size);
          recommendItemCoverAndNameLabel.setMaximumSize(size);
          recommendItemTagLabel.setMaximumSize(size);
          recommendItemDescriptionLabel.setMaximumSize(size);
          collectionItemCoverAndNameLabel.setMaximumSize(size);
          collectionItemTagLabel.setMaximumSize(size);
          collectionItemDescriptionLabel.setMaximumSize(size);
          // 滚动面板宽度
          size = new Dimension(width + 10, Integer.MAX_VALUE);
          playlistDescriptionScrollPane.setPreferredSize(size);
          albumDescriptionScrollPane.setPreferredSize(size);
          artistDescriptionScrollPane.setPreferredSize(size);
          radioDescriptionScrollPane.setPreferredSize(size);
          rankingDescriptionScrollPane.setPreferredSize(size);
          userDescriptionScrollPane.setPreferredSize(size);
          recommendItemDescriptionScrollPane.setPreferredSize(size);
          collectionItemDescriptionScrollPane.setPreferredSize(size);
  
          playlistDescriptionOuterPanel.setPreferredSize(size);
          albumDescriptionOuterPanel.setPreferredSize(size);
          artistDescriptionOuterPanel.setPreferredSize(size);
          radioDescriptionOuterPanel.setPreferredSize(size);
          rankingDescriptionOuterPanel.setPreferredSize(size);
          userDescriptionOuterPanel.setPreferredSize(size);
          recommendItemDescriptionOuterPanel.setPreferredSize(size);
          collectionItemDescriptionOuterPanel.setPreferredSize(size);
          // 滚动条最大大小
          playlistDescriptionScrollPane.setMaximumSize(size);
          albumDescriptionScrollPane.setMaximumSize(size);
          artistDescriptionScrollPane.setMaximumSize(size);
          radioDescriptionScrollPane.setMaximumSize(size);
          rankingDescriptionScrollPane.setMaximumSize(size);
          userDescriptionScrollPane.setMaximumSize(size);
          recommendItemDescriptionScrollPane.setMaximumSize(size);
          collectionItemDescriptionScrollPane.setMaximumSize(size);
  
          playlistDescriptionOuterPanel.setMaximumSize(size);
          albumDescriptionOuterPanel.setMaximumSize(size);
          artistDescriptionOuterPanel.setMaximumSize(size);
          radioDescriptionOuterPanel.setMaximumSize(size);
          rankingDescriptionOuterPanel.setMaximumSize(size);
          userDescriptionOuterPanel.setMaximumSize(size);
          recommendItemDescriptionOuterPanel.setMaximumSize(size);
          collectionItemDescriptionOuterPanel.setMaximumSize(size);
          // 滚动条监听器
          playlistDescriptionScrollPane.addMouseListener(new ScrollPaneListener(playlistDescriptionScrollPane, THIS));
          albumDescriptionScrollPane.addMouseListener(new ScrollPaneListener(albumDescriptionScrollPane, THIS));
          artistDescriptionScrollPane.addMouseListener(new ScrollPaneListener(artistDescriptionScrollPane, THIS));
          radioDescriptionScrollPane.addMouseListener(new ScrollPaneListener(radioDescriptionScrollPane, THIS));
          rankingDescriptionScrollPane.addMouseListener(new ScrollPaneListener(rankingDescriptionScrollPane, THIS));
          userDescriptionScrollPane.addMouseListener(new ScrollPaneListener(userDescriptionScrollPane, THIS));
          recommendItemDescriptionScrollPane.addMouseListener(new ScrollPaneListener(recommendItemDescriptionScrollPane, THIS));
          collectionItemDescriptionScrollPane.addMouseListener(new ScrollPaneListener(collectionItemDescriptionScrollPane, THIS));
          // 水平滚动条
          playlistDescriptionScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          albumDescriptionScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          artistDescriptionScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          radioDescriptionScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          rankingDescriptionScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          userDescriptionScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          recommendItemDescriptionScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          collectionItemDescriptionScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          // 描述面板更新
          playlistDescriptionPanel.setLayout(new BoxLayout(playlistDescriptionPanel, BoxLayout.Y_AXIS));
          albumDescriptionPanel.setLayout(new BoxLayout(albumDescriptionPanel, BoxLayout.Y_AXIS));
          artistDescriptionPanel.setLayout(new BoxLayout(artistDescriptionPanel, BoxLayout.Y_AXIS));
          radioDescriptionPanel.setLayout(new BoxLayout(radioDescriptionPanel, BoxLayout.Y_AXIS));
          rankingDescriptionPanel.setLayout(new BoxLayout(rankingDescriptionPanel, BoxLayout.Y_AXIS));
          userDescriptionPanel.setLayout(new BoxLayout(userDescriptionPanel, BoxLayout.Y_AXIS));
          recommendItemDescriptionPanel.setLayout(new BoxLayout(recommendItemDescriptionPanel, BoxLayout.Y_AXIS));
          collectionItemDescriptionPanel.setLayout(new BoxLayout(collectionItemDescriptionPanel, BoxLayout.Y_AXIS));
          playlistDescriptionPanel.add(playlistCoverAndNameLabel);
          playlistDescriptionPanel.add(playlistTagLabel);
          playlistDescriptionPanel.add(playlistDescriptionLabel);
          albumDescriptionPanel.add(albumCoverAndNameLabel);
          albumDescriptionPanel.add(albumDescriptionLabel);
          artistDescriptionPanel.add(artistCoverAndNameLabel);
          artistDescriptionPanel.add(artistTagLabel);
          artistDescriptionPanel.add(artistDescriptionLabel);
          radioDescriptionPanel.add(radioCoverAndNameLabel);
          radioDescriptionPanel.add(radioTagLabel);
          radioDescriptionPanel.add(radioDescriptionLabel);
          rankingDescriptionPanel.add(rankingCoverAndNameLabel);
          rankingDescriptionPanel.add(rankingDescriptionLabel);
          userDescriptionPanel.add(userCoverAndNameLabel);
          userDescriptionPanel.add(userTagLabel);
          userDescriptionPanel.add(userDescriptionLabel);
          recommendItemDescriptionPanel.add(recommendItemCoverAndNameLabel);
          recommendItemDescriptionPanel.add(recommendItemTagLabel);
          recommendItemDescriptionPanel.add(recommendItemDescriptionLabel);
          collectionItemDescriptionPanel.add(collectionItemCoverAndNameLabel);
          collectionItemDescriptionPanel.add(collectionItemTagLabel);
          collectionItemDescriptionPanel.add(collectionItemDescriptionLabel);
  
          // 右键菜单
          descriptionPanelPopupMenu.add(saveDescCoverImgMenuItem);
          descriptionPanelPopupMenu.add(saveDescBgImgMenuItem);
          descriptionPanelPopupMenu.add(copyDescNameMenuItem);
          descriptionPanelPopupMenu.add(copyDescTagMenuItem);
          descriptionPanelPopupMenu.add(copyDescMenuItem);
          playlistDescriptionPanel.setComponentPopupMenu(descriptionPanelPopupMenu);
          albumDescriptionPanel.setComponentPopupMenu(descriptionPanelPopupMenu);
          artistDescriptionPanel.setComponentPopupMenu(descriptionPanelPopupMenu);
          radioDescriptionPanel.setComponentPopupMenu(descriptionPanelPopupMenu);
          rankingDescriptionPanel.setComponentPopupMenu(descriptionPanelPopupMenu);
          userDescriptionPanel.setComponentPopupMenu(descriptionPanelPopupMenu);
          recommendItemDescriptionPanel.setComponentPopupMenu(descriptionPanelPopupMenu);
          collectionItemDescriptionPanel.setComponentPopupMenu(descriptionPanelPopupMenu);
          saveDescCoverImgMenuItem.addActionListener(e -> {
              NetResource resource = null;
              int si = tabbedPane.getSelectedIndex();
              if (si == TabIndex.PERSONAL) resource = collectionList.getSelectedValue();
              else if (si == TabIndex.NET_PLAYLIST) resource = netPlaylistList.getSelectedValue();
              else if (si == TabIndex.NET_ALBUM) resource = netAlbumList.getSelectedValue();
              else if (si == TabIndex.NET_ARTIST) resource = netArtistList.getSelectedValue();
              else if (si == TabIndex.NET_RADIO) resource = netRadioList.getSelectedValue();
              else if (si == TabIndex.NET_RANKING) resource = netRankingList.getSelectedValue();
              else if (si == TabIndex.NET_USER) resource = netUserList.getSelectedValue();
              else if (si == TabIndex.RECOMMEND) resource = itemRecommendList.getSelectedValue();
              if (resource instanceof NetPlaylistInfo) saveImg(((NetPlaylistInfo) resource).getCoverImg());
              else if (resource instanceof NetAlbumInfo) saveImg(((NetAlbumInfo) resource).getCoverImg());
              else if (resource instanceof NetArtistInfo) saveImg(((NetArtistInfo) resource).getCoverImg());
              else if (resource instanceof NetRadioInfo) saveImg(((NetRadioInfo) resource).getCoverImg());
              else if (resource instanceof NetRankingInfo) saveImg(((NetRankingInfo) resource).getCoverImg());
              else if (resource instanceof NetUserInfo) saveImg(((NetUserInfo) resource).getAvatar());
          });
          saveDescBgImgMenuItem.addActionListener(e -> {
              NetResource resource = null;
              int si = tabbedPane.getSelectedIndex();
              if (si == TabIndex.PERSONAL) resource = collectionList.getSelectedValue();
              else if (si == TabIndex.NET_USER) resource = netUserList.getSelectedValue();
              if (resource instanceof NetUserInfo) saveImg(((NetUserInfo) resource).getBgImg());
          });
          copyDescNameMenuItem.addActionListener(e -> {
              CustomLabel l = null;
              int si = tabbedPane.getSelectedIndex();
              if (si == TabIndex.PERSONAL) l = collectionItemCoverAndNameLabel;
              else if (si == TabIndex.NET_PLAYLIST) l = playlistCoverAndNameLabel;
              else if (si == TabIndex.NET_ALBUM) l = albumCoverAndNameLabel;
              else if (si == TabIndex.NET_ARTIST) l = artistCoverAndNameLabel;
              else if (si == TabIndex.NET_RADIO) l = radioCoverAndNameLabel;
              else if (si == TabIndex.NET_RANKING) l = rankingCoverAndNameLabel;
              else if (si == TabIndex.NET_USER) l = userCoverAndNameLabel;
              else if (si == TabIndex.RECOMMEND) l = recommendItemCoverAndNameLabel;
              copyToClipboard(StringUtil.removeHTMLLabel(l.getText()));
          });
          copyDescTagMenuItem.addActionListener(e -> {
              CustomLabel l = null;
              int si = tabbedPane.getSelectedIndex();
              if (si == TabIndex.PERSONAL) l = collectionItemTagLabel;
              else if (si == TabIndex.NET_PLAYLIST) l = playlistTagLabel;
              else if (si == TabIndex.NET_ARTIST) l = artistTagLabel;
              else if (si == TabIndex.NET_RADIO) l = radioTagLabel;
              else if (si == TabIndex.NET_USER) l = userTagLabel;
              else if (si == TabIndex.RECOMMEND) l = recommendItemTagLabel;
              if (l == null || l.getText().isEmpty()) return;
              copyToClipboard(StringUtil.removeHTMLLabel(l.getText()));
          });
          copyDescMenuItem.addActionListener(e -> {
              CustomLabel l = null;
              int si = tabbedPane.getSelectedIndex();
              if (si == TabIndex.PERSONAL) l = collectionItemDescriptionLabel;
              else if (si == TabIndex.NET_PLAYLIST) l = playlistDescriptionLabel;
              else if (si == TabIndex.NET_ALBUM) l = albumDescriptionLabel;
              else if (si == TabIndex.NET_ARTIST) l = artistDescriptionLabel;
              else if (si == TabIndex.NET_RADIO) l = radioDescriptionLabel;
              else if (si == TabIndex.NET_RANKING) l = rankingDescriptionLabel;
              else if (si == TabIndex.NET_USER) l = userDescriptionLabel;
              else if (si == TabIndex.RECOMMEND) l = recommendItemDescriptionLabel;
              if (l.getText().isEmpty()) return;
              copyToClipboard(StringUtil.removeHTMLLabel(l.getText()));
          });
  
          // 描述收藏按钮
          playlistDescriptionCollectionButton.addActionListener(e -> {
              netPlaylistCollectMenuItem.doClick();
              switchDescriptionCollectionStatus(playlistDescriptionCollectionButton);
          });
          albumDescriptionCollectionButton.addActionListener(e -> {
              netAlbumCollectMenuItem.doClick();
              switchDescriptionCollectionStatus(albumDescriptionCollectionButton);
          });
          artistDescriptionCollectionButton.addActionListener(e -> {
              netArtistCollectMenuItem.doClick();
              switchDescriptionCollectionStatus(artistDescriptionCollectionButton);
          });
          radioDescriptionCollectionButton.addActionListener(e -> {
              netRadioCollectMenuItem.doClick();
              switchDescriptionCollectionStatus(radioDescriptionCollectionButton);
          });
          rankingDescriptionCollectionButton.addActionListener(e -> {
              netRankingCollectMenuItem.doClick();
              switchDescriptionCollectionStatus(rankingDescriptionCollectionButton);
          });
          userDescriptionCollectionButton.addActionListener(e -> {
              netUserCollectMenuItem.doClick();
              switchDescriptionCollectionStatus(userDescriptionCollectionButton);
          });
          recommendItemDescriptionCollectionButton.addActionListener(e -> {
              NetResource resource = itemRecommendList.getSelectedValue();
              CustomMenuItem menuItem = null;
              if (resource instanceof NetPlaylistInfo) menuItem = netPlaylistCollectMenuItem;
              else if (resource instanceof NetAlbumInfo) menuItem = netAlbumCollectMenuItem;
              else if (resource instanceof NetArtistInfo) menuItem = netArtistCollectMenuItem;
              else if (resource instanceof NetRadioInfo) menuItem = netRadioCollectMenuItem;
              menuItem.doClick();
              switchDescriptionCollectionStatus(recommendItemDescriptionCollectionButton);
          });
          collectionItemDescriptionCollectionButton.addActionListener(e -> {
              NetResource resource = collectionList.getSelectedValue();
              if (resource == null) resource = collectionOpenResource;
              CustomMenuItem menuItem = null;
              if (resource instanceof NetPlaylistInfo) menuItem = netPlaylistCollectMenuItem;
              else if (resource instanceof NetAlbumInfo) menuItem = netAlbumCollectMenuItem;
              else if (resource instanceof NetArtistInfo) menuItem = netArtistCollectMenuItem;
              else if (resource instanceof NetRadioInfo) menuItem = netRadioCollectMenuItem;
              else if (resource instanceof NetRankingInfo) menuItem = netRankingCollectMenuItem;
              else if (resource instanceof NetUserInfo) menuItem = netUserCollectMenuItem;
              menuItem.doClick();
              switchDescriptionCollectionStatus(collectionItemDescriptionCollectionButton);
          });
          // 描述收藏按钮鼠标事件
          playlistDescriptionCollectionButton.addMouseListener(new ButtonMouseListener(playlistDescriptionCollectionButton, THIS));
          albumDescriptionCollectionButton.addMouseListener(new ButtonMouseListener(albumDescriptionCollectionButton, THIS));
          artistDescriptionCollectionButton.addMouseListener(new ButtonMouseListener(artistDescriptionCollectionButton, THIS));
          radioDescriptionCollectionButton.addMouseListener(new ButtonMouseListener(radioDescriptionCollectionButton, THIS));
          rankingDescriptionCollectionButton.addMouseListener(new ButtonMouseListener(rankingDescriptionCollectionButton, THIS));
          userDescriptionCollectionButton.addMouseListener(new ButtonMouseListener(userDescriptionCollectionButton, THIS));
          recommendItemDescriptionCollectionButton.addMouseListener(new ButtonMouseListener(recommendItemDescriptionCollectionButton, THIS));
          collectionItemDescriptionCollectionButton.addMouseListener(new ButtonMouseListener(collectionItemDescriptionCollectionButton, THIS));
          // 描述收藏按钮面板
          playlistDescriptionCollectionPanel.add(playlistDescriptionCollectionButton);
          albumDescriptionCollectionPanel.add(albumDescriptionCollectionButton);
          artistDescriptionCollectionPanel.add(artistDescriptionCollectionButton);
          radioDescriptionCollectionPanel.add(radioDescriptionCollectionButton);
          rankingDescriptionCollectionPanel.add(rankingDescriptionCollectionButton);
          userDescriptionCollectionPanel.add(userDescriptionCollectionButton);
          recommendItemDescriptionCollectionPanel.add(recommendItemDescriptionCollectionButton);
          collectionItemDescriptionCollectionPanel.add(collectionItemDescriptionCollectionButton);
  
          Dimension ms = new Dimension(Integer.MAX_VALUE, 30);
          playlistDescriptionCollectionPanel.setMaximumSize(ms);
          albumDescriptionCollectionPanel.setMaximumSize(ms);
          artistDescriptionCollectionPanel.setMaximumSize(ms);
          radioDescriptionCollectionPanel.setMaximumSize(ms);
          rankingDescriptionCollectionPanel.setMaximumSize(ms);
          userDescriptionCollectionPanel.setMaximumSize(ms);
          recommendItemDescriptionCollectionPanel.setMaximumSize(ms);
          collectionItemDescriptionCollectionPanel.setMaximumSize(ms);
          // 描述收藏面板布局
          playlistDescriptionOuterPanel.setLayout(new BorderLayout());
          albumDescriptionOuterPanel.setLayout(new BorderLayout());
          artistDescriptionOuterPanel.setLayout(new BorderLayout());
          radioDescriptionOuterPanel.setLayout(new BorderLayout());
          rankingDescriptionOuterPanel.setLayout(new BorderLayout());
          userDescriptionOuterPanel.setLayout(new BorderLayout());
          recommendItemDescriptionOuterPanel.setLayout(new BorderLayout());
          collectionItemDescriptionOuterPanel.setLayout(new BorderLayout());
          // 描述收藏面板
          playlistDescriptionOuterPanel.add(playlistDescriptionScrollPane, BorderLayout.CENTER);
          playlistDescriptionOuterPanel.add(playlistDescriptionCollectionPanel, BorderLayout.SOUTH);
          albumDescriptionOuterPanel.add(albumDescriptionScrollPane, BorderLayout.CENTER);
          albumDescriptionOuterPanel.add(albumDescriptionCollectionPanel, BorderLayout.SOUTH);
          artistDescriptionOuterPanel.add(artistDescriptionScrollPane, BorderLayout.CENTER);
          artistDescriptionOuterPanel.add(artistDescriptionCollectionPanel, BorderLayout.SOUTH);
          radioDescriptionOuterPanel.add(radioDescriptionScrollPane, BorderLayout.CENTER);
          radioDescriptionOuterPanel.add(radioDescriptionCollectionPanel, BorderLayout.SOUTH);
          rankingDescriptionOuterPanel.add(rankingDescriptionScrollPane, BorderLayout.CENTER);
          rankingDescriptionOuterPanel.add(rankingDescriptionCollectionPanel, BorderLayout.SOUTH);
          userDescriptionOuterPanel.add(userDescriptionScrollPane, BorderLayout.CENTER);
          userDescriptionOuterPanel.add(userDescriptionCollectionPanel, BorderLayout.SOUTH);
          recommendItemDescriptionOuterPanel.add(recommendItemDescriptionScrollPane, BorderLayout.CENTER);
          recommendItemDescriptionOuterPanel.add(recommendItemDescriptionCollectionPanel, BorderLayout.SOUTH);
          collectionItemDescriptionOuterPanel.add(collectionItemDescriptionScrollPane, BorderLayout.CENTER);
          collectionItemDescriptionOuterPanel.add(collectionItemDescriptionCollectionPanel, BorderLayout.SOUTH);
  
          playlistListBox.add(playlistDescriptionOuterPanel);
          albumListBox.add(albumDescriptionOuterPanel);
          artistListBox.add(artistDescriptionOuterPanel);
          radioListBox.add(radioDescriptionOuterPanel);
          rankingListBox.add(rankingDescriptionOuterPanel);
          userListBox.add(userDescriptionOuterPanel);
          recommendItemListBox.add(recommendItemDescriptionOuterPanel);
          collectionItemListBox.add(collectionItemDescriptionOuterPanel);
  
          // 设置一下列表盒子的最佳大小使其自动填充满
          Dimension d = new Dimension(300, 100);
          playlistListBox.setPreferredSize(d);
          albumListBox.setPreferredSize(d);
          artistListBox.setPreferredSize(d);
          radioListBox.setPreferredSize(d);
          rankingListBox.setPreferredSize(d);
          userListBox.setPreferredSize(d);
          recommendItemListBox.setPreferredSize(d);
          collectionItemListBox.setPreferredSize(d);
      }
  
      // 切换描述收藏按钮状态
      private void switchDescriptionCollectionStatus(CustomButton btn) {
          if (btn.getText().equals(COLLECT_TIP)) {
              btn.setIcon(ImageUtil.dye(collectedItemIcon, currUIStyle.getIconColor()));
              btn.setText(COLLECTED_TIP);
              btn.setToolTipText(COLLECTED_TIP);
          } else {
              btn.setIcon(ImageUtil.dye(collectItemIcon, currUIStyle.getIconColor()));
              btn.setText(COLLECT_TIP);
              btn.setToolTipText(COLLECTED_TIP);
          }
      }
  
      // 检查描述收藏按钮状态
      private void checkDescriptionCollectionStatus(CustomButton btn, NetResource resource) {
          if (hasBeenCollected(resource)) {
              btn.setIcon(ImageUtil.dye(collectedItemIcon, currUIStyle.getIconColor()));
              btn.setText(COLLECTED_TIP);
              btn.setToolTipText(COLLECTED_TIP);
          } else {
              btn.setIcon(ImageUtil.dye(collectItemIcon, currUIStyle.getIconColor()));
              btn.setText(COLLECT_TIP);
              btn.setToolTipText(COLLECTED_TIP);
          }
      }
  
      // 初始化在线歌单工具栏
      private void initNetPlaylistToolBar() {
          netPlaylistSearchTextField.addFocusListener(new TextFieldHintListener(netPlaylistSearchTextField, "歌单", currUIStyle.getForeColor()));
          netPlaylistSearchTextField.getDocument().addDocumentListener(new DocumentListener() {
              @Override
              public void insertUpdate(DocumentEvent e) {
                  if (netPlaylistSearchTextField.isOccupied()) netPlaylistClearInputButton.setVisible(true);
              }
  
              @Override
              public void removeUpdate(DocumentEvent e) {
                  if (netPlaylistSearchTextField.getText().isEmpty()) netPlaylistClearInputButton.setVisible(false);
              }
  
              @Override
              public void changedUpdate(DocumentEvent e) {
  
              }
          });
          // 只能输入数字
          netPlaylistPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 后退按钮事件
          netPlaylistBackwardButton.addActionListener(e -> {
              int lastComponentIndex = netPlaylistLeftBox.getComponentCount() - 1;
              Component c = netPlaylistLeftBox.getComponent(lastComponentIndex);
              if (c != netPlaylistScrollPane && c != emptyHintPanel) {
                  netMusicListForPlaylistModel.clear();
                  // 从在线音乐列表切换回歌单列表
                  netPlaylistPlayAllButton.setVisible(false);
                  netPlaylistLeftBox.remove(playlistListBox);
                  netPlaylistLeftBox.add(netPlaylistCountPanel);
                  netPlaylistLeftBox.add(netPlaylistScrollPane);
                  // 添加回工具栏的搜索
                  netPlaylistToolBar.remove(1);
                  netPlaylistToolBar.add(netPlaylistIdCheckBox);
                  netPlaylistToolBar.add(netPlaylistSearchTextField);
                  netPlaylistToolBar.add(netPlaylistClearInputButton);
                  netPlaylistToolBar.add(netPlaylistSearchButton);
                  // 添加回工具栏的搜索/标题
                  netPlaylistToolBar.removeAll();
                  netPlaylistToolBar.add(netPlaylistBackwardButton);
                  if (currPlaylistMusicInfo == null && currPlaylistPlaylistInfo == null
                          && currPlaylistCommentInfo == null && currPlaylistUserInfo == null) {
                      // 删除标题标签
                      netPlaylistToolBar.add(netPlaylistIdCheckBox);
                      netPlaylistToolBar.add(netPlaylistSearchTextField);
                      netPlaylistToolBar.add(netPlaylistClearInputButton);
                      netPlaylistToolBar.add(netPlaylistSearchButton);
                      netPlaylistSourceComboBox.setVisible(true);
                  } else {
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      netPlaylistToolBar.add(netPlaylistTitleLabel);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                  }
                  // 更新歌单歌曲数量显示
  //                netCurrPlaylistLabel.setText("");
                  netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netPlaylistCurrPage, netPlaylistMaxPage));
  //                netPlaylistBackwardButton.setEnabled(false);
              }
              // 后退到关键词面板
              else {
                  // 删除标题标签
                  netPlaylistToolBar.removeAll();
                  netPlaylistToolBar.add(netPlaylistBackwardButton);
                  netPlaylistToolBar.add(netPlaylistIdCheckBox);
                  netPlaylistToolBar.add(netPlaylistSearchTextField);
                  netPlaylistToolBar.add(netPlaylistClearInputButton);
                  netPlaylistToolBar.add(netPlaylistSearchButton);
                  clearRequestForPlaylist();
  
                  netPlaylistListModel.clear();
                  netPlaylistCountPanel.setVisible(false);
                  netPlaylistLeftBox.remove(emptyHintPanel);
                  netPlaylistLeftBox.remove(netPlaylistScrollPane);
                  netPlaylistLeftBox.add(netPlaylistKeywordsPanelScrollPane);
                  netPlaylistBackwardButton.setEnabled(false);
              }
              // 切换后一定要刷新！
              netPlaylistLeftBox.repaint();
          });
          // 清除输入事件
          netPlaylistClearInputButton.addActionListener(e -> {
              netPlaylistSearchTextField.requestFocus();
              netPlaylistSearchTextField.setText("");
              netPlaylistBackwardButton.requestFocus();
          });
          // 搜索歌单按钮事件
          netPlaylistSearchButton.addActionListener(e -> {
              netPlaylistCurrKeyword = netPlaylistSearchTextField.getText().trim();
              if (!netPlaylistCurrKeyword.isEmpty()) {
                  // 关键词加入搜索历史
                  DialogButton b = new DialogButton(netPlaylistCurrKeyword);
                  b.addActionListener(event -> {
                      netPlaylistSearchTextField.requestFocus();
                      netPlaylistSearchTextField.setText(b.getPlainText());
                      netPlaylistClearInputButton.setVisible(true);
                      netPlaylistLeftBox.remove(netPlaylistKeywordsPanelScrollPane);
                      netPlaylistLeftBox.add(netPlaylistScrollPane);
                      netPlaylistSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netPlaylistHistorySearchInnerPanel2.remove(b);
                          netPlaylistHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  // 先删除重复的关键词
                  removeKeywordInHistorySearch(netPlaylistCurrKeyword, HistorySearchType.NET_PLAYLIST);
                  netPlaylistHistorySearchInnerPanel2.add(b, 0);
                  // 删除超出上限的最后一个关键词
                  int c = netPlaylistHistorySearchInnerPanel2.getComponentCount();
                  if (c > maxSearchHistoryCount)
                      netPlaylistHistorySearchInnerPanel2.remove(c - 1);
                  netPlaylistHistorySearchPanel.repaint();
  
                  loadingAndRun(() -> {
                      try {
                          // 搜索歌单并显示歌单列表
                          netPlaylistCurrPage = 1;
                          CommonResult<NetPlaylistInfo> result = netPlaylistIdCheckBox.isSelected() ?
                                  MusicServerUtil.getPlaylistInfo(netPlaylistSourceComboBox.getSelectedIndex(), netPlaylistCurrKeyword)
                                  : MusicServerUtil.searchPlaylists(netPlaylistSourceComboBox.getSelectedIndex(), netPlaylistCurrKeyword, netPlaylistCurrPage, limit);
                          List<NetPlaylistInfo> playlistInfos = result.data;
                          Integer total = result.total;
                          netPlaylistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netPlaylistCurrPage, netPlaylistMaxPage));
                          netPlaylistCountPanel.add(netPlaylistCountLabel, netPlaylistCountPanel.getComponentIndex(netPlaylistCountLabel));
                          netPlaylistCountPanel.setVisible(true);
                          netPlaylistSourceComboBox.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netPlaylistList.setModel(emptyListModel);
                          netPlaylistListModel.clear();
                          playlistInfos.forEach(playlistInfo -> {
                              globalExecutor.execute(() -> updateCollection(playlistInfo));
                              // 设置图片加载后重绘的事件
                              playlistInfo.setInvokeLater(() -> {
                                  updateRenderer(netPlaylistList);
                                  updateRenderer(collectionList);
                                  netPlaylistList.repaint();
                                  collectionList.repaint();
                              });
                              netPlaylistListModel.addElement(playlistInfo);
                          });
                          netPlaylistList.setModel(netPlaylistListModel);
                          netPlaylistBackwardButton.setEnabled(true);
                          netPlaylistLeftBox.remove(netPlaylistKeywordsPanelScrollPane);
                          if (netPlaylistListModel.isEmpty()) {
                              netPlaylistLeftBox.remove(netPlaylistScrollPane);
                              netPlaylistLeftBox.add(emptyHintPanel);
                          } else {
                              netPlaylistLeftBox.remove(emptyHintPanel);
                              netPlaylistLeftBox.add(netPlaylistScrollPane);
                          }
                          netPlaylistScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          });
          // 搜索歌单跳页事件
          Runnable searchPlaylistGoPageAction = () -> {
              loadingAndRun(() -> {
                  boolean songRequest = currPlaylistMusicInfo != null, playlistRequest = currPlaylistPlaylistInfo != null,
                          commentRequest = currPlaylistCommentInfo != null, userRequest = currPlaylistUserInfo != null;
                  if (songRequest || playlistRequest || commentRequest || userRequest || StringUtil.notEmpty(netPlaylistCurrKeyword)) {
                      try {
                          // 搜索歌单并显示歌单列表
                          CommonResult<NetPlaylistInfo> result = songRequest ? MusicServerUtil.getRelatedPlaylists(currPlaylistMusicInfo)
                                  : playlistRequest ? MusicServerUtil.getSimilarPlaylists(currPlaylistPlaylistInfo)
                                  : commentRequest ? MusicServerUtil.getUserPlaylists(currPlaylistCommentInfo, netPlaylistCurrPage, limit)
                                  : userRequest ? MusicServerUtil.getUserPlaylists(currPlaylistUserInfo, netPlaylistCurrPage, limit)
                                  : netPlaylistIdCheckBox.isSelected() ? MusicServerUtil.getPlaylistInfo(netPlaylistSourceComboBox.getSelectedIndex(), netPlaylistCurrKeyword)
                                  : MusicServerUtil.searchPlaylists(netPlaylistSourceComboBox.getSelectedIndex(), netPlaylistCurrKeyword, netPlaylistCurrPage, limit);
                          List<NetPlaylistInfo> playlistInfos = result.data;
                          Integer total = result.total;
                          netPlaylistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netPlaylistCurrPage, netPlaylistMaxPage));
                          netPlaylistCountPanel.add(netPlaylistCountLabel, netPlaylistCountPanel.getComponentIndex(netPlaylistCountLabel));
                          netPlaylistCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netPlaylistList.setModel(emptyListModel);
                          netPlaylistListModel.clear();
                          playlistInfos.forEach(playlistInfo -> {
                              globalExecutor.execute(() -> updateCollection(playlistInfo));
                              // 设置图片加载后重绘的事件
                              playlistInfo.setInvokeLater(() -> {
                                  updateRenderer(netPlaylistList);
                                  updateRenderer(collectionList);
                                  netPlaylistList.repaint();
                                  collectionList.repaint();
                              });
                              netPlaylistListModel.addElement(playlistInfo);
                          });
                          netPlaylistList.setModel(netPlaylistListModel);
                          netPlaylistScrollPane.setVValue(0);
                          if (netPlaylistListModel.isEmpty()) {
                              netPlaylistLeftBox.remove(netPlaylistScrollPane);
                              netPlaylistLeftBox.add(emptyHintPanel);
                          } else {
                              netPlaylistLeftBox.remove(emptyHintPanel);
                              netPlaylistLeftBox.add(netPlaylistScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  }
              });
          };
          // 搜索歌单内歌曲并显示在在线歌曲列表
          Runnable searchMusicInPlaylist = () -> {
              if (!netMusicListForPlaylistModel.isEmpty()) {
                  loadingAndRun(() -> {
                      try {
                          NetPlaylistInfo playlistInfo = netPlaylistList.getSelectedValue();
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInPlaylist(playlistInfo, netMusicInPlaylistCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netMusicInPlaylistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netMusicInPlaylistCurrPage, netMusicInPlaylistMaxPage));
                          netPlaylistCountPanel.add(netPlaylistCountLabel, netPlaylistCountPanel.getComponentIndex(netPlaylistCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForPlaylistModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForPlaylistModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForPlaylistModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicListForPlaylistModel.isEmpty()) {
                              playlistListCountBox.remove(netMusicScrollPane);
                              playlistListCountBox.add(emptyHintPanel);
                          } else {
                              playlistListCountBox.remove(emptyHintPanel);
                              playlistListCountBox.add(netMusicScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 播放全部
          netPlaylistPlayAllButton.addActionListener(e -> netPlaylistPlayAllMenuItem.doClick());
          // 刷新按钮事件
          netPlaylistRefreshButton.addActionListener(e -> {
              Component lc = netPlaylistLeftBox.getComponent(netPlaylistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌单列表，刷新歌单
              if (lc == netPlaylistScrollPane || lc == emptyHintPanel) {
                  searchPlaylistGoPageAction.run();
              }
              // 当前显示的是某歌单的歌曲，刷新歌曲
              else {
                  searchMusicInPlaylist.run();
              }
          });
          // 第一页按钮事件
          netPlaylistStartPageButton.addActionListener(e -> {
              Component lc = netPlaylistLeftBox.getComponent(netPlaylistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌单列表，跳到第一页歌单
              if (lc == netPlaylistScrollPane || lc == emptyHintPanel) {
                  if (netPlaylistCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netPlaylistCurrPage = 1;
                  searchPlaylistGoPageAction.run();
              }
              // 当前显示的是某歌单的歌曲，跳到第一页歌曲
              else {
                  if (netMusicInPlaylistCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInPlaylistCurrPage = 1;
                  searchMusicInPlaylist.run();
              }
          });
          // 上一页按钮事件
          netPlaylistLastPageButton.addActionListener(e -> {
              Component lc = netPlaylistLeftBox.getComponent(netPlaylistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌单列表，跳到上一页歌单
              if (lc == netPlaylistScrollPane || lc == emptyHintPanel) {
                  if (netPlaylistCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netPlaylistCurrPage--;
                  searchPlaylistGoPageAction.run();
              }
              // 当前显示的是某歌单的歌曲，跳到上一页歌曲
              else {
                  if (netMusicInPlaylistCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInPlaylistCurrPage--;
                  searchMusicInPlaylist.run();
              }
          });
          // 下一页按钮事件
          netPlaylistNextPageButton.addActionListener(e -> {
              Component lc = netPlaylistLeftBox.getComponent(netPlaylistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌单列表，跳到下一页歌单
              if (lc == netPlaylistScrollPane || lc == emptyHintPanel) {
                  if (netPlaylistCurrPage >= netPlaylistMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netPlaylistCurrPage++;
                  searchPlaylistGoPageAction.run();
              }
              // 当前显示的是某歌单的歌曲，跳到下一页歌曲
              else {
                  if (netMusicInPlaylistCurrPage >= netMusicInPlaylistMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInPlaylistCurrPage++;
                  searchMusicInPlaylist.run();
              }
          });
          // 最后一页按钮事件
          netPlaylistEndPageButton.addActionListener(e -> {
              Component lc = netPlaylistLeftBox.getComponent(netPlaylistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌单列表，跳到最后一页歌单
              if (lc == netPlaylistScrollPane || lc == emptyHintPanel) {
                  if (netPlaylistCurrPage >= netPlaylistMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netPlaylistCurrPage = netPlaylistMaxPage;
                  searchPlaylistGoPageAction.run();
              }
              // 当前显示的是某歌单的歌曲，跳到最后一页歌曲
              else {
                  if (netMusicInPlaylistCurrPage >= netMusicInPlaylistMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInPlaylistCurrPage = netMusicInPlaylistMaxPage;
                  searchMusicInPlaylist.run();
              }
          });
          // 跳页按钮事件
          netPlaylistGoButton.addActionListener(e -> {
              Component lc = netPlaylistLeftBox.getComponent(netPlaylistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌单列表，跳页歌单
              if (lc == netPlaylistScrollPane || lc == emptyHintPanel) {
                  String text = netPlaylistPageTextField.getText();
                  netPlaylistPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netPlaylistMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netPlaylistCurrPage = destPage;
                  searchPlaylistGoPageAction.run();
              }
              // 当前显示的是某歌单的歌曲，跳页歌曲
              else {
                  String text = netPlaylistPageTextField.getText();
                  netPlaylistPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netMusicInPlaylistMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInPlaylistCurrPage = destPage;
                  searchMusicInPlaylist.run();
              }
          });
          // 按钮被禁止的图标
          netPlaylistBackwardButton.setEnabled(false);
          // 按钮初始不可见
          netPlaylistClearInputButton.setVisible(false);
          netPlaylistPlayAllButton.setVisible(false);
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netPlaylistRefreshButton.setPreferredSize(dimension);
          netPlaylistStartPageButton.setPreferredSize(dimension);
          netPlaylistLastPageButton.setPreferredSize(dimension);
          netPlaylistGoButton.setPreferredSize(dimension);
          netPlaylistNextPageButton.setPreferredSize(dimension);
          netPlaylistEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netPlaylistBackwardButton.addMouseListener(new ButtonMouseListener(netPlaylistBackwardButton, THIS));
          netPlaylistClearInputButton.addMouseListener(new ButtonMouseListener(netPlaylistClearInputButton, THIS));
          netPlaylistSearchButton.addMouseListener(new ButtonMouseListener(netPlaylistSearchButton, THIS));
          netPlaylistPlayAllButton.addMouseListener(new ButtonMouseListener(netPlaylistPlayAllButton, THIS));
          netPlaylistRefreshButton.addMouseListener(new ButtonMouseListener(netPlaylistRefreshButton, THIS));
          netPlaylistStartPageButton.addMouseListener(new ButtonMouseListener(netPlaylistStartPageButton, THIS));
          netPlaylistLastPageButton.addMouseListener(new ButtonMouseListener(netPlaylistLastPageButton, THIS));
          netPlaylistGoButton.addMouseListener(new ButtonMouseListener(netPlaylistGoButton, THIS));
          netPlaylistNextPageButton.addMouseListener(new ButtonMouseListener(netPlaylistNextPageButton, THIS));
          netPlaylistEndPageButton.addMouseListener(new ButtonMouseListener(netPlaylistEndPageButton, THIS));
          // 帮助提示
          netPlaylistBackwardButton.setToolTipText(BACKWARD_TIP);
          netPlaylistClearInputButton.setToolTipText(CLEAR_INPUT_TIP);
          netPlaylistSearchButton.setToolTipText(SEARCH_TIP);
          netPlaylistPlayAllButton.setToolTipText(PLAY_ALL_TIP);
          netPlaylistRefreshButton.setToolTipText(REFRESH_TIP);
          netPlaylistStartPageButton.setToolTipText(START_PAGE_TIP);
          netPlaylistLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netPlaylistGoButton.setToolTipText(GO_TIP);
          netPlaylistNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netPlaylistEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netPlaylistPlayAllButton.setIconTextGap(5);
  
          netPlaylistToolBar.add(netPlaylistBackwardButton);
          netPlaylistToolBar.add(netPlaylistIdCheckBox);
          netPlaylistToolBar.add(netPlaylistSearchTextField);
          netPlaylistToolBar.add(netPlaylistClearInputButton);
          netPlaylistToolBar.add(netPlaylistSearchButton);
          netPlaylistLeftBox.add(netPlaylistToolBar);
  
          for (String name : NetMusicSource.NAMES) netPlaylistSourceComboBox.addItem(name);
          netPlaylistSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netPlaylistSearchButton.doClick();
          });
  
          // 歌单数量和当前歌曲标签
          netPlaylistCountPanel.add(netPlaylistSourceComboBox);
          netPlaylistCountPanel.add(netPlaylistPlayAllButton);
          netPlaylistCountPanel.add(netPlaylistStartPageButton);
          netPlaylistCountPanel.add(netPlaylistLastPageButton);
          netPlaylistCountPanel.add(netPlaylistCountLabel);
          netPlaylistCountPanel.add(netPlaylistNextPageButton);
          netPlaylistCountPanel.add(netPlaylistEndPageButton);
          netPlaylistCountPanel.add(netPlaylistRefreshButton);
          netPlaylistCountPanel.add(netPlaylistPageTextField);
          netPlaylistCountPanel.add(netPlaylistGoButton);
          netPlaylistCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netPlaylistCountPanel.setVisible(false);
          netPlaylistLeftBox.add(netPlaylistCountPanel);
      }
  
      // 初始化在线歌单
      private void initNetPlaylistList() {
          netPlaylistList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          netPlaylistList.setModel(netPlaylistListModel);
          netPlaylistList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netPlaylistList.locationToIndex(e.getPoint());
                  Rectangle bounds = netPlaylistList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetPlaylistListRenderer renderer = (NetPlaylistListRenderer) netPlaylistList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netPlaylistList.repaint();
              }
          });
          netPlaylistList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetPlaylistListRenderer renderer = (NetPlaylistListRenderer) netPlaylistList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netPlaylistList.repaint();
              }
          });
          // 打开歌单事件
          Runnable openPlaylistAction = () -> {
              NetPlaylistInfo playlistInfo = netPlaylistList.getSelectedValue();
              if (playlistInfo == null) return;
              loadingAndRun(() -> {
                  List<Future<?>> taskList = new LinkedList<>();
  
                  // 检查收藏按钮
                  checkDescriptionCollectionStatus(playlistDescriptionCollectionButton, playlistInfo);
  
                  // 加载封面图片和描述
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                      playlistCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                      playlistCoverAndNameLabel.setText(LOADING_MSG);
                      playlistTagLabel.setText(LOADING_MSG);
                      playlistDescriptionLabel.setText(LOADING_MSG);
                      GlobalExecutors.requestExecutor.execute(() -> {
                          try {
                              MusicServerUtil.fillPlaylistInfo(playlistInfo);
                              updateRenderer(netPlaylistList);
                              playlistCoverAndNameLabel.setText(StringUtil.textToHtml(playlistInfo.getName()));
                              if (playlistInfo.getTag() != null)
                                  playlistTagLabel.setText(StringUtil.textToHtml(playlistInfo.getTag().isEmpty() ? "" : "标签：" + playlistInfo.getTag()));
                              if (playlistInfo.getDescription() != null)
                                  playlistDescriptionLabel.setText(StringUtil.textToHtml(playlistInfo.getDescription()));
                              if (playlistInfo.hasCoverImg()) {
                                  playlistCoverAndNameLabel.setIcon(new ImageIcon(
                                          ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(playlistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                  ));
                              } else {
                                  playlistInfo.setInvokeLater(() -> {
                                      playlistCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(playlistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                      netPlaylistList.repaint();
                                  });
                              }
                          } catch (HttpException e) {
                              playlistCoverAndNameLabel.setText(LOAD_FAILED);
                              playlistTagLabel.setText(LOAD_FAILED);
                              playlistDescriptionLabel.setText(LOAD_FAILED);
                          }
                      });
                  }));
  
  
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      try {
                          // 得到歌单的音乐信息
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInPlaylist(playlistInfo, netMusicInPlaylistCurrPage = 1, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netMusicInPlaylistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新歌单歌曲数量显示
  //                            netCurrPlaylistLabel.setText(StringUtils.textToHtml(playlistInfo.getName()));
                          netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netMusicInPlaylistCurrPage, netMusicInPlaylistMaxPage));
                          playlistListCountBox.add(netPlaylistCountPanel);
                          playlistListBox.add(playlistListCountBox);
                          // 歌单列表切换到在线音乐列表
                          netMusicList.setModel(netMusicListForPlaylistModel);
                          netPlaylistLeftBox.remove(netPlaylistScrollPane);
                          netPlaylistLeftBox.add(playlistListBox);
                          // 删除工具栏的搜索
                          netPlaylistToolBar.removeAll();
                          netPlaylistToolBar.add(netPlaylistBackwardButton);
                          netPlaylistToolBar.add(Box.createHorizontalGlue());
                          // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForPlaylistModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForPlaylistModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForPlaylistModel);
                          playlistDescriptionScrollPane.setVValue(0);
                          netMusicScrollPane.setVValue(0);
                          netPlaylistBackwardButton.setEnabled(true);
                          netPlaylistSourceComboBox.setVisible(false);
                          netPlaylistPlayAllButton.setVisible(true);
                          if (netMusicListForPlaylistModel.isEmpty()) {
                              playlistListCountBox.remove(netMusicScrollPane);
                              playlistListCountBox.add(emptyHintPanel);
                          } else {
                              playlistListCountBox.remove(emptyHintPanel);
                              playlistListCountBox.add(netMusicScrollPane);
                          }
                          // 切换后一定要刷新！
                          netPlaylistLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  }));
  
                  taskList.forEach(task -> {
                      try {
                          task.get();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } catch (ExecutionException e) {
                          e.printStackTrace();
                      }
                  });
              });
          };
          netPlaylistList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      openPlaylistAction.run();
                  }
              }
          });
          netPlaylistList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开歌单
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      openPlaylistAction.run();
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netPlaylistList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netPlaylistList.locationToIndex(e.getPoint());
                          if (index == -1) return;
                          if (!netPlaylistList.isSelectedIndex(index)) netPlaylistList.setSelectedIndex(index);
                          if (hasBeenCollected(netPlaylistList.getSelectedValue())) {
                              netPlaylistCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netPlaylistCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                          } else {
                              netPlaylistCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                              netPlaylistCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                          }
  
                          netPlaylistPopupMenu.show(netPlaylistList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 打开歌单
          netPlaylistOpenMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              if (index == TabIndex.NET_PLAYLIST) openPlaylistAction.run();
              else if (index == TabIndex.PERSONAL) openCollectionItemAction.run();
              else openRecommendItemAction.run();
          });
          // 播放全部
          netPlaylistPlayAllMenuItem.addActionListener(e -> {
              NetPlaylistInfo playlistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_PLAYLIST) playlistInfo = netPlaylistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  playlistInfo = (NetPlaylistInfo) collectionList.getSelectedValue();
              else playlistInfo = (NetPlaylistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInPlaylist(playlistInfo, 1, playlistInfo.hasTrackCount() ? playlistInfo.getTrackCount() : 10000);
                  List<NetMusicInfo> musicInfos = result.data;
                  if (musicInfos.isEmpty()) {
                      new TipDialog(THIS, NO_MUSIC_MSG).showDialog();
                      return;
                  }
                  playQueue.setModel(emptyListModel);
                  playQueueModel.clear();
                  musicInfos.forEach(musicInfo -> {
                      globalExecutor.execute(() -> updateCollection(musicInfo));
                      playQueueModel.addElement(musicInfo);
                  });
                  playQueue.setModel(playQueueModel);
                  playQueue.setSelectedIndex(0);
                  playExecutor.execute(() -> playSelected(playQueue, true));
              });
          });
          // 收藏歌单
          netPlaylistCollectMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              CustomList list;
              if (index == TabIndex.NET_PLAYLIST) list = netPlaylistList;
              else if (index == TabIndex.PERSONAL) list = collectionList;
              else list = itemRecommendList;
              ListModel model = collectionList.getModel();
              boolean needRefresh = model == playlistCollectionModel;
              if (netPlaylistCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT) && list.isShowing()
                      || list == netPlaylistList && playlistDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == itemRecommendList && recommendItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == collectionList && collectionItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)) {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  // 描述面板取消收藏之后，使用之前保存的对象
                  boolean userOpenResource = values.isEmpty() && !hasBeenCollected(collectionOpenResource);
                  if (userOpenResource) {
                      playlistCollectionModel.add(0, collectionOpenResource);
                  } else {
                      for (int i = values.size() - 1; i >= 0; i--) {
                          NetPlaylistInfo playlistInfo = (NetPlaylistInfo) values.get(i);
                          if (hasBeenCollected(playlistInfo)) continue;
                          playlistCollectionModel.add(0, playlistInfo);
                      }
                  }
                  if (needRefresh) collectionList.setModel(model);
                  if (userOpenResource) collectionList.setSelectedIndex(0);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  values.forEach(o -> {
                      if (list == collectionList || hasBeenCollected((Resource) o)) {
                          playlistCollectionModel.removeElement(o);
                      }
                  });
                  if (needRefresh) collectionList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 查看评论
          netPlaylistCommentMenuItem.addActionListener(e -> {
              NetPlaylistInfo playlistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_PLAYLIST) playlistInfo = netPlaylistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  playlistInfo = (NetPlaylistInfo) collectionList.getSelectedValue();
              else playlistInfo = (NetPlaylistInfo) itemRecommendList.getSelectedValue();
              getComments(playlistInfo, true);
          });
          // 查看相似歌单
          netPlaylistSimilarPlaylistMenuItem.addActionListener(e -> {
              NetPlaylistInfo playlistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_PLAYLIST) playlistInfo = netPlaylistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  playlistInfo = (NetPlaylistInfo) collectionList.getSelectedValue();
              else playlistInfo = (NetPlaylistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForPlaylist();
                      // 搜索相似歌单
                      CommonResult<NetPlaylistInfo> result = MusicServerUtil.getSimilarPlaylists(currPlaylistPlaylistInfo = playlistInfo);
                      List<NetPlaylistInfo> playlistInfos = result.data;
                      netPlaylistCurrPage = netPlaylistMaxPage = 1;
                      // 标题
                      netPlaylistTitleLabel.setText(StringUtil.textToHtml(playlistInfo.getName() + I18n.getText("similarPlaylistSuffix")));
                      netPlaylistToolBar.removeAll();
                      netPlaylistToolBar.add(netPlaylistBackwardButton);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      netPlaylistToolBar.add(netPlaylistTitleLabel);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netPlaylistCurrPage, netPlaylistMaxPage));
                      netPlaylistCountPanel.add(netPlaylistCountLabel, netPlaylistCountPanel.getComponentIndex(netPlaylistCountLabel));
                      netPlaylistLeftBox.add(netPlaylistCountPanel);
                      netPlaylistSourceComboBox.setVisible(false);
                      netPlaylistPlayAllButton.setVisible(false);
                      netPlaylistCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netPlaylistList.setModel(emptyListModel);
                      netPlaylistListModel.clear();
                      playlistInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netPlaylistList);
                              updateRenderer(collectionList);
                              netPlaylistList.repaint();
                              collectionList.repaint();
                          });
                          netPlaylistListModel.addElement(info);
                      });
                      netPlaylistList.setModel(netPlaylistListModel);
                      netPlaylistBackwardButton.setEnabled(true);
                      netPlaylistLeftBox.remove(netPlaylistKeywordsPanelScrollPane);
                      if (netPlaylistListModel.isEmpty()) {
                          netPlaylistLeftBox.remove(netPlaylistScrollPane);
                          netPlaylistLeftBox.add(emptyHintPanel);
                      } else {
                          netPlaylistLeftBox.remove(emptyHintPanel);
                          netPlaylistLeftBox.add(netPlaylistScrollPane);
                      }
                      netPlaylistScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_PLAYLIST);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看创建者
          netPlaylistCreatorMenuItem.addActionListener(e -> {
              NetPlaylistInfo playlistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_PLAYLIST) playlistInfo = netPlaylistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  playlistInfo = (NetPlaylistInfo) collectionList.getSelectedValue();
              else playlistInfo = (NetPlaylistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForUser();
                      // 获取歌单创建者
                      currUserPlaylistInfo = playlistInfo;
                      CommonResult<NetUserInfo> result = MusicServerUtil.getUserInfo(playlistInfo.getSource(), playlistInfo.getCreatorId());
                      List<NetUserInfo> userInfos = result.data;
                      netUserCurrPage = netUserMaxPage = 1;
                      // 标题
                      netUserTitleLabel.setText(StringUtil.textToHtml(playlistInfo.getName() + I18n.getText("creatorSuffix")));
                      netUserToolBar.removeAll();
                      netUserToolBar.add(netUserBackwardButton);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      netUserLeftBox.add(netUserCountPanel);
                      netUserSourceComboBox.setVisible(false);
                      netUserPlayAllButton.setVisible(false);
                      netUserRecordTypeComboBox.setVisible(false);
                      netUserCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netUserList.setModel(emptyListModel);
                      netUserListModel.clear();
                      userInfos.forEach(userInfo -> {
                          globalExecutor.execute(() -> updateCollection(userInfo));
                          // 设置图片加载后重绘的事件
                          userInfo.setInvokeLater(() -> {
                              updateRenderer(netUserList);
                              updateRenderer(collectionList);
                              netUserList.repaint();
                              collectionList.repaint();
                          });
                          netUserListModel.addElement(userInfo);
                      });
                      netUserList.setModel(netUserListModel);
                      netUserBackwardButton.setEnabled(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      if (netUserListModel.isEmpty()) {
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(emptyHintPanel);
                      } else {
                          netUserLeftBox.remove(emptyHintPanel);
                          netUserLeftBox.add(netUserScrollPane);
                      }
                      netUserScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看收藏者
          netPlaylistSubscriberMenuItem.addActionListener(e -> {
              NetPlaylistInfo playlistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_PLAYLIST) playlistInfo = netPlaylistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  playlistInfo = (NetPlaylistInfo) collectionList.getSelectedValue();
              else playlistInfo = (NetPlaylistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForUser();
                      // 获取歌单收藏者
                      CommonResult<NetUserInfo> result = MusicServerUtil.getPlaylistSubscribers(currSubscriberPlaylistInfo = playlistInfo, netUserCurrPage = 1, limit);
                      List<NetUserInfo> userInfos = result.data;
                      Integer total = result.total;
                      netUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netUserTitleLabel.setText(StringUtil.textToHtml(playlistInfo.getName() + I18n.getText("collectorSuffix")));
                      netUserToolBar.removeAll();
                      netUserToolBar.add(netUserBackwardButton);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      netUserLeftBox.add(netUserCountPanel);
                      netUserSourceComboBox.setVisible(false);
                      netUserPlayAllButton.setVisible(false);
                      netUserRecordTypeComboBox.setVisible(false);
                      netUserCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netUserList.setModel(emptyListModel);
                      netUserListModel.clear();
                      userInfos.forEach(userInfo -> {
                          globalExecutor.execute(() -> updateCollection(userInfo));
                          // 设置图片加载后重绘的事件
                          userInfo.setInvokeLater(() -> {
                              updateRenderer(netUserList);
                              updateRenderer(collectionList);
                              netUserList.repaint();
                              collectionList.repaint();
                          });
                          netUserListModel.addElement(userInfo);
                      });
                      netUserList.setModel(netUserListModel);
                      netUserBackwardButton.setEnabled(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      if (netUserListModel.isEmpty()) {
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(emptyHintPanel);
                      } else {
                          netUserLeftBox.remove(emptyHintPanel);
                          netUserLeftBox.add(netUserScrollPane);
                      }
                      netUserScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 复制名称
          netPlaylistCopyNameMenuItem.addActionListener(e -> {
              NetResource resource;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_PLAYLIST) resource = netPlaylistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL) resource = collectionList.getSelectedValue();
              else resource = itemRecommendList.getSelectedValue();
              copyToClipboard(((NetPlaylistInfo) resource).toSimpleString());
          });
          // 歌单列表右键菜单项
          netPlaylistPopupMenu.add(netPlaylistOpenMenuItem);
          netPlaylistPopupMenu.add(netPlaylistPlayAllMenuItem);
          netPlaylistPopupMenu.add(netPlaylistCollectMenuItem);
          netPlaylistPopupMenu.add(netPlaylistCommentMenuItem);
          netPlaylistPopupMenu.add(netPlaylistSimilarPlaylistMenuItem);
          netPlaylistPopupMenu.add(netPlaylistCreatorMenuItem);
          netPlaylistPopupMenu.add(netPlaylistSubscriberMenuItem);
          netPlaylistPopupMenu.add(netPlaylistCopyNameMenuItem);
  
          // 搜索历史面板
          // 清空搜索历史按钮
          netPlaylistClearHistorySearchButton.addActionListener(e -> netPlaylistHistorySearchInnerPanel2.removeAll());
          netPlaylistClearHistorySearchButton.setPreferredSize(new Dimension(30, 30));
          netPlaylistClearHistorySearchButton.setToolTipText(CLEAR_HISTORY_SEARCH_TIP);
          netPlaylistClearHistorySearchButton.addMouseListener(new ButtonMouseListener(netPlaylistClearHistorySearchButton, THIS));
  
          netPlaylistHistorySearchInnerPanel1.setVisible(false);
          netPlaylistHistorySearchInnerPanel1.add(netPlaylistHistorySearchLabel);
          netPlaylistHistorySearchInnerPanel1.add(netPlaylistClearHistorySearchButton);
          netPlaylistHistorySearchInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netPlaylistHistorySearchInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netPlaylistHistorySearchInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netPlaylistHistorySearchInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netPlaylistHistorySearchInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netPlaylistHistorySearchInnerPanel2.addContainerListener(new ContainerAdapter() {
              @Override
              public void componentAdded(ContainerEvent e) {
                  netPlaylistHistorySearchInnerPanel1.setVisible(true);
              }
  
              @Override
              public void componentRemoved(ContainerEvent e) {
                  if (netPlaylistHistorySearchInnerPanel2.getComponentCount() == 0)
                      netPlaylistHistorySearchInnerPanel1.setVisible(false);
              }
          });
          netPlaylistHistorySearchPanel.setLayout(new BoxLayout(netPlaylistHistorySearchPanel, BoxLayout.Y_AXIS));
          netPlaylistHistorySearchPanel.add(netPlaylistHistorySearchInnerPanel1);
          netPlaylistHistorySearchPanel.add(netPlaylistHistorySearchInnerPanel2);
  
          // 关键词面板
          netPlaylistKeywordsPanel.setLayout(new BoxLayout(netPlaylistKeywordsPanel, BoxLayout.Y_AXIS));
          netPlaylistKeywordsPanel.add(netPlaylistHistorySearchPanel);
  
          // 同时限制面板与滚动条的大小才能保证按钮不超出窗口！！！
          Dimension d = new Dimension(900, 500);
          netPlaylistKeywordsPanel.setPreferredSize(d);
          netPlaylistKeywordsPanelScrollPane.setPreferredSize(d);
  
          // 在线歌单最佳大小
          netPlaylistScrollPane.setPreferredSize(new Dimension(200, 600));
          netPlaylistLeftBox.add(netPlaylistScrollPane);
      }
  
      // 初始化在线专辑工具栏
      private void initNetAlbumToolBar() {
          netAlbumSearchTextField.addFocusListener(new TextFieldHintListener(netAlbumSearchTextField, "专辑", currUIStyle.getForeColor()));
          netAlbumSearchTextField.getDocument().addDocumentListener(new DocumentListener() {
              @Override
              public void insertUpdate(DocumentEvent e) {
                  if (netAlbumSearchTextField.isOccupied()) {
                      netAlbumClearInputButton.setVisible(true);
                  }
              }
  
              @Override
              public void removeUpdate(DocumentEvent e) {
                  if (netAlbumSearchTextField.getText().isEmpty()) netAlbumClearInputButton.setVisible(false);
              }
  
              @Override
              public void changedUpdate(DocumentEvent e) {
  
              }
          });
          // 只能输入数字
          netAlbumPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 后退按钮事件
          netAlbumBackwardButton.addActionListener(e -> {
              int lastComponentIndex = netAlbumLeftBox.getComponentCount() - 1;
              Component c = netAlbumLeftBox.getComponent(lastComponentIndex);
              if (c != netAlbumScrollPane && c != emptyHintPanel) {
                  netMusicListForAlbumModel.clear();
                  // 从在线音乐列表切换回专辑列表
                  netAlbumPlayAllButton.setVisible(false);
                  netAlbumLeftBox.remove(albumListBox);
                  netAlbumLeftBox.add(netAlbumCountPanel);
                  netAlbumLeftBox.add(netAlbumScrollPane);
                  // 添加回工具栏的搜索/标题
                  netAlbumToolBar.removeAll();
                  netAlbumToolBar.add(netAlbumBackwardButton);
                  if (currAlbumArtistInfo == null
                          && currAlbumMusicInfo == null
                          && currAlbumAlbumInfo == null
                          && currAlbumUserInfo == null
                          && currAlbumCommentInfo == null) {
                      // 删除标题标签
                      netAlbumToolBar.add(netAlbumSearchTextField);
                      netAlbumToolBar.add(netAlbumClearInputButton);
                      netAlbumToolBar.add(netAlbumSearchButton);
                      netAlbumSourceComboBox.setVisible(true);
                  } else {
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      netAlbumToolBar.add(netAlbumTitleLabel);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                  }
                  // 更新专辑歌曲数量显示
  //                netCurrAlbumLabel.setText("");
                  netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netAlbumCurrPage, netAlbumMaxPage));
  //                netAlbumBackwardButton.setEnabled(false);
              }
              // 后退到关键词面板
              else {
                  // 删除标题标签
                  netAlbumToolBar.removeAll();
                  netAlbumToolBar.add(netAlbumBackwardButton);
                  netAlbumToolBar.add(netAlbumSearchTextField);
                  netAlbumToolBar.add(netAlbumClearInputButton);
                  netAlbumToolBar.add(netAlbumSearchButton);
                  clearRequestForAlbum();
  
                  netAlbumListModel.clear();
                  netAlbumCountPanel.setVisible(false);
                  netAlbumLeftBox.remove(emptyHintPanel);
                  netAlbumLeftBox.remove(netAlbumScrollPane);
                  netAlbumLeftBox.add(netAlbumKeywordsPanelScrollPane);
                  netAlbumBackwardButton.setEnabled(false);
              }
              // 切换后一定要刷新！
              netAlbumLeftBox.repaint();
          });
          // 清除输入事件
          netAlbumClearInputButton.addActionListener(e -> {
              netAlbumSearchTextField.requestFocus();
              netAlbumSearchTextField.setText("");
              netAlbumBackwardButton.requestFocus();
          });
          // 搜索专辑按钮事件
          netAlbumSearchButton.addActionListener(e -> {
              netAlbumCurrKeyword = netAlbumSearchTextField.getText().trim();
              if (!netAlbumCurrKeyword.isEmpty()) {
                  // 关键词加入搜索历史
                  DialogButton b = new DialogButton(netAlbumCurrKeyword);
                  b.addActionListener(event -> {
                      netAlbumSearchTextField.requestFocus();
                      netAlbumSearchTextField.setText(b.getPlainText());
                      netAlbumClearInputButton.setVisible(true);
                      netAlbumLeftBox.remove(netAlbumKeywordsPanelScrollPane);
                      netAlbumLeftBox.add(netAlbumScrollPane);
                      netAlbumSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netAlbumHistorySearchInnerPanel2.remove(b);
                          netAlbumHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  // 先删除重复的关键词
                  removeKeywordInHistorySearch(netAlbumCurrKeyword, HistorySearchType.NET_ALBUM);
                  netAlbumHistorySearchInnerPanel2.add(b, 0);
                  // 删除超出上限的最后一个关键词
                  int c = netAlbumHistorySearchInnerPanel2.getComponentCount();
                  if (c > maxSearchHistoryCount)
                      netAlbumHistorySearchInnerPanel2.remove(c - 1);
                  netAlbumHistorySearchPanel.repaint();
  
                  loadingAndRun(() -> {
                      try {
                          // 搜索专辑并显示专辑列表
                          CommonResult<NetAlbumInfo> result = MusicServerUtil.searchAlbums(
                                  netAlbumSourceComboBox.getSelectedIndex(), netAlbumCurrKeyword, netAlbumCurrPage = 1, limit);
                          List<NetAlbumInfo> albumInfos = result.data;
                          Integer total = result.total;
                          netAlbumMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netAlbumCurrPage, netAlbumMaxPage));
                          netAlbumCountPanel.add(netAlbumCountLabel, netAlbumCountPanel.getComponentIndex(netAlbumCountLabel));
                          netAlbumCountPanel.setVisible(true);
                          netAlbumSourceComboBox.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netAlbumList.setModel(emptyListModel);
                          netAlbumListModel.clear();
                          albumInfos.forEach(albumInfo -> {
                              globalExecutor.execute(() -> updateCollection(albumInfo));
                              // 设置图片加载后重绘的事件
                              albumInfo.setInvokeLater(() -> {
                                  updateRenderer(netAlbumList);
                                  updateRenderer(collectionList);
                                  netAlbumList.repaint();
                                  collectionList.repaint();
                              });
                              netAlbumListModel.addElement(albumInfo);
                          });
                          netAlbumList.setModel(netAlbumListModel);
                          netAlbumBackwardButton.setEnabled(true);
                          netAlbumLeftBox.remove(netAlbumKeywordsPanelScrollPane);
                          if (netAlbumListModel.isEmpty()) {
                              netAlbumLeftBox.remove(netAlbumScrollPane);
                              netAlbumLeftBox.add(emptyHintPanel);
                          } else {
                              netAlbumLeftBox.remove(emptyHintPanel);
                              netAlbumLeftBox.add(netAlbumScrollPane);
                          }
                          netAlbumScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          });
          // 搜索专辑跳页事件
          Runnable searchAlbumGoPageAction = () -> {
              boolean songRequest = currAlbumMusicInfo != null, artistRequest = currAlbumArtistInfo != null, albumRequest = currAlbumAlbumInfo != null,
                      userRequest = currAlbumUserInfo != null, commentRequest = currAlbumCommentInfo != null;
              if (artistRequest || albumRequest || userRequest || commentRequest || songRequest || StringUtil.notEmpty(netAlbumCurrKeyword)) {
                  loadingAndRun(() -> {
                      try {
                          // 搜索专辑并显示专辑列表
                          CommonResult<NetAlbumInfo> result = artistRequest ? MusicServerUtil.getAlbumInfoInArtist(currAlbumArtistInfo, netAlbumCurrPage, limit)
                                  : albumRequest ? MusicServerUtil.getSimilarAlbums(currAlbumAlbumInfo)
                                  : userRequest ? MusicServerUtil.getUserAlbums(currAlbumUserInfo, netAlbumCurrPage, limit)
                                  : commentRequest ? MusicServerUtil.getUserAlbums(currAlbumCommentInfo, netAlbumCurrPage, limit)
                                  : songRequest ? MusicServerUtil.getAlbumInfo(currAlbumMusicInfo.getSource(), currAlbumMusicInfo.getAlbumId())
                                  : MusicServerUtil.searchAlbums(netAlbumSourceComboBox.getSelectedIndex(), netAlbumCurrKeyword, netAlbumCurrPage, limit);
                          List<NetAlbumInfo> albumInfos = result.data;
                          Integer total = result.total;
                          netAlbumMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netAlbumCurrPage, netAlbumMaxPage));
                          netAlbumCountPanel.add(netAlbumCountLabel, netAlbumCountPanel.getComponentIndex(netAlbumCountLabel));
                          netAlbumCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netAlbumList.setModel(emptyListModel);
                          netAlbumListModel.clear();
                          albumInfos.forEach(albumInfo -> {
                              globalExecutor.execute(() -> updateCollection(albumInfo));
                              // 设置图片加载后重绘的事件
                              albumInfo.setInvokeLater(() -> {
                                  updateRenderer(netAlbumList);
                                  updateRenderer(collectionList);
                                  netAlbumList.repaint();
                                  collectionList.repaint();
                              });
                              netAlbumListModel.addElement(albumInfo);
                          });
                          netAlbumList.setModel(netAlbumListModel);
                          netAlbumScrollPane.setVValue(0);
                          if (netAlbumListModel.isEmpty()) {
                              netAlbumLeftBox.remove(netAlbumScrollPane);
                              netAlbumLeftBox.add(emptyHintPanel);
                          } else {
                              netAlbumLeftBox.remove(emptyHintPanel);
                              netAlbumLeftBox.add(netAlbumScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 搜索专辑内歌曲并显示在在线歌曲列表
          Runnable searchMusicInAlbum = () -> {
              if (!netMusicListForAlbumModel.isEmpty()) {
                  loadingAndRun(() -> {
                      try {
                          NetAlbumInfo albumInfo = netAlbumList.getSelectedValue();
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInAlbum(albumInfo, netMusicInAlbumCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          Integer total = result.total;
                          netMusicInAlbumMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netMusicInAlbumCurrPage, netMusicInAlbumMaxPage));
                          netAlbumCountPanel.add(netAlbumCountLabel, netAlbumCountPanel.getComponentIndex(netAlbumCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForAlbumModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForAlbumModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForAlbumModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicListForAlbumModel.isEmpty()) {
                              albumListCountBox.remove(netMusicScrollPane);
                              albumListCountBox.add(emptyHintPanel);
                          } else {
                              albumListCountBox.remove(emptyHintPanel);
                              albumListCountBox.add(netMusicScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 播放全部
          netAlbumPlayAllButton.addActionListener(e -> netAlbumPlayAllMenuItem.doClick());
          // 刷新按钮事件
          netAlbumRefreshButton.addActionListener(e -> {
              Component lc = netAlbumLeftBox.getComponent(netAlbumLeftBox.getComponentCount() - 1);
              // 当前显示的是专辑列表，刷新专辑
              if (lc == netAlbumScrollPane || lc == emptyHintPanel) {
                  searchAlbumGoPageAction.run();
              }
              // 当前显示的是某专辑的歌曲，刷新歌曲
              else {
                  searchMusicInAlbum.run();
              }
          });
          // 第一页按钮事件
          netAlbumStartPageButton.addActionListener(e -> {
              Component lc = netAlbumLeftBox.getComponent(netAlbumLeftBox.getComponentCount() - 1);
              // 当前显示的是专辑列表，跳到第一页专辑
              if (lc == netAlbumScrollPane || lc == emptyHintPanel) {
                  if (netAlbumCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netAlbumCurrPage = 1;
                  searchAlbumGoPageAction.run();
              }
              // 当前显示的是某专辑的歌曲，跳到第一页歌曲
              else {
                  if (netMusicInAlbumCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInAlbumCurrPage = 1;
                  searchMusicInAlbum.run();
              }
          });
          // 上一页按钮事件
          netAlbumLastPageButton.addActionListener(e -> {
              Component lc = netAlbumLeftBox.getComponent(netAlbumLeftBox.getComponentCount() - 1);
              // 当前显示的是专辑列表，跳到上一页专辑
              if (lc == netAlbumScrollPane || lc == emptyHintPanel) {
                  if (netAlbumCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netAlbumCurrPage--;
                  searchAlbumGoPageAction.run();
              }
              // 当前显示的是某专辑的歌曲，跳到上一页歌曲
              else {
                  if (netMusicInAlbumCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInAlbumCurrPage--;
                  searchMusicInAlbum.run();
              }
          });
          // 下一页按钮事件
          netAlbumNextPageButton.addActionListener(e -> {
              Component lc = netAlbumLeftBox.getComponent(netAlbumLeftBox.getComponentCount() - 1);
              // 当前显示的是专辑列表，跳到下一页专辑
              if (lc == netAlbumScrollPane || lc == emptyHintPanel) {
                  if (netAlbumCurrPage >= netAlbumMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netAlbumCurrPage++;
                  searchAlbumGoPageAction.run();
              }
              // 当前显示的是某专辑的歌曲，跳到下一页歌曲
              else {
                  if (netMusicInAlbumCurrPage >= netMusicInAlbumMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInAlbumCurrPage++;
                  searchMusicInAlbum.run();
              }
          });
          // 最后一页按钮事件
          netAlbumEndPageButton.addActionListener(e -> {
              Component lc = netAlbumLeftBox.getComponent(netAlbumLeftBox.getComponentCount() - 1);
              // 当前显示的是专辑列表，跳到最后一页专辑
              if (lc == netAlbumScrollPane || lc == emptyHintPanel) {
                  if (netAlbumCurrPage >= netAlbumMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netAlbumCurrPage = netAlbumMaxPage;
                  searchAlbumGoPageAction.run();
              }
              // 当前显示的是某专辑的歌曲，跳到最后一页歌曲
              else {
                  if (netMusicInAlbumCurrPage >= netMusicInAlbumMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInAlbumCurrPage = netMusicInAlbumMaxPage;
                  searchMusicInAlbum.run();
              }
          });
          // 跳页按钮事件
          netAlbumGoButton.addActionListener(e -> {
              Component lc = netAlbumLeftBox.getComponent(netAlbumLeftBox.getComponentCount() - 1);
              // 当前显示的是专辑列表，跳页专辑
              if (lc == netAlbumScrollPane || lc == emptyHintPanel) {
                  String text = netAlbumPageTextField.getText();
                  netAlbumPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netAlbumMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netAlbumCurrPage = destPage;
                  searchAlbumGoPageAction.run();
              }
              // 当前显示的是某专辑的歌曲，跳页歌曲
              else {
                  String text = netAlbumPageTextField.getText();
                  netAlbumPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netMusicInAlbumMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInAlbumCurrPage = destPage;
                  searchMusicInAlbum.run();
              }
          });
          // 按钮被禁止的图标
          netAlbumBackwardButton.setEnabled(false);
          // 按钮初始不可见
          netAlbumClearInputButton.setVisible(false);
          netAlbumPlayAllButton.setVisible(false);
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netAlbumRefreshButton.setPreferredSize(dimension);
          netAlbumStartPageButton.setPreferredSize(dimension);
          netAlbumLastPageButton.setPreferredSize(dimension);
          netAlbumGoButton.setPreferredSize(dimension);
          netAlbumNextPageButton.setPreferredSize(dimension);
          netAlbumEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netAlbumBackwardButton.addMouseListener(new ButtonMouseListener(netAlbumBackwardButton, THIS));
          netAlbumClearInputButton.addMouseListener(new ButtonMouseListener(netAlbumClearInputButton, THIS));
          netAlbumSearchButton.addMouseListener(new ButtonMouseListener(netAlbumSearchButton, THIS));
          netAlbumPlayAllButton.addMouseListener(new ButtonMouseListener(netAlbumPlayAllButton, THIS));
          netAlbumRefreshButton.addMouseListener(new ButtonMouseListener(netAlbumRefreshButton, THIS));
          netAlbumStartPageButton.addMouseListener(new ButtonMouseListener(netAlbumStartPageButton, THIS));
          netAlbumLastPageButton.addMouseListener(new ButtonMouseListener(netAlbumLastPageButton, THIS));
          netAlbumGoButton.addMouseListener(new ButtonMouseListener(netAlbumGoButton, THIS));
          netAlbumNextPageButton.addMouseListener(new ButtonMouseListener(netAlbumNextPageButton, THIS));
          netAlbumEndPageButton.addMouseListener(new ButtonMouseListener(netAlbumEndPageButton, THIS));
          // 帮助提示
          netAlbumBackwardButton.setToolTipText(BACKWARD_TIP);
          netAlbumClearInputButton.setToolTipText(CLEAR_INPUT_TIP);
          netAlbumSearchButton.setToolTipText(SEARCH_TIP);
          netAlbumPlayAllButton.setToolTipText(PLAY_ALL_TIP);
          netAlbumRefreshButton.setToolTipText(REFRESH_TIP);
          netAlbumStartPageButton.setToolTipText(START_PAGE_TIP);
          netAlbumLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netAlbumGoButton.setToolTipText(GO_TIP);
          netAlbumNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netAlbumEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netAlbumPlayAllButton.setIconTextGap(5);
  
          netAlbumToolBar.add(netAlbumBackwardButton);
          netAlbumToolBar.add(netAlbumSearchTextField);
          netAlbumToolBar.add(netAlbumClearInputButton);
          netAlbumToolBar.add(netAlbumSearchButton);
          netAlbumLeftBox.add(netAlbumToolBar);
  
          for (String name : NetMusicSource.NAMES) netAlbumSourceComboBox.addItem(name);
          netAlbumSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netAlbumSearchButton.doClick();
          });
  
          // 专辑数量和当前歌曲标签
          netAlbumCountPanel.add(netAlbumSourceComboBox);
          netAlbumCountPanel.add(netAlbumPlayAllButton);
          netAlbumCountPanel.add(netAlbumStartPageButton);
          netAlbumCountPanel.add(netAlbumLastPageButton);
          netAlbumCountPanel.add(netAlbumCountLabel);
          netAlbumCountPanel.add(netAlbumNextPageButton);
          netAlbumCountPanel.add(netAlbumEndPageButton);
          netAlbumCountPanel.add(netAlbumRefreshButton);
          netAlbumCountPanel.add(netAlbumPageTextField);
          netAlbumCountPanel.add(netAlbumGoButton);
          netAlbumCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netAlbumCountPanel.setVisible(false);
          netAlbumLeftBox.add(netAlbumCountPanel);
      }
  
      // 初始化在线专辑列表
      private void initNetAlbumList() {
          netAlbumList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          netAlbumList.setModel(netAlbumListModel);
          netAlbumList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netAlbumList.locationToIndex(e.getPoint());
                  Rectangle bounds = netAlbumList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetAlbumListRenderer renderer = (NetAlbumListRenderer) netAlbumList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netAlbumList.repaint();
              }
          });
          netAlbumList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetAlbumListRenderer renderer = (NetAlbumListRenderer) netAlbumList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netAlbumList.repaint();
              }
          });
          // 打开专辑
          Runnable openAlbumAction = () -> {
              NetAlbumInfo albumInfo = netAlbumList.getSelectedValue();
              if (albumInfo == null) return;
              loadingAndRun(() -> {
                  List<Future<?>> taskList = new LinkedList<>();
  
                  // 检查收藏按钮
                  checkDescriptionCollectionStatus(albumDescriptionCollectionButton, albumInfo);
  
                  // 加载封面图片和描述
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                      albumCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                      albumCoverAndNameLabel.setText(LOADING_MSG);
                      albumDescriptionLabel.setText(LOADING_MSG);
                      GlobalExecutors.requestExecutor.execute(() -> {
                          try {
                              MusicServerUtil.fillAlbumInfo(albumInfo);
                              updateRenderer(netAlbumList);
                              albumCoverAndNameLabel.setText(StringUtil.textToHtml(albumInfo.getName()));
                              if (albumInfo.getDescription() != null)
                                  albumDescriptionLabel.setText(StringUtil.textToHtml(albumInfo.getDescription()));
                              if (albumInfo.hasCoverImg()) {
                                  albumCoverAndNameLabel.setIcon(new ImageIcon(
                                          ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(albumInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                  ));
                              } else {
                                  albumInfo.setInvokeLater(() -> {
                                      albumCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(albumInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                      netAlbumList.repaint();
                                  });
                              }
                          } catch (HttpException e) {
                              albumCoverAndNameLabel.setText(LOAD_FAILED);
                              albumDescriptionLabel.setText(LOAD_FAILED);
                          }
                      });
                  }));
  
  
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      try {
                          // 得到专辑的音乐信息
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInAlbum(albumInfo, netMusicInAlbumCurrPage = 1, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netMusicInAlbumMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新专辑歌曲数量显示
  //                            netCurrAlbumLabel.setText(StringUtils.textToHtml(albumInfo.getName()));
                          netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netMusicInAlbumCurrPage, netMusicInAlbumMaxPage));
                          albumListCountBox.add(netAlbumCountPanel);
                          albumListBox.add(albumListCountBox);
                          // 专辑列表切换到在线音乐列表
                          netMusicList.setModel(netMusicListForAlbumModel);
                          netAlbumLeftBox.remove(netAlbumScrollPane);
                          netAlbumLeftBox.add(albumListBox);
                          // 删除工具栏的搜索
                          netAlbumToolBar.remove(netAlbumSearchTextField);
                          netAlbumToolBar.remove(netAlbumClearInputButton);
                          netAlbumToolBar.remove(netAlbumSearchButton);
                          netAlbumToolBar.remove(netAlbumTitleLabel);
                          netAlbumToolBar.add(Box.createHorizontalGlue());
                          // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForAlbumModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForAlbumModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForAlbumModel);
                          albumDescriptionScrollPane.setVValue(0);
                          netMusicScrollPane.setVValue(0);
                          netAlbumBackwardButton.setEnabled(true);
                          netAlbumSourceComboBox.setVisible(false);
                          netAlbumPlayAllButton.setVisible(true);
                          if (netMusicListForAlbumModel.isEmpty()) {
                              albumListCountBox.remove(netMusicScrollPane);
                              albumListCountBox.add(emptyHintPanel);
                          } else {
                              albumListCountBox.remove(emptyHintPanel);
                              albumListCountBox.add(netMusicScrollPane);
                          }
                          // 切换后一定要刷新！
                          netAlbumLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  }));
  
                  taskList.forEach(task -> {
                      try {
                          task.get();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } catch (ExecutionException e) {
                          e.printStackTrace();
                      }
                  });
              });
          };
          netAlbumList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      openAlbumAction.run();
                  }
              }
          });
          netAlbumList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开专辑
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      openAlbumAction.run();
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netAlbumList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netAlbumList.locationToIndex(e.getPoint());
                          if (index == -1) return;
                          if (!netAlbumList.isSelectedIndex(index)) netAlbumList.setSelectedIndex(index);
                          if (hasBeenCollected(netAlbumList.getSelectedValue())) {
                              netAlbumCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netAlbumCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                          } else {
                              netAlbumCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                              netAlbumCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                          }
  
                          netAlbumPopupMenu.show(netAlbumList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 打开专辑
          netAlbumOpenMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              if (index == TabIndex.NET_ALBUM) openAlbumAction.run();
              else if (index == TabIndex.PERSONAL) openCollectionItemAction.run();
              else openRecommendItemAction.run();
          });
          // 播放全部
          netAlbumPlayAllMenuItem.addActionListener(e -> {
              NetAlbumInfo albumInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ALBUM) albumInfo = netAlbumList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  albumInfo = (NetAlbumInfo) collectionList.getSelectedValue();
              else albumInfo = (NetAlbumInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInAlbum(albumInfo, 1, albumInfo.hasSongNum() ? albumInfo.getSongNum() : 10000);
                  List<NetMusicInfo> musicInfos = result.data;
                  if (musicInfos.isEmpty()) {
                      new TipDialog(THIS, NO_MUSIC_MSG).showDialog();
                      return;
                  }
                  playQueue.setModel(emptyListModel);
                  playQueueModel.clear();
                  musicInfos.forEach(musicInfo -> {
                      globalExecutor.execute(() -> updateCollection(musicInfo));
                      playQueueModel.addElement(musicInfo);
                  });
                  playQueue.setModel(playQueueModel);
                  playQueue.setSelectedIndex(0);
                  playExecutor.execute(() -> playSelected(playQueue, true));
              });
          });
          // 收藏专辑
          netAlbumCollectMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              CustomList list;
              if (index == TabIndex.NET_ALBUM) list = netAlbumList;
              else if (index == TabIndex.PERSONAL) list = collectionList;
              else list = itemRecommendList;
              ListModel model = collectionList.getModel();
              boolean needRefresh = model == albumCollectionModel;
              if (netAlbumCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT) && list.isShowing()
                      || list == netAlbumList && albumDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == itemRecommendList && recommendItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == collectionList && collectionItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)) {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  // 描述面板取消收藏之后，使用之前保存的对象
                  boolean userOpenResource = values.isEmpty() && !hasBeenCollected(collectionOpenResource);
                  if (userOpenResource) {
                      albumCollectionModel.add(0, collectionOpenResource);
                  } else {
                      for (int i = values.size() - 1; i >= 0; i--) {
                          NetAlbumInfo albumInfo = (NetAlbumInfo) values.get(i);
                          if (hasBeenCollected(albumInfo)) continue;
                          albumCollectionModel.add(0, albumInfo);
                      }
                  }
                  if (needRefresh) collectionList.setModel(model);
                  if (userOpenResource) collectionList.setSelectedIndex(0);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  values.forEach(o -> {
                      if (list == collectionList || hasBeenCollected((Resource) o)) {
                          albumCollectionModel.removeElement(o);
                      }
                  });
                  if (needRefresh) collectionList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 查看评论
          netAlbumCommentMenuItem.addActionListener(e -> {
              NetAlbumInfo albumInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ALBUM) albumInfo = netAlbumList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  albumInfo = (NetAlbumInfo) collectionList.getSelectedValue();
              else albumInfo = (NetAlbumInfo) itemRecommendList.getSelectedValue();
              getComments(albumInfo, true);
          });
          // 查看歌手/作者
          netAlbumArtistMenuItem.addActionListener(e -> {
              NetAlbumInfo albumInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ALBUM) albumInfo = netAlbumList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  albumInfo = (NetAlbumInfo) collectionList.getSelectedValue();
              else albumInfo = (NetAlbumInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      if (albumInfo.isPhoto()) {
                          clearRequestForUser();
                          // 获取歌手/作者
                          currAuthorAlbumInfo = albumInfo;
                          CommonResult<NetUserInfo> result = MusicServerUtil.getUserInfo(albumInfo.getSource(), albumInfo.getArtistId());
                          List<NetUserInfo> userInfos = result.data;
                          netUserCurrPage = netUserMaxPage = 1;
                          // 标题
                          netUserTitleLabel.setText(StringUtil.textToHtml(albumInfo.toSimpleString() + I18n.getText("authorSuffix")));
                          netUserToolBar.removeAll();
                          netUserToolBar.add(netUserBackwardButton);
                          netUserToolBar.add(Box.createHorizontalGlue());
                          netUserToolBar.add(netUserTitleLabel);
                          netUserToolBar.add(Box.createHorizontalGlue());
                          // 更新数量显示
                          netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                          netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                          netUserLeftBox.add(netUserCountPanel);
                          netUserSourceComboBox.setVisible(false);
                          netUserPlayAllButton.setVisible(false);
                          netUserRecordTypeComboBox.setVisible(false);
                          netUserCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netUserList.setModel(emptyListModel);
                          netUserListModel.clear();
                          userInfos.forEach(userInfo -> {
                              globalExecutor.execute(() -> updateCollection(userInfo));
                              // 设置图片加载后重绘的事件
                              userInfo.setInvokeLater(() -> {
                                  updateRenderer(netUserList);
                                  updateRenderer(collectionList);
                                  netUserList.repaint();
                                  collectionList.repaint();
                              });
                              netUserListModel.addElement(userInfo);
                          });
                          netUserList.setModel(netUserListModel);
                          netUserBackwardButton.setEnabled(true);
                          netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                          netUserLeftBox.remove(userListBox);
                          if (netUserListModel.isEmpty()) {
                              netUserLeftBox.remove(netUserScrollPane);
                              netUserLeftBox.add(emptyHintPanel);
                          } else {
                              netUserLeftBox.remove(emptyHintPanel);
                              netUserLeftBox.add(netUserScrollPane);
                          }
                          netUserScrollPane.setVValue(0);
                          tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                      } else {
                          clearRequestForArtist();
                          // 搜索专辑歌手
                          currArtistAlbumInfo = albumInfo;
                          CommonResult<NetArtistInfo> result = MusicServerUtil.getArtistInfo(albumInfo.getSource(), albumInfo.getArtistId());
                          List<NetArtistInfo> artistInfos = result.data;
                          netArtistCurrPage = netArtistMaxPage = 1;
                          // 标题
                          netArtistTitleLabel.setText(StringUtil.textToHtml(albumInfo.toSimpleString() + I18n.getText("artistSuffix")));
                          netArtistToolBar.removeAll();
                          netArtistToolBar.add(netArtistBackwardButton);
                          netArtistToolBar.add(Box.createHorizontalGlue());
                          netArtistToolBar.add(netArtistTitleLabel);
                          netArtistToolBar.add(Box.createHorizontalGlue());
                          // 更新数量显示
                          netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
                          netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                          netArtistLeftBox.add(netArtistCountPanel);
                          netArtistSourceComboBox.setVisible(false);
                          netArtistPlayAllButton.setVisible(false);
                          netArtistCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netArtistList.setModel(emptyListModel);
                          netArtistListModel.clear();
                          artistInfos.forEach(artistInfo -> {
                              globalExecutor.execute(() -> updateCollection(artistInfo));
                              // 设置图片加载后重绘的事件
                              artistInfo.setInvokeLater(() -> {
                                  updateRenderer(netArtistList);
                                  updateRenderer(collectionList);
                                  netArtistList.repaint();
                                  collectionList.repaint();
                              });
                              netArtistListModel.addElement(artistInfo);
                          });
                          netArtistList.setModel(netArtistListModel);
                          netArtistBackwardButton.setEnabled(true);
                          netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                          netArtistLeftBox.remove(artistListBox);
                          if (netArtistListModel.isEmpty()) {
                              netArtistLeftBox.remove(netArtistScrollPane);
                              netArtistLeftBox.add(emptyHintPanel);
                          } else {
                              netArtistLeftBox.remove(emptyHintPanel);
                              netArtistLeftBox.add(netArtistScrollPane);
                          }
                          netArtistScrollPane.setVValue(0);
                          tabbedPane.setSelectedIndex(TabIndex.NET_ARTIST);
                      }
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看相似专辑
          netAlbumSimilarMenuItem.addActionListener(e -> {
              NetAlbumInfo albumInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ALBUM) albumInfo = netAlbumList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  albumInfo = (NetAlbumInfo) collectionList.getSelectedValue();
              else albumInfo = (NetAlbumInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForAlbum();
                      // 搜索歌手专辑并显示专辑列表
                      CommonResult<NetAlbumInfo> result = MusicServerUtil.getSimilarAlbums(currAlbumAlbumInfo = albumInfo);
                      List<NetAlbumInfo> albumInfos = result.data;
                      netAlbumCurrPage = netAlbumMaxPage = 1;
                      // 标题
                      netAlbumTitleLabel.setText(StringUtil.textToHtml(albumInfo.getName() + I18n.getText("similarAlbumSuffix")));
                      netAlbumToolBar.removeAll();
                      netAlbumToolBar.add(netAlbumBackwardButton);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      netAlbumToolBar.add(netAlbumTitleLabel);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netAlbumCurrPage, netAlbumMaxPage));
                      netAlbumCountPanel.add(netAlbumCountLabel, netAlbumCountPanel.getComponentIndex(netAlbumCountLabel));
                      netAlbumLeftBox.add(netAlbumCountPanel);
                      netAlbumSourceComboBox.setVisible(false);
                      netAlbumPlayAllButton.setVisible(false);
                      netAlbumCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netAlbumList.setModel(emptyListModel);
                      netAlbumListModel.clear();
                      albumInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netAlbumList);
                              updateRenderer(collectionList);
                              netAlbumList.repaint();
                              collectionList.repaint();
                          });
                          netAlbumListModel.addElement(info);
                      });
                      netAlbumList.setModel(netAlbumListModel);
                      netAlbumBackwardButton.setEnabled(true);
                      netAlbumLeftBox.remove(netAlbumKeywordsPanelScrollPane);
                      if (netAlbumListModel.isEmpty()) {
                          netAlbumLeftBox.remove(netAlbumScrollPane);
                          netAlbumLeftBox.add(emptyHintPanel);
                      } else {
                          netAlbumLeftBox.remove(emptyHintPanel);
                          netAlbumLeftBox.add(netAlbumScrollPane);
                      }
                      netAlbumScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_ALBUM);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看专辑照片
          netAlbumPhotosMenuItem.addActionListener(e -> {
              NetAlbumInfo albumInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ALBUM) albumInfo = netAlbumList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  albumInfo = (NetAlbumInfo) collectionList.getSelectedValue();
              else albumInfo = (NetAlbumInfo) itemRecommendList.getSelectedValue();
              try {
  //                CommonResult<String> results = MusicServerUtils.getArtistImgUrls(netArtistInfo, 1);
  //                List<String> imgUrls = results.data;
  //                Integer total = results.total;
                  imageViewDialog = new ImageViewDialog(THIS, limit) {
                      @Override
                      public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                          return MusicServerUtil.getAlbumImgUrls(albumInfo, pn, limit, cursor);
                      }
  
                      @Override
                      public void requestFailed() {
                          new TipDialog(THIS, NO_IMG_MSG).showDialog();
                      }
                  };
                  imageViewDialog.showDialog();
                  imageViewDialog = null;
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          });
          // 复制名称
          netAlbumCopyNameMenuItem.addActionListener(e -> {
              NetResource resource;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ALBUM) resource = netAlbumList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL) resource = collectionList.getSelectedValue();
              else resource = itemRecommendList.getSelectedValue();
              copyToClipboard(((NetAlbumInfo) resource).toSimpleString());
          });
          // 专辑列表右键菜单项
          netAlbumPopupMenu.add(netAlbumOpenMenuItem);
          netAlbumPopupMenu.add(netAlbumPlayAllMenuItem);
          netAlbumPopupMenu.add(netAlbumCollectMenuItem);
          netAlbumPopupMenu.add(netAlbumCommentMenuItem);
          netAlbumPopupMenu.add(netAlbumArtistMenuItem);
          netAlbumPopupMenu.add(netAlbumSimilarMenuItem);
          netAlbumPopupMenu.add(netAlbumPhotosMenuItem);
          netAlbumPopupMenu.add(netAlbumCopyNameMenuItem);
  
          // 搜索历史面板
          // 清空搜索历史按钮
          netAlbumClearHistorySearchButton.addActionListener(e -> netAlbumHistorySearchInnerPanel2.removeAll());
          netAlbumClearHistorySearchButton.setPreferredSize(new Dimension(30, 30));
          netAlbumClearHistorySearchButton.setToolTipText(CLEAR_HISTORY_SEARCH_TIP);
          netAlbumClearHistorySearchButton.addMouseListener(new ButtonMouseListener(netAlbumClearHistorySearchButton, THIS));
  
          netAlbumHistorySearchInnerPanel1.setVisible(false);
          netAlbumHistorySearchInnerPanel1.add(netAlbumHistorySearchLabel);
          netAlbumHistorySearchInnerPanel1.add(netAlbumClearHistorySearchButton);
          netAlbumHistorySearchInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netAlbumHistorySearchInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netAlbumHistorySearchInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netAlbumHistorySearchInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netAlbumHistorySearchInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netAlbumHistorySearchInnerPanel2.addContainerListener(new ContainerAdapter() {
              @Override
              public void componentAdded(ContainerEvent e) {
                  netAlbumHistorySearchInnerPanel1.setVisible(true);
              }
  
              @Override
              public void componentRemoved(ContainerEvent e) {
                  if (netAlbumHistorySearchInnerPanel2.getComponentCount() == 0)
                      netAlbumHistorySearchInnerPanel1.setVisible(false);
              }
          });
          netAlbumHistorySearchPanel.setLayout(new BoxLayout(netAlbumHistorySearchPanel, BoxLayout.Y_AXIS));
          netAlbumHistorySearchPanel.add(netAlbumHistorySearchInnerPanel1);
          netAlbumHistorySearchPanel.add(netAlbumHistorySearchInnerPanel2);
  
          // 关键词面板
          netAlbumKeywordsPanel.setLayout(new BoxLayout(netAlbumKeywordsPanel, BoxLayout.Y_AXIS));
          netAlbumKeywordsPanel.add(netAlbumHistorySearchPanel);
  
          // 同时限制面板与滚动条的大小才能保证按钮不超出窗口！！！
          Dimension d = new Dimension(900, 500);
          netAlbumKeywordsPanel.setPreferredSize(d);
          netAlbumKeywordsPanelScrollPane.setPreferredSize(d);
  
          // 在线专辑最佳大小
          netAlbumScrollPane.setPreferredSize(new Dimension(200, 600));
          netAlbumLeftBox.add(netAlbumScrollPane);
      }
  
      // 初始化歌手工具栏
      private void initNetArtistToolBar() {
          netArtistSearchTextField.addFocusListener(new TextFieldHintListener(netArtistSearchTextField, "歌手", currUIStyle.getForeColor()));
          netArtistSearchTextField.getDocument().addDocumentListener(new DocumentListener() {
              @Override
              public void insertUpdate(DocumentEvent e) {
                  if (netArtistSearchTextField.isOccupied()) {
                      netArtistClearInputButton.setVisible(true);
                  }
              }
  
              @Override
              public void removeUpdate(DocumentEvent e) {
                  if (netArtistSearchTextField.getText().isEmpty()) netArtistClearInputButton.setVisible(false);
              }
  
              @Override
              public void changedUpdate(DocumentEvent e) {
  
              }
          });
          // 只能输入数字
          netArtistPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 后退按钮事件
          netArtistBackwardButton.addActionListener(e -> {
              int lastComponentIndex = netArtistLeftBox.getComponentCount() - 1;
              Component c = netArtistLeftBox.getComponent(lastComponentIndex);
              if (c != netArtistScrollPane && c != emptyHintPanel) {
                  netMusicListForArtistModel.clear();
                  // 从在线音乐列表切换回歌手列表
                  netArtistPlayAllButton.setVisible(false);
                  netArtistLeftBox.remove(artistListBox);
                  netArtistLeftBox.add(netArtistCountPanel);
                  netArtistLeftBox.add(netArtistScrollPane);
                  // 添加回工具栏的搜索
                  netArtistToolBar.remove(1);
                  netArtistToolBar.add(netArtistSearchTextField);
                  netArtistToolBar.add(netArtistClearInputButton);
                  netArtistToolBar.add(netArtistSearchButton);
                  // 添加回工具栏的搜索/标题
                  netArtistToolBar.removeAll();
                  netArtistToolBar.add(netArtistBackwardButton);
                  if (currArtistArtistInfo == null
                          && currArtistMusicInfo == null
                          && currArtistAlbumInfo == null
                          && currBuddyArtistInfo == null
                          && currArtistMvInfo == null
                          && currArtistRadioInfo == null
                          && currCVRadioInfo == null) {
                      // 删除标题标签
                      netArtistToolBar.add(netArtistSearchTextField);
                      netArtistToolBar.add(netArtistClearInputButton);
                      netArtistToolBar.add(netArtistSearchButton);
                      netArtistSourceComboBox.setVisible(true);
                  } else {
                      netArtistToolBar.add(Box.createHorizontalGlue());
                      netArtistToolBar.add(netArtistTitleLabel);
                      netArtistToolBar.add(Box.createHorizontalGlue());
                  }
                  // 更新歌手歌曲数量显示
  //                netCurrArtistLabel.setText("");
                  netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
  //                netArtistBackwardButton.setEnabled(false);
              }
              // 后退到关键词面板
              else {
                  // 删除标题标签
                  netArtistToolBar.removeAll();
                  netArtistToolBar.add(netArtistBackwardButton);
                  netArtistToolBar.add(netArtistSearchTextField);
                  netArtistToolBar.add(netArtistClearInputButton);
                  netArtistToolBar.add(netArtistSearchButton);
                  clearRequestForArtist();
  
                  netArtistListModel.clear();
                  netArtistCountPanel.setVisible(false);
                  netArtistLeftBox.remove(emptyHintPanel);
                  netArtistLeftBox.remove(netArtistScrollPane);
                  netArtistLeftBox.add(netArtistKeywordsPanelScrollPane);
                  netArtistBackwardButton.setEnabled(false);
              }
              // 切换后一定要刷新！
              netArtistLeftBox.repaint();
          });
          // 清除输入事件
          netArtistClearInputButton.addActionListener(e -> {
              netArtistSearchTextField.requestFocus();
              netArtistSearchTextField.setText("");
              netArtistBackwardButton.requestFocus();
          });
          // 搜索歌手按钮事件
          netArtistSearchButton.addActionListener(e -> {
              netArtistCurrKeyword = netArtistSearchTextField.getText().trim();
              if (!netArtistCurrKeyword.isEmpty()) {
                  // 关键词加入搜索历史
                  DialogButton b = new DialogButton(netArtistCurrKeyword);
                  b.addActionListener(event -> {
                      netArtistSearchTextField.requestFocus();
                      netArtistSearchTextField.setText(b.getPlainText());
                      netArtistClearInputButton.setVisible(true);
                      netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                      netArtistLeftBox.add(netArtistScrollPane);
                      netArtistSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netArtistHistorySearchInnerPanel2.remove(b);
                          netArtistHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  // 先删除重复的关键词
                  removeKeywordInHistorySearch(netArtistCurrKeyword, HistorySearchType.NET_ARTIST);
                  netArtistHistorySearchInnerPanel2.add(b, 0);
                  // 删除超出上限的最后一个关键词
                  int c = netArtistHistorySearchInnerPanel2.getComponentCount();
                  if (c > maxSearchHistoryCount)
                      netArtistHistorySearchInnerPanel2.remove(c - 1);
                  netArtistHistorySearchPanel.repaint();
  
                  loadingAndRun(() -> {
                      try {
                          // 搜索歌手并显示歌手列表
                          CommonResult<NetArtistInfo> result = MusicServerUtil.searchArtists(
                                  netArtistSourceComboBox.getSelectedIndex(), netArtistCurrKeyword, netArtistCurrPage = 1, limit);
                          List<NetArtistInfo> artistInfos = result.data;
                          Integer total = result.total;
                          netArtistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
                          netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                          netArtistCountPanel.setVisible(true);
                          netArtistSourceComboBox.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netArtistList.setModel(emptyListModel);
                          netArtistListModel.clear();
                          artistInfos.forEach(artistInfo -> {
                              globalExecutor.execute(() -> updateCollection(artistInfo));
                              // 设置图片加载后重绘的事件
                              artistInfo.setInvokeLater(() -> {
                                  updateRenderer(netArtistList);
                                  updateRenderer(collectionList);
                                  netArtistList.repaint();
                                  collectionList.repaint();
                              });
                              netArtistListModel.addElement(artistInfo);
                          });
                          netArtistList.setModel(netArtistListModel);
                          netArtistBackwardButton.setEnabled(true);
                          netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                          if (netArtistListModel.isEmpty()) {
                              netArtistLeftBox.remove(netArtistScrollPane);
                              netArtistLeftBox.add(emptyHintPanel);
                          } else {
                              netArtistLeftBox.remove(emptyHintPanel);
                              netArtistLeftBox.add(netArtistScrollPane);
                          }
                          netArtistScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          });
          // 搜索歌手跳页事件
          Runnable searchArtistGoPageAction = () -> {
              boolean songRequest = currArtistMusicInfo != null, albumRequest = currArtistAlbumInfo != null, artistRequest = currArtistArtistInfo != null,
                      buddyRequest = currBuddyArtistInfo != null, mvRequest = currArtistMvInfo != null, radioRequest = currArtistRadioInfo != null,
                      radioCVRequest = currCVRadioInfo != null;
              if (artistRequest || buddyRequest || radioRequest || radioCVRequest || songRequest ||
                      albumRequest || mvRequest || StringUtil.notEmpty(netArtistCurrKeyword)) {
                  loadingAndRun(() -> {
                      try {
                          // 搜索歌手并显示歌手列表
                          CommonResult<NetArtistInfo> result = artistRequest ? MusicServerUtil.getSimilarArtists(currArtistArtistInfo)
                                  : buddyRequest ? MusicServerUtil.getArtistBuddies(currBuddyArtistInfo, netArtistCurrPage, limit)
                                  : radioRequest ? MusicServerUtil.getRadioArtists(currArtistRadioInfo)
                                  : radioCVRequest ? MusicServerUtil.getRadioArtists(currCVRadioInfo)
                                  : songRequest ? MusicServerUtil.getArtistInfo(currArtistMusicInfo.getSource(), currArtistMusicInfo.getArtistId())
                                  : albumRequest ? MusicServerUtil.getArtistInfo(currArtistAlbumInfo.getSource(), currArtistAlbumInfo.getArtistId())
                                  : mvRequest ? MusicServerUtil.getArtistInfo(currArtistMvInfo.getSource(), currArtistMvInfo.getCreatorId())
                                  : MusicServerUtil.searchArtists(netArtistSourceComboBox.getSelectedIndex(), netArtistCurrKeyword, netArtistCurrPage, limit);
                          List<NetArtistInfo> artistInfos = result.data;
                          Integer total = result.total;
                          netArtistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
                          netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                          netArtistCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netArtistList.setModel(emptyListModel);
                          netArtistListModel.clear();
                          artistInfos.forEach(artistInfo -> {
                              globalExecutor.execute(() -> updateCollection(artistInfo));
                              // 设置图片加载后重绘的事件
                              artistInfo.setInvokeLater(() -> {
                                  updateRenderer(netArtistList);
                                  updateRenderer(collectionList);
                                  netArtistList.repaint();
                                  collectionList.repaint();
                              });
                              netArtistListModel.addElement(artistInfo);
                          });
                          netArtistList.setModel(netArtistListModel);
                          netArtistScrollPane.setVValue(0);
                          if (netArtistListModel.isEmpty()) {
                              netArtistLeftBox.remove(netArtistScrollPane);
                              netArtistLeftBox.add(emptyHintPanel);
                          } else {
                              netArtistLeftBox.remove(emptyHintPanel);
                              netArtistLeftBox.add(netArtistScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 搜索歌手内歌曲并显示在在线歌曲列表
          Runnable searchMusicInArtist = () -> {
              if (!netMusicListForArtistModel.isEmpty()) {
                  loadingAndRun(() -> {
                      try {
                          NetArtistInfo artistInfo = netArtistList.getSelectedValue();
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInArtist(artistInfo, netMusicInArtistCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          Integer total = result.total;
                          netMusicInArtistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netArtistCountLabel.setText(String.format(PAGINATION_MSG, netMusicInArtistCurrPage, netMusicInArtistMaxPage));
                          netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicListForArtistModel.clear();
                          netMusicList.setModel(emptyListModel);
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForArtistModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForArtistModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicListForArtistModel.isEmpty()) {
                              artistListCountBox.remove(netMusicScrollPane);
                              artistListCountBox.add(emptyHintPanel);
                          } else {
                              artistListCountBox.remove(emptyHintPanel);
                              artistListCountBox.add(netMusicScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 播放全部
          netArtistPlayAllButton.addActionListener(e -> netArtistPlayAllMenuItem.doClick());
          // 刷新按钮事件
          netArtistRefreshButton.addActionListener(e -> {
              Component lc = netArtistLeftBox.getComponent(netArtistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌手列表，刷新歌手
              if (lc == netArtistScrollPane || lc == emptyHintPanel) {
                  searchArtistGoPageAction.run();
              }
              // 当前显示的是某歌手的歌曲，刷新歌曲
              else {
                  searchMusicInArtist.run();
              }
          });
          // 第一页按钮事件
          netArtistStartPageButton.addActionListener(e -> {
              Component lc = netArtistLeftBox.getComponent(netArtistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌手列表，跳到第一页歌手
              if (lc == netArtistScrollPane || lc == emptyHintPanel) {
                  if (netArtistCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netArtistCurrPage = 1;
                  searchArtistGoPageAction.run();
              }
              // 当前显示的是某歌手的歌曲，跳到第一页歌曲
              else {
                  if (netMusicInArtistCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInArtistCurrPage = 1;
                  searchMusicInArtist.run();
              }
          });
          // 上一页按钮事件
          netArtistLastPageButton.addActionListener(e -> {
              Component lc = netArtistLeftBox.getComponent(netArtistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌手列表，跳到上一页歌手
              if (lc == netArtistScrollPane || lc == emptyHintPanel) {
                  if (netArtistCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netArtistCurrPage--;
                  searchArtistGoPageAction.run();
              }
              // 当前显示的是某歌手的歌曲，跳到上一页歌曲
              else {
                  if (netMusicInArtistCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInArtistCurrPage--;
                  searchMusicInArtist.run();
              }
          });
          // 下一页按钮事件
          netArtistNextPageButton.addActionListener(e -> {
              Component lc = netArtistLeftBox.getComponent(netArtistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌手列表，跳到下一页歌手
              if (lc == netArtistScrollPane || lc == emptyHintPanel) {
                  if (netArtistCurrPage >= netArtistMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netArtistCurrPage++;
                  searchArtistGoPageAction.run();
              }
              // 当前显示的是某歌手的歌曲，跳到下一页歌曲
              else {
                  if (netMusicInArtistCurrPage >= netMusicInArtistMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInArtistCurrPage++;
                  searchMusicInArtist.run();
              }
          });
          // 最后一页按钮事件
          netArtistEndPageButton.addActionListener(e -> {
              Component lc = netArtistLeftBox.getComponent(netArtistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌手列表，跳到最后一页歌手
              if (lc == netArtistScrollPane || lc == emptyHintPanel) {
                  if (netArtistCurrPage >= netArtistMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netArtistCurrPage = netArtistMaxPage;
                  searchArtistGoPageAction.run();
              }
              // 当前显示的是某歌手的歌曲，跳到最后一页歌曲
              else {
                  if (netMusicInArtistCurrPage >= netMusicInArtistMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInArtistCurrPage = netMusicInArtistMaxPage;
                  searchMusicInArtist.run();
              }
          });
          // 跳页按钮事件
          netArtistGoButton.addActionListener(e -> {
              Component lc = netArtistLeftBox.getComponent(netArtistLeftBox.getComponentCount() - 1);
              // 当前显示的是歌手列表，跳页歌手
              if (lc == netArtistScrollPane || lc == emptyHintPanel) {
                  String text = netArtistPageTextField.getText();
                  netArtistPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netArtistMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netArtistCurrPage = destPage;
                  searchArtistGoPageAction.run();
              }
              // 当前显示的是某歌手的歌曲，跳页歌曲
              else {
                  String text = netArtistPageTextField.getText();
                  netArtistPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netMusicInArtistMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInArtistCurrPage = destPage;
                  searchMusicInArtist.run();
              }
          });
          // 按钮被禁止的图标
          netArtistBackwardButton.setEnabled(false);
          // 按钮初始不可见
          netArtistClearInputButton.setVisible(false);
          netArtistPlayAllButton.setVisible(false);
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netArtistRefreshButton.setPreferredSize(dimension);
          netArtistStartPageButton.setPreferredSize(dimension);
          netArtistLastPageButton.setPreferredSize(dimension);
          netArtistGoButton.setPreferredSize(dimension);
          netArtistNextPageButton.setPreferredSize(dimension);
          netArtistEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netArtistBackwardButton.addMouseListener(new ButtonMouseListener(netArtistBackwardButton, THIS));
          netArtistClearInputButton.addMouseListener(new ButtonMouseListener(netArtistClearInputButton, THIS));
          netArtistSearchButton.addMouseListener(new ButtonMouseListener(netArtistSearchButton, THIS));
          netArtistPlayAllButton.addMouseListener(new ButtonMouseListener(netArtistPlayAllButton, THIS));
          netArtistRefreshButton.addMouseListener(new ButtonMouseListener(netArtistRefreshButton, THIS));
          netArtistStartPageButton.addMouseListener(new ButtonMouseListener(netArtistStartPageButton, THIS));
          netArtistLastPageButton.addMouseListener(new ButtonMouseListener(netArtistLastPageButton, THIS));
          netArtistGoButton.addMouseListener(new ButtonMouseListener(netArtistGoButton, THIS));
          netArtistNextPageButton.addMouseListener(new ButtonMouseListener(netArtistNextPageButton, THIS));
          netArtistEndPageButton.addMouseListener(new ButtonMouseListener(netArtistEndPageButton, THIS));
          // 帮助提示
          netArtistBackwardButton.setToolTipText(BACKWARD_TIP);
          netArtistClearInputButton.setToolTipText(CLEAR_INPUT_TIP);
          netArtistSearchButton.setToolTipText(SEARCH_TIP);
          netArtistPlayAllButton.setToolTipText(PLAY_ALL_TIP);
          netArtistRefreshButton.setToolTipText(REFRESH_TIP);
          netArtistStartPageButton.setToolTipText(START_PAGE_TIP);
          netArtistLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netArtistGoButton.setToolTipText(GO_TIP);
          netArtistNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netArtistEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netArtistPlayAllButton.setIconTextGap(5);
  
          netArtistToolBar.add(netArtistBackwardButton);
          netArtistToolBar.add(netArtistSearchTextField);
          netArtistToolBar.add(netArtistClearInputButton);
          netArtistToolBar.add(netArtistSearchButton);
          netArtistLeftBox.add(netArtistToolBar);
  
          for (String name : NetMusicSource.NAMES) netArtistSourceComboBox.addItem(name);
          netArtistSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netArtistSearchButton.doClick();
          });
  
          // 歌手数量和当前歌曲标签
          netArtistCountPanel.add(netArtistSourceComboBox);
          netArtistCountPanel.add(netArtistPlayAllButton);
          netArtistCountPanel.add(netArtistStartPageButton);
          netArtistCountPanel.add(netArtistLastPageButton);
          netArtistCountPanel.add(netArtistCountLabel);
          netArtistCountPanel.add(netArtistNextPageButton);
          netArtistCountPanel.add(netArtistEndPageButton);
          netArtistCountPanel.add(netArtistRefreshButton);
          netArtistCountPanel.add(netArtistPageTextField);
          netArtistCountPanel.add(netArtistGoButton);
          netArtistCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netArtistCountPanel.setVisible(false);
          netArtistLeftBox.add(netArtistCountPanel);
      }
  
      // 初始化在线歌手列表
      private void initNetArtistList() {
          netArtistList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          netArtistList.setModel(netArtistListModel);
          netArtistList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netArtistList.locationToIndex(e.getPoint());
                  Rectangle bounds = netArtistList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetArtistListRenderer renderer = (NetArtistListRenderer) netArtistList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netArtistList.repaint();
              }
          });
          netArtistList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetArtistListRenderer renderer = (NetArtistListRenderer) netArtistList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netArtistList.repaint();
              }
          });
          // 打开歌手
          Runnable openArtistAction = () -> {
              NetArtistInfo artistInfo = netArtistList.getSelectedValue();
              if (artistInfo == null) return;
              loadingAndRun(() -> {
                  List<Future<?>> taskList = new LinkedList<>();
  
                  // 检查收藏按钮
                  checkDescriptionCollectionStatus(artistDescriptionCollectionButton, artistInfo);
  
                  // 加载封面图片和描述
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                      artistCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                      artistCoverAndNameLabel.setText(LOADING_MSG);
                      artistTagLabel.setText(LOADING_MSG);
                      artistDescriptionLabel.setText(LOADING_MSG);
                      GlobalExecutors.requestExecutor.execute(() -> {
                          try {
                              MusicServerUtil.fillArtistInfo(artistInfo);
                              updateRenderer(netArtistList);
                              artistCoverAndNameLabel.setText(StringUtil.textToHtml(artistInfo.getName()));
                              if (artistInfo.getTag() != null)
                                  artistTagLabel.setText(StringUtil.textToHtml(artistInfo.getTag().isEmpty() ? "" : artistInfo.getTag()));
                              if (artistInfo.getDescription() != null)
                                  artistDescriptionLabel.setText(StringUtil.textToHtml(artistInfo.getDescription()));
                              if (artistInfo.hasCoverImg()) {
                                  artistCoverAndNameLabel.setIcon(new ImageIcon(
                                          ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(artistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                  ));
                              } else {
                                  artistInfo.setInvokeLater(() -> {
                                      artistCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(artistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                      netArtistList.repaint();
                                  });
                              }
                          } catch (HttpException e) {
                              artistCoverAndNameLabel.setText(LOAD_FAILED);
                              artistTagLabel.setText(LOAD_FAILED);
                              artistDescriptionLabel.setText(LOAD_FAILED);
                          }
                      });
                  }));
  
  
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      try {
                          // 得到歌手的音乐信息
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInArtist(artistInfo, netMusicInArtistCurrPage = 1, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netMusicInArtistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新歌手歌曲数量显示
  //                            netCurrArtistLabel.setText(StringUtils.textToHtml(artistInfo.getName()));
                          netArtistCountLabel.setText(String.format(PAGINATION_MSG, netMusicInArtistCurrPage, netMusicInArtistMaxPage));
                          artistListCountBox.add(netArtistCountPanel);
                          artistListBox.add(artistListCountBox);
                          // 歌手列表切换到在线音乐列表
                          netMusicList.setModel(netMusicListForArtistModel);
                          netArtistLeftBox.remove(netArtistScrollPane);
                          netArtistLeftBox.add(artistListBox);
                          // 删除工具栏的搜索
                          netArtistToolBar.remove(netArtistSearchTextField);
                          netArtistToolBar.remove(netArtistClearInputButton);
                          netArtistToolBar.remove(netArtistSearchButton);
                          netArtistToolBar.remove(netArtistTitleLabel);
                          netArtistToolBar.add(Box.createHorizontalGlue());
                          // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForArtistModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForArtistModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForArtistModel);
                          artistDescriptionScrollPane.setVValue(0);
                          netMusicScrollPane.setVValue(0);
                          netArtistBackwardButton.setEnabled(true);
                          netArtistSourceComboBox.setVisible(false);
                          netArtistPlayAllButton.setVisible(true);
                          if (netMusicListForArtistModel.isEmpty()) {
                              artistListCountBox.remove(netMusicScrollPane);
                              artistListCountBox.add(emptyHintPanel);
                          } else {
                              artistListCountBox.remove(emptyHintPanel);
                              artistListCountBox.add(netMusicScrollPane);
                          }
                          // 切换后一定要刷新！
                          netArtistLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  }));
  
                  taskList.forEach(task -> {
                      try {
                          task.get();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } catch (ExecutionException e) {
                          e.printStackTrace();
                      }
                  });
              });
          };
          netArtistList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      openArtistAction.run();
                  }
              }
          });
          netArtistList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开歌手
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      openArtistAction.run();
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netArtistList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netArtistList.locationToIndex(e.getPoint());
                          if (index == -1) return;
                          if (!netArtistList.isSelectedIndex(index)) netArtistList.setSelectedIndex(index);
                          if (hasBeenCollected(netArtistList.getSelectedValue())) {
                              netArtistCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netArtistCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                          } else {
                              netArtistCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                              netArtistCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                          }
  
                          netArtistPopupMenu.show(netArtistList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 打开歌手
          netArtistOpenMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              if (index == TabIndex.NET_ARTIST) openArtistAction.run();
              else if (index == TabIndex.PERSONAL) openCollectionItemAction.run();
              else openRecommendItemAction.run();
          });
          // 播放全部
          netArtistPlayAllMenuItem.addActionListener(e -> {
              NetArtistInfo artistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) artistInfo = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  artistInfo = (NetArtistInfo) collectionList.getSelectedValue();
              else artistInfo = (NetArtistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInArtist(artistInfo, 1, artistInfo.hasSongNum() ? artistInfo.getSongNum() : 10000);
                      List<NetMusicInfo> musicInfos = result.data;
                      if (musicInfos.isEmpty()) {
                          new TipDialog(THIS, NO_MUSIC_MSG).showDialog();
                          return;
                      }
                      playQueue.setModel(emptyListModel);
                      playQueueModel.clear();
                      musicInfos.forEach(musicInfo -> {
                          globalExecutor.execute(() -> updateCollection(musicInfo));
                          playQueueModel.addElement(musicInfo);
                      });
                      playQueue.setModel(playQueueModel);
                      playQueue.setSelectedIndex(0);
                      playExecutor.execute(() -> playSelected(playQueue, true));
                  } catch (Exception ex) {
                      new TipDialog(THIS, NO_MUSIC_MSG).showDialog();
                  }
              });
          });
          // 收藏歌手
          netArtistCollectMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              CustomList list;
              if (index == TabIndex.NET_ARTIST) list = netArtistList;
              else if (index == TabIndex.PERSONAL) list = collectionList;
              else list = itemRecommendList;
              ListModel model = collectionList.getModel();
              boolean needRefresh = model == artistCollectionModel;
              if (netArtistCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT) && list.isShowing()
                      || list == netArtistList && artistDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == itemRecommendList && recommendItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == collectionList && collectionItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)) {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  // 描述面板取消收藏之后，使用之前保存的对象
                  boolean useOpenResource = values.isEmpty() && !hasBeenCollected(collectionOpenResource);
                  if (useOpenResource) {
                      artistCollectionModel.add(0, collectionOpenResource);
                  } else {
                      for (int i = values.size() - 1; i >= 0; i--) {
                          NetArtistInfo artistInfo = (NetArtistInfo) values.get(i);
                          if (hasBeenCollected(artistInfo)) continue;
                          artistCollectionModel.add(0, artistInfo);
                      }
                  }
                  if (needRefresh) collectionList.setModel(model);
                  if (useOpenResource) collectionList.setSelectedIndex(0);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  values.forEach(o -> {
                      if (list == collectionList || hasBeenCollected((Resource) o)) {
                          artistCollectionModel.removeElement(o);
                      }
                  });
                  if (needRefresh) collectionList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 查看歌手专辑
          netArtistBrowseAlbumMenuItem.addActionListener(e -> {
              NetArtistInfo artistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) artistInfo = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  artistInfo = (NetArtistInfo) collectionList.getSelectedValue();
              else artistInfo = (NetArtistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForAlbum();
                      // 搜索歌手专辑并显示专辑列表
                      CommonResult<NetAlbumInfo> result = MusicServerUtil.getAlbumInfoInArtist(currAlbumArtistInfo = artistInfo, netAlbumCurrPage = 1, limit);
                      List<NetAlbumInfo> albumInfos = result.data;
                      Integer total = result.total;
                      netAlbumMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netAlbumTitleLabel.setText(StringUtil.textToHtml(artistInfo.getName() + I18n.getText("albumSuffix")));
                      netAlbumToolBar.removeAll();
                      netAlbumToolBar.add(netAlbumBackwardButton);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      netAlbumToolBar.add(netAlbumTitleLabel);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netAlbumCurrPage, netAlbumMaxPage));
                      netAlbumCountPanel.add(netAlbumCountLabel, netAlbumCountPanel.getComponentIndex(netAlbumCountLabel));
                      netAlbumLeftBox.add(netAlbumCountPanel);
                      netAlbumSourceComboBox.setVisible(false);
                      netAlbumPlayAllButton.setVisible(false);
                      netAlbumCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netAlbumList.setModel(emptyListModel);
                      netAlbumListModel.clear();
                      albumInfos.forEach(albumInfo -> {
                          globalExecutor.execute(() -> updateCollection(albumInfo));
                          // 设置图片加载后重绘的事件
                          albumInfo.setInvokeLater(() -> {
                              updateRenderer(netAlbumList);
                              updateRenderer(collectionList);
                              netAlbumList.repaint();
                              collectionList.repaint();
                          });
                          netAlbumListModel.addElement(albumInfo);
                      });
                      netAlbumList.setModel(netAlbumListModel);
                      netAlbumBackwardButton.setEnabled(true);
                      netAlbumLeftBox.remove(netAlbumKeywordsPanelScrollPane);
                      if (netAlbumListModel.isEmpty()) {
                          netAlbumLeftBox.remove(netAlbumScrollPane);
                          netAlbumLeftBox.add(emptyHintPanel);
                      } else {
                          netAlbumLeftBox.remove(emptyHintPanel);
                          netAlbumLeftBox.add(netAlbumScrollPane);
                      }
                      netAlbumScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_ALBUM);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看歌手 MV
          netArtistBrowseMvMenuItem.addActionListener(e -> {
              NetArtistInfo artistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) artistInfo = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  artistInfo = (NetArtistInfo) collectionList.getSelectedValue();
              else artistInfo = (NetArtistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      // 网易云的 total 依赖于 mvNum 属性，需要先补全信息
                      if (!artistInfo.hasMvNum()) MusicServerUtil.fillArtistInfo(artistInfo);
                      clearRequestForMv();
                      // 搜索歌手 MV 并显示 MV 列表
                      CommonResult<NetMvInfo> result = MusicServerUtil.getMvInfoInArtist(currMvArtistInfo = artistInfo, netMvCurrPage = 1, limit);
                      List<NetMvInfo> mvInfos = result.data;
                      Integer total = result.total;
                      netMvMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netMvTitleLabel.setText(StringUtil.textToHtml(artistInfo.getName() + I18n.getText("mvSuffix")));
                      netMvToolBar.removeAll();
                      netMvToolBar.add(netMvBackwardButton);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      netMvToolBar.add(netMvTitleLabel);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netMvCountLabel.setText(String.format(PAGINATION_MSG, netMvCurrPage, netMvMaxPage));
                      netMvCountPanel.setVisible(true);
                      netMvSourceComboBox.setVisible(false);
                      netMvSortTypeComboBox.setVisible(false);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netMvList.setModel(emptyListModel);
                      netMvListModel.clear();
                      mvInfos.forEach(mvInfo -> {
                          globalExecutor.execute(() -> updateCollection(mvInfo));
                          // 设置图片加载后重绘的事件
                          mvInfo.setInvokeLater(() -> {
                              updateRenderer(netMvList);
                              updateRenderer(collectionList);
                              netMvList.repaint();
                              collectionList.repaint();
                          });
                          netMvListModel.addElement(mvInfo);
                      });
                      netMvList.setModel(netMvListModel);
                      netMvBackwardButton.setEnabled(true);
                      netMvLeftBox.remove(netMvKeywordsPanelScrollPane);
                      if (netMvListModel.isEmpty()) {
                          netMvLeftBox.remove(netMvScrollPane);
                          netMvLeftBox.add(emptyHintPanel);
                      } else {
                          netMvLeftBox.remove(emptyHintPanel);
                          netMvLeftBox.add(netMvScrollPane);
                      }
                      netMvScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_MV);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看相似歌手
          netArtistSimilarArtistMenuItem.addActionListener(e -> {
              NetArtistInfo artistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) artistInfo = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  artistInfo = (NetArtistInfo) collectionList.getSelectedValue();
              else artistInfo = (NetArtistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForArtist();
                      // 搜索相似歌手
                      CommonResult<NetArtistInfo> result = MusicServerUtil.getSimilarArtists(currArtistArtistInfo = artistInfo);
                      List<NetArtistInfo> artistInfos = result.data;
                      Integer total = result.total;
                      netArtistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netArtistTitleLabel.setText(StringUtil.textToHtml(artistInfo.getName() + I18n.getText("similarArtistSuffix")));
                      netArtistToolBar.removeAll();
                      netArtistToolBar.add(netArtistBackwardButton);
                      netArtistToolBar.add(Box.createHorizontalGlue());
                      netArtistToolBar.add(netArtistTitleLabel);
                      netArtistToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
                      netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                      netArtistLeftBox.add(netArtistCountPanel);
                      netArtistSourceComboBox.setVisible(false);
                      netArtistPlayAllButton.setVisible(false);
                      netArtistCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netArtistList.setModel(emptyListModel);
                      netArtistListModel.clear();
                      artistInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netArtistList);
                              updateRenderer(collectionList);
                              netArtistList.repaint();
                              collectionList.repaint();
                          });
                          netArtistListModel.addElement(info);
                      });
                      netArtistList.setModel(netArtistListModel);
                      netArtistBackwardButton.setEnabled(true);
                      netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                      if (netArtistListModel.isEmpty()) {
                          netArtistLeftBox.remove(netArtistScrollPane);
                          netArtistLeftBox.add(emptyHintPanel);
                      } else {
                          netArtistLeftBox.remove(emptyHintPanel);
                          netArtistLeftBox.add(netArtistScrollPane);
                      }
                      netArtistScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_ARTIST);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看歌手粉丝
          netArtistFansMenuItem.addActionListener(e -> {
              NetArtistInfo artistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) artistInfo = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  artistInfo = (NetArtistInfo) collectionList.getSelectedValue();
              else artistInfo = (NetArtistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForUser();
                      // 获取歌手粉丝
                      currUserArtistInfo = artistInfo;
                      CommonResult<NetUserInfo> result = MusicServerUtil.getArtistFans(artistInfo, netUserCurrPage = 1, limit);
                      List<NetUserInfo> userInfos = result.data;
                      Integer total = result.total;
                      netUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netUserTitleLabel.setText(StringUtil.textToHtml(artistInfo.getName() + I18n.getText("fanSuffix")));
                      netUserToolBar.removeAll();
                      netUserToolBar.add(netUserBackwardButton);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      netUserLeftBox.add(netUserCountPanel);
                      netUserSourceComboBox.setVisible(false);
                      netUserPlayAllButton.setVisible(false);
                      netUserRecordTypeComboBox.setVisible(false);
                      netUserCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netUserList.setModel(emptyListModel);
                      netUserListModel.clear();
                      userInfos.forEach(userInfo -> {
                          globalExecutor.execute(() -> updateCollection(userInfo));
                          // 设置图片加载后重绘的事件
                          userInfo.setInvokeLater(() -> {
                              updateRenderer(netUserList);
                              updateRenderer(collectionList);
                              netUserList.repaint();
                              collectionList.repaint();
                          });
                          netUserListModel.addElement(userInfo);
                      });
                      netUserList.setModel(netUserListModel);
                      netUserBackwardButton.setEnabled(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      if (netUserListModel.isEmpty()) {
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(emptyHintPanel);
                      } else {
                          netUserLeftBox.remove(emptyHintPanel);
                          netUserLeftBox.add(netUserScrollPane);
                      }
                      netUserScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看歌手合作人
          netArtistBuddyMenuItem.addActionListener(e -> {
              NetArtistInfo artistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) artistInfo = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  artistInfo = (NetArtistInfo) collectionList.getSelectedValue();
              else artistInfo = (NetArtistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForArtist();
                      // 搜索歌手合作人
                      CommonResult<NetArtistInfo> result = MusicServerUtil.getArtistBuddies(currBuddyArtistInfo = artistInfo, netArtistCurrPage = 1, limit);
                      List<NetArtistInfo> artistInfos = result.data;
                      Integer total = result.total;
                      netArtistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netArtistTitleLabel.setText(StringUtil.textToHtml(artistInfo.getName() + I18n.getText("buddySuffix")));
                      netArtistToolBar.removeAll();
                      netArtistToolBar.add(netArtistBackwardButton);
                      netArtistToolBar.add(Box.createHorizontalGlue());
                      netArtistToolBar.add(netArtistTitleLabel);
                      netArtistToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
                      netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                      netArtistLeftBox.add(netArtistCountPanel);
                      netArtistSourceComboBox.setVisible(false);
                      netArtistPlayAllButton.setVisible(false);
                      netArtistCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netArtistList.setModel(emptyListModel);
                      netArtistListModel.clear();
                      artistInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netArtistList);
                              updateRenderer(collectionList);
                              netArtistList.repaint();
                              collectionList.repaint();
                          });
                          netArtistListModel.addElement(info);
                      });
                      netArtistList.setModel(netArtistListModel);
                      netArtistBackwardButton.setEnabled(true);
                      netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                      if (netArtistListModel.isEmpty()) {
                          netArtistLeftBox.remove(netArtistScrollPane);
                          netArtistLeftBox.add(emptyHintPanel);
                      } else {
                          netArtistLeftBox.remove(emptyHintPanel);
                          netArtistLeftBox.add(netArtistScrollPane);
                      }
                      netArtistScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_ARTIST);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看歌手电台
          netArtistRadiosMenuItem.addActionListener(e -> {
              NetArtistInfo artistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) artistInfo = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  artistInfo = (NetArtistInfo) collectionList.getSelectedValue();
              else artistInfo = (NetArtistInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForRadio();
                      // 搜索用户电台
                      CommonResult<NetRadioInfo> result = MusicServerUtil.getArtistRadios(currRadioArtistInfo = artistInfo, netRadioCurrPage = 1, limit);
                      List<NetRadioInfo> radioInfos = result.data;
                      int total = result.total;
                      netRadioMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netRadioTitleLabel.setText(StringUtil.textToHtml(artistInfo.getName() + I18n.getText("radioSuffix")));
                      netRadioToolBar.removeAll();
                      netRadioToolBar.add(netRadioBackwardButton);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      netRadioToolBar.add(netRadioTitleLabel);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netRadioCountLabel.setText(String.format(PAGINATION_MSG, netRadioCurrPage, netRadioMaxPage));
                      netRadioCountPanel.add(netRadioCountLabel, netRadioCountPanel.getComponentIndex(netRadioCountLabel));
                      netRadioLeftBox.add(netRadioCountPanel);
                      netRadioSourceComboBox.setVisible(false);
                      netRadioSortTypeComboBox.setVisible(false);
                      netRadioPlayAllButton.setVisible(false);
                      netRadioCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netRadioList.setModel(emptyListModel);
                      netRadioListModel.clear();
                      radioInfos.forEach(radioInfo -> {
                          globalExecutor.execute(() -> updateCollection(radioInfo));
                          // 设置图片加载后重绘的事件
                          radioInfo.setInvokeLater(() -> {
                              updateRenderer(netRadioList);
                              updateRenderer(collectionList);
                              netRadioList.repaint();
                              collectionList.repaint();
                          });
                          netRadioListModel.addElement(radioInfo);
                      });
                      netRadioList.setModel(netRadioListModel);
                      netRadioBackwardButton.setEnabled(true);
                      netRadioLeftBox.remove(netRadioKeywordsPanelScrollPane);
                      if (netRadioListModel.isEmpty()) {
                          netRadioLeftBox.remove(netRadioScrollPane);
                          netRadioLeftBox.add(emptyHintPanel);
                      } else {
                          netRadioLeftBox.remove(emptyHintPanel);
                          netRadioLeftBox.add(netRadioScrollPane);
                      }
                      netRadioScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_RADIO);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看歌手照片
          netArtistPhotosMenuItem.addActionListener(e -> {
              NetArtistInfo artistInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) artistInfo = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  artistInfo = (NetArtistInfo) collectionList.getSelectedValue();
              else artistInfo = (NetArtistInfo) itemRecommendList.getSelectedValue();
              try {
  //                CommonResult<String> results = MusicServerUtils.getArtistImgUrls(artistInfo, 1);
  //                List<String> imgUrls = results.data;
  //                Integer total = results.total;
                  imageViewDialog = new ImageViewDialog(THIS, 30) {
                      @Override
                      public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                          return MusicServerUtil.getArtistImgUrls(artistInfo, pn);
                      }
  
                      @Override
                      public void requestFailed() {
                          new TipDialog(THIS, NO_IMG_MSG).showDialog();
                      }
                  };
                  imageViewDialog.showDialog();
                  imageViewDialog = null;
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          });
          // 复制名称
          netArtistCopyNameMenuItem.addActionListener(e -> {
              NetResource resource;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_ARTIST) resource = netArtistList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL) resource = collectionList.getSelectedValue();
              else resource = itemRecommendList.getSelectedValue();
              copyToClipboard(((NetArtistInfo) resource).toSimpleString());
          });
          // 歌手右键菜单项
          netArtistPopupMenu.add(netArtistOpenMenuItem);
          netArtistPopupMenu.add(netArtistPlayAllMenuItem);
          netArtistPopupMenu.add(netArtistCollectMenuItem);
          netArtistPopupMenu.add(netArtistBrowseAlbumMenuItem);
          netArtistPopupMenu.add(netArtistBrowseMvMenuItem);
          netArtistPopupMenu.add(netArtistSimilarArtistMenuItem);
          netArtistPopupMenu.add(netArtistFansMenuItem);
          netArtistPopupMenu.add(netArtistBuddyMenuItem);
          netArtistPopupMenu.add(netArtistRadiosMenuItem);
          netArtistPopupMenu.add(netArtistPhotosMenuItem);
          netArtistPopupMenu.add(netArtistCopyNameMenuItem);
  
          // 搜索历史面板
          // 清空搜索历史按钮
          netArtistClearHistorySearchButton.addActionListener(e -> netArtistHistorySearchInnerPanel2.removeAll());
          netArtistClearHistorySearchButton.setPreferredSize(new Dimension(30, 30));
          netArtistClearHistorySearchButton.setToolTipText(CLEAR_HISTORY_SEARCH_TIP);
          netArtistClearHistorySearchButton.addMouseListener(new ButtonMouseListener(netArtistClearHistorySearchButton, THIS));
  
          netArtistHistorySearchInnerPanel1.setVisible(false);
          netArtistHistorySearchInnerPanel1.add(netArtistHistorySearchLabel);
          netArtistHistorySearchInnerPanel1.add(netArtistClearHistorySearchButton);
          netArtistHistorySearchInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netArtistHistorySearchInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netArtistHistorySearchInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netArtistHistorySearchInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netArtistHistorySearchInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netArtistHistorySearchInnerPanel2.addContainerListener(new ContainerAdapter() {
              @Override
              public void componentAdded(ContainerEvent e) {
                  netArtistHistorySearchInnerPanel1.setVisible(true);
              }
  
              @Override
              public void componentRemoved(ContainerEvent e) {
                  if (netArtistHistorySearchInnerPanel2.getComponentCount() == 0)
                      netArtistHistorySearchInnerPanel1.setVisible(false);
              }
          });
          netArtistHistorySearchPanel.setLayout(new BoxLayout(netArtistHistorySearchPanel, BoxLayout.Y_AXIS));
          netArtistHistorySearchPanel.add(netArtistHistorySearchInnerPanel1);
          netArtistHistorySearchPanel.add(netArtistHistorySearchInnerPanel2);
  
          // 关键词面板
          netArtistKeywordsPanel.setLayout(new BoxLayout(netArtistKeywordsPanel, BoxLayout.Y_AXIS));
          netArtistKeywordsPanel.add(netArtistHistorySearchPanel);
  
          // 同时限制面板与滚动条的大小才能保证按钮不超出窗口！！！
          Dimension d = new Dimension(900, 500);
          netArtistKeywordsPanel.setPreferredSize(d);
          netArtistKeywordsPanelScrollPane.setPreferredSize(d);
  
          // 歌手最佳大小
          netArtistScrollPane.setPreferredSize(new Dimension(200, 600));
          netArtistLeftBox.add(netArtistScrollPane);
      }
  
      // 初始化电台工具栏
      private void initNetRadioToolBar() {
          netRadioSearchTextField.addFocusListener(new TextFieldHintListener(netRadioSearchTextField, "电台", currUIStyle.getForeColor()));
          netRadioSearchTextField.getDocument().addDocumentListener(new DocumentListener() {
              @Override
              public void insertUpdate(DocumentEvent e) {
                  if (netRadioSearchTextField.isOccupied()) {
                      netRadioClearInputButton.setVisible(true);
                  }
              }
  
              @Override
              public void removeUpdate(DocumentEvent e) {
                  if (netRadioSearchTextField.getText().isEmpty()) netRadioClearInputButton.setVisible(false);
              }
  
              @Override
              public void changedUpdate(DocumentEvent e) {
  
              }
          });
          // 只能输入数字
          netRadioPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 后退按钮事件
          netRadioBackwardButton.addActionListener(e -> {
              int lastComponentIndex = netRadioLeftBox.getComponentCount() - 1;
              Component c = netRadioLeftBox.getComponent(lastComponentIndex);
              if (c != netRadioScrollPane && c != emptyHintPanel) {
                  netMusicListForRadioModel.clear();
                  // 从在线音乐列表切换回用户列表
                  netRadioPlayAllButton.setVisible(false);
                  netRadioLeftBox.remove(radioListBox);
                  netRadioLeftBox.add(netRadioCountPanel);
                  netRadioLeftBox.add(netRadioScrollPane);
                  // 添加回工具栏的搜索
                  netRadioToolBar.remove(1);
                  netRadioToolBar.add(netRadioSearchTextField);
                  netRadioToolBar.add(netRadioClearInputButton);
                  netRadioToolBar.add(netRadioSearchButton);
                  // 添加回工具栏的搜索/标题
                  netRadioToolBar.removeAll();
                  netRadioToolBar.add(netRadioBackwardButton);
                  if (currRadioUserInfo == null
                          && currRadioMusicInfo == null
                          && currRecRadioMusicInfo == null
                          && currRadioArtistInfo == null
                          && currRadioRadioInfo == null) {
                      // 删除标题标签
                      netRadioToolBar.add(netRadioSearchTextField);
                      netRadioToolBar.add(netRadioClearInputButton);
                      netRadioToolBar.add(netRadioSearchButton);
                      netRadioSourceComboBox.setVisible(true);
                      netRadioSortTypeComboBox.setVisible(false);
                  } else {
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      netRadioToolBar.add(netRadioTitleLabel);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                  }
                  // 更新用户歌曲数量显示
  //                netCurrRadioLabel.setText("");
                  netRadioCountLabel.setText(String.format(PAGINATION_MSG, netRadioCurrPage, netRadioMaxPage));
  //                netRadioBackwardButton.setEnabled(false);
              }
              // 后退到关键词面板
              else {
                  // 删除标题标签
                  netRadioToolBar.removeAll();
                  netRadioToolBar.add(netRadioBackwardButton);
                  netRadioToolBar.add(netRadioSearchTextField);
                  netRadioToolBar.add(netRadioClearInputButton);
                  netRadioToolBar.add(netRadioSearchButton);
                  clearRequestForRadio();
  
                  netRadioListModel.clear();
                  netRadioCountPanel.setVisible(false);
                  netRadioLeftBox.remove(emptyHintPanel);
                  netRadioLeftBox.remove(netRadioScrollPane);
                  netRadioLeftBox.add(netRadioKeywordsPanelScrollPane);
                  netRadioBackwardButton.setEnabled(false);
              }
              // 切换后一定要刷新！
              netRadioLeftBox.repaint();
          });
          // 清除输入事件
          netRadioClearInputButton.addActionListener(e -> {
              netRadioSearchTextField.requestFocus();
              netRadioSearchTextField.setText("");
              netRadioBackwardButton.requestFocus();
          });
          // 搜索电台按钮事件
          netRadioSearchButton.addActionListener(e -> {
              netRadioCurrKeyword = netRadioSearchTextField.getText().trim();
              if (!netRadioCurrKeyword.isEmpty()) {
                  // 关键词加入搜索历史
                  DialogButton b = new DialogButton(netRadioCurrKeyword);
                  b.addActionListener(event -> {
                      netRadioSearchTextField.requestFocus();
                      netRadioSearchTextField.setText(b.getPlainText());
                      netRadioClearInputButton.setVisible(true);
                      netRadioLeftBox.remove(netRadioKeywordsPanelScrollPane);
                      netRadioLeftBox.add(netRadioScrollPane);
                      netRadioSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netRadioHistorySearchInnerPanel2.remove(b);
                          netRadioHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  // 先删除重复的关键词
                  removeKeywordInHistorySearch(netRadioCurrKeyword, HistorySearchType.NET_RADIO);
                  netRadioHistorySearchInnerPanel2.add(b, 0);
                  // 删除超出上限的最后一个关键词
                  int c = netRadioHistorySearchInnerPanel2.getComponentCount();
                  if (c > maxSearchHistoryCount)
                      netRadioHistorySearchInnerPanel2.remove(c - 1);
                  netRadioHistorySearchPanel.repaint();
  
                  loadingAndRun(() -> {
                      try {
                          // 搜索电台并显示电台列表
                          CommonResult<NetRadioInfo> result = MusicServerUtil.searchRadios(
                                  netRadioSourceComboBox.getSelectedIndex(), netRadioCurrKeyword, netRadioCurrPage = 1, limit);
                          List<NetRadioInfo> radioInfos = result.data;
                          Integer total = result.total;
                          netRadioMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netRadioCountLabel.setText(String.format(PAGINATION_MSG, netRadioCurrPage, netRadioMaxPage));
                          netRadioCountPanel.add(netRadioCountLabel, netRadioCountPanel.getComponentIndex(netRadioCountLabel));
                          netRadioCountPanel.setVisible(true);
                          netRadioSourceComboBox.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netRadioList.setModel(emptyListModel);
                          netRadioListModel.clear();
                          radioInfos.forEach(radioInfo -> {
                              globalExecutor.execute(() -> updateCollection(radioInfo));
                              // 设置图片加载后重绘的事件
                              radioInfo.setInvokeLater(() -> {
                                  updateRenderer(netRadioList);
                                  updateRenderer(collectionList);
                                  netRadioList.repaint();
                                  collectionList.repaint();
                              });
                              netRadioListModel.addElement(radioInfo);
                          });
                          netRadioList.setModel(netRadioListModel);
                          netRadioBackwardButton.setEnabled(true);
                          netRadioLeftBox.remove(netRadioKeywordsPanelScrollPane);
                          if (netRadioListModel.isEmpty()) {
                              netRadioLeftBox.remove(netRadioScrollPane);
                              netRadioLeftBox.add(emptyHintPanel);
                          } else {
                              netRadioLeftBox.remove(emptyHintPanel);
                              netRadioLeftBox.add(netRadioScrollPane);
                          }
                          netRadioScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          });
          // 搜索电台跳页事件
          Runnable searchRadioGoPageAction = () -> {
              boolean songRequest = currRadioMusicInfo != null, songRecRequest = currRecRadioMusicInfo != null, userRequest = currRadioUserInfo != null,
                      artistRequest = currRadioArtistInfo != null, radioRequest = currRadioRadioInfo != null;
              if (userRequest || artistRequest || radioRequest || songRequest || songRecRequest || StringUtil.notEmpty(netRadioCurrKeyword)) {
                  loadingAndRun(() -> {
                      try {
                          // 搜索电台并显示电台列表
                          CommonResult<NetRadioInfo> result = userRequest ? MusicServerUtil.getUserRadios(currRadioUserInfo, netRadioCurrPage, limit)
                                  : artistRequest ? MusicServerUtil.getArtistRadios(currRadioArtistInfo, netRadioCurrPage, limit)
                                  : radioRequest ? MusicServerUtil.getSimilarRadios(currRadioRadioInfo)
                                  : songRequest ? MusicServerUtil.getRadioInfo(currRadioMusicInfo.getSource(), currRadioMusicInfo.getAlbumId())
                                  : songRecRequest ? MusicServerUtil.getRecRadios(currRecRadioMusicInfo)
                                  : MusicServerUtil.searchRadios(netRadioSourceComboBox.getSelectedIndex(), netRadioCurrKeyword, netRadioCurrPage, limit);
                          List<NetRadioInfo> radioInfos = result.data;
                          Integer total = result.total;
                          netRadioMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netRadioCountLabel.setText(String.format(PAGINATION_MSG, netRadioCurrPage, netRadioMaxPage));
                          netRadioCountPanel.add(netRadioCountLabel, netRadioCountPanel.getComponentIndex(netRadioCountLabel));
                          netRadioCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netRadioList.setModel(emptyListModel);
                          netRadioListModel.clear();
                          radioInfos.forEach(radioInfo -> {
                              globalExecutor.execute(() -> updateCollection(radioInfo));
                              // 设置图片加载后重绘的事件
                              radioInfo.setInvokeLater(() -> {
                                  updateRenderer(netRadioList);
                                  updateRenderer(collectionList);
                                  netRadioList.repaint();
                                  collectionList.repaint();
                              });
                              netRadioListModel.addElement(radioInfo);
                          });
                          netRadioList.setModel(netRadioListModel);
                          netRadioScrollPane.setVValue(0);
                          if (netRadioListModel.isEmpty()) {
                              netRadioLeftBox.remove(netRadioScrollPane);
                              netRadioLeftBox.add(emptyHintPanel);
                          } else {
                              netRadioLeftBox.remove(emptyHintPanel);
                              netRadioLeftBox.add(netRadioScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 搜索电台内歌曲并显示在在线歌曲列表
          Runnable searchMusicInRadio = () -> {
              if (!netMusicListForRadioModel.isEmpty()) {
                  loadingAndRun(() -> {
                      try {
                          NetRadioInfo radioInfo = netRadioList.getSelectedValue();
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRadio(radioInfo, netRadioSortTypeComboBox.getSelectedIndex(), netMusicInRadioCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          Integer total = result.total;
                          netMusicInRadioMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netRadioCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRadioCurrPage, netMusicInRadioMaxPage));
                          netRadioCountPanel.add(netRadioCountLabel, netRadioCountPanel.getComponentIndex(netRadioCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForRadioModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForRadioModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForRadioModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicListForRadioModel.isEmpty()) {
                              radioListCountBox.remove(netMusicScrollPane);
                              radioListCountBox.add(emptyHintPanel);
                          } else {
                              radioListCountBox.remove(emptyHintPanel);
                              radioListCountBox.add(netMusicScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 播放全部
          netRadioPlayAllButton.addActionListener(e -> netRadioPlayAllMenuItem.doClick());
          // 刷新按钮事件
          netRadioRefreshButton.addActionListener(e -> {
              Component lc = netRadioLeftBox.getComponent(netRadioLeftBox.getComponentCount() - 1);
              // 当前显示的是电台列表，刷新电台
              if (lc == netRadioScrollPane || lc == emptyHintPanel) {
                  searchRadioGoPageAction.run();
              }
              // 当前显示的是某电台的歌曲，刷新歌曲
              else {
                  searchMusicInRadio.run();
              }
          });
          // 第一页按钮事件
          netRadioStartPageButton.addActionListener(e -> {
              Component lc = netRadioLeftBox.getComponent(netRadioLeftBox.getComponentCount() - 1);
              // 当前显示的是电台列表，跳到第一页电台
              if (lc == netRadioScrollPane || lc == emptyHintPanel) {
                  if (netRadioCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRadioCurrPage = 1;
                  searchRadioGoPageAction.run();
              }
              // 当前显示的是某电台的歌曲，跳到第一页歌曲
              else {
                  if (netMusicInRadioCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRadioCurrPage = 1;
                  searchMusicInRadio.run();
              }
          });
          // 上一页按钮事件
          netRadioLastPageButton.addActionListener(e -> {
              Component lc = netRadioLeftBox.getComponent(netRadioLeftBox.getComponentCount() - 1);
              // 当前显示的是电台列表，跳到上一页电台
              if (lc == netRadioScrollPane || lc == emptyHintPanel) {
                  if (netRadioCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRadioCurrPage--;
                  searchRadioGoPageAction.run();
              }
              // 当前显示的是某电台的歌曲，跳到上一页歌曲
              else {
                  if (netMusicInRadioCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRadioCurrPage--;
                  searchMusicInRadio.run();
              }
          });
          // 下一页按钮事件
          netRadioNextPageButton.addActionListener(e -> {
              Component lc = netRadioLeftBox.getComponent(netRadioLeftBox.getComponentCount() - 1);
              // 当前显示的是电台列表，跳到下一页电台
              if (lc == netRadioScrollPane || lc == emptyHintPanel) {
                  if (netRadioCurrPage >= netRadioMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRadioCurrPage++;
                  searchRadioGoPageAction.run();
              }
              // 当前显示的是某电台的歌曲，跳到下一页歌曲
              else {
                  if (netMusicInRadioCurrPage >= netMusicInRadioMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRadioCurrPage++;
                  searchMusicInRadio.run();
              }
          });
          // 最后一页按钮事件
          netRadioEndPageButton.addActionListener(e -> {
              Component lc = netRadioLeftBox.getComponent(netRadioLeftBox.getComponentCount() - 1);
              // 当前显示的是电台列表，跳到最后一页电台
              if (lc == netRadioScrollPane || lc == emptyHintPanel) {
                  if (netRadioCurrPage >= netRadioMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRadioCurrPage = netRadioMaxPage;
                  searchRadioGoPageAction.run();
              }
              // 当前显示的是某电台的歌曲，跳到最后一页歌曲
              else {
                  if (netMusicInRadioCurrPage >= netMusicInRadioMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRadioCurrPage = netMusicInRadioMaxPage;
                  searchMusicInRadio.run();
              }
          });
          // 跳页按钮事件
          netRadioGoButton.addActionListener(e -> {
              Component lc = netRadioLeftBox.getComponent(netRadioLeftBox.getComponentCount() - 1);
              // 当前显示的是电台列表，跳页电台
              if (lc == netRadioScrollPane || lc == emptyHintPanel) {
                  String text = netRadioPageTextField.getText();
                  netRadioPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netRadioMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netRadioCurrPage = destPage;
                  searchRadioGoPageAction.run();
              }
              // 当前显示的是某电台的歌曲，跳页歌曲
              else {
                  String text = netRadioPageTextField.getText();
                  netRadioPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netMusicInRadioMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRadioCurrPage = destPage;
                  searchMusicInRadio.run();
              }
          });
          // 按钮被禁止的图标
          netRadioBackwardButton.setEnabled(false);
          // 按钮初始不可见
          netRadioClearInputButton.setVisible(false);
          netRadioSortTypeComboBox.setVisible(false);
          netRadioPlayAllButton.setVisible(false);
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netRadioRefreshButton.setPreferredSize(dimension);
          netRadioStartPageButton.setPreferredSize(dimension);
          netRadioLastPageButton.setPreferredSize(dimension);
          netRadioGoButton.setPreferredSize(dimension);
          netRadioNextPageButton.setPreferredSize(dimension);
          netRadioEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netRadioBackwardButton.addMouseListener(new ButtonMouseListener(netRadioBackwardButton, THIS));
          netRadioClearInputButton.addMouseListener(new ButtonMouseListener(netRadioClearInputButton, THIS));
          netRadioSearchButton.addMouseListener(new ButtonMouseListener(netRadioSearchButton, THIS));
          netRadioPlayAllButton.addMouseListener(new ButtonMouseListener(netRadioPlayAllButton, THIS));
          netRadioRefreshButton.addMouseListener(new ButtonMouseListener(netRadioRefreshButton, THIS));
          netRadioStartPageButton.addMouseListener(new ButtonMouseListener(netRadioStartPageButton, THIS));
          netRadioLastPageButton.addMouseListener(new ButtonMouseListener(netRadioLastPageButton, THIS));
          netRadioGoButton.addMouseListener(new ButtonMouseListener(netRadioGoButton, THIS));
          netRadioNextPageButton.addMouseListener(new ButtonMouseListener(netRadioNextPageButton, THIS));
          netRadioEndPageButton.addMouseListener(new ButtonMouseListener(netRadioEndPageButton, THIS));
          // 帮助提示
          netRadioBackwardButton.setToolTipText(BACKWARD_TIP);
          netRadioClearInputButton.setToolTipText(CLEAR_INPUT_TIP);
          netRadioSearchButton.setToolTipText(SEARCH_TIP);
          netRadioPlayAllButton.setToolTipText(PLAY_ALL_TIP);
          netRadioRefreshButton.setToolTipText(REFRESH_TIP);
          netRadioStartPageButton.setToolTipText(START_PAGE_TIP);
          netRadioLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netRadioGoButton.setToolTipText(GO_TIP);
          netRadioNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netRadioEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netRadioPlayAllButton.setIconTextGap(5);
  
          netRadioToolBar.add(netRadioBackwardButton);
          netRadioToolBar.add(netRadioSearchTextField);
          netRadioToolBar.add(netRadioClearInputButton);
          netRadioToolBar.add(netRadioSearchButton);
          netRadioLeftBox.add(netRadioToolBar);
  
          for (String name : NetMusicSource.NAMES) netRadioSourceComboBox.addItem(name);
          netRadioSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netRadioSearchButton.doClick();
          });
  
          netRadioSortTypeComboBox.addItem(I18n.getText("ascend"));
          netRadioSortTypeComboBox.addItem(I18n.getText("descend"));
          // 电台排序类型切换事件
          netRadioSortTypeComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netMusicInRadioCurrPage = 1;
              netRadioRefreshButton.doClick();
          });
  
          // 电台数量和当前歌曲标签
          netRadioCountPanel.add(netRadioSourceComboBox);
          netRadioCountPanel.add(netRadioSortTypeComboBox);
          netRadioCountPanel.add(netRadioPlayAllButton);
          netRadioCountPanel.add(netRadioStartPageButton);
          netRadioCountPanel.add(netRadioLastPageButton);
          netRadioCountPanel.add(netRadioCountLabel);
          netRadioCountPanel.add(netRadioNextPageButton);
          netRadioCountPanel.add(netRadioEndPageButton);
          netRadioCountPanel.add(netRadioRefreshButton);
          netRadioCountPanel.add(netRadioPageTextField);
          netRadioCountPanel.add(netRadioGoButton);
          netRadioCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netRadioCountPanel.setVisible(false);
          netRadioLeftBox.add(netRadioCountPanel);
      }
  
      // 初始化在线电台列表
      private void initNetRadioList() {
          netRadioList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          netRadioList.setModel(netRadioListModel);
          netRadioList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netRadioList.locationToIndex(e.getPoint());
                  Rectangle bounds = netRadioList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetRadioListRenderer renderer = (NetRadioListRenderer) netRadioList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netRadioList.repaint();
              }
          });
          netRadioList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetRadioListRenderer renderer = (NetRadioListRenderer) netRadioList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netRadioList.repaint();
              }
          });
          // 打开电台
          Runnable openRadioAction = () -> {
              NetRadioInfo radioInfo = netRadioList.getSelectedValue();
              if (radioInfo == null) return;
              loadingAndRun(() -> {
                  List<Future<?>> taskList = new LinkedList<>();
  
                  // 检查收藏按钮
                  checkDescriptionCollectionStatus(radioDescriptionCollectionButton, radioInfo);
  
                  // 加载封面图片和描述
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                      radioCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                      radioCoverAndNameLabel.setText(LOADING_MSG);
                      radioTagLabel.setText(LOADING_MSG);
                      radioDescriptionLabel.setText(LOADING_MSG);
                      GlobalExecutors.requestExecutor.execute(() -> {
                          try {
                              MusicServerUtil.fillRadioInfo(radioInfo);
                              updateRenderer(netRadioList);
                              radioCoverAndNameLabel.setText(StringUtil.textToHtml(radioInfo.getName()));
                              if (radioInfo.getTag() != null)
                                  radioTagLabel.setText(StringUtil.textToHtml(radioInfo.getTag().isEmpty() ? "" : "标签：" + radioInfo.getTag()));
                              if (radioInfo.getDescription() != null)
                                  radioDescriptionLabel.setText(StringUtil.textToHtml(radioInfo.getDescription()));
                              if (radioInfo.hasCoverImg()) {
                                  radioCoverAndNameLabel.setIcon(new ImageIcon(
                                          ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(radioInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                  ));
                              } else {
                                  radioInfo.setInvokeLater(() -> {
                                      radioCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(radioInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                      netRadioList.repaint();
                                  });
                              }
                          } catch (HttpException e) {
                              radioCoverAndNameLabel.setText(LOAD_FAILED);
                              radioTagLabel.setText(LOAD_FAILED);
                              radioDescriptionLabel.setText(LOAD_FAILED);
                          }
                      });
                  }));
  
  
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      try {
                          // 得到电台的音乐信息
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRadio(radioInfo, netRadioSortTypeComboBox.getSelectedIndex(), netMusicInRadioCurrPage = 1, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netMusicInRadioMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新电台歌曲数量显示
  //                            netCurrRadioLabel.setText(StringUtils.textToHtml(radioInfo.getName()));
                          netRadioCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRadioCurrPage, netMusicInRadioMaxPage));
                          radioListCountBox.add(netRadioCountPanel);
                          radioListCountBox.add(netMusicScrollPane);
                          radioListBox.add(radioListCountBox);
                          // 电台列表切换到在线音乐列表
                          netMusicList.setModel(netMusicListForRadioModel);
                          netRadioLeftBox.remove(netRadioScrollPane);
                          netRadioLeftBox.add(radioListBox);
                          // 删除工具栏的搜索
                          netRadioToolBar.remove(netRadioSearchTextField);
                          netRadioToolBar.remove(netRadioClearInputButton);
                          netRadioToolBar.remove(netRadioSearchButton);
                          netRadioToolBar.remove(netRadioTitleLabel);
                          netRadioToolBar.add(Box.createHorizontalGlue());
                          // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForRadioModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForRadioModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForRadioModel);
                          radioDescriptionScrollPane.setVValue(0);
                          netMusicScrollPane.setVValue(0);
                          netRadioBackwardButton.setEnabled(true);
                          netRadioSourceComboBox.setVisible(false);
                          netRadioSortTypeComboBox.setVisible(radioInfo.fromXM());
                          netRadioPlayAllButton.setVisible(true);
                          if (netMusicListForRadioModel.isEmpty()) {
                              radioListCountBox.remove(netMusicScrollPane);
                              radioListCountBox.add(emptyHintPanel);
                          } else {
                              radioListCountBox.remove(emptyHintPanel);
                              radioListCountBox.add(netMusicScrollPane);
                          }
                          // 切换后一定要刷新！
                          netRadioLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  }));
  
                  taskList.forEach(task -> {
                      try {
                          task.get();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } catch (ExecutionException e) {
                          e.printStackTrace();
                      }
                  });
              });
          };
          netRadioList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      openRadioAction.run();
                  }
              }
          });
          netRadioList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开电台
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      openRadioAction.run();
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netRadioList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netRadioList.locationToIndex(e.getPoint());
                          if (index == -1) return;
                          if (!netRadioList.isSelectedIndex(index)) netRadioList.setSelectedIndex(index);
                          if (hasBeenCollected(netRadioList.getSelectedValue())) {
                              netRadioCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netRadioCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                          } else {
                              netRadioCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                              netRadioCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                          }
  
                          netRadioPopupMenu.show(netRadioList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 打开电台
          netRadioOpenMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              if (index == TabIndex.NET_RADIO) openRadioAction.run();
              else if (index == TabIndex.PERSONAL) openCollectionItemAction.run();
              else openRecommendItemAction.run();
          });
          // 播放全部
          netRadioPlayAllMenuItem.addActionListener(e -> {
              NetRadioInfo radioInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) radioInfo = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  radioInfo = (NetRadioInfo) collectionList.getSelectedValue();
              else radioInfo = (NetRadioInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRadio(
                          radioInfo, netRadioSortTypeComboBox.getSelectedIndex(), 1, radioInfo.hasTrackCount() ? radioInfo.getTrackCount() : 10000);
                  List<NetMusicInfo> musicInfos = result.data;
                  if (musicInfos.isEmpty()) {
                      new TipDialog(THIS, NO_MUSIC_MSG).showDialog();
                      return;
                  }
                  playQueue.setModel(emptyListModel);
                  playQueueModel.clear();
                  musicInfos.forEach(musicInfo -> {
                      globalExecutor.execute(() -> updateCollection(musicInfo));
                      playQueueModel.addElement(musicInfo);
                  });
                  playQueue.setModel(playQueueModel);
                  playQueue.setSelectedIndex(0);
                  playExecutor.execute(() -> playSelected(playQueue, true));
              });
          });
          // 收藏电台
          netRadioCollectMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              CustomList list;
              if (index == TabIndex.NET_RADIO) list = netRadioList;
              else if (index == TabIndex.PERSONAL) list = collectionList;
              else list = itemRecommendList;
              ListModel model = collectionList.getModel();
              boolean needRefresh = model == radioCollectionModel;
              if (netRadioCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT) && list.isShowing()
                      || list == netRadioList && radioDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == itemRecommendList && recommendItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == collectionList && collectionItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)) {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  // 描述面板取消收藏之后，使用之前保存的对象
                  boolean useOpenResource = values.isEmpty() && !hasBeenCollected(collectionOpenResource);
                  if (useOpenResource) {
                      radioCollectionModel.add(0, collectionOpenResource);
                  } else {
                      for (int i = values.size() - 1; i >= 0; i--) {
                          NetRadioInfo radioInfo = (NetRadioInfo) values.get(i);
                          if (hasBeenCollected(radioInfo)) continue;
                          radioCollectionModel.add(0, radioInfo);
                      }
                  }
                  if (needRefresh) collectionList.setModel(model);
                  if (useOpenResource) collectionList.setSelectedIndex(0);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  values.forEach(o -> {
                      if (list == collectionList || hasBeenCollected((Resource) o)) {
                          radioCollectionModel.removeElement(o);
                      }
                  });
                  if (needRefresh) collectionList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 查看评论
          netRadioCommentMenuItem.addActionListener(e -> {
              NetRadioInfo radioInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) radioInfo = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  radioInfo = (NetRadioInfo) collectionList.getSelectedValue();
              else radioInfo = (NetRadioInfo) itemRecommendList.getSelectedValue();
              getComments(radioInfo, true);
          });
          // 查看主播
          netRadioDjMenuItem.addActionListener(e -> {
              NetRadioInfo radioInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) radioInfo = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  radioInfo = (NetRadioInfo) collectionList.getSelectedValue();
              else radioInfo = (NetRadioInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForUser();
                      // 获取电台主播
                      currUserRadioInfo = radioInfo;
                      CommonResult<NetUserInfo> result = MusicServerUtil.getUserInfo(radioInfo.getSource(), radioInfo.getDjId());
                      List<NetUserInfo> userInfos = result.data;
                      netUserCurrPage = netUserMaxPage = 1;
                      // 标题
                      netUserTitleLabel.setText(StringUtil.textToHtml(radioInfo.getName() + I18n.getText("djSuffix")));
                      netUserToolBar.removeAll();
                      netUserToolBar.add(netUserBackwardButton);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      netUserLeftBox.add(netUserCountPanel);
                      netUserSourceComboBox.setVisible(false);
                      netUserPlayAllButton.setVisible(false);
                      netUserRecordTypeComboBox.setVisible(false);
                      netUserCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netUserList.setModel(emptyListModel);
                      netUserListModel.clear();
                      userInfos.forEach(userInfo -> {
                          globalExecutor.execute(() -> updateCollection(userInfo));
                          // 设置图片加载后重绘的事件
                          userInfo.setInvokeLater(() -> {
                              updateRenderer(netUserList);
                              updateRenderer(collectionList);
                              netUserList.repaint();
                              collectionList.repaint();
                          });
                          netUserListModel.addElement(userInfo);
                      });
                      netUserList.setModel(netUserListModel);
                      netUserBackwardButton.setEnabled(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      if (netUserListModel.isEmpty()) {
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(emptyHintPanel);
                      } else {
                          netUserLeftBox.remove(emptyHintPanel);
                          netUserLeftBox.add(netUserScrollPane);
                      }
                      netUserScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看订阅者
          netRadioSubscriberMenuItem.addActionListener(e -> {
              NetRadioInfo radioInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) radioInfo = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  radioInfo = (NetRadioInfo) collectionList.getSelectedValue();
              else radioInfo = (NetRadioInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForUser();
                      // 获取电台订阅者
                      CommonResult<NetUserInfo> result = MusicServerUtil.getRadioSubscribers(currSubscriberRadioInfo = radioInfo, netUserCurrPage = 1, limit);
                      List<NetUserInfo> userInfos = result.data;
                      Integer total = result.total;
                      netUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netUserTitleLabel.setText(StringUtil.textToHtml(radioInfo.getName() + I18n.getText("subscriberSuffix")));
                      netUserToolBar.removeAll();
                      netUserToolBar.add(netUserBackwardButton);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      netUserLeftBox.add(netUserCountPanel);
                      netUserSourceComboBox.setVisible(false);
                      netUserPlayAllButton.setVisible(false);
                      netUserRecordTypeComboBox.setVisible(false);
                      netUserCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netUserList.setModel(emptyListModel);
                      netUserListModel.clear();
                      userInfos.forEach(userInfo -> {
                          globalExecutor.execute(() -> updateCollection(userInfo));
                          // 设置图片加载后重绘的事件
                          userInfo.setInvokeLater(() -> {
                              updateRenderer(netUserList);
                              updateRenderer(collectionList);
                              netUserList.repaint();
                              collectionList.repaint();
                          });
                          netUserListModel.addElement(userInfo);
                      });
                      netUserList.setModel(netUserListModel);
                      netUserBackwardButton.setEnabled(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      if (netUserListModel.isEmpty()) {
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(emptyHintPanel);
                      } else {
                          netUserLeftBox.remove(emptyHintPanel);
                          netUserLeftBox.add(netUserScrollPane);
                      }
                      netUserScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看相似电台
          netRadioSimilarMenuItem.addActionListener(e -> {
              NetRadioInfo radioInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) radioInfo = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  radioInfo = (NetRadioInfo) collectionList.getSelectedValue();
              else radioInfo = (NetRadioInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForRadio();
                      // 搜索用户电台
                      CommonResult<NetRadioInfo> result = MusicServerUtil.getSimilarRadios(currRadioRadioInfo = radioInfo);
                      List<NetRadioInfo> radioInfos = result.data;
                      int total = result.total;
                      netRadioCurrPage = netRadioMaxPage = 1;
                      // 标题
                      netRadioTitleLabel.setText(StringUtil.textToHtml(radioInfo.getName() + I18n.getText("similarRadioSuffix")));
                      netRadioToolBar.removeAll();
                      netRadioToolBar.add(netRadioBackwardButton);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      netRadioToolBar.add(netRadioTitleLabel);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netRadioCountLabel.setText(String.format(PAGINATION_MSG, netRadioCurrPage, netRadioMaxPage));
                      netRadioCountPanel.add(netRadioCountLabel, netRadioCountPanel.getComponentIndex(netRadioCountLabel));
                      netRadioLeftBox.add(netRadioCountPanel);
                      netRadioSourceComboBox.setVisible(false);
                      netRadioSortTypeComboBox.setVisible(false);
                      netRadioPlayAllButton.setVisible(false);
                      netRadioCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netRadioList.setModel(emptyListModel);
                      netRadioListModel.clear();
                      radioInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netRadioList);
                              updateRenderer(collectionList);
                              netRadioList.repaint();
                              collectionList.repaint();
                          });
                          netRadioListModel.addElement(info);
                      });
                      netRadioList.setModel(netRadioListModel);
                      netRadioBackwardButton.setEnabled(true);
                      netRadioLeftBox.remove(netRadioKeywordsPanelScrollPane);
                      if (netRadioListModel.isEmpty()) {
                          netRadioLeftBox.remove(netRadioScrollPane);
                          netRadioLeftBox.add(emptyHintPanel);
                      } else {
                          netRadioLeftBox.remove(emptyHintPanel);
                          netRadioLeftBox.add(netRadioScrollPane);
                      }
                      netRadioScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_RADIO);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看演职员/CV
          netRadioArtistsMenuItem.addActionListener(e -> {
              NetRadioInfo radioInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) radioInfo = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  radioInfo = (NetRadioInfo) collectionList.getSelectedValue();
              else radioInfo = (NetRadioInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForArtist();
                      // 搜索演职员
                      CommonResult<NetArtistInfo> result = MusicServerUtil.getRadioArtists(currArtistRadioInfo = radioInfo);
                      List<NetArtistInfo> artistInfos = result.data;
                      netArtistCurrPage = netArtistMaxPage = 1;
                      // 标题
                      netArtistTitleLabel.setText(StringUtil.textToHtml(radioInfo.getName() + I18n.getText("castSuffix")));
                      netArtistToolBar.removeAll();
                      netArtistToolBar.add(netArtistBackwardButton);
                      netArtistToolBar.add(Box.createHorizontalGlue());
                      netArtistToolBar.add(netArtistTitleLabel);
                      netArtistToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
                      netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                      netArtistLeftBox.add(netArtistCountPanel);
                      netArtistSourceComboBox.setVisible(false);
                      netArtistPlayAllButton.setVisible(false);
                      netArtistCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netArtistList.setModel(emptyListModel);
                      netArtistListModel.clear();
                      artistInfos.forEach(artistInfo -> {
                          globalExecutor.execute(() -> updateCollection(artistInfo));
                          // 设置图片加载后重绘的事件
                          artistInfo.setInvokeLater(() -> {
                              updateRenderer(netArtistList);
                              updateRenderer(collectionList);
                              netArtistList.repaint();
                              collectionList.repaint();
                          });
                          netArtistListModel.addElement(artistInfo);
                      });
                      netArtistList.setModel(netArtistListModel);
                      netArtistBackwardButton.setEnabled(true);
                      netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                      if (netArtistListModel.isEmpty()) {
                          netArtistLeftBox.remove(netArtistScrollPane);
                          netArtistLeftBox.add(emptyHintPanel);
                      } else {
                          netArtistLeftBox.remove(emptyHintPanel);
                          netArtistLeftBox.add(netArtistScrollPane);
                      }
                      netArtistScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_ARTIST);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看电台照片
          netRadioPhotosMenuItem.addActionListener(e -> {
              NetRadioInfo radioInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) radioInfo = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  radioInfo = (NetRadioInfo) collectionList.getSelectedValue();
              else radioInfo = (NetRadioInfo) itemRecommendList.getSelectedValue();
              try {
                  imageViewDialog = new ImageViewDialog(THIS, radioInfo.isGame() ? 24 : 30) {
                      @Override
                      public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                          return MusicServerUtil.getRadioImgUrls(radioInfo, pn);
                      }
  
                      @Override
                      public void requestFailed() {
                          new TipDialog(THIS, NO_IMG_MSG).showDialog();
                      }
                  };
                  imageViewDialog.showDialog();
                  imageViewDialog = null;
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          });
          // 查看电台海报
          netRadioPostersMenuItem.addActionListener(e -> {
              NetRadioInfo radioInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) radioInfo = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  radioInfo = (NetRadioInfo) collectionList.getSelectedValue();
              else radioInfo = (NetRadioInfo) itemRecommendList.getSelectedValue();
              try {
                  imageViewDialog = new ImageViewDialog(THIS, 30) {
                      @Override
                      public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                          return MusicServerUtil.getRadioPosterUrls(radioInfo, pn);
                      }
  
                      @Override
                      public void requestFailed() {
                          new TipDialog(THIS, NO_IMG_MSG).showDialog();
                      }
                  };
                  imageViewDialog.showDialog();
                  imageViewDialog = null;
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          });
          // 复制名称
          netRadioCopyNameMenuItem.addActionListener(e -> {
              NetResource resource;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RADIO) resource = netRadioList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL) resource = collectionList.getSelectedValue();
              else resource = itemRecommendList.getSelectedValue();
              copyToClipboard(((NetRadioInfo) resource).toSimpleString());
          });
          // 电台列表右键菜单项
          netRadioPopupMenu.add(netRadioOpenMenuItem);
          netRadioPopupMenu.add(netRadioPlayAllMenuItem);
          netRadioPopupMenu.add(netRadioCollectMenuItem);
          netRadioPopupMenu.add(netRadioCommentMenuItem);
          netRadioPopupMenu.add(netRadioDjMenuItem);
          netRadioPopupMenu.add(netRadioSubscriberMenuItem);
          netRadioPopupMenu.add(netRadioSimilarMenuItem);
          netRadioPopupMenu.add(netRadioArtistsMenuItem);
          netRadioPopupMenu.add(netRadioPhotosMenuItem);
          netRadioPopupMenu.add(netRadioPostersMenuItem);
          netRadioPopupMenu.add(netRadioCopyNameMenuItem);
  
          // 搜索历史面板
          // 清空搜索历史按钮
          netRadioClearHistorySearchButton.addActionListener(e -> netRadioHistorySearchInnerPanel2.removeAll());
          netRadioClearHistorySearchButton.setPreferredSize(new Dimension(30, 30));
          netRadioClearHistorySearchButton.setToolTipText(CLEAR_HISTORY_SEARCH_TIP);
          netRadioClearHistorySearchButton.addMouseListener(new ButtonMouseListener(netRadioClearHistorySearchButton, THIS));
  
          netRadioHistorySearchInnerPanel1.setVisible(false);
          netRadioHistorySearchInnerPanel1.add(netRadioHistorySearchLabel);
          netRadioHistorySearchInnerPanel1.add(netRadioClearHistorySearchButton);
          netRadioHistorySearchInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netRadioHistorySearchInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netRadioHistorySearchInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netRadioHistorySearchInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netRadioHistorySearchInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netRadioHistorySearchInnerPanel2.addContainerListener(new ContainerAdapter() {
              @Override
              public void componentAdded(ContainerEvent e) {
                  netRadioHistorySearchInnerPanel1.setVisible(true);
              }
  
              @Override
              public void componentRemoved(ContainerEvent e) {
                  if (netRadioHistorySearchInnerPanel2.getComponentCount() == 0)
                      netRadioHistorySearchInnerPanel1.setVisible(false);
              }
          });
          netRadioHistorySearchPanel.setLayout(new BoxLayout(netRadioHistorySearchPanel, BoxLayout.Y_AXIS));
          netRadioHistorySearchPanel.add(netRadioHistorySearchInnerPanel1);
          netRadioHistorySearchPanel.add(netRadioHistorySearchInnerPanel2);
  
          // 关键词面板
          netRadioKeywordsPanel.setLayout(new BoxLayout(netRadioKeywordsPanel, BoxLayout.Y_AXIS));
          netRadioKeywordsPanel.add(netRadioHistorySearchPanel);
  
          // 同时限制面板与滚动条的大小才能保证按钮不超出窗口！！！
          Dimension d = new Dimension(900, 500);
          netRadioKeywordsPanel.setPreferredSize(d);
          netRadioKeywordsPanelScrollPane.setPreferredSize(d);
  
          // 电台最佳大小
          netRadioScrollPane.setPreferredSize(new Dimension(200, 600));
          netRadioLeftBox.add(netRadioScrollPane);
      }
  
      // 初始化 MV 工具栏
      private void initNetMvToolBar() {
          // 返回关键词面板事件
          netMvBackwardButton.addActionListener(e -> {
              // 删除标题标签
              netMvToolBar.removeAll();
              netMvToolBar.add(netMvBackwardButton);
              netMvToolBar.add(netMvSearchTextField);
              netMvToolBar.add(netMvClearInputButton);
              netMvToolBar.add(netMvSearchButton);
              clearRequestForMv();
  
              netMvListModel.clear();
              netMvCountPanel.setVisible(false);
              netMvLeftBox.remove(emptyHintPanel);
              netMvLeftBox.remove(netMvScrollPane);
              netMvLeftBox.add(netMvKeywordsPanelScrollPane);
              netMvBackwardButton.setEnabled(false);
              netMvLeftBox.repaint();
          });
          netMvSearchTextField.addFocusListener(new TextFieldHintListener(netMvSearchTextField, "MV", currUIStyle.getForeColor()));
          netMvSearchTextField.getDocument().addDocumentListener(new DocumentListener() {
              @Override
              public void insertUpdate(DocumentEvent e) {
                  if (netMvSearchTextField.isOccupied()) {
                      netMvClearInputButton.setVisible(true);
                  }
              }
  
              @Override
              public void removeUpdate(DocumentEvent e) {
                  if (netMvSearchTextField.getText().isEmpty()) netMvClearInputButton.setVisible(false);
              }
  
              @Override
              public void changedUpdate(DocumentEvent e) {
  
              }
          });
          // 只能输入数字
          netMvPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 清除输入事件
          netMvClearInputButton.addActionListener(e -> {
              netMvSearchTextField.requestFocus();
              netMvSearchTextField.setText("");
              netMvBackwardButton.requestFocus();
          });
          // 搜索 MV 按钮事件
          netMvSearchButton.addActionListener(e -> {
              netMvCurrKeyword = netMvSearchTextField.getText().trim();
              if (!netMvCurrKeyword.isEmpty()) {
                  // 关键词加入搜索历史
                  DialogButton b = new DialogButton(netMvCurrKeyword);
                  b.addActionListener(event -> {
                      netMvSearchTextField.requestFocus();
                      netMvSearchTextField.setText(b.getPlainText());
                      netMvClearInputButton.setVisible(true);
                      netMvLeftBox.remove(netMvKeywordsPanelScrollPane);
                      netMvLeftBox.add(netMvScrollPane);
                      netMvSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netMvHistorySearchInnerPanel2.remove(b);
                          netMvHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  // 先删除重复的关键词
                  removeKeywordInHistorySearch(netMvCurrKeyword, HistorySearchType.NET_MV);
                  netMvHistorySearchInnerPanel2.add(b, 0);
                  // 删除超出上限的最后一个关键词
                  int c = netMvHistorySearchInnerPanel2.getComponentCount();
                  if (c > maxSearchHistoryCount)
                      netMvHistorySearchInnerPanel2.remove(c - 1);
                  netMvHistorySearchPanel.repaint();
  
                  loadingAndRun(() -> {
                      try {
                          // 搜索 MV 并显示 MV 列表
                          CommonResult<NetMvInfo> result = MusicServerUtil.searchMvs(
                                  netMvSourceComboBox.getSelectedIndex(), netMvCurrKeyword, netMvCurrPage = 1, limit, mvCursor = "");
                          List<NetMvInfo> mvInfos = result.data;
                          Integer total = result.total;
                          mvCursor = result.cursor;
                          netMvMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netMvCountLabel.setText(String.format(PAGINATION_MSG, netMvCurrPage, netMvMaxPage));
                          netMvCountPanel.setVisible(true);
                          netMvSourceComboBox.setVisible(true);
                          netMvSortTypeComboBox.setVisible(false);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMvList.setModel(emptyListModel);
                          netMvListModel.clear();
                          mvInfos.forEach(mvInfo -> {
                              globalExecutor.execute(() -> updateCollection(mvInfo));
                              // 设置图片加载后重绘的事件
                              mvInfo.setInvokeLater(() -> {
                                  updateRenderer(netMvList);
                                  updateRenderer(collectionList);
                                  netMvList.repaint();
                                  collectionList.repaint();
                              });
                              netMvListModel.addElement(mvInfo);
                          });
                          netMvList.setModel(netMvListModel);
                          netMvBackwardButton.setEnabled(true);
                          netMvLeftBox.remove(netMvKeywordsPanelScrollPane);
                          if (netMvListModel.isEmpty()) {
                              netMvLeftBox.remove(netMvScrollPane);
                              netMvLeftBox.add(emptyHintPanel);
                          } else {
                              netMvLeftBox.remove(emptyHintPanel);
                              netMvLeftBox.add(netMvScrollPane);
                          }
                          netMvScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          });
          // 搜索 MV 跳页事件
          Runnable searchMvGoPageAction = () -> {
              boolean artistRequest = currMvArtistInfo != null, songRequest = currMvMusicInfo != null,
                      mvRequest = currMvMvInfo != null, episodeRequest = currEpisodesMvInfo != null, userRequest = currMvUserInfo != null;
              if (artistRequest || songRequest || mvRequest || episodeRequest || userRequest || StringUtil.notEmpty(netMvCurrKeyword)) {
                  loadingAndRun(() -> {
                      try {
                          // 搜索 MV 并显示 MV 列表
                          CommonResult<NetMvInfo> result = artistRequest ? MusicServerUtil.getMvInfoInArtist(currMvArtistInfo, netMvCurrPage, limit)
                                  : songRequest ? MusicServerUtil.getRelatedMvs(currMvMusicInfo, netMvCurrPage, limit)
                                  : mvRequest ? MusicServerUtil.getSimilarMvs(currMvMvInfo)
                                  : episodeRequest ? MusicServerUtil.getVideoEpisodes(currEpisodesMvInfo, netMvCurrPage, limit)
                                  : userRequest ? MusicServerUtil.getUserVideos(currMvUserInfo, netMvSortTypeComboBox.getSelectedIndex(), netMvCurrPage, limit, mvCursor)
                                  : MusicServerUtil.searchMvs(netMvSourceComboBox.getSelectedIndex(), netMvCurrKeyword, netMvCurrPage, limit, mvCursor);
                          List<NetMvInfo> mvInfos = result.data;
                          Integer total = result.total;
                          mvCursor = result.cursor;
                          netMvMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netMvCountLabel.setText(String.format(PAGINATION_MSG, netMvCurrPage, netMvMaxPage));
                          netMvCountPanel.setVisible(true);
                          netMvCountPanel.add(netMvCountLabel, netMvCountPanel.getComponentIndex(netMvCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMvList.setModel(emptyListModel);
                          netMvListModel.clear();
                          mvInfos.forEach(mvInfo -> {
                              globalExecutor.execute(() -> updateCollection(mvInfo));
                              // 设置图片加载后重绘的事件
                              mvInfo.setInvokeLater(() -> {
                                  updateRenderer(netMvList);
                                  updateRenderer(collectionList);
                                  netMvList.repaint();
                                  collectionList.repaint();
                              });
                              netMvListModel.addElement(mvInfo);
                          });
                          netMvList.setModel(netMvListModel);
                          netMvScrollPane.setVValue(0);
                          if (netMvListModel.isEmpty()) {
                              netMvLeftBox.remove(netMvScrollPane);
                              netMvLeftBox.add(emptyHintPanel);
                          } else {
                              netMvLeftBox.remove(emptyHintPanel);
                              netMvLeftBox.add(netMvScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          netMvSortTypeComboBox.addItem(I18n.getText("latest"));
          netMvSortTypeComboBox.addItem(I18n.getText("mostPlayed"));
          netMvSortTypeComboBox.addItem(I18n.getText("mostCollected"));
          // MV 排序类型切换事件
          netMvSortTypeComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netMvCurrPage = 1;
              netMvRefreshButton.doClick();
          });
          // 刷新按钮事件
          netMvRefreshButton.addActionListener(e -> {
              searchMvGoPageAction.run();
          });
          // 第一页按钮事件
          netMvStartPageButton.addActionListener(e -> {
              if (netMvCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netMvCurrPage = 1;
              searchMvGoPageAction.run();
          });
          // 上一页按钮事件
          netMvLastPageButton.addActionListener(e -> {
              if (netMvCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netMvCurrPage--;
              searchMvGoPageAction.run();
          });
          // 下一页按钮事件
          netMvNextPageButton.addActionListener(e -> {
              if (netMvCurrPage >= netMvMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netMvCurrPage++;
              searchMvGoPageAction.run();
          });
          // 最后一页按钮事件
          netMvEndPageButton.addActionListener(e -> {
              if (netMvCurrPage >= netMvMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netMvCurrPage = netMvMaxPage;
              searchMvGoPageAction.run();
          });
          // 跳页按钮事件
          netMvGoButton.addActionListener(e -> {
              String text = netMvPageTextField.getText();
              netMvPageTextField.setText("");
              if (text.isEmpty()) return;
              int destPage = Integer.parseInt(text);
              if (destPage < 1 || destPage > netMvMaxPage) {
                  new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                  return;
              }
              netMvCurrPage = destPage;
              searchMvGoPageAction.run();
          });
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netMvRefreshButton.setPreferredSize(dimension);
          netMvStartPageButton.setPreferredSize(dimension);
          netMvLastPageButton.setPreferredSize(dimension);
          netMvGoButton.setPreferredSize(dimension);
          netMvNextPageButton.setPreferredSize(dimension);
          netMvEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netMvBackwardButton.addMouseListener(new ButtonMouseListener(netMvBackwardButton, THIS));
          netMvClearInputButton.addMouseListener(new ButtonMouseListener(netMvClearInputButton, THIS));
          netMvSearchButton.addMouseListener(new ButtonMouseListener(netMvSearchButton, THIS));
          netMvRefreshButton.addMouseListener(new ButtonMouseListener(netMvRefreshButton, THIS));
          netMvStartPageButton.addMouseListener(new ButtonMouseListener(netMvStartPageButton, THIS));
          netMvLastPageButton.addMouseListener(new ButtonMouseListener(netMvLastPageButton, THIS));
          netMvGoButton.addMouseListener(new ButtonMouseListener(netMvGoButton, THIS));
          netMvNextPageButton.addMouseListener(new ButtonMouseListener(netMvNextPageButton, THIS));
          netMvEndPageButton.addMouseListener(new ButtonMouseListener(netMvEndPageButton, THIS));
          // 不可见
          netMvSortTypeComboBox.setVisible(false);
          // 帮助提示
          netMvBackwardButton.setToolTipText(SHOW_KEYWORD_PANEL_TIP);
          netMvClearInputButton.setToolTipText(CLEAR_INPUT_TIP);
          netMvSearchButton.setToolTipText(SEARCH_TIP);
          netMvRefreshButton.setToolTipText(REFRESH_TIP);
          netMvStartPageButton.setToolTipText(START_PAGE_TIP);
          netMvLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netMvGoButton.setToolTipText(GO_TIP);
          netMvNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netMvEndPageButton.setToolTipText(END_PAGE_TIP);
          // 回到关键词面板起始不可用
          netMvBackwardButton.setEnabled(false);
          // 清除输入按钮初始不可见
          netMvClearInputButton.setVisible(false);
  
          netMvToolBar.add(netMvBackwardButton);
          netMvToolBar.add(netMvSearchTextField);
          netMvToolBar.add(netMvClearInputButton);
          netMvToolBar.add(netMvSearchButton);
          netMvLeftBox.add(netMvToolBar);
  
          for (String name : NetMusicSource.NAMES) netMvSourceComboBox.addItem(name);
          netMvSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netMvSearchButton.doClick();
          });
  
          //  MV 数量和当前歌曲标签
          netMvCountPanel.add(netMvSourceComboBox);
          netMvCountPanel.add(netMvSortTypeComboBox);
          netMvCountPanel.add(netMvStartPageButton);
          netMvCountPanel.add(netMvLastPageButton);
          netMvCountPanel.add(netMvCountLabel);
          netMvCountPanel.add(netMvNextPageButton);
          netMvCountPanel.add(netMvEndPageButton);
          netMvCountPanel.add(netMvRefreshButton);
          netMvCountPanel.add(netMvPageTextField);
          netMvCountPanel.add(netMvGoButton);
          netMvCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netMvCountPanel.setVisible(false);
          netMvLeftBox.add(netMvCountPanel);
      }
  
      // 初始化在线 MV 列表
      private void initNetMvList() {
          netMvList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          netMvList.setModel(netMvListModel);
          netMvList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netMvList.locationToIndex(e.getPoint());
                  Rectangle bounds = netMvList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetMvListRenderer renderer = (NetMvListRenderer) netMvList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netMvList.repaint();
              }
          });
          netMvList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetMvListRenderer renderer = (NetMvListRenderer) netMvList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netMvList.repaint();
              }
          });
          // 打开 MV
          Runnable playMvAction = () -> {
              try {
                  playMv(MvCompSourceType.MV_LIST);
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          };
          netMvList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      playMvAction.run();
                  }
              }
          });
          netMvList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开 MV
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      playMvAction.run();
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netMvList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netMvList.locationToIndex(e.getPoint());
                          if (index == -1) return;
                          if (!netMvList.isSelectedIndex(index)) netMvList.setSelectedIndex(index);
                          if (hasBeenCollected(netMvList.getSelectedValue())) {
                              netMvCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netMvCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                          } else {
                              netMvCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                              netMvCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                          }
  
                          netMvPopupMenu.show(netMvList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 播放 MV
          netMvPlayMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              if (index == TabIndex.NET_MV) playMvAction.run();
              else if (index == TabIndex.PERSONAL) openCollectionItemAction.run();
              else openRecommendItemAction.run();
          });
          // 收藏 MV
          netMvCollectMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              CustomList list;
              if (index == TabIndex.NET_MV) list = netMvList;
              else if (index == TabIndex.PERSONAL) list = collectionList;
              else list = itemRecommendList;
              ListModel model = collectionList.getModel();
              boolean needRefresh = model == mvCollectionModel;
              if (netMvCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT)) {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  for (int i = values.size() - 1; i >= 0; i--) {
                      NetMvInfo mvInfo = (NetMvInfo) values.get(i);
                      if (hasBeenCollected(mvInfo)) continue;
                      mvCollectionModel.add(0, mvInfo);
                  }
                  if (needRefresh) collectionList.setModel(model);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  values.forEach(o -> {
                      if (list == collectionList || hasBeenCollected((Resource) o)) {
                          mvCollectionModel.removeElement(o);
                      }
                  });
                  if (needRefresh) collectionList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 下载 MV
          netMvDownloadMenuItem.addActionListener(e -> {
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_MV) multiDownloadMv(netMvList.getSelectedValuesList(), false);
              else if (selectedIndex == TabIndex.PERSONAL) multiDownloadMv(collectionList.getSelectedValuesList(), false);
              else multiDownloadMv(itemRecommendList.getSelectedValuesList(), false);
          });
          // 查看相似 MV
          netMvSimilarMvMenuItem.addActionListener(e -> {
              NetMvInfo mvInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_MV) mvInfo = netMvList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  mvInfo = (NetMvInfo) collectionList.getSelectedValue();
              else mvInfo = (NetMvInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForMv();
                      // 获取相似 MV
                      CommonResult<NetMvInfo> result = MusicServerUtil.getSimilarMvs(currMvMvInfo = mvInfo);
                      List<NetMvInfo> mvInfos = result.data;
                      netMvCurrPage = netMvMaxPage = 1;
                      // 标题
                      netMvTitleLabel.setText(StringUtil.textToHtml(mvInfo.toSimpleString() + I18n.getText("similarMvSuffix")));
                      netMvToolBar.removeAll();
                      netMvToolBar.add(netMvBackwardButton);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      netMvToolBar.add(netMvTitleLabel);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netMvCountLabel.setText(String.format(PAGINATION_MSG, netMvCurrPage, netMvMaxPage));
                      netMvCountPanel.add(netMvCountLabel, netMvCountPanel.getComponentIndex(netMvCountLabel));
                      netMvLeftBox.add(netMvCountPanel);
                      netMvCountPanel.setVisible(true);
                      netMvSourceComboBox.setVisible(false);
                      netMvSortTypeComboBox.setVisible(false);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netMvList.setModel(emptyListModel);
                      netMvListModel.clear();
                      mvInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netMvList);
                              updateRenderer(collectionList);
                              netMvList.repaint();
                              collectionList.repaint();
                          });
                          netMvListModel.addElement(info);
                      });
                      netMvList.setModel(netMvListModel);
                      netMvBackwardButton.setEnabled(true);
                      netMvLeftBox.remove(netMvKeywordsPanelScrollPane);
                      if (netMvListModel.isEmpty()) {
                          netMvLeftBox.remove(netMvScrollPane);
                          netMvLeftBox.add(emptyHintPanel);
                      } else {
                          netMvLeftBox.remove(emptyHintPanel);
                          netMvLeftBox.add(netMvScrollPane);
                      }
                      netMvScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_MV);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看视频分集
          netMvVideoEpisodeMenuItem.addActionListener(e -> {
              NetMvInfo mvInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_MV) mvInfo = netMvList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  mvInfo = (NetMvInfo) collectionList.getSelectedValue();
              else mvInfo = (NetMvInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForMv();
                      // 获取视频分集
                      CommonResult<NetMvInfo> result = MusicServerUtil.getVideoEpisodes(currEpisodesMvInfo = mvInfo, netMvCurrPage = 1, limit);
                      List<NetMvInfo> mvInfos = result.data;
                      Integer total = result.total;
                      netMvMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netMvTitleLabel.setText(StringUtil.textToHtml(mvInfo.toSimpleString() + I18n.getText("episodeSuffix")));
                      netMvToolBar.removeAll();
                      netMvToolBar.add(netMvBackwardButton);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      netMvToolBar.add(netMvTitleLabel);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netMvCountLabel.setText(String.format(PAGINATION_MSG, netMvCurrPage, netMvMaxPage));
                      netMvCountPanel.add(netMvCountLabel, netMvCountPanel.getComponentIndex(netMvCountLabel));
                      netMvLeftBox.add(netMvCountPanel);
                      netMvCountPanel.setVisible(true);
                      netMvSourceComboBox.setVisible(false);
                      netMvSortTypeComboBox.setVisible(false);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netMvList.setModel(emptyListModel);
                      netMvListModel.clear();
                      mvInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netMvList);
                              updateRenderer(collectionList);
                              netMvList.repaint();
                              collectionList.repaint();
                          });
                          netMvListModel.addElement(info);
                      });
                      netMvList.setModel(netMvListModel);
                      netMvBackwardButton.setEnabled(true);
                      netMvLeftBox.remove(netMvKeywordsPanelScrollPane);
                      if (netMvListModel.isEmpty()) {
                          netMvLeftBox.remove(netMvScrollPane);
                          netMvLeftBox.add(emptyHintPanel);
                      } else {
                          netMvLeftBox.remove(emptyHintPanel);
                          netMvLeftBox.add(netMvScrollPane);
                      }
                      netMvScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_MV);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看 MV 歌手/发布者
          netMvCreatorMenuItem.addActionListener(e -> {
              NetMvInfo mvInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_MV) mvInfo = netMvList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  mvInfo = (NetMvInfo) collectionList.getSelectedValue();
              else mvInfo = (NetMvInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      if (mvInfo.isRealMV()) {
                          clearRequestForArtist();
                          // 查看歌手
                          currArtistMvInfo = mvInfo;
                          CommonResult<NetArtistInfo> result = MusicServerUtil.getArtistInfo(mvInfo.getSource(), mvInfo.getCreatorId());
                          List<NetArtistInfo> artistInfos = result.data;
                          netArtistCurrPage = netArtistMaxPage = 1;
                          // 标题
                          netArtistTitleLabel.setText(StringUtil.textToHtml(mvInfo.toSimpleString() + I18n.getText("artistSuffix")));
                          netArtistToolBar.removeAll();
                          netArtistToolBar.add(netArtistBackwardButton);
                          netArtistToolBar.add(Box.createHorizontalGlue());
                          netArtistToolBar.add(netArtistTitleLabel);
                          netArtistToolBar.add(Box.createHorizontalGlue());
                          // 更新数量显示
                          netArtistCountLabel.setText(String.format(PAGINATION_MSG, netArtistCurrPage, netArtistMaxPage));
                          netArtistCountPanel.add(netArtistCountLabel, netArtistCountPanel.getComponentIndex(netArtistCountLabel));
                          netArtistLeftBox.add(netArtistCountPanel);
                          netArtistSourceComboBox.setVisible(false);
                          netArtistPlayAllButton.setVisible(false);
                          netArtistCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netArtistList.setModel(emptyListModel);
                          netArtistListModel.clear();
                          artistInfos.forEach(artistInfo -> {
                              globalExecutor.execute(() -> updateCollection(artistInfo));
                              // 设置图片加载后重绘的事件
                              artistInfo.setInvokeLater(() -> {
                                  updateRenderer(netArtistList);
                                  updateRenderer(collectionList);
                                  netArtistList.repaint();
                                  collectionList.repaint();
                              });
                              netArtistListModel.addElement(artistInfo);
                          });
                          netArtistList.setModel(netArtistListModel);
                          netArtistBackwardButton.setEnabled(true);
                          netArtistLeftBox.remove(netArtistKeywordsPanelScrollPane);
                          if (netArtistListModel.isEmpty()) {
                              netArtistLeftBox.remove(netArtistScrollPane);
                              netArtistLeftBox.add(emptyHintPanel);
                          } else {
                              netArtistLeftBox.remove(emptyHintPanel);
                              netArtistLeftBox.add(netArtistScrollPane);
                          }
                          netArtistScrollPane.setVValue(0);
                          tabbedPane.setSelectedIndex(TabIndex.NET_ARTIST);
                      } else {
                          clearRequestForUser();
                          // 获取 MV 发布者
                          currUserMvInfo = mvInfo;
                          CommonResult<NetUserInfo> result = MusicServerUtil.getUserInfo(mvInfo.getSource(), mvInfo.getCreatorId());
                          List<NetUserInfo> userInfos = result.data;
                          netUserCurrPage = netUserMaxPage = 1;
                          // 标题
                          netUserTitleLabel.setText(StringUtil.textToHtml(mvInfo.getName() + I18n.getText("publisherSuffix")));
                          netUserToolBar.removeAll();
                          netUserToolBar.add(netUserBackwardButton);
                          netUserToolBar.add(Box.createHorizontalGlue());
                          netUserToolBar.add(netUserTitleLabel);
                          netUserToolBar.add(Box.createHorizontalGlue());
                          // 更新数量显示
                          netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                          netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                          netUserLeftBox.add(netUserCountPanel);
                          netUserSourceComboBox.setVisible(false);
                          netUserPlayAllButton.setVisible(false);
                          netUserRecordTypeComboBox.setVisible(false);
                          netUserCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netUserList.setModel(emptyListModel);
                          netUserListModel.clear();
                          userInfos.forEach(userInfo -> {
                              globalExecutor.execute(() -> updateCollection(userInfo));
                              // 设置图片加载后重绘的事件
                              userInfo.setInvokeLater(() -> {
                                  updateRenderer(netUserList);
                                  updateRenderer(collectionList);
                                  netUserList.repaint();
                                  collectionList.repaint();
                              });
                              netUserListModel.addElement(userInfo);
                          });
                          netUserList.setModel(netUserListModel);
                          netUserBackwardButton.setEnabled(true);
                          netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                          if (netUserListModel.isEmpty()) {
                              netUserLeftBox.remove(netUserScrollPane);
                              netUserLeftBox.add(emptyHintPanel);
                          } else {
                              netUserLeftBox.remove(emptyHintPanel);
                              netUserLeftBox.add(netUserScrollPane);
                          }
                          netUserScrollPane.setVValue(0);
                          tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                      }
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看评论
          netMvCommentMenuItem.addActionListener(e -> {
              NetMvInfo mvInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_MV) mvInfo = netMvList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL) mvInfo = (NetMvInfo) collectionList.getSelectedValue();
              else mvInfo = (NetMvInfo) itemRecommendList.getSelectedValue();
              getComments(mvInfo, true);
          });
          // 复制名称
          netMvCopyNameMenuItem.addActionListener(e -> {
              NetResource resource;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_MV) resource = netMvList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL) resource = collectionList.getSelectedValue();
              else resource = itemRecommendList.getSelectedValue();
              copyToClipboard(((NetMvInfo) resource).toSimpleString());
          });
          // MV 列表右键菜单项
          netMvPopupMenu.add(netMvPlayMenuItem);
          netMvPopupMenu.add(netMvCollectMenuItem);
          netMvPopupMenu.add(netMvDownloadMenuItem);
          netMvPopupMenu.add(netMvSimilarMvMenuItem);
          netMvPopupMenu.add(netMvVideoEpisodeMenuItem);
          netMvPopupMenu.add(netMvCreatorMenuItem);
          netMvPopupMenu.add(netMvCommentMenuItem);
          netMvPopupMenu.add(netMvCopyNameMenuItem);
  
          // 搜索历史面板
          // 清空搜索历史按钮
          netMvClearHistorySearchButton.addActionListener(e -> netMvHistorySearchInnerPanel2.removeAll());
          netMvClearHistorySearchButton.setPreferredSize(new Dimension(30, 30));
          netMvClearHistorySearchButton.setToolTipText(CLEAR_HISTORY_SEARCH_TIP);
          netMvClearHistorySearchButton.addMouseListener(new ButtonMouseListener(netMvClearHistorySearchButton, THIS));
  
          netMvHistorySearchInnerPanel1.setVisible(false);
          netMvHistorySearchInnerPanel1.add(netMvHistorySearchLabel);
          netMvHistorySearchInnerPanel1.add(netMvClearHistorySearchButton);
          netMvHistorySearchInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netMvHistorySearchInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netMvHistorySearchInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netMvHistorySearchInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netMvHistorySearchInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netMvHistorySearchInnerPanel2.addContainerListener(new ContainerAdapter() {
              @Override
              public void componentAdded(ContainerEvent e) {
                  netMvHistorySearchInnerPanel1.setVisible(true);
              }
  
              @Override
              public void componentRemoved(ContainerEvent e) {
                  if (netMvHistorySearchInnerPanel2.getComponentCount() == 0)
                      netMvHistorySearchInnerPanel1.setVisible(false);
              }
          });
          netMvHistorySearchPanel.setLayout(new BoxLayout(netMvHistorySearchPanel, BoxLayout.Y_AXIS));
          netMvHistorySearchPanel.add(netMvHistorySearchInnerPanel1);
          netMvHistorySearchPanel.add(netMvHistorySearchInnerPanel2);
  
          // 关键词面板
          netMvKeywordsPanel.setLayout(new BoxLayout(netMvKeywordsPanel, BoxLayout.Y_AXIS));
          netMvKeywordsPanel.add(netMvHistorySearchPanel);
  
          // 同时限制面板与滚动条的大小才能保证按钮不超出窗口！！！
          Dimension d = new Dimension(900, 500);
          netMvKeywordsPanel.setPreferredSize(d);
          netMvKeywordsPanelScrollPane.setPreferredSize(d);
  
          //  MV 最佳大小
          netMvScrollPane.setPreferredSize(new Dimension(200, 600));
          netMvLeftBox.add(netMvScrollPane);
      }
  
      // 初始化榜单工具栏
      private void initNetRankingToolBar() {
          // 只能输入数字
          netRankingPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 后退按钮事件
          netRankingBackwardButton.addActionListener(e -> {
              netMusicListForRankingModel.clear();
              // 从在线音乐列表切换回榜单列表
              netRankingPlayAllButton.setVisible(false);
              netRankingLeftBox.remove(rankingListBox);
              netRankingLeftBox.add(netRankingCountPanel);
              netRankingLeftBox.add(netRankingScrollPane);
              // 更新榜单歌曲数量显示
              netRankingCountLabel.setText(String.format(PAGINATION_MSG, netRankingCurrPage, netRankingMaxPage));
              netRankingBackwardButton.setEnabled(false);
              netRankingSourceComboBox.setVisible(true);
              // 切换后一定要刷新！
              netRankingLeftBox.repaint();
          });
          // 获取榜单事件
          getRankingAction = () -> {
              loadingAndRun(() -> {
                  try {
                      // 搜索榜单并显示榜单列表
                      CommonResult<NetRankingInfo> result = MusicServerUtil.getRankings(netRankingSourceComboBox.getSelectedIndex());
                      List<NetRankingInfo> rankingInfos = result.data;
  //                        Integer total = result.total;
  //                        netRankingMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      netRankingMaxPage = 1;
                      // 更新数量显示
                      netRankingCountLabel.setText(String.format(PAGINATION_MSG, netRankingCurrPage, netRankingMaxPage));
                      netRankingCountPanel.setVisible(true);
                      netRankingSourceComboBox.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netRankingList.setModel(emptyListModel);
                      netRankingListModel.clear();
                      rankingInfos.forEach(rankingInfo -> {
                          globalExecutor.execute(() -> updateCollection(rankingInfo));
                          // 设置图片加载后重绘的事件
                          rankingInfo.setInvokeLater(() -> {
                              updateRenderer(netRankingList);
                              updateRenderer(collectionList);
                              netRankingList.repaint();
                              collectionList.repaint();
                          });
                          netRankingListModel.addElement(rankingInfo);
                      });
                      netRankingList.setModel(netRankingListModel);
                      if (netRankingListModel.isEmpty()) {
                          netRankingLeftBox.remove(netRankingScrollPane);
                          netRankingLeftBox.add(emptyHintPanel);
                      } else {
                          netRankingLeftBox.remove(emptyHintPanel);
                          netRankingLeftBox.add(netRankingScrollPane);
                      }
                      netRankingScrollPane.setVValue(0);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          };
          // 搜索榜单内歌曲并显示在在线歌曲列表
          Runnable searchMusicInRanking = () -> {
              if (!netMusicListForRankingModel.isEmpty()) {
                  loadingAndRun(() -> {
                      try {
                          NetRankingInfo rankingInfo = netRankingList.getSelectedValue();
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRanking(
                                  rankingInfo.getId(), rankingInfo.getSource(), netMusicInRankingCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          Integer total = result.total;
                          netMusicInRankingMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netRankingCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRankingCurrPage, netMusicInRankingMaxPage));
                          netRankingCountPanel.add(netRankingCountLabel, netRankingCountPanel.getComponentIndex(netRankingCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForRankingModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForRankingModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForRankingModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicListForRankingModel.isEmpty()) {
                              rankingListCountBox.remove(netMusicScrollPane);
                              rankingListCountBox.add(emptyHintPanel);
                          } else {
                              rankingListCountBox.remove(emptyHintPanel);
                              rankingListCountBox.add(netMusicScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 播放全部
          netRankingPlayAllButton.addActionListener(e -> netRankingPlayAllMenuItem.doClick());
          // 刷新按钮事件
          netRankingRefreshButton.addActionListener(e -> {
              Component lc = netRankingLeftBox.getComponent(netRankingLeftBox.getComponentCount() - 1);
              // 当前显示的是榜单列表，刷新榜单
              if (lc == netRankingScrollPane || lc == emptyHintPanel) {
                  getRankingAction.run();
              }
              // 当前显示的是某榜单的歌曲，刷新歌曲
              else {
                  searchMusicInRanking.run();
              }
          });
          // 第一页按钮事件
          netRankingStartPageButton.addActionListener(e -> {
              Component lc = netRankingLeftBox.getComponent(netRankingLeftBox.getComponentCount() - 1);
              // 当前显示的是榜单列表，跳到第一页榜单
              if (lc == netRankingScrollPane || lc == emptyHintPanel) {
                  if (netRankingCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRankingCurrPage = 1;
                  getRankingAction.run();
              }
              // 当前显示的是某榜单的歌曲，跳到第一页歌曲
              else {
                  if (netMusicInRankingCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRankingCurrPage = 1;
                  searchMusicInRanking.run();
              }
          });
          // 上一页按钮事件
          netRankingLastPageButton.addActionListener(e -> {
              Component lc = netRankingLeftBox.getComponent(netRankingLeftBox.getComponentCount() - 1);
              // 当前显示的是榜单列表，跳到上一页榜单
              if (lc == netRankingScrollPane || lc == emptyHintPanel) {
                  if (netRankingCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRankingCurrPage--;
                  getRankingAction.run();
              }
              // 当前显示的是某榜单的歌曲，跳到上一页歌曲
              else {
                  if (netMusicInRankingCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRankingCurrPage--;
                  searchMusicInRanking.run();
              }
          });
          // 下一页按钮事件
          netRankingNextPageButton.addActionListener(e -> {
              Component lc = netRankingLeftBox.getComponent(netRankingLeftBox.getComponentCount() - 1);
              // 当前显示的是榜单列表，跳到下一页榜单
              if (lc == netRankingScrollPane || lc == emptyHintPanel) {
                  if (netRankingCurrPage >= netRankingMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRankingCurrPage++;
                  getRankingAction.run();
              }
              // 当前显示的是某榜单的歌曲，跳到下一页歌曲
              else {
                  if (netMusicInRankingCurrPage >= netMusicInRankingMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRankingCurrPage++;
                  searchMusicInRanking.run();
              }
          });
          // 最后一页按钮事件
          netRankingEndPageButton.addActionListener(e -> {
              Component lc = netRankingLeftBox.getComponent(netRankingLeftBox.getComponentCount() - 1);
              // 当前显示的是榜单列表，跳到最后一页榜单
              if (lc == netRankingScrollPane || lc == emptyHintPanel) {
                  if (netRankingCurrPage >= netRankingMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRankingCurrPage = netRankingMaxPage;
                  getRankingAction.run();
              }
              // 当前显示的是某榜单的歌曲，跳到最后一页歌曲
              else {
                  if (netMusicInRankingCurrPage >= netMusicInRankingMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRankingCurrPage = netMusicInRankingMaxPage;
                  searchMusicInRanking.run();
              }
          });
          // 跳页按钮事件
          netRankingGoButton.addActionListener(e -> {
              Component lc = netRankingLeftBox.getComponent(netRankingLeftBox.getComponentCount() - 1);
              // 当前显示的是榜单列表，跳页榜单
              if (lc == netRankingScrollPane || lc == emptyHintPanel) {
                  String text = netRankingPageTextField.getText();
                  netRankingPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netRankingMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  getRankingAction.run();
              }
              // 当前显示的是某榜单的歌曲，跳页歌曲
              else {
                  String text = netRankingPageTextField.getText();
                  netRankingPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netMusicInRankingMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRankingCurrPage = destPage;
                  searchMusicInRanking.run();
              }
          });
          // 按钮被禁止的图标
          netRankingBackwardButton.setEnabled(false);
          // 按钮初始不可见
          netRankingPlayAllButton.setVisible(false);
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netRankingRefreshButton.setPreferredSize(dimension);
          netRankingStartPageButton.setPreferredSize(dimension);
          netRankingLastPageButton.setPreferredSize(dimension);
          netRankingGoButton.setPreferredSize(dimension);
          netRankingNextPageButton.setPreferredSize(dimension);
          netRankingEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netRankingBackwardButton.addMouseListener(new ButtonMouseListener(netRankingBackwardButton, THIS));
          netRankingPlayAllButton.addMouseListener(new ButtonMouseListener(netRankingPlayAllButton, THIS));
          netRankingRefreshButton.addMouseListener(new ButtonMouseListener(netRankingRefreshButton, THIS));
          netRankingStartPageButton.addMouseListener(new ButtonMouseListener(netRankingStartPageButton, THIS));
          netRankingLastPageButton.addMouseListener(new ButtonMouseListener(netRankingLastPageButton, THIS));
          netRankingGoButton.addMouseListener(new ButtonMouseListener(netRankingGoButton, THIS));
          netRankingNextPageButton.addMouseListener(new ButtonMouseListener(netRankingNextPageButton, THIS));
          netRankingEndPageButton.addMouseListener(new ButtonMouseListener(netRankingEndPageButton, THIS));
          // 帮助提示
          netRankingBackwardButton.setToolTipText(BACKWARD_TIP);
          netRankingPlayAllButton.setToolTipText(PLAY_ALL_TIP);
          netRankingRefreshButton.setToolTipText(REFRESH_TIP);
          netRankingStartPageButton.setToolTipText(START_PAGE_TIP);
          netRankingLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netRankingGoButton.setToolTipText(GO_TIP);
          netRankingNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netRankingEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netRankingPlayAllButton.setIconTextGap(5);
  
          netRankingToolBar.add(netRankingBackwardButton);
          netRankingToolBar.add(Box.createHorizontalGlue());
          netRankingLeftBox.add(netRankingToolBar);
  
          for (String name : NetMusicSource.NAMES) netRankingSourceComboBox.addItem(name);
          netRankingSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netRankingCurrPage = 1;
              netRankingRefreshButton.doClick();
          });
  
          // 榜单数量和当前歌曲标签
          netRankingCountPanel.add(netRankingSourceComboBox);
          netRankingCountPanel.add(netRankingPlayAllButton);
          netRankingCountPanel.add(netRankingStartPageButton);
          netRankingCountPanel.add(netRankingLastPageButton);
          netRankingCountPanel.add(netRankingCountLabel);
          netRankingCountPanel.add(netRankingNextPageButton);
          netRankingCountPanel.add(netRankingEndPageButton);
          netRankingCountPanel.add(netRankingRefreshButton);
          netRankingCountPanel.add(netRankingPageTextField);
          netRankingCountPanel.add(netRankingGoButton);
          netRankingCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netRankingCountPanel.setVisible(false);
          netRankingLeftBox.add(netRankingCountPanel);
      }
  
      // 初始化在线榜单列表
      private void initNetRankingList() {
          netRankingList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          netRankingList.setModel(netRankingListModel);
          netRankingList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netRankingList.locationToIndex(e.getPoint());
                  Rectangle bounds = netRankingList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetRankingListRenderer renderer = (NetRankingListRenderer) netRankingList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netRankingList.repaint();
              }
          });
          netRankingList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetRankingListRenderer renderer = (NetRankingListRenderer) netRankingList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netRankingList.repaint();
              }
          });
          // 打开榜单
          Runnable openRankingAction = () -> {
              NetRankingInfo rankingInfo = netRankingList.getSelectedValue();
              if (rankingInfo == null) return;
              loadingAndRun(() -> {
                  List<Future<?>> taskList = new LinkedList<>();
  
                  // 检查收藏按钮
                  checkDescriptionCollectionStatus(rankingDescriptionCollectionButton, rankingInfo);
  
                  // 加载封面图片和描述
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                      rankingCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                      rankingCoverAndNameLabel.setText(LOADING_MSG);
                      rankingDescriptionLabel.setText(LOADING_MSG);
                      GlobalExecutors.requestExecutor.execute(() -> {
                          try {
                              MusicServerUtil.fillRankingInfo(rankingInfo);
                              updateRenderer(netRankingList);
                              rankingCoverAndNameLabel.setText(StringUtil.textToHtml(rankingInfo.getName()));
                              if (rankingInfo.getDescription() != null)
                                  rankingDescriptionLabel.setText(StringUtil.textToHtml(rankingInfo.getDescription()));
                              if (rankingInfo.hasCoverImg()) {
                                  rankingCoverAndNameLabel.setIcon(new ImageIcon(
                                          ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(rankingInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                  ));
                              } else {
                                  rankingInfo.setInvokeLater(() -> {
                                      rankingCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(rankingInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                      netRankingList.repaint();
                                  });
                              }
                          } catch (HttpException e) {
                              rankingCoverAndNameLabel.setText(LOAD_FAILED);
                              rankingDescriptionLabel.setText(LOAD_FAILED);
                          }
                      });
                  }));
  
  
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      try {
                          // 得到榜单的音乐信息
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRanking(
                                  rankingInfo.getId(), rankingInfo.getSource(), netMusicInRankingCurrPage = 1, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          Integer total = result.total;
                          netMusicInRankingMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新榜单歌曲数量显示
  //                            netCurrRankingLabel.setText(StringUtils.textToHtml(RankingInfo.getName()));
                          netRankingCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRankingCurrPage, netMusicInRankingMaxPage));
                          rankingListCountBox.add(netRankingCountPanel);
                          rankingListBox.add(rankingListCountBox);
                          // 榜单列表切换到在线音乐列表
                          netMusicList.setModel(netMusicListForRankingModel);
                          netRankingLeftBox.remove(netRankingScrollPane);
                          netRankingLeftBox.add(rankingListBox);
                          // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForRankingModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForRankingModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForRankingModel);
                          rankingDescriptionScrollPane.setVValue(0);
                          netMusicScrollPane.setVValue(0);
                          netRankingBackwardButton.setEnabled(true);
                          netRankingSourceComboBox.setVisible(false);
                          netRankingPlayAllButton.setVisible(true);
                          if (netMusicListForRankingModel.isEmpty()) {
                              rankingListCountBox.remove(netMusicScrollPane);
                              rankingListCountBox.add(emptyHintPanel);
                          } else {
                              rankingListCountBox.remove(emptyHintPanel);
                              rankingListCountBox.add(netMusicScrollPane);
                          }
                          // 切换后一定要刷新！
                          netRankingLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  }));
  
                  taskList.forEach(task -> {
                      try {
                          task.get();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } catch (ExecutionException e) {
                          e.printStackTrace();
                      }
                  });
              });
          };
          netRankingList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      openRankingAction.run();
                  }
              }
          });
          netRankingList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开榜单
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      openRankingAction.run();
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netRankingList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netRankingList.locationToIndex(e.getPoint());
                          if (index == -1) return;
                          if (!netRankingList.isSelectedIndex(index)) netRankingList.setSelectedIndex(index);
                          if (hasBeenCollected(netRankingList.getSelectedValue())) {
                              netRankingCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netRankingCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                          } else {
                              netRankingCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                              netRankingCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                          }
  
                          netRankingPopupMenu.show(netRankingList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 打开榜单
          netRankingOpenMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              if (index == TabIndex.NET_RANKING) openRankingAction.run();
              else if (index == TabIndex.PERSONAL) openCollectionItemAction.run();
              else openRecommendItemAction.run();
          });
          // 播放全部
          netRankingPlayAllMenuItem.addActionListener(e -> {
              NetRankingInfo rankingInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RANKING) rankingInfo = netRankingList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  rankingInfo = (NetRankingInfo) collectionList.getSelectedValue();
              else rankingInfo = (NetRankingInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRanking(
                          rankingInfo.getId(), rankingInfo.getSource(), 1, 1000);
                  List<NetMusicInfo> musicInfos = result.data;
                  if (musicInfos.isEmpty()) {
                      new TipDialog(THIS, NO_MUSIC_MSG).showDialog();
                      return;
                  }
                  playQueue.setModel(emptyListModel);
                  playQueueModel.clear();
                  musicInfos.forEach(musicInfo -> {
                      globalExecutor.execute(() -> updateCollection(musicInfo));
                      playQueueModel.addElement(musicInfo);
                  });
                  playQueue.setModel(playQueueModel);
                  playQueue.setSelectedIndex(0);
                  playExecutor.execute(() -> playSelected(playQueue, true));
              });
          });
          // 收藏榜单
          netRankingCollectMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              CustomList list;
              if (index == TabIndex.NET_RANKING) list = netRankingList;
              else if (index == TabIndex.PERSONAL) list = collectionList;
              else list = itemRecommendList;
              ListModel model = collectionList.getModel();
              boolean needRefresh = model == rankingCollectionModel;
              if (netRankingCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT) && list.isShowing()
                      || list == netRankingList && rankingDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == itemRecommendList && recommendItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == collectionList && collectionItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)) {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  // 描述面板取消收藏之后，使用之前保存的对象
                  boolean userOpenResource = values.isEmpty() && !hasBeenCollected(collectionOpenResource);
                  if (userOpenResource) {
                      rankingCollectionModel.add(0, collectionOpenResource);
                  } else {
                      for (int i = values.size() - 1; i >= 0; i--) {
                          NetRankingInfo rankingInfo = (NetRankingInfo) values.get(i);
                          if (hasBeenCollected(rankingInfo)) continue;
                          rankingCollectionModel.add(0, rankingInfo);
                      }
                  }
                  if (needRefresh) collectionList.setModel(model);
                  if (userOpenResource) collectionList.setSelectedIndex(0);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  values.forEach(o -> {
                      if (list == collectionList || hasBeenCollected((Resource) o)) {
                          rankingCollectionModel.removeElement(o);
                      }
                  });
                  if (needRefresh) collectionList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 查看评论
          netRankingCommentMenuItem.addActionListener(e -> {
              NetRankingInfo rankingInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RANKING) rankingInfo = netRankingList.getSelectedValue();
              else rankingInfo = (NetRankingInfo) collectionList.getSelectedValue();
              getComments(rankingInfo, true);
          });
          // 复制名称
          netRankingCopyNameMenuItem.addActionListener(e -> {
              NetRankingInfo rankingInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_RANKING) rankingInfo = netRankingList.getSelectedValue();
              else rankingInfo = (NetRankingInfo) collectionList.getSelectedValue();
              copyToClipboard(rankingInfo.toSimpleString());
          });
          // 榜单列表右键菜单项
          netRankingPopupMenu.add(netRankingOpenMenuItem);
          netRankingPopupMenu.add(netRankingPlayAllMenuItem);
          netRankingPopupMenu.add(netRankingCollectMenuItem);
          netRankingPopupMenu.add(netRankingCommentMenuItem);
          netRankingPopupMenu.add(netRankingCopyNameMenuItem);
  
          // 榜单最佳大小
          netRankingScrollPane.setPreferredSize(new Dimension(200, 600));
          netRankingLeftBox.add(netRankingScrollPane);
      }
  
      // 初始化用户工具栏
      private void initNetUserToolBar() {
          netUserSearchTextField.addFocusListener(new TextFieldHintListener(netUserSearchTextField, "用户", currUIStyle.getForeColor()));
          netUserSearchTextField.getDocument().addDocumentListener(new DocumentListener() {
              @Override
              public void insertUpdate(DocumentEvent e) {
                  if (netUserSearchTextField.isOccupied()) {
                      netUserClearInputButton.setVisible(true);
                  }
              }
  
              @Override
              public void removeUpdate(DocumentEvent e) {
                  if (netUserSearchTextField.getText().isEmpty()) netUserClearInputButton.setVisible(false);
              }
  
              @Override
              public void changedUpdate(DocumentEvent e) {
  
              }
          });
          // 只能输入数字
          netUserPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 后退按钮事件
          netUserBackwardButton.addActionListener(e -> {
              int lastComponentIndex = netUserLeftBox.getComponentCount() - 1;
              Component c = netUserLeftBox.getComponent(lastComponentIndex);
              if (c != netUserScrollPane && c != emptyHintPanel) {
                  netMusicListForUserModel.clear();
                  // 用户播放记录下拉框不可见
                  netUserRecordTypeComboBox.setVisible(false);
                  // 从在线音乐列表切换回用户列表
                  netUserPlayAllButton.setVisible(false);
                  netUserLeftBox.remove(userListBox);
                  netUserLeftBox.add(netUserCountPanel);
                  netUserLeftBox.add(netUserScrollPane);
                  // 添加回工具栏的搜索
                  netUserToolBar.remove(1);
                  netUserToolBar.add(netUserSearchTextField);
                  netUserToolBar.add(netUserClearInputButton);
                  netUserToolBar.add(netUserSearchButton);
                  // 添加回工具栏的搜索/标题
                  netUserToolBar.removeAll();
                  netUserToolBar.add(netUserBackwardButton);
                  if (currFollowUserUserInfo == null && currFanUserUserInfo == null
                          && currUserPlaylistInfo == null && currUserMvInfo == null && currUserRadioInfo == null
                          && currUserCommentInfo == null && currSubscriberPlaylistInfo == null
                          && currSubscriberRadioInfo == null && currUserArtistInfo == null
                          && currAuthorMusicInfo == null && currAuthorAlbumInfo == null) {
                      // 删除标题标签
                      netUserToolBar.add(netUserSearchTextField);
                      netUserToolBar.add(netUserClearInputButton);
                      netUserToolBar.add(netUserSearchButton);
                      netUserSourceComboBox.setVisible(true);
                  } else {
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                  }
                  // 更新用户歌曲数量显示
  //                netCurrUserLabel.setText("");
                  netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
  //                netUserBackwardButton.setEnabled(false);
              }
              // 后退到关键词面板
              else {
                  // 删除标题标签
                  netUserToolBar.removeAll();
                  netUserToolBar.add(netUserBackwardButton);
                  netUserToolBar.add(netUserSearchTextField);
                  netUserToolBar.add(netUserClearInputButton);
                  netUserToolBar.add(netUserSearchButton);
                  clearRequestForUser();
  
                  netUserListModel.clear();
                  netUserCountPanel.setVisible(false);
                  netUserLeftBox.remove(emptyHintPanel);
                  netUserLeftBox.remove(netUserScrollPane);
                  netUserLeftBox.add(netUserKeywordsPanelScrollPane);
                  netUserBackwardButton.setEnabled(false);
              }
              // 切换后一定要刷新！
              netUserLeftBox.repaint();
          });
          // 清除输入事件
          netUserClearInputButton.addActionListener(e -> {
              netUserSearchTextField.requestFocus();
              netUserSearchTextField.setText("");
              netUserBackwardButton.requestFocus();
          });
          // 搜索用户按钮事件
          netUserSearchButton.addActionListener(e -> {
              netUserCurrKeyword = netUserSearchTextField.getText().trim();
              if (!netUserCurrKeyword.isEmpty()) {
                  // 关键词加入搜索历史
                  DialogButton b = new DialogButton(netUserCurrKeyword);
                  b.addActionListener(event -> {
                      netUserSearchTextField.requestFocus();
                      netUserSearchTextField.setText(b.getPlainText());
                      netUserClearInputButton.setVisible(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      netUserLeftBox.add(netUserScrollPane);
                      netUserSearchButton.doClick();
                  });
                  // 右击删除历史
                  b.addMouseListener(new MouseAdapter() {
                      @Override
                      public void mouseReleased(MouseEvent e) {
                          if (e.getButton() != MouseEvent.BUTTON3) return;
                          netUserHistorySearchInnerPanel2.remove(b);
                          netUserHistorySearchInnerPanel2.repaint();
                      }
                  });
                  b.setToolTipText(REMOVE_HISTORY_KEYWORD_TIP);
                  b.setForeColor(currUIStyle.getTextColor());
                  // 先删除重复的关键词
                  removeKeywordInHistorySearch(netUserCurrKeyword, HistorySearchType.NET_USER);
                  netUserHistorySearchInnerPanel2.add(b, 0);
                  // 删除超出上限的最后一个关键词
                  int c = netUserHistorySearchInnerPanel2.getComponentCount();
                  if (c > maxSearchHistoryCount)
                      netUserHistorySearchInnerPanel2.remove(c - 1);
                  netUserHistorySearchPanel.repaint();
  
                  loadingAndRun(() -> {
                      try {
                          // 搜索用户并显示用户列表
                          CommonResult<NetUserInfo> result = MusicServerUtil.searchUsers(
                                  netUserSourceComboBox.getSelectedIndex(), netUserCurrKeyword, netUserCurrPage = 1, limit);
                          List<NetUserInfo> userInfos = result.data;
                          Integer total = result.total;
                          netUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                          netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                          netUserCountPanel.setVisible(true);
                          netUserSourceComboBox.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netUserList.setModel(emptyListModel);
                          netUserListModel.clear();
                          userInfos.forEach(userInfo -> {
                              globalExecutor.execute(() -> updateCollection(userInfo));
                              // 设置图片加载后重绘的事件
                              userInfo.setInvokeLater(() -> {
                                  updateRenderer(netUserList);
                                  updateRenderer(collectionList);
                                  netUserList.repaint();
                                  collectionList.repaint();
                              });
                              netUserListModel.addElement(userInfo);
                          });
                          netUserList.setModel(netUserListModel);
                          netUserBackwardButton.setEnabled(true);
                          netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                          if (netUserListModel.isEmpty()) {
                              netUserLeftBox.remove(netUserScrollPane);
                              netUserLeftBox.add(emptyHintPanel);
                          } else {
                              netUserLeftBox.remove(emptyHintPanel);
                              netUserLeftBox.add(netUserScrollPane);
                          }
                          netUserScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          });
          // 搜索用户跳页事件
          Runnable searchUserGoPageAction = () -> {
              boolean followUserRequest = currFollowUserUserInfo != null, fanUserRequest = currFanUserUserInfo != null,
                      playlistRequest = currUserPlaylistInfo != null, mvRequest = currUserMvInfo != null, radioRequest = currUserRadioInfo != null,
                      commentRequest = currUserCommentInfo != null, playlistSubRequest = currSubscriberPlaylistInfo != null,
                      radioSubRequest = currSubscriberRadioInfo != null, artistRequest = currUserArtistInfo != null,
                      songRequest = currAuthorMusicInfo != null, albumRequest = currAuthorAlbumInfo != null;
              if (followUserRequest || fanUserRequest || playlistSubRequest || radioSubRequest || artistRequest ||
                      playlistRequest || mvRequest || radioRequest || commentRequest || songRequest || StringUtil.notEmpty(netUserCurrKeyword)) {
                  loadingAndRun(() -> {
                      try {
                          // 搜索用户并显示用户列表
                          CommonResult<NetUserInfo> result = followUserRequest ? MusicServerUtil.getUserFollows(currFollowUserUserInfo, netUserCurrPage, limit)
                                  : fanUserRequest ? MusicServerUtil.getUserFans(currFanUserUserInfo, netUserCurrPage, limit)
                                  : playlistSubRequest ? MusicServerUtil.getPlaylistSubscribers(currSubscriberPlaylistInfo, netUserCurrPage, limit)
                                  : radioSubRequest ? MusicServerUtil.getRadioSubscribers(currSubscriberRadioInfo, netUserCurrPage, limit)
                                  : artistRequest ? MusicServerUtil.getArtistFans(currUserArtistInfo, netUserCurrPage, limit)
                                  : playlistRequest ? MusicServerUtil.getUserInfo(currUserPlaylistInfo.getSource(), currUserPlaylistInfo.getCreatorId())
                                  : mvRequest ? MusicServerUtil.getUserInfo(currUserMvInfo.getSource(), currUserMvInfo.getCreatorId())
                                  : radioRequest ? MusicServerUtil.getUserInfo(currUserRadioInfo.getSource(), currUserRadioInfo.getDjId())
                                  : commentRequest ? MusicServerUtil.getUserInfo(currUserCommentInfo.getSource(), currUserCommentInfo.getUserId())
                                  : songRequest ? MusicServerUtil.getUserInfo(currAuthorMusicInfo.getSource(), currAuthorMusicInfo.getArtistId())
                                  : albumRequest ? MusicServerUtil.getUserInfo(currAuthorAlbumInfo.getSource(), currAuthorAlbumInfo.getArtistId())
                                  : MusicServerUtil.searchUsers(netUserSourceComboBox.getSelectedIndex(), netUserCurrKeyword, netUserCurrPage, limit);
                          List<NetUserInfo> userInfos = result.data;
                          Integer total = result.total;
                          netUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                          netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                          netUserCountPanel.setVisible(true);
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netUserList.setModel(emptyListModel);
                          netUserListModel.clear();
                          userInfos.forEach(userInfo -> {
                              globalExecutor.execute(() -> updateCollection(userInfo));
                              // 设置图片加载后重绘的事件
                              userInfo.setInvokeLater(() -> {
                                  updateRenderer(netUserList);
                                  updateRenderer(collectionList);
                                  netUserList.repaint();
                                  collectionList.repaint();
                              });
                              netUserListModel.addElement(userInfo);
                          });
                          netUserList.setModel(netUserListModel);
                          netUserScrollPane.setVValue(0);
                          if (netUserListModel.isEmpty()) {
                              netUserLeftBox.remove(netUserScrollPane);
                              netUserLeftBox.add(emptyHintPanel);
                          } else {
                              netUserLeftBox.remove(emptyHintPanel);
                              netUserLeftBox.add(netUserScrollPane);
                          }
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 搜索用户内歌曲并显示在在线歌曲列表
          Runnable searchMusicInUser = () -> {
  //            if (!netMusicListForUserModel.isEmpty()) {
              loadingAndRun(() -> {
                  try {
                      NetUserInfo userInfo = netUserList.getSelectedValue();
                      CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInUser(
                              netUserRecordTypeComboBox.getSelectedIndex(), userInfo, netMusicInUserCurrPage, limit);
                      List<NetMusicInfo> musicInfos = result.data;
                      Integer total = result.total;
                      netMusicInUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netMusicInUserCurrPage, netMusicInUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netMusicList.setModel(emptyListModel);
                      netMusicListForUserModel.clear();
                      musicInfos.forEach(musicInfo -> {
                          globalExecutor.execute(() -> updateCollection(musicInfo));
                          netMusicListForUserModel.addElement(musicInfo);
                      });
                      netMusicList.setModel(netMusicListForUserModel);
                      netMusicScrollPane.setVValue(0);
                      if (netMusicListForUserModel.isEmpty()) {
                          userListCountBox.remove(netMusicScrollPane);
                          userListCountBox.add(emptyHintPanel);
                      } else {
                          userListCountBox.remove(emptyHintPanel);
                          userListCountBox.add(netMusicScrollPane);
                      }
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
  //            }
          };
          recordTypeComboBoxModel.addElement(I18n.getText("recentWeek"));
          recordTypeComboBoxModel.addElement(I18n.getText("allTime"));
          orderComboBoxModel.addElement(I18n.getText("latest"));
          orderComboBoxModel.addElement(I18n.getText("mostPlayed"));
          sortTypeComboBoxModel.addElement(I18n.getText("ascend"));
          sortTypeComboBoxModel.addElement(I18n.getText("descend"));
          netUserRecordTypeComboBox.setModel(recordTypeComboBoxModel);
          // 用户播放记录类型
          netUserRecordTypeComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (netUserRecordTypeComboBox.getItemCount() <= 1
                      || netUserRecordTypeComboBox.getSelectedItem() == null
                      || e.getStateChange() != ItemEvent.SELECTED) return;
              netMusicInUserCurrPage = 1;
              searchMusicInUser.run();
          });
          // 播放全部
          netUserPlayAllButton.addActionListener(e -> netUserPlayAllMenuItem.doClick());
          // 刷新按钮事件
          netUserRefreshButton.addActionListener(e -> {
              Component lc = netUserLeftBox.getComponent(netUserLeftBox.getComponentCount() - 1);
              // 当前显示的是用户列表，刷新用户
              if (lc == netUserScrollPane || lc == emptyHintPanel) {
                  searchUserGoPageAction.run();
              }
              // 当前显示的是某用户的歌曲，刷新歌曲
              else {
                  searchMusicInUser.run();
              }
          });
          // 第一页按钮事件
          netUserStartPageButton.addActionListener(e -> {
              Component lc = netUserLeftBox.getComponent(netUserLeftBox.getComponentCount() - 1);
              // 当前显示的是用户列表，跳到第一页用户
              if (lc == netUserScrollPane || lc == emptyHintPanel) {
                  if (netUserCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netUserCurrPage = 1;
                  searchUserGoPageAction.run();
              }
              // 当前显示的是某用户的歌曲，跳到第一页歌曲
              else {
                  if (netMusicInUserCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInUserCurrPage = 1;
                  searchMusicInUser.run();
              }
          });
          // 上一页按钮事件
          netUserLastPageButton.addActionListener(e -> {
              Component lc = netUserLeftBox.getComponent(netUserLeftBox.getComponentCount() - 1);
              // 当前显示的是用户列表，跳到上一页用户
              if (lc == netUserScrollPane || lc == emptyHintPanel) {
                  if (netUserCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netUserCurrPage--;
                  searchUserGoPageAction.run();
              }
              // 当前显示的是某用户的歌曲，跳到上一页歌曲
              else {
                  if (netMusicInUserCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInUserCurrPage--;
                  searchMusicInUser.run();
              }
          });
          // 下一页按钮事件
          netUserNextPageButton.addActionListener(e -> {
              Component lc = netUserLeftBox.getComponent(netUserLeftBox.getComponentCount() - 1);
              // 当前显示的是用户列表，跳到下一页用户
              if (lc == netUserScrollPane || lc == emptyHintPanel) {
                  if (netUserCurrPage >= netUserMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netUserCurrPage++;
                  searchUserGoPageAction.run();
              }
              // 当前显示的是某用户的歌曲，跳到下一页歌曲
              else {
                  if (netMusicInUserCurrPage >= netMusicInUserMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInUserCurrPage++;
                  searchMusicInUser.run();
              }
          });
          // 最后一页按钮事件
          netUserEndPageButton.addActionListener(e -> {
              Component lc = netUserLeftBox.getComponent(netUserLeftBox.getComponentCount() - 1);
              // 当前显示的是用户列表，跳到最后一页用户
              if (lc == netUserScrollPane || lc == emptyHintPanel) {
                  if (netUserCurrPage >= netUserMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netUserCurrPage = netUserMaxPage;
                  searchUserGoPageAction.run();
              }
              // 当前显示的是某用户的歌曲，跳到最后一页歌曲
              else {
                  if (netMusicInUserCurrPage >= netMusicInUserMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInUserCurrPage = netMusicInUserMaxPage;
                  searchMusicInUser.run();
              }
          });
          // 跳页按钮事件
          netUserGoButton.addActionListener(e -> {
              Component lc = netUserLeftBox.getComponent(netUserLeftBox.getComponentCount() - 1);
              // 当前显示的是用户列表，跳页用户
              if (lc == netUserScrollPane || lc == emptyHintPanel) {
                  String text = netUserPageTextField.getText();
                  netUserPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netUserMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netUserCurrPage = destPage;
                  searchUserGoPageAction.run();
              }
              // 当前显示的是某用户的歌曲，跳页歌曲
              else {
                  String text = netUserPageTextField.getText();
                  netUserPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netMusicInUserMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInUserCurrPage = destPage;
                  searchMusicInUser.run();
              }
          });
          // 按钮被禁止的图标
          netUserBackwardButton.setEnabled(false);
          // 按钮初始不可见
          netUserClearInputButton.setVisible(false);
          netUserPlayAllButton.setVisible(false);
          netUserRecordTypeComboBox.setVisible(false);
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netUserRefreshButton.setPreferredSize(dimension);
          netUserStartPageButton.setPreferredSize(dimension);
          netUserLastPageButton.setPreferredSize(dimension);
          netUserGoButton.setPreferredSize(dimension);
          netUserNextPageButton.setPreferredSize(dimension);
          netUserEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netUserBackwardButton.addMouseListener(new ButtonMouseListener(netUserBackwardButton, THIS));
          netUserClearInputButton.addMouseListener(new ButtonMouseListener(netUserClearInputButton, THIS));
          netUserSearchButton.addMouseListener(new ButtonMouseListener(netUserSearchButton, THIS));
          netUserPlayAllButton.addMouseListener(new ButtonMouseListener(netUserPlayAllButton, THIS));
          netUserRefreshButton.addMouseListener(new ButtonMouseListener(netUserRefreshButton, THIS));
          netUserStartPageButton.addMouseListener(new ButtonMouseListener(netUserStartPageButton, THIS));
          netUserLastPageButton.addMouseListener(new ButtonMouseListener(netUserLastPageButton, THIS));
          netUserGoButton.addMouseListener(new ButtonMouseListener(netUserGoButton, THIS));
          netUserNextPageButton.addMouseListener(new ButtonMouseListener(netUserNextPageButton, THIS));
          netUserEndPageButton.addMouseListener(new ButtonMouseListener(netUserEndPageButton, THIS));
          // 帮助提示
          netUserBackwardButton.setToolTipText(BACKWARD_TIP);
          netUserClearInputButton.setToolTipText(CLEAR_INPUT_TIP);
          netUserSearchButton.setToolTipText(SEARCH_TIP);
          netUserPlayAllButton.setToolTipText(PLAY_ALL_TIP);
          netUserRefreshButton.setToolTipText(REFRESH_TIP);
          netUserStartPageButton.setToolTipText(START_PAGE_TIP);
          netUserLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netUserGoButton.setToolTipText(GO_TIP);
          netUserNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netUserEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netUserPlayAllButton.setIconTextGap(5);
  
          netUserToolBar.add(netUserBackwardButton);
          netUserToolBar.add(netUserSearchTextField);
          netUserToolBar.add(netUserClearInputButton);
          netUserToolBar.add(netUserSearchButton);
          netUserLeftBox.add(netUserToolBar);
  
          for (String name : NetMusicSource.NAMES) netUserSourceComboBox.addItem(name);
          netUserSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netUserSearchButton.doClick();
          });
  
          // 用户数量和当前歌曲标签
          netUserCountPanel.add(netUserSourceComboBox);
          netUserCountPanel.add(netUserRecordTypeComboBox);
          netUserCountPanel.add(netUserPlayAllButton);
          netUserCountPanel.add(netUserStartPageButton);
          netUserCountPanel.add(netUserLastPageButton);
          netUserCountPanel.add(netUserCountLabel);
          netUserCountPanel.add(netUserNextPageButton);
          netUserCountPanel.add(netUserEndPageButton);
          netUserCountPanel.add(netUserRefreshButton);
          netUserCountPanel.add(netUserPageTextField);
          netUserCountPanel.add(netUserGoButton);
          netUserCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netUserCountPanel.setVisible(false);
          netUserLeftBox.add(netUserCountPanel);
      }
  
      // 初始化在线用户列表
      private void initNetUserList() {
          netUserList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          netUserList.setModel(netUserListModel);
          netUserList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netUserList.locationToIndex(e.getPoint());
                  Rectangle bounds = netUserList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetUserListRenderer renderer = (NetUserListRenderer) netUserList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netUserList.repaint();
              }
          });
          netUserList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetUserListRenderer renderer = (NetUserListRenderer) netUserList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netUserList.repaint();
              }
          });
          // 打开用户
          Runnable openUserAction = () -> {
              NetUserInfo userInfo = netUserList.getSelectedValue();
              if (userInfo == null) return;
              loadingAndRun(() -> {
                  List<Future<?>> taskList = new LinkedList<>();
  
                  // 检查收藏按钮
                  checkDescriptionCollectionStatus(userDescriptionCollectionButton, userInfo);
  
                  // 加载封面图片和描述
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                      ImageIcon icon = new ImageIcon(coverImg);
                      userCoverAndNameLabel.setIcon(icon);
                      userCoverAndNameLabel.setText(LOADING_MSG);
                      userTagLabel.setText(LOADING_MSG);
                      userDescriptionLabel.setText(LOADING_MSG);
                      userDescriptionLabel.setIcon(icon);
                      GlobalExecutors.requestExecutor.execute(() -> {
                          try {
                              MusicServerUtil.fillUserInfo(userInfo);
                              updateRenderer(netUserList);
                              userCoverAndNameLabel.setText(StringUtil.textToHtml(userInfo.getName()));
                              userTagLabel.setText(StringUtil.textToHtml(userInfo.getTag()));
                              if (userInfo.getSign() != null)
                                  userDescriptionLabel.setText(StringUtil.textToHtml(userInfo.getSign()));
                              if (userInfo.hasAvatar()) {
                                  userCoverAndNameLabel.setIcon(new ImageIcon(
                                          ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(userInfo.getAvatar(), coverImageWidth), MIDDLE_ARC))
                                  ));
                              } else {
                                  userInfo.setInvokeLater(() -> {
                                      userCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(userInfo.getAvatar(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                      netUserList.repaint();
                                  });
                              }
                              if (userInfo.hasBgImg()) {
                                  userDescriptionLabel.setIcon(new ImageIcon(
                                          ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(userInfo.getBgImg(), coverImageWidth), MIDDLE_ARC))
                                  ));
                              } else {
                                  userInfo.setInvokeLater2(() -> {
                                      userDescriptionLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(userInfo.getBgImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                      netUserList.repaint();
                                  });
                              }
                          } catch (HttpException e) {
                              userCoverAndNameLabel.setText(LOAD_FAILED);
                              userTagLabel.setText(LOAD_FAILED);
                              userDescriptionLabel.setText(LOAD_FAILED);
                          }
                      });
                  }));
  
                  taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                      try {
                          // 网易云/猫耳/哔哩用户才显示下拉框
                          if (userInfo.fromNetCloud()) netUserRecordTypeComboBox.setModel(recordTypeComboBoxModel);
                          else if (userInfo.fromME() || userInfo.fromBI())
                              netUserRecordTypeComboBox.setModel(orderComboBoxModel);
                          else if (userInfo.fromXM())
                              netUserRecordTypeComboBox.setModel(sortTypeComboBoxModel);
  
                          // 得到用户的音乐信息
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInUser(
                                  netUserRecordTypeComboBox.getSelectedIndex(), userInfo, netMusicInUserCurrPage = 1, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netMusicInUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          netUserRecordTypeComboBox.setVisible(userInfo.fromNetCloud() || userInfo.fromME() || userInfo.fromXM() || userInfo.fromBI());
                          // 更新用户歌曲数量显示
                          netUserCountLabel.setText(String.format(PAGINATION_MSG, netMusicInUserCurrPage, netMusicInUserMaxPage));
                          userListCountBox.add(netUserCountPanel);
                          userListCountBox.add(netMusicScrollPane);
                          userListBox.add(userListCountBox);
                          // 用户列表切换到在线音乐列表
                          netMusicList.setModel(netMusicListForUserModel);
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(userListBox);
                          // 删除工具栏的搜索
                          netUserToolBar.remove(netUserSearchTextField);
                          netUserToolBar.remove(netUserClearInputButton);
                          netUserToolBar.remove(netUserSearchButton);
                          netUserToolBar.remove(netUserTitleLabel);
                          netUserToolBar.add(Box.createHorizontalGlue());
                          // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                          netMusicList.setModel(emptyListModel);
                          netMusicListForUserModel.clear();
                          musicInfos.forEach(musicInfo -> {
                              globalExecutor.execute(() -> updateCollection(musicInfo));
                              netMusicListForUserModel.addElement(musicInfo);
                          });
                          netMusicList.setModel(netMusicListForUserModel);
                          userDescriptionScrollPane.setVValue(0);
                          netMusicScrollPane.setVValue(0);
                          netUserBackwardButton.setEnabled(true);
                          netUserSourceComboBox.setVisible(false);
                          netUserPlayAllButton.setVisible(true);
                          if (netMusicListForUserModel.isEmpty()) {
                              userListCountBox.remove(netMusicScrollPane);
                              userListCountBox.add(emptyHintPanel);
                          } else {
                              userListCountBox.remove(emptyHintPanel);
                              userListCountBox.add(netMusicScrollPane);
                          }
                          // 切换后一定要刷新！
                          netUserLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  }));
  
                  taskList.forEach(task -> {
                      try {
                          task.get();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } catch (ExecutionException e) {
                          e.printStackTrace();
                      }
                  });
              });
          };
          netUserList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      openUserAction.run();
                  }
              }
          });
          netUserList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开用户
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      openUserAction.run();
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netUserList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netUserList.locationToIndex(e.getPoint());
                          if (index == -1) return;
                          if (!netUserList.isSelectedIndex(index)) netUserList.setSelectedIndex(index);
                          if (hasBeenCollected(netUserList.getSelectedValue())) {
                              netUserCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                              netUserCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                          } else {
                              netUserCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                              netUserCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                          }
  
                          netUserPopupMenu.show(netUserList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 打开用户
          netUserOpenMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              if (index == TabIndex.NET_USER) openUserAction.run();
              else if (index == TabIndex.PERSONAL) openCollectionItemAction.run();
              else openRecommendItemAction.run();
          });
          // 播放全部
          netUserPlayAllMenuItem.addActionListener(e -> {
              NetUserInfo userInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_USER) userInfo = netUserList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL)
                  userInfo = (NetUserInfo) collectionList.getSelectedValue();
              else userInfo = (NetUserInfo) itemRecommendList.getSelectedValue();
              loadingAndRun(() -> {
                  CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInUser(
                          netUserRecordTypeComboBox.getSelectedIndex(), userInfo, 1, 10000);
                  List<NetMusicInfo> musicInfos = result.data;
                  if (musicInfos.isEmpty()) {
                      new TipDialog(THIS, NO_MUSIC_MSG).showDialog();
                      return;
                  }
                  playQueue.setModel(emptyListModel);
                  playQueueModel.clear();
                  musicInfos.forEach(musicInfo -> {
                      globalExecutor.execute(() -> updateCollection(musicInfo));
                      playQueueModel.addElement(musicInfo);
                  });
                  playQueue.setModel(playQueueModel);
                  playQueue.setSelectedIndex(0);
                  playExecutor.execute(() -> playSelected(playQueue, true));
              });
          });
          // 收藏用户
          netUserCollectMenuItem.addActionListener(e -> {
              int index = tabbedPane.getSelectedIndex();
              CustomList list;
              if (index == TabIndex.NET_USER) list = netUserList;
              else if (index == TabIndex.PERSONAL) list = collectionList;
              else list = itemRecommendList;
              ListModel model = collectionList.getModel();
              boolean needRefresh = model == userCollectionModel;
              if (netUserCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT) && list.isShowing()
                      || list == netUserList && userDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == itemRecommendList && recommendItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)
                      || list == collectionList && collectionItemDescriptionCollectionButton.getText().equals(COLLECT_TIP)) {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  // 描述面板取消收藏之后，使用之前保存的对象
                  boolean useOpenResource = values.isEmpty() && !hasBeenCollected(collectionOpenResource);
                  if (useOpenResource) {
                      userCollectionModel.add(0, collectionOpenResource);
                  } else {
                      for (int i = values.size() - 1; i >= 0; i--) {
                          NetUserInfo userInfo = (NetUserInfo) values.get(i);
                          if (hasBeenCollected(userInfo)) continue;
                          userCollectionModel.add(0, userInfo);
                      }
                  }
                  if (needRefresh) collectionList.setModel(model);
                  if (useOpenResource) collectionList.setSelectedIndex(0);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List values = list.getSelectedValuesList();
                  if (needRefresh) collectionList.setModel(emptyListModel);
                  values.forEach(o -> {
                      if (list == collectionList || hasBeenCollected((Resource) o)) {
                          userCollectionModel.removeElement(o);
                      }
                  });
                  if (needRefresh) collectionList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 查看用户歌单
          netUserPlaylistMenuItem.addActionListener(e -> {
              NetUserInfo userInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_USER) userInfo = netUserList.getSelectedValue();
              else userInfo = (NetUserInfo) collectionList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForPlaylist();
                      // 搜索用户歌单
                      CommonResult<NetPlaylistInfo> result = MusicServerUtil.getUserPlaylists(currPlaylistUserInfo = userInfo, netPlaylistCurrPage = 1, limit);
                      List<NetPlaylistInfo> playlistInfos = result.data;
                      int total = result.total;
                      netPlaylistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netPlaylistTitleLabel.setText(StringUtil.textToHtml(userInfo.getName() + I18n.getText("playlistSuffix")));
                      netPlaylistToolBar.removeAll();
                      netPlaylistToolBar.add(netPlaylistBackwardButton);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      netPlaylistToolBar.add(netPlaylistTitleLabel);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netPlaylistCurrPage, netPlaylistMaxPage));
                      netPlaylistCountPanel.add(netPlaylistCountLabel, netPlaylistCountPanel.getComponentIndex(netPlaylistCountLabel));
                      netPlaylistLeftBox.add(netPlaylistCountPanel);
                      netPlaylistSourceComboBox.setVisible(false);
                      netPlaylistPlayAllButton.setVisible(false);
                      netPlaylistCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netPlaylistList.setModel(emptyListModel);
                      netPlaylistListModel.clear();
                      playlistInfos.forEach(playlistInfo -> {
                          globalExecutor.execute(() -> updateCollection(playlistInfo));
                          // 设置图片加载后重绘的事件
                          playlistInfo.setInvokeLater(() -> {
                              updateRenderer(netPlaylistList);
                              updateRenderer(collectionList);
                              netPlaylistList.repaint();
                              collectionList.repaint();
                          });
                          netPlaylistListModel.addElement(playlistInfo);
                      });
                      netPlaylistList.setModel(netPlaylistListModel);
                      netPlaylistBackwardButton.setEnabled(true);
                      netPlaylistLeftBox.remove(netPlaylistKeywordsPanelScrollPane);
                      if (netPlaylistListModel.isEmpty()) {
                          netPlaylistLeftBox.remove(netPlaylistScrollPane);
                          netPlaylistLeftBox.add(emptyHintPanel);
                      } else {
                          netPlaylistLeftBox.remove(emptyHintPanel);
                          netPlaylistLeftBox.add(netPlaylistScrollPane);
                      }
                      netPlaylistScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_PLAYLIST);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看用户专辑
          netUserAlbumMenuItem.addActionListener(e -> {
              NetUserInfo userInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_USER) userInfo = netUserList.getSelectedValue();
              else userInfo = (NetUserInfo) collectionList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForAlbum();
                      // 搜索歌手专辑并显示专辑列表
                      CommonResult<NetAlbumInfo> result = MusicServerUtil.getUserAlbums(currAlbumUserInfo = userInfo, netAlbumCurrPage = 1, limit);
                      List<NetAlbumInfo> albumInfos = result.data;
                      Integer total = result.total;
                      netAlbumMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netAlbumTitleLabel.setText(StringUtil.textToHtml(userInfo.getName() + I18n.getText("albumSuffix")));
                      netAlbumToolBar.removeAll();
                      netAlbumToolBar.add(netAlbumBackwardButton);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      netAlbumToolBar.add(netAlbumTitleLabel);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netAlbumCurrPage, netAlbumMaxPage));
                      netAlbumCountPanel.add(netAlbumCountLabel, netAlbumCountPanel.getComponentIndex(netAlbumCountLabel));
                      netAlbumLeftBox.add(netAlbumCountPanel);
                      netAlbumSourceComboBox.setVisible(false);
                      netAlbumPlayAllButton.setVisible(false);
                      netAlbumCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netAlbumList.setModel(emptyListModel);
                      netAlbumListModel.clear();
                      albumInfos.forEach(albumInfo -> {
                          globalExecutor.execute(() -> updateCollection(albumInfo));
                          // 设置图片加载后重绘的事件
                          albumInfo.setInvokeLater(() -> {
                              updateRenderer(netAlbumList);
                              updateRenderer(collectionList);
                              netAlbumList.repaint();
                              collectionList.repaint();
                          });
                          netAlbumListModel.addElement(albumInfo);
                      });
                      netAlbumList.setModel(netAlbumListModel);
                      netAlbumBackwardButton.setEnabled(true);
                      netAlbumLeftBox.remove(netAlbumKeywordsPanelScrollPane);
                      if (netAlbumListModel.isEmpty()) {
                          netAlbumLeftBox.remove(netAlbumScrollPane);
                          netAlbumLeftBox.add(emptyHintPanel);
                      } else {
                          netAlbumLeftBox.remove(emptyHintPanel);
                          netAlbumLeftBox.add(netAlbumScrollPane);
                      }
                      netAlbumScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_ALBUM);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看用户电台
          netUserRadioMenuItem.addActionListener(e -> {
              NetUserInfo userInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_USER) userInfo = netUserList.getSelectedValue();
              else userInfo = (NetUserInfo) collectionList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      // 搜索用户电台
                      CommonResult<NetRadioInfo> result = MusicServerUtil.getUserRadios(currRadioUserInfo = userInfo, netRadioCurrPage = 1, limit);
                      List<NetRadioInfo> radioInfos = result.data;
                      int total = result.total;
                      netRadioMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netRadioTitleLabel.setText(StringUtil.textToHtml(userInfo.getName() + I18n.getText("radioSuffix")));
                      netRadioToolBar.removeAll();
                      netRadioToolBar.add(netRadioBackwardButton);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      netRadioToolBar.add(netRadioTitleLabel);
                      netRadioToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netRadioCountLabel.setText(String.format(PAGINATION_MSG, netRadioCurrPage, netRadioMaxPage));
                      netRadioCountPanel.add(netRadioCountLabel, netRadioCountPanel.getComponentIndex(netRadioCountLabel));
                      netRadioLeftBox.add(netRadioCountPanel);
                      netRadioSourceComboBox.setVisible(false);
                      netRadioSortTypeComboBox.setVisible(false);
                      netRadioPlayAllButton.setVisible(false);
                      netRadioCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netRadioList.setModel(emptyListModel);
                      netRadioListModel.clear();
                      radioInfos.forEach(radioInfo -> {
                          globalExecutor.execute(() -> updateCollection(radioInfo));
                          // 设置图片加载后重绘的事件
                          radioInfo.setInvokeLater(() -> {
                              updateRenderer(netRadioList);
                              updateRenderer(collectionList);
                              netRadioList.repaint();
                              collectionList.repaint();
                          });
                          netRadioListModel.addElement(radioInfo);
                      });
                      netRadioList.setModel(netRadioListModel);
                      netRadioBackwardButton.setEnabled(true);
                      netRadioLeftBox.remove(netRadioKeywordsPanelScrollPane);
                      if (netRadioListModel.isEmpty()) {
                          netRadioLeftBox.remove(netRadioScrollPane);
                          netRadioLeftBox.add(emptyHintPanel);
                      } else {
                          netRadioLeftBox.remove(emptyHintPanel);
                          netRadioLeftBox.add(netRadioScrollPane);
                      }
                      netRadioScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_RADIO);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看用户视频
          netUserVideoMenuItem.addActionListener(e -> {
              NetUserInfo userInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_USER) userInfo = netUserList.getSelectedValue();
              else userInfo = (NetUserInfo) collectionList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForMv();
                      // 获取用户视频
                      CommonResult<NetMvInfo> result = MusicServerUtil.getUserVideos(currMvUserInfo = userInfo,
                              netMvSortTypeComboBox.getSelectedIndex(), netMvCurrPage = 1, limit, mvCursor = "");
                      List<NetMvInfo> mvInfos = result.data;
                      Integer total = result.total;
                      mvCursor = result.cursor;
                      netMvMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netMvTitleLabel.setText(StringUtil.textToHtml(userInfo.getName() + I18n.getText("videoSuffix")));
                      netMvToolBar.removeAll();
                      netMvToolBar.add(netMvBackwardButton);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      netMvToolBar.add(netMvTitleLabel);
                      netMvToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netMvCountLabel.setText(String.format(PAGINATION_MSG, netMvCurrPage, netMvMaxPage));
                      netMvCountPanel.add(netMvCountLabel, netMvCountPanel.getComponentIndex(netMvCountLabel));
                      netMvLeftBox.add(netMvCountPanel);
                      netMvCountPanel.setVisible(true);
                      netMvSourceComboBox.setVisible(false);
                      netMvSortTypeComboBox.setVisible(userInfo.fromBI());
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netMvList.setModel(emptyListModel);
                      netMvListModel.clear();
                      mvInfos.forEach(mvInfo -> {
                          globalExecutor.execute(() -> updateCollection(mvInfo));
                          // 设置图片加载后重绘的事件
                          mvInfo.setInvokeLater(() -> {
                              updateRenderer(netMvList);
                              updateRenderer(collectionList);
                              netMvList.repaint();
                              collectionList.repaint();
                          });
                          netMvListModel.addElement(mvInfo);
                      });
                      netMvList.setModel(netMvListModel);
                      netMvBackwardButton.setEnabled(true);
                      netMvLeftBox.remove(netMvKeywordsPanelScrollPane);
                      if (netMvListModel.isEmpty()) {
                          netMvLeftBox.remove(netMvScrollPane);
                          netMvLeftBox.add(emptyHintPanel);
                      } else {
                          netMvLeftBox.remove(emptyHintPanel);
                          netMvLeftBox.add(netMvScrollPane);
                      }
                      netMvScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_MV);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看用户关注
          netUserFollowMenuItem.addActionListener(e -> {
              NetUserInfo userInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_USER) userInfo = netUserList.getSelectedValue();
              else userInfo = (NetUserInfo) collectionList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForUser();
                      // 获取用户关注
                      CommonResult<NetUserInfo> result = MusicServerUtil.getUserFollows(currFollowUserUserInfo = userInfo, netUserCurrPage = 1, limit);
                      List<NetUserInfo> userInfos = result.data;
                      Integer total = result.total;
                      netUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netUserTitleLabel.setText(StringUtil.textToHtml(userInfo.getName() + I18n.getText("followSuffix")));
                      netUserToolBar.removeAll();
                      netUserToolBar.add(netUserBackwardButton);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      netUserLeftBox.add(netUserCountPanel);
                      netUserSourceComboBox.setVisible(false);
                      netUserPlayAllButton.setVisible(false);
                      netUserRecordTypeComboBox.setVisible(false);
                      netUserCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netUserList.setModel(emptyListModel);
                      netUserListModel.clear();
                      userInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netUserList);
                              updateRenderer(collectionList);
                              netUserList.repaint();
                              collectionList.repaint();
                          });
                          netUserListModel.addElement(info);
                      });
                      netUserList.setModel(netUserListModel);
                      netUserBackwardButton.setEnabled(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      if (netUserListModel.isEmpty()) {
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(emptyHintPanel);
                      } else {
                          netUserLeftBox.remove(emptyHintPanel);
                          netUserLeftBox.add(netUserScrollPane);
                      }
                      netUserScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看用户粉丝
          netUserFanMenuItem.addActionListener(e -> {
              NetUserInfo userInfo;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_USER) userInfo = netUserList.getSelectedValue();
              else userInfo = (NetUserInfo) collectionList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForUser();
                      // 获取用户粉丝
                      CommonResult<NetUserInfo> result = MusicServerUtil.getUserFans(currFanUserUserInfo = userInfo, netUserCurrPage = 1, limit);
                      List<NetUserInfo> userInfos = result.data;
                      Integer total = result.total;
                      netUserMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netUserTitleLabel.setText(StringUtil.textToHtml(userInfo.getName() + I18n.getText("fanSuffix")));
                      netUserToolBar.removeAll();
                      netUserToolBar.add(netUserBackwardButton);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      netUserLeftBox.add(netUserCountPanel);
                      netUserSourceComboBox.setVisible(false);
                      netUserPlayAllButton.setVisible(false);
                      netUserRecordTypeComboBox.setVisible(false);
                      netUserCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netUserList.setModel(emptyListModel);
                      netUserListModel.clear();
                      userInfos.forEach(info -> {
                          globalExecutor.execute(() -> updateCollection(info));
                          // 设置图片加载后重绘的事件
                          info.setInvokeLater(() -> {
                              updateRenderer(netUserList);
                              updateRenderer(collectionList);
                              netUserList.repaint();
                              collectionList.repaint();
                          });
                          netUserListModel.addElement(info);
                      });
                      netUserList.setModel(netUserListModel);
                      netUserBackwardButton.setEnabled(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      if (netUserListModel.isEmpty()) {
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(emptyHintPanel);
                      } else {
                          netUserLeftBox.remove(emptyHintPanel);
                          netUserLeftBox.add(netUserScrollPane);
                      }
                      netUserScrollPane.setVValue(0);
                      tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 复制名称
          netUserCopyNameMenuItem.addActionListener(e -> {
              NetResource resource;
              int selectedIndex = tabbedPane.getSelectedIndex();
              if (selectedIndex == TabIndex.NET_USER) resource = netUserList.getSelectedValue();
              else if (selectedIndex == TabIndex.PERSONAL) resource = collectionList.getSelectedValue();
              else resource = itemRecommendList.getSelectedValue();
              copyToClipboard(((NetUserInfo) resource).toSimpleString());
          });
          // 用户列表右键菜单项
          netUserPopupMenu.add(netUserOpenMenuItem);
          netUserPopupMenu.add(netUserPlayAllMenuItem);
          netUserPopupMenu.add(netUserCollectMenuItem);
          netUserPopupMenu.add(netUserPlaylistMenuItem);
          netUserPopupMenu.add(netUserAlbumMenuItem);
          netUserPopupMenu.add(netUserRadioMenuItem);
          netUserPopupMenu.add(netUserVideoMenuItem);
          netUserPopupMenu.add(netUserFollowMenuItem);
          netUserPopupMenu.add(netUserFanMenuItem);
          netUserPopupMenu.add(netUserCopyNameMenuItem);
  
          // 搜索历史面板
          // 清空搜索历史按钮
          netUserClearHistorySearchButton.addActionListener(e -> netUserHistorySearchInnerPanel2.removeAll());
          netUserClearHistorySearchButton.setPreferredSize(new Dimension(30, 30));
          netUserClearHistorySearchButton.setToolTipText(CLEAR_HISTORY_SEARCH_TIP);
          netUserClearHistorySearchButton.addMouseListener(new ButtonMouseListener(netUserClearHistorySearchButton, THIS));
  
          netUserHistorySearchInnerPanel1.setVisible(false);
          netUserHistorySearchInnerPanel1.add(netUserHistorySearchLabel);
          netUserHistorySearchInnerPanel1.add(netUserClearHistorySearchButton);
          netUserHistorySearchInnerPanel1.setLayout(new FlowLayout(FlowLayout.LEFT));
          netUserHistorySearchInnerPanel1.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netUserHistorySearchInnerPanel1.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netUserHistorySearchInnerPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
          netUserHistorySearchInnerPanel2.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
          netUserHistorySearchInnerPanel2.addContainerListener(new ContainerAdapter() {
              @Override
              public void componentAdded(ContainerEvent e) {
                  netUserHistorySearchInnerPanel1.setVisible(true);
              }
  
              @Override
              public void componentRemoved(ContainerEvent e) {
                  if (netUserHistorySearchInnerPanel2.getComponentCount() == 0)
                      netUserHistorySearchInnerPanel1.setVisible(false);
              }
          });
          netUserHistorySearchPanel.setLayout(new BoxLayout(netUserHistorySearchPanel, BoxLayout.Y_AXIS));
          netUserHistorySearchPanel.add(netUserHistorySearchInnerPanel1);
          netUserHistorySearchPanel.add(netUserHistorySearchInnerPanel2);
  
          // 关键词面板
          netUserKeywordsPanel.setLayout(new BoxLayout(netUserKeywordsPanel, BoxLayout.Y_AXIS));
          netUserKeywordsPanel.add(netUserHistorySearchPanel);
  
          // 同时限制面板与滚动条的大小才能保证按钮不超出窗口！！！
          Dimension d = new Dimension(900, 500);
          netUserKeywordsPanel.setPreferredSize(d);
          netUserKeywordsPanelScrollPane.setPreferredSize(d);
  
          // 用户最佳大小
          netUserScrollPane.setPreferredSize(new Dimension(200, 600));
          netUserLeftBox.add(netUserScrollPane);
      }
  
      // cursor 用于请求下一页数据
      private String commentCursor;
      private String mvCursor;
  
      // 获取评论
      private void getComments(NetResource resource, boolean first) {
          loadingAndRun(() -> {
              try {
                  // 获取评论并显示评论列表
                  CommonResult<NetCommentInfo> result = MusicServerUtil.getComments(
                          currCommentResource = resource, (String) netCommentTypeComboBox.getSelectedItem(),
                          first ? netCommentCurrPage = 1 : netCommentCurrPage, commentLimit, first ? commentCursor = "" : commentCursor);
                  List<NetCommentInfo> commentInfos = result.data;
                  Integer total = result.total;
                  commentCursor = result.cursor;
                  netCommentMaxPage = Math.max(total % commentLimit == 0 ? total / commentLimit : total / commentLimit + 1, 1);
                  // 更新标题和数量显示
                  String s = null;
                  if (resource instanceof NetMusicInfo) s = ((NetMusicInfo) resource).toSimpleString();
                  else if (resource instanceof NetPlaylistInfo) s = ((NetPlaylistInfo) resource).toSimpleString();
                  else if (resource instanceof NetAlbumInfo) s = ((NetAlbumInfo) resource).toSimpleString();
                  else if (resource instanceof NetRadioInfo) s = ((NetRadioInfo) resource).toSimpleString();
                  else if (resource instanceof NetMvInfo) s = ((NetMvInfo) resource).toSimpleString();
                  else if (resource instanceof NetRankingInfo) s = ((NetRankingInfo) resource).toSimpleString();
                  netCommentTitleLabel.setText(StringUtil.textToHtml(s + I18n.getText("commentSuffix")));
                  netCommentCountLabel.setText(String.format(PAGINATION_MSG, netCommentCurrPage, netCommentMaxPage));
                  netCommentCountPanel.setVisible(true);
                  // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                  netCommentList.setModel(emptyListModel);
                  netCommentListModel.clear();
                  commentInfos.forEach(commentInfo -> {
                      // 设置图片加载后重绘的事件
                      commentInfo.setInvokeLater(() -> {
                          updateRenderer(netCommentList);
                          netCommentList.repaint();
                      });
                      netCommentListModel.addElement(commentInfo);
                  });
                  netCommentList.setModel(netCommentListModel);
                  if (netCommentListModel.isEmpty()) {
                      netCommentBox.remove(netCommentScrollPane);
                      emptyHintPanelParent = emptyHintPanel.getParent();
                      netCommentBox.add(emptyHintPanel);
                  } else {
                      netCommentBox.remove(emptyHintPanel);
                      netCommentBox.add(netCommentScrollPane);
                  }
                  netCommentScrollPane.setVValue(0);
                  if (first) {
                      // 删除 Tab 面板、歌词面板、乐谱面板，加入评论面板
                      globalPanel.remove(infoAndLrcBox);
                      globalPanel.remove(tabbedPane);
                      globalPanel.remove(netSheetBox);
                      globalPanel.add(netCommentBox, BorderLayout.CENTER);
                      netCommentBox.repaint();
                      netSheetListModel.clear();
                      if (currPane != MusicPane.SHEET) lastPane = currPane;
                      currPane = MusicPane.COMMENT;
                  }
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          });
      }
  
      private Container emptyHintPanelParent;
  
      // 获取乐谱
      private void getSheets(NetMusicInfo info, boolean first) {
          loadingAndRun(() -> {
              try {
                  // 获取乐谱并显示乐谱列表
                  CommonResult<NetSheetInfo> result = MusicServerUtil.getSheets(currSheetMusicInfo = info);
                  List<NetSheetInfo> sheetInfos = result.data;
                  netSheetCurrPage = netSheetMaxPage = 1;
                  // 更新标题和数量显示
                  netSheetTitleLabel.setText(StringUtil.textToHtml(info.toSimpleString() + I18n.getText("sheetSuffix")));
                  netSheetCountLabel.setText(String.format(PAGINATION_MSG, netSheetCurrPage, netSheetMaxPage));
                  netSheetCountPanel.setVisible(true);
                  // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                  netSheetList.setModel(emptyListModel);
                  netSheetListModel.clear();
                  sheetInfos.forEach(SheetInfo -> {
                      // 设置图片加载后重绘的事件
                      SheetInfo.setInvokeLater(() -> {
                          updateRenderer(netSheetList);
                          netSheetList.repaint();
                      });
                      netSheetListModel.addElement(SheetInfo);
                  });
                  netSheetList.setModel(netSheetListModel);
                  if (netSheetListModel.isEmpty()) {
                      netSheetBox.remove(netSheetScrollPane);
                      emptyHintPanelParent = emptyHintPanel.getParent();
                      netSheetBox.add(emptyHintPanel);
                  } else {
                      netSheetBox.remove(emptyHintPanel);
                      netSheetBox.add(netSheetScrollPane);
                  }
                  netSheetScrollPane.setVValue(0);
                  if (first) {
                      // 删除 Tab 面板、歌词面板、评论面板，加入乐谱面板
                      globalPanel.remove(infoAndLrcBox);
                      globalPanel.remove(tabbedPane);
                      globalPanel.remove(netCommentBox);
                      globalPanel.add(netSheetBox, BorderLayout.CENTER);
                      netSheetBox.repaint();
                      netCommentListModel.clear();
                      if (currPane != MusicPane.COMMENT) lastPane = currPane;
                      currPane = MusicPane.SHEET;
                  }
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          });
      }
  
      // 初始化评论工具栏
      private void initNetCommentToolBar() {
          // 只能输入数字
          netCommentPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 后退按钮事件
          netCommentBackwardButton.addActionListener(e -> {
              netCommentBackwardButton.setDrawBg(false);
              // 归还占有的空白提示面板
              if (emptyHintPanelParent != null) {
                  emptyHintPanelParent.add(emptyHintPanel);
                  emptyHintPanelParent = null;
              }
              netCommentList.setModel(emptyListModel);
              if (!netCommentListModel.isEmpty()) netCommentListModel.clear();
              globalPanel.remove(netCommentBox);
              if (lastPane == MusicPane.MUSIC) {
                  globalPanel.add(tabbedPane, BorderLayout.CENTER);
              } else if (lastPane == MusicPane.LYRIC) {
                  if (nextLrc != NextLrc.BAD_FORMAT) lrcScrollAnimation = true;
                  globalPanel.add(infoAndLrcBox, BorderLayout.CENTER);
              }
              globalPanel.repaint();
              currPane = lastPane;
          });
          // 添加评论类型
          netCommentTypeComboBox.addItem(I18n.getText("hotComment"));
          netCommentTypeComboBox.addItem(I18n.getText("newComment"));
          // 评论类型
          netCommentTypeComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (netCommentTypeComboBox.getItemCount() <= 1
                      || netCommentTypeComboBox.getSelectedItem() == null
                      || e.getStateChange() != ItemEvent.SELECTED) return;
              netCommentCurrPage = 1;
              getComments(currCommentResource, false);
          });
          // 刷新按钮事件
          netCommentRefreshButton.addActionListener(e -> {
              getComments(currCommentResource, false);
          });
          // 第一页按钮事件
          netCommentStartPageButton.addActionListener(e -> {
              if (netCommentCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netCommentCurrPage = 1;
              getComments(currCommentResource, false);
          });
          // 上一页按钮事件
          netCommentLastPageButton.addActionListener(e -> {
              if (netCommentCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netCommentCurrPage--;
              getComments(currCommentResource, false);
          });
          // 下一页按钮事件
          netCommentNextPageButton.addActionListener(e -> {
              if (netCommentCurrPage >= netCommentMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netCommentCurrPage++;
              getComments(currCommentResource, false);
          });
          // 最后一页按钮事件
          netCommentEndPageButton.addActionListener(e -> {
              if (netCommentCurrPage >= netCommentMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netCommentCurrPage = netCommentMaxPage;
              getComments(currCommentResource, false);
          });
          // 跳页按钮事件
          netCommentGoButton.addActionListener(e -> {
              String text = netCommentPageTextField.getText();
              netCommentPageTextField.setText("");
              if (text.isEmpty()) return;
              int destPage = Integer.parseInt(text);
              if (destPage < 1 || destPage > netCommentMaxPage) {
                  new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                  return;
              }
              netCommentCurrPage = destPage;
              getComments(currCommentResource, false);
          });
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netCommentRefreshButton.setPreferredSize(dimension);
          netCommentStartPageButton.setPreferredSize(dimension);
          netCommentLastPageButton.setPreferredSize(dimension);
          netCommentGoButton.setPreferredSize(dimension);
          netCommentNextPageButton.setPreferredSize(dimension);
          netCommentEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netCommentBackwardButton.addMouseListener(new ButtonMouseListener(netCommentBackwardButton, THIS));
          netCommentRefreshButton.addMouseListener(new ButtonMouseListener(netCommentRefreshButton, THIS));
          netCommentStartPageButton.addMouseListener(new ButtonMouseListener(netCommentStartPageButton, THIS));
          netCommentLastPageButton.addMouseListener(new ButtonMouseListener(netCommentLastPageButton, THIS));
          netCommentGoButton.addMouseListener(new ButtonMouseListener(netCommentGoButton, THIS));
          netCommentNextPageButton.addMouseListener(new ButtonMouseListener(netCommentNextPageButton, THIS));
          netCommentEndPageButton.addMouseListener(new ButtonMouseListener(netCommentEndPageButton, THIS));
          // 帮助提示
          netCommentBackwardButton.setToolTipText(BACKWARD_TIP);
          netCommentRefreshButton.setToolTipText(REFRESH_TIP);
          netCommentStartPageButton.setToolTipText(START_PAGE_TIP);
          netCommentLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netCommentGoButton.setToolTipText(GO_TIP);
          netCommentNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netCommentEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netCommentToolBar.setBorder(BorderFactory.createEmptyBorder(0, 37, 0, 0));
          netCommentToolBar.add(netCommentBackwardButton);
          netCommentToolBar.add(Box.createHorizontalGlue());
          netCommentToolBar.add(netCommentTitleLabel);
          netCommentToolBar.add(Box.createHorizontalGlue());
          netCommentBox.add(netCommentToolBar);
  
          // 评论数量和当前歌曲标签
          netCommentCountPanel.add(netCommentTypeComboBox);
          netCommentCountPanel.add(netCommentStartPageButton);
          netCommentCountPanel.add(netCommentLastPageButton);
          netCommentCountPanel.add(netCommentCountLabel);
          netCommentCountPanel.add(netCommentNextPageButton);
          netCommentCountPanel.add(netCommentEndPageButton);
          netCommentCountPanel.add(netCommentRefreshButton);
          netCommentCountPanel.add(netCommentPageTextField);
          netCommentCountPanel.add(netCommentGoButton);
          netCommentCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netCommentCountPanel.setVisible(false);
          netCommentBox.add(netCommentCountPanel);
      }
  
      // 初始化在线评论列表
      private void initNetCommentList() {
          netCommentList.setModel(netCommentListModel);
          netCommentList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netCommentList.locationToIndex(e.getPoint());
                  Rectangle bounds = netCommentList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetCommentListRenderer renderer = (NetCommentListRenderer) netCommentList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netCommentList.repaint();
              }
          });
          netCommentList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetCommentListRenderer renderer = (NetCommentListRenderer) netCommentList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netCommentList.repaint();
              }
          });
          netCommentList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 右键弹出菜单
                  if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netCommentList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netCommentList.locationToIndex(e.getPoint());
                          if (index != -1 && !netCommentList.isSelectedIndex(index)
                                  || netCommentList.isSelectedIndex(index) && netCommentList.getSelectedIndices().length == 1) {
                              netCommentList.setSelectedIndex(index);
                          }
                          netCommentSaveProfileMenuItem.setEnabled(netCommentList.getSelectedValue().hasProfileUrl());
                          netCommentPopupMenu.show(netCommentList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 复制评论
          netCommentCopyMenuItem.addActionListener(e -> {
              NetCommentInfo commentInfo = netCommentList.getSelectedValue();
              copyToClipboard(commentInfo.toSimpleString());
          });
          // 导出用户头像
          netCommentSaveProfileMenuItem.addActionListener(e -> {
              NetCommentInfo commentInfo = netCommentList.getSelectedValue();
              if (commentInfo.hasProfileUrl()) {
                  FileChooser fileChooser = new FileChooser();
                  fileChooser.setTitle(SAVE_IMG);
                  ObservableList<FileChooser.ExtensionFilter> filters = fileChooser.getExtensionFilters();
                  // 添加可保存的图片格式
                  for (String suffix : Format.WRITE_IMAGE_TYPE_SUPPORTED) {
                      filters.add(new FileChooser.ExtensionFilter(suffix.toUpperCase(), "*." + suffix));
                  }
                  Platform.runLater(() -> {
                      File outputFile = fileChooser.showSaveDialog(null);
                      if (outputFile != null) {
                          ImageUtil.toFile(commentInfo.getProfileUrl(), outputFile);
                      }
                  });
              }
          });
          // 查看用户
          netCommentUserMenuItem.addActionListener(e -> {
              NetCommentInfo commentInfo = netCommentList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForUser();
                      // 获取评论用户
                      currUserCommentInfo = commentInfo;
                      CommonResult<NetUserInfo> result = MusicServerUtil.getUserInfo(commentInfo.getSource(), commentInfo.getUserId());
                      List<NetUserInfo> userInfos = result.data;
                      netUserCurrPage = netUserMaxPage = 1;
                      // 标题
                      netUserTitleLabel.setText(USER_COMMENTED);
                      netUserToolBar.removeAll();
                      netUserToolBar.add(netUserBackwardButton);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      netUserToolBar.add(netUserTitleLabel);
                      netUserToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netUserCountLabel.setText(String.format(PAGINATION_MSG, netUserCurrPage, netUserMaxPage));
                      netUserCountPanel.add(netUserCountLabel, netUserCountPanel.getComponentIndex(netUserCountLabel));
                      netUserLeftBox.add(netUserCountPanel);
                      netUserSourceComboBox.setVisible(false);
                      netUserPlayAllButton.setVisible(false);
                      netUserRecordTypeComboBox.setVisible(false);
                      netUserCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netUserList.setModel(emptyListModel);
                      netUserListModel.clear();
                      userInfos.forEach(userInfo -> {
                          globalExecutor.execute(() -> updateCollection(userInfo));
                          // 设置图片加载后重绘的事件
                          userInfo.setInvokeLater(() -> {
                              updateRenderer(netUserList);
                              updateRenderer(collectionList);
                              netUserList.repaint();
                              collectionList.repaint();
                          });
                          netUserListModel.addElement(userInfo);
                      });
                      netUserList.setModel(netUserListModel);
                      netUserBackwardButton.setEnabled(true);
                      netUserLeftBox.remove(netUserKeywordsPanelScrollPane);
                      if (netUserListModel.isEmpty()) {
                          netUserLeftBox.remove(netUserScrollPane);
                          netUserLeftBox.add(emptyHintPanel);
                      } else {
                          netUserLeftBox.remove(emptyHintPanel);
                          netUserLeftBox.add(netUserScrollPane);
                      }
                      netUserScrollPane.setVValue(0);
                      netUserLeftBox.remove(userListBox);
                      netCommentBackwardButton.doClick();
                      if (currPane == MusicPane.LYRIC) changePaneButton.doClick();
                      tabbedPane.setSelectedIndex(TabIndex.NET_USER);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看用户歌单
          netCommentPlaylistMenuItem.addActionListener(e -> {
              NetCommentInfo commentInfo = netCommentList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForPlaylist();
                      // 搜索用户歌单
                      CommonResult<NetPlaylistInfo> result = MusicServerUtil.getUserPlaylists(currPlaylistCommentInfo = commentInfo, netPlaylistCurrPage = 1, limit);
                      List<NetPlaylistInfo> playlistInfos = result.data;
                      int total = result.total;
                      netPlaylistMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netPlaylistTitleLabel.setText(StringUtil.textToHtml(commentInfo.getUsername() + I18n.getText("playlistSuffix")));
                      netPlaylistToolBar.removeAll();
                      netPlaylistToolBar.add(netPlaylistBackwardButton);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      netPlaylistToolBar.add(netPlaylistTitleLabel);
                      netPlaylistToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netPlaylistCountLabel.setText(String.format(PAGINATION_MSG, netPlaylistCurrPage, netPlaylistMaxPage));
                      netPlaylistCountPanel.add(netPlaylistCountLabel, netPlaylistCountPanel.getComponentIndex(netPlaylistCountLabel));
                      netPlaylistLeftBox.add(netPlaylistCountPanel);
                      netPlaylistSourceComboBox.setVisible(false);
                      netPlaylistPlayAllButton.setVisible(false);
                      netPlaylistCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netPlaylistList.setModel(emptyListModel);
                      netPlaylistListModel.clear();
                      playlistInfos.forEach(playlistInfo -> {
                          globalExecutor.execute(() -> updateCollection(playlistInfo));
                          // 设置图片加载后重绘的事件
                          playlistInfo.setInvokeLater(() -> {
                              updateRenderer(netPlaylistList);
                              updateRenderer(collectionList);
                              netPlaylistList.repaint();
                              collectionList.repaint();
                          });
                          netPlaylistListModel.addElement(playlistInfo);
                      });
                      netPlaylistList.setModel(netPlaylistListModel);
                      netPlaylistBackwardButton.setEnabled(true);
                      netPlaylistLeftBox.remove(netPlaylistKeywordsPanelScrollPane);
                      if (netPlaylistListModel.isEmpty()) {
                          netPlaylistLeftBox.remove(netPlaylistScrollPane);
                          netPlaylistLeftBox.add(emptyHintPanel);
                      } else {
                          netPlaylistLeftBox.remove(emptyHintPanel);
                          netPlaylistLeftBox.add(netPlaylistScrollPane);
                      }
                      netPlaylistScrollPane.setVValue(0);
                      netPlaylistLeftBox.remove(playlistListBox);
                      netCommentBackwardButton.doClick();
                      if (currPane == MusicPane.LYRIC) changePaneButton.doClick();
                      tabbedPane.setSelectedIndex(TabIndex.NET_PLAYLIST);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 查看用户专辑
          netCommentAlbumMenuItem.addActionListener(e -> {
              NetCommentInfo commentInfo = netCommentList.getSelectedValue();
              loadingAndRun(() -> {
                  try {
                      clearRequestForAlbum();
                      // 搜索用户专辑并显示专辑列表
                      CommonResult<NetAlbumInfo> result = MusicServerUtil.getUserAlbums(currAlbumCommentInfo = commentInfo, netAlbumCurrPage = 1, limit);
                      List<NetAlbumInfo> albumInfos = result.data;
                      Integer total = result.total;
                      netAlbumMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 标题
                      netAlbumTitleLabel.setText(StringUtil.textToHtml(commentInfo.getUsername() + I18n.getText("albumSuffix")));
                      netAlbumToolBar.removeAll();
                      netAlbumToolBar.add(netAlbumBackwardButton);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      netAlbumToolBar.add(netAlbumTitleLabel);
                      netAlbumToolBar.add(Box.createHorizontalGlue());
                      // 更新数量显示
                      netAlbumCountLabel.setText(String.format(PAGINATION_MSG, netAlbumCurrPage, netAlbumMaxPage));
                      netAlbumCountPanel.add(netAlbumCountLabel, netAlbumCountPanel.getComponentIndex(netAlbumCountLabel));
                      netAlbumLeftBox.add(netAlbumCountPanel);
                      netAlbumSourceComboBox.setVisible(false);
                      netAlbumPlayAllButton.setVisible(false);
                      netAlbumCountPanel.setVisible(true);
                      // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                      netAlbumList.setModel(emptyListModel);
                      netAlbumListModel.clear();
                      albumInfos.forEach(albumInfo -> {
                          globalExecutor.execute(() -> updateCollection(albumInfo));
                          // 设置图片加载后重绘的事件
                          albumInfo.setInvokeLater(() -> {
                              updateRenderer(netAlbumList);
                              updateRenderer(collectionList);
                              netAlbumList.repaint();
                              collectionList.repaint();
                          });
                          netAlbumListModel.addElement(albumInfo);
                      });
                      netAlbumList.setModel(netAlbumListModel);
                      netAlbumBackwardButton.setEnabled(true);
                      netAlbumLeftBox.remove(netAlbumKeywordsPanelScrollPane);
                      if (netAlbumListModel.isEmpty()) {
                          netAlbumLeftBox.remove(netAlbumScrollPane);
                          netAlbumLeftBox.add(emptyHintPanel);
                      } else {
                          netAlbumLeftBox.remove(emptyHintPanel);
                          netAlbumLeftBox.add(netAlbumScrollPane);
                      }
                      netAlbumScrollPane.setVValue(0);
                      netAlbumLeftBox.remove(albumListBox);
                      netCommentBackwardButton.doClick();
                      if (currPane == MusicPane.LYRIC) changePaneButton.doClick();
                      tabbedPane.setSelectedIndex(TabIndex.NET_ALBUM);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 评论列表右键菜单项
          netCommentPopupMenu.add(netCommentCopyMenuItem);
          netCommentPopupMenu.add(netCommentSaveProfileMenuItem);
          netCommentPopupMenu.add(netCommentUserMenuItem);
          netCommentPopupMenu.add(netCommentPlaylistMenuItem);
          netCommentPopupMenu.add(netCommentAlbumMenuItem);
  
          // 评论最佳大小
          netCommentScrollPane.setPreferredSize(new Dimension(200, 600));
          // 评论滚动速度
          netCommentBox.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
          netCommentBox.add(netCommentScrollPane);
      }
  
      // 初始化乐谱工具栏
      private void initNetSheetToolBar() {
          // 只能输入数字
          netSheetPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 后退按钮事件
          netSheetBackwardButton.addActionListener(e -> {
              netSheetBackwardButton.setDrawBg(false);
              // 归还占有的空白提示面板
              if (emptyHintPanelParent != null) {
                  emptyHintPanelParent.add(emptyHintPanel);
                  emptyHintPanelParent = null;
              }
              netSheetList.setModel(emptyListModel);
              if (!netSheetListModel.isEmpty()) netSheetListModel.clear();
              globalPanel.remove(netSheetBox);
              if (lastPane == MusicPane.MUSIC) {
                  globalPanel.add(tabbedPane, BorderLayout.CENTER);
              } else if (lastPane == MusicPane.LYRIC) {
                  if (nextLrc != NextLrc.BAD_FORMAT) lrcScrollAnimation = true;
                  globalPanel.add(infoAndLrcBox, BorderLayout.CENTER);
              }
              globalPanel.repaint();
              currPane = lastPane;
          });
          // 刷新按钮事件
          netSheetRefreshButton.addActionListener(e -> {
              getSheets(currSheetMusicInfo, false);
          });
          // 第一页按钮事件
          netSheetStartPageButton.addActionListener(e -> {
              if (netSheetCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netSheetCurrPage = 1;
              getSheets(currSheetMusicInfo, false);
          });
          // 上一页按钮事件
          netSheetLastPageButton.addActionListener(e -> {
              if (netSheetCurrPage == 1) {
                  new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                  return;
              }
              netSheetCurrPage--;
              getSheets(currSheetMusicInfo, false);
          });
          // 下一页按钮事件
          netSheetNextPageButton.addActionListener(e -> {
              if (netSheetCurrPage >= netSheetMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netSheetCurrPage++;
              getSheets(currSheetMusicInfo, false);
          });
          // 最后一页按钮事件
          netSheetEndPageButton.addActionListener(e -> {
              if (netSheetCurrPage >= netSheetMaxPage) {
                  new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                  return;
              }
              netSheetCurrPage = netSheetMaxPage;
              getSheets(currSheetMusicInfo, false);
          });
          // 跳页按钮事件
          netSheetGoButton.addActionListener(e -> {
              String text = netSheetPageTextField.getText();
              netSheetPageTextField.setText("");
              if (text.isEmpty()) return;
              int destPage = Integer.parseInt(text);
              if (destPage < 1 || destPage > netSheetMaxPage) {
                  new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                  return;
              }
              netSheetCurrPage = destPage;
              getSheets(currSheetMusicInfo, false);
          });
          // 按钮大小限制
          Dimension dimension = new Dimension(30, 30);
          netSheetRefreshButton.setPreferredSize(dimension);
          netSheetStartPageButton.setPreferredSize(dimension);
          netSheetLastPageButton.setPreferredSize(dimension);
          netSheetGoButton.setPreferredSize(dimension);
          netSheetNextPageButton.setPreferredSize(dimension);
          netSheetEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          netSheetBackwardButton.addMouseListener(new ButtonMouseListener(netSheetBackwardButton, THIS));
          netSheetRefreshButton.addMouseListener(new ButtonMouseListener(netSheetRefreshButton, THIS));
          netSheetStartPageButton.addMouseListener(new ButtonMouseListener(netSheetStartPageButton, THIS));
          netSheetLastPageButton.addMouseListener(new ButtonMouseListener(netSheetLastPageButton, THIS));
          netSheetGoButton.addMouseListener(new ButtonMouseListener(netSheetGoButton, THIS));
          netSheetNextPageButton.addMouseListener(new ButtonMouseListener(netSheetNextPageButton, THIS));
          netSheetEndPageButton.addMouseListener(new ButtonMouseListener(netSheetEndPageButton, THIS));
          // 帮助提示
          netSheetBackwardButton.setToolTipText(BACKWARD_TIP);
          netSheetRefreshButton.setToolTipText(REFRESH_TIP);
          netSheetStartPageButton.setToolTipText(START_PAGE_TIP);
          netSheetLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netSheetGoButton.setToolTipText(GO_TIP);
          netSheetNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netSheetEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netSheetToolBar.setBorder(BorderFactory.createEmptyBorder(0, 37, 0, 0));
          netSheetToolBar.add(netSheetBackwardButton);
          netSheetToolBar.add(Box.createHorizontalGlue());
          netSheetToolBar.add(netSheetTitleLabel);
          netSheetToolBar.add(Box.createHorizontalGlue());
          netSheetBox.add(netSheetToolBar);
  
          // 乐谱数量和当前歌曲标签
          netSheetCountPanel.add(netSheetStartPageButton);
          netSheetCountPanel.add(netSheetLastPageButton);
          netSheetCountPanel.add(netSheetCountLabel);
          netSheetCountPanel.add(netSheetNextPageButton);
          netSheetCountPanel.add(netSheetEndPageButton);
          netSheetCountPanel.add(netSheetRefreshButton);
          netSheetCountPanel.add(netSheetPageTextField);
          netSheetCountPanel.add(netSheetGoButton);
          netSheetCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          netSheetCountPanel.setVisible(false);
          netSheetBox.add(netSheetCountPanel);
      }
  
      // 初始化在线乐谱列表
      private void initNetSheetList() {
          netSheetList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          netSheetList.setModel(netSheetListModel);
          netSheetList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      netSheetBrowseMenuItem.doClick();
                  }
              }
          });
          netSheetList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = netSheetList.locationToIndex(e.getPoint());
                  Rectangle bounds = netSheetList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  NetSheetListRenderer renderer = (NetSheetListRenderer) netSheetList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  netSheetList.repaint();
              }
          });
          netSheetList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  NetSheetListRenderer renderer = (NetSheetListRenderer) netSheetList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  netSheetList.repaint();
              }
          });
          netSheetList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开乐谱
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      netSheetBrowseMenuItem.doClick();
                  }
                  // 右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (netSheetList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = netSheetList.locationToIndex(e.getPoint());
                          if (index != -1 && !netSheetList.isSelectedIndex(index)
                                  || netSheetList.isSelectedIndex(index) && netSheetList.getSelectedIndices().length == 1) {
                              netSheetList.setSelectedIndex(index);
                          }
                          netSheetPopupMenu.show(netSheetList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 查看乐谱
          netSheetBrowseMenuItem.addActionListener(e -> {
              try {
                  NetSheetInfo sheetInfo = netSheetList.getSelectedValue();
                  if (sheetInfo == null) return;
  //                CommonResult<String> results = MusicServerUtils.getSheetImgUrls(sheetInfo);
  //                List<String> imgUrls = results.data;
  //                Integer total = results.total;
  //                if (imgUrls.isEmpty()) {
  //                    new TipDialog(THIS, NO_IMG_MSG).showDialog();
  //                    return;
  //                }
                  imageViewDialog = new ImageViewDialog(THIS, 30) {
                      @Override
                      public CommonResult<String> requestImgUrls(int pn, int limit, String cursor) {
                          return MusicServerUtil.getSheetImgUrls(sheetInfo);
                      }
  
                      @Override
                      public void requestFailed() {
                          new TipDialog(THIS, NO_IMG_MSG).showDialog();
                      }
                  };
                  imageViewDialog.showDialog();
                  imageViewDialog = null;
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          });
          // 复制乐谱名称
          netSheetCopyNameMenuItem.addActionListener(e -> {
              NetSheetInfo sheetInfo = netSheetList.getSelectedValue();
              copyToClipboard(sheetInfo.toString());
          });
          // 乐谱列表右键菜单项
          netSheetPopupMenu.add(netSheetBrowseMenuItem);
          netSheetPopupMenu.add(netSheetCopyNameMenuItem);
  
          // 乐谱最佳大小
          netSheetScrollPane.setPreferredSize(new Dimension(200, 600));
          // 乐谱滚动速度
          netSheetBox.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
          netSheetBox.add(netSheetScrollPane);
      }
  
      // 初始化推荐工具条
      private void initRecommendToolBar() {
          // 只能输入数字
          netRecommendPageTextField.setDocument(new SafeDocument(0, Integer.MAX_VALUE));
          // 推荐后退按钮事件
          recommendBackwardButton.addActionListener(e -> {
              recommendBackwardButton.setDrawBg(false);
              // 返回到歌单/专辑/歌手/电台
              // 先清空在线音乐列表
              ((DefaultListModel) netMusicList.getModel()).clear();
              // 从在线音乐列表切换回歌单/专辑/歌手/电台列表
              if (currRecommendTab != RecommendTabIndex.HOT_MUSIC_RECOMMEND
                      && currRecommendTab != RecommendTabIndex.NEW_RADIO_RECOMMEND)
                  netRecommendTagComboBox.setVisible(true);
              netRecommendPlayAllButton.setVisible(false);
              recommendLeftBox.remove(recommendItemListBox);
              recommendLeftBox.remove(musicRecommendToolBar);
              recommendLeftBox.add(recommendCountPanel);
              recommendLeftBox.add(itemRecommendScrollPane);
              // 添加回推荐工具栏，不需要加载更多才删除之前的工具栏
              recommendLeftBox.remove(musicRecommendToolBar);
              recommendLeftBox.add(recommendToolBar, 0);
              // 更新歌单/专辑/歌手/电台数量显示
              recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
              // 解决数量标签文字显示不全问题
              recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
              recommendBackwardButton.setEnabled(false);
              netRecommendSourceComboBox.setVisible(true);
              netRecommendSortTypeComboBox.setVisible(false);
              // 切换后一定要刷新！
              recommendLeftBox.repaint();
          });
          // 推荐模块跳页事件，可复用
          Runnable recommendGoPageAction = () -> {
              // 搜索推荐歌单/专辑/歌手/电台里的歌
              if (recommendBackwardButton.isEnabled()) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          NetResource resource = itemRecommendList.getSelectedValue();
                          // 这是歌单里的歌
                          if (resource instanceof NetPlaylistInfo) {
                              NetPlaylistInfo playlistInfo = (NetPlaylistInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInPlaylist(playlistInfo, netMusicInRecommendCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              recommendCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRecommendCurrPage, netMusicInRecommendMaxPage));
                              // 解决数量标签文字显示不全问题
                              recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForPlaylistRecommendModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForPlaylistRecommendModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForPlaylistRecommendModel);
                              if (netMusicListForPlaylistRecommendModel.isEmpty()) {
                                  recommendItemListCountBox.remove(netMusicScrollPane);
                                  recommendItemListCountBox.add(emptyHintPanel);
                              } else {
                                  recommendItemListCountBox.remove(emptyHintPanel);
                                  recommendItemListCountBox.add(netMusicScrollPane);
                              }
                              recommendLeftBox.repaint();
                          }
                          // 这是专辑里的歌
                          else if (resource instanceof NetAlbumInfo) {
                              NetAlbumInfo albumInfo = (NetAlbumInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInAlbum(albumInfo, netMusicInRecommendCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              recommendCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRecommendCurrPage, netMusicInRecommendMaxPage));
                              // 解决数量标签文字显示不全问题
                              recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForAlbumRecommendModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForAlbumRecommendModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForAlbumRecommendModel);
                              if (netMusicListForAlbumRecommendModel.isEmpty()) {
                                  recommendItemListCountBox.remove(netMusicScrollPane);
                                  recommendItemListCountBox.add(emptyHintPanel);
                              } else {
                                  recommendItemListCountBox.remove(emptyHintPanel);
                                  recommendItemListCountBox.add(netMusicScrollPane);
                              }
                              recommendLeftBox.repaint();
                          }
                          // 这是歌手里的歌
                          else if (resource instanceof NetArtistInfo) {
                              NetArtistInfo artistInfo = (NetArtistInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInArtist(artistInfo, netMusicInRecommendCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              recommendCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRecommendCurrPage, netMusicInRecommendMaxPage));
                              // 解决数量标签文字显示不全问题
                              recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForArtistRecommendModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForArtistRecommendModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForArtistRecommendModel);
                              if (netMusicListForArtistRecommendModel.isEmpty()) {
                                  recommendItemListCountBox.remove(netMusicScrollPane);
                                  recommendItemListCountBox.add(emptyHintPanel);
                              } else {
                                  recommendItemListCountBox.remove(emptyHintPanel);
                                  recommendItemListCountBox.add(netMusicScrollPane);
                              }
                              recommendLeftBox.repaint();
                          }
                          // 这是电台里的歌
                          else if (resource instanceof NetRadioInfo) {
                              NetRadioInfo radioInfo = (NetRadioInfo) resource;
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRadio(
                                      radioInfo, netRecommendSortTypeComboBox.getSelectedIndex(), netMusicInRecommendCurrPage, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              Integer total = result.total;
                              netMusicInRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              recommendCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRecommendCurrPage, netMusicInRecommendMaxPage));
                              // 解决数量标签文字显示不全问题
                              recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                              // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForRadioRecommendModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForRadioRecommendModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForRadioRecommendModel);
                              if (netMusicListForRadioRecommendModel.isEmpty()) {
                                  recommendItemListCountBox.remove(netMusicScrollPane);
                                  recommendItemListCountBox.add(emptyHintPanel);
                              } else {
                                  recommendItemListCountBox.remove(emptyHintPanel);
                                  recommendItemListCountBox.add(netMusicScrollPane);
                              }
                              recommendLeftBox.repaint();
                          }
                          netMusicScrollPane.setVValue(0);
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索推荐歌单
              else if (currRecommendTab == RecommendTabIndex.PLAYLIST_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetPlaylistInfo> result = MusicServerUtil.getRecommendPlaylists(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetPlaylistInfo> playlistInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          itemRecommendList.setModel(emptyListModel);
                          playlistRecommendListModel.clear();
                          playlistInfos.forEach(playlistInfo -> {
                              globalExecutor.execute(() -> updateCollection(playlistInfo));
                              // 设置图片加载后重绘的事件
                              playlistInfo.setInvokeLater(() -> {
                                  updateRenderer(itemRecommendList);
                                  updateRenderer(collectionList);
                                  itemRecommendList.repaint();
                                  collectionList.repaint();
                              });
                              playlistRecommendListModel.addElement(playlistInfo);
                          });
                          itemRecommendList.setModel(playlistRecommendListModel);
                          itemRecommendScrollPane.setVValue(0);
                          if (playlistRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(itemRecommendScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索精品歌单
              else if (currRecommendTab == RecommendTabIndex.HIGH_QUALITY_PLAYLIST_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetPlaylistInfo> result = MusicServerUtil.getHighQualityPlaylists(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetPlaylistInfo> playlistInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          itemRecommendList.setModel(emptyListModel);
                          playlistRecommendListModel.clear();
                          playlistInfos.forEach(playlistInfo -> {
                              globalExecutor.execute(() -> updateCollection(playlistInfo));
                              // 设置图片加载后重绘的事件
                              playlistInfo.setInvokeLater(() -> {
                                  updateRenderer(itemRecommendList);
                                  updateRenderer(collectionList);
                                  itemRecommendList.repaint();
                                  collectionList.repaint();
                              });
                              playlistRecommendListModel.addElement(playlistInfo);
                          });
                          itemRecommendList.setModel(playlistRecommendListModel);
                          itemRecommendScrollPane.setVValue(0);
                          if (playlistRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(itemRecommendScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索飙升歌曲
              else if (currRecommendTab == RecommendTabIndex.HOT_MUSIC_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getHotMusicRecommend(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicRecommendListModel.clear();
                          musicInfos.forEach(musicInfo -> netMusicRecommendListModel.addElement(musicInfo));
                          netMusicList.setModel(netMusicRecommendListModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(netMusicScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(netMusicScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索新歌速递
              else if (currRecommendTab == RecommendTabIndex.NEW_MUSIC_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getNewMusic(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicRecommendListModel.clear();
                          musicInfos.forEach(musicInfo -> netMusicRecommendListModel.addElement(musicInfo));
                          netMusicList.setModel(netMusicRecommendListModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(netMusicScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(netMusicScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索新碟上架
              else if (currRecommendTab == RecommendTabIndex.NEW_ALBUM_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetAlbumInfo> result = MusicServerUtil.getNewAlbums(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetAlbumInfo> albumInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          itemRecommendList.setModel(emptyListModel);
                          albumRecommendListModel.clear();
                          albumInfos.forEach(albumInfo -> {
                              globalExecutor.execute(() -> updateCollection(albumInfo));
                              // 设置图片加载后重绘的事件
                              albumInfo.setInvokeLater(() -> {
                                  updateRenderer(itemRecommendList);
                                  updateRenderer(collectionList);
                                  itemRecommendList.repaint();
                                  collectionList.repaint();
                              });
                              albumRecommendListModel.addElement(albumInfo);
                          });
                          itemRecommendList.setModel(albumRecommendListModel);
                          itemRecommendScrollPane.setVValue(0);
                          if (albumRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(itemRecommendScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索歌手排行
              else if (currRecommendTab == RecommendTabIndex.ARTIST_LIST_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetArtistInfo> result = MusicServerUtil.getArtistLists(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetArtistInfo> artistInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          itemRecommendList.setModel(emptyListModel);
                          artistRecommendListModel.clear();
                          artistInfos.forEach(artistInfo -> {
                              globalExecutor.execute(() -> updateCollection(artistInfo));
                              // 设置图片加载后重绘的事件
                              artistInfo.setInvokeLater(() -> {
                                  updateRenderer(itemRecommendList);
                                  updateRenderer(collectionList);
                                  itemRecommendList.repaint();
                                  collectionList.repaint();
                              });
                              artistRecommendListModel.addElement(artistInfo);
                          });
                          itemRecommendList.setModel(artistRecommendListModel);
                          itemRecommendScrollPane.setVValue(0);
                          if (artistRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(itemRecommendScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索新晋电台
              else if (currRecommendTab == RecommendTabIndex.NEW_RADIO_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetRadioInfo> result = MusicServerUtil.getNewRadios(
                                  netRecommendSourceComboBox.getSelectedIndex(), netRecommendCurrPage, limit);
                          List<NetRadioInfo> radioInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          itemRecommendList.setModel(emptyListModel);
                          radioRecommendListModel.clear();
                          radioInfos.forEach(radioInfo -> {
                              globalExecutor.execute(() -> updateCollection(radioInfo));
                              // 设置图片加载后重绘的事件
                              radioInfo.setInvokeLater(() -> {
                                  updateRenderer(itemRecommendList);
                                  updateRenderer(collectionList);
                                  itemRecommendList.repaint();
                                  collectionList.repaint();
                              });
                              radioRecommendListModel.addElement(radioInfo);
                          });
                          itemRecommendList.setModel(radioRecommendListModel);
                          itemRecommendScrollPane.setVValue(0);
                          if (radioRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(itemRecommendScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索热门电台
              else if (currRecommendTab == RecommendTabIndex.HOT_RADIO_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetRadioInfo> result = MusicServerUtil.getHotRadios(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetRadioInfo> radioInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          itemRecommendList.setModel(emptyListModel);
                          radioRecommendListModel.clear();
                          radioInfos.forEach(radioInfo -> {
                              globalExecutor.execute(() -> updateCollection(radioInfo));
                              // 设置图片加载后重绘的事件
                              radioInfo.setInvokeLater(() -> {
                                  updateRenderer(itemRecommendList);
                                  updateRenderer(collectionList);
                                  itemRecommendList.repaint();
                                  collectionList.repaint();
                              });
                              radioRecommendListModel.addElement(radioInfo);
                          });
                          itemRecommendList.setModel(radioRecommendListModel);
                          itemRecommendScrollPane.setVValue(0);
                          if (radioRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(itemRecommendScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索推荐节目
              else if (currRecommendTab == RecommendTabIndex.PROGRAM_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetMusicInfo> result = MusicServerUtil.getRecommendPrograms(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetMusicInfo> musicInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          netMusicList.setModel(emptyListModel);
                          netMusicRecommendListModel.clear();
                          musicInfos.forEach(musicInfo -> netMusicRecommendListModel.addElement(musicInfo));
                          netMusicList.setModel(netMusicRecommendListModel);
                          netMusicScrollPane.setVValue(0);
                          if (netMusicRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(netMusicScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(netMusicScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
              // 搜索推荐 MV
              else if (currRecommendTab == RecommendTabIndex.MV_RECOMMEND) {
                  loadingAndRun(() -> {
                      // 搜索歌曲并显示在在线歌曲列表
                      try {
                          CommonResult<NetMvInfo> result = MusicServerUtil.getRecommendMvs(
                                  netRecommendSourceComboBox.getSelectedIndex(), (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage, limit);
                          List<NetMvInfo> mvInfos = result.data;
                          int total = result.total;
                          netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                          // 更新数量显示
                          recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                          // 解决数量标签文字显示不全问题
                          recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                          // 添加数据建议在更新数量显示之后，不然有时候会出现显示不出来的情况！
                          itemRecommendList.setModel(emptyListModel);
                          mvRecommendListModel.clear();
                          mvInfos.forEach(mvInfo -> {
                              globalExecutor.execute(() -> updateCollection(mvInfo));
                              // 设置图片加载后重绘的事件
                              mvInfo.setInvokeLater(() -> {
                                  updateRenderer(itemRecommendList);
                                  updateRenderer(collectionList);
                                  itemRecommendList.repaint();
                                  collectionList.repaint();
                              });
                              mvRecommendListModel.addElement(mvInfo);
                          });
                          itemRecommendList.setModel(mvRecommendListModel);
                          itemRecommendScrollPane.setVValue(0);
                          if (mvRecommendListModel.isEmpty()) {
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(emptyHintPanel);
                          } else {
                              recommendLeftBox.remove(emptyHintPanel);
                              recommendLeftBox.add(itemRecommendScrollPane);
                          }
                          recommendLeftBox.repaint();
                      } catch (IORuntimeException ioRuntimeException) {
                          // 无网络连接
                          new TipDialog(THIS, NO_NET_MSG).showDialog();
                      } catch (HttpException httpException) {
                          // 请求超时
                          new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                      } catch (JSONException jsonException) {
                          // 接口异常
                          new TipDialog(THIS, API_ERROR_MSG).showDialog();
                      }
                  });
              }
          };
          // 推荐标签下拉框
          netRecommendTagComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (netRecommendTagComboBox.getItemCount() <= 1
                      || netRecommendTagComboBox.getSelectedItem() == null
                      || e.getStateChange() != ItemEvent.SELECTED) return;
              netRecommendCurrPage = 1;
              recommendGoPageAction.run();
          });
          // 播放全部
          netRecommendPlayAllButton.addActionListener(e -> {
              NetResource resource = itemRecommendList.getSelectedValue();
              if (resource instanceof NetPlaylistInfo) netPlaylistPlayAllButton.doClick();
              else if (resource instanceof NetAlbumInfo) netAlbumPlayAllButton.doClick();
              else if (resource instanceof NetArtistInfo) netArtistPlayAllButton.doClick();
              else if (resource instanceof NetRadioInfo) netRadioPlayAllButton.doClick();
          });
          // 刷新按钮事件
          netRecommendRefreshButton.addActionListener(e -> {
              // 当前显示的是推荐列表，刷新推荐
              if (!recommendBackwardButton.isEnabled()) {
                  recommendGoPageAction.run();
              }
              // 当前显示的是推荐里的歌曲，刷新歌曲
              else {
                  recommendGoPageAction.run();
              }
          });
          // 第一页按钮事件
          netRecommendStartPageButton.addActionListener(e -> {
              // 当前显示的是推荐列表，跳到第一页推荐
              if (!recommendBackwardButton.isEnabled()) {
                  if (netRecommendCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRecommendCurrPage = 1;
                  recommendGoPageAction.run();
              }
              // 当前显示的是推荐里的歌曲，跳到第一页歌曲
              else {
                  if (netMusicInRecommendCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRecommendCurrPage = 1;
                  recommendGoPageAction.run();
              }
          });
          // 上一页按钮事件
          netRecommendLastPageButton.addActionListener(e -> {
              // 当前显示的是推荐列表，跳到上一页推荐
              if (!recommendBackwardButton.isEnabled()) {
                  if (netRecommendCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRecommendCurrPage--;
                  recommendGoPageAction.run();
              }
              // 当前显示的是推荐里的歌曲，跳到上一页歌曲
              else {
                  if (netMusicInRecommendCurrPage == 1) {
                      new TipDialog(THIS, FIRST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRecommendCurrPage--;
                  recommendGoPageAction.run();
              }
          });
          // 下一页按钮事件
          netRecommendNextPageButton.addActionListener(e -> {
              // 当前显示的是推荐列表，跳到下一页推荐
              if (!recommendBackwardButton.isEnabled()) {
                  // netRecommendMaxPage 为 -1 时，页数未知
                  if (netRecommendCurrPage >= netRecommendMaxPage && netRecommendMaxPage > 0) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRecommendCurrPage++;
                  recommendGoPageAction.run();
              }
              // 当前显示的是某推荐里的歌曲，跳到下一页歌曲
              else {
                  if (netMusicInRecommendCurrPage >= netMusicInRecommendMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRecommendCurrPage++;
                  recommendGoPageAction.run();
              }
          });
          // 最后一页按钮事件
          netRecommendEndPageButton.addActionListener(e -> {
              // 当前显示的是推荐列表，跳到最后一页推荐
              if (!recommendBackwardButton.isEnabled()) {
                  // netRecommendMaxPage 为 -1 时，页数未知
                  if (netRecommendCurrPage >= netRecommendMaxPage && netRecommendMaxPage > 0) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netRecommendCurrPage = netRecommendMaxPage;
                  recommendGoPageAction.run();
              }
              // 当前显示的是某推荐里的歌曲，跳到最后一页歌曲
              else {
                  if (netMusicInRecommendCurrPage >= netMusicInRecommendMaxPage) {
                      new TipDialog(THIS, LAST_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRecommendCurrPage = netMusicInRecommendMaxPage;
                  recommendGoPageAction.run();
              }
          });
          // 跳页按钮事件
          netRecommendGoButton.addActionListener(e -> {
              // 当前显示的是推荐列表，跳页推荐
              if (!recommendBackwardButton.isEnabled()) {
                  String text = netRecommendPageTextField.getText();
                  netRecommendPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  // netRecommendMaxPage 为 -1 时，页数未知
                  if (destPage < 1 || destPage > netRecommendMaxPage && netRecommendMaxPage > 0) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netRecommendCurrPage = destPage;
                  recommendGoPageAction.run();
              }
              // 当前显示的是某电台的歌曲，跳页歌曲
              else {
                  String text = netRecommendPageTextField.getText();
                  netRecommendPageTextField.setText("");
                  if (text.isEmpty()) return;
                  int destPage = Integer.parseInt(text);
                  if (destPage < 1 || destPage > netMusicInRecommendMaxPage) {
                      new TipDialog(THIS, ILLEGAL_PAGE_MSG).showDialog();
                      return;
                  }
                  netMusicInRecommendCurrPage = destPage;
                  recommendGoPageAction.run();
              }
          });
          // 推荐歌单事件
          playlistRecommendButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.PLAYLIST_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.PLAYLIST_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.recPlaylistTag.isEmpty()) MusicServerUtil.initRecPlaylistTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.recPlaylistTag.keySet()) {
                          if (shouldShowTag(Tags.recPlaylistTag, Tags.recPlaylistMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetPlaylistInfo> result = MusicServerUtil.getRecommendPlaylists(netRecommendSourceComboBox.getSelectedIndex(),
                              netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetPlaylistInfo> playlistInfos = result.data;
                      Integer total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      itemRecommendList.setModel(emptyListModel);
                      playlistRecommendListModel.clear();
                      playlistInfos.forEach(playlistInfo -> {
                          globalExecutor.execute(() -> updateCollection(playlistInfo));
                          // 设置图片加载后重绘的事件
                          playlistInfo.setInvokeLater(() -> {
                              updateRenderer(itemRecommendList);
                              updateRenderer(collectionList);
                              itemRecommendList.repaint();
                              collectionList.repaint();
                          });
                          playlistRecommendListModel.addElement(playlistInfo);
                      });
                      itemRecommendList.setModel(playlistRecommendListModel);
                      itemRecommendScrollPane.setVValue(0);
                      // 删掉推荐单曲列表，加载歌单 Model
                      recommendLeftBox.remove(netMusicScrollPane);
                      if (playlistRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(itemRecommendScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(itemRecommendScrollPane);
                      }
                      // 不需要加载更多，删除加载更多的工具栏
                      recommendLeftBox.remove(musicRecommendToolBar);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 精品歌单事件
          highQualityPlaylistButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.HIGH_QUALITY_PLAYLIST_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.HIGH_QUALITY_PLAYLIST_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.hotPlaylistTag.isEmpty()) MusicServerUtil.initPlaylistTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.hotPlaylistTag.keySet()) {
                          if (shouldShowTag(Tags.hotPlaylistTag, Tags.hotPlaylistMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetPlaylistInfo> result = MusicServerUtil.getHighQualityPlaylists(netRecommendSourceComboBox.getSelectedIndex(),
                              netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetPlaylistInfo> playlistInfos = result.data;
                      Integer total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      itemRecommendList.setModel(emptyListModel);
                      playlistRecommendListModel.clear();
                      playlistInfos.forEach(playlistInfo -> {
                          globalExecutor.execute(() -> updateCollection(playlistInfo));
                          // 设置图片加载后重绘的事件
                          playlistInfo.setInvokeLater(() -> {
                              updateRenderer(itemRecommendList);
                              updateRenderer(collectionList);
                              itemRecommendList.repaint();
                              collectionList.repaint();
                          });
                          playlistRecommendListModel.addElement(playlistInfo);
                      });
                      itemRecommendList.setModel(playlistRecommendListModel);
                      itemRecommendScrollPane.setVValue(0);
                      // 删掉推荐单曲列表，加载歌单 Model
                      recommendLeftBox.remove(netMusicScrollPane);
                      if (playlistRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(itemRecommendScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(itemRecommendScrollPane);
                      }
                      // 不需要加载更多，删除加载更多的工具栏
                      recommendLeftBox.remove(musicRecommendToolBar);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 飙升歌曲事件
          hotMusicButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.HOT_MUSIC_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.HOT_MUSIC_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.hotSongTag.isEmpty()) MusicServerUtil.initHotSongTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.hotSongTag.keySet()) {
                          if (shouldShowTag(Tags.hotSongTag, Tags.hotSongMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetMusicInfo> result = MusicServerUtil.getHotMusicRecommend(netRecommendSourceComboBox.getSelectedIndex(),
                              netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetMusicInfo> musicInfos = result.data;
                      int total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      netMusicList.setModel(emptyListModel);
                      netMusicRecommendListModel.clear();
                      musicInfos.forEach(musicInfo -> netMusicRecommendListModel.addElement(musicInfo));
                      netMusicList.setModel(netMusicRecommendListModel);
                      netMusicScrollPane.setVValue(0);
                      // 删掉歌单列表/专辑列表
                      recommendLeftBox.remove(itemRecommendScrollPane);
                      if (netMusicRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(netMusicScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(netMusicScrollPane);
                      }
                      // 不需要加载更多，删除加载更多的工具栏
                      recommendLeftBox.remove(musicRecommendToolBar);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 推荐新单曲事件
          newMusicButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.NEW_MUSIC_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.NEW_MUSIC_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.newSongTag.isEmpty()) MusicServerUtil.initNewSongTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.newSongTag.keySet()) {
                          if (shouldShowTag(Tags.newSongTag, Tags.newSongMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetMusicInfo> result = MusicServerUtil.getNewMusic(netRecommendSourceComboBox.getSelectedIndex(),
                              netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetMusicInfo> musicInfos = result.data;
                      int total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      netMusicList.setModel(emptyListModel);
                      netMusicRecommendListModel.clear();
                      musicInfos.forEach(musicInfo -> netMusicRecommendListModel.addElement(musicInfo));
                      netMusicList.setModel(netMusicRecommendListModel);
                      netMusicScrollPane.setVValue(0);
                      // 删掉歌单列表/专辑列表
                      recommendLeftBox.remove(itemRecommendScrollPane);
                      if (netMusicRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(netMusicScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(netMusicScrollPane);
                      }
                      // 不需要加载更多，删除加载更多的工具栏
                      recommendLeftBox.remove(musicRecommendToolBar);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 新碟上架事件
          newAlbumRecommendButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.NEW_ALBUM_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.NEW_ALBUM_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.newAlbumTag.isEmpty()) MusicServerUtil.initNewAlbumTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.newAlbumTag.keySet()) {
                          if (shouldShowTag(Tags.newAlbumTag, Tags.newAlbumMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetAlbumInfo> result = MusicServerUtil.getNewAlbums(netRecommendSourceComboBox.getSelectedIndex(),
                              netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetAlbumInfo> albumInfos = result.data;
                      int total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      itemRecommendList.setModel(emptyListModel);
                      albumRecommendListModel.clear();
                      albumInfos.forEach(albumInfo -> {
                          globalExecutor.execute(() -> updateCollection(albumInfo));
                          // 设置图片加载后重绘的事件
                          albumInfo.setInvokeLater(() -> {
                              updateRenderer(itemRecommendList);
                              updateRenderer(collectionList);
                              itemRecommendList.repaint();
                              collectionList.repaint();
                          });
                          albumRecommendListModel.addElement(albumInfo);
                      });
                      itemRecommendList.setModel(albumRecommendListModel);
                      itemRecommendScrollPane.setVValue(0);
                      // 删掉推荐单曲列表
                      recommendLeftBox.remove(netMusicScrollPane);
                      if (albumRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(itemRecommendScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(itemRecommendScrollPane);
                      }
                      recommendBackwardButton.setEnabled(false);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 歌手排行事件
          artistListRecommendButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.ARTIST_LIST_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.ARTIST_LIST_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.artistTag.isEmpty()) MusicServerUtil.initArtistTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.artistTag.keySet()) {
                          if (shouldShowTag(Tags.artistTag, Tags.artistMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetArtistInfo> result = MusicServerUtil.getArtistLists(netRecommendSourceComboBox.getSelectedIndex(),
                              netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetArtistInfo> artistInfos = result.data;
                      int total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      itemRecommendList.setModel(emptyListModel);
                      artistRecommendListModel.clear();
                      artistInfos.forEach(artistInfo -> {
                          globalExecutor.execute(() -> updateCollection(artistInfo));
                          // 设置图片加载后重绘的事件
                          artistInfo.setInvokeLater(() -> {
                              updateRenderer(itemRecommendList);
                              updateRenderer(collectionList);
                              itemRecommendList.repaint();
                              collectionList.repaint();
                          });
                          artistRecommendListModel.addElement(artistInfo);
                      });
                      itemRecommendList.setModel(artistRecommendListModel);
                      itemRecommendScrollPane.setVValue(0);
                      // 删掉推荐单曲列表
                      recommendLeftBox.remove(netMusicScrollPane);
                      if (artistRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(itemRecommendScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(itemRecommendScrollPane);
                      }
                      // 不需要加载更多，删除加载更多的工具栏
                      recommendLeftBox.remove(musicRecommendToolBar);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 新晋电台事件
          newRadioRecommendButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.NEW_RADIO_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.NEW_RADIO_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      CommonResult<NetRadioInfo> result = MusicServerUtil.getNewRadios(netRecommendSourceComboBox.getSelectedIndex(), netRecommendCurrPage = 1, limit);
                      List<NetRadioInfo> radioInfos = result.data;
                      int total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      itemRecommendList.setModel(emptyListModel);
                      radioRecommendListModel.clear();
                      radioInfos.forEach(radioInfo -> {
                          globalExecutor.execute(() -> updateCollection(radioInfo));
                          // 设置图片加载后重绘的事件
                          radioInfo.setInvokeLater(() -> {
                              updateRenderer(itemRecommendList);
                              updateRenderer(collectionList);
                              itemRecommendList.repaint();
                              collectionList.repaint();
                          });
                          radioRecommendListModel.addElement(radioInfo);
                      });
                      itemRecommendList.setModel(radioRecommendListModel);
                      itemRecommendScrollPane.setVValue(0);
                      // 删掉推荐单曲列表
                      recommendLeftBox.remove(netMusicScrollPane);
                      if (radioRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(itemRecommendScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(itemRecommendScrollPane);
                      }
                      recommendBackwardButton.setEnabled(false);
                      netRecommendTagComboBox.setVisible(false);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 热门电台事件
          hotRadioRecommendButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.HOT_RADIO_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.HOT_RADIO_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.radioTag.isEmpty()) MusicServerUtil.initRadioTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.radioTag.keySet()) {
                          if (shouldShowTag(Tags.radioTag, Tags.radioMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetRadioInfo> result = MusicServerUtil.getHotRadios(netRecommendSourceComboBox.getSelectedIndex(),
                              netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetRadioInfo> radioInfos = result.data;
                      int total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      itemRecommendList.setModel(emptyListModel);
                      radioRecommendListModel.clear();
                      radioInfos.forEach(radioInfo -> {
                          globalExecutor.execute(() -> updateCollection(radioInfo));
                          // 设置图片加载后重绘的事件
                          radioInfo.setInvokeLater(() -> {
                              updateRenderer(itemRecommendList);
                              updateRenderer(collectionList);
                              itemRecommendList.repaint();
                              collectionList.repaint();
                          });
                          radioRecommendListModel.addElement(radioInfo);
                      });
                      itemRecommendList.setModel(radioRecommendListModel);
                      itemRecommendScrollPane.setVValue(0);
                      // 删掉推荐单曲列表
                      recommendLeftBox.remove(netMusicScrollPane);
                      if (radioRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(itemRecommendScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(itemRecommendScrollPane);
                      }
                      recommendBackwardButton.setEnabled(false);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 推荐节目事件
          programRecommendButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.PROGRAM_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.PROGRAM_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.programTag.isEmpty()) MusicServerUtil.initProgramTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.programTag.keySet()) {
                          if (shouldShowTag(Tags.programTag, Tags.programMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetMusicInfo> result = MusicServerUtil.getRecommendPrograms(netRecommendSourceComboBox.getSelectedIndex(),
                              netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetMusicInfo> musicInfos = result.data;
                      int total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      netMusicList.setModel(emptyListModel);
                      netMusicRecommendListModel.clear();
                      musicInfos.forEach(musicInfo -> netMusicRecommendListModel.addElement(musicInfo));
                      netMusicList.setModel(netMusicRecommendListModel);
                      netMusicScrollPane.setVValue(0);
                      // 删掉歌单列表/专辑列表
                      recommendLeftBox.remove(itemRecommendScrollPane);
                      if (netMusicRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(netMusicScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(netMusicScrollPane);
                      }
                      // 不需要加载更多，删除加载更多的工具栏
                      recommendLeftBox.remove(musicRecommendToolBar);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          // 推荐 MV 事件
          mvRecommendButton.addActionListener(e -> {
              if (currRecommendTab == RecommendTabIndex.MV_RECOMMEND) return;
              currRecommendTab = RecommendTabIndex.MV_RECOMMEND;
              updateTabButtonStyle();
              loadingAndRun(() -> {
                  try {
                      // 显示分类标签
                      if (Tags.mvTag.isEmpty()) MusicServerUtil.initMvTag();
                      ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
                      for (String tag : Tags.mvTag.keySet()) {
                          if (shouldShowTag(Tags.mvTag, Tags.mvMap, tag))
                              netRecommendTagComboBox.addItem(tag);
                      }
  
                      CommonResult<NetMvInfo> result = MusicServerUtil.getRecommendMvs(
                              netRecommendSourceComboBox.getSelectedIndex(), netRecommendTagComboBox.getItemCount() <= 1 ? "默认" : (String) netRecommendTagComboBox.getSelectedItem(), netRecommendCurrPage = 1, limit);
                      List<NetMvInfo> mvInfos = result.data;
                      int total = result.total;
                      netRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                      // 更新数量显示
                      recommendCountLabel.setText(String.format(PAGINATION_MSG, netRecommendCurrPage, netRecommendMaxPage));
                      // 解决数量标签文字显示不全问题
                      recommendCountPanel.add(recommendCountLabel, recommendCountPanel.getComponentIndex(recommendCountLabel));
                      recommendCountPanel.setVisible(true);
                      itemRecommendList.setModel(emptyListModel);
                      mvRecommendListModel.clear();
                      mvInfos.forEach(mvInfo -> {
                          globalExecutor.execute(() -> updateCollection(mvInfo));
                          // 设置图片加载后重绘的事件
                          mvInfo.setInvokeLater(() -> {
                              updateRenderer(itemRecommendList);
                              updateRenderer(collectionList);
                              itemRecommendList.repaint();
                              collectionList.repaint();
                          });
                          mvRecommendListModel.addElement(mvInfo);
                      });
                      itemRecommendList.setModel(mvRecommendListModel);
                      itemRecommendScrollPane.setVValue(0);
                      // 删掉推荐单曲列表
                      recommendLeftBox.remove(netMusicScrollPane);
                      if (mvRecommendListModel.isEmpty()) {
                          recommendLeftBox.remove(itemRecommendScrollPane);
                          recommendLeftBox.add(emptyHintPanel);
                      } else {
                          recommendLeftBox.remove(emptyHintPanel);
                          recommendLeftBox.add(itemRecommendScrollPane);
                      }
                      recommendBackwardButton.setEnabled(false);
                      netRecommendTagComboBox.setVisible(true);
                  } catch (IORuntimeException ioRuntimeException) {
                      // 无网络连接
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } catch (HttpException httpException) {
                      // 请求超时
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } catch (JSONException jsonException) {
                      // 接口异常
                      new TipDialog(THIS, API_ERROR_MSG).showDialog();
                  }
              });
          });
          recommendBackwardButton.setEnabled(false);
          // 按钮不可见
          netRecommendTagComboBox.setVisible(false);
          netRecommendSortTypeComboBox.setVisible(false);
          netRecommendPlayAllButton.setVisible(false);
          // 控制按钮大小
          Dimension dimension = new Dimension(30, 30);
          netRecommendRefreshButton.setPreferredSize(dimension);
          netRecommendStartPageButton.setPreferredSize(dimension);
          netRecommendLastPageButton.setPreferredSize(dimension);
          netRecommendGoButton.setPreferredSize(dimension);
          netRecommendNextPageButton.setPreferredSize(dimension);
          netRecommendEndPageButton.setPreferredSize(dimension);
          // 按钮悬浮与点击效果
          playlistRecommendButton.addMouseListener(new ButtonMouseListener(playlistRecommendButton, THIS));
          highQualityPlaylistButton.addMouseListener(new ButtonMouseListener(highQualityPlaylistButton, THIS));
          hotMusicButton.addMouseListener(new ButtonMouseListener(hotMusicButton, THIS));
          newMusicButton.addMouseListener(new ButtonMouseListener(newMusicButton, THIS));
          newAlbumRecommendButton.addMouseListener(new ButtonMouseListener(newAlbumRecommendButton, THIS));
          artistListRecommendButton.addMouseListener(new ButtonMouseListener(artistListRecommendButton, THIS));
          newRadioRecommendButton.addMouseListener(new ButtonMouseListener(newRadioRecommendButton, THIS));
          hotRadioRecommendButton.addMouseListener(new ButtonMouseListener(hotRadioRecommendButton, THIS));
          programRecommendButton.addMouseListener(new ButtonMouseListener(programRecommendButton, THIS));
          mvRecommendButton.addMouseListener(new ButtonMouseListener(mvRecommendButton, THIS));
          recommendBackwardButton.addMouseListener(new ButtonMouseListener(recommendBackwardButton, THIS));
          netRecommendPlayAllButton.addMouseListener(new ButtonMouseListener(netRecommendPlayAllButton, THIS));
          netRecommendRefreshButton.addMouseListener(new ButtonMouseListener(netRecommendRefreshButton, THIS));
          netRecommendStartPageButton.addMouseListener(new ButtonMouseListener(netRecommendStartPageButton, THIS));
          netRecommendLastPageButton.addMouseListener(new ButtonMouseListener(netRecommendLastPageButton, THIS));
          netRecommendGoButton.addMouseListener(new ButtonMouseListener(netRecommendGoButton, THIS));
          netRecommendNextPageButton.addMouseListener(new ButtonMouseListener(netRecommendNextPageButton, THIS));
          netRecommendEndPageButton.addMouseListener(new ButtonMouseListener(netRecommendEndPageButton, THIS));
          // 提示语
          recommendBackwardButton.setToolTipText(BACKWARD_TIP);
          netRecommendPlayAllButton.setToolTipText(PLAY_ALL_TIP);
          netRecommendRefreshButton.setToolTipText(REFRESH_TIP);
          netRecommendStartPageButton.setToolTipText(START_PAGE_TIP);
          netRecommendLastPageButton.setToolTipText(LAST_PAGE_TIP);
          netRecommendGoButton.setToolTipText(GO_TIP);
          netRecommendNextPageButton.setToolTipText(NEXT_PAGE_TIP);
          netRecommendEndPageButton.setToolTipText(END_PAGE_TIP);
  
          netRecommendPlayAllButton.setIconTextGap(5);
  
          // 按钮文字与图标的位置
          int gap = 15;
          playlistRecommendButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          playlistRecommendButton.setIconTextGap(gap);
          highQualityPlaylistButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          highQualityPlaylistButton.setIconTextGap(gap);
          hotMusicButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          hotMusicButton.setIconTextGap(gap);
          newMusicButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          newMusicButton.setIconTextGap(gap);
          newAlbumRecommendButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          newAlbumRecommendButton.setIconTextGap(gap);
          artistListRecommendButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          artistListRecommendButton.setIconTextGap(gap);
          newRadioRecommendButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          newRadioRecommendButton.setIconTextGap(gap);
          hotRadioRecommendButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          hotRadioRecommendButton.setIconTextGap(gap);
          programRecommendButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          programRecommendButton.setIconTextGap(gap);
          mvRecommendButton.setHorizontalTextPosition(SwingConstants.RIGHT);
          mvRecommendButton.setIconTextGap(gap);
  
          // 推荐工具栏网格布局
          recommendToolBar.setLayout(new GridLayout(2, 5));
          recommendToolBar.add(playlistRecommendButton);
          recommendToolBar.add(highQualityPlaylistButton);
          recommendToolBar.add(hotMusicButton);
          recommendToolBar.add(newMusicButton);
          recommendToolBar.add(newAlbumRecommendButton);
          recommendToolBar.add(artistListRecommendButton);
          recommendToolBar.add(newRadioRecommendButton);
          recommendToolBar.add(hotRadioRecommendButton);
          recommendToolBar.add(programRecommendButton);
          recommendToolBar.add(mvRecommendButton);
  
          recommendToolBar.setMaximumSize(new Dimension(Integer.MAX_VALUE, 50));
          recommendLeftBox.add(recommendToolBar);
  
          // 推荐歌单/专辑中歌曲的工具栏，暂时不放入
          musicRecommendToolBar.add(recommendBackwardButton);
          musicRecommendToolBar.add(Box.createHorizontalGlue());
  
          for (String name : NetMusicSource.NAMES) netRecommendSourceComboBox.addItem(name);
          netRecommendSourceComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
  
              // 加载对应源的标签
              ((DefaultComboBoxModel) netRecommendTagComboBox.getModel()).removeAllElements();
              if (currRecommendTab == RecommendTabIndex.PLAYLIST_RECOMMEND) {
                  for (String tag : Tags.recPlaylistTag.keySet()) {
                      if (shouldShowTag(Tags.recPlaylistTag, Tags.recPlaylistMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              } else if (currRecommendTab == RecommendTabIndex.HIGH_QUALITY_PLAYLIST_RECOMMEND) {
                  for (String tag : Tags.hotPlaylistTag.keySet()) {
                      if (shouldShowTag(Tags.hotPlaylistTag, Tags.hotPlaylistMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              } else if (currRecommendTab == RecommendTabIndex.HOT_MUSIC_RECOMMEND) {
                  for (String tag : Tags.hotSongTag.keySet()) {
                      if (shouldShowTag(Tags.hotSongTag, Tags.hotSongMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              } else if (currRecommendTab == RecommendTabIndex.NEW_MUSIC_RECOMMEND) {
                  for (String tag : Tags.newSongTag.keySet()) {
                      if (shouldShowTag(Tags.newSongTag, Tags.newSongMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              } else if (currRecommendTab == RecommendTabIndex.NEW_ALBUM_RECOMMEND) {
                  for (String tag : Tags.newAlbumTag.keySet()) {
                      if (shouldShowTag(Tags.newAlbumTag, Tags.newAlbumMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              } else if (currRecommendTab == RecommendTabIndex.ARTIST_LIST_RECOMMEND) {
                  for (String tag : Tags.artistTag.keySet()) {
                      if (shouldShowTag(Tags.artistTag, Tags.artistMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              } else if (currRecommendTab == RecommendTabIndex.HOT_RADIO_RECOMMEND) {
                  for (String tag : Tags.radioTag.keySet()) {
                      if (shouldShowTag(Tags.radioTag, Tags.radioMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              } else if (currRecommendTab == RecommendTabIndex.PROGRAM_RECOMMEND) {
                  for (String tag : Tags.programTag.keySet()) {
                      if (shouldShowTag(Tags.programTag, Tags.programMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              } else if (currRecommendTab == RecommendTabIndex.MV_RECOMMEND) {
                  for (String tag : Tags.mvTag.keySet()) {
                      if (shouldShowTag(Tags.mvTag, Tags.mvMap, tag))
                          netRecommendTagComboBox.addItem(tag);
                  }
              }
  
              netRecommendCurrPage = 1;
              netRecommendRefreshButton.doClick();
          });
  
          netRecommendSortTypeComboBox.addItem(I18n.getText("ascend"));
          netRecommendSortTypeComboBox.addItem(I18n.getText("descend"));
          // 电台排序类型切换事件
          netRecommendSortTypeComboBox.addItemListener(e -> {
              // 避免事件被处理 2 次！
              if (e.getStateChange() != ItemEvent.SELECTED) return;
              netMusicInRecommendCurrPage = 1;
              netRecommendRefreshButton.doClick();
          });
  
          // 数量标签
          recommendCountPanel.add(netRecommendSourceComboBox);
          recommendCountPanel.add(netRecommendTagComboBox);
          recommendCountPanel.add(netRecommendSortTypeComboBox);
          recommendCountPanel.add(netRecommendPlayAllButton);
          recommendCountPanel.add(netRecommendStartPageButton);
          recommendCountPanel.add(netRecommendLastPageButton);
          recommendCountPanel.add(recommendCountLabel);
          recommendCountPanel.add(netRecommendNextPageButton);
          recommendCountPanel.add(netRecommendEndPageButton);
          recommendCountPanel.add(netRecommendRefreshButton);
          recommendCountPanel.add(netRecommendPageTextField);
          recommendCountPanel.add(netRecommendGoButton);
          recommendCountPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
          recommendCountPanel.setVisible(false);
          recommendLeftBox.add(recommendCountPanel);
      }
  
      // 初始化推荐歌单/专辑/歌手/电台列表
      private void initItemRecommendList() {
          itemRecommendList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
          itemRecommendList.setModel(playlistRecommendListModel);
          itemRecommendList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = itemRecommendList.locationToIndex(e.getPoint());
                  Rectangle bounds = itemRecommendList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  ItemRecommendListRenderer renderer = (ItemRecommendListRenderer) itemRecommendList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  itemRecommendList.repaint();
              }
          });
          itemRecommendList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  ItemRecommendListRenderer renderer = (ItemRecommendListRenderer) itemRecommendList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  itemRecommendList.repaint();
              }
          });
          openRecommendItemAction = () -> {
              NetResource resource = itemRecommendList.getSelectedValue();
              if (resource == null) return;
              // 打开的是 MV
              if (resource instanceof NetMvInfo) {
                  playMv(MvCompSourceType.MV_RECOMMEND_LIST);
                  return;
              }
              // 检查收藏按钮
              checkDescriptionCollectionStatus(recommendItemDescriptionCollectionButton, resource);
              loadingAndRun(() -> {
                  List<Future<?>> taskList = new LinkedList<>();
                  // 打开的是歌单
                  if (resource instanceof NetPlaylistInfo) {
                      NetPlaylistInfo playlistInfo = (NetPlaylistInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          recommendItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          recommendItemCoverAndNameLabel.setText(LOADING_MSG);
                          recommendItemTagLabel.setText(LOADING_MSG);
                          recommendItemTagLabel.setVisible(true);
                          recommendItemDescriptionLabel.setText(LOADING_MSG);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillPlaylistInfo(playlistInfo);
                                  updateRenderer(itemRecommendList);
                                  recommendItemCoverAndNameLabel.setText(StringUtil.textToHtml(playlistInfo.getName()));
                                  if (playlistInfo.getTag() != null)
                                      recommendItemTagLabel.setText(StringUtil.textToHtml(playlistInfo.getTag().isEmpty() ? "" : "标签：" + playlistInfo.getTag()));
                                  if (playlistInfo.getDescription() != null)
                                      recommendItemDescriptionLabel.setText(StringUtil.textToHtml(playlistInfo.getDescription()));
                                  if (playlistInfo.hasCoverImg()) {
                                      recommendItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(playlistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      playlistInfo.setInvokeLater(() -> {
                                          recommendItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(playlistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          itemRecommendList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  recommendItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  recommendItemTagLabel.setText(LOAD_FAILED);
                                  recommendItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到歌单的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInPlaylist(playlistInfo, netMusicInRecommendCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新歌单歌曲数量显示
                              recommendCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRecommendCurrPage, netMusicInRecommendMaxPage));
                              recommendItemListCountBox.add(recommendCountPanel);
                              recommendItemListBox.add(recommendItemListCountBox);
                              // 歌单列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForPlaylistRecommendModel);
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(recommendItemListBox);
                              // 推荐工具栏添加，需要更新 LAF
                              recommendLeftBox.add(musicRecommendToolBar, 0);
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForPlaylistRecommendModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForPlaylistRecommendModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForPlaylistRecommendModel);
                              recommendItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              recommendLeftBox.remove(recommendToolBar);
                              recommendBackwardButton.setEnabled(true);
                              netRecommendSourceComboBox.setVisible(false);
                              netRecommendSortTypeComboBox.setVisible(false);
                              netRecommendPlayAllButton.setVisible(true);
                              netRecommendTagComboBox.setVisible(false);
                              if (netMusicListForPlaylistRecommendModel.isEmpty()) {
                                  recommendItemListCountBox.remove(netMusicScrollPane);
                                  recommendItemListCountBox.add(emptyHintPanel);
                              } else {
                                  recommendItemListCountBox.remove(emptyHintPanel);
                                  recommendItemListCountBox.add(netMusicScrollPane);
                              }
                              // 切换后一定要刷新！
                              recommendLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
                  // 打开的是专辑
                  else if (resource instanceof NetAlbumInfo) {
                      NetAlbumInfo albumInfo = (NetAlbumInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          recommendItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          recommendItemCoverAndNameLabel.setText(LOADING_MSG);
                          recommendItemTagLabel.setText("");
                          recommendItemTagLabel.setVisible(false);
                          recommendItemDescriptionLabel.setText(LOADING_MSG);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillAlbumInfo(albumInfo);
                                  updateRenderer(itemRecommendList);
                                  recommendItemCoverAndNameLabel.setText(StringUtil.textToHtml(albumInfo.getName()));
                                  if (albumInfo.getDescription() != null)
                                      recommendItemDescriptionLabel.setText(StringUtil.textToHtml(albumInfo.getDescription()));
                                  if (albumInfo.hasCoverImg()) {
                                      recommendItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(albumInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      albumInfo.setInvokeLater(() -> {
                                          recommendItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(albumInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          itemRecommendList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  recommendItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  recommendItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到专辑的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInAlbum(albumInfo, netMusicInRecommendCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新专辑歌曲数量显示
                              recommendCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRecommendCurrPage, netMusicInRecommendMaxPage));
                              recommendItemListCountBox.add(recommendCountPanel);
                              recommendItemListBox.add(recommendItemListCountBox);
                              // 专辑列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForAlbumRecommendModel);
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(recommendItemListBox);
                              // 推荐工具栏添加，需要更新 LAF
                              recommendLeftBox.add(musicRecommendToolBar, 0);
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForAlbumRecommendModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForAlbumRecommendModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForAlbumRecommendModel);
                              recommendItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              recommendLeftBox.remove(recommendToolBar);
                              recommendBackwardButton.setEnabled(true);
                              netRecommendSourceComboBox.setVisible(false);
                              netRecommendSortTypeComboBox.setVisible(false);
                              netRecommendPlayAllButton.setVisible(true);
                              netRecommendTagComboBox.setVisible(false);
                              if (netMusicListForAlbumRecommendModel.isEmpty()) {
                                  recommendItemListCountBox.remove(netMusicScrollPane);
                                  recommendItemListCountBox.add(emptyHintPanel);
                              } else {
                                  recommendItemListCountBox.remove(emptyHintPanel);
                                  recommendItemListCountBox.add(netMusicScrollPane);
                              }
                              // 切换后一定要刷新！
                              recommendLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
                  // 打开的是歌手
                  else if (resource instanceof NetArtistInfo) {
                      NetArtistInfo artistInfo = (NetArtistInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          recommendItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          recommendItemCoverAndNameLabel.setText(LOADING_MSG);
                          recommendItemTagLabel.setText(LOADING_MSG);
                          recommendItemTagLabel.setVisible(true);
                          recommendItemDescriptionLabel.setText(LOADING_MSG);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillArtistInfo(artistInfo);
                                  updateRenderer(itemRecommendList);
                                  recommendItemCoverAndNameLabel.setText(StringUtil.textToHtml(artistInfo.getName()));
                                  if (artistInfo.getTag() != null)
                                      recommendItemTagLabel.setText(StringUtil.textToHtml(artistInfo.getTag().isEmpty() ? "" : artistInfo.getTag()));
                                  if (artistInfo.getDescription() != null)
                                      recommendItemDescriptionLabel.setText(StringUtil.textToHtml(artistInfo.getDescription()));
                                  if (artistInfo.hasCoverImg()) {
                                      recommendItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(artistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      artistInfo.setInvokeLater(() -> {
                                          recommendItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(artistInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          itemRecommendList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  recommendItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  recommendItemTagLabel.setText(LOAD_FAILED);
                                  recommendItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到歌手的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInArtist(artistInfo, netMusicInRecommendCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新歌手歌曲数量显示
                              recommendCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRecommendCurrPage, netMusicInRecommendMaxPage));
                              recommendItemListCountBox.add(recommendCountPanel);
                              recommendItemListBox.add(recommendItemListCountBox);
                              // 歌手列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForArtistRecommendModel);
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(recommendItemListBox);
                              // 推荐工具栏添加，需要更新 LAF
                              recommendLeftBox.add(musicRecommendToolBar, 0);
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForArtistRecommendModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForArtistRecommendModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForArtistRecommendModel);
                              recommendItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              recommendLeftBox.remove(recommendToolBar);
                              recommendBackwardButton.setEnabled(true);
                              netRecommendSourceComboBox.setVisible(false);
                              netRecommendSortTypeComboBox.setVisible(false);
                              netRecommendPlayAllButton.setVisible(true);
                              netRecommendTagComboBox.setVisible(false);
                              if (netMusicListForArtistRecommendModel.isEmpty()) {
                                  recommendItemListCountBox.remove(netMusicScrollPane);
                                  recommendItemListCountBox.add(emptyHintPanel);
                              } else {
                                  recommendItemListCountBox.remove(emptyHintPanel);
                                  recommendItemListCountBox.add(netMusicScrollPane);
                              }
                              // 切换后一定要刷新！
                              recommendLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
                  // 打开的是电台
                  else if (resource instanceof NetRadioInfo) {
                      NetRadioInfo radioInfo = (NetRadioInfo) resource;
                      // 加载封面图片和描述
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          BufferedImage coverImg = ImageUtil.borderShadow(ImageUtil.dye(loadingImage, currUIStyle.getIconColor()));
                          recommendItemCoverAndNameLabel.setIcon(new ImageIcon(coverImg));
                          recommendItemCoverAndNameLabel.setText(LOADING_MSG);
                          recommendItemTagLabel.setText(LOADING_MSG);
                          recommendItemTagLabel.setVisible(true);
                          recommendItemDescriptionLabel.setText(LOADING_MSG);
                          GlobalExecutors.requestExecutor.execute(() -> {
                              try {
                                  MusicServerUtil.fillRadioInfo(radioInfo);
                                  updateRenderer(itemRecommendList);
                                  recommendItemCoverAndNameLabel.setText(StringUtil.textToHtml(radioInfo.getName()));
                                  if (radioInfo.getTag() != null)
                                      recommendItemTagLabel.setText(StringUtil.textToHtml(radioInfo.getTag().isEmpty() ? "" : "标签：" + radioInfo.getTag()));
                                  if (radioInfo.getDescription() != null)
                                      recommendItemDescriptionLabel.setText(StringUtil.textToHtml(radioInfo.getDescription()));
                                  if (radioInfo.hasCoverImg()) {
                                      recommendItemCoverAndNameLabel.setIcon(new ImageIcon(
                                              ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(radioInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                      ));
                                  } else {
                                      radioInfo.setInvokeLater(() -> {
                                          recommendItemCoverAndNameLabel.setIcon(new ImageIcon(
                                                  ImageUtil.borderShadow(ImageUtil.radius(ImageUtil.width(radioInfo.getCoverImg(), coverImageWidth), MIDDLE_ARC))
                                          ));
                                          itemRecommendList.repaint();
                                      });
                                  }
                              } catch (HttpException e) {
                                  recommendItemCoverAndNameLabel.setText(LOAD_FAILED);
                                  recommendItemTagLabel.setText(LOAD_FAILED);
                                  recommendItemDescriptionLabel.setText(LOAD_FAILED);
                              }
                          });
                      }));
  
                      // 得到电台的音乐信息
                      taskList.add(GlobalExecutors.requestExecutor.submit(() -> {
                          try {
                              CommonResult<NetMusicInfo> result = MusicServerUtil.getMusicInfoInRadio(
                                      radioInfo, netRecommendSortTypeComboBox.getSelectedIndex(), netMusicInRecommendCurrPage = 1, limit);
                              List<NetMusicInfo> musicInfos = result.data;
                              int total = result.total;
                              netMusicInRecommendMaxPage = Math.max(total % limit == 0 ? total / limit : total / limit + 1, 1);
                              // 更新电台歌曲数量显示
                              recommendCountLabel.setText(String.format(PAGINATION_MSG, netMusicInRecommendCurrPage, netMusicInRecommendMaxPage));
                              recommendItemListCountBox.add(recommendCountPanel);
                              recommendItemListBox.add(recommendItemListCountBox);
                              // 电台列表切换到在线音乐列表
                              netMusicList.setModel(netMusicListForRadioRecommendModel);
                              recommendLeftBox.remove(itemRecommendScrollPane);
                              recommendLeftBox.add(recommendItemListBox);
                              // 推荐工具栏添加，需要更新 LAF
                              recommendLeftBox.add(musicRecommendToolBar, 0);
                              // 添加数据建议弄到更新数量显示之后，不然可能会不显示！
                              netMusicList.setModel(emptyListModel);
                              netMusicListForRadioRecommendModel.clear();
                              musicInfos.forEach(musicInfo -> {
                                  globalExecutor.execute(() -> updateCollection(musicInfo));
                                  netMusicListForRadioRecommendModel.addElement(musicInfo);
                              });
                              netMusicList.setModel(netMusicListForRadioRecommendModel);
                              recommendItemDescriptionScrollPane.setVValue(0);
                              netMusicScrollPane.setVValue(0);
                              recommendLeftBox.remove(recommendToolBar);
                              recommendBackwardButton.setEnabled(true);
                              netRecommendSourceComboBox.setVisible(false);
                              netRecommendSortTypeComboBox.setVisible(radioInfo.fromXM());
                              netRecommendPlayAllButton.setVisible(true);
                              netRecommendTagComboBox.setVisible(false);
                              if (netMusicListForRadioRecommendModel.isEmpty()) {
                                  recommendItemListCountBox.remove(netMusicScrollPane);
                                  recommendItemListCountBox.add(emptyHintPanel);
                              } else {
                                  recommendItemListCountBox.remove(emptyHintPanel);
                                  recommendItemListCountBox.add(netMusicScrollPane);
                              }
                              // 切换后一定要刷新！
                              recommendLeftBox.repaint();
                          } catch (IORuntimeException runtimeException) {
                              // 无网络连接
                              new TipDialog(THIS, NO_NET_MSG).showDialog();
                          } catch (HttpException httpException) {
                              // 请求超时
                              new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                          } catch (JSONException jsonException) {
                              // 接口问题
                              new TipDialog(THIS, API_ERROR_MSG).showDialog();
                          }
                      }));
                  }
  
                  taskList.forEach(task -> {
                      try {
                          task.get();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      } catch (ExecutionException e) {
                          e.printStackTrace();
                      }
                  });
              });
          };
          itemRecommendList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      openRecommendItemAction.run();
                  }
              }
          });
          itemRecommendList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击打开歌单/专辑/歌手/电台/MV
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      openRecommendItemAction.run();
                  }
  
                  // 右键推荐列表
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (itemRecommendList.getModel().getSize() == 0) return;
                      // 得到鼠标光标所在的选项并选中
                      int index = itemRecommendList.locationToIndex(e.getPoint());
                      if (index == -1) return;
                      if (!itemRecommendList.isSelectedIndex(index)) itemRecommendList.setSelectedIndex(index);
                      NetResource resource = itemRecommendList.getSelectedValue();
                      CustomMenuItem menuItem = null;
                      if (resource instanceof NetPlaylistInfo) menuItem = netPlaylistCollectMenuItem;
                      else if (resource instanceof NetAlbumInfo) menuItem = netAlbumCollectMenuItem;
                      else if (resource instanceof NetArtistInfo) menuItem = netArtistCollectMenuItem;
                      else if (resource instanceof NetRadioInfo) menuItem = netRadioCollectMenuItem;
                      else if (resource instanceof NetMvInfo) menuItem = netMvCollectMenuItem;
                      if (hasBeenCollected(resource)) {
                          menuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                          menuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                      } else {
                          menuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                          menuItem.setText(COLLECT_MENU_ITEM_TEXT);
                      }
  
                      if (currRecommendTab == RecommendTabIndex.PLAYLIST_RECOMMEND
                              || currRecommendTab == RecommendTabIndex.HIGH_QUALITY_PLAYLIST_RECOMMEND) {
                          netPlaylistPopupMenu.show(itemRecommendList, e.getX(), e.getY());
                      } else if (currRecommendTab == RecommendTabIndex.NEW_ALBUM_RECOMMEND) {
                          netAlbumPopupMenu.show(itemRecommendList, e.getX(), e.getY());
                      } else if (currRecommendTab == RecommendTabIndex.ARTIST_LIST_RECOMMEND) {
                          netArtistPopupMenu.show(itemRecommendList, e.getX(), e.getY());
                      } else if (currRecommendTab == RecommendTabIndex.HOT_RADIO_RECOMMEND
                              || currRecommendTab == RecommendTabIndex.NEW_RADIO_RECOMMEND) {
                          netRadioPopupMenu.show(itemRecommendList, e.getX(), e.getY());
                      } else if (currRecommendTab == RecommendTabIndex.MV_RECOMMEND) {
                          netMvPopupMenu.show(itemRecommendList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 最佳大小
          itemRecommendScrollPane.setPreferredSize(new Dimension(200, 3000));
          recommendLeftBox.add(itemRecommendScrollPane);
      }
  
      // 判断标签是不是可以显示
      private boolean shouldShowTag(Map<String, String[]> tags, int[] map, String tag) {
          if ("默认".equals(tag)) return true;
          int si = netRecommendSourceComboBox.getSelectedIndex();
          String[] vals = tags.get(tag);
          for (int i = 0, len = map.length; i < len; i++) {
              if (map[i] == si && StringUtil.notEmpty(vals[i])) return true;
          }
          return false;
      }
  
      // 初始化下载工具条
      private void initDownloadToolBar() {
          // 按钮悬浮和点击效果
          restartSelectedTasksButton.addMouseListener(new ButtonMouseListener(restartSelectedTasksButton, THIS));
          cancelSelectedTasksButton.addMouseListener(new ButtonMouseListener(cancelSelectedTasksButton, THIS));
          removeSelectedTasksButton.addMouseListener(new ButtonMouseListener(removeSelectedTasksButton, THIS));
          restartAllTasksButton.addMouseListener(new ButtonMouseListener(restartAllTasksButton, THIS));
          cancelAllTasksButton.addMouseListener(new ButtonMouseListener(cancelAllTasksButton, THIS));
          removeAllTasksButton.addMouseListener(new ButtonMouseListener(removeAllTasksButton, THIS));
  
          // 重新开始选中任务
          restartSelectedTasksButton.addActionListener(e -> restartTaskMenuItem.doClick());
          // 取消选中任务
          cancelSelectedTasksButton.addActionListener(e -> cancelTaskMenuItem.doClick());
          // 删除选中任务
          removeSelectedTasksButton.addActionListener(e -> removeTaskMenuItem.doClick());
          // 重新开始全部任务
          restartAllTasksButton.addActionListener(e -> {
              ConfirmDialog dialog = new ConfirmDialog(this, ASK_RESTART_ALL_TASKS_MSG, YES, NO);
              dialog.showDialog();
              if (dialog.getResponse() == JOptionPane.YES_OPTION) {
                  Enumeration<Task> tasks = downloadListModel.elements();
                  while (tasks.hasMoreElements()) {
                      Task task = tasks.nextElement();
                      if (!task.isProcessing()) task.start();
                  }
              }
          });
          // 取消全部任务
          cancelAllTasksButton.addActionListener(e -> {
              ConfirmDialog dialog = new ConfirmDialog(this, ASK_CANCEL_ALL_TASKS_MSG, YES, NO);
              dialog.showDialog();
              if (dialog.getResponse() == JOptionPane.YES_OPTION) {
                  Enumeration<Task> tasks = downloadListModel.elements();
                  while (tasks.hasMoreElements()) {
                      Task task = tasks.nextElement();
                      if (task.isProcessing()) task.stop();
                  }
              }
          });
          // 删除全部任务
          removeAllTasksButton.addActionListener(e -> {
              ConfirmDialog dialog = new ConfirmDialog(this, ASK_REMOVE_ALL_TASKS_MSG, YES, NO);
              dialog.showDialog();
              if (dialog.getResponse() == JOptionPane.YES_OPTION) {
                  downloadListModel.clear();
                  new TipDialog(THIS, CLEAR_SUCCESS_MSG).showDialog();
              }
          });
  
          // 标签间距
          taskCountLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
          // 帮助提示
          restartSelectedTasksButton.setToolTipText(RESTART_SELECTED_TASKS_TIP);
          cancelSelectedTasksButton.setToolTipText(CANCEL_SELECTED_TASKS_TIP);
          removeSelectedTasksButton.setToolTipText(REMOVE_SELECTED_TASKS_TIP);
          restartAllTasksButton.setToolTipText(RESTART_ALL_TASKS_TIP);
          cancelAllTasksButton.setToolTipText(CANCEL_ALL_TASKS_TIP);
          removeAllTasksButton.setToolTipText(REMOVE_ALL_TASKS_TIP);
  
          downloadToolBar.add(restartSelectedTasksButton);
          downloadToolBar.add(restartAllTasksButton);
          downloadToolBar.add(cancelSelectedTasksButton);
          downloadToolBar.add(cancelAllTasksButton);
          downloadToolBar.add(removeSelectedTasksButton);
          downloadToolBar.add(removeAllTasksButton);
          downloadToolBar.add(taskCountLabel);
          // 加胶水让工具栏左对齐
          downloadToolBar.add(Box.createHorizontalGlue());
          downloadLeftBox.add(downloadToolBar);
      }
  
      // 初始化下载列表
      private void initDownloadList() {
          ListDataListener countListener = new ListDataListener() {
              @Override
              public void intervalAdded(ListDataEvent e) {
                  int size = downloadListModel.getSize();
                  taskCountLabel.setText(String.format(TOTAL_MSG, size));
                  if (size == 1) {
                      downloadLeftBox.remove(emptyHintPanel);
                      downloadLeftBox.add(downloadListScrollPane);
                  }
              }
  
              @Override
              public void intervalRemoved(ListDataEvent e) {
                  int size = downloadListModel.getSize();
                  taskCountLabel.setText(String.format(TOTAL_MSG, size));
                  if (size == 0) {
                      downloadLeftBox.remove(downloadListScrollPane);
                      downloadLeftBox.add(emptyHintPanel);
                      downloadLeftBox.repaint();
                  }
              }
  
              @Override
              public void contentsChanged(ListDataEvent e) {
  
              }
          };
          downloadListModel.addListDataListener(countListener);
          downloadList.setModel(downloadListModel);
          downloadList.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = downloadList.locationToIndex(e.getPoint());
                  Rectangle bounds = downloadList.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  DownloadListRenderer renderer = (DownloadListRenderer) downloadList.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  downloadList.repaint();
              }
          });
          downloadList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  DownloadListRenderer renderer = (DownloadListRenderer) downloadList.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  downloadList.repaint();
              }
          });
          downloadList.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      downloadPlayMenuItem.doClick();
                  }
              }
          });
          downloadList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击播放
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      downloadPlayMenuItem.doClick();
                  }
                  // 鼠标右键弹出菜单
                  if (e.getButton() == MouseEvent.BUTTON3) {
                      if (downloadList.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = downloadList.locationToIndex(e.getPoint());
                          if (index != -1 && !downloadList.isSelectedIndex(index)
                                  || downloadList.isSelectedIndex(index) && downloadList.getSelectedIndices().length == 1) {
                              downloadList.setSelectedIndex(index);
                          }
                          // 多选任务时，只要有一个任务满足条件就启用
                          List<Task> tasks = downloadList.getSelectedValuesList();
                          cancelTaskMenuItem.setEnabled(false);
                          restartTaskMenuItem.setEnabled(false);
                          Task t = tasks.get(0);
                          downloadNextPlayMenuItem.setEnabled(t.isMusic());
                          downloadEditInfoMenuItem.setEnabled(t.isFinished() && t.isMusic()
                                  && (t.isMp3() || t.isFlac()));
                          for (Task task : tasks) {
                              if (task.isProcessing()) cancelTaskMenuItem.setEnabled(true);
                              else restartTaskMenuItem.setEnabled(true);
                              if (cancelTaskMenuItem.isEnabled() && restartTaskMenuItem.isEnabled()) break;
                          }
                          downloadPopupMenu.show(downloadList, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 播放菜单项
          downloadPlayMenuItem.addActionListener(e -> {
              Task task = downloadList.getSelectedValue();
              if (task == null) return;
              if (task.isFinished()) {
                  if (task.isMusic()) playExecutor.execute(() -> playSelected(downloadList, false));
                  else playMv(MvCompSourceType.DOWNLOAD_LIST);
              } else {
                  new TipDialog(THIS, WAIT_FOR_TASK_COMPLETED_MSG).showDialog();
              }
          });
          // 下一首播放
          downloadNextPlayMenuItem.addActionListener(e -> nextPlay(downloadList));
          // 打开文件所在位置菜单项
          downloadLocateFileMenuItem.addActionListener(e -> {
              Task task = downloadList.getSelectedValue();
              File file = new File(task.getDest());
              DesktopUtil.explore(file.exists() ? file :
                      task.isMusic() ? new File(SimplePath.DOWNLOAD_MUSIC_PATH) : new File(SimplePath.DOWNLOAD_MV_PATH));
          });
          // 编辑歌曲信息菜单项
          downloadEditInfoMenuItem.addActionListener(e -> {
              Task task = downloadList.getSelectedValue();
              if (task.isMusic()) {
                  AudioFile file = new AudioFile(task.getDest());
                  if (file.exists()) {
                      MediaUtil.fillAudioFileInfo(file);
                      editInfo(file);
                  } else new TipDialog(THIS, FILE_NOT_FOUND_MSG).showDialog();
              }
          });
          // 取消任务菜单项
          cancelTaskMenuItem.addActionListener(e -> {
              List<Task> tasks = downloadList.getSelectedValuesList();
              for (Task task : tasks) {
                  if (task.isProcessing()) task.stop();
              }
          });
          // 重新开始任务菜单项
          restartTaskMenuItem.addActionListener(e -> {
              List<Task> tasks = downloadList.getSelectedValuesList();
              for (Task task : tasks) {
                  if (!task.isProcessing()) task.start();
              }
          });
          // 删除任务菜单项
          removeTaskMenuItem.addActionListener(e -> {
              List<Task> tasks = downloadList.getSelectedValuesList();
              if (tasks.isEmpty()) return;
              ConfirmDialog dialog = new ConfirmDialog(this, ASK_REMOVE_SELECTED_TASKS_MSG, YES, NO, true, ASK_REMOVE_FILE_MSG);
              dialog.showDialog();
              if (dialog.getResponse() != JOptionPane.YES_OPTION) return;
              boolean checked = dialog.isChecked();
              downloadList.setModel(emptyListModel);
              for (Task task : tasks) {
                  if (task.isProcessing()) task.stop();
                  downloadListModel.removeElement(task);
                  if (!checked) continue;
                  // 删除歌词文件
                  if (task.isMusic()) {
                      NetMusicInfo musicInfo = (NetMusicInfo) task.getResource();
                      FileUtil.delete(SimplePath.DOWNLOAD_MUSIC_PATH + musicInfo.toSimpleLrcFileName());
                  }
                  FileUtil.delete(task.getDest());
              }
              downloadList.setModel(downloadListModel);
              new TipDialog(THIS, REMOVE_SUCCESS_MSG).showDialog();
          });
  
          downloadPopupMenu.add(downloadPlayMenuItem);
          downloadPopupMenu.add(downloadNextPlayMenuItem);
          downloadPopupMenu.add(downloadLocateFileMenuItem);
          downloadPopupMenu.add(downloadEditInfoMenuItem);
          downloadPopupMenu.add(cancelTaskMenuItem);
          downloadPopupMenu.add(restartTaskMenuItem);
          downloadPopupMenu.add(removeTaskMenuItem);
  
          downloadListScrollPane.setPreferredSize(new Dimension(600, 300));
          downloadLeftBox.add(downloadListScrollPane);
      }
  
      // 初始化播放队列工具条
      private void initPlayQueueToolBar() {
          // 按钮悬浮和点击效果
          playQueueRemoveToolButton.addMouseListener(new ButtonMouseListener(playQueueRemoveToolButton, THIS));
          playQueueClearToolButton.addMouseListener(new ButtonMouseListener(playQueueClearToolButton, THIS));
          playQueueDuplicateToolButton.addMouseListener(new ButtonMouseListener(playQueueDuplicateToolButton, THIS));
          playQueueReverseToolButton.addMouseListener(new ButtonMouseListener(playQueueReverseToolButton, THIS));
          playQueueMoveUpToolButton.addMouseListener(new ButtonMouseListener(playQueueMoveUpToolButton, THIS));
          playQueueMoveDownToolButton.addMouseListener(new ButtonMouseListener(playQueueMoveDownToolButton, THIS));
  
          // 从播放队列删除
          playQueueRemoveToolButton.addActionListener(e -> {
              List<MusicResource> selectedValues = playQueue.getSelectedValuesList();
              if (!selectedValues.isEmpty()) {
                  ConfirmDialog confirmDialog = new ConfirmDialog(THIS,
                          ASK_REMOVE_SONGS_FROM_PLAY_QUEUE_MSG, YES, NO);
                  confirmDialog.showDialog();
                  int response = confirmDialog.getResponse();
                  // 删除选中的歌曲
                  if (response == JOptionPane.YES_OPTION) {
                      // 解决删除元素带来的性能问题
                      playQueue.setModel(emptyListModel);
                      for (MusicResource resource : selectedValues) {
                          if (player.loadedMusicResource(resource)) unload();
                          playQueueModel.removeElement(resource);
                      }
                      playQueue.setModel(playQueueModel);
                      updateCurrSong();
                      new TipDialog(THIS, REMOVE_SUCCESS_MSG).showDialog();
                  }
              }
          });
          // 清空播放队列
          playQueueClearToolButton.addActionListener(e -> {
              ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_CLEAR_PLAY_QUEUE_MSG, YES, NO);
              confirmDialog.showDialog();
              int response = confirmDialog.getResponse();
              if (response == JOptionPane.YES_OPTION) {
                  if (!player.isEmpty()) unload();
                  playQueueModel.clear();
                  updateCurrSong();
                  new TipDialog(THIS, CLEAR_SUCCESS_MSG).showDialog();
              }
          });
          // 去重
          playQueueDuplicateToolButton.addActionListener(e -> {
              ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_DUPLICATE_MSG, YES, NO);
              confirmDialog.showDialog();
              int response = confirmDialog.getResponse();
              if (response == JOptionPane.YES_OPTION) {
                  Set<Object> set = new HashSet<>();
                  // 解决删除元素带来的性能问题
                  playQueue.setModel(emptyListModel);
                  for (int i = 0; i < playQueueModel.getSize(); i++) {
                      MusicResource resource = playQueueModel.get(i);
                      if (!set.contains(resource)) set.add(resource);
                      else playQueueModel.remove(i--);
                  }
                  playQueue.setModel(playQueueModel);
                  updateCurrSong();
                  new TipDialog(THIS, DUPLICATE_SUCCESS_MSG).showDialog();
              }
          });
          // 倒序
          playQueueReverseToolButton.addActionListener(e -> {
              ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_REVERSE_MSG, YES, NO);
              confirmDialog.showDialog();
              int response = confirmDialog.getResponse();
              if (response == JOptionPane.YES_OPTION) {
                  // 解决修改元素带来的性能问题
                  playQueue.setModel(emptyListModel);
                  for (int i = 0, s = playQueueModel.size(), half = s / 2; i < half; i++) {
                      MusicResource t = playQueueModel.get(i);
                      playQueueModel.set(i, playQueueModel.get(s - 1 - i));
                      playQueueModel.set(s - 1 - i, t);
                  }
                  playQueue.setModel(playQueueModel);
                  updateCurrSong();
                  new TipDialog(THIS, REVERSE_SUCCESS_MSG).showDialog();
              }
          });
          // 上移下移
          playQueueMoveUpToolButton.addActionListener(e -> {
              int selectedIndex = playQueue.getSelectedIndex();
              if (selectedIndex != -1 && selectedIndex != 0) {
                  MusicResource r1 = playQueueModel.get(selectedIndex - 1);
                  MusicResource r2 = playQueueModel.get(selectedIndex);
                  playQueueModel.set(selectedIndex - 1, r2);
                  playQueueModel.set(selectedIndex, r1);
                  playQueue.setSelectedIndex(selectedIndex - 1);
                  if (player.loadedMusicResource(r1)) currSong = selectedIndex;
                  else if (player.loadedMusicResource(r2)) currSong = selectedIndex - 1;
              }
          });
          playQueueMoveDownToolButton.addActionListener(e -> {
              int selectedIndex = playQueue.getSelectedIndex();
              if (selectedIndex != -1 && selectedIndex != playQueueModel.getSize() - 1) {
                  MusicResource r1 = playQueueModel.get(selectedIndex);
                  MusicResource r2 = playQueueModel.get(selectedIndex + 1);
                  playQueueModel.set(selectedIndex, r2);
                  playQueueModel.set(selectedIndex + 1, r1);
                  playQueue.setSelectedIndex(selectedIndex + 1);
                  if (player.loadedMusicResource(r1)) currSong = selectedIndex + 1;
                  else if (player.loadedMusicResource(r2)) currSong = selectedIndex;
              }
          });
  
          // 帮助提示
          playQueueRemoveToolButton.setToolTipText(REMOVE_FROM_PLAY_QUEUE_TIP);
          playQueueClearToolButton.setToolTipText(CLEAR_PLAY_QUEUE_TIP);
          playQueueDuplicateToolButton.setToolTipText(DUPLICATE_TIP);
          playQueueReverseToolButton.setToolTipText(REVERSE_TIP);
          playQueueMoveUpToolButton.setToolTipText(MOVE_UP_TIP);
          playQueueMoveDownToolButton.setToolTipText(MOVE_DOWN_TIP);
  
          playQueueToolBar.add(playQueueRemoveToolButton);
          playQueueToolBar.add(playQueueClearToolButton);
          playQueueToolBar.add(playQueueDuplicateToolButton);
          playQueueToolBar.add(playQueueReverseToolButton);
          playQueueToolBar.add(playQueueMoveUpToolButton);
          playQueueToolBar.add(playQueueMoveDownToolButton);
          playQueueToolBar.add(playQueueCountLabel);
          // 加胶水让工具栏左对齐
          playQueueToolBar.add(Box.createHorizontalGlue());
          playQueueLeftBox.add(playQueueToolBar);
      }
  
      // 初始化播放队列列表
      private void initPlayQueue() {
          ListDataListener countListener = new ListDataListener() {
              @Override
              public void intervalAdded(ListDataEvent e) {
                  int size = playQueueModel.getSize();
                  playQueueCountLabel.setText(String.format(TOTAL_MSG, size));
                  if (size == 1) {
                      playQueueLeftBox.remove(emptyHintPanel);
                      playQueueLeftBox.add(playQueueScrollPane);
                  }
              }
  
              @Override
              public void intervalRemoved(ListDataEvent e) {
                  int size = playQueueModel.getSize();
                  playQueueCountLabel.setText(String.format(TOTAL_MSG, size));
                  if (size == 0) {
                      playQueueLeftBox.remove(playQueueScrollPane);
                      playQueueLeftBox.add(emptyHintPanel);
                  }
              }
  
              @Override
              public void contentsChanged(ListDataEvent e) {
  
              }
          };
          playQueueModel.addListDataListener(countListener);
          playQueue.setModel(playQueueModel);
  
          playQueue.addMouseMotionListener(new MouseAdapter() {
              @Override
              public void mouseMoved(MouseEvent e) {
                  int index = playQueue.locationToIndex(e.getPoint());
                  Rectangle bounds = playQueue.getCellBounds(index, index);
                  if (bounds == null) return;
                  setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
              }
  
              private void setHoverIndex(int index) {
                  MusicListRenderer renderer = (MusicListRenderer) playQueue.getCellRenderer();
                  if (renderer == null) return;
                  int hoverIndex = renderer.getHoverIndex();
                  if (hoverIndex == index) return;
                  renderer.setHoverIndex(index);
                  playQueue.repaint();
              }
          });
          playQueue.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseExited(MouseEvent e) {
                  MusicListRenderer renderer = (MusicListRenderer) playQueue.getCellRenderer();
                  if (renderer == null) return;
                  renderer.setHoverIndex(-1);
                  playQueue.repaint();
              }
          });
          playQueue.addKeyListener(new KeyAdapter() {
              @Override
              public void keyReleased(KeyEvent e) {
                  if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                      playExecutor.execute(() -> playSelected(playQueue, false));
                  }
              }
          });
          playQueue.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  // 鼠标左键双击播放
                  if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                      playExecutor.execute(() -> playSelected(playQueue, false));
                  }
                  // 鼠标右键弹出菜单
                  else if (e.getButton() == MouseEvent.BUTTON3) {
                      if (playQueue.getModel().getSize() != 0) {
                          // 得到鼠标光标所在的选项并选中
                          int index = playQueue.locationToIndex(e.getPoint());
                          if (index != -1 && !playQueue.isSelectedIndex(index)
                                  || playQueue.isSelectedIndex(index) && playQueue.getSelectedIndices().length == 1) {
                              playQueue.setSelectedIndex(index);
                              MusicResource resource = playQueue.getSelectedValue();
                              boolean ins = resource instanceof NetMusicInfo;
                              playQueueDownloadMenuItem.setEnabled(ins);
                              playQueueCommentMenuItem.setEnabled(ins);
                              playQueueSheetMenuItem.setEnabled(ins);
                              playQueueSimilarSongMenuItem.setEnabled(ins);
                              playQueueRelatedPlaylistMenuItem.setEnabled(ins);
                              playQueueAuthorMenuItem.setEnabled(ins);
                              playQueueAlbumMenuItem.setEnabled(ins);
                              playQueueRecRadioMenuItem.setEnabled(ins);
                              playQueueRelatedMvMenuItem.setEnabled(ins);
                              playQueuePlayMvMenuItem.setEnabled(ins && ((NetMusicInfo) resource).hasMv());
                              if (hasBeenCollected(resource)) {
                                  playQueueCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                                  playQueueCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                              } else {
                                  playQueueCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                                  playQueueCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                              }
                              playQueueLocateFileMenuItem.setEnabled(!ins);
                              playQueueEditInfoMenuItem.setEnabled(!ins);
                          } else {
                              List<MusicResource> list = playQueue.getSelectedValuesList();
                              for (MusicResource resource : list) {
                                  playQueueDownloadMenuItem.setEnabled(resource instanceof NetMusicInfo);
                                  if (playQueueDownloadMenuItem.isEnabled()) break;
                              }
                              MusicResource first = list.get(0);
                              boolean ins = first instanceof NetMusicInfo;
                              playQueueCommentMenuItem.setEnabled(ins);
                              playQueueSheetMenuItem.setEnabled(ins);
                              playQueueSimilarSongMenuItem.setEnabled(ins);
                              playQueueRelatedPlaylistMenuItem.setEnabled(ins);
                              playQueueAuthorMenuItem.setEnabled(ins);
                              playQueueAlbumMenuItem.setEnabled(ins);
                              playQueueRecRadioMenuItem.setEnabled(ins);
                              playQueueRelatedMvMenuItem.setEnabled(ins);
                              playQueuePlayMvMenuItem.setEnabled(ins);
                              if (hasBeenCollected(first)) {
                                  playQueueCollectMenuItem.setIcon(ImageUtil.dye(cancelCollectionMenuItemIcon, currUIStyle.getIconColor()));
                                  playQueueCollectMenuItem.setText(CANCEL_COLLECTION_MENU_ITEM_TEXT);
                              } else {
                                  playQueueCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, currUIStyle.getIconColor()));
                                  playQueueCollectMenuItem.setText(COLLECT_MENU_ITEM_TEXT);
                              }
                              playQueueLocateFileMenuItem.setEnabled(!ins);
                              playQueueEditInfoMenuItem.setEnabled(!ins);
                          }
  
                          playQueuePopupMenu.show(playQueue, e.getX(), e.getY());
                      }
                  }
              }
          });
          // 播放菜单项
          playQueuePlayMenuItem.addActionListener(e -> {
              playExecutor.execute(() -> playSelected(playQueue, false));
          });
          // 下一首播放
          playQueueNextPlayMenuItem.addActionListener(e -> nextPlay(playQueue));
          // 播放 MV 菜单项
          playQueuePlayMvMenuItem.addActionListener(e -> {
              playMv(MvCompSourceType.PLAY_QUEUE);
          });
          // 收藏/取消收藏菜单项
          playQueueCollectMenuItem.addActionListener(e -> {
              ListModel<MusicResource> model = musicList.getModel();
              boolean needRefresh = model == collectionModel;
              if (playQueueCollectMenuItem.getText().equals(COLLECT_MENU_ITEM_TEXT)) {
                  List<MusicResource> values = playQueue.getSelectedValuesList();
                  if (needRefresh) musicList.setModel(emptyListModel);
                  for (int i = values.size() - 1; i >= 0; i--) {
                      MusicResource resource = values.get(i);
                      if (hasBeenCollected(resource)) continue;
                      collectionModel.add(0, resource);
                      if (player.loadedMusicResource(resource))
                          collectButton.setIcon(ImageUtil.dye(hasCollectedIcon, currUIStyle.getIconColor()));
                  }
                  if (needRefresh) musicList.setModel(model);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  List<MusicResource> values = playQueue.getSelectedValuesList();
                  if (needRefresh) musicList.setModel(emptyListModel);
                  values.forEach(resource -> {
                      if (hasBeenCollected(resource)) {
                          collectionModel.removeElement(resource);
                          if (player.loadedMusicResource(resource))
                              collectButton.setIcon(ImageUtil.dye(collectIcon, currUIStyle.getIconColor()));
                      }
                  });
                  if (needRefresh) musicList.setModel(model);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 下载菜单项
          playQueueDownloadMenuItem.addActionListener(e -> {
              List values = playQueue.getSelectedValuesList();
              multiDownload(values);
          });
          // 查看评论菜单项
          playQueueCommentMenuItem.addActionListener(e -> {
              MusicResource resource = playQueue.getSelectedValue();
              if (resource instanceof NetMusicInfo) {
                  getComments((NetMusicInfo) resource, true);
              }
          });
          // 查看乐谱菜单项
          playQueueSheetMenuItem.addActionListener(e -> {
              MusicResource resource = playQueue.getSelectedValue();
              if (resource instanceof NetMusicInfo) {
                  getSheets((NetMusicInfo) resource, true);
              }
          });
          // 搜索这首歌曲菜单项
          playQueueSearchSongMenuItem.addActionListener(e -> {
              MusicResource resource = playQueue.getSelectedValue();
              tabbedPane.setSelectedIndex(TabIndex.NET_MUSIC);
              if (currMusicMusicInfo != null) netMusicBackwardButton.doClick();
              searchTextField.requestFocus();
              if (resource instanceof NetMusicInfo) {
                  searchTextField.setText(((NetMusicInfo) resource).toKeywords());
              } else searchTextField.setText(((AudioFile) resource).toKeywords());
              netMusicClearInputButton.setVisible(true);
              searchButton.doClick();
          });
          // 查看相似歌曲菜单项
          playQueueSimilarSongMenuItem.addActionListener(e -> netMusicSimilarSongMenuItem.doClick());
          // 查看歌关歌单菜单项
          playQueueRelatedPlaylistMenuItem.addActionListener(e -> netMusicRelatedPlaylistMenuItem.doClick());
          // 查看歌手/作者
          playQueueAuthorMenuItem.addActionListener(e -> netMusicAuthorMenuItem.doClick());
          // 查看专辑/电台
          playQueueAlbumMenuItem.addActionListener(e -> netMusicAlbumMenuItem.doClick());
          // 查看推荐电台
          playQueueRecRadioMenuItem.addActionListener(e -> netMusicRecRadioMenuItem.doClick());
          // 查看相关 MV 菜单项
          playQueueRelatedMvMenuItem.addActionListener(e -> netMusicRelatedMvMenuItem.doClick());
          // 复制名称菜单项
          playQueueCopyNameMenuItem.addActionListener(e -> {
              MusicResource resource = playQueue.getSelectedValue();
              String s = null;
              if (resource instanceof AudioFile) s = resource.toString();
              else if (resource instanceof NetMusicInfo) s = ((NetMusicInfo) resource).toSimpleString();
              copyToClipboard(s);
          });
          // 打开文件所在位置菜单项
          playQueueLocateFileMenuItem.addActionListener(e -> {
              MusicResource resource = playQueue.getSelectedValue();
              if (resource instanceof AudioFile) {
                  File file = ((AudioFile) resource);
                  DesktopUtil.explore(file.exists() ? file : file.getParentFile());
              }
          });
          // 编辑歌曲信息菜单项
          playQueueEditInfoMenuItem.addActionListener(e -> {
              MusicResource resource = playQueue.getSelectedValue();
              if (resource instanceof AudioFile) {
                  AudioFile file = (AudioFile) resource;
                  if (file.exists()) {
                      editInfo((AudioFile) resource);
                      updateRenderer(playQueue);
                  } else new TipDialog(THIS, FILE_NOT_FOUND_MSG).showDialog();
              }
          });
          // 从播放队列删除菜单项
          playQueueRemoveMenuItem.addActionListener(e -> {
              playQueueRemoveToolButton.doClick();
          });
          // 标签间距
          playQueueCountLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
  
          playQueuePopupMenu.add(playQueuePlayMenuItem);
          playQueuePopupMenu.add(playQueueNextPlayMenuItem);
          playQueuePopupMenu.add(playQueuePlayMvMenuItem);
          playQueuePopupMenu.add(playQueueCollectMenuItem);
          playQueuePopupMenu.add(playQueueDownloadMenuItem);
          playQueuePopupMenu.add(playQueueCommentMenuItem);
          playQueuePopupMenu.add(playQueueSheetMenuItem);
          playQueuePopupMenu.add(playQueueSearchSongMenuItem);
          playQueuePopupMenu.add(playQueueSimilarSongMenuItem);
          playQueuePopupMenu.add(playQueueRelatedPlaylistMenuItem);
          playQueuePopupMenu.add(playQueueAuthorMenuItem);
          playQueuePopupMenu.add(playQueueAlbumMenuItem);
          playQueuePopupMenu.add(playQueueRecRadioMenuItem);
          playQueuePopupMenu.add(playQueueRelatedMvMenuItem);
          playQueuePopupMenu.add(playQueueCopyNameMenuItem);
          playQueuePopupMenu.add(playQueueLocateFileMenuItem);
          playQueuePopupMenu.add(playQueueEditInfoMenuItem);
          playQueuePopupMenu.add(playQueueRemoveMenuItem);
  
          playQueueScrollPane.setPreferredSize(new Dimension(600, 300));
          playQueueLeftBox.add(playQueueScrollPane);
      }
  
      private boolean lrcListDragged;
  
      // 初始化歌词列表
      private void initLrcList() {
          // 复制歌词
          copyMenuItem.addActionListener(e -> {
              Statement stmt = lrcList.getSelectedValue();
              if (stmt == null) return;
              copyToClipboard(stmt.toString());
          });
          // 定位歌词时间
          locateLrcMenuItem.addActionListener(e -> {
              Statement stmt = lrcList.getSelectedValue();
              if (stmt == null) return;
              double t = stmt.getTime() - lrcOffset;
              player.seek(t);
              seekLrc(t);
          });
          // 查看歌词文件
          browseLrcMenuItem.addActionListener(e -> {
              String lrcPath = "";
              // 在线音乐先将歌词存为临时文件再查看
              if (player.loadedNetMusic()) {
                  NetMusicInfo musicInfo = player.getMusicInfo();
                  lrcPath = new File(SimplePath.CACHE_PATH + musicInfo.toLrcFileName()).getAbsolutePath();
                  FileUtil.writeStr(lrcStr, lrcPath);
              }
              // 本地音乐直接打开 lrc 文件
              else {
                  File file = player.getAudioFile();
                  String filePath = file.getAbsolutePath();
                  lrcPath = filePath.substring(0, filePath.lastIndexOf('.')) + ".lrc";
              }
              DesktopUtil.edit(lrcPath);
          });
          // 下载歌词文件
          downloadLrcMenuItem.addActionListener(e -> downloadLrc(player.getMusicInfo()));
          currLrcOffsetMenuItem.setEnabled(false);
  
          lrcPopupMenu.add(locateLrcMenuItem);
          lrcPopupMenu.add(copyMenuItem);
          lrcPopupMenu.add(browseLrcMenuItem);
          lrcPopupMenu.add(downloadLrcMenuItem);
          lrcPopupMenu.addSeparator();
          lrcPopupMenu.add(currLrcOffsetMenuItem);
  
          for (CustomMenuItem mi : calcLrcOffsetMenuItems) {
              mi.addActionListener(e -> {
                  String text = mi.getText();
                  if (RESET.equals(text)) lrcOffset = 0;
                  else lrcOffset += Double.parseDouble(text.replaceFirst(" s", ""));
                  if (lrcOffset > lrcOffsetRadius) lrcOffset = lrcOffsetRadius;
                  else if (lrcOffset < -lrcOffsetRadius) lrcOffset = -lrcOffsetRadius;
                  currLrcOffsetMenuItem.setText(String.format(LRC_OFFSET_MSG, lrcOffset).replace(".0", ""));
                  if (nextLrc >= 0) seekLrc(player.getCurrTimeSeconds());
              });
              lrcPopupMenu.add(mi);
          }
  
          // 焦点(不画焦点框)
          lrcList.setFocusable(false);
          lrcList.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
          // 悬停框
  //        lrcList.addMouseMotionListener(new MouseAdapter() {
  //            @Override
  //            public void mouseMoved(MouseEvent e) {
  //                int index = lrcList.locationToIndex(e.getPoint());
  //                Rectangle bounds = lrcList.getCellBounds(index, index);
  //                if (bounds == null) return;
  //                setHoverIndex(bounds.contains(e.getPoint()) ? index : -1);
  //            }
  //
  //            private void setHoverIndex(int index) {
  //                LrcListRenderer renderer = (LrcListRenderer) lrcList.getCellRenderer();
  //                if (renderer == null) return;
  //                int hoverIndex = renderer.getHoverIndex();
  //                if (hoverIndex == index) return;
  //                renderer.setHoverIndex(index);
  //                lrcList.repaint();
  //            }
  //        });
  //        lrcList.addMouseListener(new MouseAdapter() {
  //            @Override
  //            public void mouseExited(MouseEvent e) {
  //                LrcListRenderer renderer = (LrcListRenderer) lrcList.getCellRenderer();
  //                if (renderer == null) return;
  //                renderer.setHoverIndex(-1);
  //                lrcList.repaint();
  //            }
  //        }
  
          // 滚动条调整事件（鼠标滚轮滑动、滚动条拖动）
          JScrollBar vs = lrcScrollPane.getVerticalScrollBar();
          swActionTimer = new Timer(2500, e -> {
              swActionTimer.stop();
              if (nextLrc != NextLrc.BAD_FORMAT) lrcScrollAnimation = true;
              ((ScrollBarUI) vs.getUI()).setActive(false);
              lrcScrollWaiting = false;
          });
          Runnable swAction = () -> {
              if (swActionTimer.isRunning()) swActionTimer.stop();
              currScrollVal = vs.getValue();
              ((ScrollBarUI) vs.getUI()).setActive(true);
              lrcScrollWaiting = true;
              swActionTimer.start();
          };
          // 歌词面板拖动时上下滚动
          Point dragFrom = new Point();
  
          // 右键弹出菜单
          lrcList.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() == MouseEvent.BUTTON3) {
                      if (lrcList.getModel().getSize() == 0) return;
                      int index = lrcList.locationToIndex(e.getPoint());
                      if (index == -1) return;
                      lrcList.setSelectedIndex(index);
                      String plainLyric = lrcListModel.get(index).getPlainLyric();
                      if (plainLyric.trim().isEmpty()) return;
                      // 歌曲有歌词时才能查看
                      locateLrcMenuItem.setEnabled(nextLrc >= 0);
                      browseLrcMenuItem.setEnabled(nextLrc != NextLrc.NOT_EXISTS && nextLrc != NextLrc.LOADING);
                      // 只允许下载在线音乐的歌词
                      downloadLrcMenuItem.setEnabled(player.loadedNetMusic() && nextLrc != NextLrc.NOT_EXISTS && nextLrc != NextLrc.LOADING);
  
                      lrcPopupMenu.show(lrcList, e.getX(), e.getY());
                  }
                  // 双击定位歌词时间
                  else if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
                      String plainLyric = lrcList.getSelectedValue().getPlainLyric();
                      if (plainLyric.trim().isEmpty()) return;
                      if (nextLrc >= 0) locateLrcMenuItem.doClick();
                  }
                  // 拖拽释放启动歌词滚动延时动画
                  if (lrcListDragged) {
                      swAction.run();
                      lrcListDragged = false;
                  }
              }
  
              @Override
              public void mousePressed(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON1) return;
                  dragFrom.x = e.getX();
                  dragFrom.y = e.getY();
              }
          });
          // 绑定数据 Model
          lrcList.setModel(lrcListModel);
  
          // 歌词面板上下边缘渐隐效果
          JViewport vp = lrcScrollPane.getViewport();
          vp.addChangeListener(e -> updateLrcListViewFading());
          // 歌词面板大小变化后调整歌词列表占位高度，对齐高亮歌词
          lrcScrollPane.addComponentListener(new ComponentAdapter() {
              @Override
              public void componentResized(ComponentEvent e) {
                  int ph = lrcScrollPane.getHeight() / 2 + 20;
                  lrcList.setBorder(BorderFactory.createEmptyBorder(ph, 0, ph, 0));
                  if (nextLrc == NextLrc.BAD_FORMAT) return;
                  currScrollVal = vs.getValue();
                  lrcScrollAnimation = true;
              }
          });
          // 歌词列表左键上下拖拽上下滚动时停止延时动画
          lrcList.addMouseMotionListener(new MouseMotionAdapter() {
              @Override
              public void mouseDragged(MouseEvent e) {
                  if (!SwingUtilities.isLeftMouseButton(e)) return;
                  lrcListDragged = true;
                  // 停止当前动画
                  swActionTimer.stop();
                  lrcScrollAnimation = false;
                  lrcScrollWaiting = true;
                  Point p = e.getPoint();
                  int yOffset = (dragFrom.y - p.y) * 2;
                  boolean ok = lrcScrollPane.setVValue(lrcScrollPane.getVValue() + yOffset);
                  dragFrom.x = p.x;
                  // 拖动时超出滚动条范围后不再叠加参数
                  dragFrom.y = ok ? p.y + yOffset : p.y;
                  ((ScrollBarUI) vs.getUI()).setActive(true);
              }
          });
          // 歌词面板滚轮滚动触发延时动画
          lrcScrollPane.addMouseWheelListener(e -> swAction.run());
          // 滚动条拖拽时停止延时动画
          vs.addMouseMotionListener(new MouseMotionAdapter() {
              @Override
              public void mouseDragged(MouseEvent e) {
                  swActionTimer.stop();
                  lrcScrollAnimation = false;
                  lrcScrollWaiting = true;
              }
          });
          // 滚动条显示/隐藏，松开时触发延时动画
          vs.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  ((ScrollBarUI) vs.getUI()).setActive(true);
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  ((ScrollBarUI) vs.getUI()).setActive(vs.getValueIsAdjusting() || lrcScrollWaiting);
              }
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  ((ScrollBarUI) vs.getUI()).setActive(vs.getVisibleRect().contains(e.getPoint()));
                  swAction.run();
              }
          });
          // 频谱右键菜单
          spectrumOpacityMenuItem.setEnabled(false);
          spectrumPopupMenu.add(spectrumOpacityMenuItem);
          for (CustomMenuItem mi : calcSpectrumOpacityMenuItems) {
              mi.addActionListener(e -> {
                  String text = mi.getText();
                  float so = specOpacity + Float.parseFloat(text.replaceFirst("%", "")) / 100;
                  specOpacity = Math.min(1, Math.max(0.1f, so));
                  spectrumOpacityMenuItem.setText(String.format(SPEC_OPACITY_MSG, (int) (specOpacity * 100)));
              });
              spectrumPopupMenu.add(mi);
          }
          spectrumPopupMenu.addSeparator();
          String[] names = SpectrumConstants.NAMES;
          for (int i = 0, len = names.length; i < len; i++) {
              CustomRadioButtonMenuItem mi = new CustomRadioButtonMenuItem(names[i]);
              int finalI = i;
              mi.addActionListener(e -> {
                  currSpecStyle = finalI;
                  updateMenuItemStatus(spectrumStyleButtonGroup, mi);
                  updateMenuItemIcon(spectrumPopupMenu);
              });
              spectrumStyleButtonGroup.add(mi);
              spectrumPopupMenu.add(mi);
          }
          spectrumPanel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (e.getButton() != MouseEvent.BUTTON3) return;
                  spectrumPopupMenu.show(spectrumPanel, e.getX(), e.getY());
              }
          });
          // 初始不可见
          spectrumPanel.setVisible(false);
          lrcScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
  //        lrcScrollPane.setMinimumSize(dimension);
  //        lrcScrollPane.setMaximumSize(new Dimension((int)(WINDOW_WIDTH * 0.6), (int)(WINDOW_HEIGHT * 1)));
          lrcAndSpecBox.add(lrcScrollPane);
          lrcAndSpecBox.add(spectrumPanel);
          infoAndLrcBox.add(lrcAndSpecBox);
  //        globalPanel.add(infoAndLrcBox, BorderLayout.CENTER);
      }
  
      // 更新歌词面板上下边缘渐隐效果
      private void updateLrcListViewFading() {
          int first = lrcList.getFirstVisibleIndex();
          int last = lrcList.getLastVisibleIndex();
          JViewport vp = lrcScrollPane.getViewport();
          Rectangle fb = lrcList.getCellBounds(first, first);
          if (fb != null) {
              Rectangle vfb = SwingUtilities.convertRectangle(lrcList, fb, vp);
              if (vfb.y > vfb.height) first -= vfb.y / vfb.height;
          }
          Rectangle lb = lrcList.getCellBounds(last, last);
          if (lb != null) {
              Rectangle vlb = SwingUtilities.convertRectangle(lrcList, lb, vp);
              if (vlb.y + vlb.height < vp.getHeight()) last += (vp.getHeight() - vlb.y - vlb.height) / vlb.height;
          }
          LrcListRenderer r = (LrcListRenderer) lrcList.getCellRenderer();
          Map<Integer, Float> alphas = r.getAlphas();
          alphas.clear();
          int t = 3;
          float step = (r.normalMaxAlpha - r.normalMinAlpha) / t;
          for (int i = 0; i < t; i++) {
              alphas.put(first + i, r.normalMinAlpha + i * step);
              alphas.put(last - i, r.normalMinAlpha + i * step);
          }
      }
  
      private int lrcTimerFrame;
  
      // 初始化动画 Timer
      private void initTimer() {
          final int specPiece = (int) ((SpectrumConstants.PLAYER_INTERVAL - 0.01) * 1000 / SpectrumConstants.TIMER_INTERVAL);
          spectrumTimer = new Timer(SpectrumConstants.TIMER_INTERVAL, e -> {
              spectrumExecutor.execute(() -> {
                  double[] specs = player.specs;
                  double[] specsOrigin = player.specsOrigin;
                  double[] specsGap = player.specsGap;
  //                double avg = 0;
                  for (int i = 0, len = SpectrumConstants.barNum; i < len; i++) {
                      if (specs[i] < specsOrigin[i])
                          specs[i] += Math.min(specsGap[i] / specPiece, specsOrigin[i] - specs[i]);
                      else if (specs[i] > specsOrigin[i])
                          specs[i] -= Math.min(specsGap[i] / specPiece, specs[i] - specsOrigin[i]);
  //                    avg += specs[i];
                  }
  //                avg /= SpectrumConstants.barNum;
  //                if (grooveOn) globalPanel.setScale(1 + 0.5f * (float) (avg / SpectrumConstants.barMaxHeight));
  //                else lrcAndSpecBox.repaint();
                  lrcAndSpecBox.repaint();
              });
          });
          final int LRC_TIMER_INTERVAL = 10, LRC_PIECE = 100 / LRC_TIMER_INTERVAL;
          lrcTimer = new Timer(LRC_TIMER_INTERVAL, e -> {
              lrcExecutor.execute(() -> {
                  LrcListRenderer renderer = (LrcListRenderer) lrcList.getCellRenderer();
                  if (nextLrc >= 0) {
                      double currRatio = desktopLyricDialog.getRatio(), ratio = 0;
                      double or = originalRatio;
                      if (currRatio < or || currRatio - or < 0.8) ratio = (or - currRatio) / LRC_PIECE + currRatio;
                      renderer.setRatio(ratio);
                      desktopLyricDialog.updateLyric(statements.get(nextLrc - 1 >= 0 ? nextLrc - 1 : nextLrc), ratio);
                  } else {
                      renderer.setRatio(0);
                      desktopLyricDialog.updateLyric(nextLrc == NextLrc.NOT_EXISTS ? NO_LRC_STMT : nextLrc == NextLrc.LOADING ? LRC_LOADING_STMT : BAD_FORMAT_LRC_STMT, 0);
                  }
                  if (!spectrumTimer.isRunning()) lrcAndSpecBox.repaint();
  
                  // 律动动画，由于过多 timer 会导致卡顿，所以尽量在同一 timer 处理
                  if (grooveOn && !globalPanelTimer.isRunning() && ++lrcTimerFrame % 5 == 0) {
                      double angle = globalPanel.getAngle();
                      globalPanel.setAngle((angle + 1) % 360);
                  }
              });
              if (lrcScrollAnimation) {
                  // 避免线程池执行顺序不一致导致的动画过渡不流畅，不提交
  //                lrcScrollExecutor.execute(() -> {
                  Rectangle bounds = lrcList.getCellBounds(row, row);
                  if (bounds == null) return;
                  Insets insets = lrcScrollPane.getInsets();
                  int val = lrcScrollPane.getVValue(), dVal = bounds.y - (lrcScrollPane.getHeight() - insets.top - insets.bottom) / 2
                          + bounds.height / 2, step = Math.max(1, Math.abs(dVal - currScrollVal) / 20);
                  int nv = val < dVal ? Math.min(dVal, val + step) : Math.max(dVal, val - step);
                  lrcScrollPane.setVValue(nv);
                  if (nv == dVal || lrcScrollPane.getVValue() == val) lrcScrollAnimation = false;
  //                });
              }
          });
          lrcDelayScrollTimer = new Timer(300, e -> {
              lrcDelayScrollTimer.stop();
              lrcScrollAnimation = true;
          });
          globalPanelTimer = new Timer(10, e -> {
              globalPanelExecutor.execute(() -> {
                  float opacity = Math.min(1, globalPanel.getOpacity() + 0.05f);
                  globalPanel.setOpacity(opacity);
                  if (opacity >= 1) globalPanelTimer.stop();
              });
          });
          searchSuggestionTimer = new Timer(100, e -> {
              globalExecutor.execute(() -> updateSearchSuggestion());
              searchSuggestionTimer.stop();
          });
      }
  
      // 初始化进度条
      private void initTimeBar() {
          // 当前播放时间
          currTimeLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
          currTimeLabel.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  timeElapsedMode = !timeElapsedMode;
                  if (!player.loadedMusicResource()) return;
                  // 更新当前播放时间
                  int val = timeBar.getValue();
                  double t = (double) (timeElapsedMode ? val : TIME_BAR_MAX - val) / TIME_BAR_MAX * player.getDurationSeconds();
                  currTimeLabel.setText(DurationUtil.format(t));
              }
          });
  
          timeBar.setMaximum(TIME_BAR_MAX);
          timeBar.setValue(0);
          // 释放时间条，播放器响应
          timeBar.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  if (player.isEmpty()) return;
                  double t = (double) timeBar.getValue() / TIME_BAR_MAX * player.getDurationSeconds();
                  player.seek(t);
                  seekLrc(t);
              }
          });
          // 改变时间条的值，当前时间标签的值随之改变
          timeBar.addChangeListener(e -> {
              int val = timeBar.getValue();
              double t = (double) (timeElapsedMode ? val : TIME_BAR_MAX - val) / TIME_BAR_MAX * player.getDurationSeconds();
              currTimeLabel.setText(DurationUtil.format(t));
          });
  
          progressPanel.add(currTimeLabel);
          progressPanel.add(timeBar);
          progressPanel.add(durationLabel);
          progressBox.add(progressPanel);
          bottomBox.add(progressBox);
      }
  
      // 初始化控制面板
      private void initControlPanel() {
          // changePaneButton 图标遮罩 UI
          changePaneButton.setToolTipText(CHANGE_TO_LYRIC_PANE_TIP);
          changePaneButton.setIconTextGap(10);
          changePaneButton.setPreferredSize(new Dimension(280, 66));
          changePaneButton.setText(NO_LRC_MSG);
          changePaneButton.setIcon(new ImageIcon(ImageUtil.radius(ImageUtil.width(ImageConstants.DEFAULT_IMG, changePaneImageWidth), TINY_ARC)));
          changePaneButton.addActionListener(e -> {
              // 动画状态无响应
              if (globalPanel.isSlideAnimating()) return;
              // 歌词页面切到列表
              if (currPane == MusicPane.LYRIC || lastPane == MusicPane.LYRIC) {
                  // 清空评论数据
                  if (!netCommentListModel.isEmpty()) netCommentListModel.clear();
                  if (!netSheetListModel.isEmpty()) netSheetListModel.clear();
                  hideDetailButton.setVisible(false);
                  // 滑入动画之后处理
                  globalPanel.setOnAfterSlide(() -> {
                      globalPanel.remove(infoAndLrcBox);
                      globalPanel.remove(netCommentBox);
                      globalPanel.remove(netSheetBox);
                      globalPanel.add(tabbedPane, BorderLayout.CENTER);
                      globalPanel.repaint();
                  });
                  // 播放组件放置的滑入动画
                  globalPanel.slideFrom(infoAndLrcBox, tabbedPane, SlideFrom.TOP);
                  globalPanel.slideFrom(netCommentBox, tabbedPane, SlideFrom.TOP);
                  globalPanel.slideFrom(netSheetBox, tabbedPane, SlideFrom.TOP);
                  // 防止事件不起作用
                  globalPanel.requestFocus();
                  changePaneButton.setToolTipText(CHANGE_TO_LYRIC_PANE_TIP);
                  currPane = MusicPane.MUSIC;
                  lastPane = -1;
              }
              // 列表切到歌词页面
              else if (currPane == MusicPane.MUSIC || lastPane == MusicPane.MUSIC) {
                  if (nextLrc != NextLrc.BAD_FORMAT) lrcScrollAnimation = true;
                  // 清空评论数据
                  if (!netCommentListModel.isEmpty()) netCommentListModel.clear();
                  if (!netSheetListModel.isEmpty()) netSheetListModel.clear();
                  hideDetailButton.setVisible(true);
                  // 滑入动画之后处理
                  globalPanel.setOnAfterSlide(() -> {
                      globalPanel.remove(tabbedPane);
                      globalPanel.remove(netCommentBox);
                      globalPanel.remove(netSheetBox);
                      // 防止事件不起作用
                      globalPanel.requestFocus();
                      globalPanel.add(infoAndLrcBox, BorderLayout.CENTER);
                      globalPanel.repaint();
                      updateLrcListViewFading();
                      lrcScrollAnimation = true;
                  });
                  // 播放组件放置的滑入动画
                  globalPanel.slideFrom(tabbedPane, infoAndLrcBox, SlideFrom.BOTTOM);
                  globalPanel.slideFrom(netCommentBox, infoAndLrcBox, SlideFrom.BOTTOM);
                  globalPanel.slideFrom(netSheetBox, infoAndLrcBox, SlideFrom.BOTTOM);
                  changePaneButton.setToolTipText(CHANGE_TO_MUSIC_PANE_TIP);
                  currPane = MusicPane.LYRIC;
                  lastPane = -1;
              }
          });
          // MV
          mvButton.setToolTipText(MV_TIP);
          mvButton.setEnabled(false);
          mvButton.addMouseListener(new ButtonMouseListener(mvButton, THIS));
          mvButton.setPreferredSize(new Dimension(mvIcon.getIconWidth() + 10, mvIcon.getIconHeight() + 10));
          mvButton.addActionListener(e -> playMv(MvCompSourceType.PLAYING));
          // 收藏
          collectButton.setEnabled(false);
          collectButton.addMouseListener(new ButtonMouseListener(collectButton, THIS));
          collectButton.setPreferredSize(new Dimension(collectIcon.getIconWidth() + 10, collectIcon.getIconHeight() + 10));
          collectButton.addActionListener(e -> {
              MusicResource resource = player.getMusicInfo();
              if (resource == null) resource = player.getAudioFile();
              if (!hasBeenCollected(resource)) {
                  collectionModel.add(0, resource);
                  collectButton.setIcon(ImageUtil.dye(hasCollectedIcon, currUIStyle.getIconColor()));
                  collectButton.setToolTipText(COLLECTED_TIP);
                  new TipDialog(THIS, COLLECT_SUCCESS_MSG).showDialog();
              } else {
                  collectionModel.removeElement(resource);
                  collectButton.setIcon(ImageUtil.dye(collectIcon, currUIStyle.getIconColor()));
                  collectButton.setToolTipText(COLLECT_TIP);
                  new TipDialog(THIS, CANCEL_COLLECTION_SUCCESS_MSG).showDialog();
              }
          });
          // 下载
          downloadButton.setToolTipText(DOWNLOAD_TIP);
          downloadButton.setEnabled(false);
          downloadButton.addMouseListener(new ButtonMouseListener(downloadButton, THIS));
          downloadButton.setPreferredSize(new Dimension(downloadIcon.getIconWidth() + 10, downloadIcon.getIconHeight() + 10));
          downloadButton.addActionListener(e -> singleDownload(player.getMusicInfo()));
          // 评论
          commentButton.setToolTipText(COMMENT_TIP);
          commentButton.setEnabled(false);
          commentButton.addMouseListener(new ButtonMouseListener(commentButton, THIS));
          commentButton.setPreferredSize(new Dimension(commentIcon.getIconWidth() + 10, commentIcon.getIconHeight() + 10));
          commentButton.addActionListener(e -> {
              NetMusicInfo musicInfo = player.getMusicInfo();
              if (currPane != MusicPane.COMMENT || currCommentResource != musicInfo)
                  getComments(musicInfo, currPane != MusicPane.COMMENT);
          });
          // 乐谱
          sheetButton.setToolTipText(SHEET_TIP);
          sheetButton.setEnabled(false);
          sheetButton.addMouseListener(new ButtonMouseListener(sheetButton, THIS));
          sheetButton.setPreferredSize(new Dimension(sheetIcon.getIconWidth() + 10, sheetIcon.getIconHeight() + 10));
          sheetButton.addActionListener(e -> {
              NetMusicInfo musicInfo = player.getMusicInfo();
              if (currPane != MusicPane.SHEET || currSheetMusicInfo != musicInfo)
                  getSheets(musicInfo, currPane != MusicPane.SHEET);
          });
          lastButton.setToolTipText(LAST_TIP);
          lastButton.addMouseListener(new ButtonMouseListener(lastButton, THIS));
          lastButton.setPreferredSize(new Dimension(lastIcon.getIconWidth() + 10, lastIcon.getIconHeight() + 10));
          lastButton.addActionListener(e -> playLast());
          playOrPauseButton.setToolTipText(PLAY_TIP);
          playOrPauseButton.setPreferredSize(new Dimension(playIcon.getIconWidth() + 10, playIcon.getIconHeight() + 10));
          playOrPauseButton.addMouseListener(new ButtonMouseListener(playOrPauseButton, THIS));
          playOrPauseButton.addActionListener(e -> playOrPause());
          nextButton.setToolTipText(NEXT_TIP);
          nextButton.addMouseListener(new ButtonMouseListener(nextButton, THIS));
          nextButton.setPreferredSize(new Dimension(nextIcon.getIconWidth() + 10, nextIcon.getIconHeight() + 10));
          nextButton.addActionListener(e -> {
              // 单曲循环和顺序播放，都播放下一首
              if (currPlayMode != PlayMode.SHUFFLE) {
                  playNext();
              }
              // 随机播放
              else {
                  // 播放下一个随机
                  playNextShuffle();
              }
          });
          // 默认提示语为“列表循环”
          playModeButton.setToolTipText(LIST_CYCLE_TIP);
          playModeButton.addMouseListener(new ButtonMouseListener(playModeButton, THIS));
          playModeButton.setPreferredSize(new Dimension(listCycleIcon.getIconWidth() + 10, listCycleIcon.getIconHeight() + 10));
          // 播放模式切换事件
          playModeButton.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  int h = playModePopupMenu.getHeight();
                  playModePopupMenu.show(playModeButton, e.getX(), e.getY() - (h == 0 ? 195 : h));
              }
          });
          // 播放模式弹出菜单
          playModeDisabledMenuItem.addActionListener(e -> {
              if (currPlayMode != PlayMode.DISABLED) changeToDisabled(true);
          });
          singleMenuItem.addActionListener(e -> {
              if (currPlayMode != PlayMode.SINGLE) changeToSingle(true);
          });
          sequenceMenuItem.addActionListener(e -> {
              if (currPlayMode != PlayMode.SEQUENCE) changeToSequence(true);
          });
          listCycleMenuItem.addActionListener(e -> {
              if (currPlayMode != PlayMode.LIST_CYCLE) changeToListCycle(true);
          });
          shuffleMenuItem.addActionListener(e -> {
              if (currPlayMode != PlayMode.SHUFFLE) changeToShuffle(true);
          });
  
          playModePopupMenu.add(playModeDisabledMenuItem);
          playModePopupMenu.add(singleMenuItem);
          playModePopupMenu.add(sequenceMenuItem);
          playModePopupMenu.add(listCycleMenuItem);
          playModePopupMenu.add(shuffleMenuItem);
  
          backwardButton.setToolTipText(BACKW_TIP);
          backwardButton.addMouseListener(new ButtonMouseListener(backwardButton, THIS));
          backwardButton.setPreferredSize(new Dimension(backwIcon.getIconWidth() + 10, backwIcon.getIconHeight() + 10));
          forwardButton.setToolTipText(FORW_TIP);
          forwardButton.addMouseListener(new ButtonMouseListener(forwardButton, THIS));
          forwardButton.setPreferredSize(new Dimension(forwIcon.getIconWidth() + 10, forwIcon.getIconHeight() + 10));
          // 快进快退
          backwardButton.addActionListener(e -> {
              double t = player.getCurrTimeSeconds() - forwardOrBackwardTime;
              player.seek(t);
              seekLrc(t);
          });
          forwardButton.addActionListener(e -> {
              double t = player.getCurrTimeSeconds() + forwardOrBackwardTime;
              player.seek(t);
              seekLrc(t);
          });
          // 静音
          muteButton.setToolTipText(SOUND_TIP);
          muteButton.addMouseListener(new ButtonMouseListener(muteButton, THIS));
          muteButton.setPreferredSize(new Dimension(muteIcon.getIconWidth() + 10, muteIcon.getIconHeight() + 10));
          muteButton.addActionListener(e -> {
              if (isMute = !isMute) {
                  muteButton.setToolTipText(MUTE_TIP);
                  muteButton.setIcon(ImageUtil.dye(muteIcon, currUIStyle.getIconColor()));
              } else {
                  muteButton.setToolTipText(SOUND_TIP);
                  muteButton.setIcon(ImageUtil.dye(soundIcon, currUIStyle.getIconColor()));
              }
              player.setMute(isMute);
          });
          // 音量调节滑动条
          volumeSlider.setPreferredSize(new Dimension(100, 20));
          volumeSlider.setMaximum(MAX_VOLUME);
          volumeSlider.setValue(DEFAULT_VOLUME);
          volumeSlider.addChangeListener(e -> {
              player.setVolume((float) volumeSlider.getValue() / MAX_VOLUME);
              if (isMute) {
                  muteButton.setToolTipText(SOUND_TIP);
                  muteButton.setIcon(ImageUtil.dye(soundIcon, currUIStyle.getIconColor()));
                  player.setMute(isMute = false);
              }
          });
          rateButton.setToolTipText(RATE_TIP);
          rateButton.addMouseListener(new ButtonMouseListener(rateButton, THIS));
          rateButton.setPreferredSize(new Dimension(rateIcon.getIconWidth() + 10, rateIcon.getIconHeight() + 10));
          rateButton.addActionListener(e -> {
              if (rateDialog == null) rateDialog = new RateDialog(THIS, null, rateButton);
              rateDialog.showDialog();
          });
          // 频谱开关按钮
          switchSpectrumButton.setToolTipText(SWITCH_SPECTRUM_TIP);
          switchSpectrumButton.addMouseListener(new ButtonMouseListener(switchSpectrumButton, THIS));
          switchSpectrumButton.setPreferredSize(new Dimension(spectrumOnIcon.getIconWidth() + 10, spectrumOnIcon.getIconHeight() + 10));
          switchSpectrumButton.addActionListener(e -> {
              if (showSpectrum = !showSpectrum) openSpectrum();
              else closeSpectrum();
              switchSpectrumButton.setIcon(ImageUtil.dye(showSpectrum ? spectrumOnIcon : spectrumOffIcon, currUIStyle.getIconColor()));
          });
          // 模糊菜单
          blurPopupMenu.add(gsMenuItem);
          blurPopupMenu.add(darkerMenuItem);
          blurPopupMenu.add(maskMenuItem);
          blurPopupMenu.add(grooveMenuItem);
          blurPopupMenu.addSeparator();
          blurPopupMenu.add(blurOffMenuItem);
          blurPopupMenu.add(cvBlurMenuItem);
          blurPopupMenu.add(mcBlurMenuItem);
          blurPopupMenu.add(lgBlurMenuItem);
          blurPopupMenu.add(fbmBlurMenuItem);
          gsMenuItem.addActionListener(e -> {
              gsOn = !gsOn;
              doBlur();
              gsMenuItem.setIcon(gsOn ? ImageUtil.dye(tickIcon, currUIStyle.getIconColor()) : null);
          });
          darkerMenuItem.addActionListener(e -> {
              darkerOn = !darkerOn;
              doBlur();
              darkerMenuItem.setIcon(darkerOn ? ImageUtil.dye(tickIcon, currUIStyle.getIconColor()) : null);
          });
          maskMenuItem.addActionListener(e -> {
              maskOn = !maskOn;
              doBlur();
              maskMenuItem.setIcon(maskOn ? ImageUtil.dye(tickIcon, currUIStyle.getIconColor()) : null);
          });
          grooveMenuItem.addActionListener(e -> {
              globalPanel.setGrooveOn(grooveOn = !grooveOn);
              doBlur();
              grooveMenuItem.setIcon(grooveOn ? ImageUtil.dye(tickIcon, currUIStyle.getIconColor()) : null);
          });
          blurOffMenuItem.addActionListener(e -> {
              blurType = BlurConstants.OFF;
              doBlur();
              blurButton.setIcon(ImageUtil.dye(blurOffIcon, currUIStyle.getIconColor()));
          });
          cvBlurMenuItem.addActionListener(e -> {
              blurType = BlurConstants.CV;
              doBlur();
              blurButton.setIcon(ImageUtil.dye(cvBlurIcon, currUIStyle.getIconColor()));
          });
          mcBlurMenuItem.addActionListener(e -> {
              blurType = BlurConstants.MC;
              doBlur();
              blurButton.setIcon(ImageUtil.dye(mcBlurIcon, currUIStyle.getIconColor()));
          });
          lgBlurMenuItem.addActionListener(e -> {
              blurType = BlurConstants.LG;
              doBlur();
              blurButton.setIcon(ImageUtil.dye(lgBlurIcon, currUIStyle.getIconColor()));
          });
          fbmBlurMenuItem.addActionListener(e -> {
              blurType = BlurConstants.FBM;
              doBlur();
              blurButton.setIcon(ImageUtil.dye(fbmBlurIcon, currUIStyle.getIconColor()));
          });
          // 模糊按钮
          blurButton.setToolTipText(SWITCH_BLUR_TIP);
          blurButton.addMouseListener(new ButtonMouseListener(blurButton, THIS));
          blurButton.setPreferredSize(new Dimension(cvBlurIcon.getIconWidth() + 10, cvBlurIcon.getIconHeight() + 10));
          blurButton.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  int h = blurPopupMenu.getHeight();
                  blurPopupMenu.show(blurButton, e.getX(), e.getY() - (h == 0 ? 342 : h));
              }
          });
          // 音效按钮
          soundEffectButton.setToolTipText(SOUND_EFFECT_TIP);
          soundEffectButton.addMouseListener(new ButtonMouseListener(soundEffectButton, THIS));
          soundEffectButton.setPreferredSize(new Dimension(soundEffectIcon.getIconWidth() + 10, soundEffectIcon.getIconHeight() + 10));
          soundEffectButton.addActionListener(e -> new SoundEffectDialog(THIS).showDialog());
          // 跳到播放队列按钮
          goToPlayQueueButton.setToolTipText(GO_TO_PLAY_QUEUE_TIP);
          goToPlayQueueButton.setPreferredSize(new Dimension(goToPlayQueueIcon.getIconWidth() + 10, goToPlayQueueIcon.getIconHeight() + 10));
          goToPlayQueueButton.addMouseListener(new ButtonMouseListener(goToPlayQueueButton, THIS));
          goToPlayQueueButton.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  tabbedPane.setSelectedIndex(TabIndex.PLAY_QUEUE);
                  // 清空评论数据
                  if (!netCommentListModel.isEmpty()) netCommentListModel.clear();
                  hideDetailButton.setVisible(false);
                  globalPanel.remove(infoAndLrcBox);
                  globalPanel.remove(netCommentBox);
                  globalPanel.add(tabbedPane, BorderLayout.CENTER);
                  changePaneButton.setToolTipText(CHANGE_TO_LYRIC_PANE_TIP);
                  globalPanel.repaint();
                  currPane = MusicPane.MUSIC;
              }
          });
          // 桌面歌词开关
          desktopLyricButton.setToolTipText(DESKTOP_LRC_TIP);
          desktopLyricButton.addMouseListener(new ButtonMouseListener(desktopLyricButton, THIS));
          desktopLyricButton.setPreferredSize(new Dimension(desktopLyricOnIcon.getIconWidth() + 10, desktopLyricOnIcon.getIconHeight() + 10));
          desktopLyricButton.addActionListener(e -> {
              if (showDesktopLyric = !showDesktopLyric) {
                  // 最大化时不显示桌面歌词
                  desktopLyricDialog.setVisible(windowState != WindowState.MAXIMIZED);
                  desktopLyricButton.setIcon(ImageUtil.dye(desktopLyricOnIcon, currUIStyle.getIconColor()));
              } else {
                  desktopLyricDialog.setVisible(false);
                  desktopLyricButton.setIcon(ImageUtil.dye(desktopLyricOffIcon, currUIStyle.getIconColor()));
              }
          });
          // 歌词类型按钮
          switchLrcTypeButton.addMouseListener(new ButtonMouseListener(switchLrcTypeButton, THIS));
          switchLrcTypeButton.setPreferredSize(new Dimension(originalIcon.getIconWidth() + 10, originalIcon.getIconHeight() + 10));
          // 歌词类型切换事件
          switchLrcTypeButton.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseReleased(MouseEvent e) {
                  int h = lyricTypePopupMenu.getHeight();
                  lyricTypePopupMenu.show(switchLrcTypeButton, e.getX(), e.getY() - (h == 0 ? 161 : h));
              }
          });
          // 歌词类型弹出菜单
          originalLrcMenuItem.addActionListener(e -> {
              if (currLrcType == LyricType.ORIGINAL) return;
              loadLrc(player.getAudioFile(), player.getMusicInfo(), currLrcType = LyricType.ORIGINAL, true);
              switchLrcTypeButton.setIcon(ImageUtil.dye(originalIcon, currUIStyle.getIconColor()));
              switchLrcTypeButton.setToolTipText(ORIGINAL_LRC_TIP);
          });
          translationMenuItem.addActionListener(e -> {
              if (currLrcType == LyricType.TRANSLATION) return;
              loadLrc(player.getAudioFile(), player.getMusicInfo(), currLrcType = LyricType.TRANSLATION, true);
              switchLrcTypeButton.setIcon(ImageUtil.dye(translationIcon, currUIStyle.getIconColor()));
              switchLrcTypeButton.setToolTipText(TRANSLATION_TIP);
          });
          romaMenuItem.addActionListener(e -> {
              if (currLrcType == LyricType.ROMA) return;
              loadLrc(player.getAudioFile(), player.getMusicInfo(), currLrcType = LyricType.ROMA, true);
              switchLrcTypeButton.setIcon(ImageUtil.dye(romajiIcon, currUIStyle.getIconColor()));
              switchLrcTypeButton.setToolTipText(ROMA_TIP);
          });
          tradChineseMenuItem.addActionListener(e -> {
              if (currLrcType == LyricType.TRADITIONAL_CN) return;
              loadLrc(player.getAudioFile(), player.getMusicInfo(), currLrcType = LyricType.TRADITIONAL_CN, true);
              switchLrcTypeButton.setIcon(ImageUtil.dye(tradChineseIcon, currUIStyle.getIconColor()));
              switchLrcTypeButton.setToolTipText(TRAD_CHINESE_TIP);
          });
  
          lyricTypePopupMenu.add(originalLrcMenuItem);
          lyricTypePopupMenu.add(translationMenuItem);
          lyricTypePopupMenu.add(romaMenuItem);
          lyricTypePopupMenu.add(tradChineseMenuItem);
  
          // 按钮水平隔开一段间距
          FlowLayout fl = new FlowLayout();
          fl.setHgap(0);
          controlPanel.setLayout(fl);
          controlPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 30));
          controlPanel.add(backwardButton);
          controlPanel.add(lastButton);
          controlPanel.add(playOrPauseButton);
          controlPanel.add(nextButton);
          controlPanel.add(forwardButton);
          controlPanel.add(playModeButton);
          fl = new FlowLayout();
          fl.setHgap(0);
          volumePanel.setLayout(fl);
          volumePanel.add(muteButton);
          volumePanel.add(volumeSlider);
          controlPanel.add(volumePanel);
          controlPanel.add(rateButton);
          controlPanel.add(switchSpectrumButton);
          controlPanel.add(blurButton);
          controlPanel.add(soundEffectButton);
          controlPanel.add(goToPlayQueueButton);
  
          fl = new FlowLayout();
          fl.setHgap(0);
          changePanePanel.setLayout(fl);
          changePanePanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0));
          changePanePanel.add(changePaneButton);
          changePanePanel.add(mvButton);
          changePanePanel.add(collectButton);
          changePanePanel.add(downloadButton);
          changePanePanel.add(commentButton);
          changePanePanel.add(sheetButton);
  
          fl = new FlowLayout();
          fl.setHgap(0);
          controlLrcPanel.setLayout(fl);
          controlLrcPanel.setBorder(BorderFactory.createEmptyBorder(16, 0, 0, 0));
          controlLrcPanel.add(desktopLyricButton);
          controlLrcPanel.add(switchLrcTypeButton);
  
          controlBox.add(changePanePanel);
          controlBox.add(Box.createHorizontalGlue());
          controlBox.add(controlPanel);
          controlBox.add(Box.createHorizontalGlue());
          controlBox.add(controlLrcPanel);
          controlBox.add(Box.createHorizontalGlue());
          bottomBox.add(controlBox);
          globalPanel.add(bottomBox, BorderLayout.SOUTH);
      }
  
      // 卸载当前文件
      private void unload() {
          player.unload();
          unloadUI();
          clearLrc();
          currSong = -1;
      }
  
      private String statusText = "";
  
      // 更新标题文字
      private void updateTitle(String st) {
          synchronized (statusText) {
              statusText = st;
              String title = String.format(TITLE + "（%s%s）", st,
                      player.loadedNetMusic() ? player.getMusicInfo().toSimpleString() : player.getAudioFile());
              titleLabel.setText(StringUtil.textToHtml(StringUtil.shorten(title, 80)));
              setTitle(title);
          }
      }
  
      // 界面加载新文件
      private void loadUI(AudioFile file, NetMusicInfo musicInfo) {
          // 设置标题
          updateTitle(LOAD_TRACK);
          // 重置当前播放时间
          timeBar.setValue(0);
          String durationString = player.getDurationString();
          if (!timeElapsedMode) currTimeLabel.setText(durationString);
          // 重置总时间
          durationLabel.setText(durationString);
          // 设置当前播放时间标签的最佳大小，避免导致进度条长度发生变化！
          String t = durationLabel.getText().replaceAll("[1-9]", "0");
          FontMetrics m = durationLabel.getFontMetrics(globalFont);
          Dimension d = new Dimension(m.stringWidth(t) + 2, durationLabel.getHeight());
          currTimeLabel.setPreferredSize(d);
          durationLabel.setPreferredSize(d);
          // 重置为“播放”
          playOrPauseButton.setIcon(ImageUtil.dye(playIcon, currUIStyle.getIconColor()));
          playOrPauseButton.setToolTipText(PLAY_TIP);
          if (miniDialog != null) {
              miniDialog.playOrPauseButton.setIcon(playOrPauseButton.getIcon());
              miniDialog.playOrPauseButton.setToolTipText(playOrPauseButton.getToolTipText());
          }
          // 重置桌面歌词
          desktopLyricDialog.updateLyric(NO_LRC_STMT, 0);
          // 允许“关闭当前歌曲”
          closeSong.setEnabled(true);
          // 开启频谱动画
          if (showSpectrum) openSpectrum();
          // 开启歌词动画
          if (!lrcTimer.isRunning()) lrcTimer.start();
  
          boolean isNetMusic = musicInfo != null;
  
          // 设置切换面板文字
          final int maxLen = 34;
          if (isNetMusic)
              changePaneButton.setText(StringUtil.textToHtml(StringUtil.shorten(musicInfo.toSimpleString(), maxLen)));
          else
              changePaneButton.setText(StringUtil.textToHtml(StringUtil.shorten(file.toString(), maxLen)));
          if (miniDialog != null) miniDialog.infoLabel.setText(changePaneButton.getText());
          // 设置 MV、收藏、下载、评论、乐谱按钮
          mvButton.setEnabled(isNetMusic && musicInfo.hasMv());
          collectButton.setEnabled(true);
          downloadButton.setEnabled(isNetMusic);
          commentButton.setEnabled(isNetMusic);
          sheetButton.setEnabled(isNetMusic);
          if (isNetMusic && hasBeenCollected(musicInfo) || hasBeenCollected(file)) {
              collectButton.setIcon(ImageUtil.dye(hasCollectedIcon, currUIStyle.getIconColor()));
              collectButton.setToolTipText(COLLECTED_TIP);
          } else {
              collectButton.setIcon(ImageUtil.dye(collectIcon, currUIStyle.getIconColor()));
              collectButton.setToolTipText(COLLECT_TIP);
          }
  
          MetaMusicInfo metaMusicInfo = player.getMetaMusicInfo();
          // 设置歌曲名称
          songNameLabel.setText(StringUtil.textToHtml(SONG_NAME_LABEL + metaMusicInfo.getName()));
          songNameLabel.setVisible(false);
          songNameLabel.setVisible(true);
          // 设置艺术家
          artistLabel.setText(StringUtil.textToHtml(StringUtil.shorten(ARTIST_LABEL + metaMusicInfo.getArtist(), 50)));
          artistLabel.setCursor(Cursor.getPredefinedCursor(isNetMusic ? Cursor.HAND_CURSOR : Cursor.DEFAULT_CURSOR));
          artistLabel.setVisible(false);
          artistLabel.setVisible(true);
          // 设置专辑名称
          albumLabel.setText(StringUtil.textToHtml(ALBUM_NAME_LABEL + metaMusicInfo.getAlbumName()));
          albumLabel.setCursor(Cursor.getPredefinedCursor(isNetMusic ? Cursor.HAND_CURSOR : Cursor.DEFAULT_CURSOR));
          albumLabel.setVisible(false);
          albumLabel.setVisible(true);
      }
  
      // 界面关闭文件
      private void unloadUI() {
          // 重置标题
          updateMotto();
          setTitle(TITLE);
          // 重置总时间
          durationLabel.setText(DEFAULT_TIME);
          // 重置为“播放”
          playOrPauseButton.setIcon(ImageUtil.dye(playIcon, currUIStyle.getIconColor()));
          playOrPauseButton.setToolTipText(PLAY_TIP);
          // 重置播放进度条
          timeBar.setValue(0);
          // 重置桌面歌词
          desktopLyricDialog.updateLyric(NO_LRC_STMT, 0);
          // 禁止“关闭当前歌曲”
          closeSong.setEnabled(false);
          // 关闭频谱动画
          closeSpectrum();
          // 关闭歌词动画
          lrcTimer.stop();
  
          // 卸载专辑图片
          albumImageLabel.setIcon(null);
          changePaneButton.setIcon(new ImageIcon(ImageUtil.radius(ImageUtil.width(ImageConstants.DEFAULT_IMG, changePaneImageWidth), TINY_ARC)));
          changePaneButton.setText(NO_LRC_MSG);
          if (miniDialog != null) {
              miniDialog.infoLabel.setIcon(changePaneButton.getIcon());
              miniDialog.infoLabel.setText(changePaneButton.getText());
          }
          // 恢复背景
          doBlur();
          // 禁止 MV、收藏、下载、评论、乐谱
          mvButton.setEnabled(false);
          collectButton.setEnabled(false);
          downloadButton.setEnabled(false);
          commentButton.setEnabled(false);
          sheetButton.setEnabled(false);
          // 设置歌曲名称
          songNameLabel.setText("");
          // 设置艺术家
          artistLabel.setText("");
          artistLabel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
          // 设置专辑名称
          albumLabel.setText("");
          albumLabel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
  
      // 显示专辑图片
      public void showAlbumImage() {
          MetaMusicInfo metaMusicInfo = player.getMetaMusicInfo();
          BufferedImage albumImage = metaMusicInfo.getAlbumImage();
          if (albumImage == null) albumImage = ImageConstants.DEFAULT_IMG;
          boolean isDefault = albumImage == ImageConstants.DEFAULT_IMG;
          saveAlbumImageMenuItem.setEnabled(!isDefault);
          // 专辑图片显示原本大小图片的一个缩小副本，并设置圆角
          BufferedImage img = ImageUtil.width(ImageUtil.cropCenter(albumImage), albumImageWidth);
          albumImageLabel.setIcon(new ImageIcon(ImageUtil.borderShadow(ImageUtil.radius(img, LARGE_ARC))));
          // 切换面板专辑图片
          changePaneButton.setIcon(new ImageIcon(ImageUtil.radius(ImageUtil.width(img, changePaneImageWidth), TINY_ARC)));
          if (miniDialog != null) miniDialog.infoLabel.setIcon(changePaneButton.getIcon());
          // 背景模糊
          if (blurType != BlurConstants.OFF) doBlur();
      }
  
      // 清空歌词
      private void clearLrc() {
          lrcList.setModel(emptyLrcListModel);
          lrcListModel.clear();
          if (statements != null) statements.clear();
          nextLrc = NextLrc.NOT_EXISTS;
      }
  
      // 歌词加载中
      private void lrcLoading() {
          clearLrc();
          lrcListModel.addElement(new Statement(0, LRC_LOADING_MSG));
  
          row = 0;
          nextLrc = NextLrc.LOADING;
          originalRatio = 0;
          lrcScrollPane.setVValue(currScrollVal = 0);
          // 更新歌词面板渲染
          LrcListRenderer renderer = (LrcListRenderer) lrcList.getCellRenderer();
          renderer.setRow(row);
          lrcList.setModel(lrcListModel);
      }
  
      // 加载歌词
      private void loadLrc(AudioFile file, NetMusicInfo musicInfo, int lyricType, boolean reload) {
          if (player.isEmpty()) return;
          clearLrc();
  
          LrcData lrcData = null;
          final int BAD_FORMAT = 0, NO_LRC = 1;
          int state = -1;
          boolean isFile = musicInfo == null;
  
          // 从文件读取歌词
          if (isFile) {
              File lrcFile = new File(FileUtil.getPathWithoutSuffix(file) + ".lrc");
              if (lrcFile.exists()) {
                  // 翻译同原歌词
                  lrcData = new LrcData(lrcFile);
                  if (lrcData.isEmpty()) {
                      statements = new LrcData(lrcFile, true).getStatements();
                      state = BAD_FORMAT;
                  } else statements = lrcData.getStatements();
                  // 繁体转换
                  if (lyricType == LyricType.TRADITIONAL_CN)
                      for (Statement stmt : statements) stmt.setLyric(StringUtil.toTraditionalChinese(stmt.getLyric()));
                      // 罗马音转换
                  else if (lyricType == LyricType.ROMA)
                      for (Statement stmt : statements) stmt.setLyric(StringUtil.toRomaji(stmt.getLyric()));
              } else state = NO_LRC;
          }
          // 在线音乐歌词
          else {
              switch (lyricType) {
                  // 歌词
                  case LyricType.ORIGINAL:
                  case LyricType.TRADITIONAL_CN:
                      if (musicInfo.hasLrc()) {
                          String lrc = musicInfo.getLrc();
                          lrcData = new LrcData(lrc);
                          if (lrcData.isEmpty()) {
                              statements = new LrcData(lrc, true).getStatements();
                              state = BAD_FORMAT;
                          } else statements = lrcData.getStatements();
                          // 繁体转换
                          if (lyricType == LyricType.TRADITIONAL_CN)
                              for (Statement stmt : statements)
                                  stmt.setLyric(StringUtil.toTraditionalChinese(stmt.getLyric()));
                      } else state = NO_LRC;
                      break;
                  // 翻译
                  case LyricType.TRANSLATION:
                      if (musicInfo.hasTrans()) {
                          String trans = musicInfo.getTrans();
                          lrcData = new LrcData(trans);
                          if (lrcData.isEmpty()) {
                              statements = new LrcData(trans, true).getStatements();
                              state = BAD_FORMAT;
                          } else statements = lrcData.getStatements();
                      }
                      // 无翻译时，加载原歌词
                      else {
                          loadLrc(file, musicInfo, LyricType.ORIGINAL, reload);
                          return;
                      }
                      break;
                  // 罗马音
                  case LyricType.ROMA:
                      if (musicInfo.hasRoma()) {
                          String roma = musicInfo.getRoma();
                          lrcData = new LrcData(roma);
                          if (lrcData.isEmpty()) {
                              statements = new LrcData(roma, true).getStatements();
                              state = BAD_FORMAT;
                          } else statements = lrcData.getStatements();
                      }
                      // 无罗马音时，使用原歌词进行转换
                      else if (musicInfo.hasLrc()) {
                          String lrc = musicInfo.getLrc();
                          lrcData = new LrcData(lrc);
                          if (lrcData.isEmpty()) {
                              statements = new LrcData(lrc, true).getStatements();
                              state = BAD_FORMAT;
                          } else statements = lrcData.getStatements();
                          for (Statement stmt : statements) stmt.setLyric(StringUtil.toRomaji(stmt.getLyric()));
                      } else state = NO_LRC;
                      break;
              }
          }
          lrcStr = lrcData == null ? "" : lrcData.getLrcStr();
          if (StringUtil.isEmpty(StringUtil.cleanLrcStr(lrcStr))) state = NO_LRC;
  
          if (state == BAD_FORMAT) lrcListModel.addElement(new Statement(0, BAD_FORMAT_LRC_MSG));
          else if (state == NO_LRC) lrcListModel.addElement(new Statement(0, NO_LRC_MSG));
          if (ListUtil.notEmpty(statements)) {
              for (int i = 0, s = statements.size(); i < s; i++) lrcListModel.addElement(statements.get(i));
          }
          // 标记为无歌词 / 不支持滚动
          nextLrc = state == BAD_FORMAT ? NextLrc.BAD_FORMAT : state == NO_LRC ? NextLrc.NOT_EXISTS : 0;
          row = state == BAD_FORMAT || state == NO_LRC ? 0 : -1;
  
          originalRatio = 0;
          lrcScrollPane.setVValue(currScrollVal = 0);
          // 更新歌词面板渲染
          LrcListRenderer renderer = (LrcListRenderer) lrcList.getCellRenderer();
          renderer.setRow(row);
          lrcList.setModel(lrcListModel);
          lrcScrollAnimation = true;
          if (reload) seekLrc(player.getCurrTimeSeconds());
      }
  
      private void resetMp() {
          player.disposeMp();
          player.initMp();
          if (!player.isPlaying()) return;
          playLoaded(false);
          seekLrc(0);
      }
  
      // 初始化播放器设置
      public void initPlayer() {
          player.setMute(isMute);
          player.setVolume((float) volumeSlider.getValue() / MAX_VOLUME);
          player.setRate(currRate);
          player.setBalance(currBalance);
          player.adjustEqualizerBands(ed);
  
          MediaPlayer mp = player.getMp();
          // 设置频谱更新间隔
          mp.setAudioSpectrumInterval(SpectrumConstants.PLAYER_INTERVAL);
          // 设置频谱更新数量
          mp.setAudioSpectrumNumBands(SpectrumConstants.NUM_BANDS);
          // 设置频谱阈值，默认是 -60
          mp.setAudioSpectrumThreshold(SpectrumConstants.THRESHOLD);
  
          // 部分无法提前获取时长的歌曲，等待播放时更新时长
          mp.totalDurationProperty().addListener((observable, oldValue, newValue) -> {
              MetaMusicInfo musicInfo = player.getMetaMusicInfo();
              if (musicInfo.hasDuration()) return;
              double duration = newValue.toSeconds();
              musicInfo.setDuration(duration);
              // 填充音乐时长
              if (player.loadedNetMusic()) player.getMusicInfo().setDuration(duration);
              else player.getAudioFile().setDuration(duration);
              // 刷新列表时长显示
              if (musicList.isShowing()) musicList.repaint();
              else if (netMusicList.isShowing()) netMusicList.repaint();
              else if (playQueue.isShowing()) playQueue.repaint();
              // 重置总时间
              durationLabel.setText(player.getDurationString());
              // 设置当前播放时间标签的最佳大小，避免导致进度条长度发生变化！
              String t = durationLabel.getText().replaceAll("[1-9]", "0");
              FontMetrics m = durationLabel.getFontMetrics(globalFont);
              Dimension d = new Dimension(m.stringWidth(t) + 2, durationLabel.getHeight());
              currTimeLabel.setPreferredSize(d);
              durationLabel.setPreferredSize(d);
          });
  
          mp.setOnError(() -> {
              MediaException.Type type = mp.getError().getType();
              NetMusicInfo musicInfo = player.getMusicInfo();
  
              // 耳机取下导致的播放异常 或者 转格式后的未知异常，重新播放
              if (type == MediaException.Type.PLAYBACK_HALTED || type == MediaException.Type.UNKNOWN && musicInfo.isFlac()) {
                  resetMp();
              }
              // 歌曲 url 过期后重新加载 url 再播放
              else if (type == MediaException.Type.MEDIA_INACCESSIBLE
                      || type == MediaException.Type.MEDIA_UNAVAILABLE
                      || type == MediaException.Type.UNKNOWN) {
                  // 重置标题
                  updateTitle(REFRESH_URL);
                  playExecutor.execute(() -> {
                      musicInfo.setUrl("");
                      MusicServerUtil.fillMusicUrl(musicInfo);
                      resetMp();
                  });
              }
          });
          // 频谱监听器
          mp.setAudioSpectrumListener(new AudioSpectrumListener() {
              @Override
              public void spectrumDataUpdate(double timestamp, double duration, float[] magnitudes, float[] phases) {
                  double[] specs = player.specs;
                  double[] specsOrigin = player.specsOrigin;
                  double[] specsGap = player.specsGap;
                  final int barNum = SpectrumConstants.barNum, nFactor = barNum - 30, numBands = SpectrumConstants.NUM_BANDS, maxHeight = SpectrumConstants.barMaxHeight;
                  double avg = 0;
                  for (int i = 0; i < numBands; i++) {
                      int mult = i / barNum;
                      int n = mult % 2 == 0 ? i - barNum * mult : barNum - (i - barNum * mult);
                      int spectrum = n > nFactor ? 0 : (int) SpectrumUtil.handleMagnitude(magnitudes[n + 20]);
                      avg += spectrum * 1.2;
                  }
                  avg = avg / numBands * 1.4 / barNum + 0.42;
                  for (int i = 0; i < barNum; i++) {
                      double h = Math.min(maxHeight, SpectrumUtil.handleMagnitude(magnitudes[i]) * avg);
                      specsOrigin[i] = h;
                      specsGap[i] = Math.abs(specsOrigin[i] - specs[i]);
                  }
  //                for (int i = 0, num = specsOrigin.length; i < num; i++) {
  //                    specsOrigin[i] = SpectrumUtils.handleMagnitude(magnitudes[i]);
  //                    specsGap[i] = Math.abs(specsOrigin[i] - specs[i]);
  //                }
              }
          });
          // 播放器状态监听
          mp.statusProperty().addListener((observable, oldValue, newValue) -> {
              // 排除正在销毁的 mp 对象的事件调用
              if (mp != player.getMp()) return;
              if (player.isLoaded() && !READY.equals(statusText)) updateTitle(READY);
              else if (player.isPlaying() && !PLAYING.equals(statusText)) updateTitle(PLAYING);
              else if (player.isPaused() && !PAUSING.equals(statusText)) updateTitle(PAUSING);
          });
          // 缓冲时间改变后刷新时间条
          mp.bufferProgressTimeProperty().addListener((observable, oldValue, newValue) -> timeBar.repaint());
          mp.currentTimeProperty().addListener((observable, oldValue, newValue) -> {
              // 随着播放，设置进度条和时间标签的值
              try {
                  // 未被操作时频繁更新时间条
                  if (!timeBar.getValueIsAdjusting()) timeBar.setValue((int) (player.getCurrScale() * TIME_BAR_MAX));
                  // 监听并更新歌词(若有歌词)
                  if (nextLrc < 0) return;
                  double currTimeSeconds = newValue.toSeconds();
                  // 判断是否该高亮下一行歌词，每次时间更新可能跳过多行歌词
                  boolean wrapped = false;
                  while (nextLrc < statements.size() && currTimeSeconds >= statements.get(nextLrc).getTime() - lrcOffset) {
                      row = nextLrc;
                      if (!lrcScrollAnimation && !lrcScrollWaiting) {
                          currScrollVal = lrcScrollPane.getVValue();
                          if (!lrcDelayScrollTimer.isRunning()) lrcDelayScrollTimer.start();
                      }
                      LrcListRenderer renderer = (LrcListRenderer) lrcList.getCellRenderer();
                      renderer.setRow(row);
                      nextLrc++;
                      originalRatio = 0;
                      wrapped = true;
                  }
                  if (wrapped) return;
                  // 每一句歌词最后一个 originalRatio 设成 1 避免歌词滚动不完整！
                  if (nextLrc > 0 && nextLrc < statements.size() && currTimeSeconds + 0.15 > statements.get(nextLrc).getTime() - lrcOffset)
                      originalRatio = 1;
                  else updateOriginalRatio(currTimeSeconds);
              } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
  
              }
          });
          // 播放结束
          mp.setOnEndOfMedia(() -> {
              switch (currPlayMode) {
                  case PlayMode.DISABLED:
                      stopPlayback();
                      break;
                  case PlayMode.SINGLE:
                      playLoaded(true);
                      seekLrc(0);
                      break;
                  case PlayMode.SEQUENCE:
                      if (currSong < playQueueModel.size() - 1) playNext();
                          // 播放完后卸载文件
                      else unload();
                      break;
                  case PlayMode.LIST_CYCLE:
                      playNext();
                      break;
                  case PlayMode.SHUFFLE:
                      // 播放下一个随机
                      playNextShuffle();
                      break;
              }
          });
      }
  
      // 更新歌词比率
      private void updateOriginalRatio(double t) {
          StringTwoColor stc = desktopLyricDialog.getStc();
          double tempRatio = 0;
          if (nextLrc > 0) {
              if (stc.isByWord()) {
                  tempRatio = stc.calcRatio(t, statements.get(nextLrc - 1).getTime() - lrcOffset);
              } else {
                  Statement ls = statements.get(nextLrc - 1), ns = nextLrc < statements.size() ? statements.get(nextLrc) : null;
                  tempRatio = (t - ls.getTime() + lrcOffset) /
                          ((ls.hasEndTime() ? ls.getEndTime() - lrcOffset
                                  : (ns != null ? ns.getTime() - lrcOffset
                                  : player.getDurationSeconds())) - ls.getTime() + lrcOffset);
              }
          }
          originalRatio = tempRatio > 1 ? (statements.get(nextLrc - 1).hasEndTime() ? 1 : 0) : tempRatio;
      }
  
      // 播放载入的歌曲
      private void playLoaded(boolean replay) {
          // 播放器停止后重新播放需要手动设置标题
          if (player.isStopped()) updateTitle(PLAYING);
          if (replay) player.replay();
          else if (!player.play()) return;
          // 开始播放，若播放成功则更新 UI
          playOrPauseButton.setIcon(ImageUtil.dye(pauseIcon, currUIStyle.getIconColor()));
          playOrPauseButton.setToolTipText(PAUSE_TIP);
          if (miniDialog != null) {
              miniDialog.playOrPauseButton.setIcon(playOrPauseButton.getIcon());
              miniDialog.playOrPauseButton.setToolTipText(playOrPauseButton.getToolTipText());
          }
          // 刷新播放中的图标
          if (musicList.isShowing()) musicList.repaint();
          else if (netMusicList.isShowing()) netMusicList.repaint();
          else if (playQueue.isShowing()) playQueue.repaint();
      }
  
      // 添加到下一首播放
      private void nextPlay(CustomList list) {
          DefaultListModel model = (DefaultListModel) list.getModel();
  
          Object obj = list.getSelectedValue();
  
          // 下载列表先创建实例
          if (model == downloadListModel) {
              Task task = (Task) obj;
              AudioFile audioFile = new AudioFile(task.getDest());
              if (!audioFile.exists()) {
                  new TipDialog(THIS, FILE_NOT_FOUND_MSG).showDialog();
                  return;
              }
              MediaUtil.fillAudioFileInfo(audioFile);
              obj = audioFile;
          }
  
          // 判断歌曲是否正在播放
          if (player.loadedMusicResource((MusicResource) obj)) {
              new TipDialog(THIS, ALREADY_PLAYING_MSG).showDialog();
              return;
          }
  
          // 先判断歌曲在不在播放队列；若在，先删除
          for (int i = 0, size = playQueueModel.size(); i < size; i++) {
              if (playQueueModel.get(i).equals(obj)) {
                  // 解决删除元素带来的性能问题
                  playQueue.setModel(emptyListModel);
                  playQueueModel.remove(i);
                  if (i < currSong) currSong--;
                  playQueue.setModel(playQueueModel);
                  break;
              }
          }
  
          // 将单首歌曲加到当前播放歌曲之后(若不在播放队列)
          int in = Math.min(currSong + 1, playQueueModel.size());
          playQueueModel.add(in, (MusicResource) obj);
          new TipDialog(THIS, NEXT_PLAY_SUCCESS_MSG).showDialog();
      }
  
      private boolean playSelected(CustomList list, boolean allowRetry) {
          return playSelected(list, allowRetry, true);
      }
  
      // 播放选中歌曲，返回是否播放成功
      private boolean playSelected(CustomList list, boolean allowRetry, boolean instantPlay) {
          Object obj = list.getSelectedValue();
          if (obj == null) return false;
  
          AudioFile file = null;
          NetMusicInfo musicInfo = null;
  
          // 将指定 list 中的歌曲加到播放队列
          DefaultListModel model = (DefaultListModel) list.getModel();
  
          // 先判断歌曲在不在播放队列，若在直接选中播放队列对应歌曲
          boolean exists = false, pre = false;
  
          // 下载列表先创建实例
          if (model == downloadListModel) {
              Task task = (Task) obj;
              AudioFile audioFile = new AudioFile(task.getDest());
              if (!audioFile.exists()) {
                  new TipDialog(THIS, FILE_NOT_FOUND_MSG).showDialog();
                  return false;
              }
              MediaUtil.fillAudioFileInfo(audioFile);
              obj = audioFile;
          }
  
          // 下载/在线音乐列表播放歌曲时，将单首歌曲加到当前播放歌曲之后播放(若不在播放队列)
          if (model == downloadListModel || model == netMusicListModel) {
              for (int i = 0, size = playQueueModel.size(); i < size; i++) {
                  if (playQueueModel.get(i).equals(obj)) {
                      exists = true;
                      playQueue.setSelectedIndex(i);
                      break;
                  }
              }
              if (!exists) {
                  int in = currSong + 1;
                  playQueueModel.add(in, (MusicResource) obj);
                  playQueue.setSelectedIndex(in);
              }
          }
  
          // 其他列表播放歌曲时，先清空播放队列，将该列表作为播放队列
          else if (model != playQueueModel) {
              pre = true;
              // 此处会造成性能问题，交给线程池处理，先开始播放
              globalExecutor.execute(() -> {
                  // 先判断两个列表元素是否完全一致
                  boolean isSame = model.size() == playQueueModel.size();
                  for (int i = 0, size = model.size(); isSame && i < size; i++) {
                      if (playQueueModel.get(i) != model.get(i)) {
                          isSame = false;
                          break;
                      }
                  }
                  if (!isSame) {
                      playQueue.setModel(emptyListModel);
                      playQueueModel.clear();
                      for (int i = 0, size = model.size(); i < size; i++) {
                          playQueueModel.addElement((MusicResource) model.get(i));
                      }
                      playQueue.setModel(playQueueModel);
                  }
                  playQueue.setSelectedIndex(list.getSelectedIndex());
              });
          }
  
          Object o = pre ? list.getSelectedValue() : playQueue.getSelectedValue();
          currSong = pre ? list.getSelectedIndex() : playQueue.getSelectedIndex();
  
          boolean isAudioFile = o instanceof AudioFile;
  
          // 本地文件
          if (isAudioFile) {
              file = (AudioFile) o;
              // 文件不存在，在不允许重试情况下询问是否从列表中删除
              if (!file.exists()) {
                  if (!allowRetry) {
                      ConfirmDialog confirmDialog = new ConfirmDialog(THIS, ASK_REMOVE_FIFE_NOT_FOUND_MSG, YES, NO);
                      confirmDialog.showDialog();
                      int response = confirmDialog.getResponse();
                      if (response == JOptionPane.YES_OPTION) {
                          model.removeElement(o);
                          // 解决删除元素带来的性能问题
                          playQueue.setModel(emptyListModel);
                          playQueueModel.removeElement(o);
                          playQueue.setModel(playQueueModel);
                          currSong--;
                          new TipDialog(THIS, REMOVE_SUCCESS_MSG).showDialog();
                      }
                  } else retryPlay();
                  return false;
              }
              // 如果歌曲信息不完整，获取歌曲文件的头信息
              if (!file.isIntegrated()) MediaUtil.fillAudioFileInfo(file);
          }
          // 在线音乐
          else {
              try {
                  musicInfo = (NetMusicInfo) o;
                  // 如果歌曲信息不完整，获取歌曲额外的信息(除了 url)
                  MusicServerUtil.fillMusicInfo(musicInfo);
                  updateRenderer(netMusicList);
              } catch (Exception e) {
                  if (e instanceof IORuntimeException) {
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } else if (e instanceof HttpException) {
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } else {
                      new TipDialog(THIS, GET_RESOURCE_FAILED_MSG).showDialog();
                  }
                  if (allowRetry) retryPlay();
                  return false;
              }
          }
          try {
              player.load(file, musicInfo);
              loadUI(file, musicInfo);
  
              if (isAudioFile && !file.isIntegrated())
                  throw new IllegalMediaException("音频文件损坏");
  
              // 本地音乐直接加载歌词，在线音乐等待歌词缓冲完成再加载 url
              if (isAudioFile) {
                  loadLrc(file, null, currLrcType, false);
              } else {
                  if (!musicInfo.hasLrc()) lrcLoading();
                  NetMusicInfo finalMusicInfo = musicInfo;
                  Future<?> lrcTask = globalExecutor.submit(() -> {
                      try {
                          MusicServerUtil.fillLrc(finalMusicInfo);
                      } catch (Exception e) {
  
                      } finally {
                          if (!finalMusicInfo.equals(player.getMusicInfo())) return;
                          loadLrc(null, finalMusicInfo, currLrcType, false);
                      }
                  });
  
                  // 加载在线音乐的 url
                  MusicServerUtil.fillMusicUrl(musicInfo);
                  String url = musicInfo.getUrl();
                  // 歌曲无版权
                  if (StringUtil.isEmpty(url)) throw new NoCopyrightException("歌曲无版权");
                  // 酷狗的链接给的 wav，实际上是 mp3 格式，这种情况以 mp3 格式下载到本地再播放
                  // 另外 flac 格式文件先下载 flac 文件，转为 mp3 格式再播放
                  if (url.endsWith(Format.WAV) || musicInfo.isFlac()) {
                      String fileName = musicInfo.toFileName();
                      file = new AudioFile(SimplePath.CACHE_PATH + fileName);
                      AudioFile tmpFile = new AudioFile(FileUtil.replaceSuffix(file, Format.MP3));
                      if (!tmpFile.exists()) {
                          // 下载歌曲
                          if (!file.exists() || FileUtil.startsWithLeftBrace(file)) {
                              loading.start();
                              loading.setText(LOADING_MSG);
                              MusicServerUtil.download(musicInfo.getUrl(), file.getPath(), (finishedSize, totalSize) -> {
                                  loading.setText("加载歌曲文件，" + String.format("%.1f%%", (double) finishedSize / totalSize * 100).replace(".0", ""));
                              });
                          }
                          // Flac 文件需要转换格式，并删除原来的文件
                          if (musicInfo.isFlac()) {
                              loading.setText("转换音频文件格式......");
                              MediaUtil.convert(file, tmpFile);
                              file.delete();
                          }
                      }
                      file = tmpFile;
                      if (loading.isShowing()) loading.stop();
                      // 刷新加载歌曲
                      player.load(file, musicInfo);
                  }
                  // 加载 url 时可能拖动进度条导致歌词偏移，让歌词重头开始
                  if (lrcTask.isDone() && nextLrc > 0) seekLrc(0);
              }
  
              // 初始化 MediaPlayer 对象
              player.initMp();
  
              if (instantPlay) playLoaded(false);
              // 添加到历史歌曲列表
              if (model != historyModel) {
                  // 先删掉重复的音乐，再添加
                  historyModel.removeElement(musicInfo == null ? file : musicInfo);
                  historyModel.add(0, musicInfo == null ? file : musicInfo);
                  // 超过最大记录条数，删除最后一条
                  int size = historyModel.getSize();
                  if (size > maxHistoryCount) historyModel.remove(size - 1);
              }
              retry = 0;
              return true;
          } catch (Exception e) {
              updateTitle(LOAD_TRACK_FAILED);
              if (e instanceof MediaException) new TipDialog(THIS, UNSUPPORTED_AUDIO_FILE_MSG).showDialog();
              else if (e instanceof IllegalArgumentException || e instanceof IllegalMediaException)
                  new TipDialog(THIS, INVALID_AUDIO_FILE_MSG).showDialog();
              else if (e instanceof IORuntimeException) new TipDialog(THIS, NO_NET_MSG).showDialog();
              else if (e instanceof HttpException) new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              else new TipDialog(THIS, GET_RESOURCE_FAILED_MSG).showDialog();
          }
          if (allowRetry) retryPlay();
          return false;
      }
  
      // 重试播放
      private void retryPlay() {
          // 异常后允许重试的情况下自动播放下一首，超出最大重试次数停止，防止死循环
          if (currPlayMode == PlayMode.SINGLE) return;
          if (++retry <= MAX_RETRY) {
              // 顺序播放
              if (currPlayMode == PlayMode.SEQUENCE) {
                  if (currSong < playQueueModel.size() - 1) playNext();
                      // 播放完后卸载文件
                  else unload();
              }
              // 列表循环
              else if (currPlayMode == PlayMode.LIST_CYCLE) playNext();
                  // 随机播放
              else if (currPlayMode == PlayMode.SHUFFLE) playNextShuffle();
          } else retry = 0;
      }
  
      // 播放上一曲
      private void playLast() {
          int size = playQueueModel.getSize();
          // 当前播放列表非空
          if (size == 0) return;
          // 选中上一曲
          currSong = currSong - 1 < 0 ? size - 1 : currSong - 1;
          playQueue.setSelectedIndex(currSong);
          playExecutor.execute(() -> playSelected(playQueue, true));
      }
  
      // 播放下一曲 / 顺序播放
      private void playNext() {
          int size = playQueueModel.getSize();
          // 当前播放列表非空
          if (size == 0) return;
          // 选中下一曲
          currSong = (currSong + 1) % size;
          playQueue.setSelectedIndex(currSong);
          playExecutor.execute(() -> playSelected(playQueue, true));
      }
  
      // 生成随机播放序列
      private void generateShuffleList() {
          shuffleList.clear();
          for (int i = 0, size = playQueueModel.getSize(); i < size; i++) shuffleList.add(i);
          Collections.shuffle(shuffleList);
          shuffleIndex = 0;
      }
  
      // 播放随机列表的下一首
      private void playNextShuffle() {
          // 随机列表为空，或者播放队列发生变化，就生成一个
          if (shuffleIndex >= shuffleList.size() || shuffleList.size() != playQueueModel.size()) {
              generateShuffleList();
          }
          if (shuffleList.isEmpty()) return;
          // 选择随机列表下一首
          playQueue.setSelectedIndex(shuffleList.get(shuffleIndex++));
          playExecutor.execute(() -> playSelected(playQueue, true));
      }
  
      // 更新 currSong 的值，在播放队列歌曲发生变化后调用
      private void updateCurrSong() {
          currSong = -1;
          for (int i = 0, size = playQueueModel.size(); i < size; i++) {
              if (!player.loadedMusicResource(playQueueModel.get(i))) continue;
              currSong = i;
              return;
          }
      }
  
      // 改变到播完暂停
      private void changeToDisabled(boolean showDialog) {
          playModeButton.setIcon(ImageUtil.dye(playModeDisabledIcon, currUIStyle.getIconColor()));
          playModeButton.setToolTipText(PLAY_MODE_DISABLED_TIP);
          currPlayMode = PlayMode.DISABLED;
          if (showDialog) new TipDialog(THIS, CHANGE_DISABLED_MSG).showDialog();
      }
  
      // 改变到单曲循环
      private void changeToSingle(boolean showDialog) {
          playModeButton.setIcon(ImageUtil.dye(singleIcon, currUIStyle.getIconColor()));
          playModeButton.setToolTipText(SINGLE_TIP);
          currPlayMode = PlayMode.SINGLE;
          if (showDialog) new TipDialog(THIS, CHANGE_SINGLE_MSG).showDialog();
      }
  
      // 改变到顺序播放
      private void changeToSequence(boolean showDialog) {
          playModeButton.setIcon(ImageUtil.dye(sequenceIcon, currUIStyle.getIconColor()));
          playModeButton.setToolTipText(SEQUENCE_TIP);
          currPlayMode = PlayMode.SEQUENCE;
          if (showDialog) new TipDialog(THIS, CHANGE_SEQUENCE_MSG).showDialog();
      }
  
      // 改变到列表循环
      private void changeToListCycle(boolean showDialog) {
          playModeButton.setIcon(ImageUtil.dye(listCycleIcon, currUIStyle.getIconColor()));
          playModeButton.setToolTipText(LIST_CYCLE_TIP);
          currPlayMode = PlayMode.LIST_CYCLE;
          if (showDialog) new TipDialog(THIS, CHANGE_LIST_CYCLE_MSG).showDialog();
      }
  
      // 变为随机播放
      private void changeToShuffle(boolean showDialog) {
          playModeButton.setIcon(ImageUtil.dye(shuffleIcon, currUIStyle.getIconColor()));
          playModeButton.setToolTipText(SHUFFLE_TIP);
          currPlayMode = PlayMode.SHUFFLE;
          if (showDialog) new TipDialog(THIS, CHANGE_SHUFFLE_MSG).showDialog();
      }
  
      // 列表排序
      private void sortFiles(int method, int order) {
          DefaultListModel model = (DefaultListModel) musicList.getModel();
          List<Object> list = Collections.list(model.elements());
          if (method == SortMethod.BY_SONG_AND_FILE_NAME) {
              Collections.sort(list, (o1, o2) -> {
                  try {
                      String s1 = null, s2 = null;
                      if (o1 instanceof AudioFile) {
                          AudioFile f1 = (AudioFile) o1;
                          s1 = f1.toString();
                      } else if (o1 instanceof NetMusicInfo) s1 = ((NetMusicInfo) o1).getName();
                      if (o2 instanceof AudioFile) {
                          AudioFile f2 = (AudioFile) o2;
                          s2 = f2.toString();
                      } else if (o2 instanceof NetMusicInfo) s2 = ((NetMusicInfo) o2).getName();
                      return order == SortMethod.ASCENDING ? StringUtil.compare(s1, s2) : StringUtil.compare(s2, s1);
                  } catch (BadHanyuPinyinOutputFormatCombination e) {
                      LogUtil.error(e);
                  }
                  return 0;
              });
          } else if (method == SortMethod.BY_SONG_NAME) {
              Collections.sort(list, (o1, o2) -> {
                  try {
                      String s1 = null, s2 = null;
                      if (o1 instanceof AudioFile) s1 = ((AudioFile) o1).getSongName();
                      else if (o1 instanceof NetMusicInfo) s1 = ((NetMusicInfo) o1).getName();
                      if (o2 instanceof AudioFile) s2 = ((AudioFile) o2).getSongName();
                      else if (o2 instanceof NetMusicInfo) s2 = ((NetMusicInfo) o2).getName();
                      return order == SortMethod.ASCENDING ? StringUtil.compare(s1, s2) : StringUtil.compare(s2, s1);
                  } catch (BadHanyuPinyinOutputFormatCombination e) {
                      LogUtil.error(e);
                  }
                  return 0;
              });
          } else if (method == SortMethod.BY_ARTIST_NAME) {
              Collections.sort(list, (o1, o2) -> {
                  try {
                      String s1 = null, s2 = null;
                      if (o1 instanceof AudioFile) s1 = ((AudioFile) o1).getArtist();
                      else if (o1 instanceof NetMusicInfo) s1 = ((NetMusicInfo) o1).getArtist();
                      if (o2 instanceof AudioFile) s2 = ((AudioFile) o2).getArtist();
                      else if (o2 instanceof NetMusicInfo) s2 = ((NetMusicInfo) o2).getArtist();
                      return order == SortMethod.ASCENDING ? StringUtil.compare(s1, s2) : StringUtil.compare(s2, s1);
                  } catch (BadHanyuPinyinOutputFormatCombination e) {
                      LogUtil.error(e);
                  }
                  return 0;
              });
          } else if (method == SortMethod.BY_ALBUM_NAME) {
              Collections.sort(list, (o1, o2) -> {
                  try {
                      String s1 = null, s2 = null;
                      if (o1 instanceof AudioFile) s1 = ((AudioFile) o1).getAlbum();
                      else if (o1 instanceof NetMusicInfo) s1 = ((NetMusicInfo) o1).getAlbumName();
                      if (o2 instanceof AudioFile) s2 = ((AudioFile) o2).getAlbum();
                      else if (o2 instanceof NetMusicInfo) s2 = ((NetMusicInfo) o2).getAlbumName();
                      return order == SortMethod.ASCENDING ? StringUtil.compare(s1, s2) : StringUtil.compare(s2, s1);
                  } catch (BadHanyuPinyinOutputFormatCombination e) {
                      LogUtil.error(e);
                  }
                  return 0;
              });
          } else if (method == SortMethod.BY_FILE_NAME) {
              Collections.sort(list, (o1, o2) -> {
                  String s1 = "", s2 = "";
                  if (o1 instanceof AudioFile) s1 = ((AudioFile) o1).getName();
                  else if (o1 instanceof NetMusicInfo) s1 = o1.toString();
                  if (o2 instanceof AudioFile) s2 = ((AudioFile) o2).getName();
                  else if (o2 instanceof NetMusicInfo) s2 = o2.toString();
                  try {
                      return order == SortMethod.ASCENDING ? StringUtil.compare(s1, s2) : StringUtil.compare(s2, s1);
                  } catch (BadHanyuPinyinOutputFormatCombination e) {
                      LogUtil.error(e);
                      return 0;
                  }
              });
          }
          // 按时长排序只支持本地音乐
          else if (method == SortMethod.BY_TIME && model == musicListModel) {
              Collections.sort(list, (o1, o2) -> {
                  double d1;
                  double d2;
                  try {
                      d1 = ((AudioFile) o1).getDuration();
                      d2 = ((AudioFile) o2).getDuration();
                  } catch (Exception e) {
                      LogUtil.error(e);
                      return 0;
                  }
                  return order == SortMethod.ASCENDING ? Double.compare(d1, d2) : Double.compare(d2, d1);
              });
          }
          // 按修改时间排序只支持本地音乐
          else if (method == SortMethod.BY_LAST_MODIFIED_TIME && model == musicListModel) {
              Collections.sort(list, (o1, o2) -> {
                  long t1 = ((File) o1).lastModified();
                  long t2 = ((File) o2).lastModified();
                  return order == SortMethod.ASCENDING ? Long.compare(t1, t2) : Long.compare(t2, t1);
              });
          }
          // 按创建时间排序只支持本地音乐
          else if (method == SortMethod.BY_CREATION_TIME && model == musicListModel) {
              Collections.sort(list, (o1, o2) -> {
                  long t1 = FileUtil.getCreationTime((File) o1);
                  long t2 = FileUtil.getCreationTime((File) o2);
                  return order == SortMethod.ASCENDING ? Long.compare(t1, t2) : Long.compare(t2, t1);
              });
          }
          // 按访问时间排序只支持本地音乐
          else if (method == SortMethod.BY_LAST_ACCESS_TIME && model == musicListModel) {
              Collections.sort(list, (o1, o2) -> {
                  long t1 = FileUtil.getAccessTime((File) o1);
                  long t2 = FileUtil.getAccessTime((File) o2);
                  return order == SortMethod.ASCENDING ? Long.compare(t1, t2) : Long.compare(t2, t1);
              });
          }
          // 文件大小排序只支持本地音乐
          else if (method == SortMethod.BY_SIZE && model == musicListModel) {
              Collections.sort(list, (o1, o2) -> {
                  long l1 = ((File) o1).length();
                  long l2 = ((File) o2).length();
                  return order == SortMethod.ASCENDING ? Long.compare(l1, l2) : Long.compare(l2, l1);
              });
          }
          musicList.setModel(emptyListModel);
          model.clear();
          list.forEach(o -> model.addElement(o));
          list.clear();
          musicList.setModel(model);
          countLabel.setText(String.format(TOTAL_MSG, model.size()));
      }
  
      // 开启频谱
      public void openSpectrum() {
          if (!player.loadedMusicResource()) return;
          if (!spectrumPanel.isDrawSpectrum()) spectrumPanel.setDrawSpectrum(true);
          // 开始动画之前先判断在不在运行，防止重复运行动画造成卡顿！
          if (!spectrumTimer.isRunning()) spectrumTimer.start();
          spectrumPanel.setVisible(true);
      }
  
      // 关闭频谱
      private void closeSpectrum() {
          spectrumTimer.stop();
          spectrumPanel.setVisible(false);
      }
  
      // 更新单选菜单项状态
      private void updateMenuItemStatus(List<CustomRadioButtonMenuItem> buttonGroup, CustomRadioButtonMenuItem menuItem) {
          buttonGroup.forEach(mi -> mi.setSelected(mi == menuItem));
      }
  
      // 改变所有单选菜单项图标
      private void updateMenuItemIcon(CustomPopupMenu popupMenu) {
          Color iconColor = currUIStyle.getIconColor();
          Component[] components = popupMenu.getComponents();
          for (Component c : components) {
              if (c instanceof CustomRadioButtonMenuItem) {
                  CustomRadioButtonMenuItem mi = (CustomRadioButtonMenuItem) c;
                  if (mi.isSelected()) mi.setIcon(ImageUtil.dye(dotIcon, iconColor));
                  else mi.setIcon(null);
              } else if (c instanceof CustomCheckMenuItem) {
                  CustomCheckMenuItem mi = (CustomCheckMenuItem) c;
                  if (mi.isSelected()) mi.setIcon(ImageUtil.dye(tickIcon, iconColor));
                  else mi.setIcon(null);
              }
          }
      }
  
      // 改变所有作为标签按钮样式
      private void updateTabButtonStyle() {
          Component[] components = personalMusicToolBar.getComponents();
          for (int i = 0, len = components.length; i < len; i++) {
              TabButton b = (TabButton) components[i];
              b.setActive(currPersonalMusicTab == i);
          }
          components = recommendToolBar.getComponents();
          for (int i = 0, len = components.length; i < len; i++) {
              TabButton b = (TabButton) components[i];
              b.setActive(currRecommendTab == i);
          }
      }
  
      // 更新菜单项样式
      private void updateMenuItemStyle(CustomPopupMenu popupMenu) {
          Color textColor = currUIStyle.getTextColor();
          Component[] components = popupMenu.getComponents();
          for (Component c : components) {
              c.setForeground(textColor);
              if (c instanceof CustomMenuItem) {
                  CustomMenuItem menuItem = (CustomMenuItem) c;
                  menuItem.setUI(new MenuItemUI(textColor));
              } else if (c instanceof CustomRadioButtonMenuItem) {
                  CustomRadioButtonMenuItem menuItem = (CustomRadioButtonMenuItem) c;
                  menuItem.setUI(new RadioButtonMenuItemUI(textColor));
              } else if (c instanceof CustomCheckMenuItem) {
                  CustomCheckMenuItem menuItem = (CustomCheckMenuItem) c;
                  menuItem.setUI(new CheckMenuItemUI(textColor));
              } else if (c instanceof CustomMenu) {
                  CustomMenu menu = (CustomMenu) c;
                  menu.setUI(new MenuUI(textColor));
              }
          }
      }
  
      // 改变 UI 主题
      public void changeUIStyle(UIStyle style) {
          if (!style.hasImg()) {
              changeUIStyle(styles.get(0));
              return;
          }
  
          currUIStyle = style;
  
          Color iconColor = style.getIconColor();
          Color darkerIconColor = ColorUtil.darker(iconColor, 0.3f);
          Color textColor = style.getTextColor();
          Color darkerTextColor = ColorUtil.darker(textColor);
          Color darkerTextAlphaColor = ColorUtil.deriveAlphaColor(darkerTextColor, 0.5f);
          Color selectedColor = style.getSelectedColor();
          Color foreColor = style.getForeColor();
          Color scrollBarColor = style.getScrollBarColor();
          Color timeBarColor = style.getTimeBarColor();
          Color lrcColor = style.getLrcColor();
          Color highlightColor = style.getHighlightColor();
  
          // 托盘
          trayIconImg.setImage(ImageUtil.dye(trayIcon, iconColor).getImage());
  
          // 标题栏
          titleLabel.setForeground(textColor);
          titleLabel.setIcon(ImageUtil.dye((ImageIcon) titleLabel.getIcon(), iconColor));
          hideDetailButton.setIcon(ImageUtil.dye((ImageIcon) hideDetailButton.getIcon(), iconColor));
          styleToolButton.setIcon(ImageUtil.dye((ImageIcon) styleToolButton.getIcon(), iconColor));
          mainMenuButton.setIcon(ImageUtil.dye((ImageIcon) mainMenuButton.getIcon(), iconColor));
          splitLabel.setIcon(ImageUtil.dye((ImageIcon) splitLabel.getIcon(), iconColor));
          miniButton.setIcon(ImageUtil.dye((ImageIcon) miniButton.getIcon(), iconColor));
          minimizeButton.setIcon(ImageUtil.dye((ImageIcon) minimizeButton.getIcon(), iconColor));
          maximizeButton.setIcon(ImageUtil.dye((ImageIcon) maximizeButton.getIcon(), iconColor));
          closeButton.setIcon(ImageUtil.dye((ImageIcon) closeButton.getIcon(), iconColor));
  
          // 列表为空提示面板
          emptyHintLabel.setIcon(ImageUtil.dye(emptyHintIcon, iconColor));
          emptyHintLabel.setIconTextGap(10);
          emptyHintLabel.setForeground(textColor);
  
          // 按钮被禁用时颜色
          netMusicBackwardButton.setDisabledIcon(ImageUtil.dye((ImageIcon) netMusicBackwardButton.getIcon(), darkerIconColor));
          netPlaylistBackwardButton.setDisabledIcon(ImageUtil.dye((ImageIcon) netPlaylistBackwardButton.getIcon(), darkerIconColor));
          netAlbumBackwardButton.setDisabledIcon(ImageUtil.dye((ImageIcon) netAlbumBackwardButton.getIcon(), darkerIconColor));
          netArtistBackwardButton.setDisabledIcon(ImageUtil.dye((ImageIcon) netArtistBackwardButton.getIcon(), darkerIconColor));
          netRadioBackwardButton.setDisabledIcon(ImageUtil.dye((ImageIcon) netRadioBackwardButton.getIcon(), darkerIconColor));
          netMvBackwardButton.setDisabledIcon(ImageUtil.dye((ImageIcon) netMvBackwardButton.getIcon(), darkerIconColor));
          netRankingBackwardButton.setDisabledIcon(ImageUtil.dye((ImageIcon) netRankingBackwardButton.getIcon(), darkerIconColor));
          netUserBackwardButton.setDisabledIcon(ImageUtil.dye((ImageIcon) netUserBackwardButton.getIcon(), darkerIconColor));
          mvButton.setDisabledIcon(ImageUtil.dye((ImageIcon) mvButton.getIcon(), darkerIconColor));
          collectButton.setDisabledIcon(ImageUtil.dye((ImageIcon) collectButton.getIcon(), darkerIconColor));
          downloadButton.setDisabledIcon(ImageUtil.dye((ImageIcon) downloadButton.getIcon(), darkerIconColor));
          commentButton.setDisabledIcon(ImageUtil.dye((ImageIcon) commentButton.getIcon(), darkerIconColor));
          sheetButton.setDisabledIcon(ImageUtil.dye((ImageIcon) sheetButton.getIcon(), darkerIconColor));
  
          // 右键菜单项禁用时颜色
          // 全局设置菜单项禁用颜色
          UIManager.put("MenuItem.disabledForeground", darkerTextColor);
  
          // 右键菜单项图标
          copyMottoMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          nextMottoMenuItem.setIcon(ImageUtil.dye(nextMottoIcon, iconColor));
  
          openMainFrameMenuItem.setIcon(ImageUtil.dye(openMainFrameIcon, iconColor));
          exitMenuItem.setIcon(ImageUtil.dye(exitIcon, iconColor));
  
          manageStyleMenuItem.setIcon(ImageUtil.dye(changeStyleIcon, iconColor));
          styleCustomMenuItem.setIcon(ImageUtil.dye(addCustomStyleIcon, iconColor));
  
          closeSong.setIcon(ImageUtil.dye(closeSongIcon, iconColor));
          closeSong.setDisabledIcon(ImageUtil.dye(closeSongIcon, darkerIconColor));
          clearCache.setIcon(ImageUtil.dye(clearCacheIcon, iconColor));
          settingMenuItem.setIcon(ImageUtil.dye(settingsIcon, iconColor));
          donateMenuItem.setIcon(ImageUtil.dye(donateIcon, iconColor));
          releaseMenuItem.setIcon(ImageUtil.dye(releaseIcon, iconColor));
          updateMenuItem.setIcon(ImageUtil.dye(updateIcon, iconColor));
          helpMenuItem.setIcon(ImageUtil.dye(helpIcon, iconColor));
          aboutMenuItem.setIcon(ImageUtil.dye(aboutIcon, iconColor));
  
          addFileMenuItem.setIcon(ImageUtil.dye(fileIcon, iconColor));
          addDirMenuItem.setIcon(ImageUtil.dye(folderIcon, iconColor));
  
          playModeDisabledMenuItem.setIcon(ImageUtil.dye(playModeDisabledIcon, iconColor));
          singleMenuItem.setIcon(ImageUtil.dye(singleIcon, iconColor));
          sequenceMenuItem.setIcon(ImageUtil.dye(sequenceIcon, iconColor));
          listCycleMenuItem.setIcon(ImageUtil.dye(listCycleIcon, iconColor));
          shuffleMenuItem.setIcon(ImageUtil.dye(shuffleIcon, iconColor));
  
          if (gsOn) gsMenuItem.setIcon(ImageUtil.dye(tickIcon, iconColor));
          if (darkerOn) darkerMenuItem.setIcon(ImageUtil.dye(tickIcon, iconColor));
          if (maskOn) maskMenuItem.setIcon(ImageUtil.dye(tickIcon, iconColor));
          if (grooveOn) grooveMenuItem.setIcon(ImageUtil.dye(tickIcon, iconColor));
          blurOffMenuItem.setIcon(ImageUtil.dye(blurOffIcon, iconColor));
          cvBlurMenuItem.setIcon(ImageUtil.dye(cvBlurIcon, iconColor));
          mcBlurMenuItem.setIcon(ImageUtil.dye(mcBlurIcon, iconColor));
          lgBlurMenuItem.setIcon(ImageUtil.dye(lgBlurIcon, iconColor));
          fbmBlurMenuItem.setIcon(ImageUtil.dye(fbmBlurIcon, iconColor));
  
          originalLrcMenuItem.setIcon(ImageUtil.dye(originalIcon, iconColor));
          translationMenuItem.setIcon(ImageUtil.dye(translationIcon, iconColor));
          romaMenuItem.setIcon(ImageUtil.dye(romajiIcon, iconColor));
          tradChineseMenuItem.setIcon(ImageUtil.dye(tradChineseIcon, iconColor));
  
          saveDescCoverImgMenuItem.setIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, iconColor));
          saveDescBgImgMenuItem.setIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, iconColor));
          copyDescNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          copyDescTagMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          copyDescMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          playMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          nextPlayMenuItem.setIcon(ImageUtil.dye(nextPlayMenuItemIcon, iconColor));
          playMvMenuItem.setIcon(ImageUtil.dye(playMvMenuItemIcon, iconColor));
          playMvMenuItem.setDisabledIcon(ImageUtil.dye(playMvMenuItemIcon, darkerIconColor));
          collectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          downloadMenuItem.setIcon(ImageUtil.dye(downloadMenuItemIcon, iconColor));
          downloadMenuItem.setDisabledIcon(ImageUtil.dye(downloadMenuItemIcon, darkerIconColor));
          commentMenuItem.setIcon(ImageUtil.dye(commentMenuItemIcon, iconColor));
          commentMenuItem.setDisabledIcon(ImageUtil.dye(commentMenuItemIcon, darkerIconColor));
          sheetMenuItem.setIcon(ImageUtil.dye(sheetMenuItemIcon, iconColor));
          sheetMenuItem.setDisabledIcon(ImageUtil.dye(sheetMenuItemIcon, darkerIconColor));
          searchSongMenuItem.setIcon(ImageUtil.dye(searchSongItemIcon, iconColor));
          similarSongMenuItem.setIcon(ImageUtil.dye(similarMenuItemIcon, iconColor));
          similarSongMenuItem.setDisabledIcon(ImageUtil.dye(similarMenuItemIcon, darkerIconColor));
          relatedPlaylistMenuItem.setIcon(ImageUtil.dye(relatedPlaylistMenuItemIcon, iconColor));
          relatedPlaylistMenuItem.setDisabledIcon(ImageUtil.dye(relatedPlaylistMenuItemIcon, darkerIconColor));
          authorMenuItem.setIcon(ImageUtil.dye(similarArtistMenuItemIcon, iconColor));
          authorMenuItem.setDisabledIcon(ImageUtil.dye(similarArtistMenuItemIcon, darkerIconColor));
          albumMenuItem.setIcon(ImageUtil.dye(browseAlbumMenuItemIcon, iconColor));
          albumMenuItem.setDisabledIcon(ImageUtil.dye(browseAlbumMenuItemIcon, darkerIconColor));
          recRadioMenuItem.setIcon(ImageUtil.dye(radioMenuItemIcon, iconColor));
          recRadioMenuItem.setDisabledIcon(ImageUtil.dye(radioMenuItemIcon, darkerIconColor));
          relatedMvMenuItem.setIcon(ImageUtil.dye(similarMvMenuItemIcon, iconColor));
          relatedMvMenuItem.setDisabledIcon(ImageUtil.dye(similarMvMenuItemIcon, darkerIconColor));
          copyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          locateFileMenuItem.setIcon(ImageUtil.dye(locateFileMenuItemIcon, iconColor));
          locateFileMenuItem.setDisabledIcon(ImageUtil.dye(locateFileMenuItemIcon, darkerIconColor));
          editInfoMenuItem.setIcon(ImageUtil.dye(editInfoMenuItemIcon, iconColor));
          editInfoMenuItem.setDisabledIcon(ImageUtil.dye(editInfoMenuItemIcon, darkerIconColor));
          removeMenuItem.setIcon(ImageUtil.dye(removeMenuItemIcon, iconColor));
  
          netMusicPlayMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          netMusicNextPlayMenuItem.setIcon(ImageUtil.dye(nextPlayMenuItemIcon, iconColor));
          netMusicPlayMvMenuItem.setIcon(ImageUtil.dye(playMvMenuItemIcon, iconColor));
          netMusicPlayMvMenuItem.setDisabledIcon(ImageUtil.dye(playMvMenuItemIcon, darkerIconColor));
          netMusicCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          netMusicDownloadMenuItem.setIcon(ImageUtil.dye(downloadMenuItemIcon, iconColor));
          netMusicCommentMenuItem.setIcon(ImageUtil.dye(commentMenuItemIcon, iconColor));
          netMusicSheetMenuItem.setIcon(ImageUtil.dye(sheetMenuItemIcon, iconColor));
          netMusicSearchSongMenuItem.setIcon(ImageUtil.dye(searchSongItemIcon, iconColor));
          netMusicSimilarSongMenuItem.setIcon(ImageUtil.dye(similarMenuItemIcon, iconColor));
          netMusicRelatedPlaylistMenuItem.setIcon(ImageUtil.dye(relatedPlaylistMenuItemIcon, iconColor));
          netMusicAuthorMenuItem.setIcon(ImageUtil.dye(similarArtistMenuItemIcon, iconColor));
          netMusicAuthorMenuItem.setDisabledIcon(ImageUtil.dye(similarArtistMenuItemIcon, darkerIconColor));
          netMusicAlbumMenuItem.setIcon(ImageUtil.dye(browseAlbumMenuItemIcon, iconColor));
          netMusicAlbumMenuItem.setDisabledIcon(ImageUtil.dye(browseAlbumMenuItemIcon, darkerIconColor));
          netMusicRecRadioMenuItem.setIcon(ImageUtil.dye(radioMenuItemIcon, iconColor));
          netMusicRecRadioMenuItem.setDisabledIcon(ImageUtil.dye(radioMenuItemIcon, darkerIconColor));
          netMusicRelatedMvMenuItem.setIcon(ImageUtil.dye(similarMvMenuItemIcon, iconColor));
          netMusicCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          netPlaylistOpenMenuItem.setIcon(ImageUtil.dye(openMenuItemIcon, iconColor));
          netPlaylistPlayAllMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          netPlaylistCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          netPlaylistCommentMenuItem.setIcon(ImageUtil.dye(commentMenuItemIcon, iconColor));
          netPlaylistSimilarPlaylistMenuItem.setIcon(ImageUtil.dye(relatedPlaylistMenuItemIcon, iconColor));
          netPlaylistCreatorMenuItem.setIcon(ImageUtil.dye(userMenuItemIcon, iconColor));
          netPlaylistSubscriberMenuItem.setIcon(ImageUtil.dye(userFanMenuItemIcon, iconColor));
          netPlaylistCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          netAlbumOpenMenuItem.setIcon(ImageUtil.dye(openMenuItemIcon, iconColor));
          netAlbumPlayAllMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          netAlbumCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          netAlbumCommentMenuItem.setIcon(ImageUtil.dye(commentMenuItemIcon, iconColor));
          netAlbumArtistMenuItem.setIcon(ImageUtil.dye(similarArtistMenuItemIcon, iconColor));
          netAlbumSimilarMenuItem.setIcon(ImageUtil.dye(browseAlbumMenuItemIcon, iconColor));
          netAlbumPhotosMenuItem.setIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, iconColor));
          netAlbumCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          netArtistOpenMenuItem.setIcon(ImageUtil.dye(openMenuItemIcon, iconColor));
          netArtistPlayAllMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          netArtistCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          netArtistBrowseAlbumMenuItem.setIcon(ImageUtil.dye(browseAlbumMenuItemIcon, iconColor));
          netArtistBrowseMvMenuItem.setIcon(ImageUtil.dye(similarMvMenuItemIcon, iconColor));
          netArtistSimilarArtistMenuItem.setIcon(ImageUtil.dye(similarArtistMenuItemIcon, iconColor));
          netArtistFansMenuItem.setIcon(ImageUtil.dye(userFanMenuItemIcon, iconColor));
          netArtistBuddyMenuItem.setIcon(ImageUtil.dye(buddyMenuItemIcon, iconColor));
          netArtistRadiosMenuItem.setIcon(ImageUtil.dye(radioMenuItemIcon, iconColor));
          netArtistPhotosMenuItem.setIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, iconColor));
          netArtistCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          netRadioOpenMenuItem.setIcon(ImageUtil.dye(openMenuItemIcon, iconColor));
          netRadioPlayAllMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          netRadioCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          netRadioCommentMenuItem.setIcon(ImageUtil.dye(commentMenuItemIcon, iconColor));
          netRadioDjMenuItem.setIcon(ImageUtil.dye(userMenuItemIcon, iconColor));
          netRadioSubscriberMenuItem.setIcon(ImageUtil.dye(userFanMenuItemIcon, iconColor));
          netRadioSimilarMenuItem.setIcon(ImageUtil.dye(radioMenuItemIcon, iconColor));
          netRadioArtistsMenuItem.setIcon(ImageUtil.dye(similarArtistMenuItemIcon, iconColor));
          netRadioPhotosMenuItem.setIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, iconColor));
          netRadioPostersMenuItem.setIcon(ImageUtil.dye(posterMenuItemIcon, iconColor));
          netRadioCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          netMvPlayMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          netMvCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          netMvDownloadMenuItem.setIcon(ImageUtil.dye(downloadMenuItemIcon, iconColor));
          netMvSimilarMvMenuItem.setIcon(ImageUtil.dye(similarMvMenuItemIcon, iconColor));
          netMvVideoEpisodeMenuItem.setIcon(ImageUtil.dye(videoEpisodeMenuItemIcon, iconColor));
          netMvCreatorMenuItem.setIcon(ImageUtil.dye(userMenuItemIcon, iconColor));
          netMvCommentMenuItem.setIcon(ImageUtil.dye(commentMenuItemIcon, iconColor));
          netMvCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          netRankingOpenMenuItem.setIcon(ImageUtil.dye(openMenuItemIcon, iconColor));
          netRankingPlayAllMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          netRankingCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          netRankingCommentMenuItem.setIcon(ImageUtil.dye(commentMenuItemIcon, iconColor));
          netRankingCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          netUserOpenMenuItem.setIcon(ImageUtil.dye(openMenuItemIcon, iconColor));
          netUserPlayAllMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          netUserCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          netUserPlaylistMenuItem.setIcon(ImageUtil.dye(relatedPlaylistMenuItemIcon, iconColor));
          netUserAlbumMenuItem.setIcon(ImageUtil.dye(browseAlbumMenuItemIcon, iconColor));
          netUserRadioMenuItem.setIcon(ImageUtil.dye(radioMenuItemIcon, iconColor));
          netUserVideoMenuItem.setIcon(ImageUtil.dye(similarMvMenuItemIcon, iconColor));
          netUserFollowMenuItem.setIcon(ImageUtil.dye(userFollowMenuItemIcon, iconColor));
          netUserFanMenuItem.setIcon(ImageUtil.dye(userFanMenuItemIcon, iconColor));
          netUserCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          netCommentCopyMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          netCommentSaveProfileMenuItem.setIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, iconColor));
          netCommentSaveProfileMenuItem.setDisabledIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, darkerIconColor));
          netCommentUserMenuItem.setIcon(ImageUtil.dye(userMenuItemIcon, iconColor));
          netCommentPlaylistMenuItem.setIcon(ImageUtil.dye(relatedPlaylistMenuItemIcon, iconColor));
          netCommentAlbumMenuItem.setIcon(ImageUtil.dye(browseAlbumMenuItemIcon, iconColor));
  
          netSheetBrowseMenuItem.setIcon(ImageUtil.dye(browseSheetMenuItemIcon, iconColor));
          netSheetCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          locateLrcMenuItem.setIcon(ImageUtil.dye(locateLrcMenuItemIcon, iconColor));
          locateLrcMenuItem.setDisabledIcon(ImageUtil.dye(locateLrcMenuItemIcon, darkerIconColor));
          copyMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          browseLrcMenuItem.setIcon(ImageUtil.dye(browseLrcMenuItemIcon, iconColor));
          browseLrcMenuItem.setDisabledIcon(ImageUtil.dye(browseLrcMenuItemIcon, darkerIconColor));
          downloadLrcMenuItem.setIcon(ImageUtil.dye(downloadIcon, iconColor));
          downloadLrcMenuItem.setDisabledIcon(ImageUtil.dye(downloadIcon, darkerIconColor));
          spectrumOpacityMenuItem.setIcon(ImageUtil.dye(spectrumOpacityMenuItemIcon, darkerIconColor));
          for (CustomMenuItem mi : calcSpectrumOpacityMenuItems) {
              mi.setIcon(ImageUtil.dye(spectrumOpacityMenuItemIcon, iconColor));
          }
          currLrcOffsetMenuItem.setIcon(ImageUtil.dye(lrcOffsetMenuItemIcon, darkerIconColor));
          for (CustomMenuItem mi : calcLrcOffsetMenuItems) {
              mi.setIcon(ImageUtil.dye(lrcOffsetMenuItemIcon, iconColor));
          }
  
          saveAlbumImageMenuItem.setIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, iconColor));
          saveAlbumImageMenuItem.setDisabledIcon(ImageUtil.dye(saveAlbumImgMenuItemIcon, darkerIconColor));
          copySongNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          copyArtistMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          copyAlbumMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
  
          downloadPlayMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          downloadNextPlayMenuItem.setIcon(ImageUtil.dye(nextPlayMenuItemIcon, iconColor));
          downloadNextPlayMenuItem.setDisabledIcon(ImageUtil.dye(nextPlayMenuItemIcon, darkerIconColor));
          downloadLocateFileMenuItem.setIcon(ImageUtil.dye(locateFileMenuItemIcon, iconColor));
          downloadEditInfoMenuItem.setIcon(ImageUtil.dye(editInfoMenuItemIcon, iconColor));
          downloadEditInfoMenuItem.setDisabledIcon(ImageUtil.dye(editInfoMenuItemIcon, darkerIconColor));
          cancelTaskMenuItem.setIcon(ImageUtil.dye(cancelTaskMenuItemIcon, iconColor));
          cancelTaskMenuItem.setDisabledIcon(ImageUtil.dye(cancelTaskMenuItemIcon, darkerIconColor));
          restartTaskMenuItem.setIcon(ImageUtil.dye(restartTaskMenuItemIcon, iconColor));
          restartTaskMenuItem.setDisabledIcon(ImageUtil.dye(restartTaskMenuItemIcon, darkerIconColor));
          removeTaskMenuItem.setIcon(ImageUtil.dye(removeMenuItemIcon, iconColor));
  
          playQueuePlayMenuItem.setIcon(ImageUtil.dye(playMenuItemIcon, iconColor));
          playQueueNextPlayMenuItem.setIcon(ImageUtil.dye(nextPlayMenuItemIcon, iconColor));
          playQueuePlayMvMenuItem.setIcon(ImageUtil.dye(playMvMenuItemIcon, iconColor));
          playQueuePlayMvMenuItem.setDisabledIcon(ImageUtil.dye(playMvMenuItemIcon, darkerIconColor));
          playQueueCollectMenuItem.setIcon(ImageUtil.dye(collectMenuItemIcon, iconColor));
          playQueueDownloadMenuItem.setIcon(ImageUtil.dye(downloadMenuItemIcon, iconColor));
          playQueueDownloadMenuItem.setDisabledIcon(ImageUtil.dye(downloadMenuItemIcon, darkerIconColor));
          playQueueCommentMenuItem.setIcon(ImageUtil.dye(commentMenuItemIcon, iconColor));
          playQueueCommentMenuItem.setDisabledIcon(ImageUtil.dye(commentMenuItemIcon, darkerIconColor));
          playQueueSheetMenuItem.setIcon(ImageUtil.dye(sheetMenuItemIcon, iconColor));
          playQueueSheetMenuItem.setDisabledIcon(ImageUtil.dye(sheetMenuItemIcon, darkerIconColor));
          playQueueSearchSongMenuItem.setIcon(ImageUtil.dye(searchSongItemIcon, iconColor));
          playQueueSimilarSongMenuItem.setIcon(ImageUtil.dye(similarMenuItemIcon, iconColor));
          playQueueSimilarSongMenuItem.setDisabledIcon(ImageUtil.dye(similarMenuItemIcon, darkerIconColor));
          playQueueRelatedPlaylistMenuItem.setIcon(ImageUtil.dye(relatedPlaylistMenuItemIcon, iconColor));
          playQueueRelatedPlaylistMenuItem.setDisabledIcon(ImageUtil.dye(relatedPlaylistMenuItemIcon, darkerIconColor));
          playQueueAuthorMenuItem.setIcon(ImageUtil.dye(similarArtistMenuItemIcon, iconColor));
          playQueueAuthorMenuItem.setDisabledIcon(ImageUtil.dye(similarArtistMenuItemIcon, darkerIconColor));
          playQueueAlbumMenuItem.setIcon(ImageUtil.dye(browseAlbumMenuItemIcon, iconColor));
          playQueueAlbumMenuItem.setDisabledIcon(ImageUtil.dye(browseAlbumMenuItemIcon, darkerIconColor));
          playQueueRecRadioMenuItem.setIcon(ImageUtil.dye(radioMenuItemIcon, iconColor));
          playQueueRecRadioMenuItem.setDisabledIcon(ImageUtil.dye(radioMenuItemIcon, darkerIconColor));
          playQueueRelatedMvMenuItem.setIcon(ImageUtil.dye(similarMvMenuItemIcon, iconColor));
          playQueueRelatedMvMenuItem.setDisabledIcon(ImageUtil.dye(similarMvMenuItemIcon, darkerIconColor));
          playQueueCopyNameMenuItem.setIcon(ImageUtil.dye(copyNameMenuItemIcon, iconColor));
          playQueueLocateFileMenuItem.setIcon(ImageUtil.dye(locateFileMenuItemIcon, iconColor));
          playQueueLocateFileMenuItem.setDisabledIcon(ImageUtil.dye(locateFileMenuItemIcon, darkerIconColor));
          playQueueEditInfoMenuItem.setIcon(ImageUtil.dye(editInfoMenuItemIcon, iconColor));
          playQueueEditInfoMenuItem.setDisabledIcon(ImageUtil.dye(editInfoMenuItemIcon, darkerIconColor));
          playQueueRemoveMenuItem.setIcon(ImageUtil.dye(removeMenuItemIcon, iconColor));
  
          // 菜单项文字颜色
          updateMenuItemStyle(mottoPopupMenu);
          updateMenuItemStyle(trayPopupMenu);
          updateMenuItemStyle(stylePopupMenu);
          updateMenuItemStyle(addPopupMenu);
          updateMenuItemStyle(sortPopupMenu);
          updateMenuItemStyle(descriptionPanelPopupMenu);
          updateMenuItemStyle(leftInfoPopupMenu);
          updateMenuItemStyle(lrcPopupMenu);
          updateMenuItemStyle(spectrumPopupMenu);
          updateMenuItemStyle(playModePopupMenu);
          updateMenuItemStyle(blurPopupMenu);
          updateMenuItemStyle(lyricTypePopupMenu);
          updateMenuItemStyle(mainMenu);
          updateMenuItemStyle(musicPopupMenu);
          updateMenuItemStyle(netMusicPopupMenu);
          updateMenuItemStyle(netPlaylistPopupMenu);
          updateMenuItemStyle(netAlbumPopupMenu);
          updateMenuItemStyle(netArtistPopupMenu);
          updateMenuItemStyle(netRadioPopupMenu);
          updateMenuItemStyle(netRankingPopupMenu);
          updateMenuItemStyle(netUserPopupMenu);
          updateMenuItemStyle(netCommentPopupMenu);
          updateMenuItemStyle(netSheetPopupMenu);
          updateMenuItemStyle(netMvPopupMenu);
          updateMenuItemStyle(downloadPopupMenu);
          updateMenuItemStyle(playQueuePopupMenu);
  
          // 工具栏消除边框和透明、选项卡面板透明
          tabbedPane.setUI(new TabbedPaneUI());
          collectionTabbedPane.setUI(new TabbedPaneUI());
  
          // 关键词面板
          netMusicSearchSuggestionLabel.setForeground(style.getTextColor());
          netMusicRefreshSearchSuggestionButton.setIcon(ImageUtil.dye((ImageIcon) netMusicRefreshSearchSuggestionButton.getIcon(), iconColor));
  
          netMusicHotSearchLabel.setForeground(style.getTextColor());
          netMusicRefreshHotSearchButton.setIcon(ImageUtil.dye((ImageIcon) netMusicRefreshHotSearchButton.getIcon(), iconColor));
  
          netMusicHistorySearchLabel.setForeground(style.getTextColor());
          netMusicClearHistorySearchButton.setIcon(ImageUtil.dye((ImageIcon) netMusicClearHistorySearchButton.getIcon(), iconColor));
  
          netPlaylistHistorySearchLabel.setForeground(style.getTextColor());
          netPlaylistClearHistorySearchButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistClearHistorySearchButton.getIcon(), iconColor));
  
          netAlbumHistorySearchLabel.setForeground(style.getTextColor());
          netAlbumClearHistorySearchButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumClearHistorySearchButton.getIcon(), iconColor));
  
          netArtistHistorySearchLabel.setForeground(style.getTextColor());
          netArtistClearHistorySearchButton.setIcon(ImageUtil.dye((ImageIcon) netArtistClearHistorySearchButton.getIcon(), iconColor));
  
          netRadioHistorySearchLabel.setForeground(style.getTextColor());
          netRadioClearHistorySearchButton.setIcon(ImageUtil.dye((ImageIcon) netRadioClearHistorySearchButton.getIcon(), iconColor));
  
          netMvHistorySearchLabel.setForeground(style.getTextColor());
          netMvClearHistorySearchButton.setIcon(ImageUtil.dye((ImageIcon) netMvClearHistorySearchButton.getIcon(), iconColor));
  
          netUserHistorySearchLabel.setForeground(style.getTextColor());
          netUserClearHistorySearchButton.setIcon(ImageUtil.dye((ImageIcon) netUserClearHistorySearchButton.getIcon(), iconColor));
  
          Component[] components = netMusicSearchSuggestionInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          components = netMusicHotSearchInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          components = netMusicHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          components = netPlaylistHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          components = netAlbumHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          components = netArtistHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          components = netRadioHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          components = netMvHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          components = netUserHistorySearchInnerPanel2.getComponents();
          for (Component c : components) {
              if (c instanceof DialogButton) {
                  DialogButton b = (DialogButton) c;
                  b.setForeColor(textColor);
              }
          }
          // 筛选框透明
          FocusListener[] focusListeners = filterTextField.getFocusListeners();
          for (FocusListener focusListener : focusListeners) {
              if (focusListener instanceof TextFieldHintListener) {
                  ((TextFieldHintListener) focusListener).setInputColor(textColor);
                  ((TextFieldHintListener) focusListener).setPlaceholderColor(darkerTextColor);
              }
          }
          filterTextField.setForeground(filterTextField.isOccupied() ? textColor : darkerTextColor);
          filterTextField.setCaretColor(textColor);
          filterTextField.setSelectedTextColor(textColor);
          filterTextField.setSelectionColor(darkerTextAlphaColor);
          // 在线音乐搜索栏透明
          focusListeners = searchTextField.getFocusListeners();
          for (FocusListener focusListener : focusListeners) {
              if (focusListener instanceof TextFieldHintListener) {
                  ((TextFieldHintListener) focusListener).setInputColor(textColor);
                  ((TextFieldHintListener) focusListener).setPlaceholderColor(darkerTextColor);
              }
          }
          searchTextField.setForeground(searchTextField.isOccupied() ? textColor : darkerTextColor);
          searchTextField.setCaretColor(textColor);
          searchTextField.setSelectedTextColor(textColor);
          searchTextField.setSelectionColor(darkerTextAlphaColor);
          netMusicSourceComboBox.setUI(new ComboBoxUI(netMusicSourceComboBox, THIS));
          netMusicSearchTypeComboBox.setUI(new ComboBoxUI(netMusicSearchTypeComboBox, THIS));
          netMusicSearchSubTypeComboBox.setUI(new ComboBoxUI(netMusicSearchSubTypeComboBox, THIS));
          netMusicPageTextField.setForeground(textColor);
          netMusicPageTextField.setCaretColor(textColor);
          netMusicPageTextField.setSelectedTextColor(textColor);
          netMusicPageTextField.setSelectionColor(darkerTextAlphaColor);
          // 歌单搜索栏透明
          netPlaylistIdCheckBox.setForeground(textColor);
          netPlaylistIdCheckBox.setIcon(ImageUtil.dye(uncheckedIcon, iconColor));
          netPlaylistIdCheckBox.setSelectedIcon(ImageUtil.dye(checkedIcon, iconColor));
          focusListeners = netPlaylistSearchTextField.getFocusListeners();
          for (FocusListener focusListener : focusListeners) {
              if (focusListener instanceof TextFieldHintListener) {
                  ((TextFieldHintListener) focusListener).setInputColor(textColor);
                  ((TextFieldHintListener) focusListener).setPlaceholderColor(darkerTextColor);
              }
          }
          netPlaylistSearchTextField.setForeground(netPlaylistSearchTextField.isOccupied() ? textColor : darkerTextColor);
          netPlaylistSearchTextField.setCaretColor(textColor);
          netPlaylistSearchTextField.setSelectedTextColor(textColor);
          netPlaylistSearchTextField.setSelectionColor(darkerTextAlphaColor);
          netPlaylistSourceComboBox.setUI(new ComboBoxUI(netPlaylistSourceComboBox, THIS));
          netPlaylistPageTextField.setForeground(textColor);
          netPlaylistPageTextField.setCaretColor(textColor);
          netPlaylistPageTextField.setSelectedTextColor(textColor);
          netPlaylistPageTextField.setSelectionColor(darkerTextAlphaColor);
          netPlaylistPlayAllButton.setForeground(textColor);
          // 专辑搜索栏透明
          focusListeners = netAlbumSearchTextField.getFocusListeners();
          for (FocusListener focusListener : focusListeners) {
              if (focusListener instanceof TextFieldHintListener) {
                  ((TextFieldHintListener) focusListener).setInputColor(textColor);
                  ((TextFieldHintListener) focusListener).setPlaceholderColor(darkerTextColor);
              }
          }
          netAlbumSearchTextField.setForeground(netAlbumSearchTextField.isOccupied() ? textColor : darkerTextColor);
          netAlbumSearchTextField.setCaretColor(textColor);
          netAlbumSearchTextField.setSelectedTextColor(textColor);
          netAlbumSearchTextField.setSelectionColor(darkerTextAlphaColor);
          netAlbumSourceComboBox.setUI(new ComboBoxUI(netAlbumSourceComboBox, THIS));
          netAlbumPageTextField.setForeground(textColor);
          netAlbumPageTextField.setCaretColor(textColor);
          netAlbumPageTextField.setSelectedTextColor(textColor);
          netAlbumPageTextField.setSelectionColor(darkerTextAlphaColor);
          netAlbumPlayAllButton.setForeground(textColor);
          // 歌手搜索栏透明
          focusListeners = netArtistSearchTextField.getFocusListeners();
          for (FocusListener focusListener : focusListeners) {
              if (focusListener instanceof TextFieldHintListener) {
                  ((TextFieldHintListener) focusListener).setInputColor(textColor);
                  ((TextFieldHintListener) focusListener).setPlaceholderColor(darkerTextColor);
              }
          }
          netArtistSearchTextField.setForeground(netArtistSearchTextField.isOccupied() ? textColor : darkerTextColor);
          netArtistSearchTextField.setCaretColor(textColor);
          netArtistSearchTextField.setSelectedTextColor(textColor);
          netArtistSearchTextField.setSelectionColor(darkerTextAlphaColor);
          netArtistSourceComboBox.setUI(new ComboBoxUI(netArtistSourceComboBox, THIS));
          netArtistPageTextField.setForeground(textColor);
          netArtistPageTextField.setCaretColor(textColor);
          netArtistPageTextField.setSelectedTextColor(textColor);
          netArtistPageTextField.setSelectionColor(darkerTextAlphaColor);
          netArtistPlayAllButton.setForeground(textColor);
          // 电台搜索栏透明
          focusListeners = netRadioSearchTextField.getFocusListeners();
          for (FocusListener focusListener : focusListeners) {
              if (focusListener instanceof TextFieldHintListener) {
                  ((TextFieldHintListener) focusListener).setInputColor(textColor);
                  ((TextFieldHintListener) focusListener).setPlaceholderColor(darkerTextColor);
              }
          }
          netRadioSearchTextField.setForeground(netRadioSearchTextField.isOccupied() ? textColor : darkerTextColor);
          netRadioSearchTextField.setCaretColor(textColor);
          netRadioSearchTextField.setSelectedTextColor(textColor);
          netRadioSearchTextField.setSelectionColor(darkerTextAlphaColor);
          netRadioSourceComboBox.setUI(new ComboBoxUI(netRadioSourceComboBox, THIS));
          netRadioPageTextField.setForeground(textColor);
          netRadioPageTextField.setCaretColor(textColor);
          netRadioPageTextField.setSelectedTextColor(textColor);
          netRadioPageTextField.setSelectionColor(darkerTextAlphaColor);
          netRadioPlayAllButton.setForeground(textColor);
          // MV 搜索栏透明
          focusListeners = netMvSearchTextField.getFocusListeners();
          for (FocusListener focusListener : focusListeners) {
              if (focusListener instanceof TextFieldHintListener) {
                  ((TextFieldHintListener) focusListener).setInputColor(textColor);
                  ((TextFieldHintListener) focusListener).setPlaceholderColor(darkerTextColor);
              }
          }
          netMvSearchTextField.setForeground(netMvSearchTextField.isOccupied() ? textColor : darkerTextColor);
          netMvSearchTextField.setCaretColor(textColor);
          netMvSearchTextField.setSelectedTextColor(textColor);
          netMvSearchTextField.setSelectionColor(darkerTextAlphaColor);
          netMvSourceComboBox.setUI(new ComboBoxUI(netMvSourceComboBox, THIS));
          netMvPageTextField.setForeground(textColor);
          netMvPageTextField.setCaretColor(textColor);
          netMvPageTextField.setSelectedTextColor(textColor);
          netMvPageTextField.setSelectionColor(darkerTextAlphaColor);
          // 榜单搜索栏透明
          netRankingPageTextField.setForeground(textColor);
          netRankingPageTextField.setCaretColor(textColor);
          netRankingPageTextField.setSelectedTextColor(textColor);
          netRankingPageTextField.setSelectionColor(darkerTextAlphaColor);
          netRankingPlayAllButton.setForeground(textColor);
          // 用户搜索栏透明
          focusListeners = netUserSearchTextField.getFocusListeners();
          for (FocusListener focusListener : focusListeners) {
              if (focusListener instanceof TextFieldHintListener) {
                  ((TextFieldHintListener) focusListener).setInputColor(textColor);
                  ((TextFieldHintListener) focusListener).setPlaceholderColor(darkerTextColor);
              }
          }
          netUserSearchTextField.setForeground(netUserSearchTextField.isOccupied() ? textColor : darkerTextColor);
          netUserSearchTextField.setCaretColor(textColor);
          netUserSearchTextField.setSelectedTextColor(textColor);
          netUserSearchTextField.setSelectionColor(darkerTextAlphaColor);
          netUserSourceComboBox.setUI(new ComboBoxUI(netUserSourceComboBox, THIS));
          netUserRecordTypeComboBox.setUI(new ComboBoxUI(netUserRecordTypeComboBox, THIS));
          netUserPageTextField.setForeground(textColor);
          netUserPageTextField.setCaretColor(textColor);
          netUserPageTextField.setSelectedTextColor(textColor);
          netUserPageTextField.setSelectionColor(darkerTextAlphaColor);
          netUserPlayAllButton.setForeground(textColor);
          // 榜单栏
          netRankingSourceComboBox.setUI(new ComboBoxUI(netRankingSourceComboBox, THIS));
          // 评论栏透明
          netCommentTypeComboBox.setUI(new ComboBoxUI(netCommentTypeComboBox, THIS));
          netCommentPageTextField.setForeground(textColor);
          netCommentPageTextField.setCaretColor(textColor);
          netCommentPageTextField.setSelectedTextColor(textColor);
          netCommentPageTextField.setSelectionColor(darkerTextAlphaColor);
          // 乐谱栏透明
          netSheetPageTextField.setForeground(textColor);
          netSheetPageTextField.setCaretColor(textColor);
          netSheetPageTextField.setSelectedTextColor(textColor);
          netSheetPageTextField.setSelectionColor(darkerTextAlphaColor);
          // 推荐页码文本框
          netRecommendSourceComboBox.setUI(new ComboBoxUI(netRecommendSourceComboBox, THIS));
          netRecommendSortTypeComboBox.setUI(new ComboBoxUI(netRecommendSortTypeComboBox, THIS));
          netRecommendPageTextField.setForeground(textColor);
          netRecommendPageTextField.setCaretColor(textColor);
          netRecommendPageTextField.setSelectedTextColor(textColor);
          netRecommendPageTextField.setSelectionColor(darkerTextAlphaColor);
          netRecommendTagComboBox.setUI(new ComboBoxUI(netRecommendTagComboBox, THIS, 240));
          netRecommendPlayAllButton.setForeground(textColor);
          // 收藏页码文本框
          collectionRecordTypeComboBox.setUI(new ComboBoxUI(collectionRecordTypeComboBox, THIS));
          collectionPageTextField.setForeground(textColor);
          collectionPageTextField.setCaretColor(textColor);
          collectionPageTextField.setSelectedTextColor(textColor);
          collectionPageTextField.setSelectionColor(darkerTextAlphaColor);
          collectionPlayAllButton.setForeground(textColor);
          // 推荐工具栏按钮透明
          playlistRecommendButton.setForeground(textColor);
          highQualityPlaylistButton.setForeground(textColor);
          hotMusicButton.setForeground(textColor);
          newMusicButton.setForeground(textColor);
          newAlbumRecommendButton.setForeground(textColor);
          artistListRecommendButton.setForeground(textColor);
          newRadioRecommendButton.setForeground(textColor);
          hotRadioRecommendButton.setForeground(textColor);
          programRecommendButton.setForeground(textColor);
          mvRecommendButton.setForeground(textColor);
          // 个人音乐工具栏按钮透明
          localMusicButton.setForeground(textColor);
          historyButton.setForeground(textColor);
          collectionButton.setForeground(textColor);
          // 工具栏按钮颜色
          localMusicButton.setIcon(ImageUtil.dye((ImageIcon) localMusicButton.getIcon(), iconColor));
          historyButton.setIcon(ImageUtil.dye((ImageIcon) historyButton.getIcon(), iconColor));
          collectionButton.setIcon(ImageUtil.dye((ImageIcon) collectionButton.getIcon(), iconColor));
          addToolButton.setIcon(ImageUtil.dye((ImageIcon) addToolButton.getIcon(), iconColor));
          reimportToolButton.setIcon(ImageUtil.dye((ImageIcon) reimportToolButton.getIcon(), iconColor));
          manageCatalogToolButton.setIcon(ImageUtil.dye((ImageIcon) manageCatalogToolButton.getIcon(), iconColor));
          removeToolButton.setIcon(ImageUtil.dye((ImageIcon) removeToolButton.getIcon(), iconColor));
          clearToolButton.setIcon(ImageUtil.dye((ImageIcon) clearToolButton.getIcon(), iconColor));
          duplicateToolButton.setIcon(ImageUtil.dye((ImageIcon) duplicateToolButton.getIcon(), iconColor));
          reverseToolButton.setIcon(ImageUtil.dye((ImageIcon) reverseToolButton.getIcon(), iconColor));
          sortToolButton.setIcon(ImageUtil.dye((ImageIcon) sortToolButton.getIcon(), iconColor));
          moveUpToolButton.setIcon(ImageUtil.dye((ImageIcon) moveUpToolButton.getIcon(), iconColor));
          moveDownToolButton.setIcon(ImageUtil.dye((ImageIcon) moveDownToolButton.getIcon(), iconColor));
          clearInputToolButton.setIcon(ImageUtil.dye((ImageIcon) clearInputToolButton.getIcon(), iconColor));
          // 在线音乐搜索栏按钮颜色
          netMusicBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netMusicBackwardButton.getIcon(), iconColor));
          netMusicClearInputButton.setIcon(ImageUtil.dye((ImageIcon) netMusicClearInputButton.getIcon(), iconColor));
          searchButton.setIcon(ImageUtil.dye((ImageIcon) searchButton.getIcon(), iconColor));
          netMusicRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netMusicRefreshButton.getIcon(), iconColor));
          netMusicStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netMusicStartPageButton.getIcon(), iconColor));
          netMusicLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netMusicLastPageButton.getIcon(), iconColor));
          netMusicGoButton.setIcon(ImageUtil.dye((ImageIcon) netMusicGoButton.getIcon(), iconColor));
          netMusicNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netMusicNextPageButton.getIcon(), iconColor));
          netMusicEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netMusicEndPageButton.getIcon(), iconColor));
          // 歌单搜索栏按钮颜色
          netPlaylistBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistBackwardButton.getIcon(), iconColor));
          netPlaylistClearInputButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistClearInputButton.getIcon(), iconColor));
          netPlaylistSearchButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistSearchButton.getIcon(), iconColor));
          netPlaylistPlayAllButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistPlayAllButton.getIcon(), iconColor));
          netPlaylistRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistRefreshButton.getIcon(), iconColor));
          netPlaylistStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistStartPageButton.getIcon(), iconColor));
          netPlaylistLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistLastPageButton.getIcon(), iconColor));
          netPlaylistGoButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistGoButton.getIcon(), iconColor));
          netPlaylistNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistNextPageButton.getIcon(), iconColor));
          netPlaylistEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netPlaylistEndPageButton.getIcon(), iconColor));
          // 专辑搜索栏按钮颜色
          netAlbumBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumBackwardButton.getIcon(), iconColor));
          netAlbumClearInputButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumClearInputButton.getIcon(), iconColor));
          netAlbumSearchButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumSearchButton.getIcon(), iconColor));
          netAlbumPlayAllButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumPlayAllButton.getIcon(), iconColor));
          netAlbumRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumRefreshButton.getIcon(), iconColor));
          netAlbumStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumStartPageButton.getIcon(), iconColor));
          netAlbumLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumLastPageButton.getIcon(), iconColor));
          netAlbumGoButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumGoButton.getIcon(), iconColor));
          netAlbumNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumNextPageButton.getIcon(), iconColor));
          netAlbumEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netAlbumEndPageButton.getIcon(), iconColor));
          // 歌手搜索栏按钮颜色
          netArtistBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netArtistBackwardButton.getIcon(), iconColor));
          netArtistClearInputButton.setIcon(ImageUtil.dye((ImageIcon) netArtistClearInputButton.getIcon(), iconColor));
          netArtistSearchButton.setIcon(ImageUtil.dye((ImageIcon) netArtistSearchButton.getIcon(), iconColor));
          netArtistPlayAllButton.setIcon(ImageUtil.dye((ImageIcon) netArtistPlayAllButton.getIcon(), iconColor));
          netArtistRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netArtistRefreshButton.getIcon(), iconColor));
          netArtistStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netArtistStartPageButton.getIcon(), iconColor));
          netArtistLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netArtistLastPageButton.getIcon(), iconColor));
          netArtistGoButton.setIcon(ImageUtil.dye((ImageIcon) netArtistGoButton.getIcon(), iconColor));
          netArtistNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netArtistNextPageButton.getIcon(), iconColor));
          netArtistEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netArtistEndPageButton.getIcon(), iconColor));
          // 电台搜索栏按钮颜色
          netRadioSortTypeComboBox.setUI(new ComboBoxUI(netRadioSortTypeComboBox, THIS));
          netRadioBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netRadioBackwardButton.getIcon(), iconColor));
          netRadioClearInputButton.setIcon(ImageUtil.dye((ImageIcon) netRadioClearInputButton.getIcon(), iconColor));
          netRadioSearchButton.setIcon(ImageUtil.dye((ImageIcon) netRadioSearchButton.getIcon(), iconColor));
          netRadioPlayAllButton.setIcon(ImageUtil.dye((ImageIcon) netRadioPlayAllButton.getIcon(), iconColor));
          netRadioRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netRadioRefreshButton.getIcon(), iconColor));
          netRadioStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netRadioStartPageButton.getIcon(), iconColor));
          netRadioLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netRadioLastPageButton.getIcon(), iconColor));
          netRadioGoButton.setIcon(ImageUtil.dye((ImageIcon) netRadioGoButton.getIcon(), iconColor));
          netRadioNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netRadioNextPageButton.getIcon(), iconColor));
          netRadioEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netRadioEndPageButton.getIcon(), iconColor));
          // MV 搜索栏按钮颜色
          netMvSortTypeComboBox.setUI(new ComboBoxUI(netMvSortTypeComboBox, THIS));
          netMvBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netMvBackwardButton.getIcon(), iconColor));
          netMvClearInputButton.setIcon(ImageUtil.dye((ImageIcon) netMvClearInputButton.getIcon(), iconColor));
          netMvSearchButton.setIcon(ImageUtil.dye((ImageIcon) netMvSearchButton.getIcon(), iconColor));
          netMvRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netMvRefreshButton.getIcon(), iconColor));
          netMvStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netMvStartPageButton.getIcon(), iconColor));
          netMvLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netMvLastPageButton.getIcon(), iconColor));
          netMvGoButton.setIcon(ImageUtil.dye((ImageIcon) netMvGoButton.getIcon(), iconColor));
          netMvNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netMvNextPageButton.getIcon(), iconColor));
          netMvEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netMvEndPageButton.getIcon(), iconColor));
          // 榜单搜索栏按钮颜色
          netRankingBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netRankingBackwardButton.getIcon(), iconColor));
          netRankingPlayAllButton.setIcon(ImageUtil.dye((ImageIcon) netRankingPlayAllButton.getIcon(), iconColor));
          netRankingRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netRankingRefreshButton.getIcon(), iconColor));
          netRankingStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netRankingStartPageButton.getIcon(), iconColor));
          netRankingLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netRankingLastPageButton.getIcon(), iconColor));
          netRankingGoButton.setIcon(ImageUtil.dye((ImageIcon) netRankingGoButton.getIcon(), iconColor));
          netRankingNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netRankingNextPageButton.getIcon(), iconColor));
          netRankingEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netRankingEndPageButton.getIcon(), iconColor));
          // 用户搜索栏按钮颜色
          netUserBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netUserBackwardButton.getIcon(), iconColor));
          netUserClearInputButton.setIcon(ImageUtil.dye((ImageIcon) netUserClearInputButton.getIcon(), iconColor));
          netUserSearchButton.setIcon(ImageUtil.dye((ImageIcon) netUserSearchButton.getIcon(), iconColor));
          netUserPlayAllButton.setIcon(ImageUtil.dye((ImageIcon) netUserPlayAllButton.getIcon(), iconColor));
          netUserRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netUserRefreshButton.getIcon(), iconColor));
          netUserStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netUserStartPageButton.getIcon(), iconColor));
          netUserLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netUserLastPageButton.getIcon(), iconColor));
          netUserGoButton.setIcon(ImageUtil.dye((ImageIcon) netUserGoButton.getIcon(), iconColor));
          netUserNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netUserNextPageButton.getIcon(), iconColor));
          netUserEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netUserEndPageButton.getIcon(), iconColor));
          // 评论栏按钮颜色
          netCommentBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netCommentBackwardButton.getIcon(), iconColor));
          netCommentRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netCommentRefreshButton.getIcon(), iconColor));
          netCommentStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netCommentStartPageButton.getIcon(), iconColor));
          netCommentLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netCommentLastPageButton.getIcon(), iconColor));
          netCommentGoButton.setIcon(ImageUtil.dye((ImageIcon) netCommentGoButton.getIcon(), iconColor));
          netCommentNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netCommentNextPageButton.getIcon(), iconColor));
          netCommentEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netCommentEndPageButton.getIcon(), iconColor));
          // 乐谱栏按钮颜色
          netSheetBackwardButton.setIcon(ImageUtil.dye((ImageIcon) netSheetBackwardButton.getIcon(), iconColor));
          netSheetRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netSheetRefreshButton.getIcon(), iconColor));
          netSheetStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netSheetStartPageButton.getIcon(), iconColor));
          netSheetLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netSheetLastPageButton.getIcon(), iconColor));
          netSheetGoButton.setIcon(ImageUtil.dye((ImageIcon) netSheetGoButton.getIcon(), iconColor));
          netSheetNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netSheetNextPageButton.getIcon(), iconColor));
          netSheetEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netSheetEndPageButton.getIcon(), iconColor));
          // 推荐工具栏按钮颜色
          playlistRecommendButton.setIcon(ImageUtil.dye((ImageIcon) playlistRecommendButton.getIcon(), iconColor));
          highQualityPlaylistButton.setIcon(ImageUtil.dye((ImageIcon) highQualityPlaylistButton.getIcon(), iconColor));
          hotMusicButton.setIcon(ImageUtil.dye((ImageIcon) hotMusicButton.getIcon(), iconColor));
          newMusicButton.setIcon(ImageUtil.dye((ImageIcon) newMusicButton.getIcon(), iconColor));
          newAlbumRecommendButton.setIcon(ImageUtil.dye((ImageIcon) newAlbumRecommendButton.getIcon(), iconColor));
          artistListRecommendButton.setIcon(ImageUtil.dye((ImageIcon) artistListRecommendButton.getIcon(), iconColor));
          newRadioRecommendButton.setIcon(ImageUtil.dye((ImageIcon) newRadioRecommendButton.getIcon(), iconColor));
          hotRadioRecommendButton.setIcon(ImageUtil.dye((ImageIcon) hotRadioRecommendButton.getIcon(), iconColor));
          programRecommendButton.setIcon(ImageUtil.dye((ImageIcon) programRecommendButton.getIcon(), iconColor));
          mvRecommendButton.setIcon(ImageUtil.dye((ImageIcon) mvRecommendButton.getIcon(), iconColor));
          recommendBackwardButton.setIcon(ImageUtil.dye((ImageIcon) recommendBackwardButton.getIcon(), iconColor));
          netRecommendPlayAllButton.setIcon(ImageUtil.dye((ImageIcon) netRecommendPlayAllButton.getIcon(), iconColor));
          netRecommendRefreshButton.setIcon(ImageUtil.dye((ImageIcon) netRecommendRefreshButton.getIcon(), iconColor));
          netRecommendStartPageButton.setIcon(ImageUtil.dye((ImageIcon) netRecommendStartPageButton.getIcon(), iconColor));
          netRecommendLastPageButton.setIcon(ImageUtil.dye((ImageIcon) netRecommendLastPageButton.getIcon(), iconColor));
          netRecommendGoButton.setIcon(ImageUtil.dye((ImageIcon) netRecommendGoButton.getIcon(), iconColor));
          netRecommendNextPageButton.setIcon(ImageUtil.dye((ImageIcon) netRecommendNextPageButton.getIcon(), iconColor));
          netRecommendEndPageButton.setIcon(ImageUtil.dye((ImageIcon) netRecommendEndPageButton.getIcon(), iconColor));
          // 收藏工具栏按钮颜色
          collectionBackwardButton.setIcon(ImageUtil.dye((ImageIcon) collectionBackwardButton.getIcon(), iconColor));
          collectionPlayAllButton.setIcon(ImageUtil.dye((ImageIcon) collectionPlayAllButton.getIcon(), iconColor));
          collectionRefreshButton.setIcon(ImageUtil.dye((ImageIcon) collectionRefreshButton.getIcon(), iconColor));
          collectionStartPageButton.setIcon(ImageUtil.dye((ImageIcon) collectionStartPageButton.getIcon(), iconColor));
          collectionLastPageButton.setIcon(ImageUtil.dye((ImageIcon) collectionLastPageButton.getIcon(), iconColor));
          collectionGoButton.setIcon(ImageUtil.dye((ImageIcon) collectionGoButton.getIcon(), iconColor));
          collectionNextPageButton.setIcon(ImageUtil.dye((ImageIcon) collectionNextPageButton.getIcon(), iconColor));
          collectionEndPageButton.setIcon(ImageUtil.dye((ImageIcon) collectionEndPageButton.getIcon(), iconColor));
          // 下载工具栏按钮颜色
          restartSelectedTasksButton.setIcon(ImageUtil.dye((ImageIcon) restartSelectedTasksButton.getIcon(), iconColor));
          cancelSelectedTasksButton.setIcon(ImageUtil.dye((ImageIcon) cancelSelectedTasksButton.getIcon(), iconColor));
          removeSelectedTasksButton.setIcon(ImageUtil.dye((ImageIcon) removeSelectedTasksButton.getIcon(), iconColor));
          restartAllTasksButton.setIcon(ImageUtil.dye((ImageIcon) restartAllTasksButton.getIcon(), iconColor));
          cancelAllTasksButton.setIcon(ImageUtil.dye((ImageIcon) cancelAllTasksButton.getIcon(), iconColor));
          removeAllTasksButton.setIcon(ImageUtil.dye((ImageIcon) removeAllTasksButton.getIcon(), iconColor));
          // 播放队列工具栏按钮颜色
          playQueueRemoveToolButton.setIcon(ImageUtil.dye((ImageIcon) playQueueRemoveToolButton.getIcon(), iconColor));
          playQueueClearToolButton.setIcon(ImageUtil.dye((ImageIcon) playQueueClearToolButton.getIcon(), iconColor));
          playQueueDuplicateToolButton.setIcon(ImageUtil.dye((ImageIcon) playQueueDuplicateToolButton.getIcon(), iconColor));
          playQueueReverseToolButton.setIcon(ImageUtil.dye((ImageIcon) playQueueReverseToolButton.getIcon(), iconColor));
          playQueueMoveUpToolButton.setIcon(ImageUtil.dye((ImageIcon) playQueueMoveUpToolButton.getIcon(), iconColor));
          playQueueMoveDownToolButton.setIcon(ImageUtil.dye((ImageIcon) playQueueMoveDownToolButton.getIcon(), iconColor));
  
          // 工具栏下面的标签颜色
          countLabel.setForeground(textColor);
          netMusicTitleLabel.setForeground(textColor);
          netMusicCountLabel.setForeground(textColor);
          netPlaylistTitleLabel.setForeground(textColor);
          netPlaylistCountLabel.setForeground(textColor);
          netAlbumTitleLabel.setForeground(textColor);
          netAlbumCountLabel.setForeground(textColor);
          netArtistTitleLabel.setForeground(textColor);
          netArtistCountLabel.setForeground(textColor);
          netRadioTitleLabel.setForeground(textColor);
          netRadioCountLabel.setForeground(textColor);
          netMvTitleLabel.setForeground(textColor);
          netMvCountLabel.setForeground(textColor);
          netRankingCountLabel.setForeground(textColor);
          netUserTitleLabel.setForeground(textColor);
          netUserCountLabel.setForeground(textColor);
          netCommentTitleLabel.setForeground(textColor);
          netCommentCountLabel.setForeground(textColor);
          netSheetTitleLabel.setForeground(textColor);
          netSheetCountLabel.setForeground(textColor);
          recommendCountLabel.setForeground(textColor);
          collectionCountLabel.setForeground(textColor);
          taskCountLabel.setForeground(textColor);
          playQueueCountLabel.setForeground(textColor);
  
          // 音乐列表透明
          MusicListRenderer musicListRenderer = new MusicListRenderer(player);
          musicListRenderer.setForeColor(foreColor);
          musicListRenderer.setSelectedColor(selectedColor);
          musicListRenderer.setTextColor(textColor);
          musicListRenderer.setIconColor(iconColor);
          musicList.setCellRenderer(musicListRenderer);
  
          NetMusicListRenderer netMusicListRenderer = new NetMusicListRenderer(player);
          netMusicListRenderer.setForeColor(foreColor);
          netMusicListRenderer.setSelectedColor(selectedColor);
          netMusicListRenderer.setTextColor(textColor);
          netMusicListRenderer.setIconColor(iconColor);
          netMusicList.setCellRenderer(netMusicListRenderer);
  
          NetPlaylistListRenderer netPlaylistListRenderer = new NetPlaylistListRenderer();
          netPlaylistListRenderer.setForeColor(foreColor);
          netPlaylistListRenderer.setSelectedColor(selectedColor);
          netPlaylistListRenderer.setTextColor(textColor);
          netPlaylistListRenderer.setIconColor(iconColor);
          netPlaylistList.setCellRenderer(netPlaylistListRenderer);
  
          NetAlbumListRenderer netAlbumListRenderer = new NetAlbumListRenderer();
          netAlbumListRenderer.setForeColor(foreColor);
          netAlbumListRenderer.setSelectedColor(selectedColor);
          netAlbumListRenderer.setTextColor(textColor);
          netAlbumListRenderer.setIconColor(iconColor);
          netAlbumList.setCellRenderer(netAlbumListRenderer);
  
          NetArtistListRenderer netArtistListRenderer = new NetArtistListRenderer();
          netArtistListRenderer.setForeColor(foreColor);
          netArtistListRenderer.setSelectedColor(selectedColor);
          netArtistListRenderer.setTextColor(textColor);
          netArtistListRenderer.setIconColor(iconColor);
          netArtistList.setCellRenderer(netArtistListRenderer);
  
          NetRadioListRenderer netRadioListRenderer = new NetRadioListRenderer();
          netRadioListRenderer.setForeColor(foreColor);
          netRadioListRenderer.setSelectedColor(selectedColor);
          netRadioListRenderer.setTextColor(textColor);
          netRadioListRenderer.setIconColor(iconColor);
          netRadioList.setCellRenderer(netRadioListRenderer);
  
          NetMvListRenderer netMvListRenderer = new NetMvListRenderer();
          netMvListRenderer.setForeColor(foreColor);
          netMvListRenderer.setSelectedColor(selectedColor);
          netMvListRenderer.setTextColor(textColor);
          netMvListRenderer.setIconColor(iconColor);
          netMvList.setCellRenderer(netMvListRenderer);
  
          NetRankingListRenderer netRankingListRenderer = new NetRankingListRenderer();
          netRankingListRenderer.setForeColor(foreColor);
          netRankingListRenderer.setSelectedColor(selectedColor);
          netRankingListRenderer.setTextColor(textColor);
          netRankingListRenderer.setIconColor(iconColor);
          netRankingList.setCellRenderer(netRankingListRenderer);
  
          NetUserListRenderer netUserListRenderer = new NetUserListRenderer();
          netUserListRenderer.setForeColor(foreColor);
          netUserListRenderer.setSelectedColor(selectedColor);
          netUserListRenderer.setTextColor(textColor);
          netUserListRenderer.setIconColor(iconColor);
          netUserList.setCellRenderer(netUserListRenderer);
  
          NetCommentListRenderer netCommentListRenderer = new NetCommentListRenderer();
          netCommentListRenderer.setForeColor(foreColor);
          netCommentListRenderer.setSelectedColor(selectedColor);
          netCommentListRenderer.setTextColor(textColor);
          netCommentListRenderer.setIconColor(iconColor);
          netCommentList.setCellRenderer(netCommentListRenderer);
  
          NetSheetListRenderer netSheetListRenderer = new NetSheetListRenderer();
          netSheetListRenderer.setForeColor(foreColor);
          netSheetListRenderer.setSelectedColor(selectedColor);
          netSheetListRenderer.setTextColor(textColor);
          netSheetListRenderer.setIconColor(iconColor);
          netSheetList.setCellRenderer(netSheetListRenderer);
  
          ItemRecommendListRenderer itemRecommendListRenderer = new ItemRecommendListRenderer();
          itemRecommendListRenderer.setForeColor(foreColor);
          itemRecommendListRenderer.setSelectedColor(selectedColor);
          itemRecommendListRenderer.setTextColor(textColor);
          itemRecommendListRenderer.setIconColor(iconColor);
          itemRecommendList.setCellRenderer(itemRecommendListRenderer);
  
          ItemRecommendListRenderer collectionListRenderer = new ItemRecommendListRenderer();
          collectionListRenderer.setForeColor(foreColor);
          collectionListRenderer.setSelectedColor(selectedColor);
          collectionListRenderer.setTextColor(textColor);
          collectionListRenderer.setIconColor(iconColor);
          collectionList.setCellRenderer(collectionListRenderer);
  
          DownloadListRenderer downloadListRenderer = new DownloadListRenderer();
          downloadListRenderer.setForeColor(foreColor);
          downloadListRenderer.setSelectedColor(selectedColor);
          downloadListRenderer.setTextColor(textColor);
          downloadListRenderer.setIconColor(iconColor);
          downloadList.setCellRenderer(downloadListRenderer);
  
          MusicListRenderer playQueueRenderer = new MusicListRenderer(player);
          playQueueRenderer.setForeColor(foreColor);
          playQueueRenderer.setSelectedColor(selectedColor);
          playQueueRenderer.setTextColor(textColor);
          playQueueRenderer.setIconColor(iconColor);
          playQueue.setCellRenderer(tabbedPane.getSelectedIndex() == TabIndex.PLAY_QUEUE ? playQueueRenderer : null);
          this.playQueueRenderer = playQueueRenderer;
  
          // 歌单/专辑/歌手/电台/榜单描述
          playlistDescriptionScrollPane.setHUI(new ScrollBarUI(scrollBarColor, false));
          playlistDescriptionScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          albumDescriptionScrollPane.setHUI(new ScrollBarUI(scrollBarColor, false));
          albumDescriptionScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          artistDescriptionScrollPane.setHUI(new ScrollBarUI(scrollBarColor, false));
          artistDescriptionScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          radioDescriptionScrollPane.setHUI(new ScrollBarUI(scrollBarColor, false));
          radioDescriptionScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          rankingDescriptionScrollPane.setHUI(new ScrollBarUI(scrollBarColor, false));
          rankingDescriptionScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          userDescriptionScrollPane.setHUI(new ScrollBarUI(scrollBarColor, false));
          userDescriptionScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          recommendItemDescriptionScrollPane.setHUI(new ScrollBarUI(scrollBarColor, false));
          recommendItemDescriptionScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          collectionItemDescriptionScrollPane.setHUI(new ScrollBarUI(scrollBarColor, false));
          collectionItemDescriptionScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          // 描述收藏按钮
          playlistDescriptionCollectionButton.setForeground(textColor);
          albumDescriptionCollectionButton.setForeground(textColor);
          artistDescriptionCollectionButton.setForeground(textColor);
          radioDescriptionCollectionButton.setForeground(textColor);
          rankingDescriptionCollectionButton.setForeground(textColor);
          userDescriptionCollectionButton.setForeground(textColor);
          recommendItemDescriptionCollectionButton.setForeground(textColor);
          collectionItemDescriptionCollectionButton.setForeground(textColor);
  
          playlistDescriptionCollectionButton.setIcon(ImageUtil.dye((ImageIcon) playlistDescriptionCollectionButton.getIcon(), iconColor));
          albumDescriptionCollectionButton.setIcon(ImageUtil.dye((ImageIcon) albumDescriptionCollectionButton.getIcon(), iconColor));
          artistDescriptionCollectionButton.setIcon(ImageUtil.dye((ImageIcon) artistDescriptionCollectionButton.getIcon(), iconColor));
          radioDescriptionCollectionButton.setIcon(ImageUtil.dye((ImageIcon) radioDescriptionCollectionButton.getIcon(), iconColor));
          rankingDescriptionCollectionButton.setIcon(ImageUtil.dye((ImageIcon) rankingDescriptionCollectionButton.getIcon(), iconColor));
          userDescriptionCollectionButton.setIcon(ImageUtil.dye((ImageIcon) userDescriptionCollectionButton.getIcon(), iconColor));
          recommendItemDescriptionCollectionButton.setIcon(ImageUtil.dye((ImageIcon) recommendItemDescriptionCollectionButton.getIcon(), iconColor));
          collectionItemDescriptionCollectionButton.setIcon(ImageUtil.dye((ImageIcon) collectionItemDescriptionCollectionButton.getIcon(), iconColor));
  
          playlistCoverAndNameLabel.setForeground(textColor);
          albumCoverAndNameLabel.setForeground(textColor);
          artistCoverAndNameLabel.setForeground(textColor);
          radioCoverAndNameLabel.setForeground(textColor);
          rankingCoverAndNameLabel.setForeground(textColor);
          userCoverAndNameLabel.setForeground(textColor);
          recommendItemCoverAndNameLabel.setForeground(textColor);
          collectionItemCoverAndNameLabel.setForeground(textColor);
  
          playlistTagLabel.setForeground(textColor);
          playlistDescriptionLabel.setForeground(textColor);
          albumDescriptionLabel.setForeground(textColor);
          artistTagLabel.setForeground(textColor);
          artistDescriptionLabel.setForeground(textColor);
          radioTagLabel.setForeground(textColor);
          radioDescriptionLabel.setForeground(textColor);
          rankingDescriptionLabel.setForeground(textColor);
          userTagLabel.setForeground(textColor);
          userDescriptionLabel.setForeground(textColor);
          recommendItemTagLabel.setForeground(textColor);
          recommendItemDescriptionLabel.setForeground(textColor);
          collectionItemTagLabel.setForeground(textColor);
          collectionItemDescriptionLabel.setForeground(textColor);
  
          // 滚动面板消除边框、自定义样式
          musicScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          musicScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 关键词面板列表滚动面板消除边框、自定义样式
          netMusicKeywordsPanelScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netMusicKeywordsPanelScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 在线音乐列表滚动面板消除边框、自定义样式
          netMusicScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netMusicScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 歌单关键词面板列表滚动面板消除边框、自定义样式
          netPlaylistKeywordsPanelScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netPlaylistKeywordsPanelScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 在线歌单滚动面板消除边框、自定义样式
          netPlaylistScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netPlaylistScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 专辑关键词面板列表滚动面板消除边框、自定义样式
          netAlbumKeywordsPanelScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netAlbumKeywordsPanelScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 在线专辑滚动面板消除边框、自定义样式
          netAlbumScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netAlbumScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 歌手关键词面板列表滚动面板消除边框、自定义样式
          netArtistKeywordsPanelScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netArtistKeywordsPanelScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 在线歌手滚动面板消除边框、自定义样式
          netArtistScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netArtistScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 电台关键词面板列表滚动面板消除边框、自定义样式
          netRadioKeywordsPanelScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netRadioKeywordsPanelScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 在线电台滚动面板消除边框、自定义样式
          netRadioScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netRadioScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // MV 关键词面板列表滚动面板消除边框、自定义样式
          netMvKeywordsPanelScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netMvKeywordsPanelScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 在线 MV 滚动面板消除边框、自定义样式
          netMvScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netMvScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 榜单滚动面板消除边框、自定义样式
          netRankingScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netRankingScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 用户关键词面板列表滚动面板消除边框、自定义样式
          netUserKeywordsPanelScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netUserKeywordsPanelScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 在线用户滚动面板消除边框、自定义样式
          netUserScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netUserScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 评论滚动面板消除边框、自定义样式
          netCommentScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netCommentScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 乐谱滚动面板消除边框、自定义样式
          netSheetScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          netSheetScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 推荐滚动面板消除边框、自定义样式
          itemRecommendScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          itemRecommendScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 收藏滚动面板消除边框、自定义样式
          collectionScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          collectionScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 下载滚动面板消除边框、自定义样式
          downloadListScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          downloadListScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
          // 播放队列滚动面板消除边框、自定义样式
          playQueueScrollPane.setHUI(new ScrollBarUI(scrollBarColor));
          playQueueScrollPane.setVUI(new ScrollBarUI(scrollBarColor));
  
          // 歌词高亮显示
          LrcListRenderer lrcListRenderer = new LrcListRenderer();
          lrcListRenderer.setRow(row);
          lrcListRenderer.setHorizontalAlignment(SwingConstants.CENTER);
          lrcListRenderer.setBgColor(lrcColor);
          lrcListRenderer.setHighlightColor(highlightColor);
          lrcList.setCellRenderer(lrcListRenderer);
          lrcList.setUI(new ListUI(-1));  // 歌词禁用字体透明，需要用到自定义 List
  
          lrcScrollPane.setVUI(new ScrollBarUI(scrollBarColor, false));
  
          // 进度条和控制面板透明
          timeBar.setUI(new SliderUI(timeBar, timeBarColor, timeBarColor, THIS, player, true));      // 自定义进度条 UI
          // 桌面歌词更新颜色
          desktopLyricDialog.setBgColor(lrcColor);
          desktopLyricDialog.setForeColor(highlightColor);
          desktopLyricDialog.updateStyle();
          // 时间标签用进度条的颜色
          currTimeLabel.setForeground(textColor);
          durationLabel.setForeground(textColor);
  
          changePaneButton.setForeground(textColor);
          ChangePaneButtonUI cui = new ChangePaneButtonUI(THIS);
          changePaneButton.setUI(cui);
          changePaneButton.addMouseListener(new ChangePaneButtonMouseListener(changePaneButton, cui, THIS));
  
          mvButton.setIcon(ImageUtil.dye((ImageIcon) mvButton.getIcon(), iconColor));
          collectButton.setIcon(ImageUtil.dye((ImageIcon) collectButton.getIcon(), iconColor));
          downloadButton.setIcon(ImageUtil.dye((ImageIcon) downloadButton.getIcon(), iconColor));
          commentButton.setIcon(ImageUtil.dye((ImageIcon) commentButton.getIcon(), iconColor));
          sheetButton.setIcon(ImageUtil.dye((ImageIcon) sheetButton.getIcon(), iconColor));
          lastButton.setIcon(ImageUtil.dye((ImageIcon) lastButton.getIcon(), iconColor));
          playOrPauseButton.setIcon(ImageUtil.dye((ImageIcon) playOrPauseButton.getIcon(), iconColor));
          nextButton.setIcon(ImageUtil.dye((ImageIcon) nextButton.getIcon(), iconColor));
          // 迷你窗口样式改变
  //        if (miniDialog != null) {
  //            miniDialog.infoLabel.setForeground(textColor);
  //            miniDialog.playLastButton.setIcon(lastButton.getIcon());
  //            miniDialog.playOrPauseButton.setIcon(playOrPauseButton.getIcon());
  //            miniDialog.playNextButton.setIcon(nextButton.getIcon());
  //            miniDialog.closeButton.setIcon(closeButton.getIcon());
  //        }
          playModeButton.setIcon(ImageUtil.dye((ImageIcon) playModeButton.getIcon(), iconColor));
          backwardButton.setIcon(ImageUtil.dye((ImageIcon) backwardButton.getIcon(), iconColor));
          forwardButton.setIcon(ImageUtil.dye((ImageIcon) forwardButton.getIcon(), iconColor));
          muteButton.setIcon(ImageUtil.dye((ImageIcon) muteButton.getIcon(), iconColor));
          rateButton.setIcon(ImageUtil.dye((ImageIcon) rateButton.getIcon(), iconColor));
          switchSpectrumButton.setIcon(ImageUtil.dye((ImageIcon) switchSpectrumButton.getIcon(), iconColor));
          blurButton.setIcon(ImageUtil.dye((ImageIcon) blurButton.getIcon(), iconColor));
          soundEffectButton.setIcon(ImageUtil.dye((ImageIcon) soundEffectButton.getIcon(), iconColor));
          goToPlayQueueButton.setIcon(ImageUtil.dye((ImageIcon) goToPlayQueueButton.getIcon(), iconColor));
          desktopLyricButton.setIcon(ImageUtil.dye((ImageIcon) desktopLyricButton.getIcon(), iconColor));
          switchLrcTypeButton.setIcon(ImageUtil.dye((ImageIcon) switchLrcTypeButton.getIcon(), iconColor));
          volumeSlider.setUI(new SliderUI(volumeSlider, style.getSliderColor(), style.getSliderColor(), THIS, player, false));
  
  //        // 按钮图标颜色
  //        if (!player.loadedMusic() || player.loadedMusic() && (player.getMetaMusicInfo().getAlbumImage() == ImageConstants.DEFAULT_IMG || player.getMetaMusicInfo().getAlbumImage() == null)) {
  //            changePaneButton.setIcon(ImageUtil.dye(new ImageIcon(
  //                    ImageUtil.setRadius(ImageUtil.width(ImageConstants.DEFAULT_IMG, changePaneImageWidth), TINY_ARC)), iconColor));
  //        }
  //        // 默认专辑图颜色
  //        if (player.loadedMusic() && (player.getMetaMusicInfo().getAlbumImage() == ImageConstants.DEFAULT_IMG || player.getMetaMusicInfo().getAlbumImage() == null)) {
  //            BufferedImage albumImage = ImageUtil.borderShadow(ImageUtil.dye(ImageUtil.setRadius(ImageUtil.width(ImageConstants.DEFAULT_IMG, albumImageWidth), LARGE_ARC), iconColor));
  //            albumImageLabel.setIcon(new ImageIcon(albumImage));
  //        }
          // 其他标签颜色
          songNameLabel.setForeground(textColor);
          artistLabel.setForeground(textColor);
          albumLabel.setForeground(textColor);
          // 切换主题，包含背景图，模糊状态并载入了音乐就不换
          if (blurType == BlurConstants.OFF || !player.loadedMusicResource()) doBlur();
          // 标题图标
          Image titleImg = ImageUtil.dye(titleIcon, iconColor).getImage();
          setIconImage(titleImg);
          desktopLyricDialog.setIconImage(titleImg);
  
          // 更新单选菜单项和标签按钮样式
          updateMenuItemIcon(sortPopupMenu);
          updateMenuItemIcon(spectrumPopupMenu);
          updateTabButtonStyle();
  
          // 根据选项卡选择的情况设置选项卡文字 + 图标颜色
          updateTabUI(tabbedPane, style);
          updateTabUI(collectionTabbedPane, style);
  
          globalPanel.repaint();
      }
  
      // 打开编辑歌曲信息弹窗
      private void editInfo(AudioFile file) {
          EditInfoDialog editInfoDialog = new EditInfoDialog(THIS, file);
          editInfoDialog.showDialog();
      }
  
      // 打开自定义主题弹窗
      private void customStyle() {
          CustomStyleDialog customStyleDialog = new CustomStyleDialog(THIS, I18n.getText("addAndApplyStyle"), currUIStyle);
          customStyleDialog.showDialog();
          if (!customStyleDialog.isConfirmed()) return;
          // 创建自定义样式并更换
          Object[] results = customStyleDialog.getResults();
          UIStyle customStyle = new UIStyle(
                  UIStyleConstants.CUSTOM, (String) results[0],
                  "", (Color) results[2], (Color) results[3],
                  (Color) results[4], (Color) results[5], (Color) results[6],
                  (Color) results[7], (Color) results[8], (Color) results[9],
                  (Color) results[10], (Color) results[11]
          );
          if (results[1] instanceof Color) customStyle.setBgColor((Color) results[1]);
          else customStyle.setImgKey((String) results[1]);
          // 添加主题菜单项、按钮组，并切换主题
          styles.add(customStyle);
          customStyle.setInvokeLater(() -> changeUIStyle(customStyle));
      }
  
      public void updateTabUI(CustomTabbedPane tabbedPane, UIStyle style) {
          updateTabUI(tabbedPane, style, null);
      }
  
      // 更新 Tab 的主题
      public void updateTabUI(CustomTabbedPane tabbedPane, UIStyle style, Point p) {
          Color textColor = style.getTextColor();
          Color iconColor = style.getIconColor();
          Color foreColor = style.getForeColor();
          Color selectedColor = style.getSelectedColor();
  
          int index = tabbedPane.getSelectedIndex();
          for (int i = 0, size = tabbedPane.getTabCount(); i < size; i++) {
              CustomPanel panel = (CustomPanel) tabbedPane.getTabComponentAt(i);
              CustomLabel label = (CustomLabel) panel.getComponent(0);
              Rectangle rect = panel.getVisibleRect();
              if (i == index) {
                  label.setIcon(ImageUtil.dye((ImageIcon) label.getIcon(), iconColor));
                  label.setForeground(textColor);
                  panel.setForeground(selectedColor);
                  panel.setDrawBg(true);
              } else {
                  label.setIcon(ImageUtil.dye((ImageIcon) label.getIcon(), iconColor));
                  label.setForeground(textColor);
                  panel.setForeground(foreColor);
                  panel.setDrawBg(p != null && rect.contains(SwingUtilities.convertPoint(tabbedPane, p, panel)));
              }
          }
      }
  
      // 播放/暂停按钮执行
      public void playOrPause() {
          switch (player.getStatus()) {
              // 空状态，载入选择的音乐并播放
              case PlayerStatus.EMPTY:
                  // 播放队列没有选中并且有歌曲在队列时自动选中第一首播放
                  if (playQueue.getSelectedIndex() == -1 && !playQueueModel.isEmpty()) playQueue.setSelectedIndex(0);
                  playExecutor.execute(() -> playSelected(playQueue, false));
                  break;
              // 就绪状态
              case PlayerStatus.LOADED:
                  playLoaded(false);
                  break;
              // 暂停状态
              case PlayerStatus.PAUSED:
                  player.play();
                  playOrPauseButton.setIcon(ImageUtil.dye(pauseIcon, currUIStyle.getIconColor()));
                  playOrPauseButton.setToolTipText(PAUSE_TIP);
                  if (miniDialog != null) {
                      miniDialog.playOrPauseButton.setIcon(playOrPauseButton.getIcon());
                      miniDialog.playOrPauseButton.setToolTipText(playOrPauseButton.getToolTipText());
                  }
                  break;
              // 播放状态
              case PlayerStatus.PLAYING:
                  // 淡出式暂停
                  Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0.25), new KeyValue(player.getMp().volumeProperty(), 0)));
                  timeline.setOnFinished(event -> {
                      player.pause();
                      player.setVolume((float) volumeSlider.getValue() / MAX_VOLUME);
                  });
                  timeline.play();
                  playOrPauseButton.setIcon(ImageUtil.dye(playIcon, currUIStyle.getIconColor()));
                  playOrPauseButton.setToolTipText(PLAY_TIP);
                  if (miniDialog != null) {
                      miniDialog.playOrPauseButton.setIcon(playOrPauseButton.getIcon());
                      miniDialog.playOrPauseButton.setToolTipText(playOrPauseButton.getToolTipText());
                  }
                  break;
              // 停止状态
              case PlayerStatus.STOPPED:
                  playLoaded(true);
                  seekLrc(0);
                  break;
          }
      }
  
      // 停止播放
      private void stopPlayback() {
          updateTitle(STOPPED);
          player.stop();
          playOrPauseButton.setIcon(ImageUtil.dye(playIcon, currUIStyle.getIconColor()));
          playOrPauseButton.setToolTipText(PLAY_TIP);
          if (miniDialog != null) {
              miniDialog.playOrPauseButton.setIcon(playOrPauseButton.getIcon());
              miniDialog.playOrPauseButton.setToolTipText(playOrPauseButton.getToolTipText());
          }
      }
  
      // 导出图片
      private void saveImg(BufferedImage albumImage) {
          if (albumImage == null) return;
          FileChooser fileChooser = new FileChooser();
          fileChooser.setTitle(SAVE_IMG);
          ObservableList<FileChooser.ExtensionFilter> filters = fileChooser.getExtensionFilters();
          // 添加可保存的图片格式
          for (String suffix : Format.WRITE_IMAGE_TYPE_SUPPORTED) {
              filters.add(new FileChooser.ExtensionFilter(suffix.toUpperCase(), "*." + suffix));
          }
          Platform.runLater(() -> {
              File outputFile = fileChooser.showSaveDialog(null);
              if (outputFile != null) {
                  ImageUtil.toFile(albumImage, outputFile);
              }
          });
      }
  
      // 下载歌词
      private void downloadLrc(NetMusicInfo musicInfo) {
          globalExecutor.execute(() -> {
              try {
                  FileUtil.mkDir(SimplePath.DOWNLOAD_MUSIC_PATH);
                  FileUtil.writeStr(lrcStr, SimplePath.DOWNLOAD_MUSIC_PATH + musicInfo.toSimpleLrcFileName());
                  new TipDialog(THIS, DOWNLOAD_COMPLETED_MSG).showDialog();
              } catch (Exception e) {
                  if (e instanceof IORuntimeException) {
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } else if (e instanceof HttpException) {
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } else {
                      new TipDialog(THIS, GET_RESOURCE_FAILED_MSG).showDialog();
                  }
              }
          });
      }
  
      // 下载歌曲(单首)
      private void singleDownload(NetMusicInfo musicInfo) {
          // 创建下载任务，并加入队列
          Task task = new Task(downloadList, TaskType.MUSIC, musicInfo);
          task.setInvokeLater(() -> {
              String destMusicPath = SimplePath.DOWNLOAD_MUSIC_PATH + musicInfo.toSimpleFileName();
              String destLrcPath = SimplePath.DOWNLOAD_MUSIC_PATH + musicInfo.toSimpleLrcFileName();
              // 写入歌曲信息
              MediaUtil.writeAudioFileInfo(destMusicPath, musicInfo);
              // 自动下载歌词
              if (isAutoDownloadLrc && StringUtil.notEmpty(musicInfo.getLrcFileText()))
                  FileUtil.writeStr(musicInfo.getLrcFileText(), destLrcPath);
          });
          task.start();
          downloadListModel.add(0, task);
          new TipDialog(this, TASK_ADDED_MSG).showDialog();
      }
  
      // 下载歌曲(多首)
      private void multiDownload(List musicInfos) {
          List<Task> tasks = new LinkedList<>();
          // 避免造成性能问题
          downloadList.setModel(emptyListModel);
          for (int i = musicInfos.size() - 1; i >= 0; i--) {
              MusicResource resource = (MusicResource) musicInfos.get(i);
              if (!(resource instanceof NetMusicInfo)) continue;
              NetMusicInfo musicInfo = (NetMusicInfo) resource;
  
              // 创建下载任务，并加入队列
              Task task = new Task(downloadList, TaskType.MUSIC, musicInfo);
              task.setInvokeLater(() -> {
                  String destMusicPath = SimplePath.DOWNLOAD_MUSIC_PATH + musicInfo.toSimpleFileName();
                  String destLrcPath = SimplePath.DOWNLOAD_MUSIC_PATH + musicInfo.toSimpleLrcFileName();
                  // 写入歌曲信息
                  MediaUtil.writeAudioFileInfo(destMusicPath, musicInfo);
                  // 自动下载歌词
                  if (isAutoDownloadLrc && StringUtil.notEmpty(musicInfo.getLrcFileText()))
                      FileUtil.writeStr(musicInfo.getLrcFileText(), destLrcPath);
              });
              tasks.add(task);
              downloadListModel.add(0, task);
          }
          downloadList.setModel(downloadListModel);
          for (int i = tasks.size() - 1; i >= 0; i--) tasks.get(i).start();
          tasks.clear();
          new TipDialog(this, TASK_ADDED_MSG).showDialog();
      }
  
      // 下载多部 MV
      public void multiDownloadMv(List mvList, boolean msgOnTop) {
          List<Task> tasks = new LinkedList<>();
          // 避免造成性能问题
          downloadList.setModel(emptyListModel);
          for (int i = mvList.size() - 1; i >= 0; i--) {
              NetMvInfo mvInfo = (NetMvInfo) mvList.get(i);
              // 创建下载任务，并加入队列
              Task task = new Task(downloadList, TaskType.MV, mvInfo);
              tasks.add(task);
              downloadListModel.add(0, task);
          }
          downloadList.setModel(downloadListModel);
          for (int i = tasks.size() - 1; i >= 0; i--) tasks.get(i).start();
          tasks.clear();
          new TipDialog(this, TASK_ADDED_MSG, msgOnTop).showDialog();
      }
  
      // 播放 MV
      private void playMv(int mvType) {
          // 播放下载列表的 MV
          if (mvType == MvCompSourceType.DOWNLOAD_LIST) {
              Task task = downloadList.getSelectedValue();
              if (task == null) return;
              NetMvInfo mvInfo = (NetMvInfo) task.getResource();
              String dest = task.getDest();
              if (!new File(dest).exists()) {
                  new TipDialog(THIS, FILE_NOT_FOUND_MSG).showDialog();
              } else {
                  if (player.isPlaying()) playOrPause();
                  if (videoOnly) setVisible(false);
                  videoDialog = new VideoDialog(mvInfo, dest, THIS);
                  videoDialog.showDialog();
                  videoDialog = null;
                  setVisible(true);
              }
              return;
          }
  
          // 右键歌曲播放其 MV
          if (mvType != MvCompSourceType.MV_LIST && mvType != MvCompSourceType.MV_RECOMMEND_LIST && mvType != MvCompSourceType.COLLECTION) {
              NetMusicInfo musicInfo = null;
              if (mvType == MvCompSourceType.NET_MUSIC_LIST) {
                  musicInfo = netMusicList.getSelectedValue();
              } else if (mvType == MvCompSourceType.MUSIC_LIST) {
                  musicInfo = (NetMusicInfo) musicList.getSelectedValue();
              } else if (mvType == MvCompSourceType.PLAY_QUEUE) {
                  musicInfo = (NetMusicInfo) playQueue.getSelectedValue();
              } else if (mvType == MvCompSourceType.PLAYING) {
                  musicInfo = player.getMusicInfo();
              }
              if (musicInfo == null) return;
  
              TipDialog dialog = new TipDialog(THIS, LOADING_MV_MSG, 0);
              dialog.showDialog();
  
              NetMvInfo mvInfo = new NetMvInfo();
              mvInfo.setSource(musicInfo.getSource());
              mvInfo.setId(musicInfo.getMvId());
              mvInfo.setName(musicInfo.getName());
              mvInfo.setArtist(musicInfo.getArtist());
  
              // 加载 MV url
              try {
                  MusicServerUtil.fillMvInfo(mvInfo);
                  String url = mvInfo.getUrl();
                  if (StringUtil.isEmpty(url)) {
                      new TipDialog(THIS, GET_RESOURCE_FAILED_MSG).showDialog();
                      return;
                  }
                  if (player.isPlaying()) playOrPause();
                  dialog.close();
                  if (videoOnly) setVisible(false);
                  videoDialog = new VideoDialog(mvInfo, null, THIS);
                  videoDialog.showDialog();
                  videoDialog = null;
                  setVisible(true);
              } catch (Exception e) {
                  if (e instanceof IORuntimeException) {
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } else if (e instanceof HttpException) {
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } else {
                      new TipDialog(THIS, GET_RESOURCE_FAILED_MSG).showDialog();
                  }
              } finally {
                  dialog.close();
                  if (videoOnly) setVisible(true);
              }
          }
          // 播放搜索/下载/收藏列表的 MV
          else {
              NetMvInfo mvInfo = null;
              File file = null;
              if (mvType == MvCompSourceType.MV_LIST) mvInfo = netMvList.getSelectedValue();
              else if (mvType == MvCompSourceType.MV_RECOMMEND_LIST)
                  mvInfo = (NetMvInfo) itemRecommendList.getSelectedValue();
              else if (mvType == MvCompSourceType.COLLECTION) mvInfo = (NetMvInfo) collectionList.getSelectedValue();
              if (mvInfo == null) return;
  
              TipDialog dialog = new TipDialog(THIS, LOADING_MV_MSG, 0);
              dialog.showDialog();
  
              try {
                  MusicServerUtil.fillMvInfo(mvInfo);
                  String url = mvInfo.getUrl();
                  if (StringUtil.isEmpty(url)) {
                      new TipDialog(THIS, GET_RESOURCE_FAILED_MSG).showDialog();
                      return;
                  }
  
                  // 不支持的格式转为 mp4 格式再播放
                  if (!mvInfo.isMp4()) {
                      String fileName = mvInfo.toFileName();
                      file = new File(SimplePath.CACHE_PATH + fileName);
                      // 转为 mp4 再播放
                      File tmpFile = FileUtil.replaceSuffix(file, Format.MP4);
                      if (!tmpFile.exists()) {
                          // 下载 MV
                          if (!file.exists() || FileUtil.startsWithLeftBrace(file)) {
                              dialog.setMessage("加载视频文件......");
                              dialog.updateSize();
                              dialog.setLocationRelativeTo(null);
                              Map<String, String> headers = null;
                              if (mvInfo.getSource() == NetMusicSource.BI) {
                                  headers = new HashMap<>();
                                  headers.put("referer", "https://www.bilibili.com/");
                              }
                              MusicServerUtil.download(mvInfo.getUrl(), file.getPath(), headers);
                          }
                          dialog.setMessage("转换视频文件格式......");
                          dialog.updateSize();
                          dialog.setLocationRelativeTo(null);
                          MediaUtil.convert(file, tmpFile);
                          // 转换成功后删除原文件
                          file.delete();
                      }
                      file = tmpFile;
                  }
  
                  if (player.isPlaying()) playOrPause();
                  dialog.close();
                  if (videoOnly) setVisible(false);
                  videoDialog = new VideoDialog(mvInfo, file == null ? null : file.getPath(), THIS);
                  videoDialog.showDialog();
                  videoDialog = null;
                  setVisible(true);
              } catch (Exception e) {
                  if (e instanceof IORuntimeException) {
                      new TipDialog(THIS, NO_NET_MSG).showDialog();
                  } else if (e instanceof HttpException) {
                      new TipDialog(THIS, TIME_OUT_MSG).showDialog();
                  } else {
                      new TipDialog(THIS, GET_RESOURCE_FAILED_MSG).showDialog();
                  }
              } finally {
                  dialog.close();
                  if (videoOnly) setVisible(true);
              }
          }
      }
  
      // 判断是否已经收藏
      public boolean hasBeenCollected(Resource resource) {
          if (resource == null) return false;
          DefaultListModel model = null;
          if (resource instanceof AudioFile || resource instanceof NetMusicInfo) model = collectionModel;
          else if (resource instanceof NetPlaylistInfo) model = playlistCollectionModel;
          else if (resource instanceof NetAlbumInfo) model = albumCollectionModel;
          else if (resource instanceof NetArtistInfo) model = artistCollectionModel;
          else if (resource instanceof NetRadioInfo) model = radioCollectionModel;
          else if (resource instanceof NetMvInfo) model = mvCollectionModel;
          else if (resource instanceof NetRankingInfo) model = rankingCollectionModel;
          else if (resource instanceof NetUserInfo) model = userCollectionModel;
          for (int i = 0, len = model.size(); i < len; i++) {
              if (model.get(i).equals(resource)) return true;
          }
          return false;
      }
  
      // 更新收藏
      private void updateCollection(Resource resource) {
          if (resource == null) return;
          DefaultListModel model = null;
          if (resource instanceof AudioFile || resource instanceof NetMusicInfo) model = collectionModel;
          else if (resource instanceof NetPlaylistInfo) model = playlistCollectionModel;
          else if (resource instanceof NetAlbumInfo) model = albumCollectionModel;
          else if (resource instanceof NetArtistInfo) model = artistCollectionModel;
          else if (resource instanceof NetRadioInfo) model = radioCollectionModel;
          else if (resource instanceof NetMvInfo) model = mvCollectionModel;
          else if (resource instanceof NetRankingInfo) model = rankingCollectionModel;
          else if (resource instanceof NetUserInfo) model = userCollectionModel;
          for (int i = 0, len = model.size(); i < len; i++) {
              if (model.get(i).equals(resource)) {
                  model.set(i, resource);
                  return;
              }
          }
      }
  
      // 更新 renderer ，避免 CustomList 各个元素大小不变
      public void updateRenderer(CustomList list) {
          ListCellRenderer renderer = list.getCellRenderer();
          if (renderer == null) return;
          synchronized (renderer) {
              list.setCellRenderer(null);
              list.setCellRenderer(renderer);
          }
      }
  
      // 移动歌词
      private void seekLrc(double t) {
          if (nextLrc < 0) return;
          for (int i = 0, size = statements.size(); i < size; i++) {
              if (t < statements.get(i).getTime() - lrcOffset) nextLrc = i;
              else if (i == size - 1) nextLrc = size;
              else continue;
              row = nextLrc - 1;
              if (!lrcScrollAnimation) {
                  currScrollVal = lrcScrollPane.getVValue();
                  lrcScrollAnimation = true;
              }
              LrcListRenderer renderer = (LrcListRenderer) lrcList.getCellRenderer();
              renderer.setRow(row);
              updateOriginalRatio(t);
              break;
          }
      }
  
      // 获取 t 时间处的歌词
      public String getTimeLrc(double t) {
          if (nextLrc < 0) return "";
          for (int i = 0, size = statements.size(); i < size; i++) {
              if (t < statements.get(i).getTime() - lrcOffset) {
                  if (i == 0) return statements.get(i).getPlainLyric();
                  else return statements.get(i - 1).getPlainLyric();
              } else if (i == size - 1) {
                  return statements.get(i).getPlainLyric();
              }
          }
          return "";
      }
  
      // 更新上层组件
      private void updateUpperComp() {
          // 更新弹出菜单
          if (currPopup != null) currPopup.repaint();
          // 更新对话框
          try {
              for (AbstractShadowDialog d : currDialogs) d.updateBlur();
          } catch (ConcurrentModificationException e) {
  
          }
      }
  
      // 模糊碟片，图像宽度设为 窗口宽度 * 1.2，等比例，毛玻璃化，暗化
      public void doBlur() {
          blurExecutor.execute(() -> {
              BufferedImage img;
              boolean loadedMusicResource = player.loadedMusicResource();
              if (blurType != BlurConstants.OFF && loadedMusicResource) {
                  img = player.getMetaMusicInfo().getAlbumImage();
                  if (img == null) img = ImageConstants.DEFAULT_IMG;
                  if (blurType == BlurConstants.MC) img = ImageUtil.dyeRect(1, 1, ImageUtil.getBestAvgColor(img));
              } else img = currUIStyle.getImg();
  
              int gw = globalPanel.getWidth(), gh = globalPanel.getHeight();
              if (gw == 0 || gh == 0) {
                  gw = windowWidth;
                  gh = windowHeight;
              }
              // 改变迷你窗口背景
              if (miniDialog != null) {
                  BufferedImage finalImg = img;
                  miniDialog.globalExecutor.execute(() -> miniDialog.doBlur(finalImg));
              }
  
              // 截取正方形(有的图片是长方形)
              if (loadedMusicResource && (blurType == BlurConstants.CV || blurType == BlurConstants.LG))
                  img = ImageUtil.cropCenter(img);
              // 消除透明度
              img = ImageUtil.eraseTransparency(img);
              if (loadedMusicResource) {
                  // 线性渐变
                  if (blurType == BlurConstants.LG) img = ImageUtil.toGradientImage(img, gw, gh);
                      // 迷幻纹理
                  else if (blurType == BlurConstants.FBM) img = ImageUtil.toFbmImage(img, gw, gh);
              }
              if (maskOn) img = ImageUtil.mask(img);
              if (gsOn) {
                  // 缩小
                  img = ImageUtil.width(img, 256);
                  // 高斯模糊
                  img = ImageUtil.gaussianBlur(img);
              }
              // 缩放至窗口大小
              img = ImageUtil.width(img, gw);
              if (gh > img.getHeight())
                  img = ImageUtil.height(img, gh);
              // 裁剪中间的一部分
              if (!loadedMusicResource || blurType == BlurConstants.CV || blurType == BlurConstants.OFF) {
                  int iw = img.getWidth(), ih = img.getHeight();
                  img = ImageUtil.region(img, iw > gw ? (iw - gw) / 2 : 0, iw > gw ? 0 : (ih - gh) / 2, gw, gh);
                  img = ImageUtil.quality(img, 0.1);
              } else {
                  img = ImageUtil.forceSize(img, gw, gh);
              }
              // 暗角滤镜
              if (darkerOn) img = ImageUtil.darker(img);
              // 设置圆角
  //                img = ImageUtils.setRadius(img, WIN_ARC);
              globalPanel.setBgImg(img);
              updateUpperComp();
              globalPanel.setOnImgScaledReady(() -> {
                  if (!globalPanelTimer.isRunning()) globalPanelTimer.start();
              });
          });
      }
  
      // 筛选个人音乐
      private void filterPersonalMusic() {
          filterModel.clear();
          String keyword = filterTextField.getText().toLowerCase().trim();
          int selectedIndex = collectionTabbedPane.getSelectedIndex();
          boolean useMusicList = currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION || selectedIndex == CollectionTabIndex.MUSIC;
          if (keyword.isEmpty()) {
              if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC) musicList.setModel(musicListModel);
              else if (currPersonalMusicTab == PersonalMusicTabIndex.HISTORY) musicList.setModel(historyModel);
              else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION) {
                  if (selectedIndex == CollectionTabIndex.MUSIC) musicList.setModel(collectionModel);
                  else if (selectedIndex == CollectionTabIndex.PLAYLIST) collectionList.setModel(playlistCollectionModel);
                  else if (selectedIndex == CollectionTabIndex.ALBUM) collectionList.setModel(albumCollectionModel);
                  else if (selectedIndex == CollectionTabIndex.ARTIST) collectionList.setModel(artistCollectionModel);
                  else if (selectedIndex == CollectionTabIndex.RADIO) collectionList.setModel(radioCollectionModel);
                  else if (selectedIndex == CollectionTabIndex.MV) collectionList.setModel(mvCollectionModel);
                  else if (selectedIndex == CollectionTabIndex.RANKING) collectionList.setModel(rankingCollectionModel);
                  else if (selectedIndex == CollectionTabIndex.USER) collectionList.setModel(userCollectionModel);
              }
              if (useMusicList) {
                  int size = musicList.getModel().getSize();
                  countLabel.setText(String.format(TOTAL_MSG, size));
                  if (size != 0) {
                      if (currPersonalMusicTab != PersonalMusicTabIndex.COLLECTION) {
                          leftBox.remove(emptyHintPanel);
                          leftBox.add(musicScrollPane);
                      } else {
                          musicCollectionLeftBox.remove(emptyHintPanel);
                          musicCollectionLeftBox.add(musicScrollPane);
                      }
                  }
              } else {
                  int size = collectionList.getModel().getSize();
                  countLabel.setText(String.format(TOTAL_MSG, size));
                  if (size != 0) {
                      Box box = null;
                      if (selectedIndex == CollectionTabIndex.PLAYLIST) box = playlistCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.ALBUM) box = albumCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.ARTIST) box = artistCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.RADIO) box = radioCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.MV) box = mvCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.RANKING) box = rankingCollectionLeftBox;
                      else if (selectedIndex == CollectionTabIndex.USER) box = userCollectionLeftBox;
                      box.remove(emptyHintPanel);
                      box.add(collectionScrollPane);
                  }
              }
              return;
          }
          DefaultListModel listModel = null;
          if (currPersonalMusicTab == PersonalMusicTabIndex.LOCAL_MUSIC) listModel = musicListModel;
          else if (currPersonalMusicTab == PersonalMusicTabIndex.HISTORY) listModel = historyModel;
          else if (currPersonalMusicTab == PersonalMusicTabIndex.COLLECTION) {
              if (selectedIndex == CollectionTabIndex.MUSIC) listModel = collectionModel;
              else if (selectedIndex == CollectionTabIndex.PLAYLIST) listModel = playlistCollectionModel;
              else if (selectedIndex == CollectionTabIndex.ALBUM) listModel = albumCollectionModel;
              else if (selectedIndex == CollectionTabIndex.ARTIST) listModel = artistCollectionModel;
              else if (selectedIndex == CollectionTabIndex.RADIO) listModel = radioCollectionModel;
              else if (selectedIndex == CollectionTabIndex.MV) listModel = mvCollectionModel;
              else if (selectedIndex == CollectionTabIndex.RANKING) listModel = rankingCollectionModel;
              else if (selectedIndex == CollectionTabIndex.USER) listModel = userCollectionModel;
          }
          // 解决选中第一项重新筛选的性能问题
          if (useMusicList) {
              // 修复筛选时程序假死问题
              if (!listModel.isEmpty()) musicList.setSelectedIndex(0);
              musicList.setModel(emptyListModel);
          } else collectionList.setModel(emptyListModel);
          for (int i = 0, size = listModel.size(); i < size; i++) {
              Resource resource = (Resource) listModel.get(i);
              if (resource.toString().toLowerCase().contains(keyword)) filterModel.addElement(resource);
          }
          if (useMusicList) {
              musicList.setModel(filterModel);
              countLabel.setText(String.format(TOTAL_MSG, musicList.getModel().getSize()));
          } else {
              collectionList.setModel(filterModel);
              countLabel.setText(String.format(TOTAL_MSG, collectionList.getModel().getSize()));
          }
      }
  
      // 从搜索历史删除关键词
      private void removeKeywordInHistorySearch(String keyword, int type) {
          CustomPanel p = null;
          if (type == HistorySearchType.NET_MUSIC) p = netMusicHistorySearchInnerPanel2;
          else if (type == HistorySearchType.NET_PLAYLIST) p = netPlaylistHistorySearchInnerPanel2;
          else if (type == HistorySearchType.NET_ALBUM) p = netAlbumHistorySearchInnerPanel2;
          else if (type == HistorySearchType.NET_ARTIST) p = netArtistHistorySearchInnerPanel2;
          else if (type == HistorySearchType.NET_RADIO) p = netRadioHistorySearchInnerPanel2;
          else if (type == HistorySearchType.NET_MV) p = netMvHistorySearchInnerPanel2;
          else if (type == HistorySearchType.NET_USER) p = netUserHistorySearchInnerPanel2;
          Component[] components = p.getComponents();
          for (Component c : components) {
              DialogButton b = (DialogButton) c;
              if (b.getPlainText().equals(keyword)) {
                  p.remove(b);
                  return;
              }
          }
      }
  
      // 加载搜索建议
      private void updateSearchSuggestion() {
          synchronized (netMusicSearchSuggestionInnerPanel2) {
              try {
                  String part = searchTextField.getText();
                  Set<String> suggestions = MusicServerUtil.getSearchSuggestion(part);
                  if (!suggestions.isEmpty()) {
                      if (!netMusicSearchSuggestionPanel.isShowing()) {
                          // 显示 “搜索建议” 面板
                          netMusicSearchSuggestionPanel.setVisible(true);
                      }
                  } else {
                      // 没有结果时隐藏 “搜索建议” 面板
                      netMusicSearchSuggestionPanel.setVisible(false);
                  }
                  netMusicSearchSuggestionInnerPanel2.removeAll();
                  DialogButton b = null;
                  for (String keyword : suggestions) {
                      b = new DialogButton(keyword);
                      b.addActionListener(event -> {
                          searchTextField.requestFocus();
                          searchTextField.setText(keyword);
                          netMusicClearInputButton.setVisible(true);
                          searchButton.doClick();
                          netLeftBox.remove(netMusicKeywordsPanelScrollPane);
                          netLeftBox.add(netMusicScrollPane);
                      });
                      b.setForeColor(currUIStyle.getTextColor());
                      netMusicSearchSuggestionInnerPanel2.add(b);
                  }
                  // 调整面板大小
                  if (b != null) {
                      DialogButton fb = b;
                      b.addComponentListener(new ComponentAdapter() {
                          @Override
                          public void componentMoved(ComponentEvent e) {
                              Point p = SwingUtilities.convertPoint(fb, 0, 0, netMusicSearchSuggestionInnerPanel2);
                              Dimension d = new Dimension(netMusicSearchSuggestionInnerPanel2.getWidth(), p.y + 50);
                              netMusicSearchSuggestionInnerPanel2.setMinimumSize(d);
                              netMusicSearchSuggestionInnerPanel2.setPreferredSize(d);
                              netMusicSearchSuggestionInnerPanel2.setVisible(false);
                              netMusicSearchSuggestionInnerPanel2.setVisible(true);
                          }
                      });
                  }
                  netMusicSearchSuggestionPanel.repaint();
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          }
          searchSuggestionProcessing = false;
      }
  
      // 加载热搜词
      private void updateHotSearch() {
          synchronized (netMusicHotSearchInnerPanel2) {
              try {
                  Set<String> hotSearch = MusicServerUtil.getHotSearch();
                  // 显示 “热门搜索” 面板
                  if (!hotSearch.isEmpty() && !netMusicHotSearchPanel.isShowing()) {
                      netMusicHotSearchPanel.setVisible(true);
                  }
                  netMusicHotSearchInnerPanel2.removeAll();
                  DialogButton b = null;
                  for (String keyword : hotSearch) {
                      b = new DialogButton(keyword);
                      DialogButton finalB = b;
                      b.addActionListener(event -> {
                          searchTextField.requestFocus();
                          searchTextField.setText(keyword);
                          netMusicClearInputButton.setVisible(true);
                          searchButton.doClick();
                          netLeftBox.remove(netMusicKeywordsPanelScrollPane);
                          netLeftBox.add(netMusicScrollPane);
                          // 热搜词搜索后取消悬停状态
                          finalB.setEntered(false);
                      });
                      b.setForeColor(currUIStyle.getTextColor());
                      netMusicHotSearchInnerPanel2.add(b);
                  }
                  // 调整面板大小
                  if (b != null) {
                      DialogButton fb = b;
                      b.addComponentListener(new ComponentAdapter() {
                          @Override
                          public void componentMoved(ComponentEvent e) {
                              Point p = SwingUtilities.convertPoint(fb, 0, 0, netMusicHotSearchInnerPanel2);
                              Dimension d = new Dimension(netMusicHotSearchInnerPanel2.getWidth(), p.y + 50);
                              netMusicHotSearchInnerPanel2.setMinimumSize(d);
                              netMusicHotSearchInnerPanel2.setPreferredSize(d);
                              netMusicHotSearchInnerPanel2.setVisible(false);
                              netMusicHotSearchInnerPanel2.setVisible(true);
                          }
                      });
                  }
                  netMusicHotSearchPanel.repaint();
              } catch (IORuntimeException ioRuntimeException) {
                  // 无网络连接
                  new TipDialog(THIS, NO_NET_MSG).showDialog();
              } catch (HttpException httpException) {
                  // 请求超时
                  new TipDialog(THIS, TIME_OUT_MSG).showDialog();
              } catch (JSONException jsonException) {
                  // 接口异常
                  new TipDialog(THIS, API_ERROR_MSG).showDialog();
              }
          }
      }
  
      // 清除歌单请求实例
      private void clearRequestForPlaylist() {
          currPlaylistMusicInfo = null;
          currPlaylistPlaylistInfo = null;
          currPlaylistCommentInfo = null;
          currPlaylistUserInfo = null;
      }
  
      // 清除专辑请求实例
      private void clearRequestForAlbum() {
          currAlbumMusicInfo = null;
          currAlbumArtistInfo = null;
          currAlbumAlbumInfo = null;
          currAlbumUserInfo = null;
          currAlbumCommentInfo = null;
      }
  
      // 清除歌手请求实例
      private void clearRequestForArtist() {
          currArtistMusicInfo = null;
          currArtistAlbumInfo = null;
          currArtistArtistInfo = null;
          currBuddyArtistInfo = null;
          currArtistMvInfo = null;
          currArtistRadioInfo = null;
          currCVRadioInfo = null;
      }
  
      // 清除电台请求实例
      private void clearRequestForRadio() {
          currRadioMusicInfo = null;
          currRecRadioMusicInfo = null;
          currRadioUserInfo = null;
          currRadioArtistInfo = null;
          currRadioRadioInfo = null;
      }
  
      // 清除 MV 请求实例
      private void clearRequestForMv() {
          currMvArtistInfo = null;
          currMvMusicInfo = null;
          currMvMvInfo = null;
          currEpisodesMvInfo = null;
          currMvUserInfo = null;
      }
  
      // 清除用户请求实例
      private void clearRequestForUser() {
          currFollowUserUserInfo = null;
          currFanUserUserInfo = null;
          currAuthorMusicInfo = null;
          currAuthorAlbumInfo = null;
          currUserPlaylistInfo = null;
          currUserArtistInfo = null;
          currUserMvInfo = null;
          currUserRadioInfo = null;
          currUserCommentInfo = null;
          currSubscriberPlaylistInfo = null;
          currSubscriberRadioInfo = null;
      }
  
      // 复制字符串到剪贴板
      private void copyToClipboard(String s) {
          StringSelection stringSelection = new StringSelection(s);
          Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
          clipboard.setContents(stringSelection, null);
          new TipDialog(THIS, COPY_SUCCESS_MSG).showDialog();
      }
  
      // 隐藏界面
      private void hideWindow() {
          closeButton.setDrawBg(false);
          closeSpectrum();
          dispose();
      }
  
      // 退出应用
      public void exit() {
          // 先暂停播放
          player.pause();
          // 缓存超出最大值时清理
          if (FileUtil.size(new File(SimplePath.CACHE_PATH)) > maxCacheSize * 1024 * 1024) clearCache();
          SystemTray.getSystemTray().remove(trayIconImg);
          saveConfig();
          System.exit(0);
      }
  
      // 清除缓存
      private void clearCache() {
          FileUtil.clean(SimplePath.CACHE_PATH);
      }
  
      // 弹出加载面板并执行
      private void loadingAndRun(Runnable runnable) {
          globalExecutor.execute(() -> {
              try {
                  loading.start();
                  loading.setText(LOADING_MSG);
                  runnable.run();
                  Thread.sleep(28);
              } catch (Exception ex) {
                  LogUtil.error(ex);
              } finally {
                  loading.stop();
              }
          });
      }
  
      // 验证数据
      private static boolean validateData() {
          String s1 = LMIconManager.getBase64("dialog.weixin");
          String s2 = LMIconManager.getBase64("dialog.alipay");
          if (!"e3721514976bb205b1ef917f817c6243".equals(CryptoUtil.md5(s1)))
              return false;
          if (!"5d62142f49aaa03205dc8cc7bed351d4".equals(CryptoUtil.md5(s2)))
              return false;
          return true;
      }
  
      public static void launch() {
          GlobalExecutors.requestExecutor.execute(() -> {
              if (validateData()) return;
              System.exit(-1);
          });
          UIPreProcessor.process();
          new MainFrame().initUI();
      }
  }
  