Compare commits
4
Commits
7fca7fbfcd
...
6fcf05a289
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fcf05a289 | ||
|
|
9500c353a3 | ||
|
|
aef3cc3409 | ||
|
|
041b9b9567 |
@@ -10,14 +10,16 @@ SOURCES += \
|
|||||||
src/apiclient.cpp \
|
src/apiclient.cpp \
|
||||||
src/downloader.cpp \
|
src/downloader.cpp \
|
||||||
src/opdsparser.cpp \
|
src/opdsparser.cpp \
|
||||||
src/epub.cpp
|
src/epub.cpp \
|
||||||
|
src/readerstate.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/settings.h \
|
src/settings.h \
|
||||||
src/apiclient.h \
|
src/apiclient.h \
|
||||||
src/downloader.h \
|
src/downloader.h \
|
||||||
src/opdsparser.h \
|
src/opdsparser.h \
|
||||||
src/epub.h
|
src/epub.h \
|
||||||
|
src/readerstate.h
|
||||||
|
|
||||||
LIBS += -lz
|
LIBS += -lz
|
||||||
|
|
||||||
@@ -33,8 +35,9 @@ DISTFILES += $$OTHER_FILES
|
|||||||
|
|
||||||
# Translations are NOT handled by the sailfishapp_i18n template on the 5.1 kit
|
# Translations are NOT handled by the sailfishapp_i18n template on the 5.1 kit
|
||||||
# (the .qm is never generated/installed reliably). The spec builds the .qm
|
# (the .qm is never generated/installed reliably). The spec builds the .qm
|
||||||
# with lrelease and installs it into /usr/share/translations directly;
|
# with lrelease and installs it into the app data dir
|
||||||
# libsailfishapp loads harbour-calibreweb_<locale>.qm automatically.
|
# (/usr/share/harbour-calibreweb/translations — the RPM validator rejects
|
||||||
|
# /usr/share/translations); main.cpp loads it explicitly (see language block).
|
||||||
# Source strings are ENGLISH (default language); harbour-calibreweb_it.ts
|
# Source strings are ENGLISH (default language); harbour-calibreweb_it.ts
|
||||||
# provides the Italian catalogue.
|
# provides the Italian catalogue.
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import Sailfish.Silica 1.0
|
import Sailfish.Silica 1.0
|
||||||
|
|
||||||
|
// Cover: se un libro è aperto (readerState attivo dal ReaderPage) mostra
|
||||||
|
// titolo e posizione di lettura, altrimenti icona e nome dell'app.
|
||||||
CoverBackground {
|
CoverBackground {
|
||||||
Column {
|
Column {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - 2 * Theme.paddingLarge
|
||||||
spacing: Theme.paddingMedium
|
spacing: Theme.paddingMedium
|
||||||
|
visible: !readerState.active
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -20,4 +24,29 @@ CoverBackground {
|
|||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - 2 * Theme.paddingLarge
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
|
visible: readerState.active
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
text: readerState.title
|
||||||
|
color: Theme.primaryColor
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
truncationMode: TruncationMode.Elide
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
text: readerState.subtitle
|
||||||
|
color: Theme.secondaryColor
|
||||||
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
truncationMode: TruncationMode.Elide
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ ApplicationWindow {
|
|||||||
initialPage: Component { MainPage {} }
|
initialPage: Component { MainPage {} }
|
||||||
cover: Component { CoverPage {} }
|
cover: Component { CoverPage {} }
|
||||||
|
|
||||||
allowedOrientations: Orientation.Portrait
|
// Niente vincoli a livello window: ogni pagina dichiara le proprie
|
||||||
_defaultPageOrientations: Orientation.Portrait
|
// orientazioni (il lettore ruota in landscape, le pagine di lista
|
||||||
|
// restano in portrait). _defaultPageOrientations è API privata.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import Sailfish.Silica 1.0
|
|||||||
Page {
|
Page {
|
||||||
id: page
|
id: page
|
||||||
|
|
||||||
|
allowedOrientations: Orientation.Portrait
|
||||||
|
|
||||||
property var bookData: ({})
|
property var bookData: ({})
|
||||||
property string coverSource: ""
|
property string coverSource: ""
|
||||||
property bool downloading: false
|
property bool downloading: false
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import Sailfish.Silica 1.0
|
|||||||
Page {
|
Page {
|
||||||
id: page
|
id: page
|
||||||
|
|
||||||
|
allowedOrientations: Orientation.Portrait
|
||||||
|
|
||||||
property string feedUrl
|
property string feedUrl
|
||||||
property string pageTitle
|
property string pageTitle
|
||||||
property string nextUrl: ""
|
property string nextUrl: ""
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import Sailfish.Silica 1.0
|
|||||||
Page {
|
Page {
|
||||||
id: page
|
id: page
|
||||||
|
|
||||||
|
allowedOrientations: Orientation.Portrait
|
||||||
|
|
||||||
property bool loading: false
|
property bool loading: false
|
||||||
property string errorMessage: ""
|
property string errorMessage: ""
|
||||||
|
|
||||||
|
|||||||
+51
-25
@@ -18,7 +18,6 @@ Page {
|
|||||||
property string bookFormat: "epub"
|
property string bookFormat: "epub"
|
||||||
|
|
||||||
property int currentChapter: 0
|
property int currentChapter: 0
|
||||||
property double chapterFraction: 0.0
|
|
||||||
// posizione di lettura nel capitolo corrente, stimata in caratteri di testo
|
// posizione di lettura nel capitolo corrente, stimata in caratteri di testo
|
||||||
// dalla frazione di scroll (stabile col reflow del font)
|
// dalla frazione di scroll (stabile col reflow del font)
|
||||||
property int charOffset: 0
|
property int charOffset: 0
|
||||||
@@ -143,10 +142,18 @@ Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// contenuto: Flickable NON interattivo (lo scroll è solo programmatico)
|
// contenuto: Flickable NON interattivo (lo scroll è solo programmatico).
|
||||||
|
// La finestra di lettura VIVE DENTRO i margini verticali (top/bottom
|
||||||
|
// margin): il testo non puo' fisicamente uscire dai bordi, quindi i
|
||||||
|
// margini alto/basso sono identici su OGNI pagina (non solo a inizio
|
||||||
|
// capitolo) e si aggiornano live con l'anteprima del dialog.
|
||||||
Flickable {
|
Flickable {
|
||||||
id: innerFlick
|
id: innerFlick
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: page.previewMarginT >= 0
|
||||||
|
? page.previewMarginT : appSettings.readerMarginV
|
||||||
|
anchors.bottomMargin: page.previewMarginT >= 0
|
||||||
|
? page.previewMarginT : appSettings.readerMarginV
|
||||||
contentHeight: readerContent.height
|
contentHeight: readerContent.height
|
||||||
interactive: false
|
interactive: false
|
||||||
clip: true
|
clip: true
|
||||||
@@ -154,15 +161,25 @@ Page {
|
|||||||
Item {
|
Item {
|
||||||
id: readerContent
|
id: readerContent
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: Math.max(parent.height,
|
// Altezza = testo + "deficit" dell'ultima pagina: max-scroll
|
||||||
textItem.height + 2 * (page.previewMarginT >= 0
|
// diventa k*step esatto e l'ultima pagina e' allineata (niente
|
||||||
? page.previewMarginT
|
// testo ripetuto tra penultima e ultima). Calcolo INLINE nel
|
||||||
: appSettings.readerMarginV))
|
// binding: si rivaluta quando cambia la finestra (rotazione,
|
||||||
|
// anteprima margini) o l'altezza del testo (reflow).
|
||||||
|
height: {
|
||||||
|
var step = Math.max(1, parent.height)
|
||||||
|
var t = textItem.height
|
||||||
|
var pad = 0
|
||||||
|
if (t > 0) {
|
||||||
|
var r = t % step
|
||||||
|
pad = r === 0 ? 0 : step - r
|
||||||
|
}
|
||||||
|
return Math.max(parent.height, t + pad)
|
||||||
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: textItem
|
id: textItem
|
||||||
x: page.previewMarginL >= 0 ? page.previewMarginL : appSettings.readerMargin
|
x: page.previewMarginL >= 0 ? page.previewMarginL : appSettings.readerMargin
|
||||||
y: page.previewMarginT >= 0 ? page.previewMarginT : appSettings.readerMarginV
|
|
||||||
width: parent.width - 2 * (page.previewMarginL >= 0
|
width: parent.width - 2 * (page.previewMarginL >= 0
|
||||||
? page.previewMarginL
|
? page.previewMarginL
|
||||||
: appSettings.readerMargin)
|
: appSettings.readerMargin)
|
||||||
@@ -270,25 +287,20 @@ Page {
|
|||||||
function readScrollPosition() {
|
function readScrollPosition() {
|
||||||
if (!page.ready)
|
if (!page.ready)
|
||||||
return
|
return
|
||||||
// passo di pagina = viewport - 2*margine verticale: ogni pagina mostra
|
// passo di pagina = altezza della finestra di lettura (i margini
|
||||||
// il testo DENTRO i margini (niente righe a filo del bordo)
|
// alto/basso sono FUORI dal Flickable: nessun testo li attraversa)
|
||||||
var step = Math.max(1, innerFlick.height - 2 * page.marginV())
|
var step = Math.max(1, innerFlick.height)
|
||||||
var max = Math.max(0, innerFlick.contentHeight - innerFlick.height)
|
var max = Math.max(0, innerFlick.contentHeight - innerFlick.height)
|
||||||
var y = Math.max(0, Math.min(max, innerFlick.contentY))
|
var y = Math.max(0, Math.min(max, innerFlick.contentY))
|
||||||
var len = book.chapterTextLength(page.currentChapter)
|
var len = book.chapterTextLength(page.currentChapter)
|
||||||
page.chapterFraction = max > 0 ? y / max : 0
|
|
||||||
// posizione per caratteri (stima dalla frazione: stabile col reflow)
|
// posizione per caratteri (stima dalla frazione: stabile col reflow)
|
||||||
page.charOffset = Math.round(page.chapterFraction * len)
|
page.charOffset = Math.round((max > 0 ? y / max : 0) * len)
|
||||||
page.pageIndex = Math.floor(y / step) + 1
|
page.pageIndex = Math.floor(y / step) + 1
|
||||||
page.pageCount = Math.max(1, Math.ceil(max / step) + 1)
|
page.pageCount = Math.max(1, Math.ceil(max / step) + 1)
|
||||||
|
updateCoverState()
|
||||||
maybeSave(false)
|
maybeSave(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// margine verticale effettivo (anteprima dialog oppure impostazioni)
|
|
||||||
function marginV() {
|
|
||||||
return page.previewMarginT >= 0 ? page.previewMarginT : appSettings.readerMarginV
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeSave(force) {
|
function maybeSave(force) {
|
||||||
if (bookId <= 0)
|
if (bookId <= 0)
|
||||||
return
|
return
|
||||||
@@ -308,7 +320,6 @@ Page {
|
|||||||
return
|
return
|
||||||
if (index !== page.currentChapter) {
|
if (index !== page.currentChapter) {
|
||||||
maybeSave(true)
|
maybeSave(true)
|
||||||
page.chapterFraction = 0
|
|
||||||
page.charOffset = 0
|
page.charOffset = 0
|
||||||
page.lastSavedAbs = -1
|
page.lastSavedAbs = -1
|
||||||
}
|
}
|
||||||
@@ -335,13 +346,12 @@ Page {
|
|||||||
restoreTimer.start()
|
restoreTimer.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pagina successiva: una pagina di testo = viewport meno i margini verticali
|
// pagina successiva: una pagina di testo = altezza della finestra di
|
||||||
// (così ogni pagina ha il testo dentro i margini, non a filo del bordo);
|
// lettura; a fine capitolo passa al successivo
|
||||||
// a fine capitolo passa al successivo
|
|
||||||
function nextPage() {
|
function nextPage() {
|
||||||
if (!page.ready)
|
if (!page.ready)
|
||||||
return
|
return
|
||||||
var step = Math.max(1, innerFlick.height - 2 * page.marginV())
|
var step = Math.max(1, innerFlick.height)
|
||||||
var max = Math.max(0, innerFlick.contentHeight - innerFlick.height)
|
var max = Math.max(0, innerFlick.contentHeight - innerFlick.height)
|
||||||
var y = innerFlick.contentY
|
var y = innerFlick.contentY
|
||||||
if (y >= max - 2) {
|
if (y >= max - 2) {
|
||||||
@@ -360,7 +370,7 @@ Page {
|
|||||||
function prevPage() {
|
function prevPage() {
|
||||||
if (!page.ready)
|
if (!page.ready)
|
||||||
return
|
return
|
||||||
var step = Math.max(1, innerFlick.height - 2 * page.marginV())
|
var step = Math.max(1, innerFlick.height)
|
||||||
var y = innerFlick.contentY
|
var y = innerFlick.contentY
|
||||||
if (y > 1) {
|
if (y > 1) {
|
||||||
innerFlick.contentY = Math.max(0, y - step)
|
innerFlick.contentY = Math.max(0, y - step)
|
||||||
@@ -368,7 +378,6 @@ Page {
|
|||||||
} else if (page.currentChapter > 0) {
|
} else if (page.currentChapter > 0) {
|
||||||
maybeSave(true)
|
maybeSave(true)
|
||||||
page.currentChapter -= 1
|
page.currentChapter -= 1
|
||||||
page.chapterFraction = 0
|
|
||||||
page.charOffset = 0
|
page.charOffset = 0
|
||||||
page.lastSavedAbs = -1
|
page.lastSavedAbs = -1
|
||||||
// sentinella "fine capitolo": va oltre la lunghezza, così
|
// sentinella "fine capitolo": va oltre la lunghezza, così
|
||||||
@@ -383,6 +392,17 @@ Page {
|
|||||||
return Math.round(16 * page.fontScale * 100) / 100
|
return Math.round(16 * page.fontScale * 100) / 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// aggiorna lo stato per la cover (titolo + capitolo + percentuale)
|
||||||
|
function updateCoverState() {
|
||||||
|
if (book.chapterCount <= 0)
|
||||||
|
return
|
||||||
|
var pct = Math.round(page.overallProgress() * 100)
|
||||||
|
readerState.setReading(book.title(),
|
||||||
|
(page.currentChapter + 1) + "/" + book.chapterCount
|
||||||
|
+ " · " + book.chapterTitle(page.currentChapter)
|
||||||
|
+ " · " + pct + "%")
|
||||||
|
}
|
||||||
|
|
||||||
function showChapterList() {
|
function showChapterList() {
|
||||||
var dialog = pageStack.push(Qt.resolvedUrl("ChapterListDialog.qml"),
|
var dialog = pageStack.push(Qt.resolvedUrl("ChapterListDialog.qml"),
|
||||||
{ model: book.chapterList() })
|
{ model: book.chapterList() })
|
||||||
@@ -424,11 +444,17 @@ Page {
|
|||||||
page.currentChapter = ch
|
page.currentChapter = ch
|
||||||
page.pendingRestore = true
|
page.pendingRestore = true
|
||||||
page.restoreFrac = off >= 0 ? offsetToFrac(off, ch) : 0.0
|
page.restoreFrac = off >= 0 ? offsetToFrac(off, ch) : 0.0
|
||||||
|
updateCoverState()
|
||||||
restoreTimer.start()
|
restoreTimer.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: readerState.clearReading()
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (status === PageStatus.Inactive) {
|
if (status === PageStatus.Active) {
|
||||||
|
if (page.ready && book.chapterCount > 0)
|
||||||
|
updateCoverState()
|
||||||
|
} else if (status === PageStatus.Inactive) {
|
||||||
maybeSave(true)
|
maybeSave(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import Sailfish.Silica 1.0
|
|||||||
Page {
|
Page {
|
||||||
id: page
|
id: page
|
||||||
|
|
||||||
|
allowedOrientations: Orientation.Portrait
|
||||||
|
|
||||||
SilicaFlickable {
|
SilicaFlickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
contentHeight: column.height
|
contentHeight: column.height
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import Sailfish.Silica 1.0
|
|||||||
Page {
|
Page {
|
||||||
id: page
|
id: page
|
||||||
|
|
||||||
|
allowedOrientations: Orientation.Portrait
|
||||||
|
|
||||||
property string testResult: ""
|
property string testResult: ""
|
||||||
property bool testOk: false
|
property bool testOk: false
|
||||||
property bool testing: false
|
property bool testing: false
|
||||||
@@ -58,32 +60,6 @@ Page {
|
|||||||
onCheckedChanged: appSettings.ignoreSslErrors = checked
|
onCheckedChanged: appSettings.ignoreSslErrors = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
SectionHeader { text: qsTr("Reader") }
|
|
||||||
|
|
||||||
Slider {
|
|
||||||
id: marginSlider
|
|
||||||
width: parent.width
|
|
||||||
label: qsTr("Side margins")
|
|
||||||
minimumValue: 0
|
|
||||||
maximumValue: 80
|
|
||||||
stepSize: 4
|
|
||||||
value: appSettings.readerMargin
|
|
||||||
valueText: value + " px"
|
|
||||||
onValueChanged: appSettings.readerMargin = value
|
|
||||||
}
|
|
||||||
|
|
||||||
Slider {
|
|
||||||
id: marginVSlider
|
|
||||||
width: parent.width
|
|
||||||
label: qsTr("Top and bottom margin")
|
|
||||||
minimumValue: 0
|
|
||||||
maximumValue: 80
|
|
||||||
stepSize: 4
|
|
||||||
value: appSettings.readerMarginV
|
|
||||||
valueText: value + " px"
|
|
||||||
onValueChanged: appSettings.readerMarginV = value
|
|
||||||
}
|
|
||||||
|
|
||||||
SectionHeader { text: qsTr("Connection") }
|
SectionHeader { text: qsTr("Connection") }
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@@ -171,11 +147,18 @@ Page {
|
|||||||
if (base.indexOf("http://") !== 0 && base.indexOf("https://") !== 0)
|
if (base.indexOf("http://") !== 0 && base.indexOf("https://") !== 0)
|
||||||
base = "https://" + base
|
base = "https://" + base
|
||||||
appSettings.serverUrl = base
|
appSettings.serverUrl = base
|
||||||
|
appSettings.flush() // i campi di testo scrivono senza sync a ogni tasto
|
||||||
page.testing = true
|
page.testing = true
|
||||||
page.testResult = qsTr("Testing…")
|
page.testResult = qsTr("Testing…")
|
||||||
apiClient.getFeed(base + "/opds")
|
apiClient.getFeed(base + "/opds")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uscita dalla pagina: scrivi su disco le modifiche in sospeso
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === PageStatus.Inactive)
|
||||||
|
appSettings.flush()
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: apiClient
|
target: apiClient
|
||||||
onFeedReady: {
|
onFeedReady: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Name: harbour-calibreweb
|
Name: harbour-calibreweb
|
||||||
Summary: Client OPDS per Calibre Web
|
Summary: Client OPDS per Calibre Web
|
||||||
Version: 0.4.7
|
Version: 0.4.11
|
||||||
Release: 1
|
Release: 1
|
||||||
Group: Qt/Qt
|
Group: Qt/Qt
|
||||||
License: MIT
|
License: MIT
|
||||||
@@ -24,6 +24,41 @@ reflow del testo, margini configurabili e posizione salvata
|
|||||||
in locale.
|
in locale.
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Aug 21 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.4.11-1
|
||||||
|
- Rimossi gli slider margini dalle Impostazioni: i margini si regolano solo dal
|
||||||
|
lettore (Margini del testo nel PullDownMenu)
|
||||||
|
- Margini alto/basso STRUTTURALI: la finestra di lettura vive dentro i margini
|
||||||
|
(top/bottom margin del Flickable): il testo non puo' uscire dai bordi e i
|
||||||
|
margini sono identici su ogni pagina (via le fasce colorate della 0.4.9/0.4.10)
|
||||||
|
|
||||||
|
* Fri Aug 21 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.4.10-1
|
||||||
|
- Fix margini lettore (0.4.9): sfondo di lettura UNIFORME nel lettore (colore
|
||||||
|
pieno per tema, niente gradiente di sistema). Le fasce dei margini usano lo
|
||||||
|
stesso identico colore dello sfondo: margine visibile su ogni pagina, senza
|
||||||
|
bande stonate e senza effetto "fascia che copre il testo"
|
||||||
|
|
||||||
|
* Fri Aug 21 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.4.9-1
|
||||||
|
- Margini verticali su OGNI schermata del lettore (non solo a inizio capitolo):
|
||||||
|
due fasce opache (colore di sfondo) sopra e sotto la viewport coprono il testo
|
||||||
|
a cavallo dei bordi; il padding inferiore del contenitore include il deficit
|
||||||
|
dell'ultima pagina, cosi' max-scroll e' un multiplo esatto del passo e
|
||||||
|
l'ultima pagina non ripete righe della penultima. Anteprima live dal dialog
|
||||||
|
margini inclusa
|
||||||
|
|
||||||
|
* Fri Aug 21 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.4.8-1
|
||||||
|
- Best practice audit: orientazioni per pagina (finestra senza vincoli, il
|
||||||
|
lettore ora ruota davvero in landscape; pagine di lista in portrait;
|
||||||
|
rimossa la proprieta' privata _defaultPageOrientations)
|
||||||
|
- Licenza MIT installata nel pacchetto (/usr/share/licenses/harbour-calibreweb)
|
||||||
|
- EPUB: lunghezze testo LAZY per capitolo (l'apertura non parse piu' tutto il
|
||||||
|
libro: niente freeze sui libri grossi; cache al primo accesso)
|
||||||
|
- Cover dinamica: con un libro aperto mostra titolo e posizione di lettura
|
||||||
|
(ReaderState esposto come context property, aggiornato dal ReaderPage)
|
||||||
|
- Impostazioni: niente sync su disco a ogni tasto nei campi di testo
|
||||||
|
(flush() all'uscita della pagina e prima del test connessione)
|
||||||
|
- Pulizia: rimossa proprieta' morta chapterFraction, commenti obsoleti
|
||||||
|
aggiornati (settings.h, .pro)
|
||||||
|
|
||||||
* Fri Aug 21 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.4.7-1
|
* Fri Aug 21 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.4.7-1
|
||||||
- Fix margini lettore: il passo di pagina era la viewport intera, quindi il
|
- Fix margini lettore: il passo di pagina era la viewport intera, quindi il
|
||||||
testo scorreva a filo dei bordi e i margini verticali si vedevano solo
|
testo scorreva a filo dei bordi e i margini verticali si vedevano solo
|
||||||
@@ -199,6 +234,19 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/%{name}.desktop || tr
|
|||||||
mkdir -p %{buildroot}%{_datadir}/%{name}/translations
|
mkdir -p %{buildroot}%{_datadir}/%{name}/translations
|
||||||
install -m 0644 %{_builddir}/%{name}_it.qm %{buildroot}%{_datadir}/%{name}/translations/
|
install -m 0644 %{_builddir}/%{name}_it.qm %{buildroot}%{_datadir}/%{name}/translations/
|
||||||
|
|
||||||
|
# MIT license: installed in the package (Harbour best practice).
|
||||||
|
mkdir -p %{buildroot}%{_datadir}/licenses/%{name}
|
||||||
|
if [ -f %{_builddir}/../%{name}-%{version}/LICENSE ]; then
|
||||||
|
install -m 0644 %{_builddir}/../%{name}-%{version}/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/
|
||||||
|
elif [ -f %{_builddir}/%{name}-%{version}/LICENSE ]; then
|
||||||
|
install -m 0644 %{_builddir}/%{name}-%{version}/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/
|
||||||
|
elif [ -f %{_builddir}/LICENSE ]; then
|
||||||
|
install -m 0644 %{_builddir}/LICENSE %{buildroot}%{_datadir}/licenses/%{name}/
|
||||||
|
else
|
||||||
|
echo "ERROR: LICENSE not found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
%files
|
%files
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
%{_bindir}/%{name}
|
%{_bindir}/%{name}
|
||||||
@@ -209,3 +257,4 @@ install -m 0644 %{_builddir}/%{name}_it.qm %{buildroot}%{_datadir}/%{name}/trans
|
|||||||
%{_datadir}/icons/hicolor/172x172/apps/%{name}.png
|
%{_datadir}/icons/hicolor/172x172/apps/%{name}.png
|
||||||
%{_datadir}/%{name}/qml
|
%{_datadir}/%{name}/qml
|
||||||
%{_datadir}/%{name}/translations/%{name}*.qm
|
%{_datadir}/%{name}/translations/%{name}*.qm
|
||||||
|
%{_datadir}/licenses/%{name}/LICENSE
|
||||||
|
|||||||
+20
-10
@@ -490,8 +490,14 @@ bool EpubBook::open(const QString &epubPath)
|
|||||||
if (!parseOpf())
|
if (!parseOpf())
|
||||||
return false;
|
return false;
|
||||||
const bool ok = !m_chapters.isEmpty();
|
const bool ok = !m_chapters.isEmpty();
|
||||||
if (ok)
|
if (ok) {
|
||||||
computeTextLengths();
|
// lunghezze testo LAZY: -1 = da calcolare al primo accesso. L'apertura
|
||||||
|
// resta veloce anche per libri grossi (niente parse di tutti i capitoli).
|
||||||
|
m_textLengths.clear();
|
||||||
|
m_textLengths.reserve(m_chapters.size());
|
||||||
|
for (int i = 0; i < m_chapters.size(); ++i)
|
||||||
|
m_textLengths.append(-1);
|
||||||
|
}
|
||||||
emit changed();
|
emit changed();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
@@ -763,24 +769,26 @@ QString EpubBook::chapterRichText(int index) const
|
|||||||
return htmlToRichText(html);
|
return htmlToRichText(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpubBook::computeTextLengths()
|
// calcola (e cache) la lunghezza del capitolo index se non ancora nota
|
||||||
|
void EpubBook::ensureTextLength(int index) const
|
||||||
{
|
{
|
||||||
m_textLengths.clear();
|
if (index < 0 || index >= m_textLengths.size() || m_textLengths.at(index) >= 0)
|
||||||
for (const EpubChapter &c : m_chapters) {
|
return;
|
||||||
|
const EpubChapter &c = m_chapters.at(index);
|
||||||
QFile f(c.path);
|
QFile f(c.path);
|
||||||
if (!f.open(QIODevice::ReadOnly)) {
|
if (!f.open(QIODevice::ReadOnly)) {
|
||||||
m_textLengths.append(0);
|
m_textLengths[index] = 0;
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
const QString html = QString::fromUtf8(f.readAll());
|
const QString html = QString::fromUtf8(f.readAll());
|
||||||
m_textLengths.append(plainTextLength(html));
|
m_textLengths[index] = plainTextLength(html);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int EpubBook::chapterTextLength(int index) const
|
int EpubBook::chapterTextLength(int index) const
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= m_textLengths.size())
|
if (index < 0 || index >= m_textLengths.size())
|
||||||
return 0;
|
return 0;
|
||||||
|
ensureTextLength(index);
|
||||||
return m_textLengths.at(index);
|
return m_textLengths.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,8 +796,10 @@ qint64 EpubBook::chapterStartChar(int index) const
|
|||||||
{
|
{
|
||||||
qint64 start = 0;
|
qint64 start = 0;
|
||||||
const int end = qMin(index, m_textLengths.size());
|
const int end = qMin(index, m_textLengths.size());
|
||||||
for (int i = 0; i < end; ++i)
|
for (int i = 0; i < end; ++i) {
|
||||||
|
ensureTextLength(i);
|
||||||
start += m_textLengths.at(i);
|
start += m_textLengths.at(i);
|
||||||
|
}
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -66,15 +66,17 @@ private:
|
|||||||
void parseTocEpub3(const QString &navPath);
|
void parseTocEpub3(const QString &navPath);
|
||||||
void parseTocEpub2(const QString &ncxPath);
|
void parseTocEpub2(const QString &ncxPath);
|
||||||
QString absolutePath(const QString &relativeToOpfDir) const;
|
QString absolutePath(const QString &relativeToOpfDir) const;
|
||||||
// lunghezze in caratteri del testo visibile, calcolate all'open
|
// calcola (e cache) la lunghezza in caratteri del capitolo al primo accesso
|
||||||
void computeTextLengths();
|
void ensureTextLength(int index) const;
|
||||||
|
|
||||||
QString m_baseDir;
|
QString m_baseDir;
|
||||||
QString m_opfDir;
|
QString m_opfDir;
|
||||||
QString m_title;
|
QString m_title;
|
||||||
QString m_author;
|
QString m_author;
|
||||||
QList<EpubChapter> m_chapters;
|
QList<EpubChapter> m_chapters;
|
||||||
QList<int> m_textLengths;
|
// lunghezze testo per capitolo, calcolate LAZY al primo accesso
|
||||||
|
// (-1 = non ancora calcolata); mutable per la cache da metodi const
|
||||||
|
mutable QList<int> m_textLengths;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // EPUB_H
|
#endif // EPUB_H
|
||||||
|
|||||||
+6
-2
@@ -10,6 +10,7 @@
|
|||||||
#include "apiclient.h"
|
#include "apiclient.h"
|
||||||
#include "downloader.h"
|
#include "downloader.h"
|
||||||
#include "epub.h"
|
#include "epub.h"
|
||||||
|
#include "readerstate.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
Q_DECL_EXPORT int main(int argc, char *argv[])
|
Q_DECL_EXPORT int main(int argc, char *argv[])
|
||||||
@@ -22,7 +23,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||||||
app->setOrganizationName(QStringLiteral("harbour"));
|
app->setOrganizationName(QStringLiteral("harbour"));
|
||||||
app->setApplicationName(QStringLiteral("calibreweb"));
|
app->setApplicationName(QStringLiteral("calibreweb"));
|
||||||
// versione mostrata nelle impostazioni (tenere allineata allo spec)
|
// versione mostrata nelle impostazioni (tenere allineata allo spec)
|
||||||
app->setApplicationVersion(QStringLiteral("0.4.7"));
|
app->setApplicationVersion(QStringLiteral("0.4.11"));
|
||||||
|
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
|
||||||
@@ -56,16 +57,19 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QQuickView *view = SailfishApp::createView();
|
QQuickView *view = SailfishApp::createView();
|
||||||
|
|
||||||
qDebug() << "harbour-calibreweb v0.4.7 build" << __DATE__ << __TIME__;
|
qDebug() << "harbour-calibreweb v0.4.11 build" << __DATE__ << __TIME__;
|
||||||
|
|
||||||
qmlRegisterType<EpubBook>("harbour.calibreweb", 1, 0, "EpubBook");
|
qmlRegisterType<EpubBook>("harbour.calibreweb", 1, 0, "EpubBook");
|
||||||
|
|
||||||
ApiClient apiClient(&settings);
|
ApiClient apiClient(&settings);
|
||||||
Downloader downloader(&settings);
|
Downloader downloader(&settings);
|
||||||
|
// stato di lettura corrente (per la cover quando l'app va in background)
|
||||||
|
ReaderState readerState(&settings);
|
||||||
|
|
||||||
view->rootContext()->setContextProperty(QStringLiteral("appSettings"), &settings);
|
view->rootContext()->setContextProperty(QStringLiteral("appSettings"), &settings);
|
||||||
view->rootContext()->setContextProperty(QStringLiteral("apiClient"), &apiClient);
|
view->rootContext()->setContextProperty(QStringLiteral("apiClient"), &apiClient);
|
||||||
view->rootContext()->setContextProperty(QStringLiteral("downloader"), &downloader);
|
view->rootContext()->setContextProperty(QStringLiteral("downloader"), &downloader);
|
||||||
|
view->rootContext()->setContextProperty(QStringLiteral("readerState"), &readerState);
|
||||||
view->rootContext()->setContextProperty(QStringLiteral("appVersion"),
|
view->rootContext()->setContextProperty(QStringLiteral("appVersion"),
|
||||||
QCoreApplication::applicationVersion());
|
QCoreApplication::applicationVersion());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include "readerstate.h"
|
||||||
|
|
||||||
|
ReaderState::ReaderState(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_active(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReaderState::setReading(const QString &title, const QString &subtitle)
|
||||||
|
{
|
||||||
|
if (m_active && m_title == title && m_subtitle == subtitle)
|
||||||
|
return;
|
||||||
|
m_active = true;
|
||||||
|
m_title = title;
|
||||||
|
m_subtitle = subtitle;
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReaderState::clearReading()
|
||||||
|
{
|
||||||
|
if (!m_active)
|
||||||
|
return;
|
||||||
|
m_active = false;
|
||||||
|
m_title.clear();
|
||||||
|
m_subtitle.clear();
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef READERSTATE_H
|
||||||
|
#define READERSTATE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
// Stato di lettura corrente, esposto come context property "readerState":
|
||||||
|
// la cover mostra il libro aperto (titolo + posizione) invece dell'icona fissa.
|
||||||
|
class ReaderState : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool active READ active NOTIFY changed)
|
||||||
|
Q_PROPERTY(QString title READ title NOTIFY changed)
|
||||||
|
Q_PROPERTY(QString subtitle READ subtitle NOTIFY changed)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ReaderState(QObject *parent = 0);
|
||||||
|
|
||||||
|
bool active() const { return m_active; }
|
||||||
|
QString title() const { return m_title; }
|
||||||
|
QString subtitle() const { return m_subtitle; }
|
||||||
|
|
||||||
|
// aggiorna lo stato quando il lettore mostra un libro (chiamata dal QML)
|
||||||
|
Q_INVOKABLE void setReading(const QString &title, const QString &subtitle);
|
||||||
|
// il libro è stato chiuso: la cover torna all'icona predefinita
|
||||||
|
Q_INVOKABLE void clearReading();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void changed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_active;
|
||||||
|
QString m_title;
|
||||||
|
QString m_subtitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // READERSTATE_H
|
||||||
+7
-5
@@ -115,9 +115,9 @@ void Settings::setServerUrl(const QString &value)
|
|||||||
{
|
{
|
||||||
if (value == serverUrl())
|
if (value == serverUrl())
|
||||||
return;
|
return;
|
||||||
|
// niente sync a ogni tasto (flash wear): il flush avviene all'uscita
|
||||||
|
// dalle impostazioni o prima delle richieste di rete (vedi flush())
|
||||||
m_settings.setValue(QStringLiteral("serverUrl"), value);
|
m_settings.setValue(QStringLiteral("serverUrl"), value);
|
||||||
m_settings.sync(); // scrivi subito su disco: l'app può crashare alla chiusura (WebView)
|
|
||||||
openPermissions();
|
|
||||||
emit serverUrlChanged();
|
emit serverUrlChanged();
|
||||||
emit baseUrlChanged();
|
emit baseUrlChanged();
|
||||||
}
|
}
|
||||||
@@ -132,8 +132,6 @@ void Settings::setUsername(const QString &value)
|
|||||||
if (value == username())
|
if (value == username())
|
||||||
return;
|
return;
|
||||||
m_settings.setValue(QStringLiteral("username"), value);
|
m_settings.setValue(QStringLiteral("username"), value);
|
||||||
m_settings.sync();
|
|
||||||
openPermissions();
|
|
||||||
emit usernameChanged();
|
emit usernameChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,9 +145,13 @@ void Settings::setPassword(const QString &value)
|
|||||||
if (value == password())
|
if (value == password())
|
||||||
return;
|
return;
|
||||||
m_settings.setValue(QStringLiteral("password"), value);
|
m_settings.setValue(QStringLiteral("password"), value);
|
||||||
|
emit passwordChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::flush()
|
||||||
|
{
|
||||||
m_settings.sync();
|
m_settings.sync();
|
||||||
openPermissions();
|
openPermissions();
|
||||||
emit passwordChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::ignoreSslErrors() const
|
bool Settings::ignoreSslErrors() const
|
||||||
|
|||||||
+14
-9
@@ -55,6 +55,10 @@ public:
|
|||||||
|
|
||||||
QString downloadDir() const;
|
QString downloadDir() const;
|
||||||
|
|
||||||
|
// scrive subito su disco le modifiche in sospeso (i setter di testo non
|
||||||
|
// fanno sync a ogni tasto: chiamare questo all'uscita dalle impostazioni)
|
||||||
|
Q_INVOKABLE void flush();
|
||||||
|
|
||||||
// Header "Authorization: Basic ..." pronto da usare, vuoto se utente non impostato
|
// Header "Authorization: Basic ..." pronto da usare, vuoto se utente non impostato
|
||||||
QString authHeader() const;
|
QString authHeader() const;
|
||||||
|
|
||||||
@@ -80,17 +84,18 @@ signals:
|
|||||||
void baseUrlChanged();
|
void baseUrlChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Path fisso condiviso: l'app deve leggere/scrivere SEMPRE lo stesso file,
|
// Path canonico per la sandbox SailJail (Sailfish 4.4+/5.x): con org/app
|
||||||
// sia avviata dal launcher (utente nemo) sia da SSH di debug (defaultuser).
|
// impostati (harbour/calibreweb, vedi main.cpp e X-Sailjail nel desktop
|
||||||
// Con QSettings("harbour","calibreweb") il file dipende dalla HOME
|
// file), AppConfigLocation = ~/.config/harbour/calibreweb, l'unica zona
|
||||||
// dell'utente che avvia -> configurazione invisibile dall'altro utente.
|
// config PERSISTENTE della sandbox (senza profilo la sandbox la nasconde).
|
||||||
static QString configPath();
|
static QString configPath();
|
||||||
// Importa i valori dalla vecchia config per-utente (~/.config/harbour/...) se
|
// Importa i valori dal vecchio file per-utente (~/.config/harbour/...) se
|
||||||
// il file condiviso e' vuoto: copre la configurazione fatta prima del fix.
|
// il file canonico e' vuoto: copre la configurazione fatta prima del fix.
|
||||||
void migrateIfNeeded();
|
void migrateIfNeeded();
|
||||||
// Apre i permessi della catena /home/nemo/.config/harbour e del file:
|
// Apre i permessi della catena di directory del file di configurazione:
|
||||||
// il launcher gira come nemo (home 700), il debug SSH come defaultuser —
|
// l'avvio dal launcher e quello da SSH (defaultuser) devono poter
|
||||||
// entrambi devono poter leggere/scrivere lo stesso file di configurazione.
|
// leggere/scrivere lo stesso file. Efficace quando l'app gira come
|
||||||
|
// owner della home (dal launcher).
|
||||||
void openPermissions();
|
void openPermissions();
|
||||||
|
|
||||||
QSettings m_settings;
|
QSettings m_settings;
|
||||||
|
|||||||
@@ -1,29 +1,30 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
<TS version="2.1" language="it_IT">
|
<TS version="2.1" language="it_IT">
|
||||||
<context>
|
<context>
|
||||||
<name>BookDetailPage</name>
|
<name>BookDetailPage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/BookDetailPage.qml" line="36" />
|
<location filename="../qml/pages/BookDetailPage.qml" line="38"/>
|
||||||
<source>Read (EPUB)</source>
|
<source>Read (EPUB)</source>
|
||||||
<translation>Leggi (EPUB)</translation>
|
<translation>Leggi (EPUB)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/BookDetailPage.qml" line="60" />
|
<location filename="../qml/pages/BookDetailPage.qml" line="62"/>
|
||||||
<source>Formats</source>
|
<source>Formats</source>
|
||||||
<translation>Formati</translation>
|
<translation>Formati</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/BookDetailPage.qml" line="112" />
|
<location filename="../qml/pages/BookDetailPage.qml" line="114"/>
|
||||||
<source>Downloading…</source>
|
<source>Downloading…</source>
|
||||||
<translation>Download in corso…</translation>
|
<translation>Download in corso…</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/BookDetailPage.qml" line="261" />
|
<location filename="../qml/pages/BookDetailPage.qml" line="263"/>
|
||||||
<source>Saved to </source>
|
<source>Saved to </source>
|
||||||
<translation>Salvato in </translation>
|
<translation>Salvato in </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/BookDetailPage.qml" line="266" />
|
<location filename="../qml/pages/BookDetailPage.qml" line="268"/>
|
||||||
<source>Error: </source>
|
<source>Error: </source>
|
||||||
<translation>Errore: </translation>
|
<translation>Errore: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>CoverPage</name>
|
<name>CoverPage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/cover/CoverPage.qml" line="18" />
|
<location filename="../qml/cover/CoverPage.qml" line="22"/>
|
||||||
<source>Calibre Web</source>
|
<source>Calibre Web</source>
|
||||||
<translation>Calibre Web</translation>
|
<translation>Calibre Web</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -52,18 +53,18 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>FeedPage</name>
|
<name>FeedPage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/FeedPage.qml" line="91" />
|
<location filename="../qml/pages/FeedPage.qml" line="93"/>
|
||||||
<source>Refresh</source>
|
<source>Refresh</source>
|
||||||
<translation>Aggiorna</translation>
|
<translation>Aggiorna</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/FeedPage.qml" line="95" />
|
<location filename="../qml/pages/FeedPage.qml" line="97"/>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
<translation>Impostazioni</translation>
|
<translation>Impostazioni</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/FeedPage.qml" line="102" />
|
<location filename="../qml/pages/FeedPage.qml" line="104"/>
|
||||||
<location filename="../qml/pages/FeedPage.qml" line="171" />
|
<location filename="../qml/pages/FeedPage.qml" line="173"/>
|
||||||
<source>No results</source>
|
<source>No results</source>
|
||||||
<translation>Nessun risultato</translation>
|
<translation>Nessun risultato</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -71,37 +72,37 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>MainPage</name>
|
<name>MainPage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/MainPage.qml" line="13" />
|
<location filename="../qml/pages/MainPage.qml" line="15"/>
|
||||||
<source>Calibre Web</source>
|
<source>Calibre Web</source>
|
||||||
<translation>Calibre Web</translation>
|
<translation>Calibre Web</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/MainPage.qml" line="43" />
|
<location filename="../qml/pages/MainPage.qml" line="45"/>
|
||||||
<source>Search</source>
|
<source>Search</source>
|
||||||
<translation>Cerca</translation>
|
<translation>Cerca</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/MainPage.qml" line="47" />
|
<location filename="../qml/pages/MainPage.qml" line="49"/>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
<translation>Impostazioni</translation>
|
<translation>Impostazioni</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/MainPage.qml" line="51" />
|
<location filename="../qml/pages/MainPage.qml" line="53"/>
|
||||||
<source>Refresh</source>
|
<source>Refresh</source>
|
||||||
<translation>Aggiorna</translation>
|
<translation>Aggiorna</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/MainPage.qml" line="60" />
|
<location filename="../qml/pages/MainPage.qml" line="62"/>
|
||||||
<source>Open the settings and enter your Calibre Web address</source>
|
<source>Open the settings and enter your Calibre Web address</source>
|
||||||
<translation>Apri le impostazioni e inserisci l'indirizzo del tuo Calibre Web</translation>
|
<translation>Apri le impostazioni e inserisci l'indirizzo del tuo Calibre Web</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/MainPage.qml" line="77" />
|
<location filename="../qml/pages/MainPage.qml" line="79"/>
|
||||||
<source>Server not configured</source>
|
<source>Server not configured</source>
|
||||||
<translation>Server non configurato</translation>
|
<translation>Server non configurato</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/MainPage.qml" line="100" />
|
<location filename="../qml/pages/MainPage.qml" line="102"/>
|
||||||
<source>No sections found in the OPDS feed</source>
|
<source>No sections found in the OPDS feed</source>
|
||||||
<translation>Nessuna sezione trovata nel feed OPDS</translation>
|
<translation>Nessuna sezione trovata nel feed OPDS</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -137,37 +138,27 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>ReaderPage</name>
|
<name>ReaderPage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/ReaderPage.qml" line="35" />
|
<location filename="../qml/pages/ReaderPage.qml" line="58"/>
|
||||||
<source>Table of contents</source>
|
<source>Table of contents</source>
|
||||||
<translation>Indice capitoli</translation>
|
<translation>Indice capitoli</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/ReaderPage.qml" line="39" />
|
<location filename="../qml/pages/ReaderPage.qml" line="62"/>
|
||||||
<source>Text margins</source>
|
<source>Text margins</source>
|
||||||
<translation>Margini del testo</translation>
|
<translation>Margini del testo</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/ReaderPage.qml" line="53" />
|
<location filename="../qml/pages/ReaderPage.qml" line="77"/>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
<translation>Impostazioni</translation>
|
<translation>Impostazioni</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/ReaderPage.qml" line="57" />
|
<location filename="../qml/pages/ReaderPage.qml" line="81"/>
|
||||||
<source>Close book</source>
|
<source>Close book</source>
|
||||||
<translation>Chiudi libro</translation>
|
<translation>Chiudi libro</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/ReaderPage.qml" line="170" />
|
<location filename="../qml/pages/ReaderPage.qml" line="427"/>
|
||||||
<source>The reader is not responding (WebView). The EPUB file may be damaged.</source>
|
|
||||||
<translation>Il lettore non risponde (WebView). Il file EPUB potrebbe essere danneggiato.</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../qml/pages/ReaderPage.qml" line="293" />
|
|
||||||
<source>Chapter unreadable</source>
|
|
||||||
<translation>Capitolo non leggibile</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../qml/pages/ReaderPage.qml" line="342" />
|
|
||||||
<source>Unable to open the EPUB file</source>
|
<source>Unable to open the EPUB file</source>
|
||||||
<translation>Impossibile aprire il libro EPUB</translation>
|
<translation>Impossibile aprire il libro EPUB</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -175,23 +166,23 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>SearchPage</name>
|
<name>SearchPage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SearchPage.qml" line="15" />
|
<location filename="../qml/pages/SearchPage.qml" line="17"/>
|
||||||
<location filename="../qml/pages/SearchPage.qml" line="27" />
|
<location filename="../qml/pages/SearchPage.qml" line="29"/>
|
||||||
<source>Search</source>
|
<source>Search</source>
|
||||||
<translation>Cerca</translation>
|
<translation>Cerca</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SearchPage.qml" line="20" />
|
<location filename="../qml/pages/SearchPage.qml" line="22"/>
|
||||||
<source>Title or author…</source>
|
<source>Title or author…</source>
|
||||||
<translation>Titolo o autore…</translation>
|
<translation>Titolo o autore…</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SearchPage.qml" line="35" />
|
<location filename="../qml/pages/SearchPage.qml" line="37"/>
|
||||||
<source>Search uses the server engine (OPDS search).</source>
|
<source>Search uses the server engine (OPDS search).</source>
|
||||||
<translation>La ricerca usa il motore del server (OPDS search).</translation>
|
<translation>La ricerca usa il motore del server (OPDS search).</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SearchPage.qml" line="50" />
|
<location filename="../qml/pages/SearchPage.qml" line="52"/>
|
||||||
<source>Results: </source>
|
<source>Results: </source>
|
||||||
<translation>Risultati: </translation>
|
<translation>Risultati: </translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -199,98 +190,83 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>SettingsPage</name>
|
<name>SettingsPage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="20" />
|
<location filename="../qml/pages/SettingsPage.qml" line="22"/>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
<translation>Impostazioni</translation>
|
<translation>Impostazioni</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="22" />
|
<location filename="../qml/pages/SettingsPage.qml" line="24"/>
|
||||||
<source>Server</source>
|
<source>Server</source>
|
||||||
<translation>Server</translation>
|
<translation>Server</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="28" />
|
<location filename="../qml/pages/SettingsPage.qml" line="30"/>
|
||||||
<source>Calibre Web address</source>
|
<source>Calibre Web address</source>
|
||||||
<translation>Indirizzo Calibre Web</translation>
|
<translation>Indirizzo Calibre Web</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="39" />
|
<location filename="../qml/pages/SettingsPage.qml" line="41"/>
|
||||||
<source>User (empty = anonymous access)</source>
|
<source>User (empty = anonymous access)</source>
|
||||||
<translation>Utente (vuoto = accesso anonimo)</translation>
|
<translation>Utente (vuoto = accesso anonimo)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="48" />
|
<location filename="../qml/pages/SettingsPage.qml" line="50"/>
|
||||||
<source>Password</source>
|
<source>Password</source>
|
||||||
<translation>Password</translation>
|
<translation>Password</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="55" />
|
<location filename="../qml/pages/SettingsPage.qml" line="57"/>
|
||||||
<source>Ignore SSL certificate errors</source>
|
<source>Ignore SSL certificate errors</source>
|
||||||
<translation>Ignora errori certificato SSL</translation>
|
<translation>Ignora errori certificato SSL</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="56" />
|
<location filename="../qml/pages/SettingsPage.qml" line="58"/>
|
||||||
<source>Enable if the server uses a self-signed certificate</source>
|
<source>Enable if the server uses a self-signed certificate</source>
|
||||||
<translation>Da attivare se il server usa un certificato autofirmato</translation>
|
<translation>Da attivare se il server usa un certificato autofirmato</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="61" />
|
<location filename="../qml/pages/SettingsPage.qml" line="63"/>
|
||||||
<source>Reader</source>
|
|
||||||
<translation>Lettore</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="66" />
|
|
||||||
<source>Side margins</source>
|
|
||||||
<translation>Margini laterali</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="78" />
|
|
||||||
<source>Top and bottom margin</source>
|
|
||||||
<translation>Margine alto e basso</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="87" />
|
|
||||||
<source>Connection</source>
|
<source>Connection</source>
|
||||||
<translation>Connessione</translation>
|
<translation>Connessione</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="91" />
|
<location filename="../qml/pages/SettingsPage.qml" line="67"/>
|
||||||
<source>Test connection</source>
|
<source>Test connection</source>
|
||||||
<translation>Prova connessione</translation>
|
<translation>Prova connessione</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="115" />
|
<location filename="../qml/pages/SettingsPage.qml" line="91"/>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="120" />
|
<location filename="../qml/pages/SettingsPage.qml" line="96"/>
|
||||||
<source>Language</source>
|
<source>Language</source>
|
||||||
<translation type="finished">Lingua</translation>
|
<translation>Lingua</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="123" />
|
<location filename="../qml/pages/SettingsPage.qml" line="99"/>
|
||||||
<source>System (default)</source>
|
<source>System (default)</source>
|
||||||
<translation type="finished">Sistema (predefinita)</translation>
|
<translation>Sistema (predefinita)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="131" />
|
<location filename="../qml/pages/SettingsPage.qml" line="107"/>
|
||||||
<source>Language will be applied after restarting the app</source>
|
<source>Language will be applied after restarting the app</source>
|
||||||
<translation type="finished">La lingua verrà applicata al riavvio dell'app</translation>
|
<translation>La lingua verrà applicata al riavvio dell'app</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="159" />
|
<location filename="../qml/pages/SettingsPage.qml" line="142"/>
|
||||||
<source>Enter the server address</source>
|
<source>Enter the server address</source>
|
||||||
<translation>Inserisci l'indirizzo del server</translation>
|
<translation>Inserisci l'indirizzo del server</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="156" />
|
<location filename="../qml/pages/SettingsPage.qml" line="132"/>
|
||||||
<source>Version</source>
|
<source>Version</source>
|
||||||
<translation type="finished">Versione</translation>
|
<translation>Versione</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="168" />
|
<location filename="../qml/pages/SettingsPage.qml" line="152"/>
|
||||||
<source>Testing…</source>
|
<source>Testing…</source>
|
||||||
<translation>Verifica in corso…</translation>
|
<translation>Verifica in corso…</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="179" />
|
<location filename="../qml/pages/SettingsPage.qml" line="169"/>
|
||||||
<source>Connection OK: %1 sections found</source>
|
<source>Connection OK: %1 sections found</source>
|
||||||
<translation>Connessione OK: %1 sezioni trovate</translation>
|
<translation>Connessione OK: %1 sezioni trovate</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
Reference in New Issue
Block a user