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 90 HyperTextView::HyperTextView(BRect frame, const char* name, BRect textRect, 91 uint32 resizeMask, uint32 flags) 92 : 93 BTextView(frame, name, textRect, resizeMask, flags), 94 fActionInfos(new ActionInfoList(100, true)) 95 { 96 } 97 98 99 HyperTextView::~HyperTextView() 100 { 101 delete fActionInfos; 102 } 103 104 105 void 106 HyperTextView::MouseDown(BPoint where) 107 { 108 // We eat all mouse button events. 109 110 BTextView::MouseDown(where); 111 } 112 113 114 void 115 HyperTextView::MouseUp(BPoint where) 116 { 117 BMessage* message = Window()->CurrentMessage(); 118 119 HyperTextAction* action = _ActionAt(where); 120 if (action != NULL) 121 action->Clicked(this, where, message); 122 123 BTextView::MouseUp(where); 124 } 125 126 127 void 128 HyperTextView::MouseMoved(BPoint where, uint32 transit, 129 const BMessage* dragMessage) 130 { 131 BMessage* message = Window()->CurrentMessage(); 132 133 uint32 buttons; 134 HyperTextAction* action; 135 if (message->FindInt32("buttons", (int32*)&buttons) == B_OK 136 && buttons == 0 && (action = _ActionAt(where)) != NULL) { 137 action->MouseOver(this, where, message); 138 return; 139 } 140 141 BTextView::MouseMoved(where, transit, dragMessage); 142 } 143 144 145 void 146 HyperTextView::AddHyperTextAction(int32 startOffset, int32 endOffset, 147 HyperTextAction* action) 148 { 149 if (action == NULL || startOffset >= endOffset) { 150 delete action; 151 return; 152 } 153 154 fActionInfos->BinaryInsert(new ActionInfo(startOffset, endOffset, action), 155 ActionInfo::Compare); 156 157 // TODO: Of course we should check for overlaps... 158 } 159 160 161 void 162 HyperTextView::InsertHyperText(const char* inText, HyperTextAction* action, 163 const text_run_array* inRuns) 164 { 165 int32 startOffset = TextLength(); 166 Insert(inText, inRuns); 167 int32 endOffset = TextLength(); 168 169 AddHyperTextAction(startOffset, endOffset, action); 170 } 171 172 173 void 174 HyperTextView::InsertHyperText(const char* inText, int32 inLength, 175 HyperTextAction* action, const text_run_array* inRuns) 176 { 177 int32 startOffset = TextLength(); 178 Insert(inText, inLength, inRuns); 179 int32 endOffset = TextLength(); 180 181 AddHyperTextAction(startOffset, endOffset, action); 182 } 183 184 185 HyperTextAction* 186 HyperTextView::_ActionAt(const BPoint& where) const 187 { 188 int32 offset = OffsetAt(where); 189 190 ActionInfo pointer(offset, offset + 1, NULL); 191 192 const ActionInfo* action = fActionInfos->BinarySearch(pointer, 193 ActionInfo::CompareEqualIfIntersecting); 194 if (action != NULL) { 195 // verify that the text region was hit 196 BRegion textRegion; 197 GetTextRegion(action->startOffset, action->endOffset, &textRegion); 198 if (textRegion.Contains(where)) 199 return action->action; 200 } 201 202 return NULL; 203 } 204 205