Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ctrl + c copies text with correct formatting #2708

Merged
merged 4 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions app/bibleview-js/src/components/modals/AmbiguousSelection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import {
getEventVerseInfo,
getHighestPriorityEventFunctions,
isBottomHalfClicked,
setupDocumentEventListener,
} from "@/utils";
import AmbiguousSelectionBookmarkButton from "@/components/modals/AmbiguousSelectionBookmarkButton.vue";
import {emit} from "@/eventbus";
Expand Down Expand Up @@ -342,6 +343,19 @@ function help() {
android.helpBookmarks()
}

setupDocumentEventListener("keydown", (e: KeyboardEvent) => {
if (!showModal.value) return
if (e.ctrlKey && e.code === "KeyC") {
if (selectionInfo.value?.verseInfo) {
console.log("Ctrl + c pressed. Copying (book initial, start ordinal, end ordinal)", selectionInfo.value?.verseInfo.bookInitials, startOrdinal.value, endOrdinal.value)
android.copyVerse(selectionInfo.value.verseInfo.bookInitials, startOrdinal.value!, endOrdinal.value!)

e.preventDefault()
e.stopPropagation()
}
}
})

const modal = ref<InstanceType<typeof ModalDialog> | null>(null);
defineExpose({handle});
</script>
Expand Down
6 changes: 6 additions & 0 deletions app/bibleview-js/src/composables/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export type BibleJavascriptInterface = {
updateGenericBookmarkToLabel: (data: JSONString) => void
shareBookmarkVerse: (bookmarkId: IdType) => void,
shareVerse: (bookInitials: string, startOrdinal: number, endOrdinal: number) => void,
copyVerse: (bookInitials: string, startOrdinal: number, endOrdinal: number) => void,
addBookmark: (bookInitials: string, startOrdinal: number, endOrdinal: number, addNote: boolean) => void,
addGenericBookmark: (bookInitials: string, osisRef: string, startOrdinal: number, endOrdinal: number, addNote: boolean) => void,
compare: (bookInitials: string, verseOrdinal: number, endOrdinal: number) => void,
Expand Down Expand Up @@ -362,6 +363,10 @@ export function useAndroid({bookmarks}: { bookmarks: Ref<BaseBookmark[]> }, conf
window.android.shareVerse(bookInitials, startOrdinal, endOrdinal ? endOrdinal : -1);
}

function copyVerse(bookInitials: string, startOrdinal: number, endOrdinal?: number) {
window.android.copyVerse(bookInitials, startOrdinal, endOrdinal ? endOrdinal : -1);
}

function addBookmark(bookInitials: string, startOrdinal: number, endOrdinal?: number, addNote: boolean = false) {
window.android.addBookmark(bookInitials, startOrdinal, endOrdinal ? endOrdinal : -1, addNote);
}
Expand Down Expand Up @@ -523,6 +528,7 @@ export function useAndroid({bookmarks}: { bookmarks: Ref<BaseBookmark[]> }, conf
openDownloads,
refChooserDialog,
shareVerse,
copyVerse,
addBookmark,
addGenericBookmark,
compare,
Expand Down
6 changes: 5 additions & 1 deletion app/bibleview-js/src/composables/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {useScroll} from "@/composables/scroll";
import {computed, ComputedRef, ref, watch} from "vue";

const altKeys: Set<string> = new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "KeyW", "KeyM", "KeyO", "KeyG"]);
const ctrlKeys: Set<string> = new Set(["KeyC"]);
const keys: Set<string> = new Set(["ArrowUp", "ArrowDown"]);
const handleJsSide: Set<string> = new Set(["ArrowUp", "ArrowDown"]);

Expand All @@ -37,11 +38,14 @@ export function useKeyboard(
})

