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