diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java
index 49ae64042..39f02c6fb 100644
--- a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java
+++ b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java
@@ -652,6 +652,26 @@ public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) {
}, 250);
return true;
}
+ case R.id.action_toggle_case:
+ if (_hlEditor != null) {
+ _hlEditor.toggleCase();
+ }
+ return true;
+ case R.id.action_switch_case:
+ if (_hlEditor != null) {
+ _hlEditor.switchCase();
+ }
+ return true;
+ case R.id.action_capitalize_words:
+ if (_hlEditor != null) {
+ _hlEditor.capitalizeWords();
+ }
+ return true;
+ case R.id.action_capitalize_sentences:
+ if (_hlEditor != null) {
+ _hlEditor.capitalizeSentences();
+ }
+ return true;
default: {
return super.onOptionsItemSelected(item);
}
diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java b/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java
index 38f2e1f12..dc6dc9d15 100644
--- a/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java
+++ b/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java
@@ -33,7 +33,9 @@
import net.gsantner.opoc.format.GsTextUtils;
import net.gsantner.opoc.wrapper.GsCallback;
import net.gsantner.opoc.wrapper.GsTextWatcherAdapter;
+import net.gsantner.markor.util.TextCasingUtils;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
@@ -297,6 +299,60 @@ private int rowEnd(final int y) {
return layout.getLineEnd(line);
}
+ // Text-Casing
+ // ---------------------------------------------------------------------------------------------
+ public void toggleCase() {
+ String text = getSelectedText();
+ if (text.isEmpty()) {
+ text = Objects.requireNonNull(getText()).toString();
+ }
+ String newText = TextCasingUtils.toggleCase(text);
+ replaceSelection(newText);
+ }
+
+ public void switchCase() {
+ String text = getSelectedText();
+ if (text.isEmpty()) {
+ text = Objects.requireNonNull(getText()).toString();
+ }
+ String newText = TextCasingUtils.switchCase(text);
+ replaceSelection(newText);
+ }
+
+ public void capitalizeWords() {
+ String text = getSelectedText();
+ if (text.isEmpty()) {
+ text = Objects.requireNonNull(getText()).toString();
+ }
+ String newText = TextCasingUtils.capitalizeWords(text);
+ replaceSelection(newText);
+ }
+
+ public void capitalizeSentences() {
+ String text = getSelectedText();
+ if (text.isEmpty()) {
+ text = Objects.requireNonNull(getText()).toString();
+ }
+ String newText = TextCasingUtils.capitalizeSentences(text);
+ replaceSelection(newText);
+ }
+
+ private String getSelectedText() {
+ int start = Math.max(0, getSelectionStart());
+ int end = Math.max(0, getSelectionEnd());
+ return Objects.requireNonNull(getText()).toString().substring(start, end);
+ }
+
+ private void replaceSelection(String replacement) {
+ int start = Math.max(0, getSelectionStart());
+ int end = Math.max(0, getSelectionEnd());
+ if (start == end) { // If no selection is made, replace all the text in the document
+ setText(replacement);
+ } else { // Replace only the selected text
+ Objects.requireNonNull(getText()).replace(start, end, replacement);
+ }
+ }
+
// Various overrides
// ---------------------------------------------------------------------------------------------
public void setSaveInstanceState(final boolean save) {
diff --git a/app/src/main/java/net/gsantner/markor/util/TextCasingUtils.java b/app/src/main/java/net/gsantner/markor/util/TextCasingUtils.java
new file mode 100644
index 000000000..cc5605d7d
--- /dev/null
+++ b/app/src/main/java/net/gsantner/markor/util/TextCasingUtils.java
@@ -0,0 +1,61 @@
+package net.gsantner.markor.util;
+
+public class TextCasingUtils {
+ public static String toggleCase(String text) { // Toggle all text to Uppercase or Lowercase
+ if (text.equals(text.toUpperCase())) {
+ return text.toLowerCase();
+ } else {
+ return text.toUpperCase();
+ }
+ }
+
+ public static String switchCase(String text) { // Switch the text case of each character
+ StringBuilder result = new StringBuilder();
+ for (char c : text.toCharArray()) {
+ if (Character.isUpperCase(c)) {
+ result.append(Character.toLowerCase(c));
+ } else if (Character.isLowerCase(c)) {
+ result.append(Character.toUpperCase(c));
+ } else {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+
+ public static String capitalizeWords(String text) { // Capitalize the first letter of each word
+ StringBuilder result = new StringBuilder();
+ boolean capitalizeNext = true;
+ for (char c : text.toCharArray()) {
+ if (Character.isWhitespace(c)) {
+ capitalizeNext = true;
+ result.append(c);
+ } else if (capitalizeNext) {
+ result.append(Character.toUpperCase(c));
+ capitalizeNext = false;
+ } else {
+ result.append(Character.toLowerCase(c));
+ }
+ }
+ return result.toString();
+ }
+
+ public static String capitalizeSentences(String text) { // Capitalize the first letter of each sentence
+ StringBuilder result = new StringBuilder();
+ boolean capitalizeNext = true;
+ for (char c : text.toCharArray()) {
+ if (c == '.' || c == '!' || c == '?') {
+ capitalizeNext = true;
+ result.append(c);
+ } else if (Character.isWhitespace(c)) {
+ result.append(c);
+ } else if (capitalizeNext) {
+ result.append(Character.toUpperCase(c));
+ capitalizeNext = false;
+ } else {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+}
diff --git a/app/src/main/res/drawable/ic_format_text_case_black_24dp.xml b/app/src/main/res/drawable/ic_format_text_case_black_24dp.xml
new file mode 100644
index 000000000..6246b2147
--- /dev/null
+++ b/app/src/main/res/drawable/ic_format_text_case_black_24dp.xml
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/document__edit__menu.xml b/app/src/main/res/menu/document__edit__menu.xml
index 57d3d1167..3f277f6ea 100644
--- a/app/src/main/res/menu/document__edit__menu.xml
+++ b/app/src/main/res/menu/document__edit__menu.xml
@@ -103,6 +103,31 @@
android:title="@string/font_size"
app:showAsAction="never" />
+
+ -
+
+
+
.
Files with fewer than %d characters not saved automatically in order to prevent data loss.
Advanced filtering syntax]]>
Filter
+ Text Case
+ Toggle Case (Ex: Case->CASE->case)
+ Switch Case (Ex: CaSe->cAsE)
+ Capitalize Words (Ex: a note->A Note)
+ Capitalize Sentences (Ex: case->Case)