From 7186635f1cfb686e6a7945ac0c4750d3dbfc5ea1 Mon Sep 17 00:00:00 2001 From: zvon Date: Tue, 25 Feb 2020 16:11:25 +0100 Subject: [PATCH] Windows: refactored GUI --- Makefile | 26 +-- windows/databasewindow.cpp | 285 ++++++++++++++++++++++++ windows/databasewindow.hpp | 40 ++++ windows/gui.cpp | 9 + windows/gui_functions.cpp | 196 +++++++++++++++++ windows/gui_functions.hpp | 32 +++ windows/mainwindow.cpp | 431 +++++++++++++++++++++++++++++++++++++ windows/mainwindow.hpp | 42 ++++ windows/patternwindow.cpp | 83 +++++++ windows/patternwindow.hpp | 34 +++ windows/progresswindow.cpp | 69 ++++++ windows/progresswindow.hpp | 27 +++ windows/searchwindow.cpp | 158 ++++++++++++++ windows/searchwindow.hpp | 44 ++++ windows/seasonwindow.cpp | 111 ++++++++++ windows/seasonwindow.hpp | 33 +++ 16 files changed, 1603 insertions(+), 17 deletions(-) create mode 100644 windows/databasewindow.cpp create mode 100644 windows/databasewindow.hpp create mode 100644 windows/gui.cpp create mode 100644 windows/gui_functions.cpp create mode 100644 windows/gui_functions.hpp create mode 100644 windows/mainwindow.cpp create mode 100644 windows/mainwindow.hpp create mode 100644 windows/patternwindow.cpp create mode 100644 windows/patternwindow.hpp create mode 100644 windows/progresswindow.cpp create mode 100644 windows/progresswindow.hpp create mode 100644 windows/searchwindow.cpp create mode 100644 windows/searchwindow.hpp create mode 100644 windows/seasonwindow.cpp create mode 100644 windows/seasonwindow.hpp diff --git a/Makefile b/Makefile index d4bb28d..cc1e464 100644 --- a/Makefile +++ b/Makefile @@ -48,19 +48,14 @@ tv_rename: functions.o filesystem.o network.o tv_rename.o progress.o main.o filesystem.o: unix/filesystem.cpp $(CXX) $(CFLAGS) -c unix/filesystem.cpp -o filesystem.o - functions.o: functions.cpp $(CXX) $(CFLAGS) -c functions.cpp - network.o: unix/network.cpp $(CXX) $(CFLAGS) -c unix/network.cpp - tv_rename.o: tv_rename.cpp $(CXX) $(CFLAGS) -c tv_rename.cpp - progress.o: progress.cpp $(CXX) $(CFLAGS) -c progress.cpp - main.o: main.cpp $(CXX) $(CFLAGS) -c main.cpp @@ -76,13 +71,10 @@ tv_rename_gui: gui.o mainwindow.o seasonwindow.o databasewindow.o\ filesystem_u_gui.o: unix/filesystem.cpp $(CXX) $(CFLAGS) -c unix/filesystem.cpp -o filesystem_u_gui.o -DGUI - functions_gui.o: functions.cpp $(CXX) $(CFLAGS) -c functions.cpp -o functions_gui.o -DGUI - tv_rename_gui.o: tv_rename.cpp $(CXX) $(CFLAGS) -c tv_rename.cpp -o tv_rename_gui.o -DGUI - gui.o: gui.cpp $(CXX) $(CFLAGS) -o $@ -c $^ $(GTKFLAGS) -DGUI mainwindow.o: mainwindow.cpp @@ -113,12 +105,12 @@ tv_rename.exe: tv_rename.cpp functions.cpp windows/filesystem.cpp\ .PHONY: windows_gui windows_gui: tv_rename_gui.exe -tv_rename_gui.exe: tv_rename_gui.res tv_rename_gui.cpp tv_rename.cpp\ - windows/filesystem.cpp functions.cpp windows/network.cpp - $(CXX) -MD -EHsc -Fe"tv_rename_gui" tv_rename_gui.cpp tv_rename.cpp\ - windows/filesystem.cpp functions.cpp windows/network.cpp -D_WIN32\ - -DUNICODE -DGUI -link wininet.lib shlwapi.lib ole32.lib shell32.lib\ - gdi32.lib user32.lib tv_rename_gui.res - -tv_rename_gui.res: tv_rename_gui.rc - rc tv_rename_gui.rc +tv_rename_gui.exe: windows/gui.cpp windows/mainwindow.cpp\ + windows/gui_functions.cpp tv_rename.cpp\ + windows/filesystem.cpp functions.cpp windows/network.cpp\ + progress.cpp sqlite3.c windows/patternwindow.cpp\ + windows/progresswindow.cpp windows/seasonwindow.cpp\ + windows/databasewindow.cpp windows/searchwindow.cpp + $(CXX) -MD -EHsc -Fe"tv_rename_gui" $^ -DGUI -D_WIN32 -DUNICODE\ + -link wininet.lib shlwapi.lib ole32.lib\ + shell32.lib user32.lib gdi32.lib comctl32.lib diff --git a/windows/databasewindow.cpp b/windows/databasewindow.cpp new file mode 100644 index 0000000..2366c8d --- /dev/null +++ b/windows/databasewindow.cpp @@ -0,0 +1,285 @@ +#include "databasewindow.hpp" +#include "../filesystem.hpp" +#include "../functions.hpp" +#include "gui_functions.hpp" +#include "progresswindow.hpp" +#include "searchwindow.hpp" + +#include +#include + +#define ID_LISTVIEW 0x0001 +#define ID_OK_BUTTON 0x0002 +#define ID_CANCEL_BUTTON 0x0003 +#define ID_SAVE_BUTTON 0x0004 +#define ID_LANG_COMBO 0x0005 + +#define SHOW_COLUMN 0 +#define PATH_COLUMN 1 +#define LANG_COLUMN 2 +#define DVD_COLUMN 3 +#define ID_COLUMN 4 +#define LANG_ID_COLUMN 5 +#define TVID_COLUMN 6 + +#define database_width( width ) width - 25 +#define database_height( height ) height - 90 +#define cancel_x( width ) width - 100 +#define cancel_y( height ) height - 73 +#define ok_x( width ) width - 185 +#define ok_y( height ) height - 73 +#define save_x( width ) 5 +#define save_y( height ) height - 73 + +DatabaseWindow *DatabaseWindow::dw = nullptr; + +void toggleDVD( LPNMITEMACTIVATE temp, HWND list_hwnd ) { + auto dvd = getItemText( list_hwnd, temp->iItem, temp->iSubItem ) != L"No"; + if ( dvd ) { + setListViewItemText( list_hwnd, temp->iItem, temp->iSubItem, L"No" ); + } else { + setListViewItemText( list_hwnd, temp->iItem, temp->iSubItem, L"Yes" ); + } +} + +DatabaseWindow::DatabaseWindow( + HINSTANCE hInstance, + std::vector< std::pair< std::wstring, std::wstring > > &languages, + HWND parent_window ) + : parent( parent_window ), hInst( hInstance ), langs( languages ) { + // need to set this here for WM_NOTIFY to work + dw = this; + window = CreateWindowW( L"DatabaseWindow", L"Progress", + WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, + CW_USEDEFAULT, window_width, window_height, parent, + NULL, hInstance, NULL ); + ShowWindow( window, SW_SHOW ); + auto hFont = ( HFONT )GetStockObject( DEFAULT_GUI_FONT ); + EnableWindow( parent, false ); + + INITCOMMONCONTROLSEX icex; // Structure for control initialization. + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx( &icex ); + + list_hwnd = createListView( 5, 5, database_width( window_width ), + database_height( window_height ), ID_LISTVIEW, + window, hFont ); + addColumnToListView( list_hwnd, L"Show", 90, SHOW_COLUMN ); + addColumnToListView( list_hwnd, L"Path", 180, PATH_COLUMN ); + addColumnToListView( list_hwnd, L"Language", 70, LANG_COLUMN ); + addColumnToListView( list_hwnd, L"DVD", 40, DVD_COLUMN ); + // create hidden columns + addColumnToListView( list_hwnd, L"ID", 0, ID_COLUMN ); + addColumnToListView( list_hwnd, L"LangID", 0, LANG_ID_COLUMN ); + addColumnToListView( list_hwnd, L"TVID", 0, TVID_COLUMN ); + + int row{ 0 }; + + for ( auto &x : dbGetShows() ) { + if ( x[L"SHOW"] == L"pattern" ) + continue; + addItemToListView( list_hwnd, row, SHOW_COLUMN, x[L"SHOW"].c_str() ); + addItemToListView( list_hwnd, row, PATH_COLUMN, x[L"PATH"].c_str() ); + addItemToListView( list_hwnd, row, DVD_COLUMN, + x[L"DVD"] == L"1" ? L"Yes" : L"No" ); + addItemToListView( list_hwnd, row, ID_COLUMN, x[L"ID"].c_str() ); + addItemToListView( list_hwnd, row, TVID_COLUMN, x[L"TVID"].c_str() ); + + // lang_id[0] will be index of the language + wchar_t lang_id[2]; + lang_id[1] = '\0'; + for ( wchar_t i = 0; i < ( wchar_t )languages.size(); i++ ) { + if ( x[L"LANGUAGE"] == languages[i].first ) { + addItemToListView( list_hwnd, row, LANG_COLUMN, + languages[i].second.c_str() ); + lang_id[0] = i; + addItemToListView( list_hwnd, row, LANG_ID_COLUMN, lang_id ); + } + } + row++; + } + + cancel_button = createButton( L"Cancel", cancel_x( window_width ), + cancel_y( window_height ), 80, 25, + ID_CANCEL_BUTTON, window, hFont ); + ok_button = + createButton( L"OK", ok_x( window_width ), ok_y( window_height ), 80, + 25, ID_OK_BUTTON, window, hFont ); + save_button = + createButton( L"Save", save_x( window_width ), save_y( window_height ), + 80, 25, ID_SAVE_BUTTON, window, hFont ); + + lang_combo = createCombo( 0, 0, 120, 110, ID_LANG_COMBO, window, hFont ); + ShowWindow( lang_combo, SW_HIDE ); + for ( auto &x : languages ) { + addItemToCombo( lang_combo, x.second.c_str() ); + } + + UpdateWindow( window ); +} + +LRESULT CALLBACK DatabaseWindow::messageHandler( HWND hwnd, UINT umsg, + WPARAM wParam, + LPARAM lParam ) { + switch ( umsg ) { + case WM_CREATE: + centerWindow( hwnd ); + break; + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + case WM_COMMAND: + switch ( LOWORD( wParam ) ) { + case ID_OK_BUTTON: + dw->save(); + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + break; + case ID_CANCEL_BUTTON: + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + break; + case ID_SAVE_BUTTON: + dw->save(); + break; + case ID_LANG_COMBO: + switch ( HIWORD( wParam ) ) { + case CBN_KILLFOCUS: + ShowWindow( dw->lang_combo, SW_HIDE ); + break; + case CBN_SELCHANGE: { + wchar_t lang_id[2]; + lang_id[1] = '\0'; + lang_id[0] = getComboCurSel( dw->lang_combo ); + setListViewItemText( dw->list_hwnd, dw->cur_row, LANG_COLUMN, + dw->langs[lang_id[0]].second.c_str() ); + setListViewItemText( dw->list_hwnd, dw->cur_row, LANG_ID_COLUMN, + lang_id ); + } break; + } + break; + } + break; + case WM_SIZE: { + auto width = LOWORD( lParam ); + auto height = HIWORD( lParam ); + SetWindowPos( dw->list_hwnd, HWND_TOP, 5, 5, database_width( width ), + database_height( height ), 0 ); + SetWindowPos( dw->cancel_button, HWND_TOP, cancel_x( width ), + cancel_y( height ), 80, 25, 0 ); + SetWindowPos( dw->ok_button, HWND_TOP, ok_x( width ), ok_y( height ), + 80, 25, 0 ); + SetWindowPos( dw->save_button, HWND_TOP, save_x( width ), + save_y( height ), 80, 25, 0 ); + UpdateWindow( dw->window ); + } break; + case WM_NOTIFY: + dw->listNotify( lParam ); + break; + } + return DefWindowProcW( hwnd, umsg, wParam, lParam ); +} + +void DatabaseWindow::mainLoop() { + MSG msg; + while ( GetMessage( &msg, NULL, 0, 0 ) ) { + if ( !IsDialogMessage( window, &msg ) ) { + TranslateMessage( &msg ); + dw = this; + DispatchMessage( &msg ); + } + } +} + +void DatabaseWindow::changed( int index ) { + auto path = getItemText( list_hwnd, index, PATH_COLUMN ); + if ( !FSLib::isDirectory( path ) ) { + MessageBox( window, + ( L"Directory '" + path + L"' doesn't exist" ).c_str(), + L"Error", MB_OK | MB_ICONERROR ); + return; + } + changed_rows.push_back( index ); +} + +void DatabaseWindow::save() { + if ( changed_rows.size() == 0 ) + return; + std::unordered_set< size_t > row_indexes; + for ( auto &row : changed_rows ) { + auto index = std::stoi( getItemText( list_hwnd, row, ID_COLUMN ) ); + auto show = getItemText( list_hwnd, row, SHOW_COLUMN ); + auto path = getItemText( list_hwnd, row, PATH_COLUMN ); + auto lang = + langs[getItemText( list_hwnd, row, LANG_ID_COLUMN )[0]].first; + auto show_id = getItemText( list_hwnd, row, TVID_COLUMN ); + bool dvd = getItemText( list_hwnd, row, DVD_COLUMN ) == L"Yes"; + changeDB( index, path, lang, show_id, dvd ); + row_indexes.insert( index ); + } + + ProgressWindow pw( hInst, window ); + std::thread t = + std::thread( refreshSelectDB, row_indexes, false, pw.getWindow() ); + t.detach(); + + pw.mainLoop(); + changed_rows.clear(); +} + +void DatabaseWindow::listNotify( LPARAM lParam ) { + NMHDR &nmh = *( NMHDR * )lParam; + if ( nmh.hwndFrom != dw->list_hwnd ) + return; + switch ( nmh.code ) { + case NM_DBLCLK: { + LPNMITEMACTIVATE temp = ( LPNMITEMACTIVATE )lParam; + if ( temp->iItem == -1 ) + break; + switch ( temp->iSubItem ) { + case SHOW_COLUMN: { + auto lang_id = + getItemText( list_hwnd, temp->iItem, LANG_ID_COLUMN ); + auto show = getItemText( list_hwnd, temp->iItem, SHOW_COLUMN ); + SearchWindow sw( hInst, langs, lang_id[0], show.c_str(), window ); + sw.mainLoop(); + // test if user clicke OK + if ( sw.accepted() ) { + setListViewItemText( list_hwnd, temp->iItem, SHOW_COLUMN, + sw.getShow().c_str() ); + setListViewItemText( list_hwnd, temp->iItem, TVID_COLUMN, + sw.getShowID().c_str() ); + setListViewItemText( list_hwnd, temp->iItem, LANG_COLUMN, + langs[sw.getLangID()].second.c_str() ); + wchar_t langid[2]; + langid[1] = '\0'; + langid[0] = sw.getLangID(); + setListViewItemText( list_hwnd, temp->iItem, LANG_ID_COLUMN, + langid ); + } + } break; + case PATH_COLUMN: { + auto dir = getDir(); + if ( !dir.empty() ) + addItemToListView( list_hwnd, temp->iItem, temp->iSubItem, + dir.c_str() ); + } break; + case LANG_COLUMN: { + RECT item_rect; + cur_row = temp->iItem; + ListView_GetSubItemRect( list_hwnd, temp->iItem, temp->iSubItem, + LVIR_BOUNDS, &item_rect ); + SetWindowPos( lang_combo, HWND_TOP, item_rect.left, item_rect.top, + item_rect.right - item_rect.left, window_height, + NULL ); + ShowWindow( lang_combo, SW_SHOW ); + SetFocus( lang_combo ); + auto id = getItemText( list_hwnd, temp->iItem, LANG_ID_COLUMN ); + SendMessage( lang_combo, CB_SETCURSEL, id[0], NULL ); + } break; + case DVD_COLUMN: + toggleDVD( temp, list_hwnd ); + break; + } + changed( temp->iItem ); + } break; + } +} diff --git a/windows/databasewindow.hpp b/windows/databasewindow.hpp new file mode 100644 index 0000000..1d20ab1 --- /dev/null +++ b/windows/databasewindow.hpp @@ -0,0 +1,40 @@ +#include +#include +#include + +class DatabaseWindow { +public: + DatabaseWindow( + HINSTANCE hInstance, + std::vector< std::pair< std::wstring, std::wstring > > &languages, + HWND parent_window ); + + void mainLoop(); + static LRESULT CALLBACK messageHandler( HWND hwnd, UINT umsg, WPARAM wParam, + LPARAM lParam ); + + ~DatabaseWindow() { + EnableWindow( parent, true ); + SetFocus( parent ); + } + +private: + void listNotify( LPARAM lParam ); + void changed( int indes ); + void save(); + + HWND window; + HWND lang_combo; + HWND list_hwnd; + HWND cancel_button; + HWND ok_button; + HWND save_button; + HWND parent; + HINSTANCE hInst; + int cur_row; + const int window_width{ 350 }; + const int window_height{ 370 }; + std::vector< int > changed_rows; + const std::vector< std::pair< std::wstring, std::wstring > > &langs; + static DatabaseWindow *dw; +}; diff --git a/windows/gui.cpp b/windows/gui.cpp new file mode 100644 index 0000000..45388ae --- /dev/null +++ b/windows/gui.cpp @@ -0,0 +1,9 @@ +#include "mainwindow.hpp" +#include "gui_functions.hpp" + +int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR szCmdLine, int nCmdShow ) { + registerWindowClasses( hInstance ); + MainWindow mw( hInstance, nCmdShow ); + mw.mainLoop(); + return 0; +} diff --git a/windows/gui_functions.cpp b/windows/gui_functions.cpp new file mode 100644 index 0000000..14ac630 --- /dev/null +++ b/windows/gui_functions.cpp @@ -0,0 +1,196 @@ +#define UNICODE +#include "gui_functions.hpp" +#include "databasewindow.hpp" +#include "mainwindow.hpp" +#include "patternwindow.hpp" +#include "progresswindow.hpp" +#include "searchwindow.hpp" +#include "seasonwindow.hpp" + +#include + +void registerWindowClasses( HINSTANCE hInstance ) { + WNDCLASSW wc{}; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpszClassName = L"MainWindow"; + wc.hInstance = hInstance; + wc.hbrBackground = GetSysColorBrush( COLOR_3DFACE ); + wc.lpfnWndProc = MainWindow::messageHandler; + wc.hCursor = LoadCursor( NULL, IDC_ARROW ); + wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); + RegisterClassW( &wc ); + + wc.lpszClassName = L"PatternWindow"; + wc.lpfnWndProc = PatternWindow::messageHandler; + RegisterClassW( &wc ); + + wc.lpszClassName = L"ProgressWindow"; + wc.lpfnWndProc = ProgressWindow::messageHandler; + RegisterClassW( &wc ); + + wc.lpszClassName = L"SeasonWindow"; + wc.lpfnWndProc = SeasonWindow::messageHandler; + RegisterClassW( &wc ); + + wc.lpszClassName = L"DatabaseWindow"; + wc.lpfnWndProc = DatabaseWindow::messageHandler; + RegisterClassW( &wc ); + + wc.lpszClassName = L"SearchWindow"; + wc.lpfnWndProc = SearchWindow::messageHandler; + RegisterClassW( &wc ); +} + +void centerWindow( HWND hwnd ) { + RECT rc = {}; + + GetWindowRect( hwnd, &rc ); + int win_w = rc.right - rc.left; + int win_h = rc.bottom - rc.top; + + int screen_w = GetSystemMetrics( SM_CXSCREEN ); + int screen_h = GetSystemMetrics( SM_CYSCREEN ); + + SetWindowPos( hwnd, HWND_TOP, ( screen_w - win_w ) / 2, + ( screen_h - win_h ) / 2, 0, 0, SWP_NOSIZE ); +} + +HWND createWindow( const wchar_t *type, const wchar_t *text, int x, int y, + int width, int height, long long id, HWND parent, + HFONT hFont, long long dwStyle, long long exStyle ) { + HWND child_hwnd = + CreateWindowExW( exStyle, type, text, dwStyle, x, y, width, height, + parent, ( HMENU )id, NULL, NULL ); + SendMessage( child_hwnd, WM_SETFONT, ( WPARAM )hFont, true ); + return child_hwnd; +} + +HWND createLabel( const wchar_t *text, int x, int y, int width, int height, + long long id, HWND parent, HFONT hFont ) { + return createWindow( L"Static", text, x, y, width, height, id, parent, + hFont, WS_VISIBLE | WS_CHILD | SS_LEFT | WS_TABSTOP, + 0 ); +} + +HWND createEditBox( int x, int y, int width, int height, long long id, + HWND parent, HFONT hFont ) { + return createWindow( L"Edit", NULL, x, y, width, height, id, parent, hFont, + WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP | + ES_AUTOHSCROLL, + WS_EX_CLIENTEDGE ); +} + +HWND createCombo( int x, int y, int width, int height, long long id, + HWND parent, HFONT hFont ) { + return createWindow( + L"Combobox", NULL, x, y, width, height, id, parent, hFont, + WS_VISIBLE | WS_CHILD | CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP, 0 ); +} + +HWND createButton( const wchar_t *text, int x, int y, int width, int height, + long long id, HWND parent, HFONT hFont ) { + return createWindow( + L"Button", text, x, y, width, height, id, parent, hFont, + WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | WS_TABSTOP, 0 ); +} + +HWND createCheckbox( const wchar_t *text, int x, int y, int width, int height, + long long id, HWND parent, HFONT hFont ) { + return createWindow( + L"Button", text, x, y, width, height, id, parent, hFont, + WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX | WS_TABSTOP, 0 ); +} + +HWND createProgressbar( int x, int y, int width, int height, long long id, + HWND parent, HFONT hFont ) { + return createWindow( PROGRESS_CLASSW, NULL, x, y, width, height, id, parent, + hFont, WS_CHILD | WS_VISIBLE | PBS_SMOOTH, 0 ); +} + +HWND createListView( int x, int y, int width, int height, long long id, + HWND parent, HFONT hFont ) { + auto hwnd = createWindow( + WC_LISTVIEWW, NULL, x, y, width, height, id, parent, hFont, + WS_CHILD | LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_VISIBLE, 0 ); + SendMessage( hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT ); + return hwnd; +} + +LVCOLUMNW addColumnToListView( HWND list_view, const wchar_t *text, int width, + int pos ) { + // not thread-safe, but I don't care + static wchar_t pszText[MAX_PATH]; + static LVCOLUMNW col{}; + wcscpy( pszText, text ); + + col.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH; + col.fmt = LVCFMT_LEFT; + col.cx = width; + col.pszText = pszText; + SendMessage( list_view, LVM_INSERTCOLUMN, pos, ( LPARAM )&col ); + return col; +} + +void addItemToCombo( HWND combo, const wchar_t *text ) { + SendMessage( combo, CB_ADDSTRING, 0, ( LPARAM )text ); +} + +void addItemToListView( HWND list_view, int row, int column, + const wchar_t *text ) { + // not thread-safe, but I don't care + static wchar_t pszText[MAX_PATH]; + static LVITEMW item{}; + wcscpy( pszText, text ); + + item.mask = LVIF_TEXT; + item.iItem = row; + item.iSubItem = column; + item.pszText = pszText; + item.cchTextMax = MAX_PATH; + if ( column == 0 ) + SendMessage( list_view, LVM_INSERTITEM, 0, ( LPARAM )&item ); + else + SendMessage( list_view, LVM_SETITEMTEXT, item.iItem, ( LPARAM )&item ); +} + +void setListViewItemText( HWND list_view, int row, int column, + const wchar_t *text ) { + static wchar_t pszText[MAX_PATH]; + static LVITEMW item{}; + wcscpy( pszText, text ); + + item.mask = LVIF_TEXT; + item.iItem = row; + item.iSubItem = column; + item.pszText = pszText; + item.cchTextMax = MAX_PATH; + SendMessage( list_view, LVM_SETITEMTEXT, row, ( LPARAM )&item ); +} + +std::wstring getDir() { + wchar_t path[MAX_PATH]; + BROWSEINFO bi{}; + bi.lpszTitle = L"Choose a folder"; + LPITEMIDLIST pidl = SHBrowseForFolder( &bi ); + if ( pidl != 0 ) { + SHGetPathFromIDList( pidl, path ); + IMalloc *imalloc = 0; + if ( SUCCEEDED( SHGetMalloc( &imalloc ) ) ) { + imalloc->Free( pidl ); + imalloc->Release(); + } + } else { + path[0] = '\0'; + } + return path; +} + +std::wstring getItemText( HWND hwnd, int row, int column ) { + wchar_t text[MAX_PATH]; + ListView_GetItemText( hwnd, row, column, text, MAX_PATH ); + return text; +} + +int getComboCurSel( HWND hwnd ) { + return SendMessage( hwnd, CB_GETCURSEL, 0, 0 ); +} diff --git a/windows/gui_functions.hpp b/windows/gui_functions.hpp new file mode 100644 index 0000000..43e8fa6 --- /dev/null +++ b/windows/gui_functions.hpp @@ -0,0 +1,32 @@ +#include +#include +#include + +void registerWindowClasses( HINSTANCE hInstance ); +void centerWindow( HWND hwnd ); + +HWND createLabel( const wchar_t *text, int x, int y, int width, int height, + long long id, HWND parent, HFONT hFont ); +HWND createEditBox( int x, int y, int width, int height, long long id, + HWND parent, HFONT hFont ); +HWND createCombo( int x, int y, int width, int height, long long id, + HWND parent, HFONT hFont ); +HWND createButton( const wchar_t *text, int x, int y, int width, int height, + long long id, HWND parent, HFONT hFont ); +HWND createCheckbox( const wchar_t *text, int x, int y, int width, int height, + long long id, HWND parent, HFONT hFont ); +HWND createProgressbar( int x, int y, int width, int height, long long id, + HWND parent, HFONT hFont ); +HWND createListView( int x, int y, int width, int height, long long id, + HWND parent, HFONT hFont ); +LVCOLUMNW addColumnToListView( HWND list_view, const wchar_t *text, int width, + int pos ); + +void addItemToCombo( HWND combo, const wchar_t *text ); +void addItemToListView( HWND list_view, int row, int column, + const wchar_t *text ); +void setListViewItemText( HWND list_view, int row, int column, + const wchar_t *text ); +std::wstring getItemText( HWND hwnd, int row, int column ); +int getComboCurSel( HWND hwnd ); +std::wstring getDir(); diff --git a/windows/mainwindow.cpp b/windows/mainwindow.cpp new file mode 100644 index 0000000..cc9eea0 --- /dev/null +++ b/windows/mainwindow.cpp @@ -0,0 +1,431 @@ +#include "mainwindow.hpp" +#include "../filesystem.hpp" +#include "../functions.hpp" +#include "../tv_rename.hpp" +#include "databasewindow.hpp" +#include "gui_functions.hpp" +#include "patternwindow.hpp" +#include "progresswindow.hpp" +#include "searchwindow.hpp" +#include "seasonwindow.hpp" + +#include +#include + +#define API_KEY "42B66F5E-C6BF-423F-ADF9-CC97163472F6" + +#define ID_SHOW_STATIC 0x0001 +#define ID_LANGUAGE_STATIC 0x0002 +#define ID_DIR_STATIC 0x0003 +#define ID_PATTERN_STATIC 0x0004 +#define ID_POSSIBLE_STATIC 0x0005 + +#define ID_SHOW_EDIT 0x1000 +#define ID_LANGUAGE_COMBO 0x1001 +#define ID_DIR_EDIT 0x1002 +#define ID_DIR_BUTTON 0x1003 +#define ID_PATTERN_EDIT 0x1004 +#define ID_PATTERN_BUTTON 0x1005 +#define ID_PROCESS_BUTTON 0x1006 +#define ID_RENAME_CHECKBOX 0x1007 +#define ID_SHOW_COMBO 0x1008 +#define ID_RENAME_BUTTON 0x1009 +#define ID_QUIT_BUTTON 0x100a +#define ID_DVD_CHECKBOX 0x100b +#define ID_DB_ADD_BUTTON 0x100c + +#define MENU_FILE_EXIT 0x2000 +#define MENU_DAT_UPDATE 0x2001 +#define MENU_DAT_REFRESH 0x2002 +#define MENU_DAT_CLEAN 0x2003 +#define MENU_DAT_MANAGE 0x2004 +#define MENU_DAT_PATTERN 0x2005 + +MainWindow *MainWindow::mw = nullptr; + +MainWindow::MainWindow( HINSTANCE hInstance, int nCmdShow ) + : hInst( hInstance ) { + window = CreateWindowW( L"MainWindow", L"TV Rename", + WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, window_width, + window_height, NULL, NULL, hInst, NULL ); + ShowWindow( window, nCmdShow ); + hFont = ( HFONT )GetStockObject( DEFAULT_GUI_FONT ); + + authenticate( API_KEY ); + languages = getLangs(); + + createLabel( L"Show:", 5, 5, 30, 15, ID_SHOW_STATIC, window, hFont ); + createLabel( L"Language:", 170, 5, 55, 15, ID_LANGUAGE_STATIC, window, + hFont ); + createLabel( L"Directory:", 5, 45, 65, 15, ID_DIR_STATIC, window, hFont ); + createLabel( L"Pattern:", 5, 85, 55, 15, ID_PATTERN_STATIC, window, hFont ); + possible_label = createLabel( L"Possible shows:", 5, 190, 100, 15, + ID_POSSIBLE_STATIC, window, hFont ); + show_input = createEditBox( 5, 20, 160, 25, ID_SHOW_EDIT, window, hFont ); + language_input = + createCombo( 170, 20, 160, 250, ID_LANGUAGE_COMBO, window, hFont ); + for ( auto &x : languages ) { + addItemToCombo( language_input, x.second.c_str() ); + } + SendMessage( language_input, CB_SETCURSEL, langPos( L"en" ), NULL ); + + dir_input = createEditBox( 5, 60, 160, 25, ID_DIR_EDIT, window, hFont ); + createButton( L"Choose directory", 170, 60, 120, 25, ID_DIR_BUTTON, window, + hFont ); + pattern_input = + createEditBox( 5, 100, 160, 25, ID_PATTERN_EDIT, window, hFont ); + createButton( L"Pattern help", 170, 100, 100, 25, ID_PATTERN_BUTTON, window, + hFont ); + createButton( L"Process", 5, 140, 80, 25, ID_PROCESS_BUTTON, window, + hFont ); + trust_input = createCheckbox( L"Don't ask for rename confirmation", 90, 146, + 180, 12, ID_RENAME_CHECKBOX, window, hFont ); + dvd_input = createCheckbox( L"Use DVD ordering", 5, 170, 180, 12, + ID_DVD_CHECKBOX, window, hFont ); + possible_input = createCombo( 5, 205, 160, window_height - 220, + ID_SHOW_COMBO, window, hFont ); + rename_button = createButton( L"Rename", 5, 230, 80, 25, ID_RENAME_BUTTON, + window, hFont ); + db_add_button = createButton( L"Add to database", 90, 230, 100, 25, + ID_DB_ADD_BUTTON, window, hFont ); + createButton( L"Quit", window_width - 100, window_height - 88, 80, 25, + ID_QUIT_BUTTON, window, hFont ); + + // MENUS + HMENU hMenuBar = CreateMenu(); + HMENU hMenuFile = CreateMenu(); + HMENU hMenuDatabase = CreateMenu(); + + AppendMenuW( hMenuFile, MF_STRING, MENU_FILE_EXIT, L"&Exit" ); + AppendMenuW( hMenuDatabase, MF_STRING, MENU_DAT_UPDATE, + L"&Update database" ); + AppendMenuW( hMenuDatabase, MF_STRING, MENU_DAT_REFRESH, + L"&Refresh database" ); + AppendMenuW( hMenuDatabase, MF_STRING, MENU_DAT_CLEAN, L"&Clean database" ); + AppendMenuW( hMenuDatabase, MF_STRING, MENU_DAT_MANAGE, + L"&Manage database" ); + AppendMenuW( hMenuDatabase, MF_STRING, MENU_DAT_PATTERN, + L"Change &pattern" ); + + AppendMenuW( hMenuBar, MF_POPUP, ( UINT_PTR )hMenuFile, L"&File" ); + AppendMenuW( hMenuBar, MF_POPUP, ( UINT_PTR )hMenuDatabase, L"&Database" ); + SetMenu( window, hMenuBar ); + + INITCOMMONCONTROLSEX icex; // Structure for control initialization. + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx( &icex ); + + auto appdata = userHome() + L"\\tv_rename"; + if ( !FSLib::isDirectory( appdata ) ) { + // create the directory so pattern can be stored when changed + CreateDirectory( appdata.c_str(), NULL ); + } else { + readDefaultPattern( appdata ); + } + + SendMessage( pattern_input, WM_SETTEXT, NULL, + ( LPARAM )default_pattern.c_str() ); + ShowWindow( possible_label, SW_HIDE ); + ShowWindow( possible_input, SW_HIDE ); + ShowWindow( rename_button, SW_HIDE ); + ShowWindow( db_add_button, SW_HIDE ); + + UpdateWindow( window ); +} + +LRESULT CALLBACK MainWindow::messageHandler( HWND hwnd, UINT umsg, + WPARAM wParam, LPARAM lParam ) { + switch ( umsg ) { + case WM_CREATE: + centerWindow( hwnd ); + break; + case WM_COMMAND: + switch ( LOWORD( wParam ) ) { + case ID_QUIT_BUTTON: + case MENU_FILE_EXIT: + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + break; + case ID_PROCESS_BUTTON: + mw->process(); + break; + case ID_DIR_BUTTON: + SendMessage( mw->dir_input, WM_SETTEXT, NULL, + ( LPARAM )getDir().c_str() ); + break; + case MENU_DAT_UPDATE: + dbUpdate(); + break; + case MENU_DAT_REFRESH: + dbRefresh(); + break; + case MENU_DAT_CLEAN: + dbClean(); + break; + case MENU_DAT_MANAGE: + dbManage(); + break; + case MENU_DAT_PATTERN: + dbPattern(); + break; + case ID_PATTERN_BUTTON: + patternHelp(); + break; + case ID_RENAME_BUTTON: + mw->rename(); + break; + case ID_DB_ADD_BUTTON: + mw->dbAdd(); + break; + } + break; + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + } + return DefWindowProcW( hwnd, umsg, wParam, lParam ); +} + +void MainWindow::mainLoop() { + MSG msg; + while ( GetMessage( &msg, NULL, 0, 0 ) ) { + if ( !IsDialogMessage( window, &msg ) ) { + TranslateMessage( &msg ); + mw = this; + DispatchMessage( &msg ); + } + } +} + +int MainWindow::langPos( const std::wstring &lang ) { + for ( unsigned long long i = 0; i < languages.size(); i++ ) { + if ( languages[i].first == lang ) + return i; + } + return -1; +} + +void MainWindow::readDefaultPattern( const std::wstring &base_dir ) { + std::wifstream file( base_dir + L"\\pattern" ); + if ( file ) { + std::getline( file, default_pattern ); + } +} + +void MainWindow::process() { + wchar_t show[256]; + SendMessage( show_input, WM_GETTEXT, ( WPARAM )255, ( LPARAM )show ); + if ( wcslen( show ) == 0 ) { + MessageBox( window, L"Show field is empty", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + auto index = SendMessage( language_input, CB_GETCURSEL, NULL, NULL ); + if ( index == CB_ERR ) { + MessageBox( window, L"No show selected", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + auto lang_code = languages[index].first; + + possible_shows = searchShow( show, lang_code ); + if ( possible_shows.size() == 0 ) { + MessageBox( window, L"No results found for given show name", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + SendMessage( possible_input, CB_RESETCONTENT, NULL, NULL ); + for ( const auto &x : possible_shows ) { + SendMessage( possible_input, CB_ADDSTRING, NULL, + ( LPARAM )x.first.c_str() ); + } + SendMessage( possible_input, CB_SETCURSEL, 0, 0 ); + + ShowWindow( mw->possible_label, SW_SHOW ); + ShowWindow( mw->possible_input, SW_SHOW ); + ShowWindow( mw->rename_button, SW_SHOW ); + ShowWindow( mw->db_add_button, SW_SHOW ); +} + +void MainWindow::rename() { + wchar_t path[MAX_PATH]; + SendMessage( dir_input, WM_GETTEXT, ( WPARAM )MAX_PATH - 1, + ( LPARAM )path ); + if ( wcslen( path ) == 0 ) { + MessageBox( window, L"Directory field is empty", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + if ( !FSLib::isDirectory( path ) ) { + MessageBox( window, L"Directory doesn't exist", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + selected.clear(); + files.clear(); + std::vector< int > options{}; + + iterateFS( files, path ); + for ( auto &x : files ) { + options.push_back( x.first ); + } + + SeasonWindow sw( hInst, options, selected, window ); + sw.mainLoop(); + + auto index = SendMessage( possible_input, CB_GETCURSEL, NULL, NULL ); + if ( index == CB_ERR ) { + MessageBox( window, L"No show selected", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + auto show = possible_shows[index].first; + auto show_id = possible_shows[index].second; + + index = SendMessage( language_input, CB_GETCURSEL, NULL, NULL ); + if ( index == CB_ERR ) { + MessageBox( window, L"No show selected", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + auto lang = languages[index].first; + bool dvd = SendMessage( dvd_input, BM_GETCHECK, NULL, NULL ) == BST_CHECKED; + bool trust = + SendMessage( trust_input, BM_GETCHECK, NULL, NULL ) == BST_CHECKED; + + wchar_t input_pattern_wchar[MAX_PATH]; + SendMessage( pattern_input, WM_GETTEXT, ( WPARAM )MAX_PATH, + ( LPARAM )input_pattern_wchar ); + if ( wcscmp( input_pattern_wchar, default_pattern.c_str() ) ) { + std::wofstream file( userHome() + L"\\tv_rename\\pattern" ); + if ( file ) { + file << input_pattern_wchar; + } + } + std::wstring input_pattern = input_pattern_wchar; + + for ( auto &season : selected ) { + auto renamed_files = getRenamedFiles( + show, season, show_id, lang, + ( input_pattern.empty() ? default_pattern : input_pattern ), false, + files[season], dvd ); + if ( renamed_files.empty() ) { + continue; + } + if ( trust ) { + renameFiles( renamed_files ); + continue; + } + + std::wstring message_text; + for ( auto &file : renamed_files ) { + message_text += std::get< 2 >( file ) + L" --> " + + std::get< 3 >( file ) + L"\n"; + } + + auto res = MessageBox( window, message_text.c_str(), L"Is this OK?", + MB_OKCANCEL ); + if ( res == IDOK ) + renameFiles( renamed_files ); + } +} + +void MainWindow::dbAdd() { + wchar_t path[MAX_PATH]; + SendMessage( dir_input, WM_GETTEXT, ( WPARAM )MAX_PATH - 1, + ( LPARAM )path ); + if ( wcslen( path ) == 0 ) { + MessageBox( window, L"Directory field is empty", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + if ( !FSLib::isDirectory( path ) ) { + MessageBox( window, L"Directory doesn't exist", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + auto index = SendMessage( possible_input, CB_GETCURSEL, NULL, NULL ); + if ( index == CB_ERR ) { + MessageBox( window, L"No show selected", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + auto show = possible_shows[index].first; + auto show_id = possible_shows[index].second; + + index = SendMessage( language_input, CB_GETCURSEL, NULL, NULL ); + if ( index == CB_ERR ) { + MessageBox( window, L"No language selected", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + auto lang = languages[index].first; + bool dvd = SendMessage( dvd_input, BM_GETCHECK, NULL, NULL ) == BST_CHECKED; + + ProgressWindow pw( hInst, window ); + std::thread t( addToDB, std::move( show ), path, std::move( lang ), + std::move( show_id ), getDBPattern(), false, dvd, + pw.getWindow() ); + t.detach(); + pw.mainLoop(); + + return; +} + +void MainWindow::dbUpdate() { + ProgressWindow pw( mw->hInst, mw->window ); + std::thread t( updateDB, false, pw.getWindow() ); + t.detach(); + pw.mainLoop(); +} + +void MainWindow::dbRefresh() { + ProgressWindow pw( mw->hInst, mw->window ); + std::thread t( refreshDB, false, pw.getWindow() ); + t.detach(); + pw.mainLoop(); +} + +void MainWindow::dbClean() { + cleanDB(); +} + +void MainWindow::dbManage() { + DatabaseWindow dw( mw->hInst, mw->languages, mw->window ); + dw.mainLoop(); +} + +void MainWindow::dbPattern() { + auto pattern = getDBPattern(); + PatternWindow pw( mw->hInst, pattern.c_str(), mw->window ); + pw.mainLoop(); + if ( pw.accepted() ) + changeDBPattern( pw.getPattern() ); + return; +} + +void MainWindow::patternHelp() { + MessageBoxW( + NULL, + L"%filename - original filename (without type extension)\n\n" + "%show - show name from thetvdb\n\n" + "%epname - episode name from thetvdb\n\n" + "%season - season number\n\n" + "%episode - episode number\n\n" + "Both season number and episode number can be padded with zeros, just " + "add width of padding" + " right after %, like this: %2season.\n\n" + "Default pattern is \"%filename - %epname\", you might want to change " + "this to" + " \"S%2seasonE%2episode - %epname\" or \"%show - S%2seasonE%2episode - " + "%epname\"", + L"Test", MB_OK ); + return; +} diff --git a/windows/mainwindow.hpp b/windows/mainwindow.hpp new file mode 100644 index 0000000..dcb5819 --- /dev/null +++ b/windows/mainwindow.hpp @@ -0,0 +1,42 @@ +#include + +#include "../tv_rename.hpp" + +class MainWindow { +public: + MainWindow( HINSTANCE hInstance, int nCmdShow ); + + void mainLoop(); + static LRESULT CALLBACK messageHandler( HWND hwnd, UINT umsg, WPARAM wParam, + LPARAM lParam ); + +private: + int langPos( const std::wstring &lang ); + void readDefaultPattern( const std::wstring &base_dir ); + void process(); + void rename(); + void dbAdd(); + static void dbUpdate(); + static void dbRefresh(); + static void dbClean(); + static void dbManage(); + static void dbPattern(); + static void patternHelp(); + + HFONT hFont; + HWND window; + HINSTANCE hInst; + const int window_width{ 450 }; + const int window_height{ 350 }; + std::vector< std::pair< string, string > > languages; + std::vector< std::pair< string, string > > possible_shows; + std::wstring default_pattern{}; + std::vector< int > selected; + std::map< int, std::map< int, string > > files; + + HWND show_input, language_input, dir_input, pattern_input, trust_input, + dvd_input; + HWND possible_label, possible_input, rename_button, db_add_button; + + static MainWindow *mw; +}; diff --git a/windows/patternwindow.cpp b/windows/patternwindow.cpp new file mode 100644 index 0000000..eebf84c --- /dev/null +++ b/windows/patternwindow.cpp @@ -0,0 +1,83 @@ +#include "patternwindow.hpp" +#include "gui_functions.hpp" + +#define ID_PATTERN_STATIC 0x0001 + +#define ID_PATTERN_EDIT 0x1000 +#define ID_OK_BUTTON 0x1001 +#define ID_CANCEL_BUTTON 0x1002 + +PatternWindow *PatternWindow::pw = nullptr; + +PatternWindow::PatternWindow( HINSTANCE hInstance, const wchar_t *pattern, + HWND parent_window ) + : parent( parent_window ) { + window = CreateWindowW( L"PatternWindow", L"Change database pattern", + WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, window_width, + window_height, parent, NULL, hInstance, NULL ); + ShowWindow( window, SW_SHOW ); + auto hFont = ( HFONT )GetStockObject( DEFAULT_GUI_FONT ); + EnableWindow( parent, false ); + + createLabel( L"Pattern:", 5, 5, 30, 15, ID_PATTERN_STATIC, window, hFont ); + pattern_input = + createEditBox( 5, 20, 190, 25, ID_PATTERN_EDIT, window, hFont ); + + createButton( L"Cancel", window_width - 100, window_height - 88, 80, 25, + ID_CANCEL_BUTTON, window, hFont ); + createButton( L"OK", window_width - 185, window_height - 88, 80, 25, + ID_OK_BUTTON, window, hFont ); + + SendMessage( pattern_input, WM_SETTEXT, NULL, ( LPARAM )pattern ); + UpdateWindow( window ); +} + +void PatternWindow::storePattern() { + SendMessage( pattern_input, WM_GETTEXT, ( WPARAM )2047, ( LPARAM )pattern ); +} + +const wchar_t *PatternWindow::getPattern() { + return pattern; +} + +LRESULT CALLBACK PatternWindow::messageHandler( HWND hwnd, UINT umsg, + WPARAM wParam, LPARAM lParam ) { + switch ( umsg ) { + case WM_CREATE: + centerWindow( hwnd ); + break; + case WM_COMMAND: + switch ( LOWORD( wParam ) ) { + case ID_OK_BUTTON: + pw->result = OK; + pw->storePattern(); + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + break; + case ID_CANCEL_BUTTON: + pw->result = Cancel; + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + break; + } + break; + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + } + return DefWindowProcW( hwnd, umsg, wParam, lParam ); +} + +void PatternWindow::mainLoop() { + MSG msg; + while ( GetMessage( &msg, NULL, 0, 0 ) ) { + if ( !IsDialogMessage( window, &msg ) ) { + TranslateMessage( &msg ); + pw = this; + DispatchMessage( &msg ); + } + } +} + +bool PatternWindow::accepted() { + return result == OK; +} diff --git a/windows/patternwindow.hpp b/windows/patternwindow.hpp new file mode 100644 index 0000000..551f67b --- /dev/null +++ b/windows/patternwindow.hpp @@ -0,0 +1,34 @@ +#include +#include + +class PatternWindow { +public: + PatternWindow( HINSTANCE hInstance, const wchar_t *pattern, + HWND parent_window ); + + void mainLoop(); + static LRESULT CALLBACK messageHandler( HWND hwnd, UINT umsg, WPARAM wParam, + LPARAM lParam ); + bool accepted(); + const wchar_t *getPattern(); + + ~PatternWindow() { + EnableWindow( parent, true ); + SetFocus( parent ); + } + +private: + enum DialogResult { OK, Cancel }; + + void storePattern(); + + HWND window; + HWND pattern_input; + HWND parent; + enum DialogResult result; + wchar_t pattern[2048]; + const int window_width{ 215 }; + const int window_height{ 150 }; + + static PatternWindow *pw; +}; diff --git a/windows/progresswindow.cpp b/windows/progresswindow.cpp new file mode 100644 index 0000000..4ad88a9 --- /dev/null +++ b/windows/progresswindow.cpp @@ -0,0 +1,69 @@ +#include "progresswindow.hpp" +#include "../progress.hpp" +#include "gui_functions.hpp" + +#include + +#define ID_PROGRESS_STATIC 0x0001 + +#define ID_PROGRESS_BAR 0x1000 + +ProgressWindow *ProgressWindow::pw = nullptr; + +ProgressWindow::ProgressWindow( HINSTANCE hInstance, HWND parent_window ) + : parent( parent_window ) { + window = CreateWindowW( L"ProgressWindow", L"Progress", + WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, + CW_USEDEFAULT, window_width, window_height, parent, + NULL, hInstance, NULL ); + ShowWindow( window, SW_SHOW ); + auto hFont = ( HFONT )GetStockObject( DEFAULT_GUI_FONT ); + EnableWindow( parent, false ); + + progress_label = createLabel( L"Progress:", 5, 5, window_width - 25, 15, + ID_PROGRESS_STATIC, window, hFont ); + progress_bar = createProgressbar( 5, 20, window_width - 25, 25, + ID_PROGRESS_BAR, window, hFont ); + SendMessage( progress_bar, PBM_SETRANGE, 0, MAKELPARAM( 0, 100 ) ); + SendMessage( progress_bar, PBM_SETPOS, 0, 0 ); + + UpdateWindow( window ); +} + +LRESULT CALLBACK ProgressWindow::messageHandler( HWND hwnd, UINT umsg, + WPARAM wParam, + LPARAM lParam ) { + switch ( umsg ) { + case WM_CREATE: + centerWindow( hwnd ); + break; + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + case WM_APP: + switch ( wParam ) { + case PROGRESS_PERC: + SendMessage( pw->progress_bar, PBM_SETPOS, ( int )lParam, 0 ); + break; + case PROGRESS_STRING: + SendMessage( pw->progress_label, WM_SETTEXT, NULL, lParam ); + break; + } + } + return DefWindowProcW( hwnd, umsg, wParam, lParam ); +} + +void ProgressWindow::mainLoop() { + MSG msg; + while ( GetMessage( &msg, NULL, 0, 0 ) ) { + if ( !IsDialogMessage( window, &msg ) ) { + TranslateMessage( &msg ); + pw = this; + DispatchMessage( &msg ); + } + } +} + +HWND ProgressWindow::getWindow() { + return window; +} diff --git a/windows/progresswindow.hpp b/windows/progresswindow.hpp new file mode 100644 index 0000000..53bad5b --- /dev/null +++ b/windows/progresswindow.hpp @@ -0,0 +1,27 @@ +#include +#include + +class ProgressWindow { +public: + ProgressWindow( HINSTANCE hInstance, HWND parent_window ); + + void mainLoop(); + static LRESULT CALLBACK messageHandler( HWND hwnd, UINT umsg, WPARAM wParam, + LPARAM lParam ); + HWND getWindow(); + + ~ProgressWindow() { + EnableWindow( parent, true ); + SetFocus( parent ); + } + +private: + HWND window; + HWND progress_bar; + HWND progress_label; + HWND parent; + + const int window_width{ 350 }; + const int window_height{ 100 }; + static ProgressWindow *pw; +}; diff --git a/windows/searchwindow.cpp b/windows/searchwindow.cpp new file mode 100644 index 0000000..31bd4ba --- /dev/null +++ b/windows/searchwindow.cpp @@ -0,0 +1,158 @@ +#include "searchwindow.hpp" +#include "../tv_rename.hpp" +#include "gui_functions.hpp" + +#define ID_SHOW_STATIC 0x0001 +#define ID_LANG_STATIC 0x0002 +#define ID_RES_STATIC 0x0002 +#define ID_SHOW_INPUT 0x1000 +#define ID_LANG_INPUT 0x1001 +#define ID_RES_INPUT 0x1002 +#define ID_SEARCH_BUTTON 0x1003 +#define ID_OK_BUTTON 0x1004 +#define ID_CANCEL_BUTTON 0x1005 + +SearchWindow *SearchWindow::sw = nullptr; + +SearchWindow::SearchWindow( + HINSTANCE hInstance, + const std::vector< std::pair< std::wstring, std::wstring > > &languages, + const int lang_pos, const wchar_t *show, HWND parent_window ) + : parent( parent_window ), langs( languages ) { + window = CreateWindowW( L"SearchWindow", L"Show Search", + WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, window_width, + window_height, parent, NULL, hInstance, NULL ); + ShowWindow( window, SW_SHOW ); + auto hFont = ( HFONT )GetStockObject( DEFAULT_GUI_FONT ); + EnableWindow( parent, false ); + + createLabel( L"Show:", 5, 5, 30, 15, ID_SHOW_STATIC, window, hFont ); + createLabel( L"Language:", 170, 5, 55, 15, ID_LANG_STATIC, window, hFont ); + possible_label = createLabel( L"Possible shows:", 5, 60, 100, 15, + ID_RES_STATIC, window, hFont ); + show_input = createEditBox( 5, 20, 160, 25, ID_SHOW_INPUT, window, hFont ); + SendMessage( show_input, WM_SETTEXT, NULL, ( LPARAM )show ); + lang_input = createCombo( 170, 20, 160, 250, ID_LANG_INPUT, window, hFont ); + for ( auto &x : languages ) { + addItemToCombo( lang_input, x.second.c_str() ); + } + SendMessage( lang_input, CB_SETCURSEL, lang_pos, NULL ); + + createButton( L"Search", 340, 18, 80, 25, ID_SEARCH_BUTTON, window, hFont ); + possible_input = createCombo( 5, 80, window_width - 125, 250, ID_RES_INPUT, + window, hFont ); + createButton( L"OK", window_width - 200, 110, 80, 25, ID_OK_BUTTON, window, + hFont ); + createButton( L"Cancel", window_width - 105, 110, 80, 25, ID_CANCEL_BUTTON, + window, hFont ); + + ShowWindow( possible_label, SW_HIDE ); + ShowWindow( possible_input, SW_HIDE ); + + UpdateWindow( window ); +} + +LRESULT CALLBACK SearchWindow::messageHandler( HWND hwnd, UINT umsg, + WPARAM wParam, LPARAM lParam ) { + switch ( umsg ) { + case WM_CREATE: + centerWindow( hwnd ); + break; + case WM_COMMAND: + switch ( LOWORD( wParam ) ) { + case ID_OK_BUTTON: + sw->ok(); + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + break; + case ID_CANCEL_BUTTON: + sw->cancel(); + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + break; + case ID_SEARCH_BUTTON: + sw->process(); + break; + } + break; + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + } + return DefWindowProcW( hwnd, umsg, wParam, lParam ); +} + +void SearchWindow::mainLoop() { + MSG msg; + while ( GetMessage( &msg, NULL, 0, 0 ) ) { + if ( !IsDialogMessage( window, &msg ) ) { + TranslateMessage( &msg ); + sw = this; + DispatchMessage( &msg ); + } + } +} + +void SearchWindow::process() { + wchar_t show[256]; + SendMessage( show_input, WM_GETTEXT, ( WPARAM )255, ( LPARAM )show ); + if ( wcslen( show ) == 0 ) { + MessageBox( window, L"Show field is empty", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + auto index = SendMessage( lang_input, CB_GETCURSEL, NULL, NULL ); + if ( index == CB_ERR ) { + MessageBox( window, L"No language selected", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + auto lang_code = langs[index].first; + + possible_shows = searchShow( show, lang_code ); + if ( possible_shows.size() == 0 ) { + MessageBox( window, L"No results found for given show name", L"Error", + MB_OK | MB_ICONERROR ); + return; + } + + SendMessage( possible_input, CB_RESETCONTENT, NULL, NULL ); + for ( const auto &x : possible_shows ) { + SendMessage( possible_input, CB_ADDSTRING, NULL, + ( LPARAM )x.first.c_str() ); + } + SendMessage( possible_input, CB_SETCURSEL, 0, 0 ); + + ShowWindow( possible_label, SW_SHOW ); + ShowWindow( possible_input, SW_SHOW ); +} + +void SearchWindow::ok() { + selected = SendMessage( possible_input, CB_GETCURSEL, NULL, NULL ); + lang_id = SendMessage( lang_input, CB_GETCURSEL, NULL, NULL ); + if ( selected == CB_ERR ) { + MessageBox( window, L"No show selected", L"Error", + MB_OK | MB_ICONERROR ); + selected = -1; + } +} + +void SearchWindow::cancel() { + selected = -1; +} + +bool SearchWindow::accepted() { + return selected != -1; +} + +std::wstring SearchWindow::getShow() { + return possible_shows[selected].first; +} + +std::wstring SearchWindow::getShowID() { + return possible_shows[selected].second; +} + +int SearchWindow::getLangID() { + return lang_id; +} diff --git a/windows/searchwindow.hpp b/windows/searchwindow.hpp new file mode 100644 index 0000000..576a088 --- /dev/null +++ b/windows/searchwindow.hpp @@ -0,0 +1,44 @@ +#include +#include +#include + +class SearchWindow { +public: + SearchWindow( + HINSTANCE hInstance, + const std::vector< std::pair< std::wstring, std::wstring > > &languages, + const int lang_pos, const wchar_t *show, HWND parent_window ); + + void mainLoop(); + static LRESULT CALLBACK messageHandler( HWND hwnd, UINT umsg, WPARAM wParam, + LPARAM lParam ); + bool accepted(); + std::wstring getShow(); + std::wstring getShowID(); + int getLangID(); + ~SearchWindow() { + EnableWindow( parent, true ); + SetFocus( parent ); + } + +private: + HWND window; + HWND show_input; + HWND lang_input; + HWND possible_label; + HWND possible_input; + HWND parent; + + const int window_width{ 450 }; + const int window_height{ 200 }; + const std::vector< std::pair< std::wstring, std::wstring > > &langs; + std::vector< std::pair< std::wstring, std::wstring > > possible_shows; + int selected{ -1 }; + int lang_id{ -1 }; + + void process(); + void ok(); + void cancel(); + + static SearchWindow *sw; +}; diff --git a/windows/seasonwindow.cpp b/windows/seasonwindow.cpp new file mode 100644 index 0000000..3cc28ca --- /dev/null +++ b/windows/seasonwindow.cpp @@ -0,0 +1,111 @@ +#include "seasonwindow.hpp" +#include "gui_functions.hpp" + +#define ID_CHECKBOX 0x0001 +#define ID_SEASONS_STATIC 0x0001 +#define ID_ALL_BUTTON 0x1000 +#define ID_NONE_BUTTON 0x1001 +#define ID_CONFIRM_BUTTON 0x1002 + +SeasonWindow *SeasonWindow::sw = nullptr; + +SeasonWindow::SeasonWindow( HINSTANCE hInstance, + const std::vector< int > &seasons, + std::vector< int > &return_vector, + HWND parent_window ) + : parent( parent_window ), returned( return_vector ), + season_nums( seasons ) { + window = CreateWindowW( L"SeasonWindow", L"Select seasons", + WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, window_width, + window_height, parent, NULL, hInstance, NULL ); + ShowWindow( window, SW_SHOW ); + auto hFont = ( HFONT )GetStockObject( DEFAULT_GUI_FONT ); + EnableWindow( parent, false ); + + size_t x{ 5 }, y{ 25 }; + + // create a check box for each season + HWND checkbox_hwnd; + for ( auto &s : season_nums ) { + checkbox_hwnd = createCheckbox( std::to_wstring( s ).c_str(), x, y, 55, + 15, ID_CHECKBOX, window, hFont ); + checkboxes.push_back( checkbox_hwnd ); + + if ( x == 185 ) { + x = 5; + y += 20; + } else { + x += 60; + } + } + + createLabel( L"Select seasons:", 5, 5, 100, 15, ID_SEASONS_STATIC, window, + hFont ); + createButton( L"Select all", 130, 175, 100, 25, ID_ALL_BUTTON, window, + hFont ); + createButton( L"Unselect all", 5, 175, 110, 25, ID_NONE_BUTTON, window, + hFont ); + createButton( L"Select", 165, 215, 80, 25, ID_CONFIRM_BUTTON, window, + hFont ); + + UpdateWindow( window ); +} + +void SeasonWindow::mainLoop() { + MSG msg; + while ( GetMessage( &msg, NULL, 0, 0 ) ) { + if ( !IsDialogMessage( window, &msg ) ) { + TranslateMessage( &msg ); + sw = this; + DispatchMessage( &msg ); + } + } +} + +LRESULT CALLBACK SeasonWindow::messageHandler( HWND hwnd, UINT umsg, + WPARAM wParam, LPARAM lParam ) { + switch ( umsg ) { + case WM_CREATE: + centerWindow( hwnd ); + break; + case WM_COMMAND: + switch ( LOWORD( wParam ) ) { + case ID_ALL_BUTTON: + sw->selectAll(); + break; + case ID_NONE_BUTTON: + sw->selectNone(); + break; + case ID_CONFIRM_BUTTON: + sw->storeResult(); + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + break; + } + break; + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + } + return DefWindowProcW( hwnd, umsg, wParam, lParam ); +} + +void SeasonWindow::selectAll() { + for ( auto &x : checkboxes ) { + SendMessage( x, BM_SETCHECK, BST_CHECKED, NULL ); + } +} + +void SeasonWindow::selectNone() { + for ( auto &x : checkboxes ) { + SendMessage( x, BM_SETCHECK, BST_UNCHECKED, NULL ); + } +} + +void SeasonWindow::storeResult() { + for ( unsigned long long i = 0; i < checkboxes.size(); i++ ) { + if ( SendMessage( checkboxes[i], BM_GETCHECK, 0, 0 ) == BST_CHECKED ) { + returned.push_back( season_nums[i] ); + } + } +} diff --git a/windows/seasonwindow.hpp b/windows/seasonwindow.hpp new file mode 100644 index 0000000..61306d6 --- /dev/null +++ b/windows/seasonwindow.hpp @@ -0,0 +1,33 @@ +#include +#include +#include + +class SeasonWindow { +public: + SeasonWindow( HINSTANCE hInstance, const std::vector< int > &seasons, + std::vector< int > &return_vector, HWND parent_window ); + + void mainLoop(); + static LRESULT CALLBACK messageHandler( HWND hwnd, UINT umsg, WPARAM wParam, + LPARAM lParam ); + ~SeasonWindow() { + EnableWindow( parent, true ); + SetFocus( parent ); + } + +private: + void selectNone(); + void selectAll(); + void storeResult(); + + HWND window; + HWND parent; + + const int window_width{ 250 }; + const int window_height{ 250 }; + std::vector< HWND > checkboxes; + std::vector< int > &returned; + const std::vector< int > &season_nums; + + static SeasonWindow *sw; +};