xref: /haiku/src/apps/aboutsystem/HyperTextView.cpp (revision 758dae382b0d8e7aa43c5caca9e29dc7503012d0)
1b14a49c9SIngo Weinhold /*
2b14a49c9SIngo Weinhold  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3b14a49c9SIngo Weinhold  * Distributed under the terms of the MIT license.
4b14a49c9SIngo Weinhold  */
5b14a49c9SIngo Weinhold 
6b14a49c9SIngo Weinhold #include "HyperTextView.h"
7b14a49c9SIngo Weinhold 
88bd89640SStephan Aßmus #include <Cursor.h>
9b14a49c9SIngo Weinhold #include <Message.h>
10b14a49c9SIngo Weinhold #include <Region.h>
11b14a49c9SIngo Weinhold #include <Window.h>
12b14a49c9SIngo Weinhold 
13b14a49c9SIngo Weinhold #include <ObjectList.h>
14b14a49c9SIngo Weinhold 
15b14a49c9SIngo Weinhold 
16b14a49c9SIngo Weinhold // #pragma mark - HyperTextAction
17b14a49c9SIngo Weinhold 
18b14a49c9SIngo Weinhold 
HyperTextAction()19b14a49c9SIngo Weinhold HyperTextAction::HyperTextAction()
20b14a49c9SIngo Weinhold {
21b14a49c9SIngo Weinhold }
22b14a49c9SIngo Weinhold 
23b14a49c9SIngo Weinhold 
~HyperTextAction()24b14a49c9SIngo Weinhold HyperTextAction::~HyperTextAction()
25b14a49c9SIngo Weinhold {
26b14a49c9SIngo Weinhold }
27b14a49c9SIngo Weinhold 
28b14a49c9SIngo Weinhold 
29b14a49c9SIngo Weinhold void
MouseOver(HyperTextView * view,BPoint where,int32 startOffset,int32 endOffset,BMessage * message)30*758dae38SSambuddha Basu HyperTextAction::MouseOver(HyperTextView* view, BPoint where, int32 startOffset,
31*758dae38SSambuddha Basu 	int32 endOffset, BMessage* message)
328bd89640SStephan Aßmus {
33c3fa67f9SStephan Aßmus 	BCursor linkCursor(B_CURSOR_ID_FOLLOW_LINK);
34c3fa67f9SStephan Aßmus 	view->SetViewCursor(&linkCursor);
35*758dae38SSambuddha Basu 
36*758dae38SSambuddha Basu 	BFont font;
37*758dae38SSambuddha Basu 	view->GetFont(&font);
38*758dae38SSambuddha Basu 	font.SetFace(B_UNDERSCORE_FACE);
39*758dae38SSambuddha Basu 	view->SetFontAndColor(startOffset, endOffset, &font, B_FONT_FACE);
40*758dae38SSambuddha Basu }
41*758dae38SSambuddha Basu 
42*758dae38SSambuddha Basu 
43*758dae38SSambuddha Basu void
MouseAway(HyperTextView * view,BPoint where,int32 startOffset,int32 endOffset,BMessage * message)44*758dae38SSambuddha Basu HyperTextAction::MouseAway(HyperTextView* view, BPoint where, int32 startOffset,
45*758dae38SSambuddha Basu 	int32 endOffset, BMessage* message)
46*758dae38SSambuddha Basu {
47*758dae38SSambuddha Basu 	BCursor linkCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
48*758dae38SSambuddha Basu 	view->SetViewCursor(&linkCursor);
49*758dae38SSambuddha Basu 
50*758dae38SSambuddha Basu 	BFont font;
51*758dae38SSambuddha Basu 	view->GetFont(&font);
52*758dae38SSambuddha Basu 	font.SetFace(B_REGULAR_FACE);
53*758dae38SSambuddha Basu 	view->SetFontAndColor(startOffset, endOffset, &font, B_FONT_FACE);
548bd89640SStephan Aßmus }
558bd89640SStephan Aßmus 
568bd89640SStephan Aßmus 
578bd89640SStephan Aßmus void
Clicked(HyperTextView * view,BPoint where,BMessage * message)58b14a49c9SIngo Weinhold HyperTextAction::Clicked(HyperTextView* view, BPoint where, BMessage* message)
59b14a49c9SIngo Weinhold {
60b14a49c9SIngo Weinhold }
61b14a49c9SIngo Weinhold 
62b14a49c9SIngo Weinhold 
63b14a49c9SIngo Weinhold // #pragma mark - HyperTextView
64b14a49c9SIngo Weinhold 
65b14a49c9SIngo Weinhold 
66b14a49c9SIngo Weinhold struct HyperTextView::ActionInfo {
ActionInfoHyperTextView::ActionInfo67b14a49c9SIngo Weinhold 	ActionInfo(int32 startOffset, int32 endOffset, HyperTextAction* action)
68b14a49c9SIngo Weinhold 		:
69b14a49c9SIngo Weinhold 		startOffset(startOffset),
70b14a49c9SIngo Weinhold 		endOffset(endOffset),
71b14a49c9SIngo Weinhold 		action(action)
72b14a49c9SIngo Weinhold 	{
73b14a49c9SIngo Weinhold 	}
74b14a49c9SIngo Weinhold 
~ActionInfoHyperTextView::ActionInfo75b14a49c9SIngo Weinhold 	~ActionInfo()
76b14a49c9SIngo Weinhold 	{
77b14a49c9SIngo Weinhold 		delete action;
78b14a49c9SIngo Weinhold 	}
79b14a49c9SIngo Weinhold 
CompareHyperTextView::ActionInfo80b14a49c9SIngo Weinhold 	static int Compare(const ActionInfo* a, const ActionInfo* b)
81b14a49c9SIngo Weinhold 	{
82b14a49c9SIngo Weinhold 		return a->startOffset - b->startOffset;
83b14a49c9SIngo Weinhold 	}
84b14a49c9SIngo Weinhold 
CompareEqualIfIntersectingHyperTextView::ActionInfo85b14a49c9SIngo Weinhold 	static int CompareEqualIfIntersecting(const ActionInfo* a,
86b14a49c9SIngo Weinhold 		const ActionInfo* b)
87b14a49c9SIngo Weinhold 	{
88b14a49c9SIngo Weinhold 		if (a->startOffset < b->endOffset && b->startOffset < a->endOffset)
89b14a49c9SIngo Weinhold 			return 0;
90b14a49c9SIngo Weinhold 		return a->startOffset - b->startOffset;
91b14a49c9SIngo Weinhold 	}
92b14a49c9SIngo Weinhold 
93b14a49c9SIngo Weinhold 	int32				startOffset;
94b14a49c9SIngo Weinhold 	int32				endOffset;
95b14a49c9SIngo Weinhold 	HyperTextAction*	action;
96b14a49c9SIngo Weinhold };
97b14a49c9SIngo Weinhold 
98b14a49c9SIngo Weinhold 
99b14a49c9SIngo Weinhold 
100b14a49c9SIngo Weinhold class HyperTextView::ActionInfoList
101b14a49c9SIngo Weinhold 	: public BObjectList<HyperTextView::ActionInfo> {
102b14a49c9SIngo Weinhold public:
ActionInfoList(int32 itemsPerBlock=20,bool owning=false)103b14a49c9SIngo Weinhold 	ActionInfoList(int32 itemsPerBlock = 20, bool owning = false)
104b14a49c9SIngo Weinhold 		: BObjectList<HyperTextView::ActionInfo>(itemsPerBlock, owning)
105b14a49c9SIngo Weinhold 	{
106b14a49c9SIngo Weinhold 	}
107b14a49c9SIngo Weinhold };
108b14a49c9SIngo Weinhold 
109b14a49c9SIngo Weinhold 
HyperTextView(const char * name,uint32 flags)11099a8d301SAxel Dörfler HyperTextView::HyperTextView(const char* name, uint32 flags)
11199a8d301SAxel Dörfler 	:
11299a8d301SAxel Dörfler 	BTextView(name, flags),
113*758dae38SSambuddha Basu 	fActionInfos(new ActionInfoList(100, true)),
114*758dae38SSambuddha Basu 	fLastActionInfo(NULL)
11599a8d301SAxel Dörfler {
11699a8d301SAxel Dörfler }
11799a8d301SAxel Dörfler 
118b14a49c9SIngo Weinhold 
HyperTextView(BRect frame,const char * name,BRect textRect,uint32 resizeMask,uint32 flags)119b14a49c9SIngo Weinhold HyperTextView::HyperTextView(BRect frame, const char* name, BRect textRect,
120b14a49c9SIngo Weinhold 	uint32 resizeMask, uint32 flags)
121b14a49c9SIngo Weinhold 	:
122b14a49c9SIngo Weinhold 	BTextView(frame, name, textRect, resizeMask, flags),
123*758dae38SSambuddha Basu 	fActionInfos(new ActionInfoList(100, true)),
124*758dae38SSambuddha Basu 	fLastActionInfo(NULL)
125b14a49c9SIngo Weinhold {
126b14a49c9SIngo Weinhold }
127b14a49c9SIngo Weinhold 
128b14a49c9SIngo Weinhold 
~HyperTextView()129b14a49c9SIngo Weinhold HyperTextView::~HyperTextView()
130b14a49c9SIngo Weinhold {
131b14a49c9SIngo Weinhold 	delete fActionInfos;
132b14a49c9SIngo Weinhold }
133b14a49c9SIngo Weinhold 
134b14a49c9SIngo Weinhold 
135b14a49c9SIngo Weinhold void
MouseDown(BPoint where)136b14a49c9SIngo Weinhold HyperTextView::MouseDown(BPoint where)
137b14a49c9SIngo Weinhold {
138b14a49c9SIngo Weinhold 	// We eat all mouse button events.
1396504b223SJérôme Duval 
1406504b223SJérôme Duval 	BTextView::MouseDown(where);
141b14a49c9SIngo Weinhold }
142b14a49c9SIngo Weinhold 
143b14a49c9SIngo Weinhold 
144b14a49c9SIngo Weinhold void
MouseUp(BPoint where)145b14a49c9SIngo Weinhold HyperTextView::MouseUp(BPoint where)
146b14a49c9SIngo Weinhold {
147b14a49c9SIngo Weinhold 	BMessage* message = Window()->CurrentMessage();
148b14a49c9SIngo Weinhold 
1498bd89640SStephan Aßmus 	HyperTextAction* action = _ActionAt(where);
1508bd89640SStephan Aßmus 	if (action != NULL)
1518bd89640SStephan Aßmus 		action->Clicked(this, where, message);
1526504b223SJérôme Duval 
1536504b223SJérôme Duval 	BTextView::MouseUp(where);
154b14a49c9SIngo Weinhold }
1558bd89640SStephan Aßmus 
1568bd89640SStephan Aßmus 
1578bd89640SStephan Aßmus void
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)1588bd89640SStephan Aßmus HyperTextView::MouseMoved(BPoint where, uint32 transit,
1598bd89640SStephan Aßmus 	const BMessage* dragMessage)
1608bd89640SStephan Aßmus {
1618bd89640SStephan Aßmus 	BMessage* message = Window()->CurrentMessage();
1628bd89640SStephan Aßmus 
1638bd89640SStephan Aßmus 	HyperTextAction* action;
164*758dae38SSambuddha Basu 	const ActionInfo* actionInfo = _ActionInfoAt(where);
165*758dae38SSambuddha Basu 	if (actionInfo != fLastActionInfo) {
166*758dae38SSambuddha Basu 		// We moved to a different "action" zone, de-highlight the previous one
167*758dae38SSambuddha Basu 		if (fLastActionInfo != NULL) {
168*758dae38SSambuddha Basu 			action = fLastActionInfo->action;
169*758dae38SSambuddha Basu 			if (action != NULL) {
170*758dae38SSambuddha Basu 				action->MouseAway(this, where, fLastActionInfo->startOffset,
171*758dae38SSambuddha Basu 						fLastActionInfo->endOffset, message);
172*758dae38SSambuddha Basu 			}
1738bd89640SStephan Aßmus 		}
1748bd89640SStephan Aßmus 
175*758dae38SSambuddha Basu 		// ... and highlight the new one
176*758dae38SSambuddha Basu 		if (actionInfo != NULL) {
177*758dae38SSambuddha Basu 			action = actionInfo->action;
178*758dae38SSambuddha Basu 			if (action != NULL) {
179*758dae38SSambuddha Basu 				action->MouseOver(this, where, actionInfo->startOffset,
180*758dae38SSambuddha Basu 						actionInfo->endOffset, message);
181*758dae38SSambuddha Basu 			}
182*758dae38SSambuddha Basu 		}
183*758dae38SSambuddha Basu 
184*758dae38SSambuddha Basu 		fLastActionInfo = actionInfo;
185*758dae38SSambuddha Basu 	}
186*758dae38SSambuddha Basu 
187*758dae38SSambuddha Basu 	int32 buttons = 0;
188*758dae38SSambuddha Basu 	message->FindInt32("buttons", (int32*)&buttons);
189*758dae38SSambuddha Basu 	if (actionInfo == NULL || buttons != 0) {
190*758dae38SSambuddha Basu 		// This will restore the default mouse pointer, so do it only when not
191*758dae38SSambuddha Basu 		// hovering a link, or when clicking
1928bd89640SStephan Aßmus 		BTextView::MouseMoved(where, transit, dragMessage);
193b14a49c9SIngo Weinhold 	}
194*758dae38SSambuddha Basu }
195b14a49c9SIngo Weinhold 
196b14a49c9SIngo Weinhold 
197b14a49c9SIngo Weinhold void
AddHyperTextAction(int32 startOffset,int32 endOffset,HyperTextAction * action)198b14a49c9SIngo Weinhold HyperTextView::AddHyperTextAction(int32 startOffset, int32 endOffset,
199b14a49c9SIngo Weinhold 	HyperTextAction* action)
200b14a49c9SIngo Weinhold {
201b14a49c9SIngo Weinhold 	if (action == NULL || startOffset >= endOffset) {
202b14a49c9SIngo Weinhold 		delete action;
203b14a49c9SIngo Weinhold 		return;
204b14a49c9SIngo Weinhold 	}
205b14a49c9SIngo Weinhold 
206b14a49c9SIngo Weinhold 	fActionInfos->BinaryInsert(new ActionInfo(startOffset, endOffset, action),
207b14a49c9SIngo Weinhold 		ActionInfo::Compare);
208b14a49c9SIngo Weinhold 
209b14a49c9SIngo Weinhold 	// TODO: Of course we should check for overlaps...
210b14a49c9SIngo Weinhold }
211b14a49c9SIngo Weinhold 
212b14a49c9SIngo Weinhold 
213b14a49c9SIngo Weinhold void
InsertHyperText(const char * inText,HyperTextAction * action,const text_run_array * inRuns)214b14a49c9SIngo Weinhold HyperTextView::InsertHyperText(const char* inText, HyperTextAction* action,
215b14a49c9SIngo Weinhold 	const text_run_array* inRuns)
216b14a49c9SIngo Weinhold {
217b14a49c9SIngo Weinhold 	int32 startOffset = TextLength();
218b14a49c9SIngo Weinhold 	Insert(inText, inRuns);
219b14a49c9SIngo Weinhold 	int32 endOffset = TextLength();
220b14a49c9SIngo Weinhold 
221b14a49c9SIngo Weinhold 	AddHyperTextAction(startOffset, endOffset, action);
222b14a49c9SIngo Weinhold }
223b14a49c9SIngo Weinhold 
224b14a49c9SIngo Weinhold 
225b14a49c9SIngo Weinhold void
InsertHyperText(const char * inText,int32 inLength,HyperTextAction * action,const text_run_array * inRuns)226b14a49c9SIngo Weinhold HyperTextView::InsertHyperText(const char* inText, int32 inLength,
227b14a49c9SIngo Weinhold 	HyperTextAction* action, const text_run_array* inRuns)
228b14a49c9SIngo Weinhold {
229b14a49c9SIngo Weinhold 	int32 startOffset = TextLength();
230b14a49c9SIngo Weinhold 	Insert(inText, inLength, inRuns);
231b14a49c9SIngo Weinhold 	int32 endOffset = TextLength();
232b14a49c9SIngo Weinhold 
233b14a49c9SIngo Weinhold 	AddHyperTextAction(startOffset, endOffset, action);
234b14a49c9SIngo Weinhold }
2358bd89640SStephan Aßmus 
2368bd89640SStephan Aßmus 
237*758dae38SSambuddha Basu const HyperTextView::ActionInfo*
_ActionInfoAt(const BPoint & where) const238*758dae38SSambuddha Basu HyperTextView::_ActionInfoAt(const BPoint& where) const
2398bd89640SStephan Aßmus {
2408bd89640SStephan Aßmus 	int32 offset = OffsetAt(where);
2418bd89640SStephan Aßmus 
2428bd89640SStephan Aßmus 	ActionInfo pointer(offset, offset + 1, NULL);
2438bd89640SStephan Aßmus 
2448bd89640SStephan Aßmus 	const ActionInfo* action = fActionInfos->BinarySearch(pointer,
2458bd89640SStephan Aßmus 			ActionInfo::CompareEqualIfIntersecting);
246*758dae38SSambuddha Basu 	return action;
247*758dae38SSambuddha Basu }
248*758dae38SSambuddha Basu 
249*758dae38SSambuddha Basu 
250*758dae38SSambuddha Basu HyperTextAction*
_ActionAt(const BPoint & where) const251*758dae38SSambuddha Basu HyperTextView::_ActionAt(const BPoint& where) const
252*758dae38SSambuddha Basu {
253*758dae38SSambuddha Basu 	const ActionInfo* action = _ActionInfoAt(where);
254*758dae38SSambuddha Basu 
2558bd89640SStephan Aßmus 	if (action != NULL) {
2568bd89640SStephan Aßmus 		// verify that the text region was hit
2578bd89640SStephan Aßmus 		BRegion textRegion;
2588bd89640SStephan Aßmus 		GetTextRegion(action->startOffset, action->endOffset, &textRegion);
2598bd89640SStephan Aßmus 		if (textRegion.Contains(where))
2608bd89640SStephan Aßmus 			return action->action;
2618bd89640SStephan Aßmus 	}
2628bd89640SStephan Aßmus 
2638bd89640SStephan Aßmus 	return NULL;
2648bd89640SStephan Aßmus }
2658bd89640SStephan Aßmus 
266