setupDocumentEventListener("keydown", (e: KeyboardEvent) => {
if (keys.has(e.code) || (e.altKey && altKeys.has(e.code))) {
if (keys.has(e.code) || (e.altKey && altKeys.has(e.code)) || (e.ctrlKey && ctrlKeys.has(e.code))) {
let key = e.code;
if (e.altKey) {
key = "Alt" + key;
}
if (e.ctrlKey) {
key = "Ctrl" + key;
}
if(handleJsSide.has(key)) {
if(isEditing.value) return
else if(key === "ArrowDown") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,13 @@ class BibleJavascriptInterface(
}
}

@JavascriptInterface
fun copyVerse(bookInitials: String, startOrdinal: Int, endOrdinal: Int) {
scope.launch(Dispatchers.Main) {
bibleView.copySelectionToClipboard(Selection(bookInitials, startOrdinal, positiveOrNull(endOrdinal)))
}
}

@JavascriptInterface
fun addBookmark(bookInitials: String, startOrdinal: Int, endOrdinal: Int, addNote: Boolean) {
bibleView.makeBookmark(Selection(bookInitials, startOrdinal, positiveOrNull(endOrdinal)), true, addNote)
Expand Down Expand Up @@ -581,6 +588,7 @@ class BibleJavascriptInterface(
}
"AltKeyO" -> mainBibleActivity.showOptionsMenu()
"AltKeyG" -> bibleView.window.pageManager.currentPage.startKeyChooser(mainBibleActivity)
"CtrlKeyC" -> bibleView.copySelectionToClipboard()
}
}
}
Expand Down
57 changes: 44 additions & 13 deletions app/src/main/java/net/bible/android/view/activity/page/BibleView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package net.bible.android.view.activity.page

import android.annotation.SuppressLint
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
Expand Down Expand Up @@ -72,6 +75,7 @@ import net.bible.android.control.bookmark.StudyPadOrderEvent
import net.bible.android.control.bookmark.StudyPadTextEntryDeleted
import net.bible.android.control.download.DownloadControl
import net.bible.android.control.event.ABEventBus
import net.bible.android.control.event.ToastEvent
import net.bible.android.control.event.window.CurrentWindowChangedEvent
import net.bible.android.control.event.window.NumberOfWindowsChangedEvent
import net.bible.android.control.event.window.ScrollSecondaryWindowEvent
Expand Down Expand Up @@ -217,6 +221,13 @@ class Selection(
val v11n = swordBook?.versification ?: KJVA
return VerseRange(v11n, Verse(v11n, startOrdinal), Verse(v11n, endOrdinal))
}

fun copyToClipboard(context: Context) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(verseRange?.name, CommonUtils.getShareableDocumentText(this))
clipboard.setPrimaryClip(clip)
ABEventBus.post(ToastEvent(context.getString(R.string.text_copied_to_clicpboard)))
}
}

