1/*
2 * Copyright 2009-2010, Philippe Houdoin, phoudoin@haiku-os.org. All rights reserved.
3 * Copyright 2013-2016, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6#include "TeamsWindow.h"
7
8
9#include <new>
10
11#include <stdio.h>
12#include <string.h>
13#include <stdarg.h>
14
15#include <Application.h>
16#include <Button.h>
17#include <ControlLook.h>
18#include <File.h>
19#include <FilePanel.h>
20#include <FindDirectory.h>
21#include <LayoutBuilder.h>
22#include <ListView.h>
23#include <Menu.h>
24#include <MenuField.h>
25#include <Path.h>
26#include <Screen.h>
27#include <ScrollView.h>
28
29#include <AutoDeleter.h>
30#include <AutoLocker.h>
31
32#include "AppMessageCodes.h"
33#include "SettingsManager.h"
34#include "TargetHostInterface.h"
35#include "TargetHostInterfaceRoster.h"
36#include "TeamsListView.h"
37
38
39enum {
40	MSG_TEAM_SELECTION_CHANGED = 'tesc',
41	MSG_CHOSE_CORE_FILE = 'chcf',
42	MSG_SWITCH_TARGET_CONNECTION = 'stco'
43};
44
45
46TeamsWindow::TeamsWindow(SettingsManager* settingsManager)
47	:
48	BWindow(BRect(100, 100, 500, 500), "Teams", B_DOCUMENT_WINDOW,
49		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
50	fTargetHostInterface(NULL),
51	fTeamsListView(NULL),
52	fAttachTeamButton(NULL),
53	fCreateTeamButton(NULL),
54	fLoadCoreButton(NULL),
55	fConnectionField(NULL),
56	fCoreSelectionPanel(NULL),
57	fSettingsManager(settingsManager),
58	fListeners()
59{
60	team_info info;
61	get_team_info(B_CURRENT_TEAM, &info);
62	fCurrentTeam = info.team;
63}
64
65
66TeamsWindow::~TeamsWindow()
67{
68}
69
70
71/*static*/ TeamsWindow*
72TeamsWindow::Create(SettingsManager* settingsManager)
73{
74	TeamsWindow* self = new TeamsWindow(settingsManager);
75
76	try {
77		self->_Init();
78	} catch (...) {
79		delete self;
80		throw;
81	}
82
83	return self;
84}
85
86
87void
88TeamsWindow::Zoom(BPoint, float, float)
89{
90	BSize preferredSize = fTeamsListView->PreferredSize();
91	ResizeBy(preferredSize.Width() - Bounds().Width(),
92		0.0);
93
94	// if the new size would extend us past the screen border,
95	// move sufficiently to the left to bring us back within the bounds
96	// + a bit of extra margin so we're not flush against the edge.
97	BScreen screen;
98	float offset = screen.Frame().right - Frame().right;
99	if (offset < 0)
100		MoveBy(offset - 5.0, 0.0);
101}
102
103
104void
105TeamsWindow::MessageReceived(BMessage* message)
106{
107	switch (message->what) {
108		case MSG_DEBUG_THIS_TEAM:
109		{
110			TeamRow* row = dynamic_cast<TeamRow*>(
111				fTeamsListView->CurrentSelection());
112			if (row != NULL) {
113				BMessage message(MSG_DEBUG_THIS_TEAM);
114				message.AddInt32("team", row->TeamID());
115				message.AddPointer("interface", fTargetHostInterface);
116				be_app_messenger.SendMessage(&message);
117			}
118			break;
119		}
120
121		case MSG_TEAM_SELECTION_CHANGED:
122		{
123			TeamRow* row = dynamic_cast<TeamRow*>(
124				fTeamsListView->CurrentSelection());
125			bool enabled = false;
126			if (row != NULL) {
127				team_id id = row->TeamID();
128				if (id != fCurrentTeam && id != B_SYSTEM_TEAM)
129					enabled = true;
130			}
131
132			fAttachTeamButton->SetEnabled(enabled);
133			break;
134		}
135
136		case MSG_SHOW_START_TEAM_WINDOW:
137		{
138			BMessage startMessage(*message);
139			startMessage.AddPointer("interface", fTargetHostInterface);
140			be_app_messenger.SendMessage(&startMessage);
141			break;
142		}
143
144		case MSG_CHOSE_CORE_FILE:
145		case B_CANCEL:
146		{
147			delete fCoreSelectionPanel;
148			fCoreSelectionPanel = NULL;
149			if (message->what == B_CANCEL)
150				break;
151
152			entry_ref ref;
153			if (message->FindRef("refs", &ref) != B_OK)
154				break;
155
156			BMessage coreMessage(MSG_LOAD_CORE_TEAM);
157			coreMessage.AddPointer("interface", fTargetHostInterface);
158			coreMessage.AddRef("core", &ref);
159			be_app_messenger.SendMessage(&coreMessage);
160			break;
161		}
162
163		case MSG_LOAD_CORE_TEAM:
164		{
165			try {
166				BMessenger messenger(this);
167				fCoreSelectionPanel = new BFilePanel(B_OPEN_PANEL, &messenger,
168					NULL, 0, false, new BMessage(MSG_CHOSE_CORE_FILE));
169				fCoreSelectionPanel->Show();
170			} catch (...) {
171				delete fCoreSelectionPanel;
172				fCoreSelectionPanel = NULL;
173			}
174			break;
175		}
176
177		case MSG_SWITCH_TARGET_CONNECTION:
178		{
179			TargetHostInterface* interface;
180			if (message->FindPointer("interface", reinterpret_cast<void**>(
181					&interface)) != B_OK) {
182				break;
183			}
184
185			if (interface == fTargetHostInterface)
186				break;
187
188			fTargetHostInterface = interface;
189			_NotifySelectedInterfaceChanged(interface);
190			fLoadCoreButton->SetEnabled(fTargetHostInterface->IsLocal());
191			break;
192		}
193
194		default:
195			BWindow::MessageReceived(message);
196			break;
197	}
198}
199
200
201bool
202TeamsWindow::QuitRequested()
203{
204	_SaveSettings();
205
206	be_app_messenger.SendMessage(MSG_TEAMS_WINDOW_CLOSED);
207	return true;
208}
209
210
211void
212TeamsWindow::AddListener(Listener* listener)
213{
214	AutoLocker<TeamsWindow> locker(this);
215	fListeners.Add(listener);
216}
217
218
219void
220TeamsWindow::RemoveListener(Listener* listener)
221{
222	AutoLocker<TeamsWindow> locker(this);
223	fListeners.Remove(listener);
224}
225
226
227// #pragma mark --
228
229
230void
231TeamsWindow::_Init()
232{
233	BMessage settings;
234	_LoadSettings(settings);
235
236	BRect frame;
237	if (settings.FindRect("teams window frame", &frame) == B_OK) {
238		MoveTo(frame.LeftTop());
239		ResizeTo(frame.Width(), frame.Height());
240	}
241
242	BMenu* connectionMenu = new BMenu("Connection");
243	ObjectDeleter<BMenu> menuDeleter(connectionMenu);
244	connectionMenu->SetLabelFromMarked(true);
245
246	TargetHostInterfaceRoster* roster = TargetHostInterfaceRoster::Default();
247	for (int32 i = 0; i < roster->CountActiveInterfaces(); i++) {
248		TargetHostInterface* interface = roster->ActiveInterfaceAt(i);
249		BMenuItem* item = new BMenuItem(interface->GetTargetHost()->Name(),
250			new BMessage(MSG_SWITCH_TARGET_CONNECTION));
251		if (item->Message()->AddPointer("interface", interface) != B_OK) {
252			delete item;
253			throw std::bad_alloc();
254		}
255
256		if (interface->IsLocal()) {
257			item->SetMarked(true);
258			fTargetHostInterface = interface;
259		}
260
261		connectionMenu->AddItem(item);
262	}
263
264	BGroupLayout* connectionLayout = NULL;
265
266	BLayoutBuilder::Group<>(this, B_VERTICAL)
267		.AddGroup(B_HORIZONTAL)
268			.SetInsets(B_USE_DEFAULT_SPACING)
269			.GetLayout(&connectionLayout)
270			.Add(fConnectionField = new BMenuField("Connected to:",
271				connectionMenu))
272			.AddGlue()
273			.Add(fCreateConnectionButton = new BButton("Create new connection"
274					B_UTF8_ELLIPSIS, new BMessage(
275						MSG_SHOW_CONNECTION_CONFIG_WINDOW)))
276		.End()
277		.Add(fTeamsListView = new TeamsListView("TeamsList"))
278		.AddGroup(B_HORIZONTAL)
279			.SetInsets(B_USE_ITEM_SPACING, 0,
280				be_control_look->GetScrollBarWidth(B_VERTICAL),
281				be_control_look->GetScrollBarWidth(B_HORIZONTAL))
282			.Add(fAttachTeamButton = new BButton("Attach", new BMessage(
283					MSG_DEBUG_THIS_TEAM)))
284			.AddGlue()
285			.Add(fCreateTeamButton = new BButton("Start new team"
286					B_UTF8_ELLIPSIS, new BMessage(MSG_SHOW_START_TEAM_WINDOW)))
287			.Add(fLoadCoreButton = new BButton("Load core" B_UTF8_ELLIPSIS,
288					new BMessage(MSG_LOAD_CORE_TEAM)))
289			.End()
290		.End();
291
292	connectionLayout->SetVisible(false);
293
294	menuDeleter.Detach();
295
296	AddListener(fTeamsListView);
297
298	connectionMenu->SetTargetForItems(this);
299
300	fTeamsListView->SetInvocationMessage(new BMessage(MSG_DEBUG_THIS_TEAM));
301	fTeamsListView->SetSelectionMessage(new BMessage(
302			MSG_TEAM_SELECTION_CHANGED));
303
304	fAttachTeamButton->SetEnabled(false);
305	fCreateTeamButton->SetTarget(this);
306	fLoadCoreButton->SetTarget(this);
307	fCreateConnectionButton->SetTarget(be_app);
308
309	_NotifySelectedInterfaceChanged(fTargetHostInterface);
310}
311
312
313status_t
314TeamsWindow::_OpenSettings(BFile& file, uint32 mode)
315{
316	BPath path;
317	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
318		return B_ERROR;
319
320	path.Append("Debugger settings");
321
322	return file.SetTo(path.Path(), mode);
323}
324
325
326status_t
327TeamsWindow::_LoadSettings(BMessage& settings)
328{
329	BFile file;
330	status_t status = _OpenSettings(file, B_READ_ONLY);
331	if (status < B_OK)
332		return status;
333
334	return settings.Unflatten(&file);
335}
336
337
338status_t
339TeamsWindow::_SaveSettings()
340{
341	BFile file;
342	status_t status = _OpenSettings(file,
343		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
344
345	if (status < B_OK)
346		return status;
347
348	BMessage settings('hdbg');
349	status = settings.AddRect("teams window frame", Frame());
350	if (status != B_OK)
351		return status;
352
353	if (status == B_OK)
354		status = settings.Flatten(&file);
355
356	return status;
357}
358
359
360void
361TeamsWindow::_NotifySelectedInterfaceChanged(TargetHostInterface* interface)
362{
363	for (ListenerList::Iterator it = fListeners.GetIterator();
364			Listener* listener = it.Next();) {
365		listener->SelectedInterfaceChanged(interface);
366	}
367}
368
369
370// #pragma mark - TeamsWindow::Listener
371
372
373TeamsWindow::Listener::~Listener()
374{
375}
376