1 | /*
|
---|
2 | * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
---|
3 | * Distributed under the terms of the MIT license.
|
---|
4 | */
|
---|
5 |
|
---|
6 | #include "HyperTextView.h"
|
---|
7 |
|
---|
8 | #include <Application.h>
|
---|
9 | #include <Cursor.h>
|
---|
10 | #include <Message.h>
|
---|
11 | #include <Region.h>
|
---|
12 | #include <Window.h>
|
---|
13 |
|
---|
14 | #include <ObjectList.h>
|
---|
15 |
|
---|
16 |
|
---|
17 | // #pragma mark - HyperTextAction
|
---|
18 |
|
---|
19 |
|
---|
20 | HyperTextAction::HyperTextAction(int32 event)
|
---|
21 | : fMessage(event)
|
---|
22 | {
|
---|
23 | }
|
---|
24 |
|
---|
25 |
|
---|
26 | HyperTextAction::~HyperTextAction()
|
---|
27 | {
|
---|
28 | }
|
---|
29 |
|
---|
30 |
|
---|
31 | #include <stdio.h>
|
---|
32 | void
|
---|
33 | HyperTextAction::MouseOver(HyperTextView* view, BPoint where, int32 startOffset,
|
---|
34 | int32 endOffset, BMessage* message)
|
---|
35 | {
|
---|
36 | BCursor linkCursor(B_CURSOR_ID_FOLLOW_LINK);
|
---|
37 | view->SetViewCursor(&linkCursor);
|
---|
38 |
|
---|
39 | BFont font;
|
---|
40 | view->GetFont(&font);
|
---|
41 | font.SetFace(B_UNDERSCORE_FACE);
|
---|
42 | view->SetFontAndColor(startOffset, endOffset, &font, B_FONT_FACE);
|
---|
43 | }
|
---|
44 |
|
---|
45 |
|
---|
46 | void
|
---|
47 | HyperTextAction::MouseAway(HyperTextView* view, BPoint where, int32 startOffset,
|
---|
48 | int32 endOffset, BMessage* message)
|
---|
49 | {
|
---|
50 | BCursor linkCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
|
---|
51 | view->SetViewCursor(&linkCursor);
|
---|
52 |
|
---|
53 | BFont font;
|
---|
54 | view->GetFont(&font);
|
---|
55 | font.SetFace(B_REGULAR_FACE);
|
---|
56 | view->SetFontAndColor(startOffset, endOffset, &font, B_FONT_FACE);
|
---|
57 | }
|
---|
58 |
|
---|
59 |
|
---|
60 | void
|
---|
61 | HyperTextAction::Clicked(HyperTextView* view, BPoint where, BMessage* message)
|
---|
62 | {
|
---|
63 | be_app->PostMessage(fMessage);
|
---|
64 | }
|
---|
65 |
|
---|
66 |
|
---|
67 | // #pragma mark - HyperTextView
|
---|
68 |
|
---|
69 |
|
---|
70 | struct HyperTextView::ActionInfo {
|
---|
71 | ActionInfo(int32 startOffset, int32 endOffset, HyperTextAction* action)
|
---|
72 | :
|
---|
73 | startOffset(startOffset),
|
---|
74 | endOffset(endOffset),
|
---|
75 | action(action)
|
---|
76 | {
|
---|
77 | }
|
---|
78 |
|
---|
79 | ~ActionInfo()
|
---|
80 | {
|
---|
81 | delete action;
|
---|
82 | }
|
---|
83 |
|
---|
84 | static int Compare(const ActionInfo* a, const ActionInfo* b)
|
---|
85 | {
|
---|
86 | return a->startOffset - b->startOffset;
|
---|
87 | }
|
---|
88 |
|
---|
89 | static int CompareEqualIfIntersecting(const ActionInfo* a,
|
---|
90 | const ActionInfo* b)
|
---|
91 | {
|
---|
92 | if (a->startOffset < b->endOffset && b->startOffset < a->endOffset)
|
---|
93 | return 0;
|
---|
94 | return a->startOffset - b->startOffset;
|
---|
95 | }
|
---|
96 |
|
---|
97 | int32 startOffset;
|
---|
98 | int32 endOffset;
|
---|
99 | HyperTextAction* action;
|
---|
100 | };
|
---|
101 |
|
---|
102 |
|
---|
103 |
|
---|
104 | class HyperTextView::ActionInfoList
|
---|
105 | : public BObjectList<HyperTextView::ActionInfo> {
|
---|
106 | public:
|
---|
107 | ActionInfoList(int32 itemsPerBlock = 20, bool owning = false)
|
---|
108 | : BObjectList<HyperTextView::ActionInfo>(itemsPerBlock, owning)
|
---|
109 | {
|
---|
110 | }
|
---|
111 | };
|
---|
112 |
|
---|
113 |
|
---|
114 | HyperTextView::HyperTextView(const char* name, uint32 flags)
|
---|
115 | :
|
---|
116 | BTextView(name, flags),
|
---|
117 | fActionInfos(new ActionInfoList(100, true)),
|
---|
118 | fLastActionInfo(NULL)
|
---|
119 | {
|
---|
120 | SetStylable(true);
|
---|
121 | }
|
---|
122 |
|
---|
123 |
|
---|
124 | HyperTextView::HyperTextView(BRect frame, const char* name, BRect textRect,
|
---|
125 | uint32 resizeMask, uint32 flags)
|
---|
126 | :
|
---|
127 | BTextView(frame, name, textRect, resizeMask, flags),
|
---|
128 | fActionInfos(new ActionInfoList(100, true)),
|
---|
129 | fLastActionInfo(NULL)
|
---|
130 | {
|
---|
131 | SetStylable(true);
|
---|
132 | }
|
---|
133 |
|
---|
134 |
|
---|
135 | HyperTextView::~HyperTextView()
|
---|
136 | {
|
---|
137 | delete fActionInfos;
|
---|
138 | }
|
---|
139 |
|
---|
140 |
|
---|
141 | void
|
---|
142 | HyperTextView::MouseDown(BPoint where)
|
---|
143 | {
|
---|
144 | // We eat all mouse button events.
|
---|
145 |
|
---|
146 | BTextView::MouseDown(where);
|
---|
147 | }
|
---|
148 |
|
---|
149 |
|
---|
150 | void
|
---|
151 | HyperTextView::MouseUp(BPoint where)
|
---|
152 | {
|
---|
153 | BMessage* message = Window()->CurrentMessage();
|
---|
154 |
|
---|
155 | HyperTextAction* action = _ActionAt(where);
|
---|
156 | if (action != NULL)
|
---|
157 | action->Clicked(this, where, message);
|
---|
158 |
|
---|
159 | BTextView::MouseUp(where);
|
---|
160 | }
|
---|
161 |
|
---|
162 |
|
---|
163 | void
|
---|
164 | HyperTextView::MouseMoved(BPoint where, uint32 transit,
|
---|
165 | const BMessage* dragMessage)
|
---|
166 | {
|
---|
167 | BMessage* message = Window()->CurrentMessage();
|
---|
168 |
|
---|
169 | HyperTextAction* action;
|
---|
170 | const ActionInfo* actionInfo = _ActionInfoAt(where);
|
---|
171 | if (actionInfo != fLastActionInfo) {
|
---|
172 | // We moved to a different "action" zone, de-highlight the previous one
|
---|
173 | if (fLastActionInfo != NULL) {
|
---|
174 | action = fLastActionInfo->action;
|
---|
175 | if (action != NULL) {
|
---|
176 | action->MouseAway(this, where, fLastActionInfo->startOffset,
|
---|
177 | fLastActionInfo->endOffset, message);
|
---|
178 | }
|
---|
179 | }
|
---|
180 |
|
---|
181 | // ... and highlight the new one
|
---|
182 | if (actionInfo != NULL) {
|
---|
183 | action = actionInfo->action;
|
---|
184 | if (action != NULL) {
|
---|
185 | action->MouseOver(this, where, actionInfo->startOffset,
|
---|
186 | actionInfo->endOffset, message);
|
---|
187 | }
|
---|
188 | }
|
---|
189 |
|
---|
190 | fLastActionInfo = actionInfo;
|
---|
191 | }
|
---|
192 |
|
---|
193 | int32 buttons = 0;
|
---|
194 | message->FindInt32("buttons", (int32*)&buttons);
|
---|
195 | if (actionInfo == NULL || buttons != 0) {
|
---|
196 | // This will restore the default mouse pointer, so do it only when not
|
---|
197 | // hovering a link, or when clicking
|
---|
198 | BTextView::MouseMoved(where, transit, dragMessage);
|
---|
199 | }
|
---|
200 | }
|
---|
201 |
|
---|
202 |
|
---|
203 | bool
|
---|
204 | HyperTextView::HasHeightForWidth()
|
---|
205 | {
|
---|
206 | return BView::HasHeightForWidth();
|
---|
207 | }
|
---|
208 |
|
---|
209 |
|
---|
210 | void
|
---|
211 | HyperTextView::GetHeightForWidth(float width, float* min, float* max, float* preferred)
|
---|
212 | {
|
---|
213 | BView::GetHeightForWidth(width, min, max, preferred);
|
---|
214 | }
|
---|
215 |
|
---|
216 |
|
---|
217 | void
|
---|
218 | HyperTextView::SetText(const char* text)
|
---|
219 | {
|
---|
220 | fLastActionInfo = NULL;
|
---|
221 | fActionInfos->MakeEmpty();
|
---|
222 | BTextView::SetText(text);
|
---|
223 | }
|
---|
224 |
|
---|
225 |
|
---|
226 | void
|
---|
227 | HyperTextView::AddHyperTextAction(int32 startOffset, int32 endOffset,
|
---|
228 | HyperTextAction* action)
|
---|
229 | {
|
---|
230 | if (action == NULL || startOffset >= endOffset) {
|
---|
231 | delete action;
|
---|
232 | return;
|
---|
233 | }
|
---|
234 |
|
---|
235 | fActionInfos->BinaryInsert(new ActionInfo(startOffset, endOffset, action),
|
---|
236 | ActionInfo::Compare);
|
---|
237 |
|
---|
238 | // TODO: Of course we should check for overlaps...
|
---|
239 | }
|
---|
240 |
|
---|
241 |
|
---|
242 | void
|
---|
243 | HyperTextView::InsertHyperText(const char* inText, HyperTextAction* action,
|
---|
244 | const text_run_array* inRuns)
|
---|
245 | {
|
---|
246 | int32 startOffset = TextLength();
|
---|
247 | Insert(inText, inRuns);
|
---|
248 | int32 endOffset = TextLength();
|
---|
249 |
|
---|
250 | AddHyperTextAction(startOffset, endOffset, action);
|
---|
251 | }
|
---|
252 |
|
---|
253 |
|
---|
254 | void
|
---|
255 | HyperTextView::InsertHyperText(const char* inText, int32 inLength,
|
---|
256 | HyperTextAction* action, const text_run_array* inRuns)
|
---|
257 | {
|
---|
258 | int32 startOffset = TextLength();
|
---|
259 | Insert(inText, inLength, inRuns);
|
---|
260 | int32 endOffset = TextLength();
|
---|
261 |
|
---|
262 | AddHyperTextAction(startOffset, endOffset, action);
|
---|
263 | }
|
---|
264 |
|
---|
265 |
|
---|
266 | const HyperTextView::ActionInfo*
|
---|
267 | HyperTextView::_ActionInfoAt(const BPoint& where) const
|
---|
268 | {
|
---|
269 | int32 offset = OffsetAt(where);
|
---|
270 |
|
---|
271 | ActionInfo pointer(offset, offset + 1, NULL);
|
---|
272 |
|
---|
273 | const ActionInfo* action = fActionInfos->BinarySearch(pointer,
|
---|
274 | ActionInfo::CompareEqualIfIntersecting);
|
---|
275 | return action;
|
---|
276 | }
|
---|
277 |
|
---|
278 |
|
---|
279 | HyperTextAction*
|
---|
280 | HyperTextView::_ActionAt(const BPoint& where) const
|
---|
281 | {
|
---|
282 | const ActionInfo* action = _ActionInfoAt(where);
|
---|
283 |
|
---|
284 | if (action != NULL) {
|
---|
285 | // verify that the text region was hit
|
---|
286 | BRegion textRegion;
|
---|
287 | GetTextRegion(action->startOffset, action->endOffset, &textRegion);
|
---|
288 | if (textRegion.Contains(where))
|
---|
289 | return action->action;
|
---|
290 | }
|
---|
291 |
|
---|
292 | return NULL;
|
---|
293 | }
|
---|
294 |
|
---|