diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index 4309fcb8b3..258976db24 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -3638,3 +3638,31 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT "「すべてのプレイリスト」アイコンの最大個数:") MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SHOW_HIDDEN_FILES, "隠しファイルとフォルダを表示:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST, + "新規プレイリスト") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ENTER_NEW_PLAYLIST_NAME, + "新しいプレイリストの名前を入力してください:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_DELETE_PLAYLIST, + "プレイリストを削除") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST, + "「%1」というプレイリストを削除しますか?") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_QUESTION, + "質問") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE, + "ファイル削除に失敗しました。") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_GATHERING_LIST_OF_FILES, + "ファイルの一覧を構築しています...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ADDING_FILES_TO_PLAYLIST, + "ファイルをプレイリストに追加しています...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY, + "プレイリストエントリー") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_CORE, + "コア:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_DATABASE, + "データベース:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FOR_THUMBNAILS, + "(サムネイルを見つかることに使う)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST_ITEM, + "「%1」というアイテムを削除しますか?") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CANNOT_ADD_TO_ALL_PLAYLISTS, + "まずひとつのプレイリストを選択してください。") diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index f9a0321199..4bb2a4a11c 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3796,3 +3796,31 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT "\"All Playlists\" max grid entries:") MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SHOW_HIDDEN_FILES, "Show hidden files and folders:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST, + "New Playlist") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ENTER_NEW_PLAYLIST_NAME, + "Please enter the new playlist name:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_DELETE_PLAYLIST, + "Delete Playlist") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST, + "Are you sure you want to delete the playlist \"%1\"?") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_QUESTION, + "Question") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE, + "Could not delete file.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_GATHERING_LIST_OF_FILES, + "Gathering list of files...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ADDING_FILES_TO_PLAYLIST, + "Adding files to playlist...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY, + "Playlist Entry") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_CORE, + "Core:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_DATABASE, + "Database:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FOR_THUMBNAILS, + "(used to find thumbnails)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST_ITEM, + "Are you sure you want to delete the item \"%1\"?") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CANNOT_ADD_TO_ALL_PLAYLISTS, + "Please choose a single playlist first.") diff --git a/msg_hash.h b/msg_hash.h index 985bff2205..6feca0ecff 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1879,6 +1879,20 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS, MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST, MENU_ENUM_LABEL_VALUE_QT_PROGRESS, + MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST, + MENU_ENUM_LABEL_VALUE_QT_ENTER_NEW_PLAYLIST_NAME, + MENU_ENUM_LABEL_VALUE_QT_DELETE_PLAYLIST, + MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST, + MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST_ITEM, + MENU_ENUM_LABEL_VALUE_QT_QUESTION, + MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE, + MENU_ENUM_LABEL_VALUE_QT_GATHERING_LIST_OF_FILES, + MENU_ENUM_LABEL_VALUE_QT_ADDING_FILES_TO_PLAYLIST, + MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY, + MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_CORE, + MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_DATABASE, + MENU_ENUM_LABEL_VALUE_QT_FOR_THUMBNAILS, + MENU_ENUM_LABEL_VALUE_QT_CANNOT_ADD_TO_ALL_PLAYLISTS, MENU_LABEL(MIDI_INPUT), MENU_LABEL(MIDI_OUTPUT), diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index fd9382c2d4..af33cb03aa 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -29,6 +29,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include "../ui_qt.h" @@ -125,9 +130,53 @@ static void scan_finished_handler(void *task_data, void *user_data, const char * } #endif -inline static bool comp_hash_label_key(const QHash &lhs, const QHash &rhs) +inline static bool comp_string_lower(const QString &lhs, const QString &rhs) { - return lhs.value("label") < rhs.value("label"); + return lhs.toLower() < rhs.toLower(); +} + +inline static bool comp_hash_ui_display_name_key_lower(const QHash &lhs, const QHash &rhs) +{ + return lhs.value("ui_display_name").toLower() < rhs.value("ui_display_name").toLower(); +} + +inline static bool comp_hash_label_key_lower(const QHash &lhs, const QHash &rhs) +{ + return lhs.value("label").toLower() < rhs.value("label").toLower(); +} + +static void addDirectoryFilesToList(QStringList &list, QDir &dir) +{ + QStringList dirList = dir.entryList(QStringList(), QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, QDir::Name); + int i; + + for (i = 0; i < dirList.count(); i++) + { + QString path(dir.path() + "/" + dirList.at(i)); + QFileInfo fileInfo(path); + + if (fileInfo.isDir()) + { + QDir fileInfoDir(path); + + addDirectoryFilesToList(list, fileInfoDir); + continue; + } + + if (fileInfo.isFile()) + { + list.append(fileInfo.absoluteFilePath()); + } + } +} + +/* https://gist.github.com/andrey-str/0f9c7709cbf0c9c49ef9 */ +static void setElidedText(QLabel *label, QWidget *clipWidget, int padding, const QString &text) +{ + QFontMetrics metrix(label->font()); + int width = clipWidget->width() - padding; + QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); + label->setText(clippedText); } GridItem::GridItem() : @@ -142,15 +191,6 @@ GridItem::GridItem() : { } -/* https://gist.github.com/andrey-str/0f9c7709cbf0c9c49ef9 */ -static void setElidedText(QLabel *label, QWidget *clipWidget, int padding, const QString &text) -{ - QFontMetrics metrix(label->font()); - int width = clipWidget->width() - padding; - QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); - label->setText(clippedText); -} - TreeView::TreeView(QWidget *parent) : QTreeView(parent) { @@ -170,6 +210,52 @@ void TreeView::selectionChanged(const QItemSelection &selected, const QItemSelec emit itemsSelected(list); } +FileDropWidget::FileDropWidget(QWidget *parent) : + QWidget(parent) +{ + setAcceptDrops(true); +} + +void FileDropWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Delete) + { + event->accept(); + emit deletePressed(); + } + else + QWidget::keyPressEvent(event); +} + +void FileDropWidget::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData *data = event->mimeData(); + + if (data->hasUrls()) + event->acceptProposedAction(); +} + +void FileDropWidget::dropEvent(QDropEvent *event) +{ + const QMimeData *data = event->mimeData(); + + if (data->hasUrls()) + { + QList urls = data->urls(); + QStringList files; + int i; + + for (i = 0; i < urls.count(); i++) + { + QString path(urls.at(i).toLocalFile()); + + files.append(path); + } + + emit filesDropped(files); + } +} + TableWidget::TableWidget(QWidget *parent) : QTableWidget(parent) { @@ -182,6 +268,11 @@ void TableWidget::keyPressEvent(QKeyEvent *event) event->accept(); emit enterPressed(); } + else if (event->key() == Qt::Key_Delete) + { + event->accept(); + emit deletePressed(); + } else QTableWidget::keyPressEvent(event); } @@ -276,6 +367,151 @@ void CoreInfoDialog::showCoreInfo() show(); } +PlaylistEntryDialog::PlaylistEntryDialog(MainWindow *mainwindow, QWidget *parent) : + QDialog(parent) + ,m_mainwindow(mainwindow) + ,m_settings(mainwindow->settings()) + ,m_coreComboBox(new QComboBox(this)) + ,m_databaseComboBox(new QComboBox(this)) +{ + QFormLayout *form = new QFormLayout(); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + QVBoxLayout *databaseVBoxLayout = new QVBoxLayout(); + QLabel *databaseLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FOR_THUMBNAILS), this); + + databaseVBoxLayout->addWidget(m_databaseComboBox); + databaseVBoxLayout->addWidget(databaseLabel); + + setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY)); + + form->setFormAlignment(Qt::AlignCenter); + form->setLabelAlignment(Qt::AlignCenter); + + setLayout(new QVBoxLayout(this)); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + connect(this, SIGNAL(accepted()), this, SLOT(onAccepted())); + connect(this, SIGNAL(rejected()), this, SLOT(onRejected())); + + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_CORE), m_coreComboBox); + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_DATABASE), databaseVBoxLayout); + + qobject_cast(layout())->addLayout(form); + layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); + layout()->addWidget(buttonBox); +} + +void PlaylistEntryDialog::loadPlaylistOptions() +{ + core_info_list_t *core_info_list = NULL; + const core_info_t *core_info = NULL; + unsigned i = 0; + int j = 0; + + m_coreComboBox->clear(); + m_databaseComboBox->clear(); + + m_coreComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK)); + m_databaseComboBox->addItem(QString("<") + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE) + ">"); + + core_info_get_list(&core_info_list); + + if (core_info_list && core_info_list->count > 0) + { + QVector > allCores; + QStringList allDatabases; + + for (i = 0; i < core_info_list->count; i++) + { + const core_info_t *core = &core_info_list->list[i]; + QStringList databases = QString(core->databases).split("|"); + QHash hash; + QString ui_display_name; + + hash["core_name"] = core->core_name; + hash["core_display_name"] = core->display_name; + hash["core_path"] = core->path; + hash["core_databases"] = core->databases; + + ui_display_name = hash.value("core_name"); + + if (ui_display_name.isEmpty()) + ui_display_name = hash.value("core_display_name"); + if (ui_display_name.isEmpty()) + ui_display_name = QFileInfo(hash.value("core_path")).fileName(); + if (ui_display_name.isEmpty()) + continue; + + hash["ui_display_name"] = ui_display_name; + + for (j = 0; j < databases.count(); j++) + { + QString database = databases.at(j); + + if (database.isEmpty()) + continue; + + if (!allDatabases.contains(database)) + allDatabases.append(database); + } + + if (!allCores.contains(hash)) + allCores.append(hash); + } + + std::sort(allCores.begin(), allCores.end(), comp_hash_ui_display_name_key_lower); + std::sort(allDatabases.begin(), allDatabases.end(), comp_string_lower); + + for (j = 0; j < allCores.count(); j++) + { + const QHash &hash = allCores.at(j); + + m_coreComboBox->addItem(hash.value("ui_display_name"), QVariant::fromValue(hash)); + } + + for (j = 0; j < allDatabases.count(); j++) + { + QString database = allDatabases.at(j); + m_databaseComboBox->addItem(database, database); + } + } +} + +const QHash PlaylistEntryDialog::getSelectedCore() +{ + return m_coreComboBox->currentData(Qt::UserRole).value >(); +} + +const QString PlaylistEntryDialog::getSelectedDatabase() +{ + return m_databaseComboBox->currentData(Qt::UserRole).toString(); +} + +void PlaylistEntryDialog::onAccepted() +{ +} + +void PlaylistEntryDialog::onRejected() +{ +} + +bool PlaylistEntryDialog::showDialog() +{ + loadPlaylistOptions(); + + if (exec() == QDialog::Accepted) + return true; + + return false; +} + +void PlaylistEntryDialog::hideDialog() +{ + reject(); +} + ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) : QDialog(parent) ,m_mainwindow(mainwindow) @@ -556,7 +792,7 @@ MainWindow::MainWindow(QWidget *parent) : ,m_gridWidget(new QWidget(this)) ,m_gridScrollArea(new QScrollArea(m_gridWidget)) ,m_gridItems() - ,m_gridLayoutWidget(new QWidget()) + ,m_gridLayoutWidget(new FileDropWidget()) ,m_zoomSlider(NULL) ,m_lastZoomSliderValue(0) ,m_pendingItemUpdates() @@ -568,6 +804,7 @@ MainWindow::MainWindow(QWidget *parent) : ,m_currentGridWidget(NULL) ,m_allPlaylistsListMaxCount(0) ,m_allPlaylistsGridMaxCount(0) + ,m_playlistEntryDialog(NULL) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); @@ -663,6 +900,7 @@ MainWindow::MainWindow(QWidget *parent) : /* ViewOptionsDialog needs m_settings set before it's constructed */ m_settings = new QSettings(configDir + "/retroarch_qt.cfg", QSettings::IniFormat, this); m_viewOptionsDialog = new ViewOptionsDialog(this, 0); + m_playlistEntryDialog = new PlaylistEntryDialog(this, 0); /* default NULL parameter for parent wasn't added until 5.7 */ m_startCorePushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_START_CORE), m_startCorePushButton)); @@ -763,6 +1001,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_tableWidget, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), this, SLOT(onCurrentTableItemChanged(QTableWidgetItem*, QTableWidgetItem*))); connect(m_tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(onContentItemDoubleClicked(QTableWidgetItem*))); connect(m_tableWidget, SIGNAL(enterPressed()), this, SLOT(onTableWidgetEnterPressed())); + connect(m_tableWidget, SIGNAL(deletePressed()), this, SLOT(onTableWidgetDeletePressed())); connect(m_startCorePushButton, SIGNAL(clicked()), this, SLOT(onStartCoreClicked())); connect(m_coreInfoPushButton, SIGNAL(clicked()), m_coreInfoDialog, SLOT(showCoreInfo())); connect(m_runPushButton, SIGNAL(clicked()), this, SLOT(onRunClicked())); @@ -774,6 +1013,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(onZoomValueChanged(int))); connect(viewTypeIconsAction, SIGNAL(triggered()), this, SLOT(onIconViewClicked())); connect(viewTypeListAction, SIGNAL(triggered()), this, SLOT(onListViewClicked())); + connect(m_gridLayoutWidget, SIGNAL(filesDropped(QStringList)), this, SLOT(onPlaylistFilesDropped(QStringList))); /* make sure these use an auto connection so it will be queued if called from a different thread (some facilities in RA log messages from other threads) */ connect(this, SIGNAL(gotLogMessage(const QString&)), this, SLOT(onGotLogMessage(const QString&)), Qt::AutoConnection); @@ -811,6 +1051,167 @@ MainWindow::~MainWindow() removeGridItems(); } +void MainWindow::onPlaylistFilesDropped(QStringList files) +{ + addFilesToPlaylist(files); +} + +/* Takes a list of files and folders and adds them to the currently selected playlist. Folders will have their contents added recursively. */ +void MainWindow::addFilesToPlaylist(QStringList files) +{ + QStringList list; + QString currentPlaylistPath; + QListWidgetItem *currentItem = m_listWidget->currentItem(); + QByteArray currentPlaylistArray; + QScopedPointer dialog(NULL); + PlaylistEntryDialog *playlistDialog = playlistEntryDialog(); + QHash selectedCore; + QString selectedDatabase; + const char *currentPlaylistData = NULL; + playlist_t *playlist = NULL; + int i; + + if (currentItem) + { + currentPlaylistPath = currentItem->data(Qt::UserRole).toString(); + + if (!currentPlaylistPath.isEmpty()) + { + currentPlaylistArray = currentPlaylistPath.toUtf8(); + currentPlaylistData = currentPlaylistArray.constData(); + } + } + + if (currentPlaylistPath == ALL_PLAYLISTS_TOKEN) + { + ui_window.qtWindow->showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CANNOT_ADD_TO_ALL_PLAYLISTS), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal); + return; + } + + if (!playlistDialog->showDialog()) + return; + + selectedCore = m_playlistEntryDialog->getSelectedCore(); + selectedDatabase = m_playlistEntryDialog->getSelectedDatabase(); + + dialog.reset(new QProgressDialog(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_GATHERING_LIST_OF_FILES), "Cancel", 0, 0, this)); + dialog->setWindowModality(Qt::ApplicationModal); + + for (i = 0; i < files.count(); i++) + { + QString path(files.at(i)); + QFileInfo fileInfo(path); + + if (dialog->wasCanceled()) + return; + + if (i % 25 == 0) + qApp->processEvents(); + + if (fileInfo.isDir()) + { + QDir dir(path); + addDirectoryFilesToList(list, dir); + continue; + } + + if (fileInfo.isFile()) + { + list.append(fileInfo.absoluteFilePath()); + } + } + + dialog->setLabelText(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADDING_FILES_TO_PLAYLIST)); + dialog->setMaximum(list.count()); + + playlist = playlist_init(currentPlaylistData, COLLECTION_SIZE); + + for (i = 0; i < list.count(); i++) + { + QString fileName = list.at(i); + QFileInfo fileInfo; + QByteArray fileBaseNameArray; + QByteArray pathArray; + QByteArray corePathArray; + QByteArray coreNameArray; + QByteArray databaseArray; + const char *pathData = NULL; + const char *fileNameNoExten = NULL; + const char *corePathData = NULL; + const char *coreNameData = NULL; + const char *databaseData = NULL; + + if (dialog->wasCanceled()) + { + playlist_free(playlist); + return; + } + + if (fileName.isEmpty()) + continue; + + fileInfo = fileName; + fileBaseNameArray = fileInfo.baseName().toUtf8(); + fileNameNoExten = fileBaseNameArray.constData(); + + /* a modal QProgressDialog calls processEvents() automatically in setValue() */ + dialog->setValue(i + 1); + + pathArray = fileName.toUtf8(); + pathData = pathArray.constData(); + + if (selectedCore.isEmpty()) + { + corePathData = "DETECT"; + coreNameData = "DETECT"; + } + else + { + corePathArray = selectedCore.value("core_path").toUtf8(); + coreNameArray = selectedCore.value("core_name").toUtf8(); + corePathData = corePathArray.constData(); + coreNameData = coreNameArray.constData(); + } + + if (selectedDatabase.isEmpty()) + { + databaseArray = QFileInfo(currentPlaylistPath).fileName().toUtf8(); + databaseData = databaseArray.constData(); + } + else + { + selectedDatabase += file_path_str(FILE_PATH_LPL_EXTENSION); + databaseArray = selectedDatabase.toUtf8(); + databaseData = databaseArray.constData(); + } + + if (path_is_compressed_file(pathData)) + { + struct string_list *list = file_archive_get_file_list(pathData, NULL); + + if (list) + { + if (list->size == 1) + { + /* assume archives with one file should have that file loaded directly */ + pathArray = (QString(pathData) + "#" + list->elems[0].data).toUtf8(); + pathData = pathArray.constData(); + } + + string_list_free(list); + } + } + + playlist_push(playlist, pathData, fileNameNoExten, + corePathData, coreNameData, "00000000|crc", databaseData); + } + + playlist_write_file(playlist); + playlist_free(playlist); + + reloadPlaylists(); +} + void MainWindow::onGridItemClicked() { QHash hash; @@ -883,9 +1284,9 @@ inline void MainWindow::calcGridItemSize(GridItem *item, int zoomValue) void MainWindow::onZoomValueChanged(int value) { - int i = 0; + int i; - for (i = 0; i < m_gridItems.count() && m_gridItems.count() > 0; i++) + for (i = 0; i < m_gridItems.count(); i++) { GridItem *item = m_gridItems.at(i); calcGridItemSize(item, value); @@ -978,7 +1379,7 @@ void MainWindow::setCustomThemeString(QString qss) m_customThemeString = qss; } -bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowModality modality) +bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowModality modality, bool showDontAsk) { QPointer msgBoxPtr; QMessageBox *msgBox = NULL; @@ -986,13 +1387,16 @@ bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowM msgBoxPtr = new QMessageBox(this); msgBox = msgBoxPtr.data(); - checkBox = new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN), msgBox); msgBox->setWindowModality(modality); msgBox->setTextFormat(Qt::RichText); - /* QMessageBox::setCheckBox() is available since 5.2 */ - msgBox->setCheckBox(checkBox); + if (showDontAsk) + { + checkBox = new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN), msgBox); + /* QMessageBox::setCheckBox() is available since 5.2 */ + msgBox->setCheckBox(checkBox); + } switch (msgType) { @@ -1014,6 +1418,13 @@ bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowM msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ERROR)); break; } + case MSGBOX_TYPE_QUESTION: + { + msgBox->setIcon(QMessageBox::Question); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_QUESTION)); + msgBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + break; + } default: break; } @@ -1024,7 +1435,10 @@ bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowM if (!msgBoxPtr) return true; - if (checkBox->isChecked()) + if (checkBox && checkBox->isChecked()) + return false; + + if (msgBox->result() == QMessageBox::Cancel) return false; return true; @@ -1037,6 +1451,8 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) QScopedPointer associateMenu; QScopedPointer hiddenPlaylistsMenu; QScopedPointer hideAction; + QScopedPointer newPlaylistAction; + QScopedPointer deletePlaylistAction; QPointer selectedAction; QPoint cursorPos = QCursor::pos(); QListWidgetItem *selectedItem = m_listWidget->itemAt(m_listWidget->viewport()->mapFromGlobal(cursorPos)); @@ -1045,6 +1461,7 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) QString currentPlaylistDirPath; QString currentPlaylistPath; QString currentPlaylistFileName; + QFile currentPlaylistFile; QByteArray currentPlaylistFileNameArray; QFileInfo currentPlaylistFileInfo; QMap coreList; @@ -1061,33 +1478,47 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) bool specialPlaylist = false; bool foundHiddenPlaylist = false; - if (!selectedItem) - return; - new_playlist_names[0] = new_playlist_cores[0] = '\0'; stnames = string_split(settings->arrays.playlist_names, ";"); stcores = string_split(settings->arrays.playlist_cores, ";"); - currentPlaylistPath = selectedItem->data(Qt::UserRole).toString(); - currentPlaylistFileInfo = QFileInfo(currentPlaylistPath); - currentPlaylistFileName = currentPlaylistFileInfo.fileName(); - currentPlaylistDirPath = currentPlaylistFileInfo.absoluteDir().absolutePath(); + if (selectedItem) + { + currentPlaylistPath = selectedItem->data(Qt::UserRole).toString(); + currentPlaylistFile.setFileName(currentPlaylistPath); - currentPlaylistFileNameArray.append(currentPlaylistFileName); - currentPlaylistFileNameData = currentPlaylistFileNameArray.constData(); + currentPlaylistFileInfo = QFileInfo(currentPlaylistPath); + currentPlaylistFileName = currentPlaylistFileInfo.fileName(); + currentPlaylistDirPath = currentPlaylistFileInfo.absoluteDir().absolutePath(); + + currentPlaylistFileNameArray.append(currentPlaylistFileName); + currentPlaylistFileNameData = currentPlaylistFileNameArray.constData(); + } menu.reset(new QMenu(this)); menu->setObjectName("menu"); hiddenPlaylistsMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS), this)); + newPlaylistAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST)) + "...", this)); + hiddenPlaylistsMenu->setObjectName("hiddenPlaylistsMenu"); - hideAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDE), this)); + menu->addAction(newPlaylistAction.data()); - menu->addAction(hideAction.data()); + if (selectedItem) + { + hideAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDE), this)); + menu->addAction(hideAction.data()); + } - for (j = 0; j < m_listWidget->count() && m_listWidget->count() > 0; j++) + if (currentPlaylistFile.exists()) + { + deletePlaylistAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DELETE_PLAYLIST)) + "...", this)); + menu->addAction(deletePlaylistAction.data()); + } + + for (j = 0; j < m_listWidget->count(); j++) { QListWidgetItem *item = m_listWidget->item(j); bool hidden = m_listWidget->isItemHidden(item); @@ -1181,6 +1612,30 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) strlcpy(settings->arrays.playlist_cores, new_playlist_cores, sizeof(settings->arrays.playlist_cores)); } + else if (selectedAction == deletePlaylistAction.data()) + { + if (currentPlaylistFile.exists()) + { + if (ui_window.qtWindow->showMessageBox(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST)).arg(selectedItem->text()), MainWindow::MSGBOX_TYPE_QUESTION, Qt::ApplicationModal, false)) + { + if (currentPlaylistFile.remove()) + reloadPlaylists(); + else + ui_window.qtWindow->showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); + } + } + } + else if (selectedAction == newPlaylistAction.data()) + { + QString name = QInputDialog::getText(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ENTER_NEW_PLAYLIST_NAME)); + QString newPlaylistPath = playlistDirAbsPath + "/" + name + file_path_str(FILE_PATH_LPL_EXTENSION); + QFile file(newPlaylistPath); + + if (file.open(QIODevice::WriteOnly)) + file.close(); + + reloadPlaylists(); + } else if (selectedAction == hideAction.data()) { int row = m_listWidget->row(selectedItem); @@ -1380,7 +1835,7 @@ void MainWindow::reloadPlaylists() if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_video_history).fileName())) m_listWidget->setRowHidden(m_listWidget->row(videoPlaylistsItem), true); - for (i = 0; i < m_playlistFiles.count() && m_playlistFiles.count() > 0; i++) + for (i = 0; i < m_playlistFiles.count(); i++) { QListWidgetItem *item = NULL; const QString &file = m_playlistFiles.at(i); @@ -1948,6 +2403,67 @@ void MainWindow::onTableWidgetEnterPressed() onRunClicked(); } +void MainWindow::onTableWidgetDeletePressed() +{ + deleteCurrentPlaylistItem(); +} + +void MainWindow::deleteCurrentPlaylistItem() +{ + QTableWidgetItem *contentItem = m_tableWidget->currentItem(); + QListWidgetItem *playlistItem = m_listWidget->currentItem(); + QHash contentHash; + QString playlistPath; + QByteArray playlistArray; + ViewType viewType = getCurrentViewType(); + playlist_t *playlist = NULL; + const char *playlistData = NULL; + unsigned index = 0; + bool ok = false; + + if (!playlistItem) + return; + + playlistPath = playlistItem->data(Qt::UserRole).toString(); + + if (playlistPath.isEmpty()) + return; + + if (viewType == VIEW_TYPE_LIST) + { + if (!contentItem) + return; + + contentHash = contentItem->data(Qt::UserRole).value >(); + } + else if (viewType == VIEW_TYPE_ICONS) + contentHash = m_currentGridHash; + else + return; + + if (contentHash.isEmpty()) + return; + + playlistArray = playlistPath.toUtf8(); + playlistData = playlistArray.constData(); + + index = contentHash.value("index").toUInt(&ok); + + if (!ok) + return; + + if (!ui_window.qtWindow->showMessageBox(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST_ITEM)).arg(contentHash["label"]), MainWindow::MSGBOX_TYPE_QUESTION, Qt::ApplicationModal, false)) + return; + + playlist = playlist_init(playlistData, COLLECTION_SIZE); + + playlist_delete_index(playlist, index); + playlist_write_file(playlist); + playlist_free(playlist); + + reloadPlaylists(); +} + void MainWindow::onContentItemDoubleClicked(QTableWidgetItem*) { onRunClicked(); @@ -1983,6 +2499,8 @@ QHash MainWindow::getSelectedCore() contentHash = contentItem->data(Qt::UserRole).value >(); else if (viewType == VIEW_TYPE_ICONS) contentHash = m_currentGridHash; + else + return coreHash; switch(coreSelection) { @@ -2188,6 +2706,8 @@ void MainWindow::onRunClicked() contentHash = item->data(Qt::UserRole).value >(); else if (viewType == VIEW_TYPE_ICONS) contentHash = m_currentGridHash; + else + return; loadContent(contentHash); #endif @@ -2208,6 +2728,11 @@ bool MainWindow::isCoreLoaded() return true; } +PlaylistEntryDialog* MainWindow::playlistEntryDialog() +{ + return m_playlistEntryDialog; +} + ViewOptionsDialog* MainWindow::viewOptionsDialog() { return m_viewOptionsDialog; @@ -2636,6 +3161,8 @@ void MainWindow::onSearchLineEditEdited(const QString &text) break; } + default: + break; } } @@ -2659,7 +3186,7 @@ void MainWindow::onViewClosedDocksAboutToShow() return; } - for (i = 0; i < dockWidgets.count() && dockWidgets.count() > 0; i++) + for (i = 0; i < dockWidgets.count(); i++) { const QDockWidget *dock = dockWidgets.at(i); @@ -2863,6 +3390,7 @@ void MainWindow::setCurrentViewType(ViewType viewType) case VIEW_TYPE_LIST: default: { + m_viewType = VIEW_TYPE_LIST; m_gridWidget->hide(); m_tableWidget->show(); break; @@ -3219,7 +3747,7 @@ void MainWindow::addPlaylistItemsToGrid(const QStringList &paths, bool add) } } finish: - std::sort(items.begin(), items.end(), comp_hash_label_key); + std::sort(items.begin(), items.end(), comp_hash_label_key_lower); addPlaylistHashToGrid(items); } @@ -3363,7 +3891,7 @@ void MainWindow::initContentGridLayout() QStringList playlists; int i = 0; - for (i = 0; i < m_playlistFiles.count() && m_playlistFiles.count() > 0; i++) + for (i = 0; i < m_playlistFiles.count(); i++) { const QString &playlist = m_playlistFiles.at(i); playlists.append(playlistDir.absoluteFilePath(playlist)); @@ -3430,7 +3958,7 @@ void MainWindow::initContentTableWidget() QStringList playlists; int i = 0; - for (i = 0; i < m_playlistFiles.count() && m_playlistFiles.count() > 0; i++) + for (i = 0; i < m_playlistFiles.count(); i++) { const QString &playlist = m_playlistFiles.at(i); playlists.append(playlistDir.absoluteFilePath(playlist)); @@ -3495,6 +4023,8 @@ QVector > MainWindow::getPlaylistItems(QString pathStrin else hash["path"] = path; + hash["index"] = QString::number(i); + if (string_is_empty(label)) { hash["label"] = path; diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index a7e9c7f846..e3fde1f24b 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -275,9 +275,12 @@ static void* ui_companion_qt_init(void) listWidget = mainwindow->playlistListWidget(); - widget = new QWidget(mainwindow); + widget = new FileDropWidget(mainwindow); widget->setObjectName("tableWidget"); + QObject::connect(widget, SIGNAL(filesDropped(QStringList)), mainwindow, SLOT(onPlaylistFilesDropped(QStringList))); + QObject::connect(widget, SIGNAL(deletePressed()), mainwindow, SLOT(deleteCurrentPlaylistItem())); + layout = new QVBoxLayout(); layout->addWidget(mainwindow->contentTableWidget()); layout->addWidget(mainwindow->contentGridWidget()); diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 3912428a2c..3fcbb8a979 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -64,6 +64,8 @@ class QFormLayout; class QStyle; class QScrollArea; class QSlider; +class QDragEnterEvent; +class QDropEvent; class LoadCoreWindow; class MainWindow; class ThumbnailWidget; @@ -138,6 +140,20 @@ protected slots: void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); }; +class FileDropWidget : public QWidget +{ + Q_OBJECT +public: + FileDropWidget(QWidget *parent = 0); +signals: + void filesDropped(QStringList files); + void deletePressed(); +protected: + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + void keyPressEvent(QKeyEvent *event); +}; + class TableWidget : public QTableWidget { Q_OBJECT @@ -145,7 +161,8 @@ public: TableWidget(QWidget *parent = 0); signals: void enterPressed(); -protected slots: + void deletePressed(); +protected: void keyPressEvent(QKeyEvent *event); }; @@ -163,6 +180,27 @@ private slots: void onLastWindowClosed(); }; +class PlaylistEntryDialog : public QDialog +{ + Q_OBJECT +public: + PlaylistEntryDialog(MainWindow *mainwindow, QWidget *parent = 0); + const QHash getSelectedCore(); + const QString getSelectedDatabase(); +public slots: + bool showDialog(); + void hideDialog(); + void onAccepted(); + void onRejected(); +private: + void loadPlaylistOptions(); + + MainWindow *m_mainwindow; + QSettings *m_settings; + QComboBox *m_coreComboBox; + QComboBox *m_databaseComboBox; +}; + class ViewOptionsDialog : public QDialog { Q_OBJECT @@ -261,6 +299,7 @@ public: MSGBOX_TYPE_INFO, MSGBOX_TYPE_WARNING, MSGBOX_TYPE_ERROR, + MSGBOX_TYPE_QUESTION, }; MainWindow(QWidget *parent = NULL); @@ -288,7 +327,7 @@ public: QString getThemeString(Theme theme); QHash getSelectedCore(); void showStatusMessage(QString msg, unsigned priority, unsigned duration, bool flush); - bool showMessageBox(QString msg, MessageBoxType msgType = MSGBOX_TYPE_INFO, Qt::WindowModality modality = Qt::ApplicationModal); + bool showMessageBox(QString msg, MessageBoxType msgType = MSGBOX_TYPE_INFO, Qt::WindowModality modality = Qt::ApplicationModal, bool showDontAsk = true); bool setCustomThemeFile(QString filePath); void setCustomThemeString(QString qss); const QString& customThemeString() const; @@ -298,6 +337,8 @@ public: ViewType getCurrentViewType(); void setAllPlaylistsListMaxCount(int count); void setAllPlaylistsGridMaxCount(int count); + PlaylistEntryDialog* playlistEntryDialog(); + void addFilesToPlaylist(QStringList files); signals: void thumbnailChanged(const QPixmap &pixmap); @@ -320,6 +361,7 @@ public slots: void loadContent(const QHash &contentHash); void onStartCoreClicked(); void onTableWidgetEnterPressed(); + void onTableWidgetDeletePressed(); void selectBrowserDir(QString path); void resizeThumbnails(bool one, bool two, bool three); void onResizeThumbnailOne(); @@ -335,6 +377,7 @@ public slots: void onIconViewClicked(); void onListViewClicked(); void onTabWidgetIndexChanged(int index); + void deleteCurrentPlaylistItem(); private slots: void onLoadCoreClicked(const QStringList &extensionFilters = QStringList()); @@ -365,6 +408,7 @@ private slots: void onPendingItemUpdates(); void onGridItemDoubleClicked(); void onGridItemClicked(); + void onPlaylistFilesDropped(QStringList files); private: void setCurrentCoreLabel(); @@ -432,6 +476,7 @@ private: QPointer m_currentGridWidget; int m_allPlaylistsListMaxCount; int m_allPlaylistsGridMaxCount; + PlaylistEntryDialog *m_playlistEntryDialog; protected: void closeEvent(QCloseEvent *event);