From 466a3e5c7f8f94921a9ee15af42dc1527b7c00fc Mon Sep 17 00:00:00 2001
From: libgenapps <33476799+libgenapps@users.noreply.github.com>
Date: Wed, 2 May 2018 04:36:29 +0300
Subject: [PATCH] Bookmarks
---
LibgenDesktop.Setup/AppFiles.cs | 1 +
LibgenDesktop.Setup/Constants.cs | 4 +-
LibgenDesktop/Common/Constants.cs | 6 +-
LibgenDesktop/LibgenDesktop.csproj | 7 +
.../Localization/LocalizationStorage.cs | 2 +-
.../Localizators/MainWindowLocalizator.cs | 4 +
.../SearchResultsTabLocalizator.cs | 4 +
.../Models/Localization/Translation.cs | 4 +
LibgenDesktop/Models/MainModel.cs | 40 +
LibgenDesktop/Models/Settings/AppSettings.cs | 55 ++
LibgenDesktop/Resources/Languages/English.lng | 4 +
LibgenDesktop/Resources/Languages/Russian.lng | 4 +
LibgenDesktop/Resources/Languages/Spanish.lng | 705 ++++++++++++++++++
.../Mirrors/libgen_io_nonfiction.xslt | 5 +
.../ViewModels/Bookmarks/BookmarkViewModel.cs | 12 +
.../Bookmarks/BookmarkViewModelList.cs | 37 +
.../Tabs/SearchResultsTabViewModel.cs | 47 ++
.../ViewModels/Windows/MainWindowViewModel.cs | 87 +++
.../Views/Controls/HorizontalScrollViewer.cs | 31 +
LibgenDesktop/Views/Controls/Toolbar.xaml | 21 +-
LibgenDesktop/Views/Styles/TabStyles.xaml | 18 +
LibgenDesktop/Views/Styles/ToolbarStyles.xaml | 15 +-
.../Views/Tabs/FictionSearchResultsTab.xaml | 27 +-
.../Tabs/NonFictionSearchResultsTab.xaml | 27 +-
.../Views/Tabs/SciMagSearchResultsTab.xaml | 27 +-
25 files changed, 1183 insertions(+), 11 deletions(-)
create mode 100644 LibgenDesktop/Resources/Languages/Spanish.lng
create mode 100644 LibgenDesktop/ViewModels/Bookmarks/BookmarkViewModel.cs
create mode 100644 LibgenDesktop/ViewModels/Bookmarks/BookmarkViewModelList.cs
create mode 100644 LibgenDesktop/Views/Controls/HorizontalScrollViewer.cs
diff --git a/LibgenDesktop.Setup/AppFiles.cs b/LibgenDesktop.Setup/AppFiles.cs
index a7d1a61..f147644 100644
--- a/LibgenDesktop.Setup/AppFiles.cs
+++ b/LibgenDesktop.Setup/AppFiles.cs
@@ -28,6 +28,7 @@ static AppFiles()
AddFile(@"Languages\Russian.lng");
AddFile(@"Languages\Romanian.lng");
AddFile(@"Languages\Ukrainian.lng");
+ AddFile(@"Languages\Spanish.lng");
AddFile(@"Mirrors\mirrors.config");
AddFile(@"Mirrors\libgen_io_nonfiction.xslt");
AddFile(@"Mirrors\libgen_io_fiction.xslt");
diff --git a/LibgenDesktop.Setup/Constants.cs b/LibgenDesktop.Setup/Constants.cs
index 018a06c..7dd584a 100644
--- a/LibgenDesktop.Setup/Constants.cs
+++ b/LibgenDesktop.Setup/Constants.cs
@@ -2,8 +2,8 @@
{
internal static class Constants
{
- public const string CURRENT_VERSION = "1.0.3";
- public const string TITLE_VERSION = "1.0.3";
+ public const string CURRENT_VERSION = "1.1.0";
+ public const string TITLE_VERSION = "1.1.0";
public const string PRODUCT_TITLE_FORMAT = "Libgen Desktop " + TITLE_VERSION + " ({0}-bit)";
public const string SHORTCUT_TITLE_FORMAT = "Libgen Desktop ({0}-bit)";
public const string PRODUCT_COMPANY = "Libgen Apps";
diff --git a/LibgenDesktop/Common/Constants.cs b/LibgenDesktop/Common/Constants.cs
index f6534eb..fc2d143 100644
--- a/LibgenDesktop/Common/Constants.cs
+++ b/LibgenDesktop/Common/Constants.cs
@@ -4,9 +4,9 @@ namespace LibgenDesktop.Common
{
internal static class Constants
{
- public const string CURRENT_VERSION = "1.0.3";
- public const string CURRENT_GITHUB_RELEASE_NAME = "1.0.3";
- public static readonly DateTime CURRENT_GITHUB_RELEASE_DATE = new DateTime(2018, 4, 7);
+ public const string CURRENT_VERSION = "1.1.0";
+ public const string CURRENT_GITHUB_RELEASE_NAME = "1.1.0";
+ public static readonly DateTime CURRENT_GITHUB_RELEASE_DATE = new DateTime(2018, 5, 2);
public const string CURRENT_DATABASE_VERSION = "1.0";
public const string APP_SETTINGS_FILE_NAME = "libgen.config";
diff --git a/LibgenDesktop/LibgenDesktop.csproj b/LibgenDesktop/LibgenDesktop.csproj
index 988eb27..5765585 100644
--- a/LibgenDesktop/LibgenDesktop.csproj
+++ b/LibgenDesktop/LibgenDesktop.csproj
@@ -226,6 +226,8 @@
+
+
@@ -267,6 +269,7 @@
+
ApplicationUpdateWindow.xaml
@@ -698,6 +701,10 @@
PreserveNewest
Languages\Ukrainian.lng
+
+ PreserveNewest
+ Languages\Spanish.lng
+
diff --git a/LibgenDesktop/Models/Localization/LocalizationStorage.cs b/LibgenDesktop/Models/Localization/LocalizationStorage.cs
index 106f3c9..07d94b0 100644
--- a/LibgenDesktop/Models/Localization/LocalizationStorage.cs
+++ b/LibgenDesktop/Models/Localization/LocalizationStorage.cs
@@ -71,7 +71,7 @@ private void LoadLanguages(string languageDirectoryPath, string selectedLanguage
}
Languages.Add(CreateLanguage(translation, translations, defaultTranslation));
}
- Languages = Languages.OrderBy(language => language.LocalizedName).ToList();
+ Languages = Languages.OrderBy(language => language.Name).ToList();
Language selectedLanguage = Languages.FirstOrDefault(language => language.Name.CompareOrdinalIgnoreCase(selectedLanguageName));
if (selectedLanguage != null)
{
diff --git a/LibgenDesktop/Models/Localization/Localizators/MainWindowLocalizator.cs b/LibgenDesktop/Models/Localization/Localizators/MainWindowLocalizator.cs
index 0aa581f..2638d0f 100644
--- a/LibgenDesktop/Models/Localization/Localizators/MainWindowLocalizator.cs
+++ b/LibgenDesktop/Models/Localization/Localizators/MainWindowLocalizator.cs
@@ -10,6 +10,8 @@ public MainWindowLocalizator(List prioritizedTranslationList, Langu
{
WindowTitle = Format(translation => translation?.WindowTitle);
ToolbarDownloadManagerTooltip = Format(translation => translation?.DownloadManagerTooltip);
+ ToolbarBookmarksTooltip = Format(translation => translation?.BookmarksTooltip);
+ ToolbarNoBookmarks = Format(translation => translation?.NoBookmarks);
ToolbarUpdate = Format(translation => translation?.Update);
ToolbarImport = Format(translation => translation?.Import);
ToolbarSynchronize = Format(translation => translation?.Synchronize);
@@ -20,6 +22,8 @@ public MainWindowLocalizator(List prioritizedTranslationList, Langu
public string WindowTitle { get; }
public string ToolbarDownloadManagerTooltip { get; }
+ public string ToolbarBookmarksTooltip { get; }
+ public string ToolbarNoBookmarks { get; }
public string ToolbarUpdate { get; }
public string ToolbarImport { get; }
public string ToolbarSynchronize { get; }
diff --git a/LibgenDesktop/Models/Localization/Localizators/SearchResultsTabLocalizator.cs b/LibgenDesktop/Models/Localization/Localizators/SearchResultsTabLocalizator.cs
index 7430376..87b7354 100644
--- a/LibgenDesktop/Models/Localization/Localizators/SearchResultsTabLocalizator.cs
+++ b/LibgenDesktop/Models/Localization/Localizators/SearchResultsTabLocalizator.cs
@@ -12,6 +12,8 @@ public SearchResultsTabLocalizator(List prioritizedTranslationList,
SearchInProgress = Format(translation => translation?.SearchInProgress);
Interrupt = Format(translation => translation?.Interrupt);
Interrupting = Format(translation => translation?.Interrupting);
+ AddToBookmarksTooltip = Format(translation => translation?.AddToBookmarksTooltip);
+ RemoveFromBookmarksTooltip = Format(translation => translation?.RemoveFromBookmarksTooltip);
ExportButtonTooltip = Format(translation => translation?.ExportButtonTooltip);
}
@@ -19,6 +21,8 @@ public SearchResultsTabLocalizator(List prioritizedTranslationList,
public string SearchInProgress { get; }
public string Interrupt { get; }
public string Interrupting { get; }
+ public string AddToBookmarksTooltip { get; }
+ public string RemoveFromBookmarksTooltip { get; }
public string ExportButtonTooltip { get; }
private string Format(Func field, object templateArguments = null)
diff --git a/LibgenDesktop/Models/Localization/Translation.cs b/LibgenDesktop/Models/Localization/Translation.cs
index 2ade293..8768a3d 100644
--- a/LibgenDesktop/Models/Localization/Translation.cs
+++ b/LibgenDesktop/Models/Localization/Translation.cs
@@ -31,6 +31,8 @@ internal class FormattingInfo
internal class MainMenuTranslation
{
public string DownloadManagerTooltip { get; set; }
+ public string BookmarksTooltip { get; set; }
+ public string NoBookmarks { get; set; }
public string Update { get; set; }
public string Import { get; set; }
public string Synchronize { get; set; }
@@ -90,6 +92,8 @@ internal class SearchResultsTabsTranslation
public string SearchInProgress { get; set; }
public string Interrupt { get; set; }
public string Interrupting { get; set; }
+ public string AddToBookmarksTooltip { get; set; }
+ public string RemoveFromBookmarksTooltip { get; set; }
public string ExportButtonTooltip { get; set; }
}
diff --git a/LibgenDesktop/Models/MainModel.cs b/LibgenDesktop/Models/MainModel.cs
index d30c24d..ec0986e 100644
--- a/LibgenDesktop/Models/MainModel.cs
+++ b/LibgenDesktop/Models/MainModel.cs
@@ -96,6 +96,7 @@ public MainModel()
public Updater.UpdateCheckResult LastApplicationUpdateCheckResult { get; set; }
public event EventHandler ApplicationUpdateCheckCompleted;
+ public event EventHandler BookmarksChanged;
public Task> SearchNonFictionAsync(string searchQuery, IProgress progressHandler,
CancellationToken cancellationToken)
@@ -548,6 +549,45 @@ public Task GetDatabaseStatsAsync()
return result;
}
+ public bool HasBookmark(LibgenObjectType libgenObjectType, string searchQuery)
+ {
+ if (String.IsNullOrWhiteSpace(searchQuery))
+ {
+ return false;
+ }
+ searchQuery = searchQuery.Trim();
+ return AppSettings.Bookmarks.Bookmarks.Any(bookmark => bookmark.Type == libgenObjectType && bookmark.Query == searchQuery);
+ }
+
+ public void AddBookmark(LibgenObjectType libgenObjectType, string name, string searchQuery)
+ {
+ AppSettings.Bookmarks.Bookmarks.Add(new AppSettings.BookmarkSettings.Bookmark
+ {
+ Name = name,
+ Query = searchQuery,
+ Type = libgenObjectType
+ });
+ SaveSettings();
+ BookmarksChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ public void DeleteBookmark(LibgenObjectType libgenObjectType, string searchQuery)
+ {
+ if (String.IsNullOrWhiteSpace(searchQuery))
+ {
+ return;
+ }
+ searchQuery = searchQuery.Trim();
+ AppSettings.BookmarkSettings.Bookmark bookmark =
+ AppSettings.Bookmarks.Bookmarks.FirstOrDefault(bookmarkItem => bookmarkItem.Type == libgenObjectType && bookmarkItem.Query == searchQuery);
+ if (bookmark != null)
+ {
+ AppSettings.Bookmarks.Bookmarks.Remove(bookmark);
+ SaveSettings();
+ BookmarksChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
public void SaveSettings()
{
SettingsStorage.SaveSettings(AppSettings, Environment.AppSettingsFilePath);
diff --git a/LibgenDesktop/Models/Settings/AppSettings.cs b/LibgenDesktop/Models/Settings/AppSettings.cs
index 0e3c109..c25fa9e 100644
--- a/LibgenDesktop/Models/Settings/AppSettings.cs
+++ b/LibgenDesktop/Models/Settings/AppSettings.cs
@@ -1,5 +1,7 @@
using System;
+using System.Collections.Generic;
using LibgenDesktop.Infrastructure;
+using LibgenDesktop.Models.Entities;
using static LibgenDesktop.Common.Constants;
namespace LibgenDesktop.Models.Settings
@@ -296,6 +298,29 @@ public static LastUpdateSettings Default
public string IgnoreReleaseName { get; set; }
}
+ internal class BookmarkSettings
+ {
+ internal class Bookmark
+ {
+ public string Name { get; set; }
+ public string Query { get; set; }
+ public LibgenObjectType Type { get; set; }
+ }
+
+ public static BookmarkSettings Default
+ {
+ get
+ {
+ return new BookmarkSettings
+ {
+ Bookmarks = new List()
+ };
+ }
+ }
+
+ public List Bookmarks { get; set; }
+ }
+
internal class GeneralSettings
{
internal enum UpdateCheckInterval
@@ -474,6 +499,7 @@ public static AppSettings Default
SciMag = SciMagSettings.Default,
DownloadManagerTab = DownloadManagerTabSettings.Default,
LastUpdate = LastUpdateSettings.Default,
+ Bookmarks = BookmarkSettings.Default,
General = GeneralSettings.Default,
Network = NetworkSettings.Default,
Download = DownloadSettings.Default,
@@ -492,6 +518,7 @@ public static AppSettings Default
public SciMagSettings SciMag { get; set; }
public DownloadManagerTabSettings DownloadManagerTab { get; set; }
public LastUpdateSettings LastUpdate { get; set; }
+ public BookmarkSettings Bookmarks { get; set; }
public GeneralSettings General { get; set; }
public NetworkSettings Network { get; set; }
public DownloadSettings Download { get; set; }
@@ -515,6 +542,7 @@ public static AppSettings ValidateAndCorrect(AppSettings appSettings)
appSettings.ValidateAndCorrectSciMagSettings();
appSettings.ValidateAndCorrectDownloadManagerTabSettings();
appSettings.ValidateAndCorrectLastUpdateSettings();
+ appSettings.ValidateAndCorrectBookmarkSettings();
appSettings.ValidateAndCorrectGeneralSettings();
appSettings.ValidateAndCorrectNetworkSettings();
appSettings.ValidateAndCorrectDownloadSettings();
@@ -804,6 +832,33 @@ private void ValidateAndCorrectLastUpdateSettings()
}
}
+ private void ValidateAndCorrectBookmarkSettings()
+ {
+ if (Bookmarks == null)
+ {
+ Bookmarks = BookmarkSettings.Default;
+ }
+ else
+ {
+ if (Bookmarks.Bookmarks == null)
+ {
+ Bookmarks = BookmarkSettings.Default;
+ }
+ else
+ {
+ for (int bookmarkIndex = Bookmarks.Bookmarks.Count - 1; bookmarkIndex >= 0; bookmarkIndex--)
+ {
+ BookmarkSettings.Bookmark bookmark = Bookmarks.Bookmarks[bookmarkIndex];
+ if (String.IsNullOrEmpty(bookmark.Name) || String.IsNullOrEmpty(bookmark.Query) ||
+ !Enum.IsDefined(typeof(LibgenObjectType), bookmark.Type))
+ {
+ Bookmarks.Bookmarks.RemoveAt(bookmarkIndex);
+ }
+ }
+ }
+ }
+ }
+
private void ValidateAndCorrectGeneralSettings()
{
if (General == null)
diff --git a/LibgenDesktop/Resources/Languages/English.lng b/LibgenDesktop/Resources/Languages/English.lng
index b3db1e5..e1aece7 100644
--- a/LibgenDesktop/Resources/Languages/English.lng
+++ b/LibgenDesktop/Resources/Languages/English.lng
@@ -27,6 +27,8 @@
"MainMenu":
{
"DownloadManagerTooltip": "Download Manager",
+ "BookmarksTooltip": "Bookmarks",
+ "NoBookmarks": "No bookmarks",
"Update": "Update...",
"Import": "Import...",
"Synchronize": "Synchronization...",
@@ -78,6 +80,8 @@
"SearchInProgress": "Search in progress...",
"Interrupt": "INTERRUPT",
"Interrupting": "INTERRUPTING...",
+ "AddToBookmarksTooltip": "Add to bookmarks",
+ "RemoveFromBookmarksTooltip": "Remove from bookmarks",
"ExportButtonTooltip": "Export search results to a file"
},
"NonFictionSearchResultsTab":
diff --git a/LibgenDesktop/Resources/Languages/Russian.lng b/LibgenDesktop/Resources/Languages/Russian.lng
index 4dad14f..cad623d 100644
--- a/LibgenDesktop/Resources/Languages/Russian.lng
+++ b/LibgenDesktop/Resources/Languages/Russian.lng
@@ -27,6 +27,8 @@
"MainMenu":
{
"DownloadManagerTooltip": "Менеджер загрузок",
+ "BookmarksTooltip": "Закладки",
+ "NoBookmarks": "Нет закладок",
"Update": "Обновление...",
"Import": "Импорт...",
"Synchronize": "Синхронизация...",
@@ -78,6 +80,8 @@
"SearchInProgress": "Идет поиск...",
"Interrupt": "ПРЕРВАТЬ",
"Interrupting": "ПРЕРЫВАЕТСЯ...",
+ "AddToBookmarksTooltip": "Добавить в закладки",
+ "RemoveFromBookmarksTooltip": "Удалить из закладок",
"ExportButtonTooltip": "Экспорт результатов поиска в файл"
},
"NonFictionSearchResultsTab":
diff --git a/LibgenDesktop/Resources/Languages/Spanish.lng b/LibgenDesktop/Resources/Languages/Spanish.lng
new file mode 100644
index 0000000..b022409
--- /dev/null
+++ b/LibgenDesktop/Resources/Languages/Spanish.lng
@@ -0,0 +1,705 @@
+{
+ "General":
+ {
+ "Name": "Spanish",
+ "LocalizedName": "ES Español",
+ "CultureCode": "es-ES",
+ "TranslatorName": "mikasa"
+ },
+ "Formatting":
+ {
+ "DecimalSeparator": ".",
+ "ThousandsSeparator": ",",
+ "DateFormat": "dd/MM/yyyy",
+ "TimeFormat": "hh:mm:ss tt",
+ "FileSizePostfixes":
+ {
+ "Byte": "bytes",
+ "Kilobyte": "KB",
+ "Megabyte": "MB",
+ "Gigabyte": "GB",
+ "Terabyte": "TB"
+ }
+ },
+ "MainWindow":
+ {
+ "WindowTitle": "Libgen Desktop",
+ "MainMenu":
+ {
+ "DownloadManagerTooltip": "Administrador de descargas",
+ "Update": "Actualizar...",
+ "Import": "Importar...",
+ "Synchronize": "Sincronización...",
+ "Database": "Base de datos...",
+ "Settings": "Ajustes",
+ "About": "Acerca de"
+ }
+ },
+ "CreateDatabaseWindow":
+ {
+ "WindowTitle": "Libgen Desktop",
+ "FirstRunMessage": "Bienvenido a Libgen Desktop.",
+ "DatabaseNotFound": "Base de datos {database} no encontrada.",
+ "DatabaseCorrupted": "La base de datos {database} está corrupta.",
+ "ChooseOption": "Escoja una opción",
+ "CreateNewDatabase": "Crear una base de datos",
+ "OpenExistingDatabase": "Abrir una base de datos existente",
+ "BrowseNewDatabaseDialogTitle": "Localización de la nueva base de datos",
+ "BrowseExistingDatabaseDialogTitle": "Localización de la base de datos existente",
+ "Databases": "Bases de datos",
+ "AllFiles": "Todos los archivos",
+ "Error": "Error",
+ "CannotCreateDatabase": "No se puede crear la base de datos.",
+ "Ok": "OK",
+ "Cancel": "CANCELAR"
+ },
+ "SearchTab":
+ {
+ "TabTitle": "Buscar",
+ "SearchPlaceHolder": "Buscar",
+ "NonFictionSelector": "Libros de no ficción",
+ "FictionSelector": "Libros de ficción",
+ "SciMagSelector": "Artículos científicos",
+ "NonFictionSearchBoxTooltip": "Buscar por título, autores, series, editorial e ISBN sin guiones",
+ "FictionSearchBoxTooltip": "Buscar por título, autores, series, editorial e ISBN con guiones",
+ "SciMagSearchBoxTooltip": "Buscar por título, autores, nombre de la revista, DOI, ID Pubmed e ISSN (p/e)",
+ "SearchInProgress": "Realizando la búsqueda...",
+ "NonFictionSearchProgress": "Libros encontrados: {count}",
+ "FictionSearchProgress": "Libros encontrados: {count}",
+ "SciMagSearchProgress": "Artículos encontrados: {count}",
+ "Interrupt": "INTERRUMPIR",
+ "Interrupting": "INTERRUMPIENDO...",
+ "DatabaseIsEmpty": "La base de datos está vacía. Descarga un volcado de la base de datos desde el sitio web de Library Genesis e impórtalo aquí.",
+ "ImportButton": "Importar"
+ },
+ "SearchResultsTabs":
+ {
+ "SearchPlaceHolder": "Buscar",
+ "SearchInProgress": "Realizando la búsqueda...",
+ "Interrupt": "INTERRUMPIR",
+ "Interrupting": "INTERRUMPIENDO...",
+ "ExportButtonTooltip": "Exportar la búsqueda a un archivo"
+ },
+ "NonFictionSearchResultsTab":
+ {
+ "SearchBoxTooltip": "Buscar por título, autores, series, editorial e ISBN sin guiones",
+ "SearchProgress": "Libros encontrados: {count}",
+ "StatusBar": "Libros encontrados: {count}",
+ "Columns":
+ {
+ "Title": "Título",
+ "Authors": "Autores",
+ "Series": "Series",
+ "Year": "Año",
+ "Publisher": "Editorial",
+ "Format": "Formato",
+ "FileSize": "Tamaño",
+ "Ocr": "OCR"
+ }
+ },
+ "FictionSearchResultsTab":
+ {
+ "SearchBoxTooltip": "Buscar por título, autores, series, editorial e ISBN con guiones",
+ "SearchProgress": "Libros encontrados: {count}",
+ "StatusBar": "Libros encontrados: {count}",
+ "Columns":
+ {
+ "Title": "Título",
+ "Authors": "Autores",
+ "Series": "Series",
+ "Year": "Año",
+ "Publisher": "Editorial",
+ "Format": "Formato",
+ "FileSize": "Tamaño"
+ }
+ },
+ "SciMagSearchResultsTab":
+ {
+ "SearchBoxTooltip": "Buscar por título, autores, nombre de la revista, DOI, ID Pubmed e ISSN (p/e)",
+ "SearchProgress": "Artículos encontrados: {count}",
+ "StatusBar": "Artículos encontrados: {count}",
+ "Columns":
+ {
+ "Title": "Título",
+ "Authors": "Autores",
+ "Magazine": "Revista",
+ "Year": "Año",
+ "FileSize": "Tamaño",
+ "Doi": "DOI"
+ }
+ },
+ "DetailsTabs":
+ {
+ "CoverIsLoading": "Cargando la cubierta...",
+ "NoCover": "No hay disponible una cubierta",
+ "NoCoverMirror": "No se ha seleccionado un espejo{new-line}para cargar la cubierta",
+ "NoCoverDueToOfflineMode": "El modo sin conexión está activo.{new-line}La cubierta no se cargará.",
+ "CoverLoadingError": "No se puede cargar la cubierta",
+ "Yes": "sí",
+ "No": "no",
+ "Unknown": "desconocido",
+ "Portrait": "retrato",
+ "Landscape": "paisaje",
+ "CopyContextMenu": "Copiar \"{text}\"",
+ "Download": "DESCARGAR",
+ "DownloadFromMirror": "DESCARGAR DESDE {mirror}",
+ "Queued": "EN COLA",
+ "Downloading": "DESCARGANDO",
+ "Stopped": "DETENIDO",
+ "Error": "ERROR DE DESCARGA",
+ "Open": "ABRIR ARCHIVO",
+ "ErrorMessageTitle": "Error",
+ "FileNotFoundError": "Archivo {file} no encontrado.",
+ "NoDownloadMirrorTooltip": "No se ha seleccionado un espejo",
+ "OfflineModeIsOnTooltip": "El modo sin conexión está activo",
+ "Close": "CERRAR"
+ },
+ "NonFictionDetailsTab":
+ {
+ "Title": "Título",
+ "Authors": "Autores",
+ "Series": "Series",
+ "Publisher": "Editorial",
+ "Year": "Año",
+ "Language": "Idioma",
+ "Format": "Formato",
+ "Isbn": "ISBN",
+ "Added": "Añadido",
+ "LastModified": "Última modificación",
+ "Library": "Biblioteca",
+ "FileSize": "Tamaño",
+ "Topics": "Temas",
+ "Volume": "Volumen",
+ "Magazine": "Revista",
+ "City": "Ciudad",
+ "Edition": "Edición",
+ "Pages": "Páginas",
+ "BodyMatterPages": "asunto del cuerpo",
+ "TotalPages": "total",
+ "Tags": "Etiquetas",
+ "Md5Hash": "MD5 hash",
+ "Comments": "Comentarios",
+ "Identifiers": "Identificadores",
+ "LibgenId": "ID Libgen",
+ "Issn": "ISSN",
+ "Udc": "UDC",
+ "Lbc": "LBC",
+ "Lcc": "LCC",
+ "Ddc": "DDC",
+ "Doi": "DOI",
+ "OpenLibraryId": "ID Open Library",
+ "GoogleBookId": "ID Google Books",
+ "Asin": "ASIN",
+ "AdditionalAttributes": "Atributos adicionales",
+ "Dpi": "DPI",
+ "Ocr": "OCR",
+ "TableOfContents": "Tabla de contenidos",
+ "Scanned": "Escaneado",
+ "Orientation": "Orientación",
+ "Paginated": "Paginado",
+ "Colored": "En color",
+ "Cleaned": "Limpio"
+ },
+ "FictionDetailsTab":
+ {
+ "Title": "Título",
+ "Authors": "Autores",
+ "RussianAuthor": "Autor en ruso",
+ "Series": "Series",
+ "Publisher": "Editorial",
+ "Edition": "Edición",
+ "Year": "Año",
+ "Language": "Idioma",
+ "Format": "Formato",
+ "Pages": "Páginas",
+ "Version": "Versión",
+ "FileSize": "Tamaño",
+ "Added": "Añadido",
+ "LastModified": "Última modificación",
+ "Md5Hash": "MD5 hash",
+ "Comments": "Comentarios",
+ "Identifiers": "Identificadores",
+ "LibgenId": "ID Libgen",
+ "Isbn": "ISBN",
+ "GoogleBookId": "ID Google Books",
+ "Asin": "ASIN"
+ },
+ "SciMagDetailsTab":
+ {
+ "Title": "Título",
+ "Authors": "Autores",
+ "Magazine": "Revista",
+ "Year": "Año",
+ "Month": "Mes",
+ "Day": "Día",
+ "Volume": "Volumen",
+ "Issue": "Número",
+ "Pages": "Páginas",
+ "FileSize": "Tamaño",
+ "AddedDateTime": "Añadido",
+ "Md5Hash": "MD5 hash",
+ "AbstractUrl": "URL abstracta",
+ "Identifiers": "Identificadores",
+ "LibgenId": "ID Libgen",
+ "Doi": "DOI",
+ "Isbn": "ISBN",
+ "MagazineId": "ID de Revista",
+ "Issnp": "ISSN (p)",
+ "Issne": "ISSN (e)",
+ "PubmedId": "ID Pubmed",
+ "Pmc": "PMC",
+ "Pii": "PII",
+ "AdditionalAttributes": "Atributos adicionales",
+ "Attribute1": "Atributo 1",
+ "Attribute2": "Atributo 2",
+ "Attribute3": "Atributo 3",
+ "Attribute4": "Atributo 4",
+ "Attribute5": "Atributo 5",
+ "Attribute6": "Atributo 6"
+ },
+ "Import":
+ {
+ "WindowTitle": "Importación desde un volcado SQL",
+ "BrowseImportFileDialogTitle": "Localización del archivo de volcado SQL",
+ "AllSupportedFiles": "Todos los archivos soportados",
+ "SqlDumps": "Volcados SQL",
+ "Archives": "Archivos",
+ "AllFiles": "Todos los archivos",
+ "Elapsed": "Tiempo transcurrido: {elapsed}",
+ "Interrupt": "INTERRUMPIR",
+ "Interrupting": "INTERRUMPIENDO...",
+ "Close": "CERRAR",
+ "StatusMessages":
+ {
+ "Step": "Paso {current} de {total}.",
+ "DataLookup": "Definición de la tabla de búsqueda",
+ "CreatingIndexes": "Creación de los índices",
+ "LoadingIds": "Cargando los identificadores",
+ "ImportingData": "Importación de los datos",
+ "ImportComplete": "Importación completa",
+ "ImportCancelled": "La importación se canceló",
+ "DataNotFound": "Datos no encontrados",
+ "ImportError": "La importación falló"
+ },
+ "LogMessages":
+ {
+ "Step": "Paso {step}",
+ "DataLookup": "Buscando la tabla de definiciones",
+ "Scanning": "Escaneando...",
+ "ScannedProgress": "{percent}% del archivo ha sido escaneado...",
+ "NonFictionTableFound": "Tabla de libros de no ficción encontrada.",
+ "FictionTableFound": "Tabla de libros de ficción encontrada.",
+ "SciMagTableFound": "Tabla de artículos científicos encontrada.",
+ "CreatingIndexes": "Creando los índices perdidos",
+ "CreatingIndexForColumn": "Creando un índice para la columna {column}...",
+ "LoadingIds": "Cargando los identificadores existentes",
+ "LoadingColumnValues": "Cargando los datos de la columna {column}...",
+ "ImportingData": "Importación de datos",
+ "ImportBooksProgressNoUpdate": "Libros añadidos: {added}.",
+ "ImportBooksProgressWithUpdate": "Libros añadidos: {added}, actualizados: {updated}.",
+ "ImportArticlesProgressNoUpdate": "Artículos añadidos: {added}.",
+ "ImportArticlesProgressWithUpdate": "Artículos añadidos: {added}, actualizados: {updated}.",
+ "ImportSuccessful": "La importación se completó satisfactoriamente.",
+ "ImportCancelled": "La importación fue cancelada por el usuario.",
+ "DataNotFound": "No se pudo encontrar ningún dato para importar.",
+ "ImportError": "Ocuurió un error durante la importación."
+ }
+ },
+ "ExportPanel":
+ {
+ "Header": "Exportar los resultados de la búsqueda a un archivo",
+ "Format": "Formato del archivo",
+ "Excel": "Microsoft Excel 2007-2016 (XLSX)",
+ "Csv": "Valores separados por coma (CSV)",
+ "Separator": "Separador",
+ "Comma": "Coma",
+ "Semicolon": "Punto y coma",
+ "Tab": "Tabulador",
+ "SaveAs": "Guarda como",
+ "Browse": "Examinar...",
+ "BrowseDialogTitle": "Exportar los resultados de la búsqueda",
+ "ExcelFiles": "Archivos de Microsoft Excel",
+ "CsvFiles": "Archivos de CSV",
+ "TsvFiles": "Archivos de TSV",
+ "AllFiles": "Todos los archivos",
+ "ExportRange": "Exportar rango",
+ "NoLimit": "todo",
+ "Limit": "solamente los primeros {count} resultados",
+ "Export": "EXPORTAR",
+ "Cancel": "CANCELAR",
+ "SavingFile": "Guardando el archivo.",
+ "RowCountSingleFile": "Filas exportadas: {rows}.",
+ "RowCountMultipleFiles": "Filas exportadas: {rows}, archivos creados: {files}.",
+ "ErrorWarningTitle": "Error",
+ "InvalidExportPath": "Invalid export file path.",
+ "DirectoryNotFound": "Directory {directory} doesn't exist.",
+ "InvalidExportFileName": "Invalid export file name.",
+ "OverwritePromptTitle": "Overwrite the file?",
+ "OverwritePromptText": "File {file} already exists. Do you want to overwrite it?",
+ "RowLimitWarningTitle": "Row limit",
+ "RowLimitWarningText": "Microsoft Excel maximum row count has been reached. Export cannot be continued. In order to export more rows please enable \"Split into multiple files\" option in the application settings.",
+ "ExportError": "An error occurred during the export.",
+ "Interrupt": "INTERRUPT",
+ "Interrupting": "INTERRUPTING...",
+ "ExportInterrupted": "Export has been interrupted.",
+ "Results": "OPEN RESULTS",
+ "Close": "CLOSE"
+ },
+ "Exporter":
+ {
+ "Yes": "yes",
+ "No": "no",
+ "Unknown": "unknown",
+ "Portrait": "portrait",
+ "Landscape": "landscape",
+ "NonFictionColumns":
+ {
+ "Id": "ID",
+ "Title": "Título",
+ "Authors": "Autores",
+ "Series": "Series",
+ "Publisher": "Editorial",
+ "Year": "Año",
+ "Language": "Idioma",
+ "Format": "Formato",
+ "Isbn": "ISBN",
+ "Added": "Añadido",
+ "LastModified": "Última modificación",
+ "Library": "Library",
+ "FileSize": "Tamaño",
+ "Topics": "Topics",
+ "Volume": "Volumen",
+ "Magazine": "Magazine",
+ "City": "City",
+ "Edition": "Edición",
+ "BodyMatterPages": "Pages (Body Matter)",
+ "TotalPages": "Pages (Total)",
+ "Tags": "Tags",
+ "Md5Hash": "MD5 hash",
+ "Comments": "Comentarios",
+ "LibgenId": "Libgen ID",
+ "Issn": "ISSN",
+ "Udc": "UDC",
+ "Lbc": "LBC",
+ "Lcc": "LCC",
+ "Ddc": "DDC",
+ "Doi": "DOI",
+ "OpenLibraryId": "Open Library ID",
+ "GoogleBookId": "Google Books ID",
+ "Asin": "ASIN",
+ "Dpi": "DPI",
+ "Ocr": "OCR",
+ "TableOfContents": "Table Of Contents",
+ "Scanned": "Scanned",
+ "Orientation": "Orientation",
+ "Paginated": "Paginated",
+ "Colored": "Colored",
+ "Cleaned": "Cleaned"
+ },
+ "FictionColumns":
+ {
+ "Id": "ID",
+ "Title": "Título",
+ "Authors": "Autores",
+ "RussianAuthor": "Author in Russian",
+ "Series": "Series",
+ "Publisher": "Editorial",
+ "Edition": "Edición",
+ "Year": "Año",
+ "Language": "Idioma",
+ "Format": "Formato",
+ "Pages": "Páginas",
+ "Version": "Versión",
+ "FileSize": "Tamaño",
+ "Added": "Añadido",
+ "LastModified": "Última modificación",
+ "Md5Hash": "MD5 hash",
+ "Comments": "Comentarios",
+ "LibgenId": "Libgen ID",
+ "Isbn": "ISBN",
+ "GoogleBookId": "Google Books ID",
+ "Asin": "ASIN"
+ },
+ "SciMagColumns":
+ {
+ "Id": "ID",
+ "Title": "Título",
+ "Authors": "Autores",
+ "Magazine": "Revista",
+ "Year": "Año",
+ "Month": "Mes",
+ "Day": "Día",
+ "Volume": "Volumen",
+ "Issue": "Número",
+ "Pages": "Páginas",
+ "FileSize": "Tamaño",
+ "AddedDateTime": "Añadido",
+ "Md5Hash": "MD5 hash",
+ "AbstractUrl": "URL abstracta",
+ "LibgenId": "ID Libgen",
+ "Doi1": "DOI 1",
+ "Doi2": "DOI 2",
+ "Isbn": "ISBN",
+ "MagazineId": "ID de Revista",
+ "Issnp": "ISSN (p)",
+ "Issne": "ISSN (e)",
+ "PubmedId": "ID Pubmed",
+ "Pmc": "PMC",
+ "Pii": "PII",
+ "Attribute1": "Atributo 1",
+ "Attribute2": "Atributo 2",
+ "Attribute3": "Atributo 3",
+ "Attribute4": "Atributo 4",
+ "Attribute5": "Atributo 5",
+ "Attribute6": "Atributo 6"
+ }
+ },
+ "Synchronization":
+ {
+ "WindowTitle": "Sincronización de la lista de libros de no ficción",
+ "ErrorMessageTitle": "Error",
+ "ImportRequired": "Se necesita completar un volcado de la base de datos de libros de no ficción para iniciar la sincronización.",
+ "NoSynchronizationMirror": "No se ha seleccionado un espejo de sincronización para los libros de no ficción.",
+ "OfflineModePromptTitle": "Modo sin conexión",
+ "OfflineModePromptText": "La sincronización no se puede iniciar en el modo sin conexión activo. ¿Desea desactivar dicho modo?",
+ "Elapsed": "Tiempo transcurrido: {elapsed}",
+ "Interrupt": "INTERRUMPIR",
+ "Interrupting": "INTERRUMPIENDO...",
+ "Close": "CERRAR",
+ "StatusMessages":
+ {
+ "Step": "Paso {current} de {total}.",
+ "Preparation": "Preparando para la sincronización",
+ "CreatingIndexes": "Creando los índices",
+ "LoadingIds": "Caragndo los identificadores",
+ "SynchronizingData": "Sincronización de los datos",
+ "SynchronizationComplete": "Sincronización completada",
+ "SynchronizationCancelled": "La sincronización ha sido cancelada",
+ "SynchronizationError": "La sincronización falló"
+ },
+ "LogMessages":
+ {
+ "Step": "Paso {step}",
+ "CreatingIndexes": "Creando los índices perdidos",
+ "CreatingIndexForColumn": "Creando el índice para la columna {column}...",
+ "LoadingIds": "Cargando los datos de los identificadores existentes",
+ "LoadingColumnValues": "Cargando los datos para la columna {column}...",
+ "SynchronizingBookList": "Sincronizando la lista de libros",
+ "DownloadingNewBooks": "Descargando nuevos medatados de libros",
+ "SynchronizationProgressNoAddedNoUpdated": "Libros descargados: {downloaded}.",
+ "SynchronizationProgressAdded": "Libros descargados: {downloaded}, añadidos: {added}.",
+ "SynchronizationProgressUpdated": "Libros descargados: {downloaded}, actualizados: {updated}.",
+ "SynchronizationProgressAddedAndUpdated": "Libros descargados: {downloaded}, añadidos: {added}, actualizados: {updated}.",
+ "SynchronizationSuccessful": "Sincronización completada satisfactoriamente.",
+ "SynchronizationCancelled": "La sincronización ha sido cancelada por el usuario.",
+ "SynchronizationError": "Ocurrió un error duante la sincronización."
+ }
+ },
+ "Database":
+ {
+ "WindowTitle": "Información de la base de datos",
+ "NonFiction": "Libros de no ficción",
+ "Fiction": "Libros de ficción",
+ "SciMagArticles": "Artículos científicos",
+ "TotalBooks": "Total de libros",
+ "TotalArticles": "Total de artículos",
+ "LastUpdate": "Última actualización",
+ "Never": "nunca",
+ "CreatingIndexes": "Por favor, espere. Creando los índices perdidos de la base de datos...",
+ "ChangeDatabase": "CAMBIAR LA BASE DE DATOS...",
+ "BrowseDatabaseDialogTitle": "Localización de la base de datos",
+ "Databases": "Bases de datos",
+ "AllFiles": "Todos los archivos",
+ "Error": "Error",
+ "CannotOpenDatabase": "Ocurrió un error mientras se abría la base de datos: {file}",
+ "Close": "CERRAR"
+ },
+ "DownloadManager":
+ {
+ "TabTitle": "Descargas",
+ "Start": "INICIAR",
+ "Stop": "DETENER",
+ "Remove": "QUITAR",
+ "StartAll": "INICIAR TODO",
+ "StopAll": "DETENER TODO",
+ "RemoveCompleted": "QUITAR TODO",
+ "QueuedStatus": "En cola",
+ "DownloadingStatus": "Descargando",
+ "StoppedStatus": "Detenido",
+ "RetryDelayStatus": "Retenido para reintentar",
+ "ErrorStatus": "Error",
+ "DownloadProgressKnownFileSize": "Descargados {downloaded} de {total} bytes ({percent}%)",
+ "DownloadProgressUnknownFileSize": "Descargados {downloaded} bytes del tamaño desconocido",
+ "Log": "Traza",
+ "TechnicalDetails": "Detalles técnicos",
+ "Copy": "Copiar al portapaeles",
+ "FileNotFoundErrorTitle": "Error",
+ "FileNotFoundErrorText": "Archivo {file} no encontrado.",
+ "LogMessages":
+ {
+ "Queued": "Agregado a la cola de descarga.",
+ "Started": "Iniciado.",
+ "Stopped": "Detenido.",
+ "RetryDelay": "Reintento en {count} segundos...",
+ "Completed": "Descarga completa.",
+ "OfflineModeIsOn": "El modo sin conexión está activo.",
+ "TransformationError": "La transformación {transformation} falló.",
+ "TransformationReturnedIncorrectUrl": "La transformación {transformation} devolvió una URL no válida.",
+ "Attempt": "Reintento {current} de {total}.",
+ "MaximumDownloadAttempts": "Se alcanzó el número máximo de reintentos.",
+ "DownloadingPage": "Descargando página: {url}",
+ "DownloadingFile": "Descargando archivo: {url}",
+ "StartingFileDownloadKnownFileSize": "Descarga de archivo iniciada, tamaño: {size} bytes.",
+ "StartingFileDownloadUnknownFileSize": "Descarga de archivo iniciada, tamaño desconocido.",
+ "ResumingFileDownloadKnownFileSize": "Descarga de archivo retomada, tamaño restante: {remaining} bytes.",
+ "ResumingFileDownloadUnknownFileSize": "Descarga de archivo retomada, tamaño restante: desconocido.",
+ "Request": "Solicitud",
+ "Response": "Respuesta del servidor",
+ "Redirect": "Redirección a {url}",
+ "TooManyRedirects": "Se han recibido del servidor muchas respuestas de redirección.",
+ "NonSuccessfulStatusCode": "El servidor devolvió: {status}.",
+ "CannotCreateDownloadDirectory": "No se puede crear la carpeta {directory}",
+ "CannotCreateOrOpenFile": "No se puede crear o abrir el archivo {file}",
+ "CannotRenamePartFile": "No se puede renombrar el archivo {source} a {destination}.",
+ "HtmlPageReturned": "El servidor devolvió una página HTML en vez de un archivo.",
+ "NoPartialDownloadSupport": "El servidor no soporta descargas parciales.",
+ "NoContentLengthWarning": "Atención: el servidor no devolvió el tamaño del archivo.",
+ "ServerResponseTimeout": "El servidor respondió con un tiempo de espera agotado.",
+ "DownloadIncompleteError": "El servidor indica que la descarga está completa, pero no es así. Reintente la descarga",
+ "FileWriteError": "Error de escritura del archivo.",
+ "UnexpectedError": "Ocurrió un error inesperado."
+ }
+ },
+ "ApplicationUpdate":
+ {
+ "WindowTitle": "Actualización de la aplicación",
+ "UpdateAvailable": "Está disponible una nueva versión de Libgen Desktop",
+ "NewVersion": "Nueva versión: {version} liberada el {date}",
+ "Download": "DESCARGAR",
+ "DownloadAndInstall": "DESCARGAR E INSTALAR",
+ "SkipThisVersion": "SALTAR ESTA VERSION",
+ "Cancel": "CANCELAR",
+ "Interrupt": "INTERRUMPIR",
+ "Interrupting": "INTERRUMPIENDO...",
+ "InterruptPromptTitle": "¿Cancelar la descargar?",
+ "InterruptPromptText": "¿Quieres cancelar la descarga de la actualización?",
+ "Error": "Error",
+ "IncompleteDownload": "La descarga de la actualización no está completa.",
+ "Close": "CERRAR"
+ },
+ "Settings":
+ {
+ "WindowTitle": "Ajustes",
+ "Ok": "OK",
+ "Cancel": "CANCELAR",
+ "DiscardChangesPromptTitle": "¿Descartar los cambios?",
+ "DiscardChangesPromptText": "Los ajustes se modificaron. ¿Estás seguro que deseas descartar los cambios?",
+ "General":
+ {
+ "TabHeader": "General",
+ "Language": "Idioma",
+ "CheckUpdates": "Buscar actualizaciones de la aplicación",
+ "UpdateCheckIntervals":
+ {
+ "Never": "nunca",
+ "Daily": "diariamente",
+ "Weekly": "semanalmente",
+ "Monthly": "mensualmente"
+ }
+ },
+ "Network":
+ {
+ "TabHeader": "Red",
+ "OfflineMode": "Modo sin conexión",
+ "UseHttpProxy": "Usar un proxy HTTP",
+ "ProxyAddress": "Dirección",
+ "ProxyAddressRequired": "Campo requerido",
+ "ProxyPort": "Puerto",
+ "ProxyPortValidation": "Números desde {min} hasta {max}",
+ "ProxyUserName": "Nombre de usuario",
+ "ProxyPassword": "Contraseña",
+ "ProxyPasswordWarning": "No es seguro almacenar la contraseña aquí."
+ },
+ "Download":
+ {
+ "TabHeader": "Descargas",
+ "DownloadMode": "Modo de descarga",
+ "OpenInBrowser": "abrir la URL de descarga en el navegador",
+ "UseDownloadManager": "usar al administrador de descargas interno",
+ "DownloadDirectory": "Descargar en",
+ "BrowseDirectoryDialogTitle": "Carpeta de descarga",
+ "DownloadDirectoryNotFound": "La carpeta no existe",
+ "Timeout": "Tiempo de espera de la descarga",
+ "TimeoutValidation": "Números: {min} — {max}",
+ "Seconds": "segundos",
+ "DownloadAttempts": "Reintentos de la descarga",
+ "DownloadAttemptsValidation": "Números: {min} — {max}",
+ "Times": "veces",
+ "RetryDelay": "Reintentar la descarga en",
+ "RetryDelayValidation": "Números: {min} — {max}"
+ },
+ "Mirrors":
+ {
+ "TabHeader": "Espejos",
+ "NonFiction": "Libros de no ficción",
+ "Fiction": "Libros de ficción",
+ "SciMagArticles": "Artículos científicos",
+ "Books": "Libros",
+ "Articles": "Artículos",
+ "Covers": "Cubiertas",
+ "Synchronization": "Sincronización",
+ "NoMirror": "sin espejo"
+ },
+ "Search":
+ {
+ "TabHeader": "Buscar",
+ "LimitResults": "Limitar los resultados de la búsqueda",
+ "MaximumResults": "Cantidad máxima de resultados",
+ "PositiveNumbersOnly": "Sólo números positivos",
+ "OpenDetails": "Abrir los detalles del libro / artículo:",
+ "InModalWindow": "en una ventana fija",
+ "InNonModalWindow": "en una ventana ajustable",
+ "InNewTab": "en una nueva pestaña"
+ },
+ "Export":
+ {
+ "TabHeader": "Exportar",
+ "OpenResults": "Abrir los resultados luego de la exportación",
+ "SplitIntoMultipleFiles": "Dividir en varios archivos",
+ "MaximumRowsPerFile": "Número máximo de filas por archivo",
+ "MaximumRowsPerFileValidation": "Números desde {min} hasta {max}",
+ "ExcelLimitNote": "Nota: Microsoft Excel no soporta archivos con más de {count} filas."
+ },
+ "Advanced":
+ {
+ "TabHeader": "Avanzados",
+ "UseLogging": "Escribe la información de diagnóstico en un archivo"
+ }
+ },
+ "About":
+ {
+ "WindowTitle": "Acerca de Libgen Desktop",
+ "ApplicationName": "Libgen Desktop",
+ "Version": "Version actual: {version} liberada el {date}",
+ "CheckForUpdates": "BUSCAR ACTUALIZACIONES",
+ "CheckingUpdates": "BUSCANDO ACTUALIZACIONES...",
+ "OfflineModeIsOnTooltip": "El modo sin conexión está activo",
+ "LatestVersion": "Estás usando la última versión.",
+ "NewVersionAvailable": "Está disponible una nueva versión: {version} liberada el {date}",
+ "Update": "ACTUALIZAR",
+ "Translators": "Traductores:"
+ },
+ "MessageBox":
+ {
+ "Ok": "OK",
+ "Yes": "SI",
+ "No": "NO"
+ },
+ "ErrorWindow":
+ {
+ "WindowTitle": "Error",
+ "UnexpectedError": "Ocurrió un error inesperado",
+ "Copy": "COPIAR AL PORTAPAPELES",
+ "Close": "CERRAR"
+ }
+}
diff --git a/LibgenDesktop/Resources/Mirrors/libgen_io_nonfiction.xslt b/LibgenDesktop/Resources/Mirrors/libgen_io_nonfiction.xslt
index 487cc91..3d2eb6d 100644
--- a/LibgenDesktop/Resources/Mirrors/libgen_io_nonfiction.xslt
+++ b/LibgenDesktop/Resources/Mirrors/libgen_io_nonfiction.xslt
@@ -12,6 +12,11 @@
+
+
+
+
+
diff --git a/LibgenDesktop/ViewModels/Bookmarks/BookmarkViewModel.cs b/LibgenDesktop/ViewModels/Bookmarks/BookmarkViewModel.cs
new file mode 100644
index 0000000..43149e3
--- /dev/null
+++ b/LibgenDesktop/ViewModels/Bookmarks/BookmarkViewModel.cs
@@ -0,0 +1,12 @@
+using LibgenDesktop.Models.Entities;
+
+namespace LibgenDesktop.ViewModels.Bookmarks
+{
+ internal class BookmarkViewModel
+ {
+ public string Name { get; set; }
+ public string SearchQuery { get; set; }
+ public LibgenObjectType LibgenObjectType { get; set; }
+ public bool IsEnabled { get; set; }
+ }
+}
diff --git a/LibgenDesktop/ViewModels/Bookmarks/BookmarkViewModelList.cs b/LibgenDesktop/ViewModels/Bookmarks/BookmarkViewModelList.cs
new file mode 100644
index 0000000..ce2d3b2
--- /dev/null
+++ b/LibgenDesktop/ViewModels/Bookmarks/BookmarkViewModelList.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using LibgenDesktop.Models;
+using static LibgenDesktop.Models.Settings.AppSettings;
+
+namespace LibgenDesktop.ViewModels.Bookmarks
+{
+ internal class BookmarkViewModelList : ObservableCollection
+ {
+ public BookmarkViewModelList(MainModel mainModel)
+ {
+ List bookmarks = mainModel.AppSettings.Bookmarks.Bookmarks;
+ if (bookmarks.Any())
+ {
+ foreach (BookmarkSettings.Bookmark bookmark in bookmarks)
+ {
+ Add(new BookmarkViewModel
+ {
+ Name = bookmark.Name,
+ SearchQuery = bookmark.Query,
+ LibgenObjectType = bookmark.Type,
+ IsEnabled = true
+ });
+ }
+ }
+ else
+ {
+ Add(new BookmarkViewModel
+ {
+ Name = mainModel.Localization.CurrentLanguage.MainWindow.ToolbarNoBookmarks,
+ IsEnabled = false
+ });
+ }
+ }
+ }
+}
diff --git a/LibgenDesktop/ViewModels/Tabs/SearchResultsTabViewModel.cs b/LibgenDesktop/ViewModels/Tabs/SearchResultsTabViewModel.cs
index e1beaec..70aacc4 100644
--- a/LibgenDesktop/ViewModels/Tabs/SearchResultsTabViewModel.cs
+++ b/LibgenDesktop/ViewModels/Tabs/SearchResultsTabViewModel.cs
@@ -13,6 +13,9 @@ internal abstract class SearchResultsTabViewModel : TabViewModel
{
private CancellationTokenSource searchCancellationTokenSource;
private string searchQuery;
+ private LibgenObjectType libgenObjectType;
+ private string lastExecutedSearchQuery;
+ private bool isBookmarkSet;
private bool isExportPanelVisible;
private bool isSearchProgressPanelVisible;
private string interruptButtonText;
@@ -21,12 +24,16 @@ internal abstract class SearchResultsTabViewModel : TabViewModel
protected SearchResultsTabViewModel(MainModel mainModel, IWindowContext parentWindowContext, LibgenObjectType libgenObjectType, string searchQuery)
: base(mainModel, parentWindowContext, searchQuery)
{
+ this.libgenObjectType = libgenObjectType;
this.searchQuery = searchQuery;
+ lastExecutedSearchQuery = searchQuery;
+ UpdateBookmarkedState();
isExportPanelVisible = false;
isSearchProgressPanelVisible = false;
ExportPanelViewModel = new ExportPanelViewModel(mainModel, libgenObjectType, parentWindowContext);
SearchCommand = new Command(Search);
InterruptSearchCommand = new Command(InterruptSearch);
+ ToggleBookmarkCommand = new Command(ToggleBookmark);
}
public string SearchQuery
@@ -46,6 +53,19 @@ public string SearchQuery
}
}
+ public bool IsBookmarkSet
+ {
+ get
+ {
+ return isBookmarkSet;
+ }
+ set
+ {
+ isBookmarkSet = value;
+ NotifyPropertyChanged();
+ }
+ }
+
public bool IsExportPanelVisible
{
get
@@ -102,6 +122,7 @@ public bool IsInterruptButtonEnabled
public Command SearchCommand { get; }
public Command InterruptSearchCommand { get; }
+ public Command ToggleBookmarkCommand { get; }
private SearchResultsTabLocalizator Localization
{
@@ -111,6 +132,12 @@ private SearchResultsTabLocalizator Localization
}
}
+ public void Search(string searchQuery)
+ {
+ SearchQuery = searchQuery;
+ Search();
+ }
+
public abstract void ShowExportPanel();
protected abstract SearchResultsTabLocalizator GetLocalization();
protected abstract Task SearchAsync(string searchQuery, CancellationToken cancellationToken);
@@ -120,6 +147,8 @@ private async void Search()
if (!String.IsNullOrWhiteSpace(SearchQuery) && !IsSearchProgressPanelVisible && !IsExportPanelVisible)
{
Title = SearchQuery;
+ lastExecutedSearchQuery = SearchQuery;
+ UpdateBookmarkedState();
InterruptButtonText = Localization.Interrupt;
IsInterruptButtonEnabled = true;
IsSearchProgressPanelVisible = true;
@@ -136,5 +165,23 @@ private void InterruptSearch()
InterruptButtonText = Localization.Interrupting;
IsInterruptButtonEnabled = false;
}
+
+ private void UpdateBookmarkedState()
+ {
+ IsBookmarkSet = MainModel.HasBookmark(libgenObjectType, lastExecutedSearchQuery);
+ }
+
+ private void ToggleBookmark()
+ {
+ if (isBookmarkSet)
+ {
+ MainModel.DeleteBookmark(libgenObjectType, lastExecutedSearchQuery);
+ }
+ else
+ {
+ MainModel.AddBookmark(libgenObjectType, lastExecutedSearchQuery, lastExecutedSearchQuery);
+ }
+ UpdateBookmarkedState();
+ }
}
}
diff --git a/LibgenDesktop/ViewModels/Windows/MainWindowViewModel.cs b/LibgenDesktop/ViewModels/Windows/MainWindowViewModel.cs
index 913ef9e..b475d6a 100644
--- a/LibgenDesktop/ViewModels/Windows/MainWindowViewModel.cs
+++ b/LibgenDesktop/ViewModels/Windows/MainWindowViewModel.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
@@ -10,6 +11,7 @@
using LibgenDesktop.Models.Localization.Localizators;
using LibgenDesktop.Models.Settings;
using LibgenDesktop.Models.Update;
+using LibgenDesktop.ViewModels.Bookmarks;
using LibgenDesktop.ViewModels.EventArguments;
using LibgenDesktop.ViewModels.Tabs;
using static LibgenDesktop.Models.Settings.AppSettings;
@@ -25,6 +27,7 @@ internal class MainWindowViewModel : LibgenWindowViewModel
private bool isCompletedDownloadCounterVisible;
private int completedDownloadCount;
private bool isApplicationUpdateAvailable;
+ private BookmarkViewModelList bookmarks;
public MainWindowViewModel(MainModel mainModel)
: base(mainModel)
@@ -40,12 +43,14 @@ public MainWindowViewModel(MainModel mainModel)
ImportCommand = new Command(Import);
SynchronizeCommand = new Command(Synchronize);
DatabaseCommand = new Command(DatabaseMenuItemClick);
+ BookmarkCommand = new Command(param => BookmarksMenuItemClick(param as BookmarkViewModel));
SettingsCommand = new Command(SettingsMenuItemClick);
AboutCommand = new Command(AboutMenuItemClick);
WindowClosedCommand = new Command(WindowClosed);
TabViewModels = new ObservableCollection();
Initialize();
mainModel.ApplicationUpdateCheckCompleted += ApplicationUpdateCheckCompleted;
+ mainModel.BookmarksChanged += BookmarksChanged;
mainModel.Localization.LanguageChanged += LocalizationLanguageChanged;
mainModel.Downloader.DownloaderEvent += DownloaderEvent;
}
@@ -173,6 +178,19 @@ public bool IsApplicationUpdateAvailable
}
}
+ public BookmarkViewModelList Bookmarks
+ {
+ get
+ {
+ return bookmarks;
+ }
+ set
+ {
+ bookmarks = value;
+ NotifyPropertyChanged();
+ }
+ }
+
public Command NewTabCommand { get; }
public Command CloseTabCommand { get; }
public Command CloseCurrentTabCommand { get; }
@@ -182,6 +200,7 @@ public bool IsApplicationUpdateAvailable
public Command ImportCommand { get; }
public Command SynchronizeCommand { get; }
public Command DatabaseCommand { get; }
+ public Command BookmarkCommand { get; }
public Command SettingsCommand { get; }
public Command AboutCommand { get; }
public Command WindowClosedCommand { get; }
@@ -213,6 +232,7 @@ private void Initialize()
isCompletedDownloadCounterVisible = false;
completedDownloadCount = 0;
isApplicationUpdateAvailable = false;
+ bookmarks = new BookmarkViewModelList(MainModel);
}
private void DefaultSearchTabViewModel_ImportRequested(object sender, EventArgs e)
@@ -618,6 +638,68 @@ private void DatabaseMenuItemClick()
databaseWindowContext.ShowDialog();
}
+ private void BookmarksMenuItemClick(BookmarkViewModel bookmarkViewModel)
+ {
+ bool useCurrentTab;
+ if (SelectedTabViewModel != null)
+ {
+ switch (bookmarkViewModel.LibgenObjectType)
+ {
+ case LibgenObjectType.NON_FICTION_BOOK:
+ useCurrentTab = SelectedTabViewModel is NonFictionSearchResultsTabViewModel;
+ break;
+ case LibgenObjectType.FICTION_BOOK:
+ useCurrentTab = SelectedTabViewModel is FictionSearchResultsTabViewModel;
+ break;
+ case LibgenObjectType.SCIMAG_ARTICLE:
+ useCurrentTab = SelectedTabViewModel is SciMagSearchResultsTabViewModel;
+ break;
+ default:
+ useCurrentTab = false;
+ break;
+ }
+ }
+ else
+ {
+ useCurrentTab = false;
+ }
+ if (useCurrentTab)
+ {
+ (SelectedTabViewModel as SearchResultsTabViewModel).Search(bookmarkViewModel.SearchQuery);
+ }
+ else
+ {
+ SearchResultsTabViewModel newTab;
+ switch (bookmarkViewModel.LibgenObjectType)
+ {
+ case LibgenObjectType.NON_FICTION_BOOK:
+ newTab = new NonFictionSearchResultsTabViewModel(MainModel, CurrentWindowContext, bookmarkViewModel.SearchQuery,
+ new List());
+ break;
+ case LibgenObjectType.FICTION_BOOK:
+ newTab = new FictionSearchResultsTabViewModel(MainModel, CurrentWindowContext, bookmarkViewModel.SearchQuery,
+ new List());
+ break;
+ case LibgenObjectType.SCIMAG_ARTICLE:
+ newTab = new SciMagSearchResultsTabViewModel(MainModel, CurrentWindowContext, bookmarkViewModel.SearchQuery,
+ new List());
+ break;
+ default:
+ newTab = null;
+ break;
+ }
+ if (newTab != null)
+ {
+ newTab.Search(bookmarkViewModel.SearchQuery);
+ TabViewModels.Add(newTab);
+ SelectedTabViewModel = newTab;
+ NotifyPropertyChanged(nameof(IsDefaultSearchTabVisible));
+ NotifyPropertyChanged(nameof(AreTabsVisible));
+ NotifyPropertyChanged(nameof(IsNewTabButtonVisible));
+ }
+ }
+ }
+
private void SettingsMenuItemClick()
{
SettingsWindowViewModel settingsWindowViewModel = new SettingsWindowViewModel(MainModel);
@@ -642,6 +724,11 @@ private void ApplicationUpdateCheckCompleted(object sender, EventArgs e)
IsApplicationUpdateAvailable = MainModel.LastApplicationUpdateCheckResult != null;
}
+ private void BookmarksChanged(object sender, EventArgs e)
+ {
+ Bookmarks = new BookmarkViewModelList(MainModel);
+ }
+
private void LocalizationLanguageChanged(object sender, EventArgs e)
{
Localization = MainModel.Localization.CurrentLanguage.MainWindow;
diff --git a/LibgenDesktop/Views/Controls/HorizontalScrollViewer.cs b/LibgenDesktop/Views/Controls/HorizontalScrollViewer.cs
new file mode 100644
index 0000000..b316164
--- /dev/null
+++ b/LibgenDesktop/Views/Controls/HorizontalScrollViewer.cs
@@ -0,0 +1,31 @@
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace LibgenDesktop.Views.Controls
+{
+ internal class HorizontalScrollViewer : ScrollViewer
+ {
+ public HorizontalScrollViewer()
+ {
+ HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
+ VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
+ }
+
+ protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
+ {
+ if (e.Delta > 0)
+ {
+ LineLeft();
+ LineLeft();
+ LineLeft();
+ }
+ else
+ {
+ LineRight();
+ LineRight();
+ LineRight();
+ }
+ e.Handled = true;
+ }
+ }
+}
diff --git a/LibgenDesktop/Views/Controls/Toolbar.xaml b/LibgenDesktop/Views/Controls/Toolbar.xaml
index 9806d87..96ead16 100644
--- a/LibgenDesktop/Views/Controls/Toolbar.xaml
+++ b/LibgenDesktop/Views/Controls/Toolbar.xaml
@@ -34,6 +34,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
+
+
-
+