GUI for sqlite

This commit is contained in:
zvon 2019-07-12 23:10:40 +02:00
parent 1ced65f632
commit f1063bb14e
12 changed files with 675 additions and 198 deletions

View File

@ -60,12 +60,12 @@ progress.o: progress.cpp
.PHONY: gui
gui: tv_rename_gui
tv_rename_gui: gui.cpp mainwindow.cpp seasonwindow.cpp network.o\
tv_rename_gui: gui.cpp mainwindow.cpp seasonwindow.cpp databasewindow.cpp network.o\
functions_gui.o filesystem_u_gui.o tv_rename_gui.o
$(CXX) $(CFLAGS) -o tv_rename_gui gui.cpp mainwindow.cpp seasonwindow.cpp\
$(CXX) $(CFLAGS) -o tv_rename_gui gui.cpp mainwindow.cpp seasonwindow.cpp databasewindow.cpp\
network.o functions_gui.o filesystem_u_gui.o\
tv_rename_gui.o `pkg-config gtkmm-3.0 --cflags --libs`\
-lcurl -DGUI
-lcurl -lsqlite3 -DGUI
filesystem_u_gui.o: unix/filesystem.cpp
$(CXX) $(CFLAGS) -c unix/filesystem.cpp -o filesystem_u_gui.o -DGUI

98
databasewindow.cpp Normal file
View File

@ -0,0 +1,98 @@
#include <gtkmm/liststore.h>
#include <iostream>
#include "databasewindow.hpp"
#include "functions.hpp"
DatabaseWindow::~DatabaseWindow() {
delete m_button_save;
delete m_button_remove;
delete m_tree_database;
std::cout << "Deleting" << std::endl;
}
DatabaseWindow::DatabaseWindow( bool _linux, Curl &_c ) : c(_c), linux(_linux) {
set_title( "Database" );
set_default_size( 550, 350 );
set_resizable( false );
auto *layout = new Gtk::Layout();
add( *layout );
m_scrolled_window->set_size_request( 550, 300 );
m_scrolled_window->add( *m_tree_database );
// set widgets' location
layout->put( *m_scrolled_window, 0, 0 );
layout->put( *m_button_save, 380, 310 );
layout->put( *m_button_remove, 470, 310 );
// set button texts
m_button_save->set_label( "Save" );
m_button_remove->set_label( "Delete" );
// set dimensions
m_button_save->set_size_request( 80, 30 );
m_button_remove->set_size_request( 80, 30 );
// put languages in combo box
m_model = Gtk::ListStore::create( m_columns_database );
m_tree_database->set_model( m_model );
for( auto &x : dbGetShows() ) {
if( x["SHOW"] == "pattern" )
continue;
auto row = *( m_model->append() );
row[m_columns_database.m_col_id] = std::stoi(x["ID"]);
row[m_columns_database.m_col_show] = x["SHOW"];
row[m_columns_database.m_col_path] = x["PATH"];
row[m_columns_database.m_col_lang] = x["LANGUAGE"];
row[m_columns_database.m_col_url] = x["URL"];
}
m_tree_database->append_column_editable( "Show", m_columns_database.m_col_show );
m_tree_database->append_column_editable( "Path", m_columns_database.m_col_path );
m_tree_database->append_column_editable( "Language", m_columns_database.m_col_lang );
m_tree_database->append_column_editable( "URL", m_columns_database.m_col_url );
m_button_save->signal_clicked().connect(
sigc::mem_fun( *this, &DatabaseWindow::save ) );
m_button_remove->signal_clicked().connect(
sigc::mem_fun( *this, &DatabaseWindow::remove ) );
m_model->signal_row_changed().connect(
sigc::mem_fun( *this, &DatabaseWindow::changed ) );
// show everything
layout->show();
layout->show_all_children();
}
void DatabaseWindow::save() {
for( auto &x : m_model->children() ) {
auto index = static_cast<size_t> ( x[m_columns_database.m_col_id] );
if( changed_rows.find(index) != changed_rows.end() ) {
auto index = static_cast< size_t > ( x[m_columns_database.m_col_id] );
auto show = static_cast< std::string > ( x[m_columns_database.m_col_show] );
auto path = static_cast< std::string > ( x[m_columns_database.m_col_path] );
auto lang = static_cast< std::string > ( x[m_columns_database.m_col_lang] );
auto url = static_cast< std::string > ( x[m_columns_database.m_col_url] );
std::cout << index << " " << show << " " << path << " " << lang << " " << url << std::endl;
changeDB( index, path, lang, url, c );
refreshSingleDB( index, linux, c );
}
}
cleanDB();
changed_rows.clear();
}
void DatabaseWindow::remove() {
auto selected = m_tree_database->get_selection()->get_selected();
std::cout << static_cast< size_t > ( (*selected)[m_columns_database.m_col_id] ) << std::endl;
removeFromDB( static_cast< std::string > ( (*selected)[m_columns_database.m_col_path] ) );
m_model->erase(selected);
}
void DatabaseWindow::changed( const Gtk::TreeModel::Path &/*UNUSED*/, const Gtk::TreeModel::iterator &row ) {
changed_rows.insert( static_cast< size_t > ( (*row)[m_columns_database.m_col_id] ) );
}

62
databasewindow.hpp Normal file
View File

