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