/** The WebView component that shows the bible and other documents */
Expand Down Expand Up @@ -561,21 +572,11 @@ class BibleView(val mainBibleActivity: MainBibleActivity,
} else {
menu.clear()
scope.launch {
val result = evaluateJavascriptAsync("bibleView.querySelection()")
if (result != "null") {
val sel = try {
json.decodeFromString(serializer<Selection?>(), result)
} catch (e: SerializationException) {
null
}
val selText = try { json.decodeFromString(serializer(), result) } catch (e: SerializationException) { result }
currentSelection = sel
currentSelectionText = sel?.text ?: selText
currentSelectionRef = linkControl.resolveRef(currentSelectionText?: "")
if (setCurrentSelection())
menuPrepared = true
} else {
else
showSystem = true
}

withContext(Dispatchers.Main) {
mode.invalidate()
}
Expand All @@ -585,6 +586,36 @@ class BibleView(val mainBibleActivity: MainBibleActivity,
}
}

/** @return true if bibleView.querySelection() result was not null */
private suspend fun setCurrentSelection(): Boolean = withContext(Dispatchers.Main) {
val result = evaluateJavascriptAsync("bibleView.querySelection()")
if (result != "null") {
val sel = try {
json.decodeFromString(serializer<Selection?>(), result)
} catch (e: SerializationException) {
null
}
val selText = try { json.decodeFromString(serializer(), result) } catch (e: SerializationException) { result }
currentSelection = sel
currentSelectionText = sel?.text ?: selText
currentSelectionRef = linkControl.resolveRef(currentSelectionText?: "")
return@withContext true
}

return@withContext false
}

fun copySelectionToClipboard(selection: Selection? = null) {
scope.launch {
// use currentSelection for partial selected text, otherwise
// JS has to send Selection by book and ordinals which is passed in selection parameter
currentSelection = null
if (selection == null)
setCurrentSelection()
(currentSelection ?: selection)?.copyToClipboard(context)
}
}

var editingTextInJs: Boolean = false

fun stopSelection(removeRanges: Boolean = false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ class ShareWidget(context: Context, attributeSet: AttributeSet?, val selection:
* - CommonUtils counterparts of widget options
*/
private fun updateWidgetState() {
updateText()
updateSelectionOptions()
updateText()
}

/**
Expand Down Expand Up @@ -128,19 +128,7 @@ class ShareWidget(context: Context, attributeSet: AttributeSet?, val selection:
*/
private fun updateText() {
// get currently selected text with markup, based on widget options
val text = SwordContentFacade.getSelectionText(
selection,
showVerseNumbers = bindings.toggleVersenumbers.isChecked,
advertiseApp = bindings.advertise.isChecked,
abbreviateReference = bindings.toggleAbbreviateReference.isChecked,
showNotes = bindings.toggleNotes.isChecked,
showVersion = bindings.toggleShowVersion.isChecked,
showReference = bindings.toggleShowReference.isChecked,
showReferenceAtFront = bindings.toggleShowReferenceAtFront.isChecked,
showSelectionOnly = bindings.toggleShowSelectionOnly.isChecked,
showEllipsis = bindings.toggleShowEllipsis.isChecked,
showQuotes = bindings.toggleShowQuotes.isChecked
)
val text = CommonUtils.getShareableDocumentText(selection)
val isRtl = TextUtils.getLayoutDirectionFromLocale(Locale(selection.book!!.language.code)) == LayoutDirection.RTL

// set widget text based on the new text
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/net/bible/service/common/CommonUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ import net.bible.android.view.activity.base.ActivityBase
import net.bible.android.view.activity.base.CurrentActivityHolder
import net.bible.android.view.activity.base.Dialogs
import net.bible.android.view.activity.download.DownloadActivity
import net.bible.android.view.activity.page.Selection
import net.bible.service.cloudsync.CloudSync
import net.bible.service.cloudsync.SyncableDatabaseDefinition
import net.bible.service.db.DatabaseContainer
Expand Down Expand Up @@ -466,6 +467,22 @@ object CommonUtils : CommonUtilsBase() {
.build()
}

fun getShareableDocumentText(selection: Selection): String {
return SwordContentFacade.getSelectionText(
selection,
showVerseNumbers = settings.getBoolean("share_verse_numbers", true),
advertiseApp = settings.getBoolean("share_show_add", true),
abbreviateReference = settings.getBoolean("share_abbreviate_reference", true),
showNotes = settings.getBoolean("show_notes", true),
showVersion = settings.getBoolean("share_show_version", true),
showReference = settings.getBoolean("share_show_reference", true),
showReferenceAtFront = settings.getBoolean("share_show_reference_at_front", true),
showSelectionOnly = settings.getBoolean("show_selection_only", true),
showEllipsis = settings.getBoolean("show_ellipsis", true),
showQuotes = settings.getBoolean("share_show_quotes", false)
)
}

fun getFreeSpace(path: String): Long {
val stat = StatFs(path)
val bytesAvailable = stat.availableBytes
Expand Down