@ -0,0 +1,62 @@
#ifndef GTKMM_DATABASE_WINDOW
#define GTKMM_DATABASE_WINDOW
#include <gtkmm/button.h>
#include <gtkmm/entry.h>
#include <gtkmm/label.h>
#include <gtkmm/layout.h>
#include <gtkmm/liststore.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/treeview.h>
#include <gtkmm/window.h>
#include <set>
#include <unordered_set>
#include <iostream>
#include "network.hpp"
#include "seasonwindow.hpp"
class DatabaseWindow : public Gtk::Window {
public:
DatabaseWindow() = delete;
DatabaseWindow( bool _linux, Curl &_c);
virtual ~DatabaseWindow();
private:
void save();
void remove();
void changed( const Gtk::TreeModel::Path &/*UNUSED*/, const Gtk::TreeModel::iterator &row );
std::unordered_set< size_t > changed_rows;
protected:
Gtk::Button *m_button_save = new Gtk::Button();
Gtk::Button *m_button_remove = new Gtk::Button();
Gtk::TreeView *m_tree_database = new Gtk::TreeView();
Gtk::ScrolledWindow *m_scrolled_window = new Gtk::ScrolledWindow();
class DatabaseColumns : public Gtk::TreeModel::ColumnRecord {
public:
DatabaseColumns() {
add(m_col_id);
add(m_col_show);
add(m_col_path);
add(m_col_lang);
add(m_col_url);
}
Gtk::TreeModelColumn< size_t > m_col_id;
Gtk::TreeModelColumn< std::string > m_col_show;
Gtk::TreeModelColumn< std::string > m_col_path;
Gtk::TreeModelColumn< std::string > m_col_lang;
Gtk::TreeModelColumn< std::string > m_col_url;
};
DatabaseColumns m_columns_database;
Glib::RefPtr< Gtk::ListStore > m_model;
Curl &c;
bool linux;
};
#endif // GTKMM_MAIN_WINDOW

View File

