15fa5e5feSStephan Aßmus/*
272d2b965SAxel Dörfler * Copyright 2007-2010, Haiku. All rights reserved.
35fa5e5feSStephan Aßmus * Distributed under the terms of the MIT License.
45fa5e5feSStephan Aßmus *
55fa5e5feSStephan Aßmus * Authors:
61b7f76bcSStephan Aßmus *		Stephan A��mus 	<superstippi@gmx.de>
71b7f76bcSStephan Aßmus *		Fredrik Mod��en	<fredrik@modeen.se>
85fa5e5feSStephan Aßmus */
974fd3b54SKarsten Heimrich
1072d2b965SAxel Dörfler
115fa5e5feSStephan Aßmus#include "PlaylistWindow.h"
125fa5e5feSStephan Aßmus
131b7f76bcSStephan Aßmus#include <stdio.h>
141b7f76bcSStephan Aßmus
15126ce071SStephan Aßmus#include <Alert.h>
161b7f76bcSStephan Aßmus#include <Application.h>
17126ce071SStephan Aßmus#include <Autolock.h>
18238df8fbSAdrien Destugues#include <Box.h>
19238df8fbSAdrien Destugues#include <Button.h>
20238df8fbSAdrien Destugues#include <Catalog.h>
21126ce071SStephan Aßmus#include <Entry.h>
22126ce071SStephan Aßmus#include <File.h>
23238df8fbSAdrien Destugues#include <FilePanel.h>
24238df8fbSAdrien Destugues#include <Locale.h>
25cc2fbed2SStephan Aßmus#include <Menu.h>
26cc2fbed2SStephan Aßmus#include <MenuBar.h>
27cc2fbed2SStephan Aßmus#include <MenuItem.h>
28126ce071SStephan Aßmus#include <NodeInfo.h>
29238df8fbSAdrien Destugues#include <Path.h>
30238df8fbSAdrien Destugues#include <Roster.h>
315fa5e5feSStephan Aßmus#include <ScrollBar.h>
325fa5e5feSStephan Aßmus#include <ScrollView.h>
33cc2fbed2SStephan Aßmus#include <String.h>
341c68b67aSMarkus Himmel#include <StringView.h>
355fa5e5feSStephan Aßmus
36cc2fbed2SStephan Aßmus#include "CommandStack.h"
371c68b67aSMarkus Himmel#include "DurationToString.h"
38126ce071SStephan Aßmus#include "MainApp.h"
3909464bc9SStephan Aßmus#include "PlaylistListView.h"
40cc2fbed2SStephan Aßmus#include "RWLocker.h"
4172d2b965SAxel Dörfler
42546208a5SOliver Tappe#undef B_TRANSLATION_CONTEXT
43546208a5SOliver Tappe#define B_TRANSLATION_CONTEXT "MediaPlayer-PlaylistWindow"
44238df8fbSAdrien Destugues
45238df8fbSAdrien Destugues
46d6aa728aSStephan Aßmus// TODO:
47d6aa728aSStephan Aßmus// Maintaining a playlist file on disk is a bit tricky. The playlist ref should
48d6aa728aSStephan Aßmus// be discarded when the user
49d6aa728aSStephan Aßmus// * loads a new playlist via Open,
50d6aa728aSStephan Aßmus// * loads a new playlist via dropping it on the MainWindow,
51d6aa728aSStephan Aßmus// * loads a new playlist via dropping it into the ListView while replacing
52d6aa728aSStephan Aßmus//   the contents,
53d6aa728aSStephan Aßmus// * replacing the contents by other stuff.
54d6aa728aSStephan Aßmus
551b7f76bcSStephan Aßmus
5672d2b965SAxel Dörflerstatic void
5772d2b965SAxel Dörflerdisplay_save_alert(const char* message)
5872d2b965SAxel Dörfler{
59238df8fbSAdrien Destugues	BAlert* alert = new BAlert(B_TRANSLATE("Save error"), message,
60238df8fbSAdrien Destugues		B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
61aed35104SHumdinger	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
6272d2b965SAxel Dörfler	alert->Go(NULL);
6372d2b965SAxel Dörfler}
6472d2b965SAxel Dörfler
6572d2b965SAxel Dörfler
6672d2b965SAxel Dörflerstatic void
6772d2b965SAxel Dörflerdisplay_save_alert(status_t error)
6872d2b965SAxel Dörfler{
69238df8fbSAdrien Destugues	BString errorMessage(B_TRANSLATE("Saving the playlist failed.\n\nError: "));
7072d2b965SAxel Dörfler	errorMessage << strerror(error);
7172d2b965SAxel Dörfler	display_save_alert(errorMessage.String());
7272d2b965SAxel Dörfler}
7372d2b965SAxel Dörfler
7472d2b965SAxel Dörfler
7572d2b965SAxel Dörfler// #pragma mark -
7672d2b965SAxel Dörfler
775fa5e5feSStephan Aßmus
7809464bc9SStephan AßmusPlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist,
7909464bc9SStephan Aßmus		Controller* controller)
8072d2b965SAxel Dörfler	:
81238df8fbSAdrien Destugues	BWindow(frame, B_TRANSLATE("Playlist"), B_DOCUMENT_WINDOW_LOOK,
82238df8fbSAdrien Destugues		B_NORMAL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS),
8372d2b965SAxel Dörfler	fPlaylist(playlist),
8472d2b965SAxel Dörfler	fLocker(new RWLocker("command stack lock")),
8572d2b965SAxel Dörfler	fCommandStack(new CommandStack(fLocker)),
861c68b67aSMarkus Himmel	fCommandStackListener(this),
871c68b67aSMarkus Himmel	fDurationListener(new DurationListener(*this))
885fa5e5feSStephan Aßmus{
8974fd3b54SKarsten Heimrich	frame = Bounds();
9074fd3b54SKarsten Heimrich
91cc2fbed2SStephan Aßmus	_CreateMenu(frame);
921b7f76bcSStephan Aßmus		// will adjust frame to account for menubar
9374fd3b54SKarsten Heimrich
945fa5e5feSStephan Aßmus	frame.right -= B_V_SCROLL_BAR_WIDTH;
951c68b67aSMarkus Himmel	frame.bottom -= B_H_SCROLL_BAR_HEIGHT;
96415ef601SStephan Aßmus	fListView = new PlaylistListView(frame, playlist, controller,
97415ef601SStephan Aßmus		fCommandStack);
985fa5e5feSStephan Aßmus
9974fd3b54SKarsten Heimrich	BScrollView* scrollView = new BScrollView("playlist scrollview", fListView,
1001b7f76bcSStephan Aßmus		B_FOLLOW_ALL_SIDES, 0, false, true, B_NO_BORDER);
1015fa5e5feSStephan Aßmus
1021b7f76bcSStephan Aßmus	fTopView = 	scrollView;
1035fa5e5feSStephan Aßmus	AddChild(fTopView);
104cc2fbed2SStephan Aßmus
105cc2fbed2SStephan Aßmus	// small visual tweak
106cc2fbed2SStephan Aßmus	if (BScrollBar* scrollBar = scrollView->ScrollBar(B_VERTICAL)) {
107cc2fbed2SStephan Aßmus		// make it so the frame of the menubar is also the frame of
108cc2fbed2SStephan Aßmus		// the scroll bar (appears to be)
109cc2fbed2SStephan Aßmus		scrollBar->MoveBy(0, -1);
1101c68b67aSMarkus Himmel		scrollBar->ResizeBy(0, 2);
1111c68b67aSMarkus Himmel	}
1121c68b67aSMarkus Himmel
1131c68b67aSMarkus Himmel	frame.top += frame.Height();
1141c68b67aSMarkus Himmel	frame.bottom += B_H_SCROLL_BAR_HEIGHT;
1151c68b67aSMarkus Himmel
1161c68b67aSMarkus Himmel	fTotalDuration = new BStringView(frame, "fDuration", "",
1171c68b67aSMarkus Himmel		B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT);
118ecec5d0eSMarkus Himmel	fTotalDuration->SetAlignment(B_ALIGN_RIGHT);
119ecec5d0eSMarkus Himmel	fTotalDuration->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1201c68b67aSMarkus Himmel	AddChild(fTotalDuration);
1211c68b67aSMarkus Himmel
1221c68b67aSMarkus Himmel	_UpdateTotalDuration(0);
1231c68b67aSMarkus Himmel
1241c68b67aSMarkus Himmel	{
1251c68b67aSMarkus Himmel		BAutolock _(fPlaylist);
1261c68b67aSMarkus Himmel
1271c68b67aSMarkus Himmel		_QueryInitialDurations();
1281c68b67aSMarkus Himmel		fPlaylist->AddListener(fDurationListener);
129cc2fbed2SStephan Aßmus	}
130cc2fbed2SStephan Aßmus
131cc2fbed2SStephan Aßmus	fCommandStack->AddListener(&fCommandStackListener);
132cc2fbed2SStephan Aßmus	_ObjectChanged(fCommandStack);
1335fa5e5feSStephan Aßmus}
1345fa5e5feSStephan Aßmus
1355fa5e5feSStephan Aßmus
1365fa5e5feSStephan AßmusPlaylistWindow::~PlaylistWindow()
1375fa5e5feSStephan Aßmus{
1385fa5e5feSStephan Aßmus	// give listeners a chance to detach themselves
1395fa5e5feSStephan Aßmus	fTopView->RemoveSelf();
1405fa5e5feSStephan Aßmus	delete fTopView;
141cc2fbed2SStephan Aßmus
142cc2fbed2SStephan Aßmus	fCommandStack->RemoveListener(&fCommandStackListener);
143cc2fbed2SStephan Aßmus	delete fCommandStack;
144cc2fbed2SStephan Aßmus	delete fLocker;
1451c68b67aSMarkus Himmel
1461c68b67aSMarkus Himmel	fPlaylist->RemoveListener(fDurationListener);
1471c68b67aSMarkus Himmel	BMessenger(fDurationListener).SendMessage(B_QUIT_REQUESTED);
1485fa5e5feSStephan Aßmus}
1495fa5e5feSStephan Aßmus
1505fa5e5feSStephan Aßmus
1515fa5e5feSStephan Aßmusbool
1525fa5e5feSStephan AßmusPlaylistWindow::QuitRequested()
1535fa5e5feSStephan Aßmus{
1545fa5e5feSStephan Aßmus	Hide();
1555fa5e5feSStephan Aßmus	return false;
1565fa5e5feSStephan Aßmus}
1575fa5e5feSStephan Aßmus
15809464bc9SStephan Aßmus
15909464bc9SStephan Aßmusvoid
16009464bc9SStephan AßmusPlaylistWindow::MessageReceived(BMessage* message)
16109464bc9SStephan Aßmus{
16209464bc9SStephan Aßmus	switch (message->what) {
16309464bc9SStephan Aßmus		case B_MODIFIERS_CHANGED:
16409464bc9SStephan Aßmus			if (LastMouseMovedView())
16509464bc9SStephan Aßmus				PostMessage(message, LastMouseMovedView());
16609464bc9SStephan Aßmus			break;
167cc2fbed2SStephan Aßmus
168cc2fbed2SStephan Aßmus		case B_UNDO:
169cc2fbed2SStephan Aßmus			fCommandStack->Undo();
170cc2fbed2SStephan Aßmus			break;
171cc2fbed2SStephan Aßmus		case B_REDO:
172cc2fbed2SStephan Aßmus			fCommandStack->Redo();
173cc2fbed2SStephan Aßmus			break;
174cc2fbed2SStephan Aßmus
175cc2fbed2SStephan Aßmus		case MSG_OBJECT_CHANGED: {
176cc2fbed2SStephan Aßmus			Notifier* notifier;
177cc2fbed2SStephan Aßmus			if (message->FindPointer("object", (void**)&notifier) == B_OK)
178cc2fbed2SStephan Aßmus				_ObjectChanged(notifier);
179cc2fbed2SStephan Aßmus			break;
180cc2fbed2SStephan Aßmus		}
181cc2fbed2SStephan Aßmus
18292cb0c5dSDario Casalinuovo		case M_URL_RECEIVED:
183cc2fbed2SStephan Aßmus		case B_REFS_RECEIVED:
184c6c220e9SFredrik Modeen			// Used for when we open a playlist from playlist window
18572d2b965SAxel Dörfler			if (!message->HasInt32("append_index")) {
18672d2b965SAxel Dörfler				message->AddInt32("append_index",
18772d2b965SAxel Dörfler					APPEND_INDEX_REPLACE_PLAYLIST);
18872d2b965SAxel Dörfler			}
18972d2b965SAxel Dörfler			// supposed to fall through
19072d2b965SAxel Dörfler		case B_SIMPLE_DATA:
19172d2b965SAxel Dörfler		{
192cc2fbed2SStephan Aßmus			// only accept this message when it comes from the
193cc2fbed2SStephan Aßmus			// player window, _not_ when it is dropped in this window
194cc2fbed2SStephan Aßmus			// outside of the playlist!
195cc2fbed2SStephan Aßmus			int32 appendIndex;
196bafcab92SAxel Dörfler			if (message->FindInt32("append_index", &appendIndex) == B_OK)
19792cb0c5dSDario Casalinuovo				fListView->ItemsReceived(message, appendIndex);
198cc2fbed2SStephan Aßmus			break;
199cc2fbed2SStephan Aßmus		}
20074fd3b54SKarsten Heimrich
20172d2b965SAxel Dörfler		case M_PLAYLIST_OPEN:
20272d2b965SAxel Dörfler		{
203126ce071SStephan Aßmus			BMessenger target(this);
204126ce071SStephan Aßmus			BMessage result(B_REFS_RECEIVED);
205126ce071SStephan Aßmus			BMessage appMessage(M_SHOW_OPEN_PANEL);
206126ce071SStephan Aßmus			appMessage.AddMessenger("target", target);
207126ce071SStephan Aßmus			appMessage.AddMessage("message", &result);
208238df8fbSAdrien Destugues			appMessage.AddString("title", B_TRANSLATE("Open Playlist"));
209238df8fbSAdrien Destugues			appMessage.AddString("label", B_TRANSLATE("Open"));
210126ce071SStephan Aßmus			be_app->PostMessage(&appMessage);
2111b7f76bcSStephan Aßmus			break;
212126ce071SStephan Aßmus		}
21372d2b965SAxel Dörfler
21472d2b965SAxel Dörfler		case M_PLAYLIST_SAVE:
215d6aa728aSStephan Aßmus			if (fSavedPlaylistRef != entry_ref()) {
216d6aa728aSStephan Aßmus				_SavePlaylist(fSavedPlaylistRef);
217d6aa728aSStephan Aßmus				break;
218d6aa728aSStephan Aßmus			}
21972d2b965SAxel Dörfler			// supposed to fall through
22072d2b965SAxel Dörfler		case M_PLAYLIST_SAVE_AS:
22172d2b965SAxel Dörfler		{
222126ce071SStephan Aßmus			BMessenger target(this);
223126ce071SStephan Aßmus			BMessage result(M_PLAYLIST_SAVE_RESULT);
224126ce071SStephan Aßmus			BMessage appMessage(M_SHOW_SAVE_PANEL);
225126ce071SStephan Aßmus			appMessage.AddMessenger("target", target);
226126ce071SStephan Aßmus			appMessage.AddMessage("message", &result);
227238df8fbSAdrien Destugues			appMessage.AddString("title", B_TRANSLATE("Save Playlist"));
228238df8fbSAdrien Destugues			appMessage.AddString("label", B_TRANSLATE("Save"));
229126ce071SStephan Aßmus			be_app->PostMessage(&appMessage);
23074fd3b54SKarsten Heimrich			break;
231126ce071SStephan Aßmus		}
23272d2b965SAxel Dörfler
23372d2b965SAxel Dörfler		case M_PLAYLIST_SAVE_RESULT:
234126ce071SStephan Aßmus			_SavePlaylist(message);
23574fd3b54SKarsten Heimrich			break;
23634ff70daSStephan Aßmus
2378a5f363dSJonas Sundström		case B_SELECT_ALL:
2388a5f363dSJonas Sundström			fListView->SelectAll();
2398a5f363dSJonas Sundström			break;
2408a5f363dSJonas Sundström
24134ff70daSStephan Aßmus		case M_PLAYLIST_RANDOMIZE:
24234ff70daSStephan Aßmus			fListView->Randomize();
24334ff70daSStephan Aßmus			break;
244663ed1f6SDario Casalinuovo
245bae0154cSStephan Aßmus		case M_PLAYLIST_REMOVE:
246bae0154cSStephan Aßmus			fListView->RemoveSelected();
247bae0154cSStephan Aßmus			break;
248663ed1f6SDario Casalinuovo
249e28afdf1SJonas Sundström		case M_PLAYLIST_MOVE_TO_TRASH:
250dd091096SStephan Aßmus		{
251dd091096SStephan Aßmus			int32 index;
252dd091096SStephan Aßmus			if (message->FindInt32("playlist index", &index) == B_OK)
253dd091096SStephan Aßmus				fListView->RemoveToTrash(index);
254dd091096SStephan Aßmus			else
255dd091096SStephan Aßmus				fListView->RemoveSelectionToTrash();
2569327db63SFredrik Modeen			break;
257dd091096SStephan Aßmus		}
258663ed1f6SDario Casalinuovo
25909464bc9SStephan Aßmus		default:
26009464bc9SStephan Aßmus			BWindow::MessageReceived(message);
26109464bc9SStephan Aßmus			break;
26209464bc9SStephan Aßmus	}
26309464bc9SStephan Aßmus}
26409464bc9SStephan Aßmus
265cc2fbed2SStephan Aßmus
266cc2fbed2SStephan Aßmus// #pragma mark -
267cc2fbed2SStephan Aßmus
268cc2fbed2SStephan Aßmus
269cc2fbed2SStephan Aßmusvoid
270cc2fbed2SStephan AßmusPlaylistWindow::_CreateMenu(BRect& frame)
271cc2fbed2SStephan Aßmus{
272cc2fbed2SStephan Aßmus	frame.bottom = 15;
273cc2fbed2SStephan Aßmus	BMenuBar* menuBar = new BMenuBar(frame, "main menu");
274238df8fbSAdrien Destugues	BMenu* fileMenu = new BMenu(B_TRANSLATE("Playlist"));
275cc2fbed2SStephan Aßmus	menuBar->AddItem(fileMenu);
276991dadd6SHumdinger	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Open" B_UTF8_ELLIPSIS),
277126ce071SStephan Aßmus		new BMessage(M_PLAYLIST_OPEN), 'O'));
278991dadd6SHumdinger	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS),
279d6aa728aSStephan Aßmus		new BMessage(M_PLAYLIST_SAVE_AS), 'S', B_SHIFT_KEY));
280d6aa728aSStephan Aßmus//	fileMenu->AddItem(new BMenuItem("Save",
281d6aa728aSStephan Aßmus//		new BMessage(M_PLAYLIST_SAVE), 'S'));
282d6aa728aSStephan Aßmus
283126ce071SStephan Aßmus	fileMenu->AddSeparatorItem();
284d6aa728aSStephan Aßmus
285238df8fbSAdrien Destugues	fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
2861b7f76bcSStephan Aßmus		new BMessage(B_QUIT_REQUESTED), 'W'));
287cc2fbed2SStephan Aßmus
288238df8fbSAdrien Destugues	BMenu* editMenu = new BMenu(B_TRANSLATE("Edit"));
289238df8fbSAdrien Destugues	fUndoMI = new BMenuItem(B_TRANSLATE("Undo"), new BMessage(B_UNDO), 'Z');
290cc2fbed2SStephan Aßmus	editMenu->AddItem(fUndoMI);
291238df8fbSAdrien Destugues	fRedoMI = new BMenuItem(B_TRANSLATE("Redo"), new BMessage(B_REDO), 'Z',
292238df8fbSAdrien Destugues		B_SHIFT_KEY);
293cc2fbed2SStephan Aßmus	editMenu->AddItem(fRedoMI);
2941b7f76bcSStephan Aßmus	editMenu->AddSeparatorItem();
2958a5f363dSJonas Sundström	editMenu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
2968a5f363dSJonas Sundström		new BMessage(B_SELECT_ALL), 'A'));
2978a5f363dSJonas Sundström	editMenu->AddSeparatorItem();
298238df8fbSAdrien Destugues	editMenu->AddItem(new BMenuItem(B_TRANSLATE("Randomize"),
29934ff70daSStephan Aßmus		new BMessage(M_PLAYLIST_RANDOMIZE), 'R'));
3009327db63SFredrik Modeen	editMenu->AddSeparatorItem();
301e28afdf1SJonas Sundström	editMenu->AddItem(new BMenuItem(B_TRANSLATE("Remove"),
302bae0154cSStephan Aßmus		new BMessage(M_PLAYLIST_REMOVE)/*, B_DELETE, 0*/));
303e28afdf1SJonas Sundström			// TODO: See if we can support the modifier-less B_DELETE
304e28afdf1SJonas Sundström			// and draw it properly too. B_NO_MODIFIER?
305e28afdf1SJonas Sundström	editMenu->AddItem(new BMenuItem(B_TRANSLATE("Move file to Trash"),
306e28afdf1SJonas Sundström		new BMessage(M_PLAYLIST_MOVE_TO_TRASH), 'T'));
3079327db63SFredrik Modeen
308cc2fbed2SStephan Aßmus	menuBar->AddItem(editMenu);
309cc2fbed2SStephan Aßmus
310cc2fbed2SStephan Aßmus	AddChild(menuBar);
311cc2fbed2SStephan Aßmus	fileMenu->SetTargetForItems(this);
312cc2fbed2SStephan Aßmus	editMenu->SetTargetForItems(this);
313cc2fbed2SStephan Aßmus
314cc2fbed2SStephan Aßmus	menuBar->ResizeToPreferred();
315cc2fbed2SStephan Aßmus	frame = Bounds();
316cc2fbed2SStephan Aßmus	frame.top = menuBar->Frame().bottom + 1;
317cc2fbed2SStephan Aßmus}
318cc2fbed2SStephan Aßmus
319cc2fbed2SStephan Aßmus
320cc2fbed2SStephan Aßmusvoid
321cc2fbed2SStephan AßmusPlaylistWindow::_ObjectChanged(const Notifier* object)
322cc2fbed2SStephan Aßmus{
323cc2fbed2SStephan Aßmus	if (object == fCommandStack) {
324cc2fbed2SStephan Aßmus		// relable Undo item and update enabled status
325238df8fbSAdrien Destugues		BString label(B_TRANSLATE("Undo"));
326cc2fbed2SStephan Aßmus		fUndoMI->SetEnabled(fCommandStack->GetUndoName(label));
327cc2fbed2SStephan Aßmus		if (fUndoMI->IsEnabled())
328cc2fbed2SStephan Aßmus			fUndoMI->SetLabel(label.String());
329cc2fbed2SStephan Aßmus		else
330238df8fbSAdrien Destugues			fUndoMI->SetLabel(B_TRANSLATE("<nothing to undo>"));
331cc2fbed2SStephan Aßmus
332cc2fbed2SStephan Aßmus		// relable Redo item and update enabled status
333238df8fbSAdrien Destugues		label.SetTo(B_TRANSLATE("Redo"));
334cc2fbed2SStephan Aßmus		fRedoMI->SetEnabled(fCommandStack->GetRedoName(label));
335cc2fbed2SStephan Aßmus		if (fRedoMI->IsEnabled())
336cc2fbed2SStephan Aßmus			fRedoMI->SetLabel(label.String());
337cc2fbed2SStephan Aßmus		else
338238df8fbSAdrien Destugues			fRedoMI->SetLabel(B_TRANSLATE("<nothing to redo>"));
339cc2fbed2SStephan Aßmus	}
340cc2fbed2SStephan Aßmus}
341cc2fbed2SStephan Aßmus
342126ce071SStephan Aßmus
343126ce071SStephan Aßmusvoid
344126ce071SStephan AßmusPlaylistWindow::_SavePlaylist(const BMessage* message)
345126ce071SStephan Aßmus{
346126ce071SStephan Aßmus	entry_ref ref;
347126ce071SStephan Aßmus	const char* name;
348126ce071SStephan Aßmus	if (message->FindRef("directory", &ref) != B_OK
349126ce071SStephan Aßmus		|| message->FindString("name", &name) != B_OK) {
350238df8fbSAdrien Destugues		display_save_alert(B_TRANSLATE("Internal error (malformed message). "
351238df8fbSAdrien Destugues			"Saving the playlist failed."));
352126ce071SStephan Aßmus		return;
353126ce071SStephan Aßmus	}
354126ce071SStephan Aßmus
355126ce071SStephan Aßmus	BString tempName(name);
356126ce071SStephan Aßmus	tempName << system_time();
357126ce071SStephan Aßmus
358126ce071SStephan Aßmus	BPath origPath(&ref);
359126ce071SStephan Aßmus	BPath tempPath(&ref);
360126ce071SStephan Aßmus	if (origPath.InitCheck() != B_OK || tempPath.InitCheck() != B_OK
361126ce071SStephan Aßmus		|| origPath.Append(name) != B_OK
362126ce071SStephan Aßmus		|| tempPath.Append(tempName.String()) != B_OK) {
363238df8fbSAdrien Destugues		display_save_alert(B_TRANSLATE("Internal error (out of memory). "
364238df8fbSAdrien Destugues			"Saving the playlist failed."));
365126ce071SStephan Aßmus		return;
366126ce071SStephan Aßmus	}
367126ce071SStephan Aßmus
368126ce071SStephan Aßmus	BEntry origEntry(origPath.Path());
369126ce071SStephan Aßmus	BEntry tempEntry(tempPath.Path());
370126ce071SStephan Aßmus	if (origEntry.InitCheck() != B_OK || tempEntry.InitCheck() != B_OK) {
371238df8fbSAdrien Destugues		display_save_alert(B_TRANSLATE("Internal error (out of memory). "
372238df8fbSAdrien Destugues			"Saving the playlist failed."));
373126ce071SStephan Aßmus		return;
374126ce071SStephan Aßmus	}
375126ce071SStephan Aßmus
376d6aa728aSStephan Aßmus	_SavePlaylist(origEntry, tempEntry, name);
377d6aa728aSStephan Aßmus}
378d6aa728aSStephan Aßmus
379d6aa728aSStephan Aßmus
380d6aa728aSStephan Aßmusvoid
381d6aa728aSStephan AßmusPlaylistWindow::_SavePlaylist(const entry_ref& ref)
382d6aa728aSStephan Aßmus{
383d6aa728aSStephan Aßmus	BString tempName(ref.name);
384d6aa728aSStephan Aßmus	tempName << system_time();
385d6aa728aSStephan Aßmus	entry_ref tempRef(ref);
386d6aa728aSStephan Aßmus	tempRef.set_name(tempName.String());
387d6aa728aSStephan Aßmus
388d6aa728aSStephan Aßmus	BEntry origEntry(&ref);
389d6aa728aSStephan Aßmus	BEntry tempEntry(&tempRef);
390d6aa728aSStephan Aßmus
391d6aa728aSStephan Aßmus	_SavePlaylist(origEntry, tempEntry, ref.name);
392d6aa728aSStephan Aßmus}
393d6aa728aSStephan Aßmus
394d6aa728aSStephan Aßmus
395d6aa728aSStephan Aßmusvoid
396d6aa728aSStephan AßmusPlaylistWindow::_SavePlaylist(BEntry& origEntry, BEntry& tempEntry,
397d6aa728aSStephan Aßmus	const char* finalName)
398d6aa728aSStephan Aßmus{
399126ce071SStephan Aßmus	class TempEntryRemover {
400126ce071SStephan Aßmus	public:
401126ce071SStephan Aßmus		TempEntryRemover(BEntry* entry)
402126ce071SStephan Aßmus			: fEntry(entry)
403126ce071SStephan Aßmus		{
404126ce071SStephan Aßmus		}
405126ce071SStephan Aßmus		~TempEntryRemover()
406126ce071SStephan Aßmus		{
407126ce071SStephan Aßmus			if (fEntry)
408126ce071SStephan Aßmus				fEntry->Remove();
409126ce071SStephan Aßmus		}
410126ce071SStephan Aßmus		void Detach()
411126ce071SStephan Aßmus		{
412126ce071SStephan Aßmus			fEntry = NULL;
413126ce071SStephan Aßmus		}
414126ce071SStephan Aßmus	private:
415126ce071SStephan Aßmus		BEntry* fEntry;
416126ce071SStephan Aßmus	} remover(&tempEntry);
417126ce071SStephan Aßmus
418126ce071SStephan Aßmus	BFile file(&tempEntry, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
419126ce071SStephan Aßmus	if (file.InitCheck() != B_OK) {
420238df8fbSAdrien Destugues		BString errorMessage(B_TRANSLATE(
421238df8fbSAdrien Destugues			"Saving the playlist failed:\n\nError: "));
422126ce071SStephan Aßmus		errorMessage << strerror(file.InitCheck());
423126ce071SStephan Aßmus		display_save_alert(errorMessage.String());
424126ce071SStephan Aßmus		return;
425126ce071SStephan Aßmus	}
426126ce071SStephan Aßmus
427126ce071SStephan Aßmus	AutoLocker<Playlist> lock(fPlaylist);
428126ce071SStephan Aßmus	if (!lock.IsLocked()) {
429238df8fbSAdrien Destugues		display_save_alert(B_TRANSLATE("Internal error (locking failed). "
430238df8fbSAdrien Destugues			"Saving the playlist failed."));
431126ce071SStephan Aßmus		return;
432126ce071SStephan Aßmus	}
433126ce071SStephan Aßmus
434126ce071SStephan Aßmus	status_t ret = fPlaylist->Flatten(&file);
435126ce071SStephan Aßmus	if (ret != B_OK) {
436126ce071SStephan Aßmus		display_save_alert(ret);
437126ce071SStephan Aßmus		return;
438126ce071SStephan Aßmus	}
439126ce071SStephan Aßmus	lock.Unlock();
440126ce071SStephan Aßmus
441126ce071SStephan Aßmus	if (origEntry.Exists()) {
442126ce071SStephan Aßmus		// TODO: copy attributes
443126ce071SStephan Aßmus	}
444126ce071SStephan Aßmus
445126ce071SStephan Aßmus	// clobber original entry, if it exists
446d6aa728aSStephan Aßmus	tempEntry.Rename(finalName, true);
447126ce071SStephan Aßmus	remover.Detach();
448126ce071SStephan Aßmus
449126ce071SStephan Aßmus	BNodeInfo info(&file);
450126ce071SStephan Aßmus	info.SetType("application/x-vnd.haiku-playlist");
451126ce071SStephan Aßmus}
452126ce071SStephan Aßmus
4531c68b67aSMarkus Himmel
4541c68b67aSMarkus Himmelvoid
4551c68b67aSMarkus HimmelPlaylistWindow::_QueryInitialDurations()
4561c68b67aSMarkus Himmel{
4571c68b67aSMarkus Himmel	BAutolock lock(fPlaylist);
4581c68b67aSMarkus Himmel
4591c68b67aSMarkus Himmel	BMessage addMessage(MSG_PLAYLIST_ITEM_ADDED);
4601c68b67aSMarkus Himmel	for (int32 i = 0; i < fPlaylist->CountItems(); i++) {
4611c68b67aSMarkus Himmel		addMessage.AddPointer("item", fPlaylist->ItemAt(i));
4621c68b67aSMarkus Himmel		addMessage.AddInt32("index", i);
4631c68b67aSMarkus Himmel	}
4641c68b67aSMarkus Himmel
4651c68b67aSMarkus Himmel	BMessenger(fDurationListener).SendMessage(&addMessage);
4661c68b67aSMarkus Himmel}
4671c68b67aSMarkus Himmel
4681c68b67aSMarkus Himmel
4691c68b67aSMarkus Himmelvoid
4701c68b67aSMarkus HimmelPlaylistWindow::_UpdateTotalDuration(bigtime_t duration)
4711c68b67aSMarkus Himmel{
4721c68b67aSMarkus Himmel	BAutolock lock(this);
4731c68b67aSMarkus Himmel
4741c68b67aSMarkus Himmel	char buffer[64];
4751c68b67aSMarkus Himmel	duration /= 1000000;
4761c68b67aSMarkus Himmel	duration_to_string(duration, buffer, sizeof(buffer));
4771c68b67aSMarkus Himmel
4781c68b67aSMarkus Himmel	BString text;
479ecec5d0eSMarkus Himmel	text.SetToFormat(B_TRANSLATE("Total duration: %s"), buffer);
4801c68b67aSMarkus Himmel
4811c68b67aSMarkus Himmel	fTotalDuration->SetText(text.String());
4821c68b67aSMarkus Himmel}
4831c68b67aSMarkus Himmel
4841c68b67aSMarkus Himmel
4851c68b67aSMarkus Himmel// #pragma mark -
4861c68b67aSMarkus Himmel
4871c68b67aSMarkus Himmel
4881c68b67aSMarkus HimmelPlaylistWindow::DurationListener::DurationListener(PlaylistWindow& parent)
4891c68b67aSMarkus Himmel	:
4901c68b67aSMarkus Himmel	PlaylistObserver(this),
4911c68b67aSMarkus Himmel	fKnown(20, true),
4921c68b67aSMarkus Himmel	fTotalDuration(0),
4931c68b67aSMarkus Himmel	fParent(parent)
4941c68b67aSMarkus Himmel{
4951c68b67aSMarkus Himmel	Run();
4961c68b67aSMarkus Himmel}
4971c68b67aSMarkus Himmel
4981c68b67aSMarkus Himmel
4991c68b67aSMarkus HimmelPlaylistWindow::DurationListener::~DurationListener()
5001c68b67aSMarkus Himmel{
5011c68b67aSMarkus Himmel}
5021c68b67aSMarkus Himmel
5031c68b67aSMarkus Himmel
5041c68b67aSMarkus Himmelvoid
5051c68b67aSMarkus HimmelPlaylistWindow::DurationListener::MessageReceived(BMessage* message)
5061c68b67aSMarkus Himmel{
5071c68b67aSMarkus Himmel	switch (message->what) {
5081c68b67aSMarkus Himmel		case MSG_PLAYLIST_ITEM_ADDED:
5091c68b67aSMarkus Himmel		{
5101c68b67aSMarkus Himmel			void* item;
5111c68b67aSMarkus Himmel			int32 index;
5121c68b67aSMarkus Himmel
5131c68b67aSMarkus Himmel			int32 currentItem = 0;
5141c68b67aSMarkus Himmel			while (message->FindPointer("item", currentItem, &item) == B_OK
5151c68b67aSMarkus Himmel				&& message->FindInt32("index", currentItem, &index) == B_OK) {
5161c68b67aSMarkus Himmel				_HandleItemAdded(static_cast<PlaylistItem*>(item), index);
5171c68b67aSMarkus Himmel				++currentItem;
5181c68b67aSMarkus Himmel			}
5191c68b67aSMarkus Himmel
5201c68b67aSMarkus Himmel			break;
5211c68b67aSMarkus Himmel		}
5221c68b67aSMarkus Himmel
5231c68b67aSMarkus Himmel		case MSG_PLAYLIST_ITEM_REMOVED:
5241c68b67aSMarkus Himmel		{
5251c68b67aSMarkus Himmel			int32 index;
5261c68b67aSMarkus Himmel
5271c68b67aSMarkus Himmel			if (message->FindInt32("index", &index) == B_OK) {
5281c68b67aSMarkus Himmel				_HandleItemRemoved(index);
5291c68b67aSMarkus Himmel			}
5301c68b67aSMarkus Himmel
5311c68b67aSMarkus Himmel			break;
5321c68b67aSMarkus Himmel		}
5331c68b67aSMarkus Himmel
5341c68b67aSMarkus Himmel		default:
5351c68b67aSMarkus Himmel			BLooper::MessageReceived(message);
5361c68b67aSMarkus Himmel			break;
5371c68b67aSMarkus Himmel	}
5381c68b67aSMarkus Himmel}
5391c68b67aSMarkus Himmel
5401c68b67aSMarkus Himmel
5411c68b67aSMarkus Himmelbigtime_t
5421c68b67aSMarkus HimmelPlaylistWindow::DurationListener::TotalDuration()
5431c68b67aSMarkus Himmel{
5441c68b67aSMarkus Himmel	return fTotalDuration;
5451c68b67aSMarkus Himmel}
5461c68b67aSMarkus Himmel
5471c68b67aSMarkus Himmel
5481c68b67aSMarkus Himmelvoid
5491c68b67aSMarkus HimmelPlaylistWindow::DurationListener::_HandleItemAdded(PlaylistItem* item,
5501c68b67aSMarkus Himmel	int32 index)
5511c68b67aSMarkus Himmel{
552b5387effSMarkus Himmel	bigtime_t duration = item->Duration();
5531c68b67aSMarkus Himmel	fTotalDuration += duration;
5541c68b67aSMarkus Himmel	fParent._UpdateTotalDuration(fTotalDuration);
5551c68b67aSMarkus Himmel	fKnown.AddItem(new bigtime_t(duration), index);
5561c68b67aSMarkus Himmel}
5571c68b67aSMarkus Himmel
5581c68b67aSMarkus Himmel
5591c68b67aSMarkus Himmelvoid
5601c68b67aSMarkus HimmelPlaylistWindow::DurationListener::_HandleItemRemoved(int32 index)
5611c68b67aSMarkus Himmel{
5621c68b67aSMarkus Himmel	bigtime_t* deleted = fKnown.RemoveItemAt(index);
5638487f27dSDario Casalinuovo	if (deleted == NULL)
5648487f27dSDario Casalinuovo		return;
5658487f27dSDario Casalinuovo
5661c68b67aSMarkus Himmel	fTotalDuration -= *deleted;
5671c68b67aSMarkus Himmel	fParent._UpdateTotalDuration(fTotalDuration);
5681c68b67aSMarkus Himmel
5691c68b67aSMarkus Himmel	delete deleted;
5701c68b67aSMarkus Himmel}
5711c68b67aSMarkus Himmel
572