/src/mozilla-central/layout/base/nsCaret.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /* the caret is the text cursor used, e.g., when editing */ |
8 | | |
9 | | #ifndef nsCaret_h__ |
10 | | #define nsCaret_h__ |
11 | | |
12 | | #include "mozilla/MemoryReporting.h" |
13 | | #include "mozilla/dom/Selection.h" |
14 | | #include "nsCoord.h" |
15 | | #include "nsISelectionListener.h" |
16 | | #include "nsIWeakReferenceUtils.h" |
17 | | #include "CaretAssociationHint.h" |
18 | | #include "nsPoint.h" |
19 | | #include "nsRect.h" |
20 | | |
21 | | class nsDisplayListBuilder; |
22 | | class nsFrameSelection; |
23 | | class nsIContent; |
24 | | class nsIFrame; |
25 | | class nsINode; |
26 | | class nsIPresShell; |
27 | | class nsITimer; |
28 | | |
29 | | namespace mozilla { |
30 | | namespace gfx { |
31 | | class DrawTarget; |
32 | | } // namespace gfx |
33 | | } // namespace mozilla |
34 | | |
35 | | //----------------------------------------------------------------------------- |
36 | | class nsCaret final : public nsISelectionListener |
37 | | { |
38 | | typedef mozilla::gfx::DrawTarget DrawTarget; |
39 | | |
40 | | public: |
41 | | nsCaret(); |
42 | | |
43 | | protected: |
44 | | virtual ~nsCaret(); |
45 | | |
46 | | public: |
47 | | NS_DECL_ISUPPORTS |
48 | | |
49 | | typedef mozilla::CaretAssociationHint CaretAssociationHint; |
50 | | |
51 | | nsresult Init(nsIPresShell *inPresShell); |
52 | | void Terminate(); |
53 | | |
54 | | void SetSelection(mozilla::dom::Selection *aDOMSel); |
55 | | mozilla::dom::Selection* GetSelection(); |
56 | | |
57 | | /** |
58 | | * Sets whether the caret should only be visible in nodes that are not |
59 | | * user-modify: read-only, or whether it should be visible in all nodes. |
60 | | * |
61 | | * @param aIgnoreUserModify true to have the cursor visible in all nodes, |
62 | | * false to have it visible in all nodes except |
63 | | * those with user-modify: read-only |
64 | | */ |
65 | | void SetIgnoreUserModify(bool aIgnoreUserModify); |
66 | | /** SetVisible will set the visibility of the caret |
67 | | * @param inMakeVisible true to show the caret, false to hide it |
68 | | */ |
69 | | void SetVisible(bool intMakeVisible); |
70 | | /** IsVisible will get the visibility of the caret. |
71 | | * This returns false if the caret is hidden because it was set |
72 | | * to not be visible, or because the selection is not collapsed, or |
73 | | * because an open popup is hiding the caret. |
74 | | * It does not take account of blinking or the caret being hidden |
75 | | * because we're in non-editable/disabled content. |
76 | | */ |
77 | | bool IsVisible(mozilla::dom::Selection* aSelection = nullptr) |
78 | 0 | { |
79 | 0 | if (!mVisible || mHideCount) { |
80 | 0 | return false; |
81 | 0 | } |
82 | 0 | |
83 | 0 | if (!mShowDuringSelection) { |
84 | 0 | mozilla::dom::Selection* selection; |
85 | 0 | if (aSelection) { |
86 | 0 | selection = aSelection; |
87 | 0 | } else { |
88 | 0 | selection = GetSelection(); |
89 | 0 | } |
90 | 0 | if (!selection || !selection->IsCollapsed()) { |
91 | 0 | return false; |
92 | 0 | } |
93 | 0 | } |
94 | 0 | |
95 | 0 | if (IsMenuPopupHidingCaret()) { |
96 | 0 | return false; |
97 | 0 | } |
98 | 0 | |
99 | 0 | return true; |
100 | 0 | } |
101 | | /** |
102 | | * AddForceHide() increases mHideCount and hide the caret even if |
103 | | * SetVisible(true) has been or will be called. This is useful when the |
104 | | * caller wants to hide caret temporarily and it needs to cancel later. |
105 | | * Especially, in the latter case, it's too difficult to decide if the |
106 | | * caret should be actually visible or not because caret visible state |
107 | | * is set from a lot of event handlers. So, it's very stateful. |
108 | | */ |
109 | | void AddForceHide(); |
110 | | /** |
111 | | * RemoveForceHide() decreases mHideCount if it's over 0. |
112 | | * If the value becomes 0, this may show the caret if SetVisible(true) |
113 | | * has been called. |
114 | | */ |
115 | | void RemoveForceHide(); |
116 | | /** SetCaretReadOnly set the appearance of the caret |
117 | | * @param inMakeReadonly true to show the caret in a 'read only' state, |
118 | | * false to show the caret in normal, editing state |
119 | | */ |
120 | | void SetCaretReadOnly(bool inMakeReadonly); |
121 | | /** |
122 | | * @param aVisibility true if the caret should be visible even when the |
123 | | * selection is not collapsed. |
124 | | */ |
125 | | void SetVisibilityDuringSelection(bool aVisibility); |
126 | | |
127 | | /** |
128 | | * Set the caret's position explicitly to the specified node and offset |
129 | | * instead of tracking its selection. |
130 | | * Passing null for aNode would set the caret to track its selection again. |
131 | | **/ |
132 | | void SetCaretPosition(nsINode* aNode, int32_t aOffset); |
133 | | |
134 | | /** |
135 | | * Schedule a repaint for the frame where the caret would appear. |
136 | | * Does not check visibility etc. |
137 | | */ |
138 | | void SchedulePaint(mozilla::dom::Selection* aSelection = nullptr); |
139 | | |
140 | | /** |
141 | | * Returns a frame to paint in, and the bounds of the painted caret |
142 | | * relative to that frame. |
143 | | * The rectangle includes bidi decorations. |
144 | | * Returns null if the caret should not be drawn (including if it's blinked |
145 | | * off). |
146 | | */ |
147 | | nsIFrame* GetPaintGeometry(nsRect* aRect); |
148 | | /** |
149 | | * A simple wrapper around GetGeometry. Does not take any caret state into |
150 | | * account other than the current selection. |
151 | | */ |
152 | | nsIFrame* GetGeometry(nsRect* aRect) |
153 | 0 | { |
154 | 0 | return GetGeometry(GetSelection(), aRect); |
155 | 0 | } |
156 | | |
157 | | /** PaintCaret |
158 | | * Actually paint the caret onto the given rendering context. |
159 | | */ |
160 | | void PaintCaret(DrawTarget& aDrawTarget, |
161 | | nsIFrame *aForFrame, |
162 | | const nsPoint &aOffset); |
163 | | |
164 | | //nsISelectionListener interface |
165 | | NS_DECL_NSISELECTIONLISTENER |
166 | | |
167 | | /** |
168 | | * Gets the position and size of the caret that would be drawn for |
169 | | * the focus node/offset of aSelection (assuming it would be drawn, |
170 | | * i.e., disregarding blink status). The geometry is stored in aRect, |
171 | | * and we return the frame aRect is relative to. |
172 | | * Only looks at the focus node of aSelection, so you can call it even if |
173 | | * aSelection is not collapsed. |
174 | | * This rect does not include any extra decorations for bidi. |
175 | | * @param aRect must be non-null |
176 | | */ |
177 | | static nsIFrame* GetGeometry(mozilla::dom::Selection* aSelection, |
178 | | nsRect* aRect); |
179 | | static nsresult GetCaretFrameForNodeOffset(nsFrameSelection* aFrameSelection, |
180 | | nsIContent* aContentNode, |
181 | | int32_t aOffset, |
182 | | CaretAssociationHint aFrameHint, |
183 | | uint8_t aBidiLevel, |
184 | | nsIFrame** aReturnFrame, |
185 | | int32_t* aReturnOffset); |
186 | | static nsRect GetGeometryForFrame(nsIFrame* aFrame, |
187 | | int32_t aFrameOffset, |
188 | | nscoord* aBidiIndicatorSize); |
189 | | |
190 | | // Get the frame and frame offset based on the focus node and focus offset |
191 | | // of aSelection. If aOverrideNode and aOverride are provided, use them |
192 | | // instead. |
193 | | // @param aFrameOffset return the frame offset if non-null. |
194 | | // @return the frame of the focus node. |
195 | | static nsIFrame* GetFrameAndOffset(mozilla::dom::Selection* aSelection, |
196 | | nsINode* aOverrideNode, |
197 | | int32_t aOverrideOffset, |
198 | | int32_t* aFrameOffset); |
199 | | |
200 | | size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
201 | | |
202 | | nsIFrame* GetFrame(int32_t* aContentOffset); |
203 | | void ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset, |
204 | | nsRect* aCaretRect, nsRect* aHookRect); |
205 | | |
206 | | protected: |
207 | | static void CaretBlinkCallback(nsITimer *aTimer, void *aClosure); |
208 | | |
209 | | void CheckSelectionLanguageChange(); |
210 | | |
211 | | void ResetBlinking(); |
212 | | void StopBlinking(); |
213 | | |
214 | | struct Metrics { |
215 | | nscoord mBidiIndicatorSize; // width and height of bidi indicator |
216 | | nscoord mCaretWidth; // full caret width including bidi indicator |
217 | | }; |
218 | | static Metrics ComputeMetrics(nsIFrame* aFrame, int32_t aOffset, |
219 | | nscoord aCaretHeight); |
220 | | |
221 | | // Returns true if we should not draw the caret because of XUL menu popups. |
222 | | // The caret should be hidden if: |
223 | | // 1. An open popup contains the caret, but a menu popup exists before the |
224 | | // caret-owning popup in the popup list (i.e. a menu is in front of the |
225 | | // popup with the caret). If the menu itself contains the caret we don't |
226 | | // hide it. |
227 | | // 2. A menu popup is open, but there is no caret present in any popup. |
228 | | // 3. The caret selection is empty. |
229 | | bool IsMenuPopupHidingCaret(); |
230 | | |
231 | | nsWeakPtr mPresShell; |
232 | | mozilla::WeakPtr<mozilla::dom::Selection> mDomSelectionWeak; |
233 | | |
234 | | nsCOMPtr<nsITimer> mBlinkTimer; |
235 | | |
236 | | /** |
237 | | * The content to draw the caret at. If null, we use mDomSelectionWeak's |
238 | | * focus node instead. |
239 | | */ |
240 | | nsCOMPtr<nsINode> mOverrideContent; |
241 | | /** |
242 | | * The character offset to draw the caret at. |
243 | | * Ignored if mOverrideContent is null. |
244 | | */ |
245 | | int32_t mOverrideOffset; |
246 | | /** |
247 | | * mBlinkCount is used to control the number of times to blink the caret |
248 | | * before stopping the blink. This is reset each time we reset the |
249 | | * blinking. |
250 | | */ |
251 | | int32_t mBlinkCount; |
252 | | /** |
253 | | * mBlinkRate is the rate of the caret blinking the last time we read it. |
254 | | * It is used as a way to optimize whether we need to reset the blinking |
255 | | * timer. |
256 | | */ |
257 | | uint32_t mBlinkRate; |
258 | | /** |
259 | | * mHideCount is not 0, it means that somebody doesn't want the caret |
260 | | * to be visible. See AddForceHide() and RemoveForceHide(). |
261 | | */ |
262 | | uint32_t mHideCount; |
263 | | |
264 | | /** |
265 | | * mIsBlinkOn is true when we're in a blink cycle where the caret is on. |
266 | | */ |
267 | | bool mIsBlinkOn; |
268 | | /** |
269 | | * mIsVisible is true when SetVisible was last called with 'true'. |
270 | | */ |
271 | | bool mVisible; |
272 | | /** |
273 | | * mReadOnly is true when the caret is set to "read only" mode (i.e., |
274 | | * it doesn't blink). |
275 | | */ |
276 | | bool mReadOnly; |
277 | | /** |
278 | | * mShowDuringSelection is true when the caret should be shown even when |
279 | | * the selection is not collapsed. |
280 | | */ |
281 | | bool mShowDuringSelection; |
282 | | /** |
283 | | * mIgnoreUserModify is true when the caret should be shown even when |
284 | | * it's in non-user-modifiable content. |
285 | | */ |
286 | | bool mIgnoreUserModify; |
287 | | }; |
288 | | |
289 | | #endif //nsCaret_h__ |