1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef mozilla_TextRage_h_ 7 #define mozilla_TextRage_h_ 8 9 #include <stdint.h> 10 11 #include "mozilla/EventForwards.h" 12 13 #include "nsColor.h" 14 #include "nsISelectionController.h" 15 #include "nsITextInputProcessor.h" 16 #include "nsTArray.h" 17 18 namespace mozilla { 19 20 /****************************************************************************** 21 * mozilla::TextRangeStyle 22 ******************************************************************************/ 23 24 struct TextRangeStyle { 25 typedef uint8_t LineStyleType; 26 // FYI: Modify IME_RANGE_LINE_* too when you modify LineStyle. 27 enum class LineStyle : LineStyleType { 28 None, 29 Solid, 30 Dotted, 31 Dashed, 32 Double, 33 Wavy, 34 }; ToLineStyleTextRangeStyle35 inline static LineStyle ToLineStyle(RawTextRangeType aRawLineStyle) { 36 switch (static_cast<LineStyle>(aRawLineStyle)) { 37 case LineStyle::None: 38 case LineStyle::Solid: 39 case LineStyle::Dotted: 40 case LineStyle::Dashed: 41 case LineStyle::Double: 42 case LineStyle::Wavy: 43 return static_cast<LineStyle>(aRawLineStyle); 44 } 45 MOZ_ASSERT_UNREACHABLE("aRawLineStyle value is invalid"); 46 return LineStyle::None; 47 } 48 49 enum { 50 DEFINED_NONE = 0x00, 51 DEFINED_LINESTYLE = 0x01, 52 DEFINED_FOREGROUND_COLOR = 0x02, 53 DEFINED_BACKGROUND_COLOR = 0x04, 54 DEFINED_UNDERLINE_COLOR = 0x08 55 }; 56 57 // Initialize all members, because TextRange instances may be compared by 58 // memcomp. 59 // 60 // FIXME(emilio): I don't think that'd be sound, as it has padding which the 61 // compiler is not guaranteed to initialize. TextRangeStyleTextRangeStyle62 TextRangeStyle() { Clear(); } 63 ClearTextRangeStyle64 void Clear() { 65 mDefinedStyles = DEFINED_NONE; 66 mLineStyle = LineStyle::None; 67 mIsBoldLine = false; 68 mForegroundColor = mBackgroundColor = mUnderlineColor = NS_RGBA(0, 0, 0, 0); 69 } 70 IsDefinedTextRangeStyle71 bool IsDefined() const { return mDefinedStyles != DEFINED_NONE; } 72 IsLineStyleDefinedTextRangeStyle73 bool IsLineStyleDefined() const { 74 return (mDefinedStyles & DEFINED_LINESTYLE) != 0; 75 } 76 IsForegroundColorDefinedTextRangeStyle77 bool IsForegroundColorDefined() const { 78 return (mDefinedStyles & DEFINED_FOREGROUND_COLOR) != 0; 79 } 80 IsBackgroundColorDefinedTextRangeStyle81 bool IsBackgroundColorDefined() const { 82 return (mDefinedStyles & DEFINED_BACKGROUND_COLOR) != 0; 83 } 84 IsUnderlineColorDefinedTextRangeStyle85 bool IsUnderlineColorDefined() const { 86 return (mDefinedStyles & DEFINED_UNDERLINE_COLOR) != 0; 87 } 88 IsNoChangeStyleTextRangeStyle89 bool IsNoChangeStyle() const { 90 return !IsForegroundColorDefined() && !IsBackgroundColorDefined() && 91 IsLineStyleDefined() && mLineStyle == LineStyle::None; 92 } 93 EqualsTextRangeStyle94 bool Equals(const TextRangeStyle& aOther) const { 95 if (mDefinedStyles != aOther.mDefinedStyles) return false; 96 if (IsLineStyleDefined() && (mLineStyle != aOther.mLineStyle || 97 !mIsBoldLine != !aOther.mIsBoldLine)) 98 return false; 99 if (IsForegroundColorDefined() && 100 (mForegroundColor != aOther.mForegroundColor)) 101 return false; 102 if (IsBackgroundColorDefined() && 103 (mBackgroundColor != aOther.mBackgroundColor)) 104 return false; 105 if (IsUnderlineColorDefined() && 106 (mUnderlineColor != aOther.mUnderlineColor)) 107 return false; 108 return true; 109 } 110 111 bool operator!=(const TextRangeStyle& aOther) const { 112 return !Equals(aOther); 113 } 114 115 bool operator==(const TextRangeStyle& aOther) const { return Equals(aOther); } 116 117 uint8_t mDefinedStyles; 118 LineStyle mLineStyle; // DEFINED_LINESTYLE 119 120 bool mIsBoldLine; // DEFINED_LINESTYLE 121 122 nscolor mForegroundColor; // DEFINED_FOREGROUND_COLOR 123 nscolor mBackgroundColor; // DEFINED_BACKGROUND_COLOR 124 nscolor mUnderlineColor; // DEFINED_UNDERLINE_COLOR 125 }; 126 127 /****************************************************************************** 128 * mozilla::TextRange 129 ******************************************************************************/ 130 131 enum class TextRangeType : RawTextRangeType { 132 eUninitialized = 0x00, 133 eCaret = 0x01, 134 eRawClause = nsITextInputProcessor::ATTR_RAW_CLAUSE, 135 eSelectedRawClause = nsITextInputProcessor::ATTR_SELECTED_RAW_CLAUSE, 136 eConvertedClause = nsITextInputProcessor::ATTR_CONVERTED_CLAUSE, 137 eSelectedClause = nsITextInputProcessor::ATTR_SELECTED_CLAUSE 138 }; 139 140 bool IsValidRawTextRangeValue(RawTextRangeType aRawTextRangeValue); 141 RawTextRangeType ToRawTextRangeType(TextRangeType aTextRangeType); 142 TextRangeType ToTextRangeType(RawTextRangeType aRawTextRangeType); 143 const char* ToChar(TextRangeType aTextRangeType); 144 SelectionType ToSelectionType(TextRangeType aTextRangeType); 145 146 struct TextRange { TextRangeTextRange147 TextRange() 148 : mStartOffset(0), 149 mEndOffset(0), 150 mRangeType(TextRangeType::eUninitialized) {} 151 152 uint32_t mStartOffset; 153 // XXX Storing end offset makes the initializing code very complicated. 154 // We should replace it with mLength. 155 uint32_t mEndOffset; 156 157 TextRangeStyle mRangeStyle; 158 159 TextRangeType mRangeType; 160 LengthTextRange161 uint32_t Length() const { return mEndOffset - mStartOffset; } 162 IsClauseTextRange163 bool IsClause() const { return mRangeType != TextRangeType::eCaret; } 164 EqualsTextRange165 bool Equals(const TextRange& aOther) const { 166 return mStartOffset == aOther.mStartOffset && 167 mEndOffset == aOther.mEndOffset && mRangeType == aOther.mRangeType && 168 mRangeStyle == aOther.mRangeStyle; 169 } 170 RemoveCharacterTextRange171 void RemoveCharacter(uint32_t aOffset) { 172 if (mStartOffset > aOffset) { 173 --mStartOffset; 174 --mEndOffset; 175 } else if (mEndOffset > aOffset) { 176 --mEndOffset; 177 } 178 } 179 }; 180 181 /****************************************************************************** 182 * mozilla::TextRangeArray 183 ******************************************************************************/ 184 class TextRangeArray final : public AutoTArray<TextRange, 10> { 185 friend class WidgetCompositionEvent; 186 187 ~TextRangeArray() = default; 188 NS_INLINE_DECL_REFCOUNTING(TextRangeArray)189 NS_INLINE_DECL_REFCOUNTING(TextRangeArray) 190 191 const TextRange* GetTargetClause() const { 192 for (uint32_t i = 0; i < Length(); ++i) { 193 const TextRange& range = ElementAt(i); 194 if (range.mRangeType == TextRangeType::eSelectedRawClause || 195 range.mRangeType == TextRangeType::eSelectedClause) { 196 return ⦥ 197 } 198 } 199 return nullptr; 200 } 201 202 // Returns target clause offset. If there are selected clauses, this returns 203 // the first selected clause offset. Otherwise, 0. TargetClauseOffset()204 uint32_t TargetClauseOffset() const { 205 const TextRange* range = GetTargetClause(); 206 return range ? range->mStartOffset : 0; 207 } 208 209 // Returns target clause length. If there are selected clauses, this returns 210 // the first selected clause length. Otherwise, UINT32_MAX. TargetClauseLength()211 uint32_t TargetClauseLength() const { 212 const TextRange* range = GetTargetClause(); 213 return range ? range->Length() : UINT32_MAX; 214 } 215 216 public: IsComposing()217 bool IsComposing() const { 218 for (uint32_t i = 0; i < Length(); ++i) { 219 if (ElementAt(i).IsClause()) { 220 return true; 221 } 222 } 223 return false; 224 } 225 Equals(const TextRangeArray & aOther)226 bool Equals(const TextRangeArray& aOther) const { 227 size_t len = Length(); 228 if (len != aOther.Length()) { 229 return false; 230 } 231 for (size_t i = 0; i < len; i++) { 232 if (!ElementAt(i).Equals(aOther.ElementAt(i))) { 233 return false; 234 } 235 } 236 return true; 237 } 238 RemoveCharacter(uint32_t aOffset)239 void RemoveCharacter(uint32_t aOffset) { 240 for (size_t i = 0, len = Length(); i < len; i++) { 241 ElementAt(i).RemoveCharacter(aOffset); 242 } 243 } 244 HasCaret()245 bool HasCaret() const { 246 for (const TextRange& range : *this) { 247 if (range.mRangeType == TextRangeType::eCaret) { 248 return true; 249 } 250 } 251 return false; 252 } 253 HasClauses()254 bool HasClauses() const { 255 for (const TextRange& range : *this) { 256 if (range.IsClause()) { 257 return true; 258 } 259 } 260 return false; 261 } 262 GetCaretPosition()263 uint32_t GetCaretPosition() const { 264 for (const TextRange& range : *this) { 265 if (range.mRangeType == TextRangeType::eCaret) { 266 return range.mStartOffset; 267 } 268 } 269 return UINT32_MAX; 270 } 271 GetFirstClause()272 const TextRange* GetFirstClause() const { 273 for (const TextRange& range : *this) { 274 // Look for the range of a clause whose start offset is 0 because the 275 // first clause's start offset is always 0. 276 if (range.IsClause() && !range.mStartOffset) { 277 return ⦥ 278 } 279 } 280 MOZ_ASSERT(!HasClauses()); 281 return nullptr; 282 } 283 }; 284 285 } // namespace mozilla 286 287 #endif // mozilla_TextRage_h_ 288