#include #include #include #include #include #include #include #include #include #include "../filesystem.hpp" #include "../functions.hpp" #include "../resources_linux.h" #include "../tv_rename.hpp" #include "databasewindow.hpp" #include "gtkfunctions.hpp" #include "mainwindow.hpp" #include "progresswindow.hpp" void MainWindow::chooseFile() { // create a dialog for choosing directory Gtk::FileChooserDialog dialog( _( SELECT_DIR ), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER ); dialog.set_transient_for( *this ); // add cancel and select buttons dialog.add_button( "_" + _( CANCEL ), Gtk::RESPONSE_CANCEL ); dialog.add_button( _( SELECT ), Gtk::RESPONSE_OK ); auto result = dialog.run(); switch ( result ) { case Gtk::RESPONSE_OK: m_entry_dir->set_text( dialog.get_filename() ); break; case Gtk::RESPONSE_CANCEL: break; default: break; } } void MainWindow::quit() { hide(); } // TODO maybe fix help to not force translators to make 2 versions void MainWindow::patternHelp() { Gtk::MessageDialog dialog( *this, _( PATTERN_ESC_SEQ ) ); std::string help = "%filename - " + _( PATTERN_FILENAME ) + "\n"; help += "%show - " + _( PATTERN_SHOW ) + "\n"; help += "%epname - " + _( PATTERN_EPNAME ) + "\n"; help += "%season - " + _( PATTERN_SEASON ) + "\n"; help += "%episode - " + _( PATTERN_EPNAME ) + "\n"; help += _( PATTERN_LEADING_ZERO ) + "\n"; help += " %2season, %2episode - " + _( PATTERN_LEADING_NUM ) + "\n"; help += _( PATTERN_DEFAULT ) + " \"%filename - %epname\"\n"; help += _( PATTERN_CHANGE ) + "\"S%2seasonE%2episode - %epname\" " + _( OR ) + "\"%show - S%2seasonE%2episode - %epname\""; dialog.set_secondary_text( help ); dialog.run(); } void MainWindow::process() { language_code = ( *m_combo_language->get_active() )[m_columns_language.m_col_code]; searchShow( m_entry_show, m_combo_possible, m_columns_show.m_col_show, m_columns_show.m_col_id, language_code, this ); m_button_rename->show(); m_button_db_add->show(); } void MainWindow::getNames() { // check required field is filled out if ( m_entry_dir->get_text().empty() ) { Gtk::MessageDialog dialog( *this, _( DIR_EMPTY ) ); dialog.run(); return; } // check directory exists if ( !FSLib::isDirectory( m_entry_dir->get_text() ) ) { Gtk::MessageDialog dialog( *this, _( DIR_NOT_EXIST, m_entry_dir->get_text().c_str() ) ); dialog.run(); return; } path = m_entry_dir->get_text(); selected.clear(); files.clear(); std::vector< int > options; // get all files in path and seperate them in map `files` by season iterateFS( files, path ); for ( auto &x : files ) { options.push_back( x.first ); } // create a window with possible seasons to rename // store selected seasons in `selected` sw.reset( new SeasonWindow( options, selected ) ); sw->signal_hide().connect( sigc::mem_fun( *this, &MainWindow::finishedSelection ) ); app->add_window( *sw ); sw->show(); } void MainWindow::finishedSelection() { // remove created SeasonWindow and delete it from memory app->remove_window( *sw ); sw.reset(); auto iter = m_combo_possible->get_active(); std::string show_id = static_cast< Glib::ustring >( ( *iter )[m_columns_show.m_col_id] ); // shouldn't ever happen, but just to be sure if ( show_id.empty() ) return; std::string input_pattern = m_entry_pattern->get_text(); // store pattern to cache if it's different from default if ( input_pattern != default_pattern ) { std::ofstream file( userHome() + "/.cache/tv_rename_pattern" ); if ( file ) { file << input_pattern; } } for ( auto &x : selected ) { // get renamed files for given season auto renamed_files = getRenamedFiles( static_cast< Glib::ustring >( ( *iter )[m_columns_show.m_col_show] ), x, show_id, language_code, ( input_pattern.empty() ? default_pattern : input_pattern ), !m_check_linux->get_active(), files[x], m_check_dvd->get_active() ); if ( renamed_files.empty() ) continue; // if trust checkbox is ticked, rename files if ( m_check_trust->get_active() ) { renameFiles( renamed_files ); continue; } // create a custom dialog box with textview of new episode names std::unique_ptr< Gtk::Dialog > dialog( new Gtk::Dialog( _( RENAME_CONFIRM ), *this ) ); dialog->set_default_size( 550, 350 ); auto content = dialog->get_content_area(); std::unique_ptr< Gtk::ScrolledWindow > sc_w( new Gtk::ScrolledWindow ); std::unique_ptr< Gtk::TextView > tx( new Gtk::TextView ); content->pack_start( *sc_w ); sc_w->add( *tx ); tx->set_editable( false ); tx->set_cursor_visible( false ); dialog->add_button( "_" + _( NO ), Gtk::RESPONSE_CANCEL ); dialog->add_button( "_" + _( YES ), Gtk::RESPONSE_OK ); sc_w->show(); tx->show(); auto buff = tx->get_buffer(); buff->place_cursor( buff->begin() ); buff->insert_at_cursor( std::get< 2 >( renamed_files[0] ).c_str() ); buff->insert_at_cursor( " --> " ); buff->insert_at_cursor( std::get< 3 >( renamed_files[0] ).c_str() ); for ( size_t i = 1; i < renamed_files.size(); i++ ) { buff->insert_at_cursor( "\n" ); buff->insert_at_cursor( std::get< 2 >( renamed_files[i] ).c_str() ); buff->insert_at_cursor( " --> " ); buff->insert_at_cursor( std::get< 3 >( renamed_files[i] ).c_str() ); } auto response = dialog->run(); // if user clicked "Yes" in dialog, rename files switch ( response ) { case Gtk::RESPONSE_OK: renameFiles( renamed_files ); default: break; } } } MainWindow::~MainWindow() { auto children = get_children(); freeChildren( children ); } MainWindow::MainWindow( const Glib::RefPtr< Gtk::Application > &ptr ) : app( ptr ) { set_title( "TV Rename" ); // create widgets Gtk::Button *button_dir = new Gtk::Button(); Gtk::Button *button_quit = new Gtk::Button(); Gtk::Button *button_process = new Gtk::Button(); Gtk::Button *button_pattern = new Gtk::Button(); Gtk::Label *label_language = new Gtk::Label(); Gtk::Label *label_show = new Gtk::Label(); Gtk::Label *label_dir = new Gtk::Label(); Gtk::Label *label_pattern = new Gtk::Label(); set_default_size( 400, 345 ); { // if cached pattern exists, load that instead of default std::ifstream file( userHome() + "/.cache/tv_rename_pattern" ); if ( file ) { std::getline( file, default_pattern ); } else { default_pattern = "%filename - %epname"; } } auto *box = new Gtk::Box( Gtk::ORIENTATION_VERTICAL ); auto *menu = new Gtk::MenuBar(); auto *inputs = new Gtk::Grid(); auto *processing = new Gtk::Grid(); auto *buttons = new Gtk::Box( Gtk::ORIENTATION_HORIZONTAL ); add( *box ); box->pack_start( *menu, false, false ); box->pack_start( *inputs, false, true ); box->pack_start( *processing, false, true ); box->pack_start( *m_check_dvd, false, true ); box->pack_start( *m_label_possible, false, true ); box->pack_start( *m_combo_possible, false, true ); box->pack_start( *buttons, false, true ); box->pack_start( *button_quit, false, true ); auto *item = new Gtk::MenuItem(); item->set_use_underline( true ); auto *submenu = new Gtk::Menu(); menu->append( *item ); // File menu item->set_label( _( ID_FILE ) ); item->set_submenu( *submenu ); // Exit item for File menu item = new Gtk::MenuItem(); item->set_use_underline( true ); item->set_label( _( EXIT ) ); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::quit ) ); submenu->append( *item ); // Database menu item = new Gtk::MenuItem(); item->set_use_underline( true ); submenu = new Gtk::Menu(); item->set_label( _( DATABASE ) ); item->set_submenu( *submenu ); menu->append( *item ); // Update database item = new Gtk::MenuItem(); item->set_use_underline( true ); item->set_label( _( UPDATE_DB ) ); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbUpdate ) ); submenu->append( *item ); // Refresh database item = new Gtk::MenuItem(); item->set_use_underline( true ); item->set_label( _( REFRESH_DB ) ); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbRefresh ) ); submenu->append( *item ); // Clean database item = new Gtk::MenuItem(); item->set_use_underline( true ); item->set_label( _( CLEAN_DB ) ); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbClean ) ); submenu->append( *item ); // Manage database item = new Gtk::MenuItem(); item->set_use_underline( true ); item->set_label( _( MANAGE_DB ) ); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbManage ) ); submenu->append( *item ); item = new Gtk::MenuItem(); item->set_use_underline( true ); item->set_label( _( CHANGE_DB_PATTERN ) ); item->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::dbPattern ) ); submenu->append( *item ); std::vector< Gtk::Widget * > left_aligned_widgets = { label_show, label_language, m_entry_show, m_combo_language, label_dir, m_entry_dir, label_pattern, m_entry_pattern, button_pattern, button_process, m_check_linux, m_check_trust, m_check_dvd, m_label_possible, m_combo_possible, m_button_rename, m_button_db_add, button_dir }; // set widgets' position in boxes inputs->attach( *label_show, 0, 0, 1, 1 ); inputs->attach( *label_language, 1, 0, 1, 1 ); inputs->attach( *m_entry_show, 0, 1, 1, 1 ); inputs->attach( *m_combo_language, 1, 1, 1, 1 ); inputs->attach( *label_dir, 0, 2, 2, 1 ); inputs->attach( *m_entry_dir, 0, 3, 1, 1 ); inputs->attach( *button_dir, 1, 3, 1, 1 ); inputs->attach( *label_pattern, 0, 4, 2, 1 ); inputs->attach( *m_entry_pattern, 0, 5, 1, 1 ); inputs->attach( *button_pattern, 1, 5, 1, 1 ); inputs->set_column_homogeneous( true ); processing->attach( *button_process, 0, 0, 1, 2 ); processing->attach( *m_check_linux, 1, 0, 1, 1 ); processing->attach( *m_check_trust, 1, 1, 1, 1 ); buttons->pack_start( *m_button_rename, false, true ); buttons->pack_start( *m_button_db_add, false, true ); // set widgets alignment and margins for ( auto &x : left_aligned_widgets ) { x->set_halign( Gtk::ALIGN_START ); x->set_valign( Gtk::ALIGN_CENTER ); x->set_margin_left( 5 ); x->set_margin_top( 5 ); } m_entry_show->set_halign( Gtk::ALIGN_FILL ); m_entry_dir->set_halign( Gtk::ALIGN_FILL ); m_entry_pattern->set_halign( Gtk::ALIGN_FILL ); button_quit->set_halign( Gtk::ALIGN_END ); button_quit->set_valign( Gtk::ALIGN_CENTER ); button_quit->set_margin_right( 5 ); button_quit->set_margin_top( 5 ); button_quit->set_margin_bottom( 5 ); // set button texts button_process->set_label( _( PROCESS ) ); button_quit->set_label( _( QUIT ) ); button_dir->set_label( _( CHOOSE_DIR ) ); button_pattern->set_label( _( PATTERN_HELP ) ); m_button_rename->set_label( _( RENAME ) ); m_button_db_add->set_label( _( ADD_TO_DB ) ); m_check_linux->set_label( _( REPLACE_WIN ) ); m_check_trust->set_label( _( DONT_RENAME_CONFIRM ) ); m_check_dvd->set_label( _( USE_DVD ) ); // set label texts label_show->set_label( _( SHOW ) + ":" ); label_language->set_label( _( LANGUAGE ) + ":" ); label_dir->set_label( _( DIRECTORY ) + ":" ); label_pattern->set_label( _( PATTERN ) + ":" ); m_label_possible->set_label( _( POSSIBLE_SHOWS ) + ":" ); m_entry_show->set_size_request( 170, 30 ); m_entry_dir->set_size_request( 170, 30 ); button_dir->set_size_request( 80, 30 ); button_quit->set_size_request( 80, 30 ); button_process->set_size_request( 80, 30 ); m_button_rename->set_size_request( 80, 30 ); m_button_db_add->set_size_request( 80, 30 ); // set default pattern m_entry_pattern->set_text( default_pattern ); language_map = getLangs(); // put languages in combo box { auto model = Gtk::ListStore::create( m_columns_language ); m_combo_language->set_model( model ); for ( const auto &x : language_map ) { auto row = *( model->append() ); row[m_columns_language.m_col_code] = x.first; row[m_columns_language.m_col_language] = x.second; if ( x.first == "en" ) m_combo_language->set_active( row ); } } { auto model = Gtk::ListStore::create( m_columns_show ); m_combo_possible->set_model( model ); } // set column to be shown in comboboxes m_combo_language->pack_start( m_columns_language.m_col_language ); m_combo_possible->pack_start( m_columns_show.m_col_show ); // set dimensions m_combo_language->set_size_request( 120, 30 ); m_combo_possible->set_size_request( 200, 30 ); // set signals button_dir->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::chooseFile ) ); button_quit->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::quit ) ); button_process->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::process ) ); button_pattern->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::patternHelp ) ); m_button_rename->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::getNames ) ); m_button_db_add->signal_clicked().connect( sigc::mem_fun( *this, &MainWindow::dbAdd ) ); m_entry_show->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::process ) ); m_entry_dir->signal_activate().connect( sigc::mem_fun( *this, &MainWindow::process ) ); // show everything except possible shows and items related to them show_all_children(); m_check_linux->set_active( true ); m_label_possible->hide(); m_combo_possible->hide(); m_button_rename->hide(); m_button_db_add->hide(); } void MainWindow::dbUpdate() { auto *pw = new ProgressWindow; app->add_window( *pw ); auto app_ptr = app; pw->signal_hide().connect( [pw, app_ptr]() { app_ptr->remove_window( *pw ); delete pw; } ); pw->show(); std::thread t( updateDB, !m_check_linux->get_active(), pw ); t.detach(); } void MainWindow::dbClean() { auto *pw = new ProgressWindow; app->add_window( *pw ); auto app_ptr = app; pw->signal_hide().connect( [pw, app_ptr]() { app_ptr->remove_window( *pw ); delete pw; } ); pw->show(); std::thread t( cleanDB, pw ); t.detach(); } void MainWindow::dbRefresh() { auto *pw = new ProgressWindow; app->add_window( *pw ); auto app_ptr = app; pw->signal_hide().connect( [pw, app_ptr]() { app_ptr->remove_window( *pw ); delete pw; } ); pw->show(); std::thread t( refreshDB, !m_check_linux->get_active(), pw ); t.detach(); } void MainWindow::dbAdd() { // check required field is filled out if ( m_entry_dir->get_text().empty() ) { Gtk::MessageDialog dialog( *this, _( DIR_EMPTY ) ); dialog.run(); return; } // check directory exists if ( !FSLib::isDirectory( m_entry_dir->get_text() ) ) { Gtk::MessageDialog dialog( *this, _( DIR_EMPTY ) ); dialog.run(); return; } auto iter = m_combo_possible->get_active(); std::string show = static_cast< Glib::ustring >( ( *iter )[m_columns_show.m_col_show] ); std::string language_code = static_cast< Glib::ustring >( ( *m_combo_language->get_active() )[m_columns_language.m_col_code] ); std::string show_id = static_cast< Glib::ustring >( ( *iter )[m_columns_show.m_col_id] ); auto *pw = new ProgressWindow; app->add_window( *pw ); auto app_ptr = app; pw->signal_hide().connect( [pw, app_ptr]() { app_ptr->remove_window( *pw ); delete pw; } ); pw->show(); std::thread t( addToDB, std::move( show ), m_entry_dir->get_text(), std::move( language_code ), std::move( show_id ), getDBPattern(), !m_check_linux->get_active(), m_check_dvd->get_active(), pw ); t.detach(); } void MainWindow::dbManage() { auto *dbWindow = new DatabaseWindow( !m_check_linux->get_active(), language_map, app ); app->add_window( *dbWindow ); auto app_ptr = app; dbWindow->signal_hide().connect( [dbWindow, app_ptr]() { app_ptr->remove_window( *dbWindow ); delete dbWindow; } ); dbWindow->show(); } void MainWindow::dbPattern() { // Create a custom dialog box for pattern change std::unique_ptr< Gtk::Dialog > dialog( new Gtk::Dialog( _( CHANGE_DB_PATTERN ), *this ) ); dialog->set_default_size( 350, 0 ); auto content = dialog->get_content_area(); auto pattern = getDBPattern(); std::unique_ptr< Gtk::Entry > pattern_entry( new Gtk::Entry ); content->pack_start( *pattern_entry ); dialog->add_button( "_" + _( CANCEL ), Gtk::RESPONSE_CANCEL ); dialog->add_button( "_" + _( OK ), Gtk::RESPONSE_OK ); dialog->show_all_children(); dialog->signal_response().connect( [&pattern, &pattern_entry]( int response ) { if ( response == Gtk::RESPONSE_OK ) pattern = pattern_entry->get_text(); } ); pattern_entry->set_text( pattern ); auto response = dialog->run(); if ( response == Gtk::RESPONSE_OK ) { changeDBPattern( pattern ); } }