1/* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2011-2017, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8#include "ThreadListView.h" 9 10#include <new> 11 12#include <Looper.h> 13#include <Message.h> 14 15#include <AutoLocker.h> 16#include <ControlLook.h> 17#include <ObjectList.h> 18#include <ToolTip.h> 19 20#include "GuiSettingsUtils.h" 21#include "table/TableColumns.h" 22#include "UiUtils.h" 23 24 25enum { 26 MSG_SYNC_THREAD_LIST = 'sytl' 27}; 28 29 30// #pragma mark - ThreadsTableModel 31 32 33class ThreadListView::ThreadsTableModel : public TableModel, 34 public TableToolTipProvider { 35public: 36 ThreadsTableModel(Team* team) 37 : 38 fTeam(team) 39 { 40 Update(-1); 41 } 42 43 ~ThreadsTableModel() 44 { 45 fTeam = NULL; 46 Update(-1); 47 } 48 49 bool Update(thread_id threadID) 50 { 51 if (fTeam == NULL) { 52 for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++) 53 thread->ReleaseReference(); 54 fThreads.MakeEmpty(); 55 56 return true; 57 } 58 59 AutoLocker<Team> locker(fTeam); 60 61 ThreadList::ConstIterator it = fTeam->Threads().GetIterator(); 62 Thread* newThread = it.Next(); 63 int32 index = 0; 64 65 // remove no longer existing threads 66 while (Thread* oldThread = fThreads.ItemAt(index)) { 67 if (oldThread == newThread) { 68 if (threadID >= 0 && oldThread->ID() == threadID) 69 NotifyRowsChanged(index, 1); 70 index++; 71 newThread = it.Next(); 72 } else { 73 // TODO: Not particularly efficient! 74 fThreads.RemoveItemAt(index); 75 oldThread->ReleaseReference(); 76 NotifyRowsRemoved(index, 1); 77 } 78 } 79 80 // add new threads 81 int32 countBefore = fThreads.CountItems(); 82 while (newThread != NULL) { 83 if (!fThreads.AddItem(newThread)) 84 return false; 85 86 newThread->AcquireReference(); 87 newThread = it.Next(); 88 } 89 90 int32 count = fThreads.CountItems(); 91 if (count > countBefore) 92 NotifyRowsAdded(countBefore, count - countBefore); 93 94 return true; 95 } 96 97 virtual int32 CountColumns() const 98 { 99 return 4; 100 } 101 102 virtual int32 CountRows() const 103 { 104 return fThreads.CountItems(); 105 } 106 107 virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value) 108 { 109 Thread* thread = fThreads.ItemAt(rowIndex); 110 if (thread == NULL) 111 return false; 112 113 switch (columnIndex) { 114 case 0: 115 value.SetTo(thread->ID()); 116 return true; 117 case 1: 118 { 119 const char* string = UiUtils::ThreadStateToString( 120 thread->State(), thread->StoppedReason()); 121 value.SetTo(string, B_VARIANT_DONT_COPY_DATA); 122 return true; 123 } 124 case 2: 125 value.SetTo(thread->Name(), B_VARIANT_DONT_COPY_DATA); 126 return true; 127 case 3: 128 { 129 if (thread->State() != THREAD_STATE_RUNNING) { 130 value.SetTo(thread->StoppedReasonInfo(), 131 B_VARIANT_DONT_COPY_DATA); 132 return true; 133 } 134 return false; 135 } 136 default: 137 return false; 138 } 139 } 140 141 virtual bool GetToolTipForTableCell(int32 rowIndex, int32 columnIndex, 142 BToolTip** _tip) 143 { 144 Thread* thread = fThreads.ItemAt(rowIndex); 145 if (thread == NULL) 146 return false; 147 148 BString text; 149 text << "Thread: \"" << thread->Name() << "\" (" << thread->ID() 150 << ")\n"; 151 152 switch (thread->State()) { 153 case THREAD_STATE_RUNNING: 154 text << "Running"; 155 break; 156 case THREAD_STATE_STOPPED: 157 { 158 switch (thread->StoppedReason()) { 159 case THREAD_STOPPED_DEBUGGER_CALL: 160 text << "Called debugger(): " 161 << thread->StoppedReasonInfo(); 162 break; 163 case THREAD_STOPPED_EXCEPTION: 164 text << "Caused exception: " 165 << thread->StoppedReasonInfo(); 166 break; 167 case THREAD_STOPPED_BREAKPOINT: 168 case THREAD_STOPPED_WATCHPOINT: 169 case THREAD_STOPPED_SINGLE_STEP: 170 case THREAD_STOPPED_DEBUGGED: 171 case THREAD_STOPPED_UNKNOWN: 172 default: 173 text << "Stopped for debugging"; 174 break; 175 } 176 break; 177 } 178 case THREAD_STATE_UNKNOWN: 179 default: 180 text << "Current State Unknown"; 181 break; 182 } 183 184 BTextToolTip* tip = new(std::nothrow) BTextToolTip(text); 185 if (tip == NULL) 186 return false; 187 188 *_tip = tip; 189 return true; 190 } 191 192 Thread* ThreadAt(int32 index) const 193 { 194 return fThreads.ItemAt(index); 195 } 196 197private: 198 Team* fTeam; 199 BObjectList<Thread> fThreads; 200}; 201 202 203// #pragma mark - ThreadListView 204 205 206ThreadListView::ThreadListView(Team* team, Listener* listener) 207 : 208 BGroupView(B_VERTICAL), 209 fTeam(team), 210 fThread(NULL), 211 fThreadsTable(NULL), 212 fThreadsTableModel(NULL), 213 fListener(listener) 214{ 215 SetName("Threads"); 216} 217 218 219ThreadListView::~ThreadListView() 220{ 221 fTeam->RemoveListener(this); 222 fThreadsTable->SetTableModel(NULL); 223 delete fThreadsTableModel; 224} 225 226 227/*static*/ ThreadListView* 228ThreadListView::Create(Team* team, Listener* listener) 229{ 230 ThreadListView* self = new ThreadListView(team, listener); 231 232 try { 233 self->_Init(); 234 } catch (...) { 235 delete self; 236 throw; 237 } 238 239 return self; 240} 241 242 243void 244ThreadListView::UnsetListener() 245{ 246 fListener = NULL; 247} 248 249 250void 251ThreadListView::SetThread(Thread* thread) 252{ 253 if (thread == fThread) 254 return; 255 256 if (fThread != NULL) 257 fThread->ReleaseReference(); 258 259 fThread = thread; 260 261 if (fThread != NULL) { 262 fThread->AcquireReference(); 263 264 for (int32 i = 0; Thread* other = fThreadsTableModel->ThreadAt(i); 265 i++) { 266 if (fThread == other) { 267 fThreadsTable->SelectRow(i, false); 268 return; 269 } 270 } 271 } 272 273 fThreadsTable->DeselectAllRows(); 274} 275 276 277void 278ThreadListView::MessageReceived(BMessage* message) 279{ 280 switch (message->what) { 281 case MSG_SYNC_THREAD_LIST: 282 { 283 thread_id threadID; 284 if (message->FindInt32("thread", &threadID) != B_OK) 285 threadID = -1; 286 287 fThreadsTableModel->Update(threadID); 288 break; 289 } 290 default: 291 BGroupView::MessageReceived(message); 292 break; 293 } 294} 295 296 297void 298ThreadListView::LoadSettings(const BMessage& settings) 299{ 300 BMessage tableSettings; 301 if (settings.FindMessage("threadsTable", &tableSettings) == B_OK) { 302 GuiSettingsUtils::UnarchiveTableSettings(tableSettings, 303 fThreadsTable); 304 } 305} 306 307 308status_t 309ThreadListView::SaveSettings(BMessage& settings) 310{ 311 settings.MakeEmpty(); 312 313 BMessage tableSettings; 314 status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings, 315 fThreadsTable); 316 if (result == B_OK) 317 result = settings.AddMessage("threadsTable", &tableSettings); 318 319 return result; 320} 321 322 323 324 325void 326ThreadListView::ThreadAdded(const Team::ThreadEvent& event) 327{ 328 Looper()->PostMessage(MSG_SYNC_THREAD_LIST, this); 329} 330 331 332void 333ThreadListView::ThreadRemoved(const Team::ThreadEvent& event) 334{ 335 Looper()->PostMessage(MSG_SYNC_THREAD_LIST, this); 336} 337 338 339void 340ThreadListView::ThreadStateChanged(const Team::ThreadEvent& event) 341{ 342 BMessage message(MSG_SYNC_THREAD_LIST); 343 message.AddInt32("thread", event.GetThread()->ID()); 344 345 Looper()->PostMessage(&message, this); 346} 347 348 349void 350ThreadListView::TableSelectionChanged(Table* table) 351{ 352 if (fListener == NULL) 353 return; 354 355 Thread* thread = NULL; 356 TableSelectionModel* selectionModel = table->SelectionModel(); 357 thread = fThreadsTableModel->ThreadAt(selectionModel->RowAt(0)); 358 359 fListener->ThreadSelectionChanged(thread); 360} 361 362 363void 364ThreadListView::_Init() 365{ 366 fThreadsTable = new Table("threads list", 0, B_FANCY_BORDER); 367 AddChild(fThreadsTable->ToView()); 368 369 // columns 370 const float padding = be_control_look->DefaultLabelSpacing() * 2; 371 fThreadsTable->AddColumn(new Int32TableColumn(0, "ID", 372 be_plain_font->StringWidth("12345") + padding, 20, 1000, 373 B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT)); 374 fThreadsTable->AddColumn(new StringTableColumn(1, "State", 375 be_plain_font->StringWidth("Debugged") + padding, 40, 1000, 376 B_TRUNCATE_END, B_ALIGN_LEFT)); 377 fThreadsTable->AddColumn(new StringTableColumn(2, "Name", 200, 40, 1000, 378 B_TRUNCATE_END, B_ALIGN_LEFT)); 379 fThreadsTable->AddColumn(new StringTableColumn(3, "Stop reason", 380 200, 40, 1000, B_TRUNCATE_END, B_ALIGN_LEFT)); 381 382 fThreadsTable->SetSelectionMode(B_SINGLE_SELECTION_LIST); 383 fThreadsTable->AddTableListener(this); 384 385 fThreadsTableModel = new ThreadsTableModel(fTeam); 386 fThreadsTable->SetTableModel(fThreadsTableModel); 387 fThreadsTable->SetToolTipProvider(fThreadsTableModel); 388 fTeam->AddListener(this); 389} 390 391 392// #pragma mark - Listener 393 394 395ThreadListView::Listener::~Listener() 396{ 397} 398