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)