1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "ImageFunctionsView.h" 7 8 #include <stdio.h> 9 10 #include <algorithm> 11 #include <new> 12 13 #include <AutoDeleter.h> 14 15 #include "table/TableColumns.h" 16 17 #include "FunctionInstance.h" 18 #include "Image.h" 19 #include "ImageDebugInfo.h" 20 #include "LocatableFile.h" 21 #include "Tracing.h" 22 23 24 // #pragma mark - FunctionsTableModel 25 26 27 class ImageFunctionsView::FunctionsTableModel : public TreeTableModel { 28 public: 29 FunctionsTableModel() 30 : 31 fImageDebugInfo(NULL), 32 fFunctions(NULL), 33 fFunctionCount(0), 34 fSourceFileIndices(NULL), 35 fSourceFileCount(0) 36 { 37 } 38 39 ~FunctionsTableModel() 40 { 41 SetImageDebugInfo(NULL); 42 } 43 44 void SetImageDebugInfo(ImageDebugInfo* imageDebugInfo) 45 { 46 // unset old functions 47 if (fSourceFileIndices != NULL) { 48 NotifyNodesRemoved(TreeTablePath(), 0, fSourceFileCount); 49 50 delete[] fFunctions; 51 fFunctions = NULL; 52 fFunctionCount = 0; 53 54 delete[] fSourceFileIndices; 55 fSourceFileIndices = NULL; 56 fSourceFileCount = 0; 57 } 58 59 fImageDebugInfo = imageDebugInfo; 60 61 // set new functions 62 if (fImageDebugInfo == NULL || fImageDebugInfo->CountFunctions() == 0) 63 return; 64 65 // create an array with the functions 66 int32 functionCount = fImageDebugInfo->CountFunctions(); 67 FunctionInstance** functions 68 = new(std::nothrow) FunctionInstance*[functionCount]; 69 if (functions == NULL) 70 return; 71 ArrayDeleter<FunctionInstance*> functionsDeleter(functions); 72 73 for (int32 i = 0; i < functionCount; i++) 74 functions[i] = fImageDebugInfo->FunctionAt(i); 75 76 // sort them 77 std::sort(functions, functions + functionCount, &_FunctionLess); 78 79 // eliminate duplicate function instances 80 if (functionCount > 0) { 81 Function* previousFunction = functions[0]->GetFunction(); 82 int32 removed = 0; 83 for (int32 i = 1; i < functionCount; i++) { 84 if (functions[i]->GetFunction() == previousFunction) { 85 removed++; 86 } else { 87 functions[i - removed] = functions[i]; 88 previousFunction = functions[i]->GetFunction(); 89 } 90 } 91 92 functionCount -= removed; 93 // The array might now be too large, but we can live with that. 94 } 95 96 // count the different source files 97 int32 sourceFileCount = 1; 98 for (int32 i = 1; i < functionCount; i++) { 99 if (_CompareSourceFileNames(functions[i - 1]->SourceFile(), 100 functions[i]->SourceFile()) != 0) { 101 sourceFileCount++; 102 } 103 } 104 105 // allocate and init the indices for the source files 106 fSourceFileIndices = new(std::nothrow) int32[sourceFileCount]; 107 if (fSourceFileIndices == NULL) 108 return; 109 fSourceFileCount = sourceFileCount; 110 111 fSourceFileIndices[0] = 0; 112 113 int32 sourceFileIndex = 1; 114 for (int32 i = 1; i < functionCount; i++) { 115 if (_CompareSourceFileNames(functions[i - 1]->SourceFile(), 116 functions[i]->SourceFile()) != 0) { 117 fSourceFileIndices[sourceFileIndex++] = i; 118 } 119 } 120 121 fFunctions = functionsDeleter.Detach(); 122 fFunctionCount = functionCount; 123 124 NotifyNodesAdded(TreeTablePath(), 0, fSourceFileCount); 125 } 126 127 virtual int32 CountColumns() const 128 { 129 return 1; 130 } 131 132 virtual void* Root() const 133 { 134 return (void*)this; 135 } 136 137 virtual int32 CountChildren(void* parent) const 138 { 139 if (parent == this) 140 return fSourceFileCount; 141 142 if (parent >= fSourceFileIndices 143 && parent < fSourceFileIndices + fSourceFileCount) { 144 int32 sourceIndex = (int32*)parent - fSourceFileIndices; 145 return _CountSourceFileFunctions(sourceIndex); 146 } 147 148 return 0; 149 } 150 151 virtual void* ChildAt(void* parent, int32 index) const 152 { 153 if (parent == this) { 154 return index >= 0 && index < fSourceFileCount 155 ? fSourceFileIndices + index : NULL; 156 } 157 158 if (parent >= fSourceFileIndices 159 && parent < fSourceFileIndices + fSourceFileCount) { 160 int32 sourceIndex = (int32*)parent - fSourceFileIndices; 161 int32 count = _CountSourceFileFunctions(sourceIndex); 162 int32 firstFunctionIndex = fSourceFileIndices[sourceIndex]; 163 return index >= 0 && index < count 164 ? fFunctions[firstFunctionIndex + index] : NULL; 165 } 166 167 return NULL; 168 } 169 170 virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value) 171 { 172 if (columnIndex != 0) 173 return false; 174 175 if (object == this) 176 return false; 177 178 if (object >= fSourceFileIndices 179 && object < fSourceFileIndices + fSourceFileCount) { 180 int32 index = *(int32*)object; 181 if (LocatableFile* file = fFunctions[index]->SourceFile()) { 182 BString path; 183 file->GetPath(path); 184 value.SetTo(path); 185 } else 186 value.SetTo("<no source file>", B_VARIANT_DONT_COPY_DATA); 187 188 return true; 189 } 190 191 FunctionInstance* function = (FunctionInstance*)object; 192 value.SetTo(function->PrettyName(), B_VARIANT_DONT_COPY_DATA); 193 return true; 194 } 195 196 bool GetFunctionPath(FunctionInstance* function, TreeTablePath& _path) 197 { 198 int32 index = -1; 199 for (int32 i = 0; i < fFunctionCount; i++) { 200 if (fFunctions[i] == function) { 201 index = i; 202 break; 203 } 204 } 205 206 if (index < 0) 207 return false; 208 209 int32 sourceIndex = fSourceFileCount - 1; 210 while (fSourceFileIndices[sourceIndex] > index) 211 sourceIndex--; 212 213 _path.Clear(); 214 return _path.AddComponent(sourceIndex) 215 && _path.AddComponent(index - fSourceFileIndices[sourceIndex]); 216 } 217 218 bool GetObjectForPath(const TreeTablePath& path, 219 LocatableFile*& _sourceFile, FunctionInstance*& _function) 220 { 221 int32 componentCount = path.CountComponents(); 222 if (componentCount == 0 || componentCount > 2) 223 return false; 224 225 int32 sourceIndex = path.ComponentAt(0); 226 if (sourceIndex < 0 || sourceIndex >= fSourceFileCount) 227 return false; 228 229 _sourceFile = fFunctions[fSourceFileIndices[sourceIndex]]->SourceFile(); 230 231 _function = NULL; 232 233 if (componentCount == 2) { 234 int32 index = path.ComponentAt(1); 235 if (index >= 0 && index < _CountSourceFileFunctions(sourceIndex)) 236 _function = fFunctions[fSourceFileIndices[sourceIndex] + index]; 237 } 238 239 return true; 240 } 241 242 int32 CountSourceFiles() const 243 { 244 return fSourceFileCount; 245 } 246 247 private: 248 int32 _CountSourceFileFunctions(int32 sourceIndex) const 249 { 250 if (sourceIndex < 0 || sourceIndex >= fSourceFileCount) 251 return 0; 252 253 int32 nextFunctionIndex = sourceIndex + 1 < fSourceFileCount 254 ? fSourceFileIndices[sourceIndex + 1] : fFunctionCount; 255 return nextFunctionIndex - fSourceFileIndices[sourceIndex]; 256 } 257 258 static int _CompareSourceFileNames(LocatableFile* a, LocatableFile* b) 259 { 260 if (a == b) 261 return 0; 262 263 if (a == NULL) 264 return 1; 265 if (b == NULL) 266 return -1; 267 268 BString pathA; 269 a->GetPath(pathA); 270 271 BString pathB; 272 b->GetPath(pathB); 273 274 return pathA.Compare(pathB); 275 } 276 277 static bool _FunctionLess(const FunctionInstance* a, 278 const FunctionInstance* b) 279 { 280 // compare source file name first 281 int compared = _CompareSourceFileNames(a->SourceFile(), 282 b->SourceFile()); 283 if (compared != 0) 284 return compared < 0; 285 286 // source file names are equal -- compare the function names 287 return strcasecmp(a->PrettyName(), b->PrettyName()) < 0; 288 } 289 290 private: 291 ImageDebugInfo* fImageDebugInfo; 292 FunctionInstance** fFunctions; 293 int32 fFunctionCount; 294 int32* fSourceFileIndices; 295 int32 fSourceFileCount; 296 }; 297 298 299 // #pragma mark - ImageFunctionsView 300 301 302 ImageFunctionsView::ImageFunctionsView(Listener* listener) 303 : 304 BGroupView(B_VERTICAL), 305 fImageDebugInfo(NULL), 306 fFunctionsTable(NULL), 307 fFunctionsTableModel(NULL), 308 fListener(listener) 309 { 310 SetName("Functions"); 311 } 312 313 314 ImageFunctionsView::~ImageFunctionsView() 315 { 316 SetImageDebugInfo(NULL); 317 fFunctionsTable->SetTreeTableModel(NULL); 318 delete fFunctionsTableModel; 319 } 320 321 322 /*static*/ ImageFunctionsView* 323 ImageFunctionsView::Create(Listener* listener) 324 { 325 ImageFunctionsView* self = new ImageFunctionsView(listener); 326 327 try { 328 self->_Init(); 329 } catch (...) { 330 delete self; 331 throw; 332 } 333 334 return self; 335 } 336 337 338 void 339 ImageFunctionsView::UnsetListener() 340 { 341 fListener = NULL; 342 } 343 344 345 void 346 ImageFunctionsView::SetImageDebugInfo(ImageDebugInfo* imageDebugInfo) 347 { 348 if (imageDebugInfo == fImageDebugInfo) 349 return; 350 351 TRACE_GUI("ImageFunctionsView::SetImageDebugInfo(%p)\n", imageDebugInfo); 352 353 if (fImageDebugInfo != NULL) 354 fImageDebugInfo->ReleaseReference(); 355 356 fImageDebugInfo = imageDebugInfo; 357 358 if (fImageDebugInfo != NULL) 359 fImageDebugInfo->AcquireReference(); 360 361 fFunctionsTableModel->SetImageDebugInfo(fImageDebugInfo); 362 363 // If there's only one source file (i.e. "no source file"), expand the item. 364 if (fImageDebugInfo != NULL 365 && fFunctionsTableModel->CountSourceFiles() == 1) { 366 TreeTablePath path; 367 path.AddComponent(0); 368 fFunctionsTable->SetNodeExpanded(path, true, false); 369 } 370 371 if (fImageDebugInfo != NULL) 372 fFunctionsTable->ResizeAllColumnsToPreferred(); 373 374 TRACE_GUI("ImageFunctionsView::SetImageDebugInfo(%p) done\n", 375 imageDebugInfo); 376 } 377 378 379 void 380 ImageFunctionsView::SetFunction(FunctionInstance* function) 381 { 382 TRACE_GUI("ImageFunctionsView::SetFunction(%p)\n", function); 383 384 TreeTablePath path; 385 if (fFunctionsTableModel->GetFunctionPath(function, path)) { 386 fFunctionsTable->SetNodeExpanded(path, true, true); 387 fFunctionsTable->SelectNode(path, false); 388 fFunctionsTable->ScrollToNode(path); 389 } else 390 fFunctionsTable->DeselectAllNodes(); 391 } 392 393 394 void 395 ImageFunctionsView::TreeTableSelectionChanged(TreeTable* table) 396 { 397 if (fListener == NULL) 398 return; 399 400 LocatableFile* sourceFile = NULL; 401 FunctionInstance* function = NULL; 402 TreeTablePath path; 403 if (table->SelectionModel()->GetPathAt(0, path)) 404 fFunctionsTableModel->GetObjectForPath(path, sourceFile, function); 405 406 fListener->FunctionSelectionChanged(function); 407 } 408 409 410 void 411 ImageFunctionsView::_Init() 412 { 413 fFunctionsTable = new TreeTable("functions", 0, B_FANCY_BORDER); 414 AddChild(fFunctionsTable->ToView()); 415 fFunctionsTable->SetSortingEnabled(false); 416 417 // columns 418 fFunctionsTable->AddColumn(new StringTableColumn(0, "File/Function", 300, 419 100, 1000, B_TRUNCATE_END, B_ALIGN_LEFT)); 420 421 fFunctionsTableModel = new FunctionsTableModel(); 422 fFunctionsTable->SetTreeTableModel(fFunctionsTableModel); 423 424 fFunctionsTable->SetSelectionMode(B_SINGLE_SELECTION_LIST); 425 fFunctionsTable->AddTreeTableListener(this); 426 } 427 428 429 // #pragma mark - Listener 430 431 432 ImageFunctionsView::Listener::~Listener() 433 { 434 } 435