/*
* 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 com.onegravity.rteditor.RTEditText;
import com.onegravity.rteditor.spans.RTSpan;
import com.onegravity.rteditor.utils.Selection;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* Base class for all effects.
* An "effect" is a particular type of styling to apply to the selected text in a rich text editor.
* Most of them are wrappers around the corresponding CharacterStyle (Bold, Italic, font size etc.)
* or ParagraphStyle classes (e.g. BulletSpan).
*
* @param <V> is the sort of configuration information that the effect needs.
* Many will be Effect<Boolean>, meaning the effect is a toggle (on or off), such as bold.
*
* @param <C> is the RTSpan<V> used by the Effect (e.g. BoldEffect uses BoldSpan)
*/
abstract public class Effect<V, C extends RTSpan<V>> {
private SpanCollector<V> mSpanCollector;
/**
* Check whether the effect exists in the currently selected text of the active RTEditText.
*
* @param editor The RTEditText we want the check.
*
* @return True if the effect exists in the current selection, False otherwise.
*/
final public boolean existsInSelection(RTEditText editor) {
Selection selection = getSelection(editor);
List<RTSpan<V>> spans = getSpans(editor.getText(), selection, SpanCollectMode.SPAN_FLAGS);
return ! spans.isEmpty();
}
/**
* Returns the value(s) of this effect in the currently selected text of the active RTEditText.
*
* @return The returned list, must NEVER be null.
*/
final public List<V> valuesInSelection(RTEditText editor) {
List<V> result = new ArrayList<V>();
Selection selection = getSelection(editor);
List<RTSpan<V>> spans = getSpans(editor.getText(), selection, SpanCollectMode.SPAN_FLAGS);
for (RTSpan<V> span : spans) {
result.add( span.getValue() );
}
return result;
}
/**
* Remove all effects of this type from the currently selected text of the active RTEditText.
* If the selection is empty (cursor), formatting for the whole text is removed.
*/
final public void clearFormattingInSelection(RTEditText editor) {
Spannable text = editor.getText();
// if no selection --> select the whole text
// otherwise use the getSelection method (implented by sub classes)
Selection selection = new Selection(editor);
selection = selection.isEmpty() ? new Selection(0, text.length()) : getSelection(editor);
List<RTSpan<V>> spans = getSpans(text, selection, SpanCollectMode.EXACT);
for (Object span : spans) {
editor.getText().removeSpan(span);
}
}
/**
* Equivalent to the Spanned.getSpans(int, int, Class<T>) method.
* Return the markup objects (spans) attached to the specified slice of a Spannable.
* The type of the spans is defined in the SpanCollector.
*
* @param str The Spannable to search for spans.
* @param selection The selection within the Spannable to search for spans.
* @param mode details see SpanCollectMode.
*
* @return the list of spans in this Spannable/Selection, never Null
*/
final public List<RTSpan<V>> getSpans(Spannable str, Selection selection, SpanCollectMode mode) {
if (mSpanCollector == null) {
// lazy initialize the SpanCollector
Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
Class<? extends RTSpan<V>> spanClazz = (Class<? extends RTSpan<V>>) types[types.length - 1];
mSpanCollector = newSpanCollector(spanClazz);
}
return mSpanCollector.getSpans(str, selection, mode);
}
/**
* @return a new SpanCollector for this effect
*/
abstract protected SpanCollector<V> newSpanCollector(Class<? extends RTSpan<V>> spanClazz);
/**
* @return the Selection for the specified RTEditText.
*/
abstract protected Selection getSelection(RTEditText 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)
*/
abstract public void applyToSelection(RTEditText editor, V value);
}