xref: /haiku/src/apps/aboutsystem/HyperTextView.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
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 <Cursor.h>
9 #include <Message.h>
10 #include <Region.h>
11 #include <Window.h>
12 
13 #include <ObjectList.h>
14 
15 
16 // #pragma mark - HyperTextAction
17 
18 
19 HyperTextAction::HyperTextAction()
20 {
21 }
22 
23 
24 HyperTextAction::~HyperTextAction()
25 {
26 }
27 
28 
29 void
30 HyperTextAction::MouseOver(HyperTextView* view, BPoint where, BMessage* message)
31 {
32 	BCursor linkCursor(B_CURSOR_ID_FOLLOW_LINK);
33 	view->SetViewCursor(&linkCursor);
34 }
35 
36 
37 void
38 HyperTextAction::Clicked(HyperTextView* view, BPoint where, BMessage* message)
39 {
40 }
41 
42 
43 // #pragma mark - HyperTextView
44 
45 
46 struct HyperTextView::ActionInfo {
47 	ActionInfo(int32 startOffset, int32 endOffset, HyperTextAction* action)
48 		:
49 		startOffset(startOffset),
50 		endOffset(endOffset),
51 		action(action)
52 	{
53 	}
54 
55 	~ActionInfo()
56 	{
57 		delete action;
58 	}
59 
60 	static int Compare(const ActionInfo* a, const ActionInfo* b)
61 	{
62 		return a->startOffset - b->startOffset;
63 	}
64 
65 	static int CompareEqualIfIntersecting(const ActionInfo* a,
66 		const ActionInfo* b)
67 	{
68 		if (a->startOffset < b->endOffset && b->startOffset < a->endOffset)
69 			return 0;
70 		return a->startOffset - b->startOffset;
71 	}
72 
73 	int32				startOffset;
74 	int32				endOffset;
75 	HyperTextAction*	action;
76 };
77 
78 
79 
80 class HyperTextView::ActionInfoList
81 	: public BObjectList<HyperTextView::ActionInfo> {
82 public:
83 	ActionInfoList(int32 itemsPerBlock = 20, bool owning = false)
84 		: BObjectList<HyperTextView::ActionInfo>(itemsPerBlock, owning)
85 	{
86 	}
87 };
88 
89 
90 HyperTextView::HyperTextView(const char* name, uint32 flags)
91 	:
92 	BTextView(name, flags),
93 	fActionInfos(new ActionInfoList(100, true))
94 {
95 }
96 
97 
98 HyperTextView::HyperTextView(BRect frame, const char* name, BRect textRect,
99 	uint32 resizeMask, uint32 flags)
100 	:
101 	BTextView(frame, name, textRect, resizeMask, flags),
102 	fActionInfos(new ActionInfoList(100, true))
103 {
104 }
105 
106 
107 HyperTextView::~HyperTextView()
108 {
109 	delete fActionInfos;
110 }
111 
112 
113 void
114 HyperTextView::MouseDown(BPoint where)
115 {
116 	// We eat all mouse button events.
117 
118 	BTextView::MouseDown(where);
119 }
120 
121 
122 void
123 HyperTextView::MouseUp(BPoint where)
124 {
125 	BMessage* message = Window()->CurrentMessage();
126 
127 	HyperTextAction* action = _ActionAt(where);
128 	if (action != NULL)
129 		action->Clicked(this, where, message);
130 
131 	BTextView::MouseUp(where);
132 }
133 
134 
135 void
136 HyperTextView::MouseMoved(BPoint where, uint32 transit,
137 	const BMessage* dragMessage)
138 {
139 	BMessage* message = Window()->CurrentMessage();
140 
141 	uint32 buttons;
142 	HyperTextAction* action;
143 	if (message->FindInt32("buttons", (int32*)&buttons) == B_OK
144 		&& buttons == 0 && (action = _ActionAt(where)) != NULL) {
145 		action->MouseOver(this, where, message);
146 		return;
147 	}
148 
149 	BTextView::MouseMoved(where, transit, dragMessage);
150 }
151 
152 
153 void
154 HyperTextView::AddHyperTextAction(int32 startOffset, int32 endOffset,
155 	HyperTextAction* action)
156 {
157 	if (action == NULL || startOffset >= endOffset) {
158 		delete action;
159 		return;
160 	}
161 
162 	fActionInfos->BinaryInsert(new ActionInfo(startOffset, endOffset, action),
163 		ActionInfo::Compare);
164 
165 	// TODO: Of course we should check for overlaps...
166 }
167 
168 
169 void
170 HyperTextView::InsertHyperText(const char* inText, HyperTextAction* action,
171 	const text_run_array* inRuns)
172 {
173 	int32 startOffset = TextLength();
174 	Insert(inText, inRuns);
175 	int32 endOffset = TextLength();
176 
177 	AddHyperTextAction(startOffset, endOffset, action);
178 }
179 
180 
181 void
182 HyperTextView::InsertHyperText(const char* inText, int32 inLength,
183 	HyperTextAction* action, const text_run_array* inRuns)
184 {
185 	int32 startOffset = TextLength();
186 	Insert(inText, inLength, inRuns);
187 	int32 endOffset = TextLength();
188 
189 	AddHyperTextAction(startOffset, endOffset, action);
190 }
191 
192 
193 HyperTextAction*
194 HyperTextView::_ActionAt(const BPoint& where) const
195 {
196 	int32 offset = OffsetAt(where);
197 
198 	ActionInfo pointer(offset, offset + 1, NULL);
199 
200     const ActionInfo* action = fActionInfos->BinarySearch(pointer,
201 			ActionInfo::CompareEqualIfIntersecting);
202 	if (action != NULL) {
203 		// verify that the text region was hit
204 		BRegion textRegion;
205 		GetTextRegion(action->startOffset, action->endOffset, &textRegion);
206 		if (textRegion.Contains(where))
207 			return action->action;
208 	}
209 
210 	return NULL;
211 }
212 
213