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