/* * Copyright (C) 2015-2017 Emanuel Moecklin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.onegravity.rteditor.effects; import android.text.Spannable; import android.text.Spanned; import com.onegravity.rteditor.RTEditText; import com.onegravity.rteditor.spans.RTSpan; import com.onegravity.rteditor.utils.Selection; /** * CharacterEffects are always applied to the current selection, like bold text or typeface. */ abstract class CharacterEffect<V, C extends RTSpan<V>> extends Effect<V, C> { @Override final protected SpanCollector<V> newSpanCollector(Class<? extends RTSpan<V>> spanClazz) { return new CharacterSpanCollector<V>(spanClazz); } @Override final protected Selection getSelection(RTEditText editor) { return new Selection(editor); } /** * Apply this effect to the selection. * If value is Null then the effect will be removed from the current selection. * * @param editor The editor to apply the effect to (current selection) * @param value The value to apply (depends on the Effect) */ public void applyToSelection(RTEditText editor, V value) { Selection selection = getSelection(editor); // SPAN_INCLUSIVE_INCLUSIVE is default for empty spans int flags = selection.isEmpty() ? Spanned.SPAN_INCLUSIVE_INCLUSIVE : Spanned.SPAN_EXCLUSIVE_INCLUSIVE; Spannable str = editor.getText(); for (RTSpan<V> span : getSpans(str, selection, SpanCollectMode.SPAN_FLAGS)) { boolean sameSpan = span.getValue().equals(value); int spanStart = str.getSpanStart(span); if (spanStart < selection.start()) { // process preceding spans if (sameSpan) { // we have a preceding span --> use SPAN_EXCLUSIVE_INCLUSIVE instead of SPAN_INCLUSIVE_INCLUSIVE flags = Spanned.SPAN_EXCLUSIVE_INCLUSIVE; selection.offset(selection.start() - spanStart, 0); } else { str.setSpan(newSpan(span.getValue()), spanStart, selection.start(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } int spanEnd = str.getSpanEnd(span); if (spanEnd > selection.end()) { // process succeeding spans if (sameSpan) { selection.offset(0, spanEnd - selection.end()); } else { str.setSpan(newSpan(span.getValue()), selection.end(), spanEnd, Spanned.SPAN_EXCLUSIVE_INCLUSIVE); } } str.removeSpan(span); } if (value != null) { RTSpan<V> newSpan = newSpan(value); if (newSpan != null) { str.setSpan(newSpan, selection.start(), selection.end(), flags); } } } /** * Create an RTSpan for this effect. * This method is used in applyToSelection(RTEditText, V). * If the RTSpan can't be created only with V as value parameter then applyToSelection needs * to be implemented by the sub class. * * @return the class of the span this effect supports. Can be Null but then the subclass has to * override applyToSelection(RTEditText, V) */ abstract protected RTSpan<V> newSpan(V value); }