@ -32,13 +32,10 @@ using char_t = char;
namespace FSLib {
#ifndef GUI
bool exists( const string &path );
string canonical( const string &path );
#endif
bool isDirectory( const string &path );
bool rename( const string &file_a, const string &file_b );
string canonical( const string &path );
class Directory {
public:

View File

@ -202,8 +202,47 @@ void iterateFS( std::map< int, std::set< string > > &seasons,
}
}
#ifndef GUI
// following functions are only needed for CLI version
string getDefUrl( string &show, const string &language, Curl &c ) {
std::replace( show.begin(), show.end(), ' ', '+' );
string base_url = TEXT( "https://www.thetvdb.com" );
#ifdef _WIN32
string source_code = utf8_to_wstring(
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
encodeUrl( show ) + TEXT( "&l=" ) + language ) );
#else
string source_code =
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
encodeUrl( show ) + TEXT( "&l=" ) + language );
#endif
size_t order{}, pos{};
std::vector< std::pair< string, string > > urls;
// find all possible shows
while ( true ) {
pos = source_code.find( TEXT( "/ser" ), pos );
if ( pos != string::npos ) {
auto end = source_code.find( TEXT( ">" ), pos );
end--;
auto end2 = source_code.find( TEXT( "<" ), end + 2 );
// store shows in urls, first is name, second is url
urls.emplace_back( source_code.substr( end + 2, end2 - end - 2 ),
source_code.substr( pos, end - pos ) );
cout << ++order << ". " << urls.back().first << std::endl;
pos = end2;
} else {
break;
}
}
cout << "Which TV Show is the right one? ";
cin >> pos;
cin.clear();
cin.ignore( 1, '\n' );
show = urls[pos - 1].first;
return base_url + urls[pos - 1].second;
}
// find all files for provided season in `path` and store it in `files`
void findSeason( std::set< string > &files, int season, const string &path ) {
@ -246,47 +285,8 @@ void findSeasons( std::map< int, std::set< string > > &seasons,
}
}
string getDefUrl( string &show, const string &language, Curl &c ) {
std::replace( show.begin(), show.end(), ' ', '+' );
string base_url = TEXT( "https://www.thetvdb.com" );
#ifdef _WIN32
string source_code = utf8_to_wstring(
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
encodeUrl( show ) + TEXT( "&l=" ) + language ) );
#else
string source_code =
c.execute( TEXT( "https://www.thetvdb.com/search?q=" ) +
encodeUrl( show ) + TEXT( "&l=" ) + language );
#endif
size_t order{}, pos{};
std::vector< std::pair< string, string > > urls;
// find all possible shows
while ( true ) {
pos = source_code.find( TEXT( "/ser" ), pos );
if ( pos != string::npos ) {
auto end = source_code.find( TEXT( ">" ), pos );
end--;
auto end2 = source_code.find( TEXT( "<" ), end + 2 );
// store shows in urls, first is name, second is url
urls.emplace_back( source_code.substr( end + 2, end2 - end - 2 ),
source_code.substr( pos, end - pos ) );
cout << ++order << ". " << urls.back().first << std::endl;
pos = end2;
} else {
break;
}
}
cout << "Which TV Show is the right one? ";
cin >> pos;
cin.clear();
cin.ignore( 1, '\n' );
show = urls[pos - 1].first;
return base_url + urls[pos - 1].second;
}
#ifndef GUI
// following functions are only needed for CLI version
void printHelp() {
cout << "Usage:" << std::endl;
@ -579,8 +579,6 @@ string compilePattern( const string &pattern, int season, int episode,
return output;
}
#ifndef GUI
#ifdef WIN32
std::wstring getDBName() {
@ -609,7 +607,7 @@ string sanitize( const string &str ) {
return ret;
}
void prepareDB() {
void prepareDB( const std::string &_pattern ) {
SQLite::Database db{};
try {
db.open( getDBName(), SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE );
@ -624,18 +622,34 @@ void prepareDB() {
db.exec( "CREATE TABLE IF NOT EXISTS EPISODES (SHOWID INTEGER NOT NULL,"
"PATH TEXT NOT NULL UNIQUE, FOREIGN KEY(SHOWID) "
"REFERENCES SHOWS(ID));" );
cout << "Insert name pattern for database:" << std::endl;
string pattern;
std::getline( cin, pattern );
const string *pattern;
if( _pattern.empty() ) {
cout << "Insert name pattern for database:" << std::endl;
auto *p = new string;
std::getline( cin, *p );
pattern = p;
} else {
pattern = &_pattern;
}
db.exec( TEXT( "INSERT INTO SHOWS ( URL, SHOW, PATH, LANGUAGE ) "
"VALUES ( 'pattern', 'pattern', '" ) +
sanitize( pattern ) + TEXT( "', 'pattern' );" ) );
"VALUES ( 'pattern', 'pattern', '" ) + sanitize( *pattern )
+ TEXT( "', 'pattern' );" ) );
if( pattern != &_pattern ) {
delete pattern;
}
}
#ifndef GUI
void addToDB( string &show, const string &path, const string &language,
bool linux, Curl &c ) {
if ( !FSLib::exists( getDBName() ) )
prepareDB();
#else
void addToDB( string &show, const string &path, const string &language,
const string &url, const string &pattern, bool linux, Curl &c ) {
if ( !FSLib::exists( getDBName() ) )
prepareDB( pattern );
#endif
SQLite::Database db{};
auto absolute = FSLib::canonical( path );
try {
@ -644,17 +658,21 @@ void addToDB( string &show, const string &path, const string &language,
cerr << "Can't open database, make sure it exists" << std::endl;
throw e;
}
#ifndef GUI
auto url = getDefUrl( show, language, c );
#endif
string show_id{};
string pattern{};
db.exec( TEXT( "INSERT OR IGNORE INTO SHOWS ( URL, SHOW, PATH, LANGUAGE ) "
"VALUES ( '" ) +
sanitize( url ) + TEXT( "', '" ) + sanitize( show ) +
TEXT( "', '" ) + sanitize( absolute ) + TEXT( "', '" ) +
sanitize( language ) + TEXT( "' );" ) );
show_id = std::to_string( db.lastRowID() );
#ifndef GUI
string pattern{};
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE URL == 'pattern';" ),
pattern );
#endif
std::map< int, std::set< string > > seasons;
// get all seasons and episodes
@ -665,14 +683,20 @@ void addToDB( string &show, const string &path, const string &language,
#ifdef WIN32
cout << std::endl;
#endif
#ifndef GUI
ProgressBar::print( 0 );
#endif
for ( const auto &x : seasons ) {
singleSeason( absolute, show, x.first, url, language, pattern, linux,
true, c, &x.second, false );
i++;
#ifndef GUI
ProgressBar::print( ( i * 100 ) / size );
#endif
}
#ifndef GUI
cout << std::endl;
#endif
seasons.clear();
iterateFS( seasons, absolute );
size = seasons.size();
@ -682,7 +706,9 @@ void addToDB( string &show, const string &path, const string &language,
#ifdef WIN32
cout << std::endl;
#endif
#ifndef GUI
ProgressBar::print( 0 );
#endif
for ( auto &season : seasons ) {
for ( auto &episode : season.second ) {
db.exec( TEXT( "INSERT OR IGNORE INTO EPISODES ( SHOWID, PATH ) "
@ -691,9 +717,13 @@ void addToDB( string &show, const string &path, const string &language,
TEXT( "' );" ) );
}
i++;
#ifndef GUI
ProgressBar::print( ( i * 100 ) / size );
#endif
}
#ifndef GUI
cout << std::endl;
#endif
}
void cleanUpLine() {
@ -756,11 +786,15 @@ void refreshDB( bool linux, Curl &c ) {
#ifdef WIN32
cout << std::endl;
#endif
#ifndef GUI
ProgressBar::print( 0 );
#endif
std::map< int, std::set< string > > seasons;
// get all season number from this directory and subdirectories
iterateFS( seasons, show[TEXT( "PATH" )] );
#ifndef GUI
auto size = seasons.size();
#endif
size_t i{};
for ( const auto &x : seasons ) {
singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )],
@ -768,10 +802,14 @@ void refreshDB( bool linux, Curl &c ) {
show[TEXT( "LANGUAGE" )], pattern, linux, true, c,
&x.second, false );
i++;
#ifndef GUI
ProgressBar::print( ( i * 100 ) / size );
#endif
}
#ifndef GUI
ProgressBar::print( 100 );
cout << std::endl;
#endif
seasons.clear();
iterateFS( seasons, show[TEXT( "PATH" )] );
for ( auto &season : seasons ) {
@ -831,9 +869,13 @@ void updateDB( bool linux, Curl &c ) {
}
}
}
#ifndef GUI
ProgressBar::print( 0 );
#endif
if ( !new_eps.empty() ) {
#ifndef GUI
auto size = new_eps.size();
#endif
size_t i{};
for ( const auto &x : new_eps ) {
singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )],
@ -841,7 +883,9 @@ void updateDB( bool linux, Curl &c ) {
show[TEXT( "LANGUAGE" )], pattern, linux, true, c,
&x.second, false );
i++;
#ifndef GUI
ProgressBar::print( ( i * 100 ) / size );
#endif
}
seasons.clear();
iterateFS( seasons, show[TEXT( "PATH" )] );
@ -854,8 +898,10 @@ void updateDB( bool linux, Curl &c ) {
}
}
}
#ifndef GUI
ProgressBar::print( 100 );
cout << std::endl;
#endif
}
}
@ -889,7 +935,7 @@ void cleanDB() {
pattern );
for ( auto &show : shows ) {
if ( !FSLib::exists( show[TEXT( "PATH" )] ) ) {
if ( FSLib::exists( show[TEXT( "PATH" )] ) ) {
continue;
}
std::unordered_set< string > episodes;
@ -930,4 +976,115 @@ void removeFromDB( const string &path ) {
db.exec( TEXT( "DELETE FROM SHOWS WHERE ID == " ) + show_id + TEXT( ";" ) );
}
std::vector< std::unordered_map< std::string, std::string > > dbGetShows() {
SQLite::Database db{};
try {
db.open( getDBName(), SQLite::OPEN_READWRITE );
} catch ( std::exception &e ) {
cerr << "Can't open database, make sure it exists" << std::endl;
throw e;
}
std::vector< std::unordered_map< std::string, std::string > > ret;
db.exec( TEXT( "SELECT * FROM SHOWS;"), ret );
return ret;
}
void changeDB( size_t index, const string &path, const string &language,
const string &url, Curl &c ) {
SQLite::Database db{};
auto absolute = FSLib::canonical( path );
try {
db.open( getDBName(), SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE );
} catch ( std::exception &e ) {
cerr << "Can't open database, make sure it exists" << std::endl;
throw e;
}
#ifndef WIN32
string show_id = std::to_string(index);
#else
string show_id = std::to_wstring(index);
#endif
#ifdef _WIN32
string source_code = utf8_to_wstring(
c.execute( url );
#else
string source_code =
c.execute( url );
#endif
auto pos = source_code.find( "series_title" );
pos = source_code.find( ' ', pos );
pos = source_code.find_first_not_of( " ", pos );
auto end = source_code.find( '<', pos );
end--;
while( source_code[end] == ' ' )
end--;
end++;
auto real_show = source_code.substr(pos, end - pos);
db.exec( TEXT( "UPDATE SHOWS SET URL = '" ) + sanitize( url ) + TEXT("', SHOW = '") + sanitize(real_show) + TEXT("', PATH = '") + sanitize(absolute) + TEXT("', LANGUAGE = '") +
sanitize( language ) + TEXT( "' WHERE ID == " + show_id + ";" ) );
}
void refreshSingleDB( const size_t &index, bool linux, Curl &c ) {
std::vector< std::unordered_map< string, string > > shows;
string show_id = std::to_string( index );
SQLite::Database db{};
try {
db.open( getDBName(), SQLite::OPEN_READWRITE );
} catch ( std::exception &e ) {
cerr << "Can't open database, make sure it exists" << std::endl;
throw e;
}
db.exec( "DELETE FROM EPISODES WHERE SHOWID == " + show_id + ";" );
db.exec( TEXT( "SELECT URL, SHOW, PATH, LANGUAGE FROM SHOWS WHERE ID == " + show_id + ";" ), shows );
std::unordered_map< string, string > &show = shows[0];
string pattern{};
db.exec( TEXT( "SELECT PATH FROM SHOWS WHERE URL == 'pattern';" ),
pattern );
cout << "Refreshing " << show[TEXT( "SHOW" )] << std::endl << std::endl << std::endl;
#ifdef WIN32
cout << std::endl;
#endif
if ( FSLib::exists( show[TEXT( "PATH" )] ) ) {
#ifdef WIN32
cout << std::endl;
#endif
#ifndef GUI
ProgressBar::print( 0 );
#endif
std::map< int, std::set< string > > seasons;
// get all season number from this directory and subdirectories
iterateFS( seasons, show[TEXT( "PATH" )] );
#ifndef GUI
auto size = seasons.size();
#endif
size_t i{};
for ( const auto &x : seasons ) {
singleSeason( show[TEXT( "PATH" )], show[TEXT( "SHOW" )],
x.first, show[TEXT( "URL" )],
show[TEXT( "LANGUAGE" )], pattern, linux, true, c,
&x.second, false );
i++;
#ifndef GUI
ProgressBar::print( ( i * 100 ) / size );
#endif
}
#ifndef GUI
ProgressBar::print( 100 );
cout << std::endl;
#endif
seasons.clear();
iterateFS( seasons, show[TEXT( "PATH" )] );
for ( auto &season : seasons ) {
for ( auto &episode : season.second ) {
db.exec( TEXT( "INSERT INTO EPISODES ( SHOWID, PATH ) "
"VALUES ( " ) +
show_id + TEXT( ", '" ) +
sanitize( episode ) + TEXT( "' );" ) );
}
}
}
}

View File

@ -5,6 +5,7 @@
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#ifdef GUI
@ -28,13 +29,13 @@ using char_t = char;
#endif
#ifndef GUI
// CLI functions
string getDefUrl( string &show, const string &language, Curl &c );
void findSeason( std::set< string > &files, int season, const string &path );
void findSeasons( std::map< int, std::set< string > > &seasons,
const string &path, const std::set< int > &season_numbers );
#ifndef GUI
// CLI functions
void printHelp();
bool searchSpecificSeason( const char_t *const path, const string &number );
@ -57,16 +58,23 @@ getPossibleShows( string show, const string &language, Curl &c );
string userHome();
void prepareDB( const std::string &_pattern = "" );
#ifndef GUI
void prepareDB();
void addToDB( string &show, const string &path, const string &language,
bool linux, Curl &c );
#else
void addToDB( string &show, const string &path, const string &language,
const string &url, const string &pattern, bool linux, Curl &c );
std::vector< std::unordered_map< std::string, std::string > > dbGetShows();
#endif
void removeFromDB( const string &path );
void changeDBPattern( const string &pattern );
void refreshDB( bool linux, Curl &c );
void updateDB( bool linux, Curl &c );
void cleanDB();
#endif
void changeDB( size_t index, const string &path, const string &language,
const string &url, Curl &c);
void refreshSingleDB( const size_t &index, bool linux, Curl &c );
void iterateFS( std::map< int, std::set< string > > &seasons,
const string &path );

View File

@ -2,12 +2,14 @@
#include <gtkmm/filechooserdialog.h>
#include <gtkmm/liststore.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/textview.h>
#include <iostream>
#include "filesystem.hpp"
#include "functions.hpp"
#include "mainwindow.hpp"
#include "databasewindow.hpp"
#include "tv_rename.hpp"
constexpr std::array< const char *, 46 > languages{
@ -32,7 +34,7 @@ void MainWindow::chooseFile() {
switch ( result ) {
case Gtk::RESPONSE_OK:
m_entry_dir.set_text( dialog.get_filename() );
m_entry_dir->set_text( dialog.get_filename() );
std::cout << dialog.get_filename() << std::endl;
break;
case Gtk::RESPONSE_CANCEL:
@ -69,7 +71,7 @@ void MainWindow::patternHelp() {
void MainWindow::process() {
// check required field is filled out
if ( m_entry_show.get_text().empty() ) {
if ( m_entry_show->get_text().empty() ) {
Gtk::MessageDialog dialog( *this, "Show field is empty" );
dialog.run();
return;
@ -77,11 +79,11 @@ void MainWindow::process() {
// language code
language_code =
( *m_combo_language.get_active() )[m_columns_language.m_col_code];
( *m_combo_language->get_active() )[m_columns_language.m_col_code];
// fill up m_combo_possible with possible tv shows
auto possible_shows = getPossibleShows(
std::string( m_entry_show.get_text() ), language_code, c );
std::string( m_entry_show->get_text() ), language_code, c );
// if no possible shows were found, tell the user
if ( possible_shows.size() == 0 ) {
@ -92,20 +94,21 @@ void MainWindow::process() {
}
// show widgets
m_label_possible.show();
m_button_get_names.show();
m_combo_possible.show();
m_label_possible->show();
m_button_rename->show();
m_button_db_add->show();
m_combo_possible->show();
// fill up combo box with results from thetvdb
auto model = Gtk::ListStore::create( m_columns_url );
m_combo_possible.set_model( model );
m_combo_possible->set_model( model );
auto row = *( model->append() );
row[m_columns_url.m_col_show] = possible_shows[0].first;
row[m_columns_url.m_col_url] = possible_shows[0].second;
m_combo_possible.set_active( row );
m_combo_possible->set_active( row );
for ( size_t i = 1; i < possible_shows.size(); i++ ) {
auto row = *( model->append() );
@ -116,19 +119,19 @@ void MainWindow::process() {
void MainWindow::getNames() {
// check required field is filled out
if ( m_entry_dir.get_text().empty() ) {
if ( m_entry_dir->get_text().empty() ) {
Gtk::MessageDialog dialog( *this, "Directory field is empty" );
dialog.run();
return;
}
// check directory exists
if ( !FSLib::isDirectory( m_entry_dir.get_text() ) ) {
if ( !FSLib::isDirectory( m_entry_dir->get_text() ) ) {
Gtk::MessageDialog dialog( *this, "Directory doesn't exist" );
dialog.run();
return;
}
path = m_entry_dir.get_text();
path = m_entry_dir->get_text();
selected.clear();
files.clear();
@ -144,7 +147,7 @@ void MainWindow::getNames() {
// create a window with possible seasons to rename
// store selected seasons in `selected`
sw = new SeasonWindow( options, selected );
sw.reset( new SeasonWindow( options, selected ) );
sw->signal_hide().connect(
sigc::mem_fun( *this, &MainWindow::finishedSelection ) );
@ -173,9 +176,8 @@ void renameFiles(
void MainWindow::finishedSelection() {
// remove created SeasonWindow and delete it from memory
app->remove_window( *sw );
delete sw;
auto iter = m_combo_possible.get_active();
auto iter = m_combo_possible->get_active();
// debug output
std::cout << ( *iter )[m_columns_url.m_col_show] << " " << language_code
@ -190,7 +192,7 @@ void MainWindow::finishedSelection() {
std::cout << "https://www.thetvdb.com" << url << std::endl;
std::string input_pattern = m_entry_pattern.get_text();
std::string input_pattern = m_entry_pattern->get_text();
// store pattern to cache if it's different from default
if ( input_pattern != default_pattern ) {
@ -206,29 +208,36 @@ void MainWindow::finishedSelection() {
static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_show] ),
x, "https://www.thetvdb.com" + url, language_code,
( input_pattern.empty() ? default_pattern : input_pattern ),
!m_check_linux.get_active(), c, files[x] );
!m_check_linux->get_active(), c, files[x] );
if ( renamed_files.empty() )
continue;
// if trust checkbox is ticked, rename files
if ( m_check_trust.get_active() ) {
if ( m_check_trust->get_active() ) {
renameFiles( renamed_files );
continue;
}
// create a custom dialog box with textview of new episode names
Gtk::Dialog dialog( "Rename confirmation", *this );
auto content = dialog.get_content_area();
std::unique_ptr<Gtk::Dialog> dialog( new Gtk::Dialog( "Rename confirmation", *this ) );
dialog->set_default_size( 550, 350 );
dialog->set_resizable( false );
auto content = dialog->get_content_area();
Gtk::TextView tx;
content->pack_start( tx );
tx.set_editable( false );
dialog.add_button( "_No", Gtk::RESPONSE_CANCEL );
dialog.add_button( "_Yes", Gtk::RESPONSE_OK );
tx.show();
std::unique_ptr<Gtk::ScrolledWindow> sw( new Gtk::ScrolledWindow );
std::unique_ptr<Gtk::TextView> tx( new Gtk::TextView );
content->pack_start( *sw );
sw->add( *tx );
tx->set_editable( false );
tx->set_cursor_visible( false );
auto buff = tx.get_buffer();
dialog->add_button( "_No", Gtk::RESPONSE_CANCEL );
dialog->add_button( "_Yes", Gtk::RESPONSE_OK );
sw->show();
tx->show();
auto buff = tx->get_buffer();
buff->place_cursor( buff->begin() );
buff->insert_at_cursor( renamed_files[0].second.first.c_str() );
buff->insert_at_cursor( " --> " );
@ -241,7 +250,7 @@ void MainWindow::finishedSelection() {
buff->insert_at_cursor( renamed_files[i].second.second.c_str() );
}
auto response = dialog.run();
auto response = dialog->run();
// if user clicked "Yes" in dialog, rename files
switch ( response ) {
@ -253,11 +262,29 @@ void MainWindow::finishedSelection() {
}
}
MainWindow::~MainWindow() {
auto children = get_children();
size_t max = children.size();
size_t index{};
while( index < max ) {
if( auto *p = dynamic_cast<Gtk::Container*>(children[index]) ) {
auto temp = p->get_children();
children.insert( children.end(), temp.begin(), temp.end() );
max = children.size();
}
index++;
}
std::cout << children.size() << std::endl;
for( int i = max - 1; i >= 0; i-- ) {
delete children[i];
}
}
MainWindow::MainWindow( const Glib::RefPtr< Gtk::Application > &ptr )
: app( ptr ) {
set_title( "TV Rename" );
set_default_size( 400, 310 );
set_default_size( 400, 345 );
set_resizable( false );
{
@ -270,68 +297,136 @@ MainWindow::MainWindow( const Glib::RefPtr< Gtk::Application > &ptr )
}
}
add( m_layout );
auto *box = new Gtk::Box(Gtk::ORIENTATION_VERTICAL);
auto *menu = new Gtk::MenuBar();
auto *layout = new Gtk::Layout();
add( *box );
box->pack_start(*menu, false, false);
box->pack_start(*layout, true, true);
auto *item = new Gtk::MenuItem();
auto *submenu = new Gtk::Menu();
menu->append(*item);
// File menu
item->set_label("File");
item->set_submenu(*submenu);
// Exit item for File menu
item = new Gtk::MenuItem();
item->set_label("Exit");
item->signal_activate().connect(
sigc::mem_fun( *this, &MainWindow::quit ) );
submenu->append(*item);
// Database menu
item = new Gtk::MenuItem();
submenu = new Gtk::Menu();
item->set_label("Database");
item->set_submenu(*submenu);
menu->append(*item);
// Update database
item = new Gtk::MenuItem();
item->set_label("Update database");
item->signal_activate().connect(
sigc::mem_fun( *this, &MainWindow::dbUpdate ) );
submenu->append(*item);
// Refresh database
item = new Gtk::MenuItem();
item->set_label("Refresh database");
item->signal_activate().connect(
sigc::mem_fun( *this, &MainWindow::dbRefresh ) );
submenu->append(*item);
// Clean database
item = new Gtk::MenuItem();
item->set_label("Clean database");
item->signal_activate().connect(
sigc::mem_fun( *this, &MainWindow::dbClean ) );
submenu->append(*item);
// Manage database
item = new Gtk::MenuItem();
item->set_label("Manage database");
item->signal_activate().connect(
sigc::mem_fun( *this, &MainWindow::dbManage ) );
submenu->append(*item);
// set widgets' location
m_layout.put( m_label_show, 5, 5 );
m_layout.put( m_label_language, 190, 5 );
m_layout.put( m_entry_show, 5, 25 );
m_layout.put( m_combo_language, 190, 25 );
m_layout.put( m_label_dir, 5, 60 );
m_layout.put( m_entry_dir, 5, 80 );
m_layout.put( m_button_dir, 190, 80 );
m_layout.put( m_label_pattern, 5, 115 );
m_layout.put( m_entry_pattern, 5, 135 );
m_layout.put( m_button_pattern, 190, 135 );
m_layout.put( m_check_linux, 95, 169 );
m_layout.put( m_button_process, 5, 173 );
m_layout.put( m_check_trust, 95, 187 );
m_layout.put( m_label_possible, 5, 210 );
m_layout.put( m_combo_possible, 5, 230 );
m_layout.put( m_button_get_names, 5, 265 );
m_layout.put( m_button_quit, 315, 275 );
layout->put( *m_label_show, 5, 5 );
layout->put( *m_label_language, 190, 5 );
layout->put( *m_entry_show, 5, 25 );
layout->put( *m_combo_language, 190, 25 );
layout->put( *m_label_dir, 5, 60 );
layout->put( *m_entry_dir, 5, 80 );
layout->put( *m_button_dir, 190, 80 );
layout->put( *m_label_pattern, 5, 115 );
layout->put( *m_entry_pattern, 5, 135 );
layout->put( *m_button_pattern, 190, 135 );
layout->put( *m_check_linux, 95, 169 );
layout->put( *m_button_process, 5, 173 );
layout->put( *m_check_trust, 95, 187 );
layout->put( *m_label_possible, 5, 210 );
layout->put( *m_combo_possible, 5, 230 );
layout->put( *m_button_rename, 5, 270 );
layout->put( *m_button_db_add, 95, 270 );
layout->put( *m_button_quit, 315, 280 );
// set button texts
m_button_process.set_label( "Process" );
m_button_get_names.set_label( "Get names" );
m_button_quit.set_label( "Quit" );
m_button_dir.set_label( "Choose directory" );
m_button_pattern.set_label( "Pattern help" );
m_check_linux.set_label( "Replace windows-illegal characters" );
m_check_trust.set_label( "Don't ask for rename confirmation" );
m_button_process->set_label( "Process" );
m_button_rename->set_label( "Rename" );
m_button_db_add->set_label( "Add to database" );
m_button_quit->set_label( "Quit" );
m_button_dir->set_label( "Choose directory" );
m_button_pattern->set_label( "Pattern help" );
m_check_linux->set_label( "Replace windows-illegal characters" );
m_check_trust->set_label( "Don't ask for rename confirmation" );
// set label texts
m_label_show.set_label( "Show:" );
m_label_language.set_label( "Language:" );
m_label_possible.set_label( "Possible shows:" );
m_label_dir.set_label( "Directory:" );
m_label_pattern.set_label( "Pattern:" );
m_label_show->set_label( "Show:" );
m_label_language->set_label( "Language:" );
m_label_possible->set_label( "Possible shows:" );
m_label_dir->set_label( "Directory:" );
m_label_pattern->set_label( "Pattern:" );
// set dimensions
m_combo_language.set_size_request( 120 );
m_combo_possible.set_size_request( 200 );
m_combo_language->set_size_request( 120 );
m_combo_possible->set_size_request( 200 );
m_entry_show.set_size_request( 170, 30 );
m_entry_dir.set_size_request( 170, 30 );
m_entry_show->set_size_request( 170, 30 );
m_entry_dir->set_size_request( 170, 30 );
m_button_dir.set_size_request( 80, 30 );
m_button_quit.set_size_request( 80, 30 );
m_button_process.set_size_request( 80, 30 );
m_button_get_names.set_size_request( 80, 30 );
m_button_dir->set_size_request( 80, 30 );
m_button_quit->set_size_request( 80, 30 );
m_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 );
m_entry_pattern->set_text( default_pattern );
// put languages in combo box
{
auto model = Gtk::ListStore::create( m_columns_language );
m_combo_language.set_model( model );
m_combo_language->set_model( model );
auto row = *( model->append() );
row[m_columns_language.m_col_code] = "en";
row[m_columns_language.m_col_language] = "English";
m_combo_language.set_active( row );
m_combo_language->set_active( row );
for ( size_t i = 2; i < languages.size(); i += 2 ) {
row = *( model->append() );
@ -341,40 +436,98 @@ MainWindow::MainWindow( const Glib::RefPtr< Gtk::Application > &ptr )
}
// 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_url.m_col_show );
m_combo_language->pack_start( m_columns_language.m_col_language );
m_combo_possible->pack_start( m_columns_url.m_col_show );
// set signals
m_button_dir.signal_clicked().connect(
m_button_dir->signal_clicked().connect(
sigc::mem_fun( *this, &MainWindow::chooseFile ) );
m_button_quit.signal_clicked().connect(
m_button_quit->signal_clicked().connect(
sigc::mem_fun( *this, &MainWindow::quit ) );
m_button_process.signal_clicked().connect(
m_button_process->signal_clicked().connect(
sigc::mem_fun( *this, &MainWindow::process ) );
m_button_get_names.signal_clicked().connect(
m_button_rename->signal_clicked().connect(
sigc::mem_fun( *this, &MainWindow::getNames ) );
m_entry_show.signal_activate().connect(
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(
m_entry_dir->signal_activate().connect(
sigc::mem_fun( *this, &MainWindow::process ) );
m_button_pattern.signal_clicked().connect(
m_button_pattern->signal_clicked().connect(
sigc::mem_fun( *this, &MainWindow::patternHelp ) );
// show everything except possible shows and items related to them
m_layout.show();
m_label_show.show();
m_label_language.show();
m_entry_show.show();
m_entry_dir.show();
m_combo_language.show();
m_button_process.show();
m_button_quit.show();
m_button_dir.show();
m_check_linux.show();
m_check_linux.set_active( true );
m_check_trust.show();
m_button_pattern.show();
m_entry_pattern.show();
m_label_pattern.show();
m_label_dir.show();
box->show();
menu->show();
menu->show_all_children();
layout->show();
m_label_show->show();
m_label_language->show();
m_entry_show->show();
m_entry_dir->show();
m_combo_language->show();
m_button_process->show();
m_button_quit->show();
m_button_dir->show();
m_check_linux->show();
m_check_linux->set_active( true );
m_check_trust->show();
m_button_pattern->show();
m_entry_pattern->show();
m_label_pattern->show();
m_label_dir->show();
}
void MainWindow::dbUpdate() {
updateDB( !m_check_linux->get_active(), c );
}
void MainWindow::dbClean() {
cleanDB();
}
void MainWindow::dbRefresh() {
refreshDB( !m_check_linux->get_active(), c );
}
void MainWindow::dbAdd() {
// check required field is filled out
if ( m_entry_dir->get_text().empty() ) {
Gtk::MessageDialog dialog( *this, "Directory field is empty" );
dialog.run();
return;
}
// check directory exists
if ( !FSLib::isDirectory( m_entry_dir->get_text() ) ) {
Gtk::MessageDialog dialog( *this, "Directory doesn't exist" );
dialog.run();
return;
}
auto iter = m_combo_possible->get_active();
std::string input_pattern = m_entry_pattern->get_text();
std::string show = static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_show] );
std::string language_code = static_cast< Glib::ustring >( ( *m_combo_language->get_active() )[m_columns_language.m_col_code] );
std::string url = "https://www.thetvdb.com" + static_cast< Glib::ustring >( ( *iter )[m_columns_url.m_col_url] );
std::cout << show << " " << language_code << " " << url << std::endl;
addToDB( show, m_entry_dir->get_text(), language_code, url,
m_entry_pattern->get_text(), !m_check_linux->get_active(), c );
}
void MainWindow::dbManage() {
auto *dbWindow = new DatabaseWindow( !m_check_linux->get_active(), c );
app->add_window( *dbWindow );
auto app_ptr = app;
dbWindow->signal_hide().connect(
[dbWindow, app_ptr](){
app_ptr->remove_window( *dbWindow );
delete dbWindow;
});
dbWindow->show();
}

View File

@ -1,14 +1,19 @@
#ifndef GTKMM_MAIN_WINDOW
#define GTKMM_MAIN_WINDOW
#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/checkbutton.h>
#include <gtkmm/combobox.h>
#include <gtkmm/container.h>
#include <gtkmm/entry.h>
#include <gtkmm/label.h>
#include <gtkmm/layout.h>
#include <gtkmm/menubar.h>
#include <gtkmm/menuitem.h>
#include <gtkmm/window.h>
#include <set>
#include <iostream>
#include "network.hpp"
#include "seasonwindow.hpp"
@ -16,7 +21,7 @@
class MainWindow : public Gtk::Window {
public:
MainWindow( const Glib::RefPtr< Gtk::Application > &ptr );
virtual ~MainWindow() = default;
virtual ~MainWindow();
private:
void quit();
@ -25,31 +30,35 @@ private:
void finishedSelection();
void chooseFile();
void patternHelp();
void dbUpdate();
void dbClean();
void dbRefresh();
void dbAdd();
void dbManage();
protected:
Gtk::Button m_button_dir;
Gtk::Button m_button_get_names;
Gtk::Button m_button_quit;
Gtk::Button m_button_process;
Gtk::Button m_button_pattern;
Gtk::Button *m_button_dir = new Gtk::Button();
Gtk::Button *m_button_rename = new Gtk::Button();
Gtk::Button *m_button_db_add = new Gtk::Button();
Gtk::Button *m_button_quit = new Gtk::Button();
Gtk::Button *m_button_process = new Gtk::Button();
Gtk::Button *m_button_pattern = new Gtk::Button();
Gtk::CheckButton m_check_linux;
Gtk::CheckButton m_check_trust;
Gtk::CheckButton *m_check_linux = new Gtk::CheckButton();
Gtk::CheckButton *m_check_trust = new Gtk::CheckButton();
Gtk::ComboBox m_combo_language;
Gtk::ComboBox m_combo_possible;
Gtk::ComboBox *m_combo_language = new Gtk::ComboBox();
Gtk::ComboBox *m_combo_possible = new Gtk::ComboBox();
Gtk::Entry m_entry_show;
Gtk::Entry m_entry_dir;
Gtk::Entry m_entry_pattern;
Gtk::Entry *m_entry_show = new Gtk::Entry();
Gtk::Entry *m_entry_dir = new Gtk::Entry();
Gtk::Entry *m_entry_pattern = new Gtk::Entry();
Gtk::Label m_label_language;
Gtk::Label m_label_possible;
Gtk::Label m_label_show;
Gtk::Label m_label_dir;
Gtk::Label m_label_pattern;
Gtk::Layout m_layout;
Gtk::Label *m_label_language = new Gtk::Label();
Gtk::Label *m_label_possible = new Gtk::Label();
Gtk::Label *m_label_show = new Gtk::Label();
Gtk::Label *m_label_dir = new Gtk::Label();
Gtk::Label *m_label_pattern = new Gtk::Label();
Curl c;
@ -59,7 +68,6 @@ protected:
add( m_col_code );
add( m_col_language );
}
Gtk::TreeModelColumn< std::string > m_col_code;
Gtk::TreeModelColumn< std::string > m_col_language;
};
@ -79,7 +87,7 @@ protected:
Glib::RefPtr< Gtk::Application > app;
SeasonWindow *sw;
std::unique_ptr<SeasonWindow> sw{nullptr};
std::vector< int > selected;
std::map< int, std::set< std::string > > files;
std::string path;

View File

@ -11,9 +11,10 @@
#endif
#include "filesystem.hpp"
#ifndef GUI
#include "filesystem.hpp"
#include <iostream>
#include <sstream>
#include <vector>
@ -203,8 +204,6 @@ getRenamedFiles( const string &show, int season, string url,
return renamed_files;
}
#ifndef GUI
void singleSeason( const string &path, string &show, int season, string url,
const string &language, const string &pattern,
const bool &linux, const bool &trust, Curl &c,
@ -253,6 +252,8 @@ void singleSeason( const string &path, string &show, int season, string url,
}
}
#ifndef GUI
void multipleSeasons( const string &path, string &show,
const std::map< int, std::set< string > > &seasons,
const string &language, const string &pattern,

View File

@ -21,6 +21,12 @@ using char_t = char;
#endif
void singleSeason( const string &path, string &show, int season, string url,
const string &language, const string &pattern,
const bool &linux, const bool &trust, Curl &c,
std::set< string > const *files = nullptr,
bool print = true );
#ifdef GUI
std::vector< std::pair< string, std::pair< string, string > > >
@ -30,11 +36,6 @@ getRenamedFiles( const string &show, int season, string url,
#else
void singleSeason( const string &path, string &show, int season, string url,
const string &language, const string &pattern,
const bool &linux, const bool &trust, Curl &c,
std::set< string > const *files = nullptr,
bool print = true );
void multipleSeasons( const string &path, string &show,
const std::set< int > seasons, const string &language,
const string &pattern, const bool &linux,

View File

@ -20,8 +20,6 @@ FSLib::Directory::Iterator::~Iterator() {
closedir( d );
}
#ifndef GUI // these functions aren't needed in GUI
bool FSLib::exists( const string &path ) {
struct stat path_stat;
return stat( path.c_str(), &path_stat ) == 0;
@ -43,8 +41,6 @@ string FSLib::canonical( const string &path ) {
return canonical_string;
}
#endif // ndef GUI
bool FSLib::isDirectory( const string &path ) {
struct stat path_stat;

View File

@ -29,8 +29,6 @@ FSLib::Directory::Iterator::~Iterator() {
// windows.h
FSLib::Directory::Iterator::Iterator( bool ended_ ) : ended( ended_ ) {}
#ifndef GUI // these functions aren't needed in GUI
bool FSLib::exists( const string &path ) {
struct _stat path_stat;
return _wstat( path.c_str(), &path_stat ) == 0;
@ -62,8 +60,6 @@ string FSLib::canonical( const string &path ) {
return canonical_string;
}
#endif // ndef GUI
bool FSLib::isDirectory( const string &path ) {
struct _stat path_stat;