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