diff --git a/lib/I18n/translations/belarusian.yaml b/lib/I18n/translations/belarusian.yaml index 8ce3e024..3ba890b8 100644 --- a/lib/I18n/translations/belarusian.yaml +++ b/lib/I18n/translations/belarusian.yaml @@ -176,6 +176,9 @@ STR_EXIT: "« Выхад" STR_HOME: "« Галоўная" STR_SELECT: "Абраць" STR_TOGGLE: "Выбар" +STR_TOGGLE_BOOKMARK: "Пераключыць закладку" +STR_BOOKMARK_REMOVED: "Закладка выдалена." +STR_HOLD_OPEN_TO_DELETE: "Утрымлівайце Адкрыць, каб выдаліць" STR_CONFIRM: "Пацв." STR_CANCEL: "Адмена" STR_CONNECT: "Падкл." diff --git a/lib/I18n/translations/catalan.yaml b/lib/I18n/translations/catalan.yaml index a3c527ef..7f3680cb 100644 --- a/lib/I18n/translations/catalan.yaml +++ b/lib/I18n/translations/catalan.yaml @@ -175,8 +175,7 @@ STR_DOWNLOADING: "S'està baixant..." STR_DOWNLOAD_FAILED: "Ha fallat la baixada" STR_ERROR_MSG: "Error:" STR_UNNAMED: "Sense nom" -STR_HOLD_CONFIRM_TO_DELETE: "Manteniu premut Confirma per esborrar" -STR_BOOKMARK_INSTRUCTIONS: "Manteniu premut Confirma al lector per crear un punt de llibre." +STR_HOLD_OPEN_TO_DELETE: "Manteniu premut Obre per esborrar" STR_NO_SERVER_URL: "No s'ha configurat cap URL de servidor" STR_FETCH_FEED_FAILED: "Ha fallat l'obtenció del feed" STR_PARSE_FEED_FAILED: "Ha fallat l'anàlisi del feed" @@ -192,6 +191,7 @@ STR_HOME: "« Inici" STR_SELECT: "Selecciona" STR_SELECTED: "Seleccionat" STR_TOGGLE: "Canvia" +STR_TOGGLE_BOOKMARK: "Commuta punt de llibre" STR_CONFIRM: "Confirma" STR_CANCEL: "Cancel·la" STR_CONNECT: "Connecta" @@ -236,6 +236,7 @@ STR_QUICK_RESUME_TIMEOUT: "Represa ràpida després del temps" STR_REMAP_FRONT_BUTTONS: "Reassigna els botons frontals" STR_BOOKMARKS: "Punts de llibre" STR_BOOKMARK_ADDED: "S'ha afegit el punt de llibre." +STR_BOOKMARK_REMOVED: "S'ha eliminat el punt de llibre." STR_OPDS_BROWSER: "Navegador OPDS" STR_COVER_CUSTOM: "Portada + Personalitzat" STR_QUICK_RESUME: "Represa ràpida" diff --git a/lib/I18n/translations/czech.yaml b/lib/I18n/translations/czech.yaml index f3836652..c46b5ab9 100644 --- a/lib/I18n/translations/czech.yaml +++ b/lib/I18n/translations/czech.yaml @@ -181,6 +181,9 @@ STR_EXIT: "« Konec" STR_HOME: "« Domů" STR_SELECT: "Vybrat" STR_TOGGLE: "Přepnout" +STR_TOGGLE_BOOKMARK: "Přepnout záložku" +STR_BOOKMARK_REMOVED: "Záložka odstraněna." +STR_HOLD_OPEN_TO_DELETE: "Podržte Otevřít pro smazání" STR_CONFIRM: "Potvrdit" STR_CANCEL: "Zrušit" STR_CONNECT: "Připojit" diff --git a/lib/I18n/translations/danish.yaml b/lib/I18n/translations/danish.yaml index 2d05aeb9..e6f96b45 100644 --- a/lib/I18n/translations/danish.yaml +++ b/lib/I18n/translations/danish.yaml @@ -187,6 +187,9 @@ STR_HOME: "« Hjem" STR_SELECT: "Vælg" STR_SELECTED: "Valgt" STR_TOGGLE: "Skift" +STR_TOGGLE_BOOKMARK: "Skift bogmærke" +STR_BOOKMARK_REMOVED: "Bogmærke fjernet." +STR_HOLD_OPEN_TO_DELETE: "Hold Åbn nede for at slette" STR_CONFIRM: "Bekræft" STR_CANCEL: "Annuller" STR_CONNECT: "Forbind" diff --git a/lib/I18n/translations/dutch.yaml b/lib/I18n/translations/dutch.yaml index 4f445be7..d9461477 100644 --- a/lib/I18n/translations/dutch.yaml +++ b/lib/I18n/translations/dutch.yaml @@ -187,6 +187,9 @@ STR_HOME: "« Home" STR_SELECT: "Kies" STR_SELECTED: "Geselecteerd" STR_TOGGLE: "Wissel" +STR_TOGGLE_BOOKMARK: "Bladwijzer wisselen" +STR_BOOKMARK_REMOVED: "Bladwijzer verwijderd." +STR_HOLD_OPEN_TO_DELETE: "Houd Openen ingedrukt om te verwijderen" STR_CONFIRM: "Bevestig" STR_CANCEL: "Annuleer" STR_CONNECT: "Verbind" diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index 0b530755..96ebbdd3 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -179,8 +179,7 @@ STR_DOWNLOADING: "Downloading..." STR_DOWNLOAD_FAILED: "Download failed" STR_ERROR_MSG: "Error:" STR_UNNAMED: "Unnamed" -STR_HOLD_CONFIRM_TO_DELETE: "Hold Confirm to Delete" -STR_BOOKMARK_INSTRUCTIONS: "Hold Confirm from the reader to create a bookmark." +STR_HOLD_OPEN_TO_DELETE: "Hold Open to Delete" STR_NO_SERVER_URL: "No server URL configured" STR_FETCH_FEED_FAILED: "Failed to fetch feed" STR_PARSE_FEED_FAILED: "Failed to parse feed" @@ -198,6 +197,7 @@ STR_HOME: "« Home" STR_SELECT: "Select" STR_SELECTED: "Selected" STR_TOGGLE: "Toggle" +STR_TOGGLE_BOOKMARK: "Toggle Bookmark" STR_CONFIRM: "Confirm" STR_CANCEL: "Cancel" STR_CONNECT: "Connect" @@ -260,6 +260,7 @@ STR_SUNLIGHT_FADING_FIX: "Sunlight Fading Fix" STR_REMAP_FRONT_BUTTONS: "Remap Front Buttons" STR_BOOKMARKS: "Bookmarks" STR_BOOKMARK_ADDED: "Bookmark added." +STR_BOOKMARK_REMOVED: "Bookmark removed." STR_OPDS_BROWSER: "OPDS Browser" STR_SEARCH: "Search" STR_COVER_CUSTOM: "Cover + Custom" diff --git a/lib/I18n/translations/finnish.yaml b/lib/I18n/translations/finnish.yaml index 7401587d..3ccaa39c 100644 --- a/lib/I18n/translations/finnish.yaml +++ b/lib/I18n/translations/finnish.yaml @@ -181,6 +181,9 @@ STR_EXIT: "« Poistu" STR_HOME: "« Koti" STR_SELECT: "Valitse" STR_TOGGLE: "Vaihda" +STR_TOGGLE_BOOKMARK: "Vaihda kirjanmerkki" +STR_BOOKMARK_REMOVED: "Kirjanmerkki poistettu." +STR_HOLD_OPEN_TO_DELETE: "Pidä Avaa painettuna poistaaksesi" STR_CONFIRM: "Vahvista" STR_CANCEL: "Peruuta" STR_CONNECT: "Yhdistä" diff --git a/lib/I18n/translations/french.yaml b/lib/I18n/translations/french.yaml index 867e5714..9d9e32cd 100644 --- a/lib/I18n/translations/french.yaml +++ b/lib/I18n/translations/french.yaml @@ -187,6 +187,9 @@ STR_HOME: "« Accueil" STR_SELECT: "OK" STR_SELECTED: "Sélectionné" STR_TOGGLE: "Modifier" +STR_TOGGLE_BOOKMARK: "Basculer le marque-page" +STR_BOOKMARK_REMOVED: "Marque-page supprimé." +STR_HOLD_OPEN_TO_DELETE: "Maintenir Ouvrir pour supprimer" STR_CONFIRM: "Confirmer" STR_CANCEL: "Annuler" STR_CONNECT: "Connecter" diff --git a/lib/I18n/translations/german.yaml b/lib/I18n/translations/german.yaml index a8e680e0..a0f57fdd 100644 --- a/lib/I18n/translations/german.yaml +++ b/lib/I18n/translations/german.yaml @@ -168,8 +168,7 @@ STR_DOWNLOADING: "Herunterladen…" STR_DOWNLOAD_FAILED: "Ladefehler" STR_ERROR_MSG: "Fehler:" STR_UNNAMED: "Unbenannt" -STR_HOLD_CONFIRM_TO_DELETE: "Halte Bestätigen zum Löschen" -STR_BOOKMARK_INSTRUCTIONS: "Halte Bestätigen im Lesemodus um ein Lesezeichen anzulegen." +STR_HOLD_OPEN_TO_DELETE: "Halte Öffnen zum Löschen" STR_NO_SERVER_URL: "Keine Server-URL konfiguriert" STR_FETCH_FEED_FAILED: "Feedfehler" STR_PARSE_FEED_FAILED: "Feed-Format ungültig" @@ -187,6 +186,7 @@ STR_HOME: "« Start" STR_SELECT: "Auswahl" STR_SELECTED: "Ausgewählt" STR_TOGGLE: "Ändern" +STR_TOGGLE_BOOKMARK: "Lesezeichen umschalten" STR_CONFIRM: "Bestätigen" STR_CANCEL: "Abbrechen" STR_CONNECT: "Verbinden" @@ -250,6 +250,7 @@ STR_QUICK_RESUME_TIMEOUT: "Schnelles Fortsetzen nach Timeout" STR_REMAP_FRONT_BUTTONS: "Vordere Tasten belegen" STR_BOOKMARKS: "Lesezeichen" STR_BOOKMARK_ADDED: "Lesezeichen hinzugefügt." +STR_BOOKMARK_REMOVED: "Lesezeichen entfernt." STR_SEARCH: "Suche" STR_OPDS_BROWSER: "OPDS-Browser" STR_COVER_CUSTOM: "Cover + Eigenes" diff --git a/lib/I18n/translations/hebrew.yaml b/lib/I18n/translations/hebrew.yaml index f519e0b8..210ca2ad 100644 --- a/lib/I18n/translations/hebrew.yaml +++ b/lib/I18n/translations/hebrew.yaml @@ -189,6 +189,7 @@ STR_HOME: "מסך הבית »" STR_SELECT: "בחר" STR_SELECTED: "נבחר" STR_TOGGLE: "בחר" +STR_TOGGLE_BOOKMARK: "הוסף/הסר סימנייה" STR_CONFIRM: "אישור" STR_CANCEL: "ביטול" STR_CONNECT: "התחבר" @@ -360,8 +361,7 @@ STR_FRONT_BTN_FOLLOW_ORIENTATION: "התאמת לחצנים קדמיים לכיו STR_REMOVE_READ_FROM_RECENTS: "הסר ספרים שנקראו מרשימת האחרונים" STR_MOVE_FINISHED_TO_READ: "העבר ספרים שהסתיימו לתיקיית 'נקראו'" STR_DISABLED: "מבוטל" -STR_HOLD_CONFIRM_TO_DELETE: "החזק לחוץ על אישור כדי למחוק" -STR_BOOKMARK_INSTRUCTIONS: "החזק לחוץ על כפתור אישור בזמן הקריאה כדי להוסיף סימנייה" +STR_HOLD_OPEN_TO_DELETE: "לחיצה ארוכה על 'פתח' כדי למחוק" STR_CLOCK: "שעון" STR_CLOCK_UTC_OFFSET: "הפרש זמן UTC" STR_CLOCK_FORMAT: "תבנית השעון" @@ -379,6 +379,7 @@ STR_CLOCK_SYNC_NO_WIFI_HINT: "התחבר תחילה לרשת אלחוטית, ו STR_CLOCK_SYNCED: "השעון סונכרן" STR_BOOKMARKS: "סימניות" STR_BOOKMARK_ADDED: "הסימנייה התווספה" +STR_BOOKMARK_REMOVED: "הסימנייה הוסרה" STR_QUICK_RESUME: "חזרה מהירה" STR_REMOVE_FROM_RECENTS: "להסיר מרשימת הספרים האחרונים?" STR_CONFIRM_DELETE_BOOKMARK: "למחוק סימנייה זו?" diff --git a/lib/I18n/translations/hungarian.yaml b/lib/I18n/translations/hungarian.yaml index 033c7bf2..14a5358e 100644 --- a/lib/I18n/translations/hungarian.yaml +++ b/lib/I18n/translations/hungarian.yaml @@ -184,6 +184,9 @@ STR_HOME: "« Főoldal" STR_SELECT: "Kiválasztás" STR_SELECTED: "Kiválasztva" STR_TOGGLE: "Váltás" +STR_TOGGLE_BOOKMARK: "Könyvjelző váltása" +STR_BOOKMARK_REMOVED: "Könyvjelző eltávolítva." +STR_HOLD_OPEN_TO_DELETE: "Tartsa lenyomva a Megnyitás gombot a törléshez" STR_CONFIRM: "Megerősítés" STR_CANCEL: "Mégse" STR_CONNECT: "Csatlakozás" diff --git a/lib/I18n/translations/italian.yaml b/lib/I18n/translations/italian.yaml index acd7cc50..9ef433b6 100644 --- a/lib/I18n/translations/italian.yaml +++ b/lib/I18n/translations/italian.yaml @@ -192,6 +192,7 @@ STR_HOME: "« Home" STR_SELECT: "Seleziona" STR_SELECTED: "Selezionato" STR_TOGGLE: "Cambia" +STR_TOGGLE_BOOKMARK: "Attiva/disattiva segnalibro" STR_CONFIRM: "Conferma" STR_CANCEL: "Annulla" STR_CONNECT: "Connetti" @@ -357,9 +358,9 @@ STR_FIRMWARE_WRITE_FAILED: "Aggiornamento firmware non riuscito" STR_FIRMWARE_UPDATE_DO_NOT_POWER_OFF: "Non spegnere il dispositivo!" STR_RECOVERY_MODE: "Modalità ripristino" STR_RECOVERY_MODE_HINT: "Metti firmware.bin nella scheda SD e selezionalo" -STR_BOOKMARK_INSTRUCTIONS: "Tieni premuto Conferma nel lettore per creare un segnalibro" STR_BOOKMARKS: "Segnalibri" STR_BOOKMARK_ADDED: "Segnalibro aggiunto" +STR_BOOKMARK_REMOVED: "Segnalibro rimosso" STR_CONFIRM_DELETE_BOOKMARK: "Eliminare questo segnalibro?" STR_CLOCK_SYNC_NO_WIFI_HINT: "Connettiti prima al Wi-Fi e poi riprova" STR_CLOCK_SYNC_OK: "Orologio sincronizzato" @@ -374,10 +375,10 @@ STR_CLOCK_SYNCING: "Sincronizzazione con il server NTP..." STR_CLOCK_SYNC_FAIL: "Sincronizzazione non riuscita" STR_CLOCK_SYNC_NOW: "Sincronizza l'orologio adesso" STR_CLOCK_SYNC_NO_WIFI: "Wi-Fi non connesso" -STR_HOLD_CONFIRM_TO_DELETE: "Tieni premuto Conferma per cancellare" +STR_HOLD_OPEN_TO_DELETE: "Tieni premuto Apri per eliminare" STR_NEXT_FIELD: "Succ." STR_CURRENT_TIME: "Ora attuale: " STR_DISABLED: "Disattivato" STR_BOOKMARK_OPTION: "Segnalibro" STR_KOSYNC: "KOSync" -STR_PWR_BTN_FOOTNOTE_BACK: "Rientro rapido dalle note" \ No newline at end of file +STR_PWR_BTN_FOOTNOTE_BACK: "Rientro rapido dalle note" diff --git a/lib/I18n/translations/kazakh.yaml b/lib/I18n/translations/kazakh.yaml index 6508bf77..dfccc77b 100644 --- a/lib/I18n/translations/kazakh.yaml +++ b/lib/I18n/translations/kazakh.yaml @@ -177,6 +177,9 @@ STR_EXIT: "« Шығу" STR_HOME: "« Басты" STR_SELECT: "Таңдау" STR_TOGGLE: "Ауыстыру" +STR_TOGGLE_BOOKMARK: "Бетбелгіні ауыстыру" +STR_BOOKMARK_REMOVED: "Бетбелгі жойылды." +STR_HOLD_OPEN_TO_DELETE: "Жою үшін Ашу түймесін ұстап тұрыңыз" STR_CONFIRM: "Растау" STR_CANCEL: "Болдырмау" STR_CONNECT: "Қосылу" diff --git a/lib/I18n/translations/lithuanian.yaml b/lib/I18n/translations/lithuanian.yaml index 42bcd64e..d3c43ed3 100644 --- a/lib/I18n/translations/lithuanian.yaml +++ b/lib/I18n/translations/lithuanian.yaml @@ -184,6 +184,9 @@ STR_HOME: "« Pradžia" STR_SELECT: "Rinktis" STR_SELECTED: "Pasirinkta" STR_TOGGLE: "Keisti" +STR_TOGGLE_BOOKMARK: "Perjungti žymę" +STR_BOOKMARK_REMOVED: "Žymė pašalinta." +STR_HOLD_OPEN_TO_DELETE: "Laikykite Atidaryti, kad ištrintumėte" STR_CONFIRM: "Gerai" STR_CANCEL: "Atšaukti" STR_CONNECT: "Jungtis" diff --git a/lib/I18n/translations/polish.yaml b/lib/I18n/translations/polish.yaml index 0a384260..ba46e715 100644 --- a/lib/I18n/translations/polish.yaml +++ b/lib/I18n/translations/polish.yaml @@ -191,6 +191,9 @@ STR_HOME: "« Home" STR_SELECT: "Wybierz" STR_SELECTED: "Wybrano" STR_TOGGLE: "Zmień" +STR_TOGGLE_BOOKMARK: "Przełącz zakładkę" +STR_BOOKMARK_REMOVED: "Zakładka usunięta." +STR_HOLD_OPEN_TO_DELETE: "Przytrzymaj Otwórz, aby usunąć" STR_CONFIRM: "Potwierdź" STR_CANCEL: "Anuluj" STR_CONNECT: "Połącz" diff --git a/lib/I18n/translations/portuguese.yaml b/lib/I18n/translations/portuguese.yaml index 2f5511d0..516651ab 100644 --- a/lib/I18n/translations/portuguese.yaml +++ b/lib/I18n/translations/portuguese.yaml @@ -181,6 +181,9 @@ STR_EXIT: "« Sair" STR_HOME: "« Início" STR_SELECT: "Escolher" STR_TOGGLE: "Alternar" +STR_TOGGLE_BOOKMARK: "Alternar marcador" +STR_BOOKMARK_REMOVED: "Marcador removido." +STR_HOLD_OPEN_TO_DELETE: "Mantenha Abrir pressionado para excluir" STR_CONFIRM: "Confirmar" STR_CANCEL: "Cancelar" STR_CONNECT: "Conectar" diff --git a/lib/I18n/translations/romanian.yaml b/lib/I18n/translations/romanian.yaml index e62c5f76..67052d10 100644 --- a/lib/I18n/translations/romanian.yaml +++ b/lib/I18n/translations/romanian.yaml @@ -187,6 +187,9 @@ STR_HOME: "« Acasă" STR_SELECT: "Selectează" STR_SELECTED: "Selectat" STR_TOGGLE: "Schimbă" +STR_TOGGLE_BOOKMARK: "Comută marcajul" +STR_BOOKMARK_REMOVED: "Marcaj eliminat." +STR_HOLD_OPEN_TO_DELETE: "Țineți apăsat Deschideți pentru a șterge" STR_CONFIRM: "Confirmă" STR_CANCEL: "Anulare" STR_CONNECT: "Conectare" diff --git a/lib/I18n/translations/russian.yaml b/lib/I18n/translations/russian.yaml index ae7e9f67..ff1187d4 100644 --- a/lib/I18n/translations/russian.yaml +++ b/lib/I18n/translations/russian.yaml @@ -180,8 +180,7 @@ STR_DOWNLOADING: "Загрузка..." STR_DOWNLOAD_FAILED: "Ошибка загрузки" STR_ERROR_MSG: "Ошибка:" STR_UNNAMED: "Без имени" -STR_HOLD_CONFIRM_TO_DELETE: "Удерживайте ОТКРЫТЬ для удаления закладки" -STR_BOOKMARK_INSTRUCTIONS: "Удерживайте ВЫБРАТЬ для добавления закладки" +STR_HOLD_OPEN_TO_DELETE: "Удерживайте Открыть для удаления" STR_NO_SERVER_URL: "URL сервера не настроен" STR_FETCH_FEED_FAILED: "Не удалось получить ленту" STR_PARSE_FEED_FAILED: "Не удалось обработать ленту" @@ -199,6 +198,7 @@ STR_HOME: "« Главная" STR_SELECT: "Выбрать" STR_SELECTED: "Выбран" STR_TOGGLE: "Выбор" +STR_TOGGLE_BOOKMARK: "Переключить закладку" STR_CONFIRM: "Подтв." STR_CANCEL: "Отмена" STR_CONNECT: "Подкл." @@ -261,6 +261,7 @@ STR_SUNLIGHT_FADING_FIX: "Компенсация выцветания" STR_REMAP_FRONT_BUTTONS: "Переназначить передние кнопки" STR_BOOKMARKS: "Закладки" STR_BOOKMARK_ADDED: "Закладка добавлена" +STR_BOOKMARK_REMOVED: "Закладка удалена" STR_OPDS_BROWSER: "OPDS браузер" STR_SEARCH: "Поиск" STR_COVER_CUSTOM: "Обложка + Свой" diff --git a/lib/I18n/translations/slovak.yaml b/lib/I18n/translations/slovak.yaml index ad329a6a..b3210422 100644 --- a/lib/I18n/translations/slovak.yaml +++ b/lib/I18n/translations/slovak.yaml @@ -176,8 +176,7 @@ STR_DOWNLOADING: "Sťahovanie..." STR_DOWNLOAD_FAILED: "Sťahovanie zlyhalo" STR_ERROR_MSG: "Chyba:" STR_UNNAMED: "Nepomenované" -STR_HOLD_CONFIRM_TO_DELETE: "Podrž potvrdiť pre vymazanie" -STR_BOOKMARK_INSTRUCTIONS: "Podrž tlačidlo Potvrdiť pre vytvorenie záložky." +STR_HOLD_OPEN_TO_DELETE: "Podržte Otvoriť pre vymazanie" STR_NO_SERVER_URL: "Nie je nakonfigurovaná URL adresa servera" STR_FETCH_FEED_FAILED: "Načítanie kanála zlyhalo" STR_PARSE_FEED_FAILED: "Spracovanie kanála zlyhalo" @@ -194,7 +193,8 @@ STR_EXIT: "« Koniec" STR_HOME: "« Domov" STR_SELECT: "Vybrať" STR_SELECTED: "Vybrané" -STR_TOGGLE: "Prepnúť" +STR_TOGGLE: "Prepnúť" +STR_TOGGLE_BOOKMARK: "Prepnúť záložku" STR_CONFIRM: "Potvrdiť" STR_CANCEL: "Zrušiť" STR_CONNECT: "Pripojiť" @@ -255,8 +255,9 @@ STR_THEME_ROUNDEDRAFF: "RoundedRaff" STR_THEME_LYRA_EXTENDED: "Lyra Extended" STR_SUNLIGHT_FADING_FIX: "Oprava blednutia na slnku" STR_REMAP_FRONT_BUTTONS: "Premapovať predné tlačidlá" -STR_BOOKMARKS: "Záložky" -STR_BOOKMARK_ADDED: "Záložka pridaná." +STR_BOOKMARKS: "Záložky" +STR_BOOKMARK_ADDED: "Záložka pridaná." +STR_BOOKMARK_REMOVED: "Záložka odstránená." STR_OPDS_BROWSER: "Prehliadač OPDS" STR_SEARCH: "Hľadať" STR_COVER_CUSTOM: "Obálka + Vlastné" diff --git a/lib/I18n/translations/slovenian.yaml b/lib/I18n/translations/slovenian.yaml index 109240e3..c01ebc14 100644 --- a/lib/I18n/translations/slovenian.yaml +++ b/lib/I18n/translations/slovenian.yaml @@ -184,6 +184,9 @@ STR_HOME: "« Domov" STR_SELECT: "Izberi" STR_SELECTED: "Izbrano" STR_TOGGLE: "Preklopi" +STR_TOGGLE_BOOKMARK: "Preklopi zaznamek" +STR_BOOKMARK_REMOVED: "Zaznamek odstranjen." +STR_HOLD_OPEN_TO_DELETE: "Držite Odpri za brisanje" STR_CONFIRM: "Potrdi" STR_CANCEL: "Prekliči" STR_CONNECT: "Poveži" diff --git a/lib/I18n/translations/spanish.yaml b/lib/I18n/translations/spanish.yaml index b56bdf6a..8156f891 100644 --- a/lib/I18n/translations/spanish.yaml +++ b/lib/I18n/translations/spanish.yaml @@ -178,8 +178,7 @@ STR_DOWNLOADING: "Descargando..." STR_DOWNLOAD_FAILED: "Fallo de descarga" STR_ERROR_MSG: "Error:" STR_UNNAMED: "Sin nombre" -STR_HOLD_CONFIRM_TO_DELETE: "Mantenga pulsado Confirmar para borrar" -STR_BOOKMARK_INSTRUCTIONS: "Mantenga pulsado Confirmar en el lector para crear un marcador." +STR_HOLD_OPEN_TO_DELETE: "Mantenga pulsado Abrir para borrar" STR_NO_SERVER_URL: "No se configuró URL de servidor" STR_FETCH_FEED_FAILED: "Fallo al obtener el feed" STR_PARSE_FEED_FAILED: "Fallo al procesar el feed" @@ -197,6 +196,7 @@ STR_HOME: "« Inicio" STR_SELECT: "Selecc." STR_SELECTED: "Seleccionado" STR_TOGGLE: "Cambiar" +STR_TOGGLE_BOOKMARK: "Alternar marcador" STR_CONFIRM: "Confirmar" STR_CANCEL: "Cancelar" STR_CONNECT: "Conectar" @@ -260,6 +260,7 @@ STR_QUICK_RESUME_TIMEOUT: "Reanudación rápida tras tiempo" STR_REMAP_FRONT_BUTTONS: "Reconfigurar botones frontales" STR_BOOKMARKS: "Marcadores" STR_BOOKMARK_ADDED: "Marcador añadido." +STR_BOOKMARK_REMOVED: "Marcador eliminado." STR_OPDS_BROWSER: "Navegador OPDS" STR_SEARCH: "Buscar" STR_COVER_CUSTOM: "Portada + Pers." diff --git a/lib/I18n/translations/swedish.yaml b/lib/I18n/translations/swedish.yaml index a0750943..ad588f19 100644 --- a/lib/I18n/translations/swedish.yaml +++ b/lib/I18n/translations/swedish.yaml @@ -176,8 +176,7 @@ STR_DOWNLOADING: "Laddar ner…" STR_DOWNLOAD_FAILED: "Nedladdning misslyckades" STR_ERROR_MSG: "Fel:" STR_UNNAMED: "Ej namngiven" -STR_HOLD_CONFIRM_TO_DELETE: "Håll ned Bekräfta för att radera" -STR_BOOKMARK_INSTRUCTIONS: "Håll Bekräfta i läsaren för att skapa ett bokmärke." +STR_HOLD_OPEN_TO_DELETE: "Håll ned Öppna för att radera" STR_NO_SERVER_URL: "Ingen serveradress konfigurerad" STR_FETCH_FEED_FAILED: "Misslyckades att hämta flöde" STR_PARSE_FEED_FAILED: "Misslyckades att analysera flöde" @@ -195,6 +194,7 @@ STR_HOME: "« Hem" STR_SELECT: "Välj " STR_SELECTED: "Vald" STR_TOGGLE: "Växla" +STR_TOGGLE_BOOKMARK: "Växla bokmärke" STR_CONFIRM: "Bekräfta" STR_CANCEL: "Avbryt" STR_CONNECT: "Anslut" @@ -258,6 +258,7 @@ STR_QUICK_RESUME_TIMEOUT: "Snabb återupptagning efter timeout" STR_REMAP_FRONT_BUTTONS: "Ändra frontknappar" STR_BOOKMARKS: "Bokmärken" STR_BOOKMARK_ADDED: "Bokmärke tillagt." +STR_BOOKMARK_REMOVED: "Bokmärke borttaget." STR_OPDS_BROWSER: "OPDS-webbläsare" STR_SEARCH: "Sök" STR_COVER_CUSTOM: "Omslag + Valfri" diff --git a/lib/I18n/translations/turkish.yaml b/lib/I18n/translations/turkish.yaml index 197e5f9e..0faa13ec 100644 --- a/lib/I18n/translations/turkish.yaml +++ b/lib/I18n/translations/turkish.yaml @@ -181,6 +181,9 @@ STR_EXIT: "« Çıkış" STR_HOME: "« Ana Sayfa" STR_SELECT: "Seç" STR_TOGGLE: "Değiştir" +STR_TOGGLE_BOOKMARK: "Yer imini değiştir" +STR_BOOKMARK_REMOVED: "Yer imi kaldırıldı." +STR_HOLD_OPEN_TO_DELETE: "Silmek için Aç düğmesini basılı tutun" STR_CONFIRM: "Onayla" STR_CANCEL: "İptal" STR_CONNECT: "Bağlan" diff --git a/lib/I18n/translations/ukrainian.yaml b/lib/I18n/translations/ukrainian.yaml index 6b69fab0..c60de68f 100644 --- a/lib/I18n/translations/ukrainian.yaml +++ b/lib/I18n/translations/ukrainian.yaml @@ -176,8 +176,7 @@ STR_DOWNLOADING: "Завантаження..." STR_DOWNLOAD_FAILED: "Завантаження не вдалося" STR_ERROR_MSG: "Помилка:" STR_UNNAMED: "Без назви" -STR_HOLD_CONFIRM_TO_DELETE: "Утрим. Підтвердити, щоб Видалити" -STR_BOOKMARK_INSTRUCTIONS: "Утрим. Підтвердити в книзі, щоб створ. закладку" +STR_HOLD_OPEN_TO_DELETE: "Утримуйте Відкрити, щоб видалити" STR_NO_SERVER_URL: "URL сервера не налаштовано" STR_FETCH_FEED_FAILED: "Не вдалося отримати стрічку" STR_PARSE_FEED_FAILED: "Не вдалося розпарсити стрічку" @@ -195,6 +194,7 @@ STR_HOME: "« Додому" STR_SELECT: "Вибрати" STR_SELECTED: "Вибрано" STR_TOGGLE: "Обрати" +STR_TOGGLE_BOOKMARK: "Перемкнути закладку" STR_CONFIRM: "Підтвердити" STR_CANCEL: "Скасувати" STR_CONNECT: "Приєдн." @@ -257,6 +257,7 @@ STR_SUNLIGHT_FADING_FIX: "Виправлення вицвітання на со STR_REMAP_FRONT_BUTTONS: "Налаштувати передні кнопки" STR_BOOKMARKS: "Закладки" STR_BOOKMARK_ADDED: "Закладку додано" +STR_BOOKMARK_REMOVED: "Закладку видалено" STR_OPDS_BROWSER: "Браузер OPDS" STR_SEARCH: "Пошук" STR_COVER_CUSTOM: "Обкл. + власне" diff --git a/lib/I18n/translations/valencian.yaml b/lib/I18n/translations/valencian.yaml index a4c2f147..6dd28a25 100644 --- a/lib/I18n/translations/valencian.yaml +++ b/lib/I18n/translations/valencian.yaml @@ -180,8 +180,7 @@ STR_DOWNLOADING: "S'està baixant..." STR_DOWNLOAD_FAILED: "Ha fallat la baixada" STR_ERROR_MSG: "Error:" STR_UNNAMED: "Sense nom" -STR_HOLD_CONFIRM_TO_DELETE: "Manteniu premut Confirma per esborrar" -STR_BOOKMARK_INSTRUCTIONS: "Manteniu premut Confirma al lector per crear un punt de llibre." +STR_HOLD_OPEN_TO_DELETE: "Manteniu premut Obre per esborrar" STR_NO_SERVER_URL: "No s'ha configurat cap URL de servidor" STR_FETCH_FEED_FAILED: "Ha fallat l'obtenció del feed" STR_PARSE_FEED_FAILED: "Ha fallat l'anàlisi del feed" @@ -197,6 +196,7 @@ STR_HOME: "« Inici" STR_SELECT: "Selecciona" STR_SELECTED: "Seleccionat" STR_TOGGLE: "Canvia" +STR_TOGGLE_BOOKMARK: "Canvia punt de llibre" STR_CONFIRM: "Confirma" STR_CANCEL: "Cancel·la" STR_CONNECT: "Connecta" @@ -240,6 +240,7 @@ STR_SUNLIGHT_FADING_FIX: "Correcció de l'esvaïment pel sol" STR_REMAP_FRONT_BUTTONS: "Reassigna els botons frontals" STR_BOOKMARKS: "Punts de llibre" STR_BOOKMARK_ADDED: "S'ha afegit el punt de llibre." +STR_BOOKMARK_REMOVED: "S'ha eliminat el punt de llibre." STR_OPDS_BROWSER: "Navegador OPDS" STR_COVER_CUSTOM: "Portada + Personalitzat" STR_QUICK_RESUME: "Represa ràpida" diff --git a/lib/I18n/translations/vietnamese.yaml b/lib/I18n/translations/vietnamese.yaml index 38a23450..2551b275 100644 --- a/lib/I18n/translations/vietnamese.yaml +++ b/lib/I18n/translations/vietnamese.yaml @@ -176,8 +176,7 @@ STR_DOWNLOADING: "Đang tải về..." STR_DOWNLOAD_FAILED: "Tải về thất bại" STR_ERROR_MSG: "Lỗi:" STR_UNNAMED: "Không tên" -STR_HOLD_CONFIRM_TO_DELETE: "Giữ Xác nhận để xóa" -STR_BOOKMARK_INSTRUCTIONS: "Giữ Xác nhận trong trình đọc để tạo dấu trang." +STR_HOLD_OPEN_TO_DELETE: "Giữ Mở để xóa" STR_NO_SERVER_URL: "Chưa cấu hình URL máy chủ" STR_FETCH_FEED_FAILED: "Không tải được nguồn cấp" STR_PARSE_FEED_FAILED: "Không phân tích được nguồn cấp" @@ -195,6 +194,7 @@ STR_HOME: "« Thư viện" STR_SELECT: "Chọn" STR_SELECTED: "Đã chọn" STR_TOGGLE: "Bật/Tắt" +STR_TOGGLE_BOOKMARK: "Bật/tắt dấu trang" STR_CONFIRM: "Xác nhận" STR_CANCEL: "Hủy" STR_CONNECT: "Kết nối" @@ -257,6 +257,7 @@ STR_SUNLIGHT_FADING_FIX: "Khắc phục mờ dưới nắng" STR_REMAP_FRONT_BUTTONS: "Gán lại nút mặt trước" STR_BOOKMARKS: "Dấu trang" STR_BOOKMARK_ADDED: "Đã thêm dấu trang." +STR_BOOKMARK_REMOVED: "Đã xóa dấu trang." STR_OPDS_BROWSER: "Trình duyệt OPDS" STR_SEARCH: "Tìm kiếm" STR_COVER_CUSTOM: "Ảnh bìa + Tùy chỉnh" diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index d001f066..af274e3c 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -238,8 +238,8 @@ class CrossPointSettings { // Long-press page turn button behavior uint8_t longPressButtonBehavior = OFF; // Long-press Confirm function in EPUB reader (cycles through LONG_PRESS_MENU_FUNCTION values). - // Defaults to Bookmark to preserve the upstream long-press-Confirm-adds-bookmark behavior. - uint8_t longPressMenuFunction = LP_MENU_BOOKMARK; + // Defaults to Disabled so shortcut-based bookmark toggling remains opt-in. + uint8_t longPressMenuFunction = LP_MENU_DISABLED; // UI Theme uint8_t uiTheme = LYRA; // Sunlight fading compensation diff --git a/src/JsonSettingsIO.cpp b/src/JsonSettingsIO.cpp index 4b9e0fc6..6f0a51d2 100644 --- a/src/JsonSettingsIO.cpp +++ b/src/JsonSettingsIO.cpp @@ -423,6 +423,9 @@ bool JsonSettingsIO::saveBookmarks(const std::vector& bookmarks, obj["xpath"] = bookmark.xpath; obj["percentage"] = bookmark.percentage; obj["summary"] = bookmark.summary; + obj["si"] = bookmark.computedSpineIndex; + obj["pc"] = bookmark.computedChapterPageCount; + obj["pp"] = bookmark.computedChapterProgress; } String json; @@ -447,6 +450,9 @@ bool JsonSettingsIO::loadBookmarks(std::vector& bookmarks, const bookmark.xpath = obj["xpath"] | std::string(""); bookmark.percentage = obj["percentage"] | static_cast(0); bookmark.summary = obj["summary"] | std::string(""); + bookmark.computedSpineIndex = obj["si"] | static_cast(0); + bookmark.computedChapterPageCount = obj["pc"] | static_cast(0); + bookmark.computedChapterProgress = obj["pp"] | static_cast(0); } LOG_DBG("BKM", "Loaded %zu bookmarks from file", bookmarks.size()); diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index e1328787..3d2df14e 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -41,6 +41,8 @@ namespace { // pagesPerRefresh now comes from SETTINGS.getRefreshFrequency() // pages per minute, first item is 1 to prevent division by zero if accessed constexpr int PAGE_TURN_RATES[] = {1, 1, 3, 6, 12}; +constexpr size_t initialBookmarkCacheCapacity = 16; +constexpr float bookmarkProgressEpsilon = 0.0001f; int clampPercent(int percent) { if (percent < 0) { @@ -63,6 +65,35 @@ bool isInReadFolder(const std::string& path) { return path.size() > n && path.compare(0, n, READ_FOLDER) == 0 && path[n] == '/'; } +struct ProgressRange { + float start; + float end; +}; + +ProgressRange getPageProgressRange(const std::shared_ptr& epub, const int spineIndex, const int page, + const int pageCount) { + if (pageCount <= 1) { + return {epub->calculateProgress(spineIndex, 0.0f), epub->calculateProgress(spineIndex, 1.0f)}; + } + + const float step = 1.0f / static_cast(pageCount - 1); + const float anchor = std::clamp(static_cast(page) * step, 0.0f, 1.0f); + const float start = std::max(0.0f, anchor - (step * 0.5f)); + const float end = std::min(1.0f, anchor + (step * 0.5f)); + return {epub->calculateProgress(spineIndex, start), epub->calculateProgress(spineIndex, end)}; +} + +bool bookmarkMatchesProgress(const BookmarkEntry& bookmark, const SavedProgressPosition& progress, + const ProgressRange& pageRange) { + if (bookmark.xpath == progress.xpath) { + return true; + } + + const float bookmarkProgress = std::clamp(bookmark.percentage, 0.0f, 1.0f); + return bookmarkProgress + bookmarkProgressEpsilon >= pageRange.start && + bookmarkProgress - bookmarkProgressEpsilon <= pageRange.end; +} + // Pick a non-colliding destination path inside /Read/ for a finished book. // Mirrors the suffixing scheme used elsewhere: "name.epub" -> "name (2).epub", etc. std::string buildReadFolderDestination(const std::string& srcPath) { @@ -165,6 +196,8 @@ void EpubReaderActivity::onEnter() { APP_STATE.saveToFile(); RECENT_BOOKS.addBook(epub->getPath(), epub->getTitle(), epub->getAuthor(), epub->getThumbBmpPath()); + loadCachedBookmarks(); + // Trigger first update requestUpdate(); } @@ -281,7 +314,7 @@ void EpubReaderActivity::loop() { const int bookProgressPercent = clampPercent(static_cast(bookProgress + 0.5f)); startActivityForResult(std::make_unique( renderer, mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent, - SETTINGS.orientation, !currentPageFootnotes.empty()), + SETTINGS.orientation, !currentPageFootnotes.empty(), !cachedBookmarks.empty()), [this](const ActivityResult& result) { // Always apply orientation change even if the menu was cancelled const auto& menu = std::get(result.data); @@ -500,6 +533,7 @@ void EpubReaderActivity::jumpToPercent(int percent) { void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action) { auto progressChangeResultHandler = [this](const ActivityResult& result) { + loadCachedBookmarks(); if (!result.isCancelled) { const auto& sync = std::get(result.data); if (currentSpineIndex != sync.spineIndex || (section && section->currentPage != sync.page)) { @@ -615,6 +649,10 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction progressChangeResultHandler); break; } + case EpubReaderMenuActivity::MenuAction::TOGGLE_BOOKMARK: { + addBookmark(); + break; + } } } @@ -902,6 +940,8 @@ void EpubReaderActivity::render(RenderLock&& lock) { return; } + updateBookmarkFlag(); + { auto p = section->loadPageFromSectionFile(); if (!p) { @@ -933,7 +973,7 @@ void EpubReaderActivity::render(RenderLock&& lock) { } if (showBookmarkMessage) { - GUI.drawPopup(renderer, tr(STR_BOOKMARK_ADDED)); + GUI.drawPopup(renderer, bookmarkRemoved ? tr(STR_BOOKMARK_REMOVED) : tr(STR_BOOKMARK_ADDED)); } } @@ -1172,7 +1212,7 @@ void EpubReaderActivity::renderStatusBar() const { title = epub->getTitle(); } - GUI.drawStatusBar(renderer, bookProgress, currentPage, pageCount, title, 0, textYOffset); + GUI.drawStatusBar(renderer, bookProgress, currentPage, pageCount, title, 0, textYOffset, true, currentPageBookmarked); } void EpubReaderActivity::navigateToHref(const std::string& hrefStr, const bool savePosition) { @@ -1234,11 +1274,31 @@ void EpubReaderActivity::restoreSavedPosition() { requestUpdate(); } +void EpubReaderActivity::loadCachedBookmarks() { + cachedBookmarks.clear(); + if (cachedBookmarks.capacity() < initialBookmarkCacheCapacity) { + cachedBookmarks.reserve(initialBookmarkCacheCapacity); + } + if (!epub) { + currentPageBookmarked = false; + return; + } + + const std::string bmPath = BookmarkUtil::getBookmarkPath(epub->getPath()); + if (Storage.exists(bmPath.c_str())) { + String json = Storage.readFile(bmPath.c_str()); + if (!json.isEmpty()) { + JsonSettingsIO::loadBookmarks(cachedBookmarks, json.c_str()); + } + } + updateBookmarkFlag(); +} + void EpubReaderActivity::addBookmark() { if (!section || !epub) { return; } - LOG_DBG("ERS", "Adding bookmark at spine %d, page %d", currentSpineIndex, section ? section->currentPage : -1); + LOG_DBG("ERS", "Toggle bookmark at spine %d, page %d", currentSpineIndex, section ? section->currentPage : -1); int currentPage; int pageCount; { @@ -1247,45 +1307,57 @@ void EpubReaderActivity::addBookmark() { currentPage = section->currentPage; } - std::string pageText; - if (currentPage >= 0 && currentPage < pageCount) { - pageText = section->getTextFromSectionFile(); + SavedProgressPosition progress = ProgressMapper::toSavedProgress(epub, getCurrentPosition()); + const ProgressRange pageRange = getPageProgressRange(epub, currentSpineIndex, currentPage, pageCount); + + const size_t bookmarkCountBeforeToggle = cachedBookmarks.size(); + cachedBookmarks.erase( + std::remove_if(cachedBookmarks.begin(), cachedBookmarks.end(), + [&](const BookmarkEntry& b) { return bookmarkMatchesProgress(b, progress, pageRange); }), + cachedBookmarks.end()); + if (cachedBookmarks.size() != bookmarkCountBeforeToggle) { + bookmarkRemoved = true; + currentPageBookmarked = false; + } else { + std::string pageText; + if (currentPage >= 0 && currentPage < pageCount) { + pageText = section->getTextFromSectionFile(); + } + BookmarkEntry entry; + entry.percentage = progress.percentage; + entry.xpath = progress.xpath; + entry.summary = BookmarkUtil::sanitizeBookmarkSummary(pageText); + entry.computedSpineIndex = currentSpineIndex; + entry.computedChapterPageCount = pageCount; + entry.computedChapterProgress = currentPage; + cachedBookmarks.insert(cachedBookmarks.begin(), entry); + bookmarkRemoved = false; + currentPageBookmarked = true; } - SavedProgressPosition progress = ProgressMapper::toSavedProgress(epub, getCurrentPosition()); - - BookmarkEntry entry; - entry.percentage = progress.percentage; - entry.xpath = progress.xpath; - entry.summary = BookmarkUtil::sanitizeBookmarkSummary(pageText); - - // Add bookmark const std::string path = BookmarkUtil::getBookmarkPath(epub->getPath()); - LOG_DBG("ERS", "Bookmark path: %s", path.c_str()); const std::string bookmarksDir = BookmarkUtil::getBookmarksDir(); Storage.mkdir(bookmarksDir.c_str()); - std::vector bookmarks; - if (Storage.exists(path.c_str())) { - LOG_DBG("ERS", "Existing bookmark file found, loading bookmarks"); - String json = Storage.readFile(path.c_str()); - if (!json.isEmpty()) { - JsonSettingsIO::loadBookmarks(bookmarks, json.c_str()); - } - } else { - LOG_DBG("ERS", "No existing bookmark file, starting with empty bookmark list"); + const bool ok = JsonSettingsIO::saveBookmarks(cachedBookmarks, path.c_str()); + if (!ok) { + LOG_ERR("ERS", "Failed to save bookmarks to: %s", path.c_str()); } - bookmarks.insert(bookmarks.begin(), entry); - LOG_DBG("ERS", "Saving bookmark to file: %s", path.c_str()); - const bool ok = JsonSettingsIO::saveBookmarks(bookmarks, path.c_str()); - if (ok) { - showBookmarkMessage = true; - } else { - LOG_ERR("ERS", "Failed to save bookmark to: %s", path.c_str()); - } - requestUpdate(); } +void EpubReaderActivity::updateBookmarkFlag() { + if (!section || !epub || cachedBookmarks.empty()) { + currentPageBookmarked = false; + return; + } + SavedProgressPosition progress = ProgressMapper::toSavedProgress(epub, getCurrentPosition()); + const ProgressRange pageRange = + getPageProgressRange(epub, currentSpineIndex, section->currentPage, section->pageCount); + currentPageBookmarked = std::any_of(cachedBookmarks.begin(), cachedBookmarks.end(), [&](const BookmarkEntry& b) { + return bookmarkMatchesProgress(b, progress, pageRange); + }); +} + ScreenshotInfo EpubReaderActivity::getScreenshotInfo() const { ScreenshotInfo info; info.readerType = ScreenshotInfo::ReaderType::Epub; diff --git a/src/activities/reader/EpubReaderActivity.h b/src/activities/reader/EpubReaderActivity.h index d1b46225..7e2a4ad2 100644 --- a/src/activities/reader/EpubReaderActivity.h +++ b/src/activities/reader/EpubReaderActivity.h @@ -5,6 +5,7 @@ #include +#include "BookmarkEntry.h" #include "EpubReaderMenuActivity.h" #include "ProgressMapper.h" #include "activities/Activity.h" @@ -34,6 +35,9 @@ class EpubReaderActivity final : public Activity { bool automaticPageTurnActive = false; bool showBookmarkMessage = false; bool ignoreNextConfirmRelease = false; + bool currentPageBookmarked = false; + bool bookmarkRemoved = false; // true when last toggle removed (controls popup text) + std::vector cachedBookmarks; // Tracks whether this book is currently removed from Recent Books by the // removeReadBooksFromRecents feature (set at End-of-Book, cleared if paged back in). bool recentsEntryRemoved = false; @@ -66,7 +70,9 @@ class EpubReaderActivity final : public Activity { void applyOrientation(uint8_t orientation); void toggleAutoPageTurn(uint8_t selectedPageTurnOption); void pageTurn(bool isForwardTurn); + void loadCachedBookmarks(); void addBookmark(); + void updateBookmarkFlag(); // Footnote navigation void navigateToHref(const std::string& href, bool savePosition = false); diff --git a/src/activities/reader/EpubReaderBookmarksActivity.cpp b/src/activities/reader/EpubReaderBookmarksActivity.cpp index 93c54ade..0b1f3f30 100644 --- a/src/activities/reader/EpubReaderBookmarksActivity.cpp +++ b/src/activities/reader/EpubReaderBookmarksActivity.cpp @@ -39,14 +39,6 @@ void EpubReaderBookmarksActivity::onEnter() { bookmarks.shrink_to_fit(); } else { JsonSettingsIO::loadBookmarks(bookmarks, json.c_str()); - - // pre-compute bookmark page values for quicker rendering - for (auto& bookmark : bookmarks) { - CrossPointPosition pos = ProgressMapper::toCrossPoint(epub, {bookmark.xpath, bookmark.percentage}, renderer); - bookmark.computedSpineIndex = pos.spineIndex; - bookmark.computedChapterPageCount = pos.totalPages; - bookmark.computedChapterProgress = pos.pageNumber; - } } } else { LOG_DBG("EPB", "No bookmark file found at %s, starting with empty bookmarks", path.c_str()); @@ -93,6 +85,14 @@ void EpubReaderBookmarksActivity::loop() { selectorIndex--; } + if (bookmarks.empty()) { + ActivityResult result; + result.isCancelled = true; + setResult(std::move(result)); + finish(); + return; + } + requestUpdate(); confirmingDelete = DELETE_MODE_OFF; return; @@ -186,9 +186,12 @@ void EpubReaderBookmarksActivity::render(RenderLock&&) { auto bookmark = bookmarks.at(confirmingDelete >= DELETE_MODE_DISPLAY ? selectorIndex : index); auto tocIndex = epub->getTocIndexForSpineIndex(bookmark.computedSpineIndex); auto tocTitle = (tocIndex >= 0) ? (epub->getTocItem(tocIndex)).title : tr(STR_UNNAMED); - return std::to_string((int)(std::clamp(bookmark.percentage, 0.0f, 1.0f) * 100.0f + 0.5f)) + "% - " + - std::to_string(bookmark.computedChapterProgress + 1) + "/" + - std::to_string(bookmark.computedChapterPageCount) + " - " + tocTitle; + std::string subtitle = std::to_string((int)(std::clamp(bookmark.percentage, 0.0f, 1.0f) * 100.0f + 0.5f)) + "% - "; + if (bookmark.computedChapterPageCount > 0) { + subtitle += std::to_string(bookmark.computedChapterProgress + 1) + "/" + + std::to_string(bookmark.computedChapterPageCount) + " - "; + } + return subtitle + tocTitle; }; const auto getBookmarkIcon = [isPortrait](int index) { // only enabled icon in portrait mode due to limitation with rotating icons for other orientations @@ -208,11 +211,8 @@ void EpubReaderBookmarksActivity::render(RenderLock&&) { getBookmarkTitle, getBookmarkSubtitle, getBookmarkIcon); GUI.drawHelpText(renderer, Rect{contentX, pageHeight - hintGutterBottom, contentWidth, LINE_HEIGHT}, - tr(STR_HOLD_CONFIRM_TO_DELETE)); + tr(STR_HOLD_OPEN_TO_DELETE)); } - } else { - GUI.drawHelpText(renderer, Rect{contentX, LINE_HEIGHT * 2, contentWidth, LINE_HEIGHT}, - tr(STR_BOOKMARK_INSTRUCTIONS)); } const auto backLabel = confirmingDelete >= DELETE_MODE_DISPLAY ? tr(STR_CANCEL) : tr(STR_BACK); diff --git a/src/activities/reader/EpubReaderMenuActivity.cpp b/src/activities/reader/EpubReaderMenuActivity.cpp index 634db50c..69e1fccd 100644 --- a/src/activities/reader/EpubReaderMenuActivity.cpp +++ b/src/activities/reader/EpubReaderMenuActivity.cpp @@ -10,23 +10,27 @@ EpubReaderMenuActivity::EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title, const int currentPage, const int totalPages, const int bookProgressPercent, const uint8_t currentOrientation, - const bool hasFootnotes) + const bool hasFootnotes, const bool hasBookmarks) : Activity("EpubReaderMenu", renderer, mappedInput), - menuItems(buildMenuItems(hasFootnotes)), + menuItems(buildMenuItems(hasFootnotes, hasBookmarks)), title(title), pendingOrientation(currentOrientation), currentPage(currentPage), totalPages(totalPages), bookProgressPercent(bookProgressPercent) {} -std::vector EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes) { +std::vector EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes, + bool hasBookmarks) { std::vector items; - items.reserve(11); + items.reserve(12); items.push_back({MenuAction::SELECT_CHAPTER, StrId::STR_SELECT_CHAPTER}); if (hasFootnotes) { items.push_back({MenuAction::FOOTNOTES, StrId::STR_FOOTNOTES}); } - items.push_back({MenuAction::BOOKMARKS, StrId::STR_BOOKMARKS}); + if (hasBookmarks) { + items.push_back({MenuAction::BOOKMARKS, StrId::STR_BOOKMARKS}); + } + items.push_back({MenuAction::TOGGLE_BOOKMARK, StrId::STR_TOGGLE_BOOKMARK}); items.push_back({MenuAction::ROTATE_SCREEN, StrId::STR_ORIENTATION}); items.push_back({MenuAction::AUTO_PAGE_TURN, StrId::STR_AUTO_TURN_PAGES_PER_MIN}); items.push_back({MenuAction::GO_TO_PERCENT, StrId::STR_GO_TO_PERCENT}); diff --git a/src/activities/reader/EpubReaderMenuActivity.h b/src/activities/reader/EpubReaderMenuActivity.h index 43272f48..285ab76e 100644 --- a/src/activities/reader/EpubReaderMenuActivity.h +++ b/src/activities/reader/EpubReaderMenuActivity.h @@ -18,6 +18,7 @@ class EpubReaderMenuActivity final : public Activity { AUTO_PAGE_TURN, ROTATE_SCREEN, BOOKMARKS, + TOGGLE_BOOKMARK, SCREENSHOT, DISPLAY_QR, GO_HOME, @@ -27,7 +28,7 @@ class EpubReaderMenuActivity final : public Activity { explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title, const int currentPage, const int totalPages, const int bookProgressPercent, - const uint8_t currentOrientation, const bool hasFootnotes); + const uint8_t currentOrientation, const bool hasFootnotes, bool hasBookmarks); void onEnter() override; void onExit() override; @@ -40,7 +41,7 @@ class EpubReaderMenuActivity final : public Activity { StrId labelId; }; - static std::vector buildMenuItems(bool hasFootnotes); + static std::vector buildMenuItems(bool hasFootnotes, bool hasBookmarks); // Fixed menu layout const std::vector menuItems; diff --git a/src/components/icons/bookmark.h b/src/components/icons/bookmark.h index 9f99e03c..6343332d 100644 --- a/src/components/icons/bookmark.h +++ b/src/components/icons/bookmark.h @@ -10,3 +10,8 @@ static const uint8_t BookmarkIcon[] = { 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +// size: 16x16 +static const uint8_t BookmarkStatusIcon[] = {0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, + 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xF8, + 0x0F, 0x78, 0x0E, 0x38, 0x0C, 0x18, 0x08, 0x08, 0x00, 0x00}; diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index caa7e6cc..ff7e927d 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -13,6 +13,7 @@ #include "I18n.h" #include "RecentBooksStore.h" #include "components/UITheme.h" +#include "components/icons/bookmark.h" #include "fontIds.h" // Internal constants @@ -20,6 +21,27 @@ namespace { constexpr int homeMenuMargin = 20; constexpr int homeMarginTop = 30; constexpr int subtitleY = 738; +constexpr int bookmarkStatusIconWidth = 16; +constexpr int bookmarkStatusIconHeight = 14; +constexpr int bookmarkStatusIconGap = 4; +constexpr int bookmarkStatusIconTopCrop = 2; + +bool statusBarTextLaneVisible() { + return SETTINGS.statusBarChapterPageCount || SETTINGS.statusBarBookProgressPercentage || + SETTINGS.statusBarTitle != CrossPointSettings::STATUS_BAR_TITLE::HIDE_TITLE || SETTINGS.statusBarBattery || + (SETTINGS.statusBarClock && halClock.isAvailable()); +} + +void drawBookmarkStatusIcon(const GfxRenderer& renderer, const int x, const int y) { + constexpr int bytesPerRow = bookmarkStatusIconWidth / 8; + for (int row = 0; row < bookmarkStatusIconHeight; ++row) { + for (int col = 0; col < bookmarkStatusIconWidth; ++col) { + const uint8_t byte = BookmarkStatusIcon[(row + bookmarkStatusIconTopCrop) * bytesPerRow + col / 8]; + const uint8_t mask = 1U << (7 - (col % 8)); + renderer.drawPixel(x + col, y + row, (byte & mask) != 0); + } + } +} } // namespace @@ -727,11 +749,12 @@ void BaseTheme::fillPopupProgress(const GfxRenderer& renderer, const Rect& layou void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, const int currentPage, const int pageCount, std::string title, const int paddingBottom, const int textYOffset, - const bool fillMargin) const { + const bool fillMargin, const bool isPageBookmarked) const { auto metrics = UITheme::getInstance().getMetrics(); int orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft; renderer.getOrientedViewableTRBL(&orientedMarginTop, &orientedMarginRight, &orientedMarginBottom, &orientedMarginLeft); + const bool showStatusBarTextLane = statusBarTextLaneVisible(); // Draw Progress Text const auto screenHeight = renderer.getScreenHeight(); @@ -777,14 +800,24 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c renderer.fillRect(barMarginLeft, progressBarY, barWidth, barHeight, true); } + // Draw Bookmark + const int leftClusterX = metrics.statusBarHorizontalMargin + orientedMarginLeft + 1; + const bool showBookmarkIcon = showStatusBarTextLane && isPageBookmarked; + const int bookmarkReserveWidth = showBookmarkIcon ? (bookmarkStatusIconWidth + bookmarkStatusIconGap) : 0; + if (showBookmarkIcon) { + const int bookmarkY = textY + 5; + drawBookmarkStatusIcon(renderer, leftClusterX, bookmarkY); + } + // Draw Battery const bool showBatteryPercentage = SETTINGS.hideBatteryPercentage == CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_NEVER; + int leftClusterWidth = bookmarkReserveWidth; if (SETTINGS.statusBarBattery) { GUI.drawBatteryLeft(renderer, - Rect{metrics.statusBarHorizontalMargin + orientedMarginLeft + 1, textY, metrics.batteryWidth, - metrics.batteryHeight}, + Rect{leftClusterX + bookmarkReserveWidth, textY, metrics.batteryWidth, metrics.batteryHeight}, showBatteryPercentage); + leftClusterWidth += showBatteryPercentage ? 50 : 20; } // Draw Clock (X3 only — DS3231 RTC) @@ -808,8 +841,7 @@ void BaseTheme::drawStatusBar(GfxRenderer& renderer, const float bookProgress, c const int rendererableScreenWidth = renderer.getScreenWidth() - (metrics.statusBarHorizontalMargin * 2) - orientedMarginLeft - orientedMarginRight; - const int batterySize = SETTINGS.statusBarBattery ? (showBatteryPercentage ? 50 : 20) : 0; - const int titleMarginLeft = batterySize + 30; + const int titleMarginLeft = leftClusterWidth + 30; const int clockReserve = clockTextWidth > 0 ? (clockTextWidth + 10) : 0; const int titleMarginRight = progressTextWidth + clockReserve + 30; diff --git a/src/components/themes/BaseTheme.h b/src/components/themes/BaseTheme.h index fbb3feeb..39adb758 100644 --- a/src/components/themes/BaseTheme.h +++ b/src/components/themes/BaseTheme.h @@ -210,7 +210,7 @@ class BaseTheme { virtual void fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const; void drawStatusBar(GfxRenderer& renderer, const float bookProgress, const int currentPage, const int pageCount, std::string title, const int paddingBottom = 0, const int textYOffset = 0, - const bool fillMargin = true) const; + const bool fillMargin = true, const bool isPageBookmarked = false) const; void drawHelpText(const GfxRenderer& renderer, Rect rect, const char* label) const; virtual void drawTextField(const GfxRenderer& renderer, Rect rect, const int textWidth, bool cursorMode = false, int contentStartX = 0, int contentWidth = 0) const;