1/*
2 * Copyright 2014-2017, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "MidiSettingsView.h"
7
8#include <MidiSettings.h>
9
10#include <Box.h>
11#include <Catalog.h>
12#include <Directory.h>
13#include <Entry.h>
14#include <FindDirectory.h>
15#include <GridView.h>
16#include <LayoutBuilder.h>
17#include <ListView.h>
18#include <Locale.h>
19#include <Node.h>
20#include <NodeInfo.h>
21#include <NodeMonitor.h>
22#include <Path.h>
23#include <PathFinder.h>
24#include <ScrollView.h>
25#include <StringList.h>
26#include <StringView.h>
27
28#include <stdio.h>
29
30
31#undef B_TRANSLATION_CONTEXT
32#define B_TRANSLATION_CONTEXT "Midi View"
33
34const static uint32 kSelectSoundFont = 'SeSf';
35const static uint32 kDoubleClick = 'DClk';
36
37
38MidiSettingsView::MidiSettingsView()
39	:
40	SettingsView()
41{
42	BBox* defaultsBox = new BBox("SoundFonts");
43	defaultsBox->SetLabel(B_TRANSLATE("SoundFonts"));
44	BGridView* defaultsGridView = new BGridView();
45
46	fListView = new BListView(B_SINGLE_SELECTION_LIST);
47	fListView->SetSelectionMessage(new BMessage(kSelectSoundFont));
48	fListView->SetInvocationMessage(new BMessage(kDoubleClick));
49
50	BScrollView* scrollView = new BScrollView("ScrollView", fListView,
51			0, false, true);
52	BLayoutBuilder::Grid<>(defaultsGridView)
53		.SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING,
54				B_USE_DEFAULT_SPACING)
55		.Add(scrollView, 0, 0);
56
57	defaultsBox->AddChild(defaultsGridView);
58	fSoundFontStatus = new BStringView("SoundFontStatus", "");
59
60	BLayoutBuilder::Group<>(this)
61		.SetInsets(0, 0, 0, 0)
62		.Add(defaultsBox)
63		.AddGroup(B_HORIZONTAL)
64			.AddGlue()
65			.Add(fSoundFontStatus)
66			.AddGlue()
67			.End()
68		.AddGlue();
69}
70
71
72/* virtual */
73void
74MidiSettingsView::AttachedToWindow()
75{
76	SettingsView::AttachedToWindow();
77
78	fListView->SetTarget(this);
79	_LoadSettings();
80	_RetrieveSoundFontList();
81	_SelectActiveSoundFont();
82	_UpdateSoundFontStatus();
83	_WatchFolders();
84}
85
86
87/* virtual */
88void
89MidiSettingsView::DetachedFromWindow()
90{
91	SettingsView::DetachedFromWindow();
92
93	stop_watching(this);
94}
95
96
97/* virtual */
98void
99MidiSettingsView::MessageReceived(BMessage* message)
100{
101	switch (message->what) {
102		case B_NODE_MONITOR:
103		{
104			int32 selected = fListView->CurrentSelection();
105			BStringItem* olditem = (BStringItem*)fListView->ItemAt(selected);
106
107			_RetrieveSoundFontList();
108
109			int32 count = fListView->CountItems();
110			if (count == 1) {
111				fListView->Select(0);
112				_SaveSettings();
113			} else if (olditem != NULL) {
114				for (int32 i = 0; i < fListView->CountItems(); i++) {
115					BStringItem* item = (BStringItem*)fListView->ItemAt(i);
116					if (strcmp(item->Text(), olditem->Text()) == 0) {
117						fListView->Select(i);
118						break;
119					}
120				}
121			}
122			_UpdateSoundFontStatus();
123			break;
124		}
125		case kSelectSoundFont:
126		{
127			int selection = fListView->CurrentSelection();
128			if (selection < 0)
129				_SelectActiveSoundFont();
130			else
131				_SaveSettings();
132
133			_UpdateSoundFontStatus();
134			break;
135		}
136		case kDoubleClick:
137		{
138			int selection = fListView->CurrentSelection();
139			if (selection < 0)
140				break;
141
142			BStringItem* item = (BStringItem*)fListView->ItemAt(selection);
143
144			BEntry entry(item->Text());
145			BEntry parent;
146			entry.GetParent(&parent);
147			entry_ref folderRef;
148			parent.GetRef(&folderRef);
149			BMessenger msgr("application/x-vnd.Be-TRAK");
150			BMessage refMsg(B_REFS_RECEIVED);
151			refMsg.AddRef("refs",&folderRef);
152			msgr.SendMessage(&refMsg);
153			break;
154		}
155
156		default:
157			SettingsView::MessageReceived(message);
158			break;
159	}
160}
161
162
163void
164MidiSettingsView::_RetrieveSoundFontList()
165{
166	// TODO: Duplicated code between here
167	// and BSoftSynth::SetDefaultInstrumentsFile
168	BStringList paths;
169	status_t status = BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY,
170			"synth", paths);
171	if (status != B_OK)
172		return;
173
174	fListView->MakeEmpty();
175
176	for (int32 i = 0; i < paths.CountStrings(); i++) {
177		BDirectory directory(paths.StringAt(i).String());
178		BEntry entry;
179		if (directory.InitCheck() != B_OK)
180			continue;
181		while (directory.GetNextEntry(&entry) == B_OK) {
182			BNode node(&entry);
183			BNodeInfo nodeInfo(&node);
184			char mimeType[B_MIME_TYPE_LENGTH];
185			// TODO: For some reason the mimetype check fails.
186			// maybe because the file hasn't yet been sniffed and recognized?
187			if (nodeInfo.GetType(mimeType) == B_OK
188				/*&& !strcmp(mimeType, "audio/x-soundfont")*/) {
189				BPath fullPath = paths.StringAt(i).String();
190				fullPath.Append(entry.Name());
191				fListView->AddItem(new BStringItem(fullPath.Path()));
192			}
193		}
194	}
195}
196
197
198void
199MidiSettingsView::_LoadSettings()
200{
201	struct BPrivate::midi_settings settings;
202	if (BPrivate::read_midi_settings(&settings) == B_OK)
203		fActiveSoundFont.SetTo(settings.soundfont_file);
204}
205
206
207void
208MidiSettingsView::_SaveSettings()
209{
210	fActiveSoundFont = _SelectedSoundFont();
211	if (fActiveSoundFont.Length() > 0) {
212		struct BPrivate::midi_settings settings;
213		strlcpy(settings.soundfont_file, fActiveSoundFont.String(),
214			sizeof(settings.soundfont_file));
215		BPrivate::write_midi_settings(settings);
216	}
217}
218
219
220void
221MidiSettingsView::_SelectActiveSoundFont()
222{
223	int32 count = fListView->CountItems();
224	if (count == 1) {
225		fListView->Select(0);
226		_SaveSettings();
227		return;
228	}
229	for (int32 i = 0; i < fListView->CountItems(); i++) {
230		BStringItem* item = (BStringItem*)fListView->ItemAt(i);
231		if (strcmp(item->Text(), fActiveSoundFont.String()) == 0) {
232			fListView->Select(i);
233			break;
234		}
235	}
236}
237
238
239BString
240MidiSettingsView::_SelectedSoundFont() const
241{
242	BString string;
243	int32 selection = fListView->CurrentSelection();
244	if (selection >= 0) {
245		BStringItem* item = (BStringItem*)fListView->ItemAt(selection);
246		if (item != NULL)
247			string = item->Text();
248	}
249
250	return string;
251}
252
253
254void
255MidiSettingsView::_UpdateSoundFontStatus()
256{
257	if (fListView->IsEmpty()) {
258		fSoundFontStatus->SetText(
259			B_TRANSLATE("There are no SoundFonts installed."));
260		return;
261	}
262	int32 selection = fListView->CurrentSelection();
263	if (selection < 0) {
264		fSoundFontStatus->SetText(
265			B_TRANSLATE("Please select a SoundFont."));
266		return;
267	}
268	fSoundFontStatus->SetText("");
269}
270
271
272void
273MidiSettingsView::_WatchFolders()
274{
275	BStringList paths;
276	BPathFinder().FindPaths(B_FIND_PATH_DATA_DIRECTORY, "synth", paths);
277	for (int32 i = 0; i < paths.CountStrings(); i++) {
278		BEntry entry(paths.StringAt(i));
279		node_ref nref;
280		entry.GetNodeRef(&nref);
281
282		watch_node(&nref, B_WATCH_ALL, this);
283	}
284}
285