diff --git a/App.config b/App.config index 72a71af..8e15646 100644 --- a/App.config +++ b/App.config @@ -1,6 +1,6 @@ - + - - - + + + \ No newline at end of file diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..9afe297 --- /dev/null +++ b/App.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..06fc145 --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,16 @@ +using System.Windows; +using LibgenDesktop.Infrastructure; + +namespace LibgenDesktop +{ + public partial class App : Application + { + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + IWindowContext mainWindowContext = WindowManager.CreateWindow(RegisteredWindows.WindowKey.MAIN_WINDOW); + mainWindowContext.Closed += (sender, args) => Shutdown(); + mainWindowContext.Show(); + } + } +} diff --git a/Cache/CachedDataAccessor.cs b/Cache/CachedDataAccessor.cs deleted file mode 100644 index b1c39db..0000000 --- a/Cache/CachedDataAccessor.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Windows.Forms; -using BrightIdeasSoftware; -using LibgenDesktop.Database; - -namespace LibgenDesktop.Cache -{ - internal class CachedDataAccessor : IVirtualListDataSource - { - private readonly List cachedBooks; - - public CachedDataAccessor(List cachedBooks) - { - this.cachedBooks = cachedBooks; - } - - public event EventHandler SortingRequested; - - public void PrepareCache(int first, int last) - { - } - - public object GetNthObject(int n) - { - if (cachedBooks.Count > n) - { - return cachedBooks[n]; - } - else - { - return null; - } - } - - public int GetObjectCount() - { - return cachedBooks.Count; - } - - public int GetObjectIndex(object model) - { - return -1; - } - - public void AddObjects(ICollection modelObjects) - { - throw new NotImplementedException(); - } - - public void InsertObjects(int index, ICollection modelObjects) - { - throw new NotImplementedException(); - } - - public void RemoveObjects(ICollection modelObjects) - { - throw new NotImplementedException(); - } - - public int SearchText(string value, int first, int last, OLVColumn column) - { - throw new NotImplementedException(); - } - - public void SetObjects(IEnumerable collection) - { - } - - public void Sort(OLVColumn column, SortOrder order) - { - } - - public void UpdateObject(int index, object modelObject) - { - throw new NotImplementedException(); - } - - private void RaiseSortingRequested() - { - - } - } -} diff --git a/Cache/DataCache.cs b/Cache/DataCache.cs deleted file mode 100644 index f11eae3..0000000 --- a/Cache/DataCache.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using LibgenDesktop.Database; -using LibgenDesktop.Import; -using LibgenDesktop.Infrastructure; - -namespace LibgenDesktop.Cache -{ - internal class DataCache - { - private readonly LocalDatabase localDatabase; - private List cachedBooks; - private List searchResults; - private CachedDataAccessor cachedBooksDataAccessor; - private CachedDataAccessor searchResultsDataAccessor; - - public DataCache(LocalDatabase localDatabase) - { - this.localDatabase = localDatabase; - cachedBooks = new List(); - cachedBooksDataAccessor = new CachedDataAccessor(cachedBooks); - searchResults = new List(); - searchResultsDataAccessor = new CachedDataAccessor(searchResults); - DataAccessor = cachedBooksDataAccessor; - IsInAllBooksMode = true; - } - - public CachedDataAccessor DataAccessor { get; private set; } - public bool IsInAllBooksMode { get; private set; } - - public ProgressOperation CreateLoadBooksOperation() - { - searchResults.Clear(); - DataAccessor = cachedBooksDataAccessor; - return new LoadBooksOperation(localDatabase, cachedBooks); - } - - public ProgressOperation CreateSearchBooksOperation(string searchQuery) - { - searchResults.Clear(); - searchQuery = searchQuery.Trim(); - if (searchQuery == String.Empty) - { - DataAccessor = cachedBooksDataAccessor; - IsInAllBooksMode = true; - return null; - } - else - { - DataAccessor = searchResultsDataAccessor; - IsInAllBooksMode = false; - return new SearchBooksOperation(localDatabase, searchResults, searchQuery); - } - } - - public ProgressOperation CreateImportSqlDumpOperation(string sqlDumpFilePath) - { - cachedBooks.Clear(); - IsInAllBooksMode = true; - return new ImportSqlDumpOperation(localDatabase, sqlDumpFilePath, cachedBooks); - } - } -} diff --git a/Cache/LoadBooksOperation.cs b/Cache/LoadBooksOperation.cs deleted file mode 100644 index 4132e0f..0000000 --- a/Cache/LoadBooksOperation.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using LibgenDesktop.Database; -using LibgenDesktop.Infrastructure; -using LibgenDesktop.Interface; - -namespace LibgenDesktop.Cache -{ - internal class LoadBooksOperation : ProgressOperation - { - private readonly LocalDatabase localDatabase; - private readonly List targetList; - - public LoadBooksOperation(LocalDatabase localDatabase, List targetList) - { - this.localDatabase = localDatabase; - this.targetList = targetList; - } - - protected override void DoWork(CancellationToken token) - { - int totalBookCount = localDatabase.CountBooks(); - int currentBatchBookNumber = 0; - int reportProgressBatchSize = totalBookCount / 1000; - foreach (Book book in localDatabase.GetAllBooks()) - { - if (token.IsCancellationRequested) - { - return; - } - targetList.Add(book); - currentBatchBookNumber++; - if (currentBatchBookNumber == reportProgressBatchSize) - { - RaiseProgressEvent(new ProgressEventArgs - { - ProgressDescription = $"Загрузка книг (загружено {targetList.Count.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)} из {totalBookCount.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)})...", - PercentCompleted = (double)targetList.Count * 100 / totalBookCount - }); - currentBatchBookNumber = 0; - } - } - if (currentBatchBookNumber > 0) - { - RaiseProgressEvent(new ProgressEventArgs - { - PercentCompleted = 100 - }); - } - RaiseCompletedEvent(); - } - } -} diff --git a/Cache/SearchBooksOperation.cs b/Cache/SearchBooksOperation.cs deleted file mode 100644 index bc23a27..0000000 --- a/Cache/SearchBooksOperation.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using LibgenDesktop.Database; -using LibgenDesktop.Infrastructure; -using LibgenDesktop.Interface; - -namespace LibgenDesktop.Cache -{ - internal class SearchBooksOperation : ProgressOperation - { - private const int REPORT_PROGRESS_BATCH_SIZE = 2000; - - private readonly LocalDatabase localDatabase; - private readonly List targetList; - private readonly string searchQuery; - - public SearchBooksOperation(LocalDatabase localDatabase, List targetList, string searchQuery) - { - this.localDatabase = localDatabase; - this.targetList = targetList; - this.searchQuery = searchQuery; - } - - public override bool IsUnbounded => true; - - protected override void DoWork(CancellationToken token) - { - int currentBatchBookNumber = 0; - foreach (Book book in localDatabase.SearchBooks(searchQuery)) - { - if (token.IsCancellationRequested) - { - return; - } - targetList.Add(book); - currentBatchBookNumber++; - if (currentBatchBookNumber == REPORT_PROGRESS_BATCH_SIZE) - { - RaiseProgressEvent(new ProgressEventArgs - { - ProgressDescription = $"Поиск книг (найдено {targetList.Count.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)})...", - PercentCompleted = Double.NaN - }); - currentBatchBookNumber = 0; - } - } - RaiseCompletedEvent(); - } - } -} diff --git a/Common/Constants.cs b/Common/Constants.cs new file mode 100644 index 0000000..cfe6484 --- /dev/null +++ b/Common/Constants.cs @@ -0,0 +1,40 @@ +namespace LibgenDesktop.Common +{ + internal static class Constants + { + public const int MAIN_WINDOW_MIN_WIDTH = 1000; + public const int MAIN_WINDOW_MIN_HEIGHT = 500; + public const int BOOK_WINDOW_MIN_WIDTH = 1000; + public const int BOOK_WINDOW_MIN_HEIGHT = 500; + public const int ERROR_WINDOW_MIN_WIDTH = 400; + public const int ERROR_WINDOW_MIN_HEIGHT = 300; + public const int TITLE_COLUMN_MIN_WIDTH = 150; + public const int AUTHORS_COLUMN_MIN_WIDTH = 150; + public const int SERIES_COLUMN_MIN_WIDTH = 150; + public const int YEAR_COLUMN_MIN_WIDTH = 60; + public const int PUBLISHER_COLUMN_MIN_WIDTH = 150; + public const int FORMAT_COLUMN_MIN_WIDTH = 80; + public const int FILESIZE_COLUMN_MIN_WIDTH = 130; + public const int OCR_COLUMN_MIN_WIDTH = 55; + + public const string DEFAULT_DATABASE_FILE_NAME = "libgen.db"; + public const int DEFAULT_MAIN_WINDOW_WIDTH = 1200; + public const int DEFAULT_MAIN_WINDOW_HEIGHT = 650; + public const int DEFAULT_BOOK_WINDOW_WIDTH = 1200; + public const int DEFAULT_BOOK_WINDOW_HEIGHT = 618; + public const int DEFAULT_TITLE_COLUMN_WIDTH = 200; + public const int DEFAULT_AUTHORS_COLUMN_WIDTH = 200; + public const int DEFAULT_SERIES_COLUMN_WIDTH = 180; + public const int DEFAULT_YEAR_COLUMN_WIDTH = 60; + public const int DEFAULT_PUBLISHER_COLUMN_WIDTH = 180; + public const int DEFAULT_FORMAT_COLUMN_WIDTH = 100; + public const int DEFAULT_FILESIZE_COLUMN_WIDTH = 150; + public const int DEFAULT_OCR_COLUMN_WIDTH = 55; + + public const int SEARCH_REPORT_PROGRESS_BATCH_SIZE = 2000; + public const int INSERT_TRANSACTION_BATCH = 500; + + public const string BOOK_COVER_URL_PREFIX = "http://libgen.io/covers/"; + public const string BOOK_DOWNLOAD_URL_PREFIX = "http://libgen.io/book/index.php?md5="; + } +} diff --git a/Import/ImportSqlDumpOperation.cs b/Import/ImportSqlDumpOperation.cs deleted file mode 100644 index 12be7e5..0000000 --- a/Import/ImportSqlDumpOperation.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; -using LibgenDesktop.Database; -using LibgenDesktop.Infrastructure; -using LibgenDesktop.Interface; - -namespace LibgenDesktop.Import -{ - internal class ImportSqlDumpOperation : ProgressOperation - { - private readonly LocalDatabase localDatabase; - private readonly string sqlDumpFilePath; - private readonly List targetList; - - public ImportSqlDumpOperation(LocalDatabase localDatabase, string sqlDumpFilePath, List targetList) - { - this.localDatabase = localDatabase; - this.sqlDumpFilePath = sqlDumpFilePath; - this.targetList = targetList; - } - - public override string Title => "Импорт из SQL-дампа"; - - protected override void DoWork(CancellationToken token) - { - RaiseProgressEvent(new ProgressEventArgs - { - ProgressDescription = "Импорт из SQL-дампа...", - PercentCompleted = 0 - }); - using (SqlDumpReader sqlDumpReader = new SqlDumpReader(sqlDumpFilePath)) - { - sqlDumpReader.ReadRowsProgress += SqlDumpReader_ReadRowsProgress; - List currentBatchBooks = new List(LocalDatabase.INSERT_TRANSACTION_BATCH); - foreach (Book book in sqlDumpReader.ReadRows()) - { - if (token.IsCancellationRequested) - { - break; - } - currentBatchBooks.Add(book); - if (currentBatchBooks.Count == LocalDatabase.INSERT_TRANSACTION_BATCH) - { - localDatabase.AddBooks(currentBatchBooks); - foreach (Book currentBatchBook in currentBatchBooks) - { - currentBatchBook.ExtendedProperties = null; - } - targetList.AddRange(currentBatchBooks); - currentBatchBooks.Clear(); - } - } - if (currentBatchBooks.Any()) - { - localDatabase.AddBooks(currentBatchBooks); - foreach (Book currentBatchBook in currentBatchBooks) - { - currentBatchBook.ExtendedProperties = null; - } - targetList.AddRange(currentBatchBooks); - } - sqlDumpReader.ReadRowsProgress -= SqlDumpReader_ReadRowsProgress; - } - RaiseCompletedEvent(); - } - - private void SqlDumpReader_ReadRowsProgress(object sender, SqlDumpReader.ReadRowsProgressEventArgs e) - { - RaiseProgressEvent(new ProgressEventArgs - { - ProgressDescription = $"Импорт из SQL-дампа... (импортировано {e.RowsParsed.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)} книг)", - PercentCompleted = ((double)e.CurrentPosition * 100 / e.TotalLength) - }); - } - } -} diff --git a/Infrastructure/Command.cs b/Infrastructure/Command.cs new file mode 100644 index 0000000..87a5f00 --- /dev/null +++ b/Infrastructure/Command.cs @@ -0,0 +1,49 @@ +using System; +using System.Windows.Input; + +namespace LibgenDesktop.Infrastructure +{ + internal class Command : ICommand + { + Predicate canExecute = null; + Action executeAction = null; + + public Command(Action executeAction) + : this(param => true, param => executeAction()) + { + } + + public Command(Action executeAction) + : this(param => true, executeAction) + { + } + + public Command(Predicate canExecute, Action executeAction) + { + this.canExecute = canExecute; + this.executeAction = executeAction; + } + + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + if (canExecute != null) + { + return canExecute(parameter); + } + return true; + } + + public void Execute(object parameter) + { + executeAction?.Invoke(parameter); + UpdateCanExecuteState(); + } + + public void UpdateCanExecuteState() + { + CanExecuteChanged?.Invoke(this, new EventArgs()); + } + } +} diff --git a/Infrastructure/ErrorEventArgs.cs b/Infrastructure/ErrorEventArgs.cs deleted file mode 100644 index 3ff0e3e..0000000 --- a/Infrastructure/ErrorEventArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace LibgenDesktop.Infrastructure -{ - public class ErrorEventArgs : EventArgs - { - public Exception Exception { get; set; } - } -} \ No newline at end of file diff --git a/Infrastructure/FuncCommand.cs b/Infrastructure/FuncCommand.cs new file mode 100644 index 0000000..e2dc959 --- /dev/null +++ b/Infrastructure/FuncCommand.cs @@ -0,0 +1,39 @@ +using System; +using System.Windows.Input; + +namespace LibgenDesktop.Infrastructure +{ + public class FuncCommand : ICommand + { + private readonly Func executeFunction; + + public FuncCommand(Func executeFunction) + { + this.executeFunction = executeFunction; + } + + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + return true; + } + + public void Execute(object parameter) + { + Execute(); + } + + public TResult Execute() + { + TResult result = executeFunction != null ? executeFunction() : default(TResult); + OnCanExecuteChanged(); + return result; + } + + protected virtual void OnCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/Infrastructure/IWindowContext.cs b/Infrastructure/IWindowContext.cs new file mode 100644 index 0000000..a599ba1 --- /dev/null +++ b/Infrastructure/IWindowContext.cs @@ -0,0 +1,20 @@ +using System; + +namespace LibgenDesktop.Infrastructure +{ + internal interface IWindowContext + { + object DataContext { get; } + + event EventHandler Activated; + event EventHandler Closed; + event EventHandler Closing; + event EventHandler Showing; + + void Close(); + void CloseDialog(bool dialogResult); + void Focus(); + void Show(bool showMaximized = false); + bool? ShowDialog(int? width = null, int? height = null, bool showMaximized = false); + } +} \ No newline at end of file diff --git a/Infrastructure/OpenFileDialogParameters.cs b/Infrastructure/OpenFileDialogParameters.cs new file mode 100644 index 0000000..73134d7 --- /dev/null +++ b/Infrastructure/OpenFileDialogParameters.cs @@ -0,0 +1,10 @@ +namespace LibgenDesktop.Infrastructure +{ + internal class OpenFileDialogParameters + { + public string DialogTitle { get; set; } + public string Filter { get; set; } + public bool Multiselect { get; set; } + public string InitialDirectory { get; set; } + } +} \ No newline at end of file diff --git a/Infrastructure/OpenFileDialogResult.cs b/Infrastructure/OpenFileDialogResult.cs new file mode 100644 index 0000000..849f3b7 --- /dev/null +++ b/Infrastructure/OpenFileDialogResult.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace LibgenDesktop.Infrastructure +{ + public class OpenFileDialogResult + { + public bool DialogResult { get; set; } + public List SelectedFilePaths { get; set; } + } +} \ No newline at end of file diff --git a/Infrastructure/ProgressEventArgs.cs b/Infrastructure/ProgressEventArgs.cs deleted file mode 100644 index aeca41c..0000000 --- a/Infrastructure/ProgressEventArgs.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace LibgenDesktop.Infrastructure -{ - public class ProgressEventArgs : EventArgs - { - public string ProgressDescription { get; set; } - public double PercentCompleted { get; set; } - } -} \ No newline at end of file diff --git a/Infrastructure/ProgressOperation.cs b/Infrastructure/ProgressOperation.cs deleted file mode 100644 index c348088..0000000 --- a/Infrastructure/ProgressOperation.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LibgenDesktop.Infrastructure -{ - internal abstract class ProgressOperation - { - private readonly CancellationTokenSource cancellationTokenSource; - private Task task; - - public ProgressOperation() - { - cancellationTokenSource = new CancellationTokenSource(); - task = null; - Failed = false; - } - - public bool Failed { get; private set; } - public virtual string Title { get; } - public virtual bool IsUnbounded => false; - - public event EventHandler ProgressEvent; - public event EventHandler CompletedEvent; - public event EventHandler CancelledEvent; - public event EventHandler ErrorEvent; - - public void Start() - { - Action taskAction = () => - { - try - { - DoWork(cancellationTokenSource.Token); - } - catch (Exception exception) - { - Failed = true; - RaiseErrorEvent(new ErrorEventArgs - { - Exception = exception - }); - } - }; - task = Task.Factory.StartNew(taskAction, cancellationTokenSource.Token); - } - - public void Cancel() - { - if (task != null && !Failed) - { - cancellationTokenSource.Cancel(); - task.Wait(); - task = null; - RaiseCancelledEvent(); - } - } - - protected abstract void DoWork(CancellationToken token); - - protected virtual void RaiseProgressEvent(ProgressEventArgs progressEventArgs) - { - ProgressEvent?.Invoke(this, progressEventArgs); - } - - protected virtual void RaiseCompletedEvent() - { - CompletedEvent?.Invoke(this, EventArgs.Empty); - } - - private void RaiseCancelledEvent() - { - CancelledEvent?.Invoke(this, EventArgs.Empty); - } - - protected virtual void RaiseErrorEvent(ErrorEventArgs errorEventArgs) - { - ErrorEvent?.Invoke(this, errorEventArgs); - } - } -} diff --git a/Infrastructure/RegisteredWindows.cs b/Infrastructure/RegisteredWindows.cs new file mode 100644 index 0000000..5090bf1 --- /dev/null +++ b/Infrastructure/RegisteredWindows.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using LibgenDesktop.ViewModels; +using LibgenDesktop.Views; + +namespace LibgenDesktop.Infrastructure +{ + internal static class RegisteredWindows + { + internal enum WindowKey + { + MAIN_WINDOW = 1, + BOOK_DETAILS_WINDOW, + ERROR_WINDOW, + SQL_DUMP_IMPORT_WINDOW + } + + internal class RegisteredWindow + { + public RegisteredWindow(WindowKey windowKey, Type windowType, Type viewModelType) + { + WindowKey = windowKey; + WindowType = windowType; + ViewModelType = viewModelType; + } + + public WindowKey WindowKey { get; } + public Type WindowType { get; } + public Type ViewModelType { get; } + } + + static RegisteredWindows() + { + AllWindows = new Dictionary + { + { WindowKey.MAIN_WINDOW, new RegisteredWindow(WindowKey.MAIN_WINDOW, typeof(MainWindow), typeof(MainWindowViewModel)) }, + { WindowKey.BOOK_DETAILS_WINDOW, new RegisteredWindow(WindowKey.BOOK_DETAILS_WINDOW, typeof(BookDetailsWindow), typeof(BookDetailsWindowViewModel)) }, + { WindowKey.ERROR_WINDOW, new RegisteredWindow(WindowKey.ERROR_WINDOW, typeof(ErrorWindow), typeof(ErrorWindowViewModel)) }, + { WindowKey.SQL_DUMP_IMPORT_WINDOW, new RegisteredWindow(WindowKey.SQL_DUMP_IMPORT_WINDOW, typeof(SqlDumpImportWindow), typeof(SqlDumpImportWindowViewModel)) } + }; + } + + public static Dictionary AllWindows { get; } + } +} diff --git a/Infrastructure/WindowContext.cs b/Infrastructure/WindowContext.cs new file mode 100644 index 0000000..16c56e6 --- /dev/null +++ b/Infrastructure/WindowContext.cs @@ -0,0 +1,136 @@ +using System; +using System.ComponentModel; +using System.Windows; + +namespace LibgenDesktop.Infrastructure +{ + internal class WindowContext : IWindowContext + { + private readonly Window parentWindow; + + public WindowContext(Window window, object dataContext, Window parentWindow) + { + Window = window; + DataContext = dataContext; + this.parentWindow = parentWindow; + Window.Activated += Window_Activated; + Window.Closing += Window_Closing; + Window.Closed += Window_Closed; + } + + public Window Window { get; } + public object DataContext { get; } + + public event EventHandler Activated; + public event EventHandler Showing; + public event EventHandler Closing; + public event EventHandler Closed; + + public void Show(bool showMaximized = false) + { + if (!Window.IsVisible) + { + OnShowing(); + Window.WindowState = GetWindowState(showMaximized); + Window.WindowStartupLocation = WindowStartupLocation.CenterScreen; + Window.Show(); + } + else + { + if (Window.WindowState == WindowState.Minimized) + { + Window.WindowState = WindowState.Normal; + } + else + { + Window.Activate(); + } + } + } + + public bool? ShowDialog(int? width = null, int? height = null, bool showMaximized = false) + { + OnShowing(); + if (width.HasValue) + { + Window.Width = width.Value; + } + if (height.HasValue) + { + Window.Height = height.Value; + } + if (parentWindow != null) + { + Window.Owner = parentWindow; + Window.WindowStartupLocation = WindowStartupLocation.CenterOwner; + } + return ShowDialog(showMaximized, true); + } + + public void Close() + { + Window.Close(); + } + + public void CloseDialog(bool dialogResult) + { + Window.DialogResult = dialogResult; + } + + public void Focus() + { + Window.Focus(); + } + + protected virtual void OnActivated() + { + Activated?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnShowing() + { + Showing?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnClosing() + { + Closing?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnClosed() + { + Closed?.Invoke(this, EventArgs.Empty); + } + + private bool? ShowDialog(bool showMaximized, bool hasOwner) + { + Window.WindowStartupLocation = hasOwner ? WindowStartupLocation.CenterOwner : WindowStartupLocation.CenterScreen; + Window.ShowInTaskbar = false; + Window.WindowState = GetWindowState(showMaximized); + return Window.ShowDialog(); + } + + private WindowState GetWindowState(bool isMaximized) + { + return isMaximized ? WindowState.Maximized : WindowState.Normal; + } + + private void Window_Activated(object sender, EventArgs e) + { + OnActivated(); + } + + private void Window_Closing(object sender, CancelEventArgs e) + { + OnClosing(); + } + + private void Window_Closed(object sender, EventArgs e) + { + OnClosed(); + Window.Activated -= Window_Activated; + Window.Closing -= Window_Closing; + Window.Closed -= Window_Closed; + } + } +} diff --git a/Infrastructure/WindowManager.cs b/Infrastructure/WindowManager.cs new file mode 100644 index 0000000..19ea27a --- /dev/null +++ b/Infrastructure/WindowManager.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; +using Microsoft.Win32; + +namespace LibgenDesktop.Infrastructure +{ + internal static class WindowManager + { + private const int GWL_STYLE = -16; + private const int WS_MAXIMIZEBOX = 0x10000; + private const int WS_MINIMIZEBOX = 0x20000; + private const int GWL_EXSTYLE = -20; + private const int WS_EX_DLGMODALFRAME = 0x0001; + private const int SWP_NOSIZE = 0x0001; + private const int SWP_NOMOVE = 0x0002; + private const int SWP_NOZORDER = 0x0004; + private const int SWP_FRAMECHANGED = 0x0020; + private const int WM_SETICON = 0x0080; + + private static List createdWindowContexts; + + [DllImport("user32.dll")] + private static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + private static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags); + + static WindowManager() + { + createdWindowContexts = new List(); + } + + public static int ScreenWidth + { + get + { + return (int)SystemParameters.WorkArea.Width; + } + } + + public static int ScreenHeight + { + get + { + return (int)SystemParameters.WorkArea.Height; + } + } + + public static IWindowContext CreateWindow(RegisteredWindows.WindowKey windowKey, object viewModel = null, IWindowContext parentWindowContext = null) + { + RegisteredWindows.RegisteredWindow registeredWindow = RegisteredWindows.AllWindows[windowKey]; + if (viewModel == null) + { + viewModel = Activator.CreateInstance(registeredWindow.ViewModelType); + if (viewModel == null) + { + throw new InvalidOperationException($"There was an error while trying to create an instance of {registeredWindow.WindowType.FullName} view model class."); + } + } + Window window = Activator.CreateInstance(registeredWindow.WindowType) as Window; + if (window == null) + { + throw new InvalidOperationException($"There was an error while trying to create an instance of {registeredWindow.WindowType.FullName} window class."); + } + window.DataContext = viewModel; + Window parentWindow = (parentWindowContext as WindowContext)?.Window; + WindowContext result = new WindowContext(window, viewModel, parentWindow); + result.Closed += WindowContext_Closed; + createdWindowContexts.Add(result); + return result; + } + + public static IWindowContext GetCreatedWindowContext(object viewModel) + { + return createdWindowContexts.FirstOrDefault(windowContext => ReferenceEquals(windowContext.DataContext, viewModel)); + } + + public static OpenFileDialogResult ShowOpenFileDialog(OpenFileDialogParameters openFileDialogParameters) + { + if (openFileDialogParameters == null) + { + throw new ArgumentNullException("openFileDialogParameters"); + } + OpenFileDialog openFileDialog = new OpenFileDialog(); + if (!String.IsNullOrEmpty(openFileDialogParameters.DialogTitle)) + { + openFileDialog.Title = openFileDialogParameters.DialogTitle; + } + if (!String.IsNullOrEmpty(openFileDialogParameters.Filter)) + { + openFileDialog.Filter = openFileDialogParameters.Filter; + } + openFileDialog.Multiselect = openFileDialogParameters.Multiselect; + if (!String.IsNullOrEmpty(openFileDialogParameters.InitialDirectory)) + { + openFileDialog.InitialDirectory = openFileDialogParameters.InitialDirectory; + } + OpenFileDialogResult result = new OpenFileDialogResult(); + result.DialogResult = openFileDialog.ShowDialog() == true; + result.SelectedFilePaths = result.DialogResult ? openFileDialog.FileNames.ToList() : new List(); + return result; + } + + public static void RemoveWindowMaximizeButton(Window window) + { + RemoveWindowStyle(window, WS_MAXIMIZEBOX); + } + + public static void RemoveWindowMinimizeButton(Window window) + { + RemoveWindowStyle(window, WS_MINIMIZEBOX); + } + + public static void RemoveWindowIcon(Window window) + { + IntPtr windowHandle = new WindowInteropHelper(window).Handle; + int windowExStyle = GetWindowLong(windowHandle, GWL_EXSTYLE); + SetWindowLong(windowHandle, GWL_EXSTYLE, windowExStyle | WS_EX_DLGMODALFRAME); + SendMessage(windowHandle, WM_SETICON, IntPtr.Zero, IntPtr.Zero); + SendMessage(windowHandle, WM_SETICON, new IntPtr(1), IntPtr.Zero); + SetWindowPos(windowHandle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + } + + private static void WindowContext_Closed(object sender, EventArgs e) + { + WindowContext closedWindowContext = (WindowContext)sender; + createdWindowContexts.Remove(closedWindowContext); + closedWindowContext.Closed -= WindowContext_Closed; + } + + private static void RemoveWindowStyle(Window window, int styleAttribute) + { + IntPtr windowHandle = new WindowInteropHelper(window).Handle; + int windowStyle = GetWindowLong(windowHandle, GWL_STYLE); + SetWindowLong(windowHandle, GWL_STYLE, windowStyle & ~styleAttribute); + } + } +} diff --git a/Interface/BookForm.Designer.cs b/Interface/BookForm.Designer.cs deleted file mode 100644 index f2931db..0000000 --- a/Interface/BookForm.Designer.cs +++ /dev/null @@ -1,216 +0,0 @@ -namespace LibgenDesktop.Interface -{ - partial class BookForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle(); - this.valueLabelContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); - this.copyValueLabelTextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.bookDetailsDataGridView = new System.Windows.Forms.DataGridView(); - this.downloadButton = new System.Windows.Forms.Button(); - this.closeButton = new System.Windows.Forms.Button(); - this.coverPanel = new System.Windows.Forms.Panel(); - this.coverLoadingLabel = new System.Windows.Forms.Label(); - this.bookCoverPictureBox = new System.Windows.Forms.PictureBox(); - this.titleColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.valueColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.valueLabelContextMenu.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.bookDetailsDataGridView)).BeginInit(); - this.coverPanel.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.bookCoverPictureBox)).BeginInit(); - this.SuspendLayout(); - // - // valueLabelContextMenu - // - this.valueLabelContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.copyValueLabelTextMenuItem}); - this.valueLabelContextMenu.Name = "valueLabelContextMenu"; - this.valueLabelContextMenu.Size = new System.Drawing.Size(147, 26); - // - // copyValueLabelTextMenuItem - // - this.copyValueLabelTextMenuItem.Name = "copyValueLabelTextMenuItem"; - this.copyValueLabelTextMenuItem.Size = new System.Drawing.Size(146, 22); - this.copyValueLabelTextMenuItem.Text = "Скопировать"; - this.copyValueLabelTextMenuItem.Click += new System.EventHandler(this.copyValueLabelTextMenuItem_Click); - // - // bookDetailsDataGridView - // - this.bookDetailsDataGridView.AllowUserToAddRows = false; - this.bookDetailsDataGridView.AllowUserToDeleteRows = false; - this.bookDetailsDataGridView.AllowUserToResizeRows = false; - this.bookDetailsDataGridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.bookDetailsDataGridView.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells; - this.bookDetailsDataGridView.BackgroundColor = System.Drawing.Color.White; - this.bookDetailsDataGridView.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.bookDetailsDataGridView.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None; - this.bookDetailsDataGridView.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.EnableWithoutHeaderText; - this.bookDetailsDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.bookDetailsDataGridView.ColumnHeadersVisible = false; - this.bookDetailsDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.titleColumn, - this.valueColumn}); - this.bookDetailsDataGridView.Location = new System.Drawing.Point(311, 11); - this.bookDetailsDataGridView.Name = "bookDetailsDataGridView"; - this.bookDetailsDataGridView.ReadOnly = true; - this.bookDetailsDataGridView.RowHeadersVisible = false; - this.bookDetailsDataGridView.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.bookDetailsDataGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; - this.bookDetailsDataGridView.Size = new System.Drawing.Size(862, 518); - this.bookDetailsDataGridView.TabIndex = 100; - this.bookDetailsDataGridView.CellContextMenuStripNeeded += new System.Windows.Forms.DataGridViewCellContextMenuStripNeededEventHandler(this.bookDetailsDataGridView_CellContextMenuStripNeeded); - this.bookDetailsDataGridView.SelectionChanged += new System.EventHandler(this.dataGridView1_SelectionChanged); - // - // downloadButton - // - this.downloadButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.downloadButton.Location = new System.Drawing.Point(867, 535); - this.downloadButton.Name = "downloadButton"; - this.downloadButton.Size = new System.Drawing.Size(150, 33); - this.downloadButton.TabIndex = 93; - this.downloadButton.Text = "Скачать с libgen.io"; - this.downloadButton.UseVisualStyleBackColor = true; - this.downloadButton.Click += new System.EventHandler(this.downloadButton_Click); - // - // closeButton - // - this.closeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.closeButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.closeButton.Location = new System.Drawing.Point(1023, 535); - this.closeButton.Name = "closeButton"; - this.closeButton.Size = new System.Drawing.Size(150, 33); - this.closeButton.TabIndex = 94; - this.closeButton.Text = "Закрыть"; - this.closeButton.UseVisualStyleBackColor = true; - // - // coverPanel - // - this.coverPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left))); - this.coverPanel.Controls.Add(this.coverLoadingLabel); - this.coverPanel.Controls.Add(this.bookCoverPictureBox); - this.coverPanel.Location = new System.Drawing.Point(11, 11); - this.coverPanel.Name = "coverPanel"; - this.coverPanel.Size = new System.Drawing.Size(294, 521); - this.coverPanel.TabIndex = 101; - // - // coverLoadingLabel - // - this.coverLoadingLabel.BackColor = System.Drawing.Color.Transparent; - this.coverLoadingLabel.Dock = System.Windows.Forms.DockStyle.Fill; - this.coverLoadingLabel.Location = new System.Drawing.Point(0, 0); - this.coverLoadingLabel.Name = "coverLoadingLabel"; - this.coverLoadingLabel.Size = new System.Drawing.Size(294, 521); - this.coverLoadingLabel.TabIndex = 95; - this.coverLoadingLabel.Text = "Загружается обложка..."; - this.coverLoadingLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // bookCoverPictureBox - // - this.bookCoverPictureBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.bookCoverPictureBox.Location = new System.Drawing.Point(0, 0); - this.bookCoverPictureBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.bookCoverPictureBox.Name = "bookCoverPictureBox"; - this.bookCoverPictureBox.Size = new System.Drawing.Size(294, 521); - this.bookCoverPictureBox.TabIndex = 0; - this.bookCoverPictureBox.TabStop = false; - // - // titleColumn - // - this.titleColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.titleColumn.DataPropertyName = "Title"; - dataGridViewCellStyle3.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - dataGridViewCellStyle3.ForeColor = System.Drawing.SystemColors.ControlText; - dataGridViewCellStyle3.Padding = new System.Windows.Forms.Padding(2); - this.titleColumn.DefaultCellStyle = dataGridViewCellStyle3; - this.titleColumn.HeaderText = "titleColumn"; - this.titleColumn.Name = "titleColumn"; - this.titleColumn.ReadOnly = true; - this.titleColumn.Width = 5; - // - // valueColumn - // - this.valueColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; - this.valueColumn.DataPropertyName = "Value"; - dataGridViewCellStyle4.ForeColor = System.Drawing.SystemColors.ControlText; - dataGridViewCellStyle4.Padding = new System.Windows.Forms.Padding(0, 2, 2, 2); - this.valueColumn.DefaultCellStyle = dataGridViewCellStyle4; - this.valueColumn.HeaderText = "valueColumn"; - this.valueColumn.Name = "valueColumn"; - this.valueColumn.ReadOnly = true; - // - // BookForm - // - this.AcceptButton = this.downloadButton; - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.White; - this.CancelButton = this.closeButton; - this.ClientSize = new System.Drawing.Size(1184, 579); - this.Controls.Add(this.coverPanel); - this.Controls.Add(this.downloadButton); - this.Controls.Add(this.closeButton); - this.Controls.Add(this.bookDetailsDataGridView); - this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.MinimumSize = new System.Drawing.Size(800, 600); - this.Name = "BookForm"; - this.Padding = new System.Windows.Forms.Padding(8); - this.ShowInTaskbar = false; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "BookForm"; - this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.BookForm_FormClosed); - this.Load += new System.EventHandler(this.BookForm_Load); - this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.BookForm_MouseMove); - this.valueLabelContextMenu.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.bookDetailsDataGridView)).EndInit(); - this.coverPanel.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.bookCoverPictureBox)).EndInit(); - this.ResumeLayout(false); - - } - - #endregion - private System.Windows.Forms.Button downloadButton; - private System.Windows.Forms.Button closeButton; - private System.Windows.Forms.ContextMenuStrip valueLabelContextMenu; - private System.Windows.Forms.ToolStripMenuItem copyValueLabelTextMenuItem; - private System.Windows.Forms.DataGridView bookDetailsDataGridView; - private System.Windows.Forms.Panel coverPanel; - private System.Windows.Forms.Label coverLoadingLabel; - private System.Windows.Forms.PictureBox bookCoverPictureBox; - private System.Windows.Forms.DataGridViewTextBoxColumn titleColumn; - private System.Windows.Forms.DataGridViewTextBoxColumn valueColumn; - } -} \ No newline at end of file diff --git a/Interface/BookForm.cs b/Interface/BookForm.cs deleted file mode 100644 index bcf9cd4..0000000 --- a/Interface/BookForm.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Windows.Forms; -using LibgenDesktop.Database; -using LibgenDesktop.Settings; - -namespace LibgenDesktop.Interface -{ - public partial class BookForm : Form - { - internal class BookProperty - { - public string Title { get; set; } - public string Value { get; set; } - } - - private const string BOOK_COVER_URL_PREFIX = "http://libgen.io/covers/"; - private const string BOOK_DOWNLOAD_URL_PREFIX = "http://libgen.io/book/index.php?md5="; - - private readonly Book book; - private readonly bool offlineMode; - private List bookProperties; - private ToolTip offlineModeTooltip; - private bool offlineModeTooltipVisible; - - internal BookForm(Book book) - { - InitializeComponent(); - this.book = book; - AppSettings appSettings = SettingsStorage.AppSettings; - offlineMode = appSettings.OfflineMode; - Width = appSettings.BookWindow.Width; - Height = appSettings.BookWindow.Height; - MinimumSize = new Size(AppSettings.BOOK_WINDOW_MIN_WIDTH, AppSettings.BOOK_WINDOW_MIN_HEIGHT); - Text = $"{book.Title} (книга №{book.Id})"; - PopulateBookFields(); - LoadCover(book.ExtendedProperties.CoverUrl); - } - - private void BookForm_Load(object sender, EventArgs e) - { - Icon = IconUtils.GetAppIcon(); - if (offlineMode) - { - downloadButton.Enabled = false; - offlineModeTooltip = new ToolTip(); - offlineModeTooltipVisible = false; - } - } - - private void PopulateBookFields() - { - bookProperties = new List(); - AddBookProperty("Наименование", book.Title); - AddBookProperty("Авторы", book.Authors); - AddBookProperty("Серия", book.Series); - AddBookProperty("Издатель", book.Title); - AddBookProperty("Год", book.Year); - AddBookProperty("Язык", book.ExtendedProperties.Language); - AddBookProperty("Формат", book.Format); - AddBookProperty("ISBN", book.ExtendedProperties.Identifier); - AddBookProperty("Добавлено", book.ExtendedProperties.AddedDateTime.ToString("dd.MM.yyyy HH:mm:ss")); - AddBookProperty("Обновлено", book.ExtendedProperties.LastModifiedDateTime.ToString("dd.MM.yyyy HH:mm:ss")); - AddBookProperty("Библиотека", book.ExtendedProperties.Library); - AddBookProperty("Размер файла", book.FileSizeWithBytesString); - AddBookProperty("Темы", book.ExtendedProperties.Topic); - AddBookProperty("Том", book.ExtendedProperties.VolumeInfo); - AddBookProperty("Журнал", book.ExtendedProperties.Periodical); - AddBookProperty("Город", book.ExtendedProperties.City); - AddBookProperty("Издание", book.ExtendedProperties.Edition); - AddBookProperty("Страниц", PagesToString(book.ExtendedProperties.Pages, book.ExtendedProperties.PagesInFile)); - AddBookProperty("Теги", book.ExtendedProperties.Tags); - AddBookProperty("MD5-хэш", book.ExtendedProperties.Md5Hash); - AddBookProperty("Libgen ID", book.ExtendedProperties.LibgenId.ToString()); - AddBookProperty("ISSN", book.ExtendedProperties.Issn); - AddBookProperty("UDC", book.ExtendedProperties.Udc); - AddBookProperty("LBC", book.ExtendedProperties.Lbc); - AddBookProperty("LCC", book.ExtendedProperties.Lcc); - AddBookProperty("DDC", book.ExtendedProperties.Ddc); - AddBookProperty("DOI", book.ExtendedProperties.Doi); - AddBookProperty("OpenLibraryID", book.ExtendedProperties.Doi); - AddBookProperty("GoogleID", book.ExtendedProperties.Doi); - AddBookProperty("ASIN", book.ExtendedProperties.Doi); - AddBookProperty("DPI", book.ExtendedProperties.Dpi.ToString()); - AddBookProperty("OCR", StringBooleanToLabelString(book.Searchable, "да", "нет", "неизвестно")); - AddBookProperty("Оглавление", StringBooleanToLabelString(book.ExtendedProperties.Bookmarked, "есть", "нет", "неизвестно")); - AddBookProperty("Отсканирована", StringBooleanToLabelString(book.ExtendedProperties.Scanned, "да", "нет", "неизвестно")); - AddBookProperty("Ориентация", StringBooleanToLabelString(book.ExtendedProperties.Orientation, "портретная", "альбомная", "неизвестно")); - AddBookProperty("Постраничная", StringBooleanToLabelString(book.ExtendedProperties.Paginated, "есть", "нет", "неизвестно")); - AddBookProperty("Цветная", StringBooleanToLabelString(book.ExtendedProperties.Color, "да", "нет", "неизвестно")); - AddBookProperty("Вычищенная", StringBooleanToLabelString(book.ExtendedProperties.Cleaned, "да", "нет", "неизвестно")); - AddBookProperty("Комментарий", book.ExtendedProperties.Commentary); - bookDetailsDataGridView.DataSource = bookProperties; - } - - private void AddBookProperty(string title, string value) - { - bookProperties.Add(new BookProperty - { - Title = title + ':', - Value = value - }); - } - - private void ValueLabel_MouseDown(object sender, MouseEventArgs e) - { - if (e.Button == MouseButtons.Right) - { - copyValueLabelTextMenuItem.Text = $"Скопировать \"{(sender as Label).Text}\""; - } - } - - private string StringBooleanToLabelString(string value, string value1Label, string value0Label, string valueUnknownLabel) - { - switch (value) - { - case "0": - return value0Label; - case "1": - return value1Label; - default: - return valueUnknownLabel; - } - } - - private string PagesToString(string pages, int pagesInFile) - { - StringBuilder resultBuilder = new StringBuilder(); - if (!String.IsNullOrWhiteSpace(pages)) - { - resultBuilder.Append(pages); - } - else - { - resultBuilder.Append("неизвестно"); - } - resultBuilder.Append(" (содержательная часть) / "); - resultBuilder.Append(pagesInFile.ToString()); - resultBuilder.Append(" (всего в файле)"); - return resultBuilder.ToString(); - } - - private void LoadCover(string bookCoverUrl) - { - if (String.IsNullOrWhiteSpace(bookCoverUrl)) - { - coverLoadingLabel.Text = "Обложка отсутствует"; - } - else - { - if (offlineMode) - { - coverLoadingLabel.Text = "Обложка не загружена, потому что\r\nвключен автономный режим"; - } - else - { - coverLoadingLabel.Text = "Загружается обложка..."; - WebClient webClient = new WebClient(); - webClient.DownloadDataCompleted += WebClient_DownloadDataCompleted; - webClient.DownloadDataAsync(new Uri(BOOK_COVER_URL_PREFIX + bookCoverUrl)); - } - } - } - - private void WebClient_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e) - { - if (e.Error != null) - { - coverLoadingLabel.Text = "Не удалось загрузить обложку"; - } - else - { - coverLoadingLabel.Visible = false; - using (MemoryStream memoryStream = new MemoryStream(e.Result)) - using (Image image = Image.FromStream(memoryStream)) - { - bookCoverPictureBox.Image = ResizeImage(image); - } - } - } - - private Image ResizeImage(Image source) - { - int width = bookCoverPictureBox.Width; - int height = (int)Math.Round(source.Height * ((double)width / source.Width)); - using (Bitmap bitmap = new Bitmap(width, height)) - using (Graphics graphics = Graphics.FromImage((Image)bitmap)) - { - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.DrawImage(source, 0, 0, width, height); - return (Image)bitmap.Clone(); - } - } - - private void downloadButton_Click(object sender, EventArgs e) - { - Process.Start(BOOK_DOWNLOAD_URL_PREFIX + book.ExtendedProperties.Md5Hash); - } - - private void BookForm_MouseMove(object sender, MouseEventArgs e) - { - if (offlineModeTooltip == null) - { - return; - } - Control controlUnderPointer = GetChildAtPoint(e.Location); - if (controlUnderPointer == downloadButton) - { - if (!offlineModeTooltipVisible) - { - offlineModeTooltip.Show("Включен автономный режим", downloadButton, downloadButton.Width / 2, downloadButton.Height); - offlineModeTooltipVisible = true; - } - } - else - { - if (offlineModeTooltipVisible) - { - offlineModeTooltip.Hide(downloadButton); - offlineModeTooltipVisible = false; - } - } - } - - private void BookForm_FormClosed(object sender, FormClosedEventArgs e) - { - SettingsStorage.AppSettings.BookWindow.Width = Width; - SettingsStorage.AppSettings.BookWindow.Height = Height; - SettingsStorage.SaveSettings(); - } - - private void copyValueLabelTextMenuItem_Click(object sender, EventArgs e) - { - Clipboard.SetText((sender as ToolStripMenuItem).Tag.ToString()); - } - - private void dataGridView1_SelectionChanged(object sender, EventArgs e) - { - bookDetailsDataGridView.ClearSelection(); - } - - private void bookDetailsDataGridView_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e) - { - if (e.ColumnIndex == 1) - { - string cellText = bookDetailsDataGridView.Rows[e.RowIndex].Cells[1].Value.ToString(); - if (!String.IsNullOrWhiteSpace(cellText)) - { - copyValueLabelTextMenuItem.Text = $"Скопировать \"{cellText}\""; - copyValueLabelTextMenuItem.Tag = cellText; - e.ContextMenuStrip = valueLabelContextMenu; - } - } - } - } -} diff --git a/Interface/BookForm.resx b/Interface/BookForm.resx deleted file mode 100644 index 463d076..0000000 --- a/Interface/BookForm.resx +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - - True - - - True - - - True - - - True - - \ No newline at end of file diff --git a/Interface/ErrorForm.Designer.cs b/Interface/ErrorForm.Designer.cs deleted file mode 100644 index eb05adf..0000000 --- a/Interface/ErrorForm.Designer.cs +++ /dev/null @@ -1,117 +0,0 @@ -namespace LibgenDesktop.Interface -{ - partial class ErrorForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.errorHeaderLabel = new System.Windows.Forms.Label(); - this.detailsTextBox = new System.Windows.Forms.TextBox(); - this.copyButton = new System.Windows.Forms.Button(); - this.closeButton = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // errorHeaderLabel - // - this.errorHeaderLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.errorHeaderLabel.Location = new System.Drawing.Point(14, 12); - this.errorHeaderLabel.Name = "errorHeaderLabel"; - this.errorHeaderLabel.Size = new System.Drawing.Size(576, 30); - this.errorHeaderLabel.TabIndex = 0; - this.errorHeaderLabel.Text = "Возникла непредвиденная ошибка:"; - this.errorHeaderLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // detailsTextBox - // - this.detailsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.detailsTextBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); - this.detailsTextBox.Location = new System.Drawing.Point(12, 45); - this.detailsTextBox.Multiline = true; - this.detailsTextBox.Name = "detailsTextBox"; - this.detailsTextBox.ReadOnly = true; - this.detailsTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.detailsTextBox.Size = new System.Drawing.Size(580, 321); - this.detailsTextBox.TabIndex = 3; - this.detailsTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.detailsTextBox_KeyDown); - // - // copyButton - // - this.copyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.copyButton.Location = new System.Drawing.Point(12, 372); - this.copyButton.Name = "copyButton"; - this.copyButton.Size = new System.Drawing.Size(160, 33); - this.copyButton.TabIndex = 1; - this.copyButton.Text = "Скопировать детали"; - this.copyButton.UseVisualStyleBackColor = true; - this.copyButton.Click += new System.EventHandler(this.copyButton_Click); - // - // closeButton - // - this.closeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.closeButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.closeButton.Location = new System.Drawing.Point(488, 372); - this.closeButton.Name = "closeButton"; - this.closeButton.Size = new System.Drawing.Size(104, 33); - this.closeButton.TabIndex = 2; - this.closeButton.Text = "Закрыть"; - this.closeButton.UseVisualStyleBackColor = true; - // - // ErrorForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.White; - this.CancelButton = this.closeButton; - this.ClientSize = new System.Drawing.Size(604, 417); - this.Controls.Add(this.closeButton); - this.Controls.Add(this.copyButton); - this.Controls.Add(this.detailsTextBox); - this.Controls.Add(this.errorHeaderLabel); - this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.MinimumSize = new System.Drawing.Size(400, 300); - this.Name = "ErrorForm"; - this.ShowIcon = false; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "Ошибка"; - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Label errorHeaderLabel; - private System.Windows.Forms.TextBox detailsTextBox; - private System.Windows.Forms.Button copyButton; - private System.Windows.Forms.Button closeButton; - } -} \ No newline at end of file diff --git a/Interface/ErrorForm.cs b/Interface/ErrorForm.cs deleted file mode 100644 index dafb5a4..0000000 --- a/Interface/ErrorForm.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Windows.Forms; - -namespace LibgenDesktop.Interface -{ - public partial class ErrorForm : Form - { - public ErrorForm(string errorDetails) - { - InitializeComponent(); - detailsTextBox.Text = errorDetails; - } - - private void copyButton_Click(object sender, EventArgs e) - { - Clipboard.SetText(detailsTextBox.Text); - } - - private void detailsTextBox_KeyDown(object sender, KeyEventArgs e) - { - if (e.Control && (e.KeyCode == Keys.A)) - { - if (sender != null) - { - ((TextBox)sender).SelectAll(); - } - e.Handled = true; - } - } - } -} diff --git a/Interface/ErrorForm.resx b/Interface/ErrorForm.resx deleted file mode 100644 index 1af7de1..0000000 --- a/Interface/ErrorForm.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Interface/IconUtils.cs b/Interface/IconUtils.cs deleted file mode 100644 index 6af9456..0000000 --- a/Interface/IconUtils.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Drawing; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Security; -using System.Text; - -namespace LibgenDesktop.Interface -{ - internal static class IconUtils - { - private static Icon cachedIcon; - - static IconUtils() - { - cachedIcon = null; - } - - public static Icon GetAppIcon() - { - if (cachedIcon != null) - { - return cachedIcon; - } - string appFilePath = Assembly.GetExecutingAssembly().Location; - Uri uri; - try - { - uri = new Uri(appFilePath); - } - catch (UriFormatException) - { - uri = new Uri(Path.GetFullPath(appFilePath)); - } - if (uri.IsFile) - { - if (!File.Exists(appFilePath)) - { - throw new FileNotFoundException(appFilePath); - } - StringBuilder iconPath = new StringBuilder(260); - iconPath.Append(appFilePath); - int index = 0; - IntPtr handle = SafeNativeMethods.ExtractAssociatedIcon(new HandleRef(null, IntPtr.Zero), iconPath, ref index); - if (handle != IntPtr.Zero) - { - cachedIcon = Icon.FromHandle(handle); - } - } - return cachedIcon; - } - } - - [SuppressUnmanagedCodeSecurity] - internal static class SafeNativeMethods - { - [DllImport("shell32.dll", EntryPoint = "ExtractAssociatedIcon", CharSet = CharSet.Auto)] - internal static extern IntPtr ExtractAssociatedIcon(HandleRef hInst, StringBuilder iconPath, ref int index); - } -} diff --git a/Interface/MainForm.Designer.cs b/Interface/MainForm.Designer.cs deleted file mode 100644 index c21e4b6..0000000 --- a/Interface/MainForm.Designer.cs +++ /dev/null @@ -1,391 +0,0 @@ -namespace LibgenDesktop.Interface -{ - partial class MainForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); - this.mainMenu = new System.Windows.Forms.MenuStrip(); - this.fileSubMenu = new System.Windows.Forms.ToolStripMenuItem(); - this.newDatabaseMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.openDatabaseMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.importFromSqlDumpMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.syncMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.offlineModeMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.exitMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.searchTextBox = new System.Windows.Forms.TextBox(); - this.openSqlDumpDialog = new System.Windows.Forms.OpenFileDialog(); - this.bookListView = new BrightIdeasSoftware.VirtualObjectListView(); - this.idColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.titleColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.authorsColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.seriesColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.yearColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.publisherColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.formatColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.fileSizeColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.ocrColumn = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); - this.bookListImages = new System.Windows.Forms.ImageList(this.components); - this.progressBar = new System.Windows.Forms.ProgressBar(); - this.statusPanel = new System.Windows.Forms.StatusStrip(); - this.statusLabel = new System.Windows.Forms.ToolStripStatusLabel(); - this.spacerLabel = new System.Windows.Forms.ToolStripStatusLabel(); - this.connectionStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); - this.mainMenu.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.bookListView)).BeginInit(); - this.statusPanel.SuspendLayout(); - this.SuspendLayout(); - // - // mainMenu - // - this.mainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileSubMenu}); - this.mainMenu.Location = new System.Drawing.Point(0, 0); - this.mainMenu.Name = "mainMenu"; - this.mainMenu.Padding = new System.Windows.Forms.Padding(7, 3, 0, 3); - this.mainMenu.Size = new System.Drawing.Size(1184, 25); - this.mainMenu.TabIndex = 0; - this.mainMenu.Text = "menuStrip1"; - // - // fileSubMenu - // - this.fileSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.newDatabaseMenuItem, - this.openDatabaseMenuItem, - this.importFromSqlDumpMenuItem, - this.syncMenuItem, - this.offlineModeMenuItem, - this.exitMenuItem}); - this.fileSubMenu.Name = "fileSubMenu"; - this.fileSubMenu.Size = new System.Drawing.Size(48, 19); - this.fileSubMenu.Text = "Файл"; - // - // newDatabaseMenuItem - // - this.newDatabaseMenuItem.Enabled = false; - this.newDatabaseMenuItem.Name = "newDatabaseMenuItem"; - this.newDatabaseMenuItem.Size = new System.Drawing.Size(205, 22); - this.newDatabaseMenuItem.Text = "Новая БД..."; - // - // openDatabaseMenuItem - // - this.openDatabaseMenuItem.Enabled = false; - this.openDatabaseMenuItem.Name = "openDatabaseMenuItem"; - this.openDatabaseMenuItem.Size = new System.Drawing.Size(205, 22); - this.openDatabaseMenuItem.Text = "Открыть БД..."; - this.openDatabaseMenuItem.Click += new System.EventHandler(this.openDatabaseMenuItem_Click); - // - // importFromSqlDumpMenuItem - // - this.importFromSqlDumpMenuItem.Enabled = false; - this.importFromSqlDumpMenuItem.Name = "importFromSqlDumpMenuItem"; - this.importFromSqlDumpMenuItem.Size = new System.Drawing.Size(205, 22); - this.importFromSqlDumpMenuItem.Text = "Импорт из SQL-дампа..."; - this.importFromSqlDumpMenuItem.Click += new System.EventHandler(this.importFromSqlDumpMenuItem_Click); - // - // syncMenuItem - // - this.syncMenuItem.Enabled = false; - this.syncMenuItem.Name = "syncMenuItem"; - this.syncMenuItem.Size = new System.Drawing.Size(205, 22); - this.syncMenuItem.Text = "Синхронизация..."; - // - // offlineModeMenuItem - // - this.offlineModeMenuItem.CheckOnClick = true; - this.offlineModeMenuItem.Name = "offlineModeMenuItem"; - this.offlineModeMenuItem.Size = new System.Drawing.Size(205, 22); - this.offlineModeMenuItem.Text = "Работать автономно"; - this.offlineModeMenuItem.Click += new System.EventHandler(this.offlineModeMenuItem_Click); - // - // exitMenuItem - // - this.exitMenuItem.Name = "exitMenuItem"; - this.exitMenuItem.Size = new System.Drawing.Size(205, 22); - this.exitMenuItem.Text = "Выход"; - this.exitMenuItem.Click += new System.EventHandler(this.exitMenuItem_Click); - // - // searchTextBox - // - this.searchTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.searchTextBox.BackColor = System.Drawing.Color.White; - this.searchTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.searchTextBox.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.searchTextBox.Location = new System.Drawing.Point(5, 31); - this.searchTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.searchTextBox.Name = "searchTextBox"; - this.searchTextBox.ReadOnly = true; - this.searchTextBox.Size = new System.Drawing.Size(1172, 29); - this.searchTextBox.TabIndex = 2; - this.searchTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.searchTextBox_KeyDown); - this.searchTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.searchTextBox_KeyPress); - // - // openSqlDumpDialog - // - this.openSqlDumpDialog.Filter = "SQL-дампы (*.sql, *.zip, *.rar)|*.sql;*zip;*.rar|Все файлы (*.*)|*.*"; - this.openSqlDumpDialog.Title = "Выбор SQL-дампа"; - // - // bookListView - // - this.bookListView.AllColumns.Add(this.idColumn); - this.bookListView.AllColumns.Add(this.titleColumn); - this.bookListView.AllColumns.Add(this.authorsColumn); - this.bookListView.AllColumns.Add(this.seriesColumn); - this.bookListView.AllColumns.Add(this.yearColumn); - this.bookListView.AllColumns.Add(this.publisherColumn); - this.bookListView.AllColumns.Add(this.formatColumn); - this.bookListView.AllColumns.Add(this.fileSizeColumn); - this.bookListView.AllColumns.Add(this.ocrColumn); - this.bookListView.AllowColumnReorder = true; - this.bookListView.AlternateRowBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); - this.bookListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.bookListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.bookListView.CellEditUseWholeCell = false; - this.bookListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.idColumn, - this.titleColumn, - this.authorsColumn, - this.seriesColumn, - this.yearColumn, - this.publisherColumn, - this.formatColumn, - this.fileSizeColumn, - this.ocrColumn}); - this.bookListView.Cursor = System.Windows.Forms.Cursors.Default; - this.bookListView.FullRowSelect = true; - this.bookListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; - this.bookListView.HeaderUsesThemes = true; - this.bookListView.IsSearchOnSortColumn = false; - this.bookListView.Location = new System.Drawing.Point(5, 67); - this.bookListView.MultiSelect = false; - this.bookListView.Name = "bookListView"; - this.bookListView.RowHeight = 26; - this.bookListView.SelectAllOnControlA = false; - this.bookListView.ShowGroups = false; - this.bookListView.Size = new System.Drawing.Size(1172, 519); - this.bookListView.SmallImageList = this.bookListImages; - this.bookListView.TabIndex = 3; - this.bookListView.UseAlternatingBackColors = true; - this.bookListView.UseCompatibleStateImageBehavior = false; - this.bookListView.UseHotControls = false; - this.bookListView.UseOverlays = false; - this.bookListView.UseTranslucentSelection = true; - this.bookListView.View = System.Windows.Forms.View.Details; - this.bookListView.VirtualMode = true; - this.bookListView.BeforeSearching += new System.EventHandler(this.bookListView_BeforeSearching); - this.bookListView.DoubleClick += new System.EventHandler(this.bookListView_DoubleClick); - this.bookListView.KeyDown += new System.Windows.Forms.KeyEventHandler(this.bookListView_KeyDown); - // - // idColumn - // - this.idColumn.AspectName = "Id"; - this.idColumn.MinimumWidth = 20; - this.idColumn.Searchable = false; - this.idColumn.Text = "№"; - // - // titleColumn - // - this.titleColumn.AspectName = "Title"; - this.titleColumn.MinimumWidth = 100; - this.titleColumn.Searchable = false; - this.titleColumn.Text = "Наименование"; - this.titleColumn.Width = 100; - // - // authorsColumn - // - this.authorsColumn.AspectName = "Authors"; - this.authorsColumn.MinimumWidth = 100; - this.authorsColumn.Searchable = false; - this.authorsColumn.Text = "Авторы"; - this.authorsColumn.Width = 100; - // - // seriesColumn - // - this.seriesColumn.AspectName = "Series"; - this.seriesColumn.MinimumWidth = 100; - this.seriesColumn.Searchable = false; - this.seriesColumn.Text = "Серия"; - this.seriesColumn.Width = 100; - // - // yearColumn - // - this.yearColumn.AspectName = "Year"; - this.yearColumn.MinimumWidth = 20; - this.yearColumn.Searchable = false; - this.yearColumn.Text = "Год"; - this.yearColumn.Width = 110; - // - // publisherColumn - // - this.publisherColumn.AspectName = "Publisher"; - this.publisherColumn.MinimumWidth = 50; - this.publisherColumn.Searchable = false; - this.publisherColumn.Text = "Издатель"; - this.publisherColumn.Width = 150; - // - // formatColumn - // - this.formatColumn.AspectName = "Format"; - this.formatColumn.MinimumWidth = 20; - this.formatColumn.Searchable = false; - this.formatColumn.Text = "Формат"; - // - // fileSizeColumn - // - this.fileSizeColumn.AspectName = "FileSizeString"; - this.fileSizeColumn.MinimumWidth = 30; - this.fileSizeColumn.Searchable = false; - this.fileSizeColumn.Text = "Размер файла"; - this.fileSizeColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.fileSizeColumn.Width = 100; - // - // ocrColumn - // - this.ocrColumn.AspectName = ""; - this.ocrColumn.MinimumWidth = 40; - this.ocrColumn.Searchable = false; - this.ocrColumn.Text = "OCR"; - this.ocrColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; - this.ocrColumn.Width = 40; - // - // bookListImages - // - this.bookListImages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("bookListImages.ImageStream"))); - this.bookListImages.TransparentColor = System.Drawing.Color.Transparent; - this.bookListImages.Images.SetKeyName(0, "check"); - // - // progressBar - // - this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.progressBar.Location = new System.Drawing.Point(5, 62); - this.progressBar.Maximum = 1000; - this.progressBar.Name = "progressBar"; - this.progressBar.Size = new System.Drawing.Size(1172, 3); - this.progressBar.Style = System.Windows.Forms.ProgressBarStyle.Continuous; - this.progressBar.TabIndex = 4; - this.progressBar.Visible = false; - // - // statusPanel - // - this.statusPanel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.statusPanel.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.statusLabel, - this.spacerLabel, - this.connectionStatusLabel}); - this.statusPanel.Location = new System.Drawing.Point(0, 586); - this.statusPanel.Name = "statusPanel"; - this.statusPanel.RenderMode = System.Windows.Forms.ToolStripRenderMode.ManagerRenderMode; - this.statusPanel.Size = new System.Drawing.Size(1184, 25); - this.statusPanel.TabIndex = 5; - // - // statusLabel - // - this.statusLabel.Margin = new System.Windows.Forms.Padding(1, 3, 0, 5); - this.statusLabel.Name = "statusLabel"; - this.statusLabel.Size = new System.Drawing.Size(71, 17); - this.statusLabel.Text = "Status text."; - // - // spacerLabel - // - this.spacerLabel.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; - this.spacerLabel.Name = "spacerLabel"; - this.spacerLabel.Size = new System.Drawing.Size(970, 20); - this.spacerLabel.Spring = true; - this.spacerLabel.TextDirection = System.Windows.Forms.ToolStripTextDirection.Horizontal; - // - // connectionStatusLabel - // - this.connectionStatusLabel.Name = "connectionStatusLabel"; - this.connectionStatusLabel.Size = new System.Drawing.Size(127, 20); - this.connectionStatusLabel.Text = "Автономный режим"; - // - // MainForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(1184, 611); - this.Controls.Add(this.statusPanel); - this.Controls.Add(this.progressBar); - this.Controls.Add(this.bookListView); - this.Controls.Add(this.searchTextBox); - this.Controls.Add(this.mainMenu); - this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.MainMenuStrip = this.mainMenu; - this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.Name = "MainForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Libgen Desktop"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); - this.Load += new System.EventHandler(this.MainForm_Load); - this.Shown += new System.EventHandler(this.MainForm_Shown); - this.mainMenu.ResumeLayout(false); - this.mainMenu.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.bookListView)).EndInit(); - this.statusPanel.ResumeLayout(false); - this.statusPanel.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.MenuStrip mainMenu; - private System.Windows.Forms.ToolStripMenuItem fileSubMenu; - private System.Windows.Forms.TextBox searchTextBox; - private System.Windows.Forms.ToolStripMenuItem newDatabaseMenuItem; - private System.Windows.Forms.ToolStripMenuItem openDatabaseMenuItem; - private System.Windows.Forms.ToolStripMenuItem importFromSqlDumpMenuItem; - private System.Windows.Forms.ToolStripMenuItem syncMenuItem; - private System.Windows.Forms.ToolStripMenuItem exitMenuItem; - private System.Windows.Forms.OpenFileDialog openSqlDumpDialog; - private BrightIdeasSoftware.OLVColumn idColumn; - private BrightIdeasSoftware.OLVColumn titleColumn; - private BrightIdeasSoftware.OLVColumn authorsColumn; - private BrightIdeasSoftware.OLVColumn seriesColumn; - private BrightIdeasSoftware.OLVColumn yearColumn; - private BrightIdeasSoftware.OLVColumn publisherColumn; - private BrightIdeasSoftware.OLVColumn formatColumn; - private BrightIdeasSoftware.OLVColumn fileSizeColumn; - private BrightIdeasSoftware.VirtualObjectListView bookListView; - private System.Windows.Forms.ProgressBar progressBar; - private System.Windows.Forms.StatusStrip statusPanel; - private System.Windows.Forms.ToolStripStatusLabel statusLabel; - private System.Windows.Forms.ToolStripMenuItem offlineModeMenuItem; - private System.Windows.Forms.ToolStripStatusLabel spacerLabel; - private System.Windows.Forms.ToolStripStatusLabel connectionStatusLabel; - private BrightIdeasSoftware.OLVColumn ocrColumn; - private System.Windows.Forms.ImageList bookListImages; - } -} - diff --git a/Interface/MainForm.cs b/Interface/MainForm.cs deleted file mode 100644 index 35791cb..0000000 --- a/Interface/MainForm.cs +++ /dev/null @@ -1,298 +0,0 @@ -using System; -using System.Diagnostics; -using System.Drawing; -using System.Windows.Forms; -using BrightIdeasSoftware; -using LibgenDesktop.Cache; -using LibgenDesktop.Database; -using LibgenDesktop.Infrastructure; -using LibgenDesktop.Settings; - -namespace LibgenDesktop.Interface -{ - public partial class MainForm : Form - { - private const int MIN_BOOK_COUNT_FOR_INITIAL_LOAD_UPDATE = 20000; - - private readonly LocalDatabase localDatabase; - private readonly DataCache dataCache; - private ProgressOperation currentProgressOperation; - - public MainForm() - { - SettingsStorage.LoadSettings(); - localDatabase = new LocalDatabase(SettingsStorage.AppSettings.DatabaseFileName); - dataCache = new DataCache(localDatabase); - currentProgressOperation = null; - InitializeComponent(); - ocrColumn.ImageGetter = book => (book as Book).Ocr ? "check" : null; - MinimumSize = new Size(AppSettings.MAIN_WINDOW_MIN_WIDTH, AppSettings.MAIN_WINDOW_MIN_HEIGHT); - } - - private void MainForm_Load(object sender, EventArgs e) - { - AppSettings appSettings = SettingsStorage.AppSettings; - if (appSettings.MainWindow.Maximized) - { - WindowState = FormWindowState.Maximized; - } - else - { - Left = appSettings.MainWindow.Left; - Top = appSettings.MainWindow.Top; - Width = appSettings.MainWindow.Width; - Height = appSettings.MainWindow.Height; - } - idColumn.MinimumWidth = AppSettings.ID_COLUMN_MIN_WIDTH; - idColumn.Width = appSettings.Columns.IdColumnWidth; - titleColumn.MinimumWidth = AppSettings.TITLE_COLUMN_MIN_WIDTH; - titleColumn.Width = appSettings.Columns.TitleColumnWidth; - authorsColumn.MinimumWidth = AppSettings.AUTHORS_COLUMN_MIN_WIDTH; - authorsColumn.Width = appSettings.Columns.AuthorsColumnWidth; - seriesColumn.MinimumWidth = AppSettings.SERIES_COLUMN_MIN_WIDTH; - seriesColumn.Width = appSettings.Columns.SeriesColumnWidth; - yearColumn.MinimumWidth = AppSettings.YEAR_COLUMN_MIN_WIDTH; - yearColumn.Width = appSettings.Columns.YearColumnWidth; - publisherColumn.MinimumWidth = AppSettings.PUBLISHER_COLUMN_MIN_WIDTH; - publisherColumn.Width = appSettings.Columns.PublisherColumnWidth; - formatColumn.MinimumWidth = AppSettings.FORMAT_COLUMN_MIN_WIDTH; - formatColumn.Width = appSettings.Columns.FormatColumnWidth; - fileSizeColumn.MinimumWidth = AppSettings.FILESIZE_COLUMN_MIN_WIDTH; - fileSizeColumn.Width = appSettings.Columns.FileSizeColumnWidth; - offlineModeMenuItem.Checked = appSettings.OfflineMode; - UpdateOfflineModeStatus(false); - Icon = IconUtils.GetAppIcon(); - mainMenu.BackColor = Color.White; - statusPanel.BackColor = Color.White; - StartProgressOperation(dataCache.CreateLoadBooksOperation()); - bookListView.VirtualListDataSource = dataCache.DataAccessor; - } - - private void MainForm_Shown(object sender, EventArgs e) - { - searchTextBox.Focus(); - } - - private void exitMenuItem_Click(object sender, EventArgs e) - { - Close(); - } - - private void openDatabaseMenuItem_Click(object sender, EventArgs e) - { - - } - - private void importFromSqlDumpMenuItem_Click(object sender, EventArgs e) - { - if (openSqlDumpDialog.ShowDialog() == DialogResult.OK) - { - ProgressOperation importSqlDumpOperation = dataCache.CreateImportSqlDumpOperation(openSqlDumpDialog.FileName); - ProgressForm progressForm = new ProgressForm(importSqlDumpOperation); - progressForm.ShowDialog(); - searchTextBox.Clear(); - bookListView.VirtualListDataSource = dataCache.DataAccessor; - ShowBookCountInStatusLabel(); - importFromSqlDumpMenuItem.Enabled = dataCache.DataAccessor.GetObjectCount() == 0; - } - } - - private void StartProgressOperation(ProgressOperation progressOperation) - { - currentProgressOperation = progressOperation; - progressOperation.ProgressEvent += ProgressOperation_ProgressEvent; - progressOperation.CompletedEvent += ProgressOperation_CompletedEvent; - progressOperation.CancelledEvent += ProgressOperation_CancelledEvent; - progressOperation.ErrorEvent += ProgressOperation_ErrorEvent; - progressBar.Value = 0; - progressBar.Visible = true; - searchTextBox.ReadOnly = true; - progressBar.Style = progressOperation.IsUnbounded ? ProgressBarStyle.Marquee : ProgressBarStyle.Continuous; - progressOperation.Start(); - } - - private void ProgressOperation_ProgressEvent(object sender, ProgressEventArgs e) - { - BeginInvoke(new Action(() => - { - bool updateBookListView = bookListView.VirtualListSize == 0; - if (!updateBookListView) - { - int newBookCount = dataCache.DataAccessor.GetObjectCount() - bookListView.VirtualListSize; - updateBookListView = newBookCount > MIN_BOOK_COUNT_FOR_INITIAL_LOAD_UPDATE && newBookCount > bookListView.VirtualListSize; - } - if (updateBookListView) - { - bookListView.UpdateVirtualListSize(); - } - if (!Double.IsNaN(e.PercentCompleted)) - { - progressBar.SetProgressNoAnimation((int)Math.Truncate(e.PercentCompleted * 10)); - } - if (e.ProgressDescription != null) - { - statusLabel.Text = e.ProgressDescription; - } - })); - } - - private void ProgressOperation_CompletedEvent(object sender, EventArgs e) - { - BeginInvoke(new Action(() => - { - bookListView.UpdateVirtualListSize(); - progressBar.Visible = false; - searchTextBox.ReadOnly = false; - ShowBookCountInStatusLabel(); - if (dataCache.IsInAllBooksMode) - { - importFromSqlDumpMenuItem.Enabled = dataCache.DataAccessor.GetObjectCount() == 0; - } - })); - RemoveProgressOperation(); - } - - private void ProgressOperation_CancelledEvent(object sender, EventArgs e) - { - BeginInvoke(new Action(() => progressBar.Visible = false)); - RemoveProgressOperation(); - } - - private void ProgressOperation_ErrorEvent(object sender, ErrorEventArgs e) - { - BeginInvoke(new Action(() => - { - progressBar.Visible = false; - searchTextBox.ReadOnly = false; - ErrorForm errorForm = new ErrorForm(e.Exception.ToString()); - errorForm.ShowDialog(); - })); - StopProgressOperation(); - } - - private void StopProgressOperation() - { - currentProgressOperation?.Cancel(); - RemoveProgressOperation(); - } - - private void RemoveProgressOperation() - { - ProgressOperation removingProgressOperation = currentProgressOperation; - if (removingProgressOperation != null) - { - removingProgressOperation.ProgressEvent -= ProgressOperation_ProgressEvent; - removingProgressOperation.CompletedEvent -= ProgressOperation_CompletedEvent; - removingProgressOperation.CancelledEvent -= ProgressOperation_CancelledEvent; - removingProgressOperation.ErrorEvent -= ProgressOperation_ErrorEvent; - currentProgressOperation = null; - } - } - - private void MainForm_FormClosing(object sender, FormClosingEventArgs e) - { - StopProgressOperation(); - SettingsStorage.AppSettings.MainWindow = new AppSettings.MainWindowSettings - { - Maximized = WindowState == FormWindowState.Maximized, - Left = Left, - Top = Top, - Width = Width, - Height = Height - }; - SettingsStorage.AppSettings.Columns = new AppSettings.ColumnSettings - { - IdColumnWidth = idColumn.Width, - TitleColumnWidth = titleColumn.Width, - AuthorsColumnWidth = authorsColumn.Width, - SeriesColumnWidth = seriesColumn.Width, - YearColumnWidth = yearColumn.Width, - PublisherColumnWidth = publisherColumn.Width, - FormatColumnWidth = formatColumn.Width, - FileSizeColumnWidth = fileSizeColumn.Width - }; - SettingsStorage.SaveSettings(); - } - - private void bookListView_DoubleClick(object sender, EventArgs e) - { - OpenSelectedBookDetails(); - } - - private void searchTextBox_KeyPress(object sender, KeyPressEventArgs e) - { - if (!searchTextBox.ReadOnly && e.KeyChar == '\r') - { - e.Handled = true; - ProgressOperation searchBookOperation = dataCache.CreateSearchBooksOperation(searchTextBox.Text); - bookListView.VirtualListDataSource = dataCache.DataAccessor; - if (searchBookOperation != null) - { - StartProgressOperation(searchBookOperation); - } - else - { - ShowBookCountInStatusLabel(); - } - } - } - - private void ShowBookCountInStatusLabel() - { - string statusLabelPrefix = dataCache.IsInAllBooksMode ? "Всего книг" : "Найдено книг"; - statusLabel.Text = $"{statusLabelPrefix}: {dataCache.DataAccessor.GetObjectCount().ToString("N0", Formatters.ThousandsSeparatedNumberFormat)}"; - } - - private void bookListView_KeyDown(object sender, KeyEventArgs e) - { - switch (e.KeyCode) - { - case Keys.Escape: - searchTextBox.Focus(); - searchTextBox.SelectAll(); - break; - case Keys.Enter: - OpenSelectedBookDetails(); - break; - } - } - - private void OpenSelectedBookDetails() - { - Book selectedBook = bookListView.SelectedObject as Book; - selectedBook = localDatabase.GetBookById(selectedBook.Id); - BookForm bookForm = new BookForm(selectedBook); - bookForm.ShowDialog(); - } - - private void UpdateOfflineModeStatus(bool saveSettings) - { - bool offlineMode = offlineModeMenuItem.Checked; - connectionStatusLabel.Visible = offlineMode; - if (saveSettings) - { - SettingsStorage.AppSettings.OfflineMode = offlineMode; - SettingsStorage.SaveSettings(); - } - } - - private void offlineModeMenuItem_Click(object sender, EventArgs e) - { - UpdateOfflineModeStatus(true); - } - - private void bookListView_BeforeSearching(object sender, BeforeSearchingEventArgs e) - { - e.Canceled = true; - } - - private void searchTextBox_KeyDown(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Escape) - { - e.Handled = true; - e.SuppressKeyPress = true; - } - } - } -} diff --git a/Interface/MainForm.resx b/Interface/MainForm.resx deleted file mode 100644 index 7245ced..0000000 --- a/Interface/MainForm.resx +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - - 127, 17 - - - 405, 17 - - - - AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w - LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 - ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADu - BwAAAk1TRnQBSQFMAwEBAAEQAQABEAEAARABAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA - AUADAAEQAwABAQEAAQgGAAEEGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA - AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA - AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm - AwABmQMAAcwCAAEzAwACMwIAATMBZgIAATMBmQIAATMBzAIAATMB/wIAAWYDAAFmATMCAAJmAgABZgGZ - AgABZgHMAgABZgH/AgABmQMAAZkBMwIAAZkBZgIAApkCAAGZAcwCAAGZAf8CAAHMAwABzAEzAgABzAFm - AgABzAGZAgACzAIAAcwB/wIAAf8BZgIAAf8BmQIAAf8BzAEAATMB/wIAAf8BAAEzAQABMwEAAWYBAAEz - AQABmQEAATMBAAHMAQABMwEAAf8BAAH/ATMCAAMzAQACMwFmAQACMwGZAQACMwHMAQACMwH/AQABMwFm - AgABMwFmATMBAAEzAmYBAAEzAWYBmQEAATMBZgHMAQABMwFmAf8BAAEzAZkCAAEzAZkBMwEAATMBmQFm - AQABMwKZAQABMwGZAcwBAAEzAZkB/wEAATMBzAIAATMBzAEzAQABMwHMAWYBAAEzAcwBmQEAATMCzAEA - ATMBzAH/AQABMwH/ATMBAAEzAf8BZgEAATMB/wGZAQABMwH/AcwBAAEzAv8BAAFmAwABZgEAATMBAAFm - AQABZgEAAWYBAAGZAQABZgEAAcwBAAFmAQAB/wEAAWYBMwIAAWYCMwEAAWYBMwFmAQABZgEzAZkBAAFm - ATMBzAEAAWYBMwH/AQACZgIAAmYBMwEAA2YBAAJmAZkBAAJmAcwBAAFmAZkCAAFmAZkBMwEAAWYBmQFm - AQABZgKZAQABZgGZAcwBAAFmAZkB/wEAAWYBzAIAAWYBzAEzAQABZgHMAZkBAAFmAswBAAFmAcwB/wEA - AWYB/wIAAWYB/wEzAQABZgH/AZkBAAFmAf8BzAEAAcwBAAH/AQAB/wEAAcwBAAKZAgABmQEzAZkBAAGZ - AQABmQEAAZkBAAHMAQABmQMAAZkCMwEAAZkBAAFmAQABmQEzAcwBAAGZAQAB/wEAAZkBZgIAAZkBZgEz - AQABmQEzAWYBAAGZAWYBmQEAAZkBZgHMAQABmQEzAf8BAAKZATMBAAKZAWYBAAOZAQACmQHMAQACmQH/ - AQABmQHMAgABmQHMATMBAAFmAcwBZgEAAZkBzAGZAQABmQLMAQABmQHMAf8BAAGZAf8CAAGZAf8BMwEA - AZkBzAFmAQABmQH/AZkBAAGZAf8BzAEAAZkC/wEAAcwDAAGZAQABMwEAAcwBAAFmAQABzAEAAZkBAAHM - AQABzAEAAZkBMwIAAcwCMwEAAcwBMwFmAQABzAEzAZkBAAHMATMBzAEAAcwBMwH/AQABzAFmAgABzAFm - ATMBAAGZAmYBAAHMAWYBmQEAAcwBZgHMAQABmQFmAf8BAAHMAZkCAAHMAZkBMwEAAcwBmQFmAQABzAKZ - AQABzAGZAcwBAAHMAZkB/wEAAswCAALMATMBAALMAWYBAALMAZkBAAPMAQACzAH/AQABzAH/AgABzAH/ - ATMBAAGZAf8BZgEAAcwB/wGZAQABzAH/AcwBAAHMAv8BAAHMAQABMwEAAf8BAAFmAQAB/wEAAZkBAAHM - ATMCAAH/AjMBAAH/ATMBZgEAAf8BMwGZAQAB/wEzAcwBAAH/ATMB/wEAAf8BZgIAAf8BZgEzAQABzAJm - AQAB/wFmAZkBAAH/AWYBzAEAAcwBZgH/AQAB/wGZAgAB/wGZATMBAAH/AZkBZgEAAf8CmQEAAf8BmQHM - AQAB/wGZAf8BAAH/AcwCAAH/AcwBMwEAAf8BzAFmAQAB/wHMAZkBAAH/AswBAAH/AcwB/wEAAv8BMwEA - AcwB/wFmAQAC/wGZAQAC/wHMAQACZgH/AQABZgH/AWYBAAFmAv8BAAH/AmYBAAH/AWYB/wEAAv8BZgEA - ASEBAAGlAQADXwEAA3cBAAOGAQADlgEAA8sBAAOyAQAD1wEAA90BAAPjAQAD6gEAA/EBAAP4AQAB8AH7 - Af8BAAGkAqABAAOAAwAB/wIAAf8DAAL/AQAB/wMAAf8BAAH/AQAC/wIAA/8GAAL/PQAB8wFxAXcB/zsA - AfMDcQGdAf85AAHzBXEB8DgAAfMCcQF3AfQBnQJxAf82AAH0AnEBdwH0AQAB/wJxAZ0B/zUAAfQBcQF3 - AfQDAAHxAnEBCDYAAfQB/wQAAf8BmAJxAfQ8AAH/AnEBnQH/PAAB8QJxAQg9AAGYAnEB9DwAAf8BdwFx - AZ0B/zwAAfICcQEIPQABmAJxAfQ8AAH/AXcBcQH0PQAB/wH0MQABQgFNAT4HAAE+AwABKAMAAUADAAEQ - AwABAQEAAQEFAAGAFwAD/wEAAfkB/wYAAfAB/wYAAeABfwYAAcABfwYAAYABPwYAAQQBHwYAAQ4BHwYA - AZ4BDwYAAf8BBwYAAf8BhwYAAf8BwwYAAf8BwQYAAf8B4QYAAf8B8AYAAf8B8AYAAf8B+QYACw== - - - - 292, 17 - - \ No newline at end of file diff --git a/Interface/ProgressBarExtensions.cs b/Interface/ProgressBarExtensions.cs deleted file mode 100644 index 2c6b7e6..0000000 --- a/Interface/ProgressBarExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Windows.Forms; - -namespace LibgenDesktop.Interface -{ - internal static class ProgressBarExtensions - { - public static void SetProgressNoAnimation(this ProgressBar progressBar, int value) - { - // To get around the progressive animation, we need to move the progress bar backwards. - if (value == progressBar.Maximum) - { - progressBar.Maximum = value + 1; - progressBar.Value = value + 1; - progressBar.Maximum = value; - } - else - { - progressBar.Value = value + 1; - progressBar.Value = value; - } - } - } -} diff --git a/Interface/ProgressForm.Designer.cs b/Interface/ProgressForm.Designer.cs deleted file mode 100644 index f3e067f..0000000 --- a/Interface/ProgressForm.Designer.cs +++ /dev/null @@ -1,111 +0,0 @@ -namespace LibgenDesktop.Interface -{ - partial class ProgressForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.progressBar = new System.Windows.Forms.ProgressBar(); - this.progressDescription = new System.Windows.Forms.Label(); - this.cancelButton = new System.Windows.Forms.Button(); - this.estimatedTimeLabel = new System.Windows.Forms.Label(); - this.SuspendLayout(); - // - // progressBar - // - this.progressBar.Location = new System.Drawing.Point(12, 32); - this.progressBar.Maximum = 1000; - this.progressBar.Name = "progressBar"; - this.progressBar.Size = new System.Drawing.Size(593, 23); - this.progressBar.TabIndex = 0; - // - // progressDescription - // - this.progressDescription.AutoEllipsis = true; - this.progressDescription.Location = new System.Drawing.Point(12, 9); - this.progressDescription.Name = "progressDescription"; - this.progressDescription.Size = new System.Drawing.Size(593, 20); - this.progressDescription.TabIndex = 1; - this.progressDescription.Text = "Progress description"; - this.progressDescription.TextAlign = System.Drawing.ContentAlignment.TopCenter; - // - // cancelButton - // - this.cancelButton.Location = new System.Drawing.Point(498, 61); - this.cancelButton.Name = "cancelButton"; - this.cancelButton.Size = new System.Drawing.Size(107, 31); - this.cancelButton.TabIndex = 2; - this.cancelButton.Text = "Прервать"; - this.cancelButton.UseVisualStyleBackColor = true; - this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); - // - // estimatedTimeLabel - // - this.estimatedTimeLabel.AutoSize = true; - this.estimatedTimeLabel.Location = new System.Drawing.Point(9, 68); - this.estimatedTimeLabel.Name = "estimatedTimeLabel"; - this.estimatedTimeLabel.Size = new System.Drawing.Size(94, 17); - this.estimatedTimeLabel.TabIndex = 3; - this.estimatedTimeLabel.Text = "Estimated time"; - // - // ProgressForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(610, 98); - this.ControlBox = false; - this.Controls.Add(this.estimatedTimeLabel); - this.Controls.Add(this.cancelButton); - this.Controls.Add(this.progressDescription); - this.Controls.Add(this.progressBar); - this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.MaximizeBox = false; - this.MaximumSize = new System.Drawing.Size(626, 137); - this.MinimizeBox = false; - this.MinimumSize = new System.Drawing.Size(626, 137); - this.Name = "ProgressForm"; - this.ShowIcon = false; - this.ShowInTaskbar = false; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "ProgressForm"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ProgressForm_FormClosing); - this.Shown += new System.EventHandler(this.ProgressForm_Shown); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.ProgressBar progressBar; - private System.Windows.Forms.Label progressDescription; - private System.Windows.Forms.Button cancelButton; - private System.Windows.Forms.Label estimatedTimeLabel; - } -} \ No newline at end of file diff --git a/Interface/ProgressForm.cs b/Interface/ProgressForm.cs deleted file mode 100644 index 214d347..0000000 --- a/Interface/ProgressForm.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Windows.Forms; -using LibgenDesktop.Infrastructure; - -namespace LibgenDesktop.Interface -{ - internal partial class ProgressForm : Form - { - private ProgressOperation progressOperation; - private DateTime operationStartTime; - private TimeSpan displayingElapsedTime; - - public ProgressForm(ProgressOperation progressOperation) - { - InitializeComponent(); - this.progressOperation = progressOperation; - Text = progressOperation.Title; - progressOperation.ProgressEvent += ProgressOperation_ProgressEvent; - progressOperation.CompletedEvent += ProgressOperation_CompletedEvent; - progressOperation.CancelledEvent += ProgressOperation_CancelledEvent; - progressOperation.ErrorEvent += ProgressOperation_ErrorEvent; - } - - private void ProgressForm_Shown(object sender, EventArgs e) - { - operationStartTime = DateTime.Now; - displayingElapsedTime = TimeSpan.Zero; - estimatedTimeLabel.Text = "Прошло 00:00"; - progressOperation.Start(); - } - - private void ProgressOperation_ProgressEvent(object sender, ProgressEventArgs e) - { - BeginInvoke(new Action(() => - { - DateTime now = DateTime.Now; - TimeSpan elapsed = now - operationStartTime; - if (elapsed.Seconds != displayingElapsedTime.Seconds) - { - estimatedTimeLabel.Text = $"Прошло {elapsed:mm\\:ss}"; - displayingElapsedTime = elapsed; - if (e.PercentCompleted > 5) - { - TimeSpan remaining = TimeSpan.FromSeconds(elapsed.TotalSeconds / e.PercentCompleted * (100 - e.PercentCompleted)); - estimatedTimeLabel.Text += $", осталось {remaining:mm\\:ss}"; - } - } - progressDescription.Text = e.ProgressDescription; - progressBar.SetProgressNoAnimation((int)Math.Truncate(e.PercentCompleted * 10)); - })); - } - - private void ProgressOperation_CompletedEvent(object sender, EventArgs e) - { - BeginInvoke(new Action(() => Close())); - } - - private void ProgressOperation_CancelledEvent(object sender, EventArgs e) - { - BeginInvoke(new Action(() => Close())); - } - - private void ProgressOperation_ErrorEvent(object sender, ErrorEventArgs e) - { - BeginInvoke(new Action(() => - { - ErrorForm errorForm = new ErrorForm(e.Exception.ToString()); - errorForm.ShowDialog(); - Close(); - })); - } - - private void cancelButton_Click(object sender, EventArgs e) - { - Close(); - } - - private void ProgressForm_FormClosing(object sender, FormClosingEventArgs e) - { - progressOperation?.Cancel(); - RemoveOperation(); - } - - private void RemoveOperation() - { - ProgressOperation removingProgressOperation = progressOperation; - if (removingProgressOperation != null) - { - removingProgressOperation.ProgressEvent -= ProgressOperation_ProgressEvent; - removingProgressOperation.CompletedEvent -= ProgressOperation_CompletedEvent; - removingProgressOperation.CancelledEvent -= ProgressOperation_CancelledEvent; - progressOperation = null; - } - } - } -} diff --git a/Interface/ProgressForm.resx b/Interface/ProgressForm.resx deleted file mode 100644 index 1af7de1..0000000 --- a/Interface/ProgressForm.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/LibgenDesktop.csproj b/LibgenDesktop.csproj index 6381a32..b611561 100644 --- a/LibgenDesktop.csproj +++ b/LibgenDesktop.csproj @@ -4,17 +4,16 @@ Debug AnyCPU - {5B0ED485-1C8D-4EC9-AE28-20FC5E74FADD} + {7158E1EB-44F7-4C26-BF9A-DA055F85D0BB} WinExe LibgenDesktop LibgenDesktop - v4.0 + v4.5 512 - true + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 - - AnyCPU @@ -25,7 +24,6 @@ DEBUG;TRACE prompt 4 - false AnyCPU @@ -35,128 +33,196 @@ TRACE prompt 4 - false + + + Resources\app_icon.ico - - packages\Newtonsoft.Json.10.0.3\lib\net40\Newtonsoft.Json.dll + + packages\Costura.Fody.1.6.2\lib\dotnet\Costura.dll + False - - packages\ObjectListView.Official.2.9.1\lib\net20\ObjectListView.dll - True + + packages\MaterialDesignColors.1.1.2\lib\net45\MaterialDesignColors.dll + + + packages\MaterialDesignThemes.2.3.1.953\lib\net45\MaterialDesignThemes.Wpf.dll + + + packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - packages\SharpCompress.0.18.2\lib\net35\SharpCompress.dll + packages\SharpCompress.0.18.2\lib\net45\SharpCompress.dll + - packages\System.Data.SQLite.Core.1.0.106.0\lib\net40\System.Data.SQLite.dll + packages\System.Data.SQLite.Core.1.0.106.0\lib\net45\System.Data.SQLite.dll - + + + - - - - - - + + + 4.0 + + + + - - - - - - - - - - - - - Form + + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + + + BookDetailsWindow.xaml - - BookForm.cs + + BookAttributeValueLabel.xaml - - Form + + + ErrorWindow.xaml - - ErrorForm.cs + + SqlDumpImportWindow.xaml - - - - Form + + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code - - MainForm.cs + + + + + + + + + + + + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + Code - - - - Form + + True + True + Resources.resx - - ProgressForm.cs + + True + Settings.settings + True - - - - - - BookForm.cs - - - ErrorForm.cs - - - MainForm.cs - - - ProgressForm.cs - ResXFileCodeGenerator Resources.Designer.cs - Designer - - True - Resources.resx - True - SettingsSingleFileGenerator Settings.Designer.cs - - True - Settings.settings - True - - - + - - - + + + + + + - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + + + + + \ No newline at end of file diff --git a/LibgenDesktop.sln b/LibgenDesktop.sln index ebe0c9a..b220b4f 100644 --- a/LibgenDesktop.sln +++ b/LibgenDesktop.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27004.2005 +VisualStudioVersion = 15.0.27004.2009 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibgenDesktop", "LibgenDesktop.csproj", "{5B0ED485-1C8D-4EC9-AE28-20FC5E74FADD}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibgenDesktop", "LibgenDesktop.csproj", "{7158E1EB-44F7-4C26-BF9A-DA055F85D0BB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5B0ED485-1C8D-4EC9-AE28-20FC5E74FADD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B0ED485-1C8D-4EC9-AE28-20FC5E74FADD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B0ED485-1C8D-4EC9-AE28-20FC5E74FADD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B0ED485-1C8D-4EC9-AE28-20FC5E74FADD}.Release|Any CPU.Build.0 = Release|Any CPU + {7158E1EB-44F7-4C26-BF9A-DA055F85D0BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7158E1EB-44F7-4C26-BF9A-DA055F85D0BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7158E1EB-44F7-4C26-BF9A-DA055F85D0BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7158E1EB-44F7-4C26-BF9A-DA055F85D0BB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {5D628854-083D-4A9B-A5A4-BC50250A71B5} + SolutionGuid = {1CB75FAD-8C25-4486-BEFB-DF4CA961ACD6} EndGlobalSection EndGlobal diff --git a/Database/LocalDatabase.cs b/Models/Database/LocalDatabase.cs similarity index 99% rename from Database/LocalDatabase.cs rename to Models/Database/LocalDatabase.cs index b79689c..353fa38 100644 --- a/Database/LocalDatabase.cs +++ b/Models/Database/LocalDatabase.cs @@ -4,14 +4,12 @@ using System.Data.SQLite; using System.Globalization; using System.IO; -using System.Text; +using LibgenDesktop.Models.Entities; -namespace LibgenDesktop.Database +namespace LibgenDesktop.Models.Database { internal class LocalDatabase { - public const int INSERT_TRANSACTION_BATCH = 500; - private readonly SQLiteConnection connection; public LocalDatabase(string databaseFileName) diff --git a/Database/SqlScripts.cs b/Models/Database/SqlScripts.cs similarity index 96% rename from Database/SqlScripts.cs rename to Models/Database/SqlScripts.cs index 85fce48..0427bff 100644 --- a/Database/SqlScripts.cs +++ b/Models/Database/SqlScripts.cs @@ -1,4 +1,10 @@ -namespace LibgenDesktop.Database +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LibgenDesktop.Models.Database { internal static class SqlScripts { diff --git a/Database/Book.cs b/Models/Entities/Book.cs similarity index 53% rename from Database/Book.cs rename to Models/Entities/Book.cs index d61932a..ab797fb 100644 --- a/Database/Book.cs +++ b/Models/Entities/Book.cs @@ -1,7 +1,8 @@ using System; -using LibgenDesktop.Interface; +using System.Text; +using LibgenDesktop.Models.Utils; -namespace LibgenDesktop.Database +namespace LibgenDesktop.Models.Entities { internal class Book { @@ -46,6 +47,35 @@ internal class BookExtendedProperties public string Tags { get; set; } public string IdentifierPlain { get; set; } public int LibgenId { get; set; } + + public string AddedDateTimeString => AddedDateTime.ToString("dd.MM.yyyy HH:mm:ss"); + public string LastModifiedDateTimeString => LastModifiedDateTime.ToString("dd.MM.yyyy HH:mm:ss"); + public string BookmarkedString => StringBooleanToLabelString(Bookmarked, "есть", "нет", "неизвестно"); + public string ScannedString => StringBooleanToLabelString(Scanned, "да", "нет", "неизвестно"); + public string OrientationString => StringBooleanToLabelString(Orientation, "портретная", "альбомная", "неизвестно"); + public string PaginatedString => StringBooleanToLabelString(Paginated, "да", "нет", "неизвестно"); + public string ColorString => StringBooleanToLabelString(Color, "да", "нет", "неизвестно"); + public string CleanedString => StringBooleanToLabelString(Cleaned, "да", "нет", "неизвестно"); + + public string PagesString + { + get + { + StringBuilder resultBuilder = new StringBuilder(); + if (!String.IsNullOrWhiteSpace(Pages)) + { + resultBuilder.Append(Pages); + } + else + { + resultBuilder.Append("неизвестно"); + } + resultBuilder.Append(" (содержательная часть) / "); + resultBuilder.Append(PagesInFile.ToString()); + resultBuilder.Append(" (всего в файле)"); + return resultBuilder.ToString(); + } + } } public int Id { get; set; } @@ -62,5 +92,19 @@ internal class BookExtendedProperties public string FileSizeString => Formatters.FileSizeToString(SizeInBytes, false); public string FileSizeWithBytesString => Formatters.FileSizeToString(SizeInBytes, true); public bool Ocr => Searchable == "1"; + public string SearchableString => StringBooleanToLabelString(Searchable, "да", "нет", "неизвестно"); + + private static string StringBooleanToLabelString(string value, string value1Label, string value0Label, string valueUnknownLabel) + { + switch (value) + { + case "0": + return value0Label; + case "1": + return value1Label; + default: + return valueUnknownLabel; + } + } } } diff --git a/Models/MainModel.cs b/Models/MainModel.cs new file mode 100644 index 0000000..f0eb2f3 --- /dev/null +++ b/Models/MainModel.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using LibgenDesktop.Models.Database; +using LibgenDesktop.Models.Entities; +using LibgenDesktop.Models.ProgressArgs; +using LibgenDesktop.Models.Settings; +using LibgenDesktop.Models.SqlDump; +using LibgenDesktop.Models.Utils; +using static LibgenDesktop.Common.Constants; + +namespace LibgenDesktop.Models +{ + internal class MainModel + { + private readonly LocalDatabase localDatabase; + + public MainModel() + { + AppSettings = SettingsStorage.LoadSettings(); + localDatabase = new LocalDatabase(AppSettings.DatabaseFileName); + AllBooks = new AsyncBookCollection(); + SearchResults = new AsyncBookCollection(); + } + + public AppSettings AppSettings { get; } + public AsyncBookCollection AllBooks { get; } + public AsyncBookCollection SearchResults { get; } + + public Task LoadAllBooksAsync(IProgress progressHandler, CancellationToken cancellationToken) + { + return Task.Run(() => + { + int totalBookCount = localDatabase.CountBooks(); + AllBooks.SetCapacity(totalBookCount); + int currentBatchBookNumber = 0; + int reportProgressBatchSize = totalBookCount / 1000; + foreach (Book book in localDatabase.GetAllBooks()) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + AllBooks.AddBook(book); + currentBatchBookNumber++; + if (currentBatchBookNumber == reportProgressBatchSize) + { + progressHandler.Report(new LoadAllBooksProgress(AllBooks.AddedBookCount, totalBookCount)); + currentBatchBookNumber = 0; + if (AllBooks.AddedBookCount - AllBooks.Count > AllBooks.Count) + { + AllBooks.UpdateReportedBookCount(); + } + } + } + if (currentBatchBookNumber > 0) + { + progressHandler.Report(new LoadAllBooksProgress(AllBooks.AddedBookCount, totalBookCount, isFinished: true)); + } + AllBooks.UpdateReportedBookCount(); + }); + } + + public void ClearSearchResults() + { + SearchResults.Clear(); + } + + public Task SearchBooksAsync(string searchQuery, IProgress progressHandler, CancellationToken cancellationToken) + { + return Task.Run(() => + { + int currentBatchBookNumber = 0; + foreach (Book book in localDatabase.SearchBooks(searchQuery)) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + SearchResults.AddBook(book); + currentBatchBookNumber++; + if (currentBatchBookNumber == SEARCH_REPORT_PROGRESS_BATCH_SIZE) + { + progressHandler.Report(new SearchBooksProgress(SearchResults.AddedBookCount)); + currentBatchBookNumber = 0; + if (SearchResults.AddedBookCount - SearchResults.Count > SearchResults.Count) + { + SearchResults.UpdateReportedBookCount(); + } + } + } + if (currentBatchBookNumber > 0) + { + progressHandler.Report(new SearchBooksProgress(SearchResults.AddedBookCount, isFinished: true)); + } + SearchResults.UpdateReportedBookCount(); + }); + } + + public Task LoadBookAsync(int bookId) + { + return Task.Run(() => + { + return localDatabase.GetBookById(bookId); + }); + } + + public Task ImportSqlDumpAsync(string sqlDumpFilePath, IProgress progressHandler, CancellationToken cancellationToken) + { + return Task.Run(() => + { + using (SqlDumpReader sqlDumpReader = new SqlDumpReader(sqlDumpFilePath)) + { + EventHandler readRowsProgressHandler = (sender, e) => + { + progressHandler.Report(new ImportSqlDumpProgress(e.RowsParsed, e.CurrentPosition, e.TotalLength)); + }; + sqlDumpReader.ReadRowsProgress += readRowsProgressHandler; + List currentBatchBooks = new List(INSERT_TRANSACTION_BATCH); + foreach (Book book in sqlDumpReader.ReadRows()) + { + if (cancellationToken.IsCancellationRequested) + { + break; + } + currentBatchBooks.Add(book); + if (currentBatchBooks.Count == INSERT_TRANSACTION_BATCH) + { + localDatabase.AddBooks(currentBatchBooks); + foreach (Book currentBatchBook in currentBatchBooks) + { + currentBatchBook.ExtendedProperties = null; + } + AllBooks.AddBooks(currentBatchBooks); + if (AllBooks.Count == 0) + { + AllBooks.UpdateReportedBookCount(); + } + currentBatchBooks.Clear(); + } + } + if (currentBatchBooks.Any()) + { + localDatabase.AddBooks(currentBatchBooks); + foreach (Book currentBatchBook in currentBatchBooks) + { + currentBatchBook.ExtendedProperties = null; + } + AllBooks.AddBooks(currentBatchBooks); + } + sqlDumpReader.ReadRowsProgress -= readRowsProgressHandler; + } + AllBooks.UpdateReportedBookCount(); + }); + } + + public void SaveSettings() + { + SettingsStorage.SaveSettings(AppSettings); + } + } +} diff --git a/Models/ProgressArgs/ImportSqlDumpProgress.cs b/Models/ProgressArgs/ImportSqlDumpProgress.cs new file mode 100644 index 0000000..c16369c --- /dev/null +++ b/Models/ProgressArgs/ImportSqlDumpProgress.cs @@ -0,0 +1,16 @@ +namespace LibgenDesktop.Models.ProgressArgs +{ + internal class ImportSqlDumpProgress + { + public ImportSqlDumpProgress(int booksImported, long bytesParsed, long totalBytes) + { + BooksImported = booksImported; + BytesParsed = bytesParsed; + TotalBytes = totalBytes; + } + + public int BooksImported { get; } + public long BytesParsed { get; } + public long TotalBytes { get; } + } +} diff --git a/Models/ProgressArgs/LoadAllBooksProgress.cs b/Models/ProgressArgs/LoadAllBooksProgress.cs new file mode 100644 index 0000000..bc0dfbb --- /dev/null +++ b/Models/ProgressArgs/LoadAllBooksProgress.cs @@ -0,0 +1,16 @@ +namespace LibgenDesktop.Models.ProgressArgs +{ + internal class LoadAllBooksProgress + { + public LoadAllBooksProgress(int booksLoaded, int totalBookCount, bool isFinished = false) + { + BooksLoaded = booksLoaded; + TotalBookCount = totalBookCount; + IsFinished = isFinished; + } + + public int BooksLoaded { get; } + public int TotalBookCount { get; } + public bool IsFinished { get; } + } +} diff --git a/Models/ProgressArgs/SearchBooksProgress.cs b/Models/ProgressArgs/SearchBooksProgress.cs new file mode 100644 index 0000000..72568e2 --- /dev/null +++ b/Models/ProgressArgs/SearchBooksProgress.cs @@ -0,0 +1,14 @@ +namespace LibgenDesktop.Models.ProgressArgs +{ + internal class SearchBooksProgress + { + public SearchBooksProgress(int booksFound, bool isFinished = false) + { + BooksFound = booksFound; + IsFinished = isFinished; + } + + public int BooksFound { get; } + public bool IsFinished { get; } + } +} diff --git a/Settings/AppSettings.cs b/Models/Settings/AppSettings.cs similarity index 60% rename from Settings/AppSettings.cs rename to Models/Settings/AppSettings.cs index 0cb8784..410fe4c 100644 --- a/Settings/AppSettings.cs +++ b/Models/Settings/AppSettings.cs @@ -1,6 +1,7 @@ -using System.Windows.Forms; +using LibgenDesktop.Infrastructure; +using static LibgenDesktop.Common.Constants; -namespace LibgenDesktop.Settings +namespace LibgenDesktop.Models.Settings { internal class AppSettings { @@ -21,7 +22,6 @@ internal class BookWindowSettings internal class ColumnSettings { - public int IdColumnWidth { get; set; } public int TitleColumnWidth { get; set; } public int AuthorsColumnWidth { get; set; } public int SeriesColumnWidth { get; set; } @@ -32,35 +32,6 @@ internal class ColumnSettings public int OcrColumnWidth { get; set; } } - public const int MAIN_WINDOW_MIN_WIDTH = 800; - public const int MAIN_WINDOW_MIN_HEIGHT = 600; - public const int BOOK_WINDOW_MIN_WIDTH = 800; - public const int BOOK_WINDOW_MIN_HEIGHT = 600; - public const int ID_COLUMN_MIN_WIDTH = 20; - public const int TITLE_COLUMN_MIN_WIDTH = 100; - public const int AUTHORS_COLUMN_MIN_WIDTH = 100; - public const int SERIES_COLUMN_MIN_WIDTH = 100; - public const int YEAR_COLUMN_MIN_WIDTH = 20; - public const int PUBLISHER_COLUMN_MIN_WIDTH = 50; - public const int FORMAT_COLUMN_MIN_WIDTH = 20; - public const int FILESIZE_COLUMN_MIN_WIDTH = 30; - public const int OCR_COLUMN_MIN_WIDTH = 40; - - private const string DEFAULT_DATABASE_FILE_NAME = "libgen.db"; - private const int DEFAULT_MAIN_WINDOW_WIDTH = 1200; - private const int DEFAULT_MAIN_WINDOW_HEIGHT = 650; - private const int DEFAULT_BOOK_WINDOW_WIDTH = 1200; - private const int DEFAULT_BOOK_WINDOW_HEIGHT = 618; - public const int DEFAULT_ID_COLUMN_WIDTH = 60; - public const int DEFAULT_TITLE_COLUMN_WIDTH = 200; - public const int DEFAULT_AUTHORS_COLUMN_WIDTH = 200; - public const int DEFAULT_SERIES_COLUMN_WIDTH = 200; - public const int DEFAULT_YEAR_COLUMN_WIDTH = 60; - public const int DEFAULT_PUBLISHER_COLUMN_WIDTH = 200; - public const int DEFAULT_FORMAT_COLUMN_WIDTH = 60; - public const int DEFAULT_FILESIZE_COLUMN_WIDTH = 110; - public const int DEFAULT_OCR_COLUMN_WIDTH = 40; - public static AppSettings Default { get @@ -72,7 +43,7 @@ public static AppSettings Default // ResultLimit = 0, MainWindow = DefaultMainWindowSettings, BookWindow = DefaultBookWindowSettings, - Columns = DefaultColumnSettings + Columns = DefaultColumnSettings }; } } @@ -84,8 +55,8 @@ private static MainWindowSettings DefaultMainWindowSettings return new MainWindowSettings { Maximized = false, - Left = (Screen.PrimaryScreen.WorkingArea.Width - DEFAULT_MAIN_WINDOW_WIDTH) / 2, - Top = (Screen.PrimaryScreen.WorkingArea.Height - DEFAULT_MAIN_WINDOW_HEIGHT) / 2, + Left = (WindowManager.ScreenWidth - DEFAULT_MAIN_WINDOW_WIDTH) / 2, + Top = (WindowManager.ScreenHeight - DEFAULT_MAIN_WINDOW_HEIGHT) / 2, Width = DEFAULT_MAIN_WINDOW_WIDTH, Height = DEFAULT_MAIN_WINDOW_HEIGHT }; @@ -110,7 +81,6 @@ private static ColumnSettings DefaultColumnSettings { return new ColumnSettings { - IdColumnWidth = DEFAULT_ID_COLUMN_WIDTH, TitleColumnWidth = DEFAULT_TITLE_COLUMN_WIDTH, AuthorsColumnWidth = DEFAULT_AUTHORS_COLUMN_WIDTH, SeriesColumnWidth = DEFAULT_SERIES_COLUMN_WIDTH, @@ -136,8 +106,6 @@ public static void ValidateAndCorrect(AppSettings appSettings) { appSettings.DatabaseFileName = DEFAULT_DATABASE_FILE_NAME; } - int screenWidth = Screen.PrimaryScreen.WorkingArea.Width; - int screenHeight = Screen.PrimaryScreen.WorkingArea.Height; if (appSettings.MainWindow == null) { appSettings.MainWindow = DefaultMainWindowSettings; @@ -152,13 +120,13 @@ public static void ValidateAndCorrect(AppSettings appSettings) { appSettings.MainWindow.Height = MAIN_WINDOW_MIN_HEIGHT; } - if (appSettings.MainWindow.Left >= screenWidth) + if (appSettings.MainWindow.Left >= WindowManager.ScreenWidth) { - appSettings.MainWindow.Left = screenWidth - appSettings.MainWindow.Width; + appSettings.MainWindow.Left = WindowManager.ScreenWidth - appSettings.MainWindow.Width; } - if (appSettings.MainWindow.Top >= screenHeight) + if (appSettings.MainWindow.Top >= WindowManager.ScreenHeight) { - appSettings.MainWindow.Top = screenHeight - appSettings.MainWindow.Height; + appSettings.MainWindow.Top = WindowManager.ScreenHeight - appSettings.MainWindow.Height; } if (appSettings.MainWindow.Left < 0) { @@ -190,37 +158,33 @@ public static void ValidateAndCorrect(AppSettings appSettings) } else { - if (appSettings.Columns.IdColumnWidth < ID_COLUMN_MIN_WIDTH) - { - appSettings.Columns.IdColumnWidth = ID_COLUMN_MIN_WIDTH; - } - if (appSettings.Columns.TitleColumnWidth < ID_COLUMN_MIN_WIDTH) + if (appSettings.Columns.TitleColumnWidth < TITLE_COLUMN_MIN_WIDTH) { - appSettings.Columns.TitleColumnWidth = ID_COLUMN_MIN_WIDTH; + appSettings.Columns.TitleColumnWidth = TITLE_COLUMN_MIN_WIDTH; } - if (appSettings.Columns.AuthorsColumnWidth < ID_COLUMN_MIN_WIDTH) + if (appSettings.Columns.AuthorsColumnWidth < AUTHORS_COLUMN_MIN_WIDTH) { - appSettings.Columns.AuthorsColumnWidth = ID_COLUMN_MIN_WIDTH; + appSettings.Columns.AuthorsColumnWidth = AUTHORS_COLUMN_MIN_WIDTH; } - if (appSettings.Columns.SeriesColumnWidth < ID_COLUMN_MIN_WIDTH) + if (appSettings.Columns.SeriesColumnWidth < SERIES_COLUMN_MIN_WIDTH) { - appSettings.Columns.SeriesColumnWidth = ID_COLUMN_MIN_WIDTH; + appSettings.Columns.SeriesColumnWidth = SERIES_COLUMN_MIN_WIDTH; } - if (appSettings.Columns.YearColumnWidth < ID_COLUMN_MIN_WIDTH) + if (appSettings.Columns.YearColumnWidth < YEAR_COLUMN_MIN_WIDTH) { - appSettings.Columns.YearColumnWidth = ID_COLUMN_MIN_WIDTH; + appSettings.Columns.YearColumnWidth = YEAR_COLUMN_MIN_WIDTH; } - if (appSettings.Columns.PublisherColumnWidth < ID_COLUMN_MIN_WIDTH) + if (appSettings.Columns.PublisherColumnWidth < PUBLISHER_COLUMN_MIN_WIDTH) { - appSettings.Columns.PublisherColumnWidth = ID_COLUMN_MIN_WIDTH; + appSettings.Columns.PublisherColumnWidth = PUBLISHER_COLUMN_MIN_WIDTH; } - if (appSettings.Columns.FormatColumnWidth < ID_COLUMN_MIN_WIDTH) + if (appSettings.Columns.FormatColumnWidth < FORMAT_COLUMN_MIN_WIDTH) { - appSettings.Columns.FormatColumnWidth = ID_COLUMN_MIN_WIDTH; + appSettings.Columns.FormatColumnWidth = FORMAT_COLUMN_MIN_WIDTH; } - if (appSettings.Columns.FileSizeColumnWidth < ID_COLUMN_MIN_WIDTH) + if (appSettings.Columns.FileSizeColumnWidth < FILESIZE_COLUMN_MIN_WIDTH) { - appSettings.Columns.FileSizeColumnWidth = ID_COLUMN_MIN_WIDTH; + appSettings.Columns.FileSizeColumnWidth = FILESIZE_COLUMN_MIN_WIDTH; } if (appSettings.Columns.OcrColumnWidth < OCR_COLUMN_MIN_WIDTH) { diff --git a/Settings/SettingsStorage.cs b/Models/Settings/SettingsStorage.cs similarity index 67% rename from Settings/SettingsStorage.cs rename to Models/Settings/SettingsStorage.cs index 596fe41..86823fd 100644 --- a/Settings/SettingsStorage.cs +++ b/Models/Settings/SettingsStorage.cs @@ -1,23 +1,15 @@ using System.IO; using Newtonsoft.Json; -namespace LibgenDesktop.Settings +namespace LibgenDesktop.Models.Settings { internal static class SettingsStorage { private const string CONFIG_FILE_NAME = "libgen.config"; - private static AppSettings appSettings; - - static SettingsStorage() - { - appSettings = AppSettings.Default; - } - - public static AppSettings AppSettings => appSettings; - - public static void LoadSettings() + public static AppSettings LoadSettings() { + AppSettings result = AppSettings.Default; try { if (File.Exists(CONFIG_FILE_NAME)) @@ -26,18 +18,19 @@ public static void LoadSettings() using (StreamReader streamReader = new StreamReader(CONFIG_FILE_NAME)) using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader)) { - appSettings = jsonSerializer.Deserialize(jsonTextReader); + result = jsonSerializer.Deserialize(jsonTextReader); } } - AppSettings.ValidateAndCorrect(appSettings); + AppSettings.ValidateAndCorrect(result); } catch { - appSettings = AppSettings.Default; + result = AppSettings.Default; } + return result; } - public static void SaveSettings() + public static void SaveSettings(AppSettings appSettings) { JsonSerializer jsonSerializer = new JsonSerializer(); using (StreamWriter streamWriter = new StreamWriter(CONFIG_FILE_NAME)) diff --git a/Import/SqlDumpReader.cs b/Models/SqlDump/SqlDumpReader.cs similarity index 99% rename from Import/SqlDumpReader.cs rename to Models/SqlDump/SqlDumpReader.cs index 29a337f..50589be 100644 --- a/Import/SqlDumpReader.cs +++ b/Models/SqlDump/SqlDumpReader.cs @@ -3,11 +3,11 @@ using System.Globalization; using System.IO; using System.Linq; -using LibgenDesktop.Database; +using LibgenDesktop.Models.Entities; using SharpCompress.Archives.Rar; using SharpCompress.Archives.Zip; -namespace LibgenDesktop.Import +namespace LibgenDesktop.Models.SqlDump { internal class SqlDumpReader : IDisposable { diff --git a/Models/Utils/AsyncBookCollection.cs b/Models/Utils/AsyncBookCollection.cs new file mode 100644 index 0000000..8d1c53e --- /dev/null +++ b/Models/Utils/AsyncBookCollection.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Threading; +using LibgenDesktop.Models.Entities; + +namespace LibgenDesktop.Models.Utils +{ + internal class AsyncBookCollection : IReadOnlyList, IList, INotifyCollectionChanged, INotifyPropertyChanged + { + internal class BookEnumerator : IEnumerator, IEnumerator + { + private readonly List sourceList; + private readonly int sourceListItemLimit; + private int currentIndex; + + public BookEnumerator(List sourceList, int sourceListItemLimit) + { + this.sourceList = sourceList; + this.sourceListItemLimit = sourceListItemLimit; + currentIndex = -1; + } + + public Book Current => sourceList[currentIndex]; + + object IEnumerator.Current => Current; + + public void Dispose() + { + } + + public bool MoveNext() + { + currentIndex++; + System.Diagnostics.Debug.WriteLine("MoveNext " + currentIndex.ToString()); + return currentIndex < sourceListItemLimit; + } + + public void Reset() + { + throw new NotSupportedException(); + } + } + + private readonly List internalList; + private readonly SynchronizationContext synchronizationContext; + + private int reportedBookCount; + + public AsyncBookCollection() + { + internalList = new List(); + synchronizationContext = SynchronizationContext.Current; + reportedBookCount = 0; + } + + public Book this[int index] => internalList[index]; + + public int Count => reportedBookCount; + public int AddedBookCount => internalList.Count; + + public bool IsReadOnly => true; + + public bool IsFixedSize => throw new NotImplementedException(); + + public object SyncRoot => throw new NotImplementedException(); + + public bool IsSynchronized => throw new NotImplementedException(); + + object IList.this[int index] { get => this[index]; set => throw new NotImplementedException(); } + + public event NotifyCollectionChangedEventHandler CollectionChanged; + public event PropertyChangedEventHandler PropertyChanged; + + public void SetCapacity(int capacity) + { + internalList.Capacity = capacity; + } + + public void AddBook(Book book) + { + internalList.Add(book); + } + + public void AddBooks(IEnumerable books) + { + internalList.AddRange(books); + } + + public void UpdateReportedBookCount() + { + reportedBookCount = AddedBookCount; + NotifyReset(); + } + + public IEnumerator GetEnumerator() + { + return new BookEnumerator(internalList, reportedBookCount); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new BookEnumerator(internalList, reportedBookCount); + } + + public int Add(object value) + { + throw new NotImplementedException(); + } + + public bool Contains(object value) + { + return internalList.Contains((Book)value); + } + + public void Clear() + { + reportedBookCount = 0; + internalList.Clear(); + NotifyReset(); + } + + public int IndexOf(object value) + { + return internalList.IndexOf((Book)value); + } + + public void Insert(int index, object value) + { + throw new NotImplementedException(); + } + + public void Remove(object value) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + if (SynchronizationContext.Current == synchronizationContext) + { + RaiseCollectionChanged(e); + } + else + { + synchronizationContext.Post(RaiseCollectionChanged, e); + } + } + + protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) + { + if (SynchronizationContext.Current == synchronizationContext) + { + RaisePropertyChanged(e); + } + else + { + synchronizationContext.Post(RaisePropertyChanged, e); + } + } + + private void NotifyReset() + { + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + private void RaiseCollectionChanged(object param) + { + CollectionChanged?.Invoke(this, param as NotifyCollectionChangedEventArgs); + } + + private void RaisePropertyChanged(object param) + { + PropertyChanged?.Invoke(this, param as PropertyChangedEventArgs); + } + } +} diff --git a/Interface/Formatters.cs b/Models/Utils/Formatters.cs similarity index 97% rename from Interface/Formatters.cs rename to Models/Utils/Formatters.cs index bd4aefd..00a407f 100644 --- a/Interface/Formatters.cs +++ b/Models/Utils/Formatters.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Text; -namespace LibgenDesktop.Interface +namespace LibgenDesktop.Models.Utils { internal static class Formatters { diff --git a/Models/Utils/RangeCollection.cs b/Models/Utils/RangeCollection.cs new file mode 100644 index 0000000..1929b91 --- /dev/null +++ b/Models/Utils/RangeCollection.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LibgenDesktop.Models.Utils +{ + internal class RangeCollection : IList + { + internal class RangeCollectionEnumerator : IEnumerator + { + private readonly IList sourceList; + private readonly int sourceListItemLimit; + private int currentIndex; + + public RangeCollectionEnumerator(IList sourceList, int sourceListItemLimit) + { + this.sourceList = sourceList; + this.sourceListItemLimit = sourceListItemLimit; + currentIndex = -1; + } + + public object Current => sourceList[currentIndex]; + + public bool MoveNext() + { + currentIndex++; + return currentIndex < sourceListItemLimit; + } + + public void Reset() + { + throw new NotSupportedException(); + } + } + + private readonly IList fullCollection; + private readonly int itemLimit; + + public RangeCollection(IList fullCollection, int itemLimit) + { + this.fullCollection = fullCollection; + this.itemLimit = itemLimit; + } + + public object this[int index] + { + get => fullCollection[index]; + set => throw new NotImplementedException(); + } + + public bool IsReadOnly => true; + + public bool IsFixedSize => false; + + public int Count => itemLimit; + + public object SyncRoot => throw new NotImplementedException(); + + public bool IsSynchronized => throw new NotImplementedException(); + + public int Add(object value) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(object value) + { + throw new NotImplementedException(); + } + + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + return new RangeCollectionEnumerator(fullCollection, itemLimit); + } + + public int IndexOf(object value) + { + return fullCollection.IndexOf(value); + } + + public void Insert(int index, object value) + { + throw new NotImplementedException(); + } + + public void Remove(object value) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + } +} diff --git a/Program.cs b/Program.cs deleted file mode 100644 index c5e59dd..0000000 --- a/Program.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Windows.Forms; -using LibgenDesktop.Interface; - -namespace LibgenDesktop -{ - static class Program - { - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm()); - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 7169ede..be17be2 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -1,6 +1,8 @@ using System.Reflection; +using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Windows; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -19,8 +21,25 @@ // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("5b0ed485-1c8d-4ec9-ae28-20fc5e74fadd")] +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + // Version information for an assembly consists of the following four values: // diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs index aa688cd..f98095c 100644 --- a/Properties/Resources.Designer.cs +++ b/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace LibgenDesktop.Properties { - using System; - - +namespace LibgenDesktop.Properties +{ + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,43 +19,51 @@ namespace LibgenDesktop.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - + internal class Resources + { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { + internal Resources() + { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LibgenDesktop.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { + internal static global::System.Globalization.CultureInfo Culture + { + get + { return resourceCulture; } - set { + set + { resourceCulture = value; } } diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs index 5db80c8..0c99f17 100644 --- a/Properties/Settings.Designer.cs +++ b/Properties/Settings.Designer.cs @@ -8,17 +8,21 @@ // //------------------------------------------------------------------------------ -namespace LibgenDesktop.Properties { - - +namespace LibgenDesktop.Properties +{ + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { + + public static Settings Default + { + get + { return defaultInstance; } } diff --git a/Properties/Settings.settings b/Properties/Settings.settings index 3964565..033d7a5 100644 --- a/Properties/Settings.settings +++ b/Properties/Settings.settings @@ -1,7 +1,7 @@  - + - + \ No newline at end of file diff --git a/Resources/check.png b/Resources/check.png deleted file mode 100644 index 2db34cc..0000000 Binary files a/Resources/check.png and /dev/null differ diff --git a/ViewModels/BookDetailsWindowViewModel.cs b/ViewModels/BookDetailsWindowViewModel.cs new file mode 100644 index 0000000..de8e91e --- /dev/null +++ b/ViewModels/BookDetailsWindowViewModel.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; +using System.Windows.Media.Imaging; +using LibgenDesktop.Common; +using LibgenDesktop.Infrastructure; +using LibgenDesktop.Models; +using LibgenDesktop.Models.Entities; +using LibgenDesktop.Models.Settings; + +namespace LibgenDesktop.ViewModels +{ + internal class BookDetailsWindowViewModel : ViewModel + { + private readonly MainModel mainModel; + private bool bookCoverNotLoadedDueToOfflineMode; + private bool bookCoverLoading; + private bool bookCoverLoadingFailed; + private bool noCover; + private bool bookCoverVisible; + private BitmapImage bookCover; + + public BookDetailsWindowViewModel(MainModel mainModel, Book mainBookInfo) + { + this.mainModel = mainModel; + DownloadBookCommand = new Command(DownloadBook); + CloseCommand = new Command(CloseWindow); + WindowClosedCommand = new Command(WindowClosed); + Initialize(mainBookInfo); + } + + public Book Book { get; private set; } + public string WindowTitle { get; private set; } + public int WindowWidth { get; set; } + public int WindowHeight { get; set; } + public bool IsInOfflineMode { get; private set; } + + public bool BookCoverNotLoadedDueToOfflineMode + { + get + { + return bookCoverNotLoadedDueToOfflineMode; + } + private set + { + bookCoverNotLoadedDueToOfflineMode = value; + NotifyPropertyChanged(); + } + } + + public bool BookCoverLoading + { + get + { + return bookCoverLoading; + } + private set + { + bookCoverLoading = value; + NotifyPropertyChanged(); + } + } + + public bool BookCoverLoadingFailed + { + get + { + return bookCoverLoadingFailed; + } + private set + { + bookCoverLoadingFailed = value; + NotifyPropertyChanged(); + } + } + + public bool NoCover + { + get + { + return noCover; + } + private set + { + noCover = value; + NotifyPropertyChanged(); + } + } + + public bool BookCoverVisible + { + get + { + return bookCoverVisible; + } + private set + { + bookCoverVisible = value; + NotifyPropertyChanged(); + } + } + + public BitmapImage BookCover + { + get + { + return bookCover; + } + private set + { + bookCover = value; + NotifyPropertyChanged(); + } + } + + public Command DownloadBookCommand { get; } + public Command CloseCommand { get; } + public Command WindowClosedCommand { get; } + + private async void Initialize(Book mainBookInfo) + { + WindowTitle = mainBookInfo.Title; + WindowWidth = mainModel.AppSettings.BookWindow.Width; + WindowHeight = mainModel.AppSettings.BookWindow.Height; + if (mainModel.AppSettings.OfflineMode) + { + IsInOfflineMode = true; + BookCoverNotLoadedDueToOfflineMode = true; + BookCoverLoading = false; + } + else + { + IsInOfflineMode = false; + BookCoverNotLoadedDueToOfflineMode = false; + BookCoverLoading = true; + } + BookCoverLoadingFailed = false; + NoCover = false; + BookCoverVisible = false; + BookCover = null; + Book = await mainModel.LoadBookAsync(mainBookInfo.Id); + NotifyPropertyChanged(nameof(Book)); + if (String.IsNullOrWhiteSpace(Book.ExtendedProperties.CoverUrl)) + { + BookCoverNotLoadedDueToOfflineMode = false; + BookCoverLoading = false; + NoCover = true; + } + else + { + if (!IsInOfflineMode) + { + try + { + WebClient webClient = new WebClient(); + byte[] imageData = await webClient.DownloadDataTaskAsync(new Uri(Constants.BOOK_COVER_URL_PREFIX + Book.ExtendedProperties.CoverUrl)); + BitmapImage bitmapImage = new BitmapImage(); + using (MemoryStream memoryStream = new MemoryStream(imageData)) + { + bitmapImage.BeginInit(); + bitmapImage.CacheOption = BitmapCacheOption.OnLoad; + bitmapImage.StreamSource = memoryStream; + bitmapImage.EndInit(); + bitmapImage.Freeze(); + } + BookCover = bitmapImage; + BookCoverVisible = true; + } + catch + { + BookCoverLoadingFailed = true; + } + BookCoverLoading = false; + } + } + } + + private void DownloadBook() + { + Process.Start(Constants.BOOK_DOWNLOAD_URL_PREFIX + Book.ExtendedProperties.Md5Hash); + } + + private void CloseWindow() + { + IWindowContext currentWindowContext = WindowManager.GetCreatedWindowContext(this); + currentWindowContext.CloseDialog(false); + } + + private void WindowClosed() + { + mainModel.AppSettings.BookWindow = new AppSettings.BookWindowSettings + { + Width = WindowWidth, + Height = WindowHeight + }; + mainModel.SaveSettings(); + } + } +} diff --git a/ViewModels/ErrorWindowViewModel.cs b/ViewModels/ErrorWindowViewModel.cs new file mode 100644 index 0000000..7caf529 --- /dev/null +++ b/ViewModels/ErrorWindowViewModel.cs @@ -0,0 +1,23 @@ +using System.Windows; +using LibgenDesktop.Infrastructure; + +namespace LibgenDesktop.ViewModels +{ + internal class ErrorWindowViewModel : ViewModel + { + public ErrorWindowViewModel(string error) + { + Error = error; + CopyErrorCommand = new Command(CopyErrorToClipboard); + } + + public string Error { get; } + + public Command CopyErrorCommand { get; } + + private void CopyErrorToClipboard() + { + Clipboard.SetText(Error); + } + } +} diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..ce181ec --- /dev/null +++ b/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,359 @@ +using System; +using System.Linq; +using System.Threading; +using LibgenDesktop.Infrastructure; +using LibgenDesktop.Models; +using LibgenDesktop.Models.Entities; +using LibgenDesktop.Models.ProgressArgs; +using LibgenDesktop.Models.Settings; +using LibgenDesktop.Models.Utils; + +namespace LibgenDesktop.ViewModels +{ + internal class MainWindowViewModel : ViewModel + { + private readonly MainModel mainModel; + private AsyncBookCollection books; + private bool allowSqlDumpImport; + private bool isInOfflineMode; + private bool isInSearchMode; + private double progressValue; + private bool isProgressVisible; + private bool isProgressIndeterminate; + private bool isSearchindDisabled; + private string statusText; + + public MainWindowViewModel() + { + mainModel = new MainModel(); + OpenBookDetailsCommand = new Command(param => OpenBookDetails(param as Book)); + ImportSqlDumpCommand = new Command(ImportSqlDump); + ExitCommand = new Command(Exit); + SearchCommand = new Command(Search); + BookDataGridEnterKeyCommand = new Command(BookDataGridEnterKeyPressed); + WindowClosedCommand = new Command(WindowClosed); + Initialize(); + } + + public int WindowWidth { get; set; } + public int WindowHeight { get; set; } + public int WindowLeft { get; set; } + public int WindowTop { get; set; } + public bool IsWindowMaximized { get; set; } + public int TitleColumnWidth { get; set; } + public int AuthorsColumnWidth { get; set; } + public int SeriesColumnWidth { get; set; } + public int YearColumnWidth { get; set; } + public int PublisherColumnWidth { get; set; } + public int FormatColumnWidth { get; set; } + public int FileSizeColumnWidth { get; set; } + public int OcrColumnWidth { get; set; } + public string SearchQuery { get; set; } + public Book SelectedBook { get; set; } + + public AsyncBookCollection Books + { + get + { + return books; + } + private set + { + books = value; + NotifyPropertyChanged(); + } + } + + public bool AllowSqlDumpImport + { + get + { + return allowSqlDumpImport; + } + set + { + allowSqlDumpImport = value; + NotifyPropertyChanged(); + } + } + + public bool IsInOfflineMode + { + get + { + return isInOfflineMode; + } + set + { + isInOfflineMode = value; + NotifyPropertyChanged(); + mainModel.AppSettings.OfflineMode = value; + mainModel.SaveSettings(); + } + } + + public double ProgressValue + { + get + { + return progressValue; + } + private set + { + progressValue = value; + NotifyPropertyChanged(); + } + } + + public bool IsProgressVisible + { + get + { + return isProgressVisible; + } + private set + { + isProgressVisible = value; + NotifyPropertyChanged(); + } + } + + public bool IsProgressIndeterminate + { + get + { + return isProgressIndeterminate; + } + set + { + isProgressIndeterminate = value; + NotifyPropertyChanged(); + } + } + + public bool IsSearchindDisabled + { + get + { + return isSearchindDisabled; + } + private set + { + isSearchindDisabled = value; + NotifyPropertyChanged(); + } + } + + public string StatusText + { + get + { + return statusText; + } + private set + { + statusText = value; + NotifyPropertyChanged(); + } + } + + public Command OpenBookDetailsCommand { get; } + public Command ImportSqlDumpCommand { get; } + public Command ExitCommand { get; } + public Command SearchCommand { get; } + public Command BookDataGridEnterKeyCommand { get; } + public Command WindowClosedCommand { get; } + + private async void Initialize() + { + AppSettings appSettings = mainModel.AppSettings; + AppSettings.MainWindowSettings mainWindowSettings = appSettings.MainWindow; + WindowWidth = mainWindowSettings.Width; + WindowHeight = mainWindowSettings.Height; + WindowLeft = mainWindowSettings.Left; + WindowTop = mainWindowSettings.Top; + IsWindowMaximized = mainWindowSettings.Maximized; + AppSettings.ColumnSettings columnSettings = appSettings.Columns; + TitleColumnWidth = columnSettings.TitleColumnWidth; + AuthorsColumnWidth = columnSettings.AuthorsColumnWidth; + SeriesColumnWidth = columnSettings.SeriesColumnWidth; + YearColumnWidth = columnSettings.YearColumnWidth; + PublisherColumnWidth = columnSettings.PublisherColumnWidth; + FormatColumnWidth = columnSettings.FormatColumnWidth; + FileSizeColumnWidth = columnSettings.FileSizeColumnWidth; + OcrColumnWidth = columnSettings.OcrColumnWidth; + isInOfflineMode = appSettings.OfflineMode; + allowSqlDumpImport = false; + SearchQuery = String.Empty; + IsSearchindDisabled = false; + books = mainModel.AllBooks; + isInSearchMode = false; + progressValue = 0; + isProgressVisible = true; + isProgressIndeterminate = false; + statusText = "Загрузка книг..."; + Progress loadAllBooksProgressHandler = new Progress(HandleLoadAllBooksProgress); + CancellationToken cancellationToken = new CancellationToken(); + try + { + await mainModel.LoadAllBooksAsync(loadAllBooksProgressHandler, cancellationToken); + } + catch (Exception exception) + { + ShowErrorWindow(exception); + } + InitialLoadCompleted(); + } + + private void HandleLoadAllBooksProgress(LoadAllBooksProgress loadAllBooksProgress) + { + if (!isInSearchMode) + { + IsProgressIndeterminate = false; + if (!loadAllBooksProgress.IsFinished) + { + IsProgressVisible = true; + StatusText = $"Загрузка книг (загружено { loadAllBooksProgress.BooksLoaded.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)} из { loadAllBooksProgress.TotalBookCount.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)})..."; + ProgressValue = (double)loadAllBooksProgress.BooksLoaded / loadAllBooksProgress.TotalBookCount; + } + } + } + + private void BookDataGridEnterKeyPressed() + { + OpenBookDetails(SelectedBook); + } + + private void OpenBookDetails(Book book) + { + IWindowContext currentWindowContext = WindowManager.GetCreatedWindowContext(this); + BookDetailsWindowViewModel bookDetailsWindowViewModel = new BookDetailsWindowViewModel(mainModel, book); + IWindowContext bookDetailsWindowContext = WindowManager.CreateWindow(RegisteredWindows.WindowKey.BOOK_DETAILS_WINDOW, bookDetailsWindowViewModel, currentWindowContext); + AppSettings.BookWindowSettings bookWindowSettings = mainModel.AppSettings.BookWindow; + bookDetailsWindowContext.ShowDialog(bookWindowSettings.Width, bookWindowSettings.Height); + } + + private void ImportSqlDump() + { + OpenFileDialogParameters selectSqlDumpFileDialogParameters = new OpenFileDialogParameters + { + DialogTitle = "Выбор SQL-дампа", + Filter = "SQL-дампы (*.sql, *.zip, *.rar)|*.sql;*zip;*.rar|Все файлы (*.*)|*.*", + Multiselect = false + }; + OpenFileDialogResult selectSqlDumpFileDialogResult = WindowManager.ShowOpenFileDialog(selectSqlDumpFileDialogParameters); + if (selectSqlDumpFileDialogResult.DialogResult) + { + StatusText = "Импорт из SQL-дампа..."; + IWindowContext currentWindowContext = WindowManager.GetCreatedWindowContext(this); + SqlDumpImportWindowViewModel sqlDumpImportWindowViewModel = new SqlDumpImportWindowViewModel(mainModel, selectSqlDumpFileDialogResult.SelectedFilePaths.First()); + IWindowContext sqlDumpImportWindowContext = WindowManager.CreateWindow(RegisteredWindows.WindowKey.SQL_DUMP_IMPORT_WINDOW, sqlDumpImportWindowViewModel, currentWindowContext); + sqlDumpImportWindowContext.ShowDialog(); + SetTotalBookNumberStatus(); + } + } + + private async void Search() + { + string searchQuery = SearchQuery.Trim(); + if (String.IsNullOrEmpty(searchQuery)) + { + Books = mainModel.AllBooks; + SetTotalBookNumberStatus(); + IsSearchindDisabled = false; + isInSearchMode = false; + } + else + { + isInSearchMode = true; + IsProgressIndeterminate = true; + IsProgressVisible = true; + IsSearchindDisabled = true; + statusText = "Поиск книг..."; + mainModel.ClearSearchResults(); + Books = mainModel.SearchResults; + Progress searchBooksProgressHandler = new Progress(HandleSearchBooksProgress); + CancellationToken cancellationToken = new CancellationToken(); + try + { + await mainModel.SearchBooksAsync(searchQuery, searchBooksProgressHandler, cancellationToken); + } + catch (Exception exception) + { + ShowErrorWindow(exception); + } + IsProgressVisible = false; + IsSearchindDisabled = false; + SetFoundBooksStatus(); + } + } + + private void HandleSearchBooksProgress(SearchBooksProgress searchBooksProgress) + { + if (isInSearchMode) + { + if (!searchBooksProgress.IsFinished) + { + IsProgressVisible = true; + IsProgressIndeterminate = true; + StatusText = $"Поиск книг (найдено { searchBooksProgress.BooksFound.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)})..."; + } + } + } + + private void SetTotalBookNumberStatus() + { + StatusText = $"Всего книг: {Books.Count.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)}"; + } + + private void SetFoundBooksStatus() + { + StatusText = $"Найдено книг: {Books.Count.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)}"; + } + + private void InitialLoadCompleted() + { + UpdateAllowSqlDumpImport(); + if (!isInSearchMode) + { + IsProgressVisible = false; + SetTotalBookNumberStatus(); + } + } + + private void UpdateAllowSqlDumpImport() + { + AllowSqlDumpImport = mainModel.AllBooks.Count == 0; + } + + private void Exit() + { + IWindowContext currentWindowContext = WindowManager.GetCreatedWindowContext(this); + currentWindowContext.Close(); + } + + private void WindowClosed() + { + mainModel.AppSettings.MainWindow = new AppSettings.MainWindowSettings + { + Width = WindowWidth, + Height = WindowHeight, + Left = WindowLeft, + Top = WindowTop, + Maximized = IsWindowMaximized + }; + mainModel.AppSettings.Columns = new AppSettings.ColumnSettings + { + TitleColumnWidth = TitleColumnWidth, + AuthorsColumnWidth = AuthorsColumnWidth, + SeriesColumnWidth = SeriesColumnWidth, + YearColumnWidth = YearColumnWidth, + PublisherColumnWidth = PublisherColumnWidth, + FormatColumnWidth = FormatColumnWidth, + FileSizeColumnWidth = FileSizeColumnWidth, + OcrColumnWidth = OcrColumnWidth + }; + mainModel.SaveSettings(); + } + } +} diff --git a/ViewModels/SqlDumpImportWindowViewModel.cs b/ViewModels/SqlDumpImportWindowViewModel.cs new file mode 100644 index 0000000..9e8d8e5 --- /dev/null +++ b/ViewModels/SqlDumpImportWindowViewModel.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using LibgenDesktop.Infrastructure; +using LibgenDesktop.Models; +using LibgenDesktop.Models.ProgressArgs; +using LibgenDesktop.Models.Utils; + +namespace LibgenDesktop.ViewModels +{ + internal class SqlDumpImportWindowViewModel : ViewModel + { + private readonly MainModel mainModel; + private readonly string sqlDumpFilePath; + private DateTime operationStartTime; + private CancellationTokenSource cancellationTokenSource; + private bool allowWindowClose; + private string progressDescription; + private double progressValue; + private string status; + private bool cancellationEnabled; + + public SqlDumpImportWindowViewModel(MainModel mainModel, string sqlDumpFilePath) + { + this.mainModel = mainModel; + this.sqlDumpFilePath = sqlDumpFilePath; + CancelCommand = new Command(CancelImport); + WindowClosingCommand = new FuncCommand(WindowClosing); + Initialize(); + } + + public string ProgressDescription + { + get + { + return progressDescription; + } + private set + { + progressDescription = value; + NotifyPropertyChanged(); + } + } + + public double ProgressValue + { + get + { + return progressValue; + } + private set + { + progressValue = value; + NotifyPropertyChanged(); + } + } + + public string Status + { + get + { + return status; + } + private set + { + status = value; + NotifyPropertyChanged(); + } + } + + public bool CancellationEnabled + { + get + { + return cancellationEnabled; + } + private set + { + cancellationEnabled = value; + NotifyPropertyChanged(); + } + } + + public Command CancelCommand { get; } + public FuncCommand WindowClosingCommand { get; } + + private async void Initialize() + { + operationStartTime = DateTime.Now; + ProgressDescription = "Импорт из SQL-дампа..."; + UpdateStatus(0); + cancellationEnabled = true; + allowWindowClose = false; + Progress importSqlDumpProgressHandler = new Progress(HandleImportSqlDumpProgress); + cancellationTokenSource = new CancellationTokenSource(); + CancellationToken cancellationToken = cancellationTokenSource.Token; + try + { + await mainModel.ImportSqlDumpAsync(sqlDumpFilePath, importSqlDumpProgressHandler, cancellationToken); + } + catch (Exception exception) + { + ShowErrorWindow(exception); + } + allowWindowClose = true; + IWindowContext currentWindowContext = WindowManager.GetCreatedWindowContext(this); + currentWindowContext.Close(); + } + + private void UpdateStatus(double completed) + { + DateTime now = DateTime.Now; + TimeSpan elapsed = now - operationStartTime; + string newStatus = $"Прошло {elapsed:mm\\:ss}"; + if (completed > 0.05) + { + TimeSpan remaining = TimeSpan.FromSeconds(elapsed.TotalSeconds / completed * (1 - completed)); + newStatus += $", осталось {remaining:mm\\:ss}"; + } + Status = newStatus; + ProgressValue = completed; + } + + private void HandleImportSqlDumpProgress(ImportSqlDumpProgress importSqlDumpProgress) + { + ProgressDescription = $"Импорт из SQL-дампа... (импортировано {importSqlDumpProgress.BooksImported.ToString("N0", Formatters.ThousandsSeparatedNumberFormat)} книг)"; + UpdateStatus((double)importSqlDumpProgress.BytesParsed / importSqlDumpProgress.TotalBytes); + } + + private void CancelImport() + { + CancellationEnabled = false; + cancellationTokenSource.Cancel(); + } + + private bool WindowClosing() + { + if (!allowWindowClose) + { + CancelImport(); + } + return allowWindowClose; + } + } +} diff --git a/ViewModels/ViewModel.cs b/ViewModels/ViewModel.cs new file mode 100644 index 0000000..fc068dc --- /dev/null +++ b/ViewModels/ViewModel.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using LibgenDesktop.Infrastructure; + +namespace LibgenDesktop.ViewModels +{ + internal abstract class ViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void NotifyPropertyChanged([CallerMemberName]string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected void ShowErrorWindow(Exception exception) + { + IWindowContext currentWindowContext = WindowManager.GetCreatedWindowContext(this); + ErrorWindowViewModel errorWindowViewModel = new ErrorWindowViewModel(exception.ToString()); + IWindowContext bookDetailsWindowContext = WindowManager.CreateWindow(RegisteredWindows.WindowKey.ERROR_WINDOW, errorWindowViewModel, currentWindowContext); + bookDetailsWindowContext.ShowDialog(); + } + } +} diff --git a/Views/BookDetailsWindow.xaml b/Views/BookDetailsWindow.xaml new file mode 100644 index 0000000..a86e4d7 --- /dev/null +++ b/Views/BookDetailsWindow.xaml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + Обложка не загружена, потому что + + включен автономный режим + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +