1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011-2016, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "GraphicalUserInterface.h"
9
10#include <Alert.h>
11#include <AutoDeleter.h>
12#include <Autolock.h>
13#include <FilePanel.h>
14#include <Locker.h>
15
16#include "AlertWithCheckbox.h"
17#include "GuiTeamUiSettings.h"
18#include "MessageCodes.h"
19#include "TeamWindow.h"
20#include "Tracing.h"
21
22
23// #pragma mark - GraphicalUserInterface::FilePanelHandler
24
25
26class GraphicalUserInterface::FilePanelHandler : public BHandler {
27public:
28								FilePanelHandler();
29	virtual						~FilePanelHandler();
30
31			status_t			Init();
32
33	virtual	void				MessageReceived(BMessage* message);
34
35			status_t			WaitForPanel();
36
37			void				SetCurrentRef(entry_ref* ref);
38
39			BLocker&			Locker()
40									{ return fPanelLock; }
41
42private:
43			entry_ref*			fCurrentRef;
44			BLocker				fPanelLock;
45			sem_id				fPanelWaitSem;
46};
47
48
49GraphicalUserInterface::FilePanelHandler::FilePanelHandler()
50	:
51	BHandler("GuiPanelHandler"),
52	fCurrentRef(NULL),
53	fPanelLock(),
54	fPanelWaitSem(-1)
55{
56}
57
58
59GraphicalUserInterface::FilePanelHandler::~FilePanelHandler()
60{
61	if (fPanelWaitSem >= 0)
62		delete_sem(fPanelWaitSem);
63}
64
65
66status_t
67GraphicalUserInterface::FilePanelHandler::Init()
68{
69	fPanelWaitSem = create_sem(0, "FilePanelWaitSem");
70
71	if (fPanelWaitSem < 0)
72		return fPanelWaitSem;
73
74	return B_OK;
75}
76
77
78void
79GraphicalUserInterface::FilePanelHandler::MessageReceived(BMessage* message)
80{
81	switch (message->what) {
82		case MSG_USER_INTERFACE_FILE_CHOSEN:
83		{
84			entry_ref ref;
85			if (message->FindRef("refs", &ref) == B_OK
86				&& fCurrentRef != NULL) {
87				*fCurrentRef = ref;
88				fCurrentRef = NULL;
89			}
90			// fall through
91		}
92
93		case B_CANCEL:
94		{
95			release_sem(fPanelWaitSem);
96			break;
97		}
98
99		default:
100			BHandler::MessageReceived(message);
101			break;
102	}
103}
104
105
106status_t
107GraphicalUserInterface::FilePanelHandler::WaitForPanel()
108{
109	status_t result = B_OK;
110	do {
111		result = acquire_sem(fPanelWaitSem);
112	} while (result == B_INTERRUPTED);
113
114	return result;
115}
116
117
118void
119GraphicalUserInterface::FilePanelHandler::SetCurrentRef(entry_ref* ref)
120{
121	fCurrentRef = ref;
122}
123
124
125// #pragma mark - GraphicalUserInterface
126
127
128GraphicalUserInterface::GraphicalUserInterface()
129	:
130	fTeamWindow(NULL),
131	fTeamWindowMessenger(NULL),
132	fFilePanelHandler(NULL),
133	fFilePanel(NULL),
134	fDefaultActions(10, true)
135{
136}
137
138
139GraphicalUserInterface::~GraphicalUserInterface()
140{
141	delete fTeamWindowMessenger;
142	delete fFilePanel;
143	delete fFilePanelHandler;
144}
145
146
147const char*
148GraphicalUserInterface::ID() const
149{
150	return "GraphicalUserInterface";
151}
152
153
154status_t
155GraphicalUserInterface::Init(Team* team, UserInterfaceListener* listener)
156{
157	try {
158		fTeamWindow = TeamWindow::Create(team, listener);
159		fTeamWindowMessenger = new BMessenger(fTeamWindow);
160		fFilePanelHandler = new FilePanelHandler();
161		status_t error = fFilePanelHandler->Init();
162		if (error != B_OK) {
163			ERROR("Error: Failed to create file panel semaphore!\n");
164			return error;
165		}
166		fTeamWindow->AddHandler(fFilePanelHandler);
167
168		// start the message loop
169		fTeamWindow->Hide();
170		fTeamWindow->Show();
171	} catch (...) {
172		// TODO: Notify the user!
173		ERROR("Error: Failed to create team window!\n");
174		return B_NO_MEMORY;
175	}
176
177	return B_OK;
178}
179
180
181void
182GraphicalUserInterface::Show()
183{
184	if (fTeamWindow->IsHidden())
185		fTeamWindow->Show();
186	else
187		fTeamWindow->Activate();
188}
189
190
191void
192GraphicalUserInterface::Terminate()
193{
194	// quit window
195	if (fTeamWindowMessenger && fTeamWindowMessenger->LockTarget())
196		fTeamWindow->Quit();
197}
198
199
200UserInterface*
201GraphicalUserInterface::Clone() const
202{
203	return new(std::nothrow) GraphicalUserInterface;
204}
205
206
207bool
208GraphicalUserInterface::IsInteractive() const
209{
210	return true;
211}
212
213
214status_t
215GraphicalUserInterface::LoadSettings(const TeamUiSettings* settings)
216{
217	status_t result = fTeamWindow->LoadSettings((GuiTeamUiSettings*)settings);
218
219	return result;
220}
221
222
223status_t
224GraphicalUserInterface::SaveSettings(TeamUiSettings*& settings) const
225{
226	settings = new(std::nothrow) GuiTeamUiSettings(ID());
227	if (settings == NULL)
228		return B_NO_MEMORY;
229
230	fTeamWindow->SaveSettings((GuiTeamUiSettings*)settings);
231
232	return B_OK;
233}
234
235
236void
237GraphicalUserInterface::NotifyUser(const char* title, const char* message,
238	user_notification_type type)
239{
240	// convert notification type to alert type
241	alert_type alertType;
242	switch (type) {
243		case USER_NOTIFICATION_INFO:
244			alertType = B_INFO_ALERT;
245			break;
246		case USER_NOTIFICATION_WARNING:
247		case USER_NOTIFICATION_ERROR:
248		default:
249			alertType = B_WARNING_ALERT;
250			break;
251	}
252
253	BAlert* alert = new(std::nothrow) BAlert(title, message, "OK",
254		NULL, NULL, B_WIDTH_AS_USUAL, alertType);
255	if (alert != NULL)
256		alert->Go(NULL);
257
258	// TODO: We need to let the alert run asynchronously, but we shouldn't just
259	// create it and don't care anymore. Maybe an error window, which can
260	// display a list of errors would be the better choice.
261}
262
263
264void
265GraphicalUserInterface::NotifyBackgroundWorkStatus(const char* message)
266{
267	fTeamWindow->DisplayBackgroundStatus(message);
268}
269
270
271int32
272GraphicalUserInterface::SynchronouslyAskUser(const char* title,
273	const char* message, const char* choice1, const char* choice2,
274	const char* choice3)
275{
276	// If the user already answered the question and asked for their choice to be remembered,
277	// return the previously made choice again
278	BString key(title);
279	key += choice1;
280	key += choice2;
281	key += choice3;
282
283	for (int i = 0; i < fDefaultActions.CountItems(); i++) {
284		if (fDefaultActions.ItemAt(i)->fKey == key)
285			return fDefaultActions.ItemAt(i)->fDecision;
286	}
287
288	AlertWithCheckbox* alert = new(std::nothrow) AlertWithCheckbox(title, message,
289		"Don't ask again", choice1, choice2, choice3);
290
291	if (alert == NULL)
292		return 0;
293
294	bool dontAskAgain = false;
295	int result = alert->Go(dontAskAgain);
296
297	if (dontAskAgain) {
298		DefaultAction* defaultAction = new DefaultAction;
299		defaultAction->fKey = key;
300		defaultAction->fDecision = result;
301		fDefaultActions.AddItem(defaultAction);
302	}
303
304	return result;
305}
306
307
308status_t
309GraphicalUserInterface::SynchronouslyAskUserForFile(entry_ref* _ref)
310{
311	BAutolock lock(&fFilePanelHandler->Locker());
312
313	if (fFilePanel == NULL) {
314		BMessenger messenger(fFilePanelHandler);
315		BMessage* message = new(std::nothrow) BMessage(
316			MSG_USER_INTERFACE_FILE_CHOSEN);
317		if (message == NULL)
318			return B_NO_MEMORY;
319		ObjectDeleter<BMessage> messageDeleter(message);
320		fFilePanel = new(std::nothrow) BFilePanel(B_OPEN_PANEL,
321			&messenger, NULL, B_FILE_NODE, false, message);
322		if (fFilePanel == NULL)
323			return B_NO_MEMORY;
324		messageDeleter.Detach();
325	}
326
327	fFilePanelHandler->SetCurrentRef(_ref);
328	fFilePanel->Show();
329	return fFilePanelHandler->WaitForPanel();
330}
331