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