/// <summary> /// Add category to the book /// </summary> private void Category_OnKeyUp(object sender, KeyEventArgs e) { var textBox = (TextBox)sender; if (textBox.Text.Length <= 1 || (textBox.Text.Substring(textBox.Text.Length - 1) != ";" && textBox.Text.Substring(textBox.Text.Length - 1) != ",")) { return; } var category = textBox.Text.Substring(0, textBox.Text.Length - 1); textBox.Text = String.Empty; if (_categoryTagList.Any(categoryTag => categoryTag.Name == category)) { return; } CategoryTagsBorder.Visibility = Visibility.Visible; _categoryTagList.Add(new CategoryTag { Name = category }); Db.NonQuery("INSERT OR IGNORE INTO categories VALUES(@Path, @Name, 0)", new [] { new SQLiteParameter("Path", BookKeeper.GetRelativeBookFilePath(_bookFile)), new SQLiteParameter("Name", category) }); }
/// <summary> /// Go through all database records along with all book files in the library root folder and remove any database entries which point to non-existing files, /// then attempt to generate database entries for all book files, which don't have them. /// </summary> public static void SyncDbWithFileTree() { MainWindow.Busy(true); Task.Factory.StartNew(() => { GenerateFileTree(); Tools.RemoveEmptyDirectories(Properties.Settings.Default.BooksDir); var fileTree = GetFileTree(); const string sql = "SELECT Path FROM books"; const string sqlDelete = "DELETE FROM books WHERE Path = @Path"; var query = Db.Query(sql); var pathList = new List <string>(); MainWindow.Busy(UiLang.Get("BusyCleaningDb")); //Delete rows pointing to non-existing files while (query.Read()) { if (File.Exists(BookKeeper.GetAbsoluteBookFilePath(query["Path"].ToString()))) { pathList.Add(query["Path"].ToString()); } else { Db.NonQuery(sqlDelete, new[] { new SQLiteParameter("Path", query["Path"].ToString()) }); } } MainWindow.BusyMax(fileTree.Count(bookFile => !pathList.Contains(BookKeeper.GetRelativeBookFilePath(bookFile)))); var i = 0; //Generate rows for any books missing them foreach ( var bookFile in fileTree.Where(bookFile => !pathList.Contains(BookKeeper.GetRelativeBookFilePath(bookFile)))) { MainWindow.Busy(BookKeeper.GetRelativeBookFilePath(bookFile)); MainWindow.Busy(i++); try { BookKeeper.GetData(bookFile); } catch (Exception e) { DebugConsole.WriteLine( "Library structure: I found a book file without any entry in the database (" + bookFile + "), but an error occurred during attempted adding: " + e); } } MainWindow.MW.Dispatcher.Invoke(() => MainWindow.MW.BookGridReload()); MainWindow.Busy(false); }); }
private void Discard_OnClick(object sender, RoutedEventArgs e) { if (MessageBox.Show(String.Format(UiLang.Get("BookDeleteConfirm"), BookInfoGet("Title")), UiLang.Get("BookDeleteConfirmTitle"), MessageBoxButton.YesNo) != MessageBoxResult.Yes) { return; } BookKeeper.Discard(_bookFile); Close(); }
private void BookGrid_OnKeyUp(object sender, KeyEventArgs e) { var dataGrid = (DataGrid)sender; var forcedSettingValue = (Keyboard.IsKeyDown(Key.LeftShift) ? true : (Keyboard.IsKeyDown(Key.LeftCtrl) ? false : (bool?)null)); switch (e.Key) { case Key.Delete: if ( MessageBox.Show( String.Format(UiLang.Get("DeleteBooksConfirm"), dataGrid.SelectedItems.Count), UiLang.Get("DiscardBook"), MessageBoxButton.YesNo) != MessageBoxResult.Yes) { return; } foreach (var book in dataGrid.SelectedItems.Cast <Book>().ToList()) { BookKeeper.Discard(book.BookFile); } break; case Key.F: foreach (var book in dataGrid.SelectedItems.Cast <Book>().ToList()) { BookInfoSet("Favorite", (forcedSettingValue ?? (!book.Favorite)), book.BookFile); } BookGridReload(); //Reload grid in the main window break; case Key.S: foreach (var book in dataGrid.SelectedItems.Cast <Book>().ToList()) { BookInfoSet("Sync", (forcedSettingValue ?? (!book.Sync)), book.BookFile); } BookGridReload(); //Reload grid in the main window break; case Key.D: foreach (var book in dataGrid.SelectedItems.Cast <Book>().ToList()) { BookInfoSet("Sync", (forcedSettingValue ?? (!book.Sync)), book.BookFile); BookInfoSet("Favorite", (forcedSettingValue ?? (!book.Favorite)), book.BookFile); } BookGridReload(); //Reload grid in the main window break; } }
/// <summary> /// Remove category from the book /// </summary> private void CategoryTag_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var categoryTag = (CategoryTag)(((Button)sender).DataContext); _categoryTagList.Remove(categoryTag); if (_categoryTagList.Count == 0) { CategoryTagsBorder.Visibility = Visibility.Collapsed; } Db.NonQuery("DELETE FROM categories WHERE Name = @Name AND Path = @Path", new [] { new SQLiteParameter("Name", categoryTag.Name), new SQLiteParameter("Path", BookKeeper.GetRelativeBookFilePath(_bookFile)) }); }
/// <summary> /// Mark automatically added category as user added category /// </summary> private void CategoryTag_OnPreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) { var categoryTag = (CategoryTag)(((Button)sender).DataContext); if (_categoryTagList.Count == 0) { CategoryTagsBorder.Visibility = Visibility.Collapsed; } Db.NonQuery("UPDATE categories SET FromFile = 0 WHERE Name = @Name AND Path = @Path", new[] { new SQLiteParameter("Name", categoryTag.Name), new SQLiteParameter("Path", BookKeeper.GetRelativeBookFilePath(_bookFile)) }); categoryTag.FromFile = false; ((Button)sender).BorderBrush = Brushes.DarkRed; }
//Change value in existing .dat file for a book private static void BookInfoSet(string key, object value, string bookFile) { try { var bookData = BookKeeper.GetData(bookFile); if ((typeof(BookData)).GetField("Sync") == null) { return; } typeof(BookData).GetField(key).SetValue(bookData, value); BookKeeper.SaveData(bookData); } catch (Exception e) { DebugConsole.WriteLine("Edit book: It was not possible to save the provided book data: " + e.Message); } }
/// <summary> /// Save data from the EditBook fields into the database /// </summary> /// <param name="key">Name of the BookData field to which to save the data</param> /// <param name="value">Value to be saved into the specified field</param> private void BookInfoSet(string key, object value) { if (_bookData == null) { return; } typeof(BookData).GetField(key).SetValue(_bookData, value); try { BookKeeper.SaveData(_bookData); } catch (Exception e) { MainWindow.Info(String.Format(UiLang.Get("DatFileNotAvailable"), _bookData.Title), 1); DebugConsole.WriteLine("Edit book: It was not possible to save the provided value into the data file: " + e.Message); Close(); } }
/// <summary> /// Fetch data to fill the form fields, from the BookInfo object based on the key /// </summary> /// <param name="key">Name of the BookData field from which to get the data</param> /// <returns>Value from the BookData field specified by the given key</returns> private object BookInfoGet(string key) { if (_bookData == null) //Singleton, so we don't have to reopen the DB with saved info, after every form field loads and its load event handler calls BookInfoGet { try { _bookData = BookKeeper.GetData(_bookFile); } catch (Exception) { MainWindow.Info(UiLang.Get("BookInfoNotAvailable"), 1); IsEnabled = false; Close(); _bookData = new BookData(); return(null); } } return(typeof(BookData).GetField(key) != null ? typeof(BookData).GetField(key).GetValue(_bookData) : null); }
private static void AddBooksFromList(IEnumerable <string> list) { Busy(true); Task.Factory.StartNew(() => { foreach (var file in list) { BookKeeper.Add(file); } MW.Dispatcher.Invoke(() => { LibraryStructure.GenerateFileTree(); MW.BookGridReload(); }); Busy(false); }); }
/// <summary> /// Generate a list of BookData objects, where each of them contains information about a book /// </summary> public static List <BookData> List() { var bookData = new List <BookData>(); const string sql = "SELECT * FROM books"; var query = Db.Query(sql); while (query.Read()) { if (!File.Exists(BookKeeper.GetAbsoluteBookFilePath(query["Path"].ToString()))) { continue; } bookData.Add(BookKeeper.CastSqlBookRowToBookData(query)); } query.Dispose(); return(bookData); }
private void Category_OnLoaded(object sender, RoutedEventArgs e) { var query = Db.Query("SELECT Name, FromFile FROM categories WHERE Path = @Path", new [] { new SQLiteParameter("Path", BookKeeper.GetRelativeBookFilePath(_bookFile)) }); while (query.Read()) { var category = new CategoryTag { Name = query["Name"].ToString(), FromFile = SQLiteConvert.ToBoolean(query["FromFile"]) }; _categoryTagList.Add(category); } if (_categoryTagList.Count > 0) { CategoryTagsBorder.Visibility = Visibility.Visible; } Categories.ItemsSource = _categoryTagList; var defaultCategories = Properties.Settings.Default.DefaultCategories.Split(';'); var hintList = new List <string>(defaultCategories); hintList.AddRange(LibraryStructure.CategoryList()); hintList.Sort(); new Whisperer { TextBox = (TextBox)sender, HintList = hintList }; }
private void BookGrid_OnLoaded(object sender, RoutedEventArgs e) { DebugConsole.WriteLine("Loading the book grid..."); var gridLoadStopwatch = new Stopwatch(); gridLoadStopwatch.Start(); var sqlCategoriesQueryStopwatch = new Stopwatch(); var sqlbooksQueryStopwatch = new Stopwatch(); var grid = (DataGrid)sender; var sql = AssembleQuery(); var categoryList = new Dictionary <string, List <string> >(); var bookDataList = new List <BookData>(); var bookList = new List <Book>(); var cultureList = CultureInfo.GetCultures(CultureTypes.SpecificCultures); sqlbooksQueryStopwatch.Start(); //Turn rows form the db into BookData objects using (var query = Db.Query(sql.Item1, sql.Item2)) { sqlbooksQueryStopwatch.Stop(); sqlCategoriesQueryStopwatch.Start(); //Add list of attached categories to each BookData object using (var queryAllCategories = Db.Query("SELECT Path, Name FROM categories")) //Pull all categories from database, so that we don't have to do a separate query for each book { sqlCategoriesQueryStopwatch.Stop(); //Turn category rows into a single dictionary, where each key is a Path, whose value is a List of category Names associated with said Path while (queryAllCategories.Read()) { var key = queryAllCategories["Path"].ToString(); var value = queryAllCategories["Name"].ToString(); if (categoryList.ContainsKey(key)) { categoryList[key].Add(value); } else { categoryList.Add(key, new List <string> { value }); } } } //Cast book rows into BookData objects and assign them their category lists while (query.Read()) { var bookData = BookKeeper.CastSqlBookRowToBookData(query); if (categoryList.ContainsKey(query["Path"].ToString())) { bookData.Categories = categoryList[query["Path"].ToString()]; } bookDataList.Add(bookData); } } foreach (var bookData in bookDataList) { var countryCode = "_unknown"; if (bookData.Language != "" && cultureList.FirstOrDefault(x => x.Name == bookData.Language) != null) //Make sure the book language is not neutral (ex: en instead of en-US), or invalid. This will make sure we don't display for example US flag for british english. { countryCode = new RegionInfo((new CultureInfo(bookData.Language)).Name).TwoLetterISORegionName; } bookList.Add(new Book { Title = bookData.Title, Author = bookData.Author, Publisher = bookData.Publisher, CountryCode = countryCode, Published = bookData.Published, Description = bookData.Description, Series = bookData.Series, Categories = bookData.Categories, Size = Tools.BytesFormat(bookData.Size), Favorite = bookData.Favorite, Sync = bookData.Sync, Cover = bookData.Cover, BookFile = bookData.Path, }); } grid.ItemsSource = bookList.OrderBy(x => x.Title); //By default, the list is sorted by book titles. Note: this is much faster than ORDER BY Title directly in the sqlite query gridLoadStopwatch.Stop(); DebugConsole.WriteLine(String.Format("Book grid loaded. [Book count: {0}], [Grid load: {1}], [Categories query: {2}], [Books query: {3}]", bookList.Count, gridLoadStopwatch.Elapsed, sqlCategoriesQueryStopwatch.Elapsed, sqlbooksQueryStopwatch.Elapsed)); }
/// <summary> /// Take all books files marked for sync and copy them onto the reader device, if one is found. /// If any book files are found on the reader device, which are not marked for sync in the local library, they will be deleted from the device. /// </summary> private static void SyncBookFiles() { var localBookList = LibraryStructure.List(); var localBookListForSync = (from bookData in localBookList where bookData.Sync select BookKeeper.GetAbsoluteBookFilePath(bookData.Path)).ToList(); var bookList = GetFileList(); if (bookList == null) //The reader is not connected, or the specified storage folder on it doesn't exist, no point to continue { MainWindow.Busy(false); return; } var filesToDelete = (from file in bookList let fileName = Path.GetFileName(file) where !localBookListForSync.Select(Path.GetFileName).Contains(fileName) select file).ToArray(); var filesToCopy = localBookListForSync.Where( file => File.Exists(file) && !bookList.Select(Path.GetFileName).Contains(Path.GetFileName(file))).ToArray(); MainWindow.BusyMax(filesToDelete.Length + filesToCopy.Length); var busyCount = 0; foreach (var file in filesToDelete) //Delete files from the reader which don't exist in the local Sync list { MainWindow.Busy(busyCount++); MainWindow.Busy(file); try { File.SetAttributes(file, FileAttributes.Normal); File.Delete(file); } catch (Exception e) { DebugConsole.WriteLine("Usb sync: Failed to delete " + file + ": " + e); } } foreach (var file in filesToCopy) //Copy files (which don't exist in the reader) into the reader, from the local Sync list { DebugConsole.WriteLine("Copying " + file); MainWindow.Busy(busyCount++); MainWindow.Busy(file); try { if (file != null) { File.Copy(file, Path.Combine(_deviceDir, Path.GetFileName(file))); } } catch (Exception e) { MainWindow.Info(String.Format(UiLang.Get("SyncFileCopyFailed"), file)); DebugConsole.WriteLine("Usb sync: Error while copying " + file + ": " + e); } } MainWindow.Info(UiLang.Get("SyncFinished")); MainWindow.Busy(false); }