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