/////////////////////////////////////////////////////////////////////////////
// Name:        TitlesetManager.h
// Purpose:     Titleset Manager
// Author:      Alex Thuering
// Created:	27.01.2003
// RCS-ID:      $Id: TitlesetManager.cpp,v 1.67 2012/10/14 16:23:02 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#include "TitlesetManager.h"
#include "MainWin.h"
#include "TitlePropDlg.h"
#include "MenuPropDlg.h"
#include "MPEG.h"
#include "Config.h"
#include "Utils.h"
#include "MessageDlg.h"
#include <wxVillaLib/ThumbnailFactory.h>
#include <wxVillaLib/ImageProc.h>
#include <wxVillaLib/utils.h>
#include <wx/splitter.h>
#include <wx/dnd.h>
#include <wx/clipbrd.h>
#include <wx/utils.h>
#include <wx/artprov.h>
#include "rc/noaudio.png.h"

//////////////////////////////////////////////////////////////////////////////
///////////////////////////// TitleDnDFile ///////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

class TitleDnDFile: public wxDropTarget {
public:
	TitleDnDFile(TitlesetManager* pOwner): m_pOwner(pOwner) {
		wxDataFormat actionDataFormat(DATAFORMAT_ACTION);
		wxDataObjectComposite* dataObject = new wxDataObjectComposite();
		dataObject->Add(m_fileDataObject = new wxFileDataObject);
		dataObject->Add(m_actionDataObject = new wxCustomDataObject(actionDataFormat));
		SetDataObject(dataObject);
		SetDefaultAction(wxDragMove);
	}

	virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def) {
		wxDropTarget::OnEnter(x, y, def);
		return def;
	}

	virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def) {
		wxDropTarget::OnDragOver(x, y, def);
		return def;
	}

	virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def) {
		if (!GetData())
			return wxDragError;
		
		wxDataObjectComposite* dobj = (wxDataObjectComposite*) m_dataObject;
		
		if (dobj->GetReceivedFormat() == wxDF_FILENAME) {
			return OnDropFiles(x, y, m_fileDataObject->GetFilenames(), def) ? def : wxDragError;
		} else if (dobj->GetReceivedFormat().GetId() == DATAFORMAT_ACTION) {
			int x1, y1;
			m_pOwner->CalcUnscrolledPosition(x, y, &x1, &y1);
			m_pOwner->MoveSelectedTo(m_pOwner->GetItemIndex(x1, y1));
			return def;
		}
		
		return wxDragError;
	}

	bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames, wxDragResult def) {
		bool res = false;
		for (int i = 0; i<(int)filenames.Count(); i++)
			res = AddFile(filenames[i], i == 0, def) || res;
		return res;
	}
	
	bool AddFile(wxString filename, bool first, wxDragResult def) {
		if (wxImage::FindHandler(filename.AfterLast('.').Lower(), BITMAP_TYPE_ANY)) {
			m_pOwner->AddImage(filename, def == wxDragMove && first);
			return true;
		} else if (wxThumbnails::IsVideo(filename)) {
			wxFfmpegMediaDecoder decoder;
			if (!decoder.Load(filename))
				return false;
			bool hasVideo = false;
			bool hasAudio = false;
			for (unsigned int i = 0; i < decoder.GetStreamCount(); i++) {
				if (decoder.GetStreamType(i) == stVIDEO)
					hasVideo = true;
				else if (decoder.GetStreamType(i) == stAUDIO)
					hasAudio = true;
			}
			if (!hasVideo && !hasAudio) {
				wxLogError(_("File '%s' doesn't contain any audio or video streams."));
				return false;
			}
			if (hasVideo)
				m_pOwner->AddVideo(filename, def == wxDragMove);
			else
				m_pOwner->AddAudio(filename);
			return true;
		} else if (wxThumbnails::IsAudio(filename)) {
			m_pOwner->AddAudio(filename);
			return true;
		} else if (wxThumbnails::IsSubtitle(filename)) {
			m_pOwner->AddSubtitles(filename);
			return true;
		}
		
		// try to load it as video
		wxFfmpegMediaDecoder decoder;
		if (!decoder.Load(filename))
			return false;
		m_pOwner->AddVideo(filename, def == wxDragMove);
		return true;
	}
protected:
	TitlesetManager *m_pOwner;
	wxFileDataObject* m_fileDataObject;
	wxCustomDataObject* m_actionDataObject;
};

//////////////////////////////////////////////////////////////////////////////
/////////////////////////// TitlesetManager //////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

enum {
	MENU_TITLE_DEL_ID = 2650,
	MENU_TITLE_COPY_ID,
	MENU_TITLE_PASTE_ID,
	MENU_TITLE_PROP_ID,
	MENU_TITLE_ADD_ID,
	MENU_TITLE_MOVE_ID,
	MENU_MAIN_ADD_ID,
	MENU_MOVE_TITLE_LEFT_ID,
	MENU_MOVE_TITLE_RIGHT_ID
};

BEGIN_EVENT_TABLE(TitlesetManager, wxThumbnails)
	EVT_UPDATE_UI(MainWin::MENU_DVD_ADD_TITLESET_ID, TitlesetManager::OnAddTitlesetUpdateUI)
	EVT_MENU(MENU_MOVE_TITLE_LEFT_ID, TitlesetManager::OnMoveTitleLeft)
	EVT_MENU(MENU_MOVE_TITLE_RIGHT_ID, TitlesetManager::OnMoveTitleRight)
	EVT_UPDATE_UI(MENU_MOVE_TITLE_LEFT_ID, TitlesetManager::OnMoveTitleLeftUpdateUI)
	EVT_UPDATE_UI(MENU_MOVE_TITLE_RIGHT_ID, TitlesetManager::OnMoveTitleRightUpdateUI)
	EVT_MENU(MENU_TITLE_DEL_ID, TitlesetManager::OnDelete)
	EVT_MENU(MENU_TITLE_COPY_ID, TitlesetManager::OnCopy)
	EVT_MENU(MENU_TITLE_PASTE_ID, TitlesetManager::OnPaste)
	EVT_UPDATE_UI(MENU_TITLE_PASTE_ID, TitlesetManager::OnUpdateUIObjectPaste)
	EVT_MENU(MENU_TITLE_PROP_ID, TitlesetManager::OnProps)
END_EVENT_TABLE()

DEFINE_EVENT_TYPE(EVT_COMMAND_MENU_SELECTED)
DEFINE_EVENT_TYPE(EVT_COMMAND_DVD_CHANGED)

TitlesetManager::TitlesetManager(wxWindow* parent, int id): wxThumbnails(parent, id) {
	SetThumbSize(96, 76);
	SetOrientaion(wxTHUMB_HORIZONTAL);
	SetDropTarget(new TitleDnDFile(this));
	EnableDragging(false); // disable standard dragging of wxThumbnails
	m_dvd = NULL;
	m_selectedMenu = -1;

	wxMenu* addMenu = new wxMenu;
	addMenu->Append(MainWin::MENU_DVD_ADD_FILE_ID, _("&File..."));
	addMenu->Append(MainWin::MENU_DVD_ADD_FILE_AS_CHAPTER_ID, _("File as &chapter..."));
	addMenu->Append(MainWin::MENU_DVD_ADD_MENU_ID, _("&Menu"));
	addMenu->Append(MainWin::MENU_DVD_ADD_VMMENU_ID, _("&vmMenu"));
	addMenu->Append(MainWin::MENU_DVD_ADD_TITLESET_ID, _("&Titleset"));

	m_mainMenu = new wxMenu;
	m_mainMenu->Append(MENU_MAIN_ADD_ID, _("&Add"), addMenu);
	wxMenuItem* item = new wxMenuItem(m_mainMenu, MENU_TITLE_PASTE_ID, _("&Paste"));
	item->SetBitmap(wxArtProvider::GetBitmap(wxART_PASTE, wxART_MENU));
	m_mainMenu->Append(item);
	SetGlobalPopupMenu(m_mainMenu);


	wxMenu* moveMenu = new wxMenu;
	moveMenu->Append(MENU_MOVE_TITLE_LEFT_ID, _("&Left"));
	moveMenu->Append(MENU_MOVE_TITLE_RIGHT_ID, _("&Right"));

	wxMenu* addMenu2 = new wxMenu;
	addMenu2->Append(MainWin::MENU_DVD_ADD_FILE_ID, _("&File..."));
	addMenu2->Append(MainWin::MENU_DVD_ADD_FILE_AS_CHAPTER_ID, _("File as &chapter..."));
	addMenu2->Append(MainWin::MENU_DVD_ADD_MENU_ID, _("&Menu"));
	addMenu2->Append(MainWin::MENU_DVD_ADD_VMMENU_ID, _("&vmMenu"));
	addMenu2->Append(MainWin::MENU_DVD_ADD_TITLESET_ID, _("&Titleset"));

	m_titleMenu = new wxMenu;
	m_titleMenu->Append(MENU_TITLE_ADD_ID, _("&Add"), addMenu2);
	m_titleMenu->Append(MENU_TITLE_MOVE_ID, _("&Move"), moveMenu);
	item = new wxMenuItem(m_titleMenu, MENU_TITLE_COPY_ID, _("&Copy"));
	item->SetBitmap(wxArtProvider::GetBitmap(wxART_COPY, wxART_MENU));
	m_titleMenu->Append(item);
	item = new wxMenuItem(m_mainMenu, MENU_TITLE_PASTE_ID, _("&Paste"));
	item->SetBitmap(wxArtProvider::GetBitmap(wxART_PASTE, wxART_MENU));
	m_titleMenu->Append(item);
	item = new wxMenuItem(m_titleMenu, MENU_TITLE_DEL_ID, _("&Delete"));
	item->SetBitmap(wxArtProvider::GetBitmap(wxART_DELETE, wxART_MENU));
	m_titleMenu->Append(item);
	m_titleMenu->Append(MENU_TITLE_PROP_ID, _("&Properties..."));
	SetPopupMenu(m_titleMenu);
}

TitlesetManager::~TitlesetManager() {
	// nothing to do
}

void TitlesetManager::SetDVD(DVD* dvd) {
	m_dvd = dvd;
	SetSelected(-1);
	m_selectedMenu = 0;
	UpdateItems();
}

wxBitmap TitlesetManager::GetThumbImage(wxThumb& thumb) {
	if (!m_dvd)
		return wxBitmap();
	int id = thumb.GetId();
	Vob* vob = m_dvd->GetVob(DVD::GetTsi(id), DVD::IsMenu(id), DVD::GetPgci(id), DVD::GetVobi(id));
	wxBitmap img = thumb.GetBitmap();
	if (!img.Ok()) {
		int titleHeight = m_tTextHeight + 2;
		if (vob->GetMenu()) {
			img = wxBitmap(vob->GetMenu()->GetImage(m_tWidth-8, m_tHeight-titleHeight));
			thumb.SetBitmap(img);
		} else if (vob->GetSlideshow()) {
			img = wxBitmap(vob->GetSlideshow()->GetThumbImage(m_tWidth-8, m_tHeight-titleHeight));
			thumb.SetBitmap(img);
		} else
			img = thumb.GetBitmap(this, m_tWidth, m_tHeight-titleHeight);
	}
	return img;
}

void TitlesetManager::DrawThumbnail(wxBitmap& bmp, wxThumb& thumb, int index) {
	if (!m_dvd)
		return;
	int id = thumb.GetId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	int vobi = DVD::GetVobi(id);
	bool isMenu = DVD::IsMenu(id);
	Pgc* pgc = m_dvd->GetPgc(tsi, isMenu, pgci);
	Vob* vob = pgc->GetVobs()[vobi];

	wxMemoryDC dc;
	dc.SelectObject(bmp);
	dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
	int x = m_tBorder/2;
	int y = m_tBorder/2;
	int tw = bmp.GetWidth() - m_tBorder + 2;
	int th = bmp.GetHeight() - m_tBorder + 2;
	int titleHeight = m_tTextHeight + 2;
	// background
	dc.SetPen(wxPen(*wxBLACK, 0, wxTRANSPARENT));
	dc.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
	dc.DrawRectangle(0, 0, bmp.GetWidth(), bmp.GetHeight());
	if (pgc->GetVobs().Count() > 1) {
		dc.SetBrush(wxBrush(wxColour(64, 64, 64), wxSOLID));
		if (vobi == 0) {
			dc.DrawRectangle(bmp.GetWidth()/2, y, bmp.GetWidth()/2, th);
		} else if (vobi == (int) pgc->GetVobs().Count() - 1) {
			dc.DrawRectangle(0, y, bmp.GetWidth()/2, th);
		} else {
			dc.DrawRectangle(0, y, bmp.GetWidth(), th);
		}
	}
	// rounded rectangle
	wxColour rectColour = isMenu ? wxColour(64, 64, 64) : wxColour(0, 0, 0);
	dc.SetBrush(wxBrush(rectColour, wxSOLID));
	dc.DrawRoundedRectangle(x, y, tw, th, 12);
	// draw begin & end of titleset
	if (IsBeginOfTitleset(tsi, pgci, vobi, isMenu)) {
		int h = bmp.GetHeight();
		dc.SetPen(wxPen(*wxRED, 0, wxSOLID));
		dc.DrawLine(1, 1, 10, 1);
		dc.DrawLine(1, 1, 1, 10);
		dc.DrawLine(1, h-2, 10, h-2);
		dc.DrawLine(1, h-2, 1, h-11);
	}
	if (IsEndOfTitleset(tsi, pgci, vobi, isMenu)) {
		int h = bmp.GetHeight();
		int w = bmp.GetWidth();
		dc.SetPen(wxPen(*wxRED, 0, wxSOLID));
		dc.DrawLine(w-1, 1, w-10, 1);
		dc.DrawLine(w-1, 1, w-1, 10);
		dc.DrawLine(w-1, h-2, w-10, h-2);
		dc.DrawLine(w-1, h-2, w-1, h-11);
	}
	// image
	wxBitmap img = GetThumbImage(thumb);
	if (index == m_pointed) {
		wxImage imgTmp = img.ConvertToImage();
		wxAdjustBrightness(imgTmp, wxRect(0, 0, img.GetWidth(), img.GetHeight()),
				POINTED_BRIGHTNESS);
		img = wxBitmap(imgTmp);
	}
	wxRect imgRect(x + (m_tWidth-img.GetWidth())/2, y + titleHeight + (m_tHeight-titleHeight-img.GetHeight())/2,
			img.GetWidth(), img.GetHeight());
	dc.DrawBitmap(img, imgRect.x, imgRect.y);
	// check audio
	if (!vob->GetMenu() && !vob->GetSlideshow() && !vob->HasAudio()) {
		wxBitmap noaudioBitmap = wxBITMAP_FROM_MEMORY(noaudio);
		dc.DrawBitmap(noaudioBitmap,
				imgRect.x + img.GetWidth() - noaudioBitmap.GetWidth(),
				imgRect.y + img.GetHeight() - noaudioBitmap.GetHeight());
	} else if (vob->GetAudioFilenames().GetCount() > 0) {
		wxString text = vob->GetAudioFilenames()[0].AfterLast(wxT('.')).Lower();
		for (int fi = 1; fi < (int)vob->GetAudioFilenames().GetCount(); fi++)
			text += wxT(",") + vob->GetAudioFilenames()[fi].AfterLast(wxT('.')).Lower();
		dc.SetTextForeground(*wxWHITE);
		wxFont font(8, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
		dc.SetFont(font);
		wxCoord w, h;
		dc.GetTextExtent(text, &w, &h);
		dc.DrawText(text, imgRect.x + img.GetWidth() - w - 4, imgRect.y + img.GetHeight() - h);
	}
	// outline
	if (index == m_selectedMenu) {
		dc.SetPen(wxPen(*wxBLUE, 0, wxSOLID));
		dc.SetBrush(wxBrush(*wxBLACK, wxTRANSPARENT));
		dc.DrawRectangle(imgRect.x-1, imgRect.y-1, imgRect.width+1, imgRect.height+1);
	}
	// draw title caption
	dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
	dc.SetTextForeground(*wxWHITE);
	wxString caption;
	if (isMenu && tsi<0)
		caption = _("vmMenu");
	else if (isMenu)
		caption = _("Menu");
	else
		caption = _("Title");
	caption += wxString::Format(_T(" %d"), pgci+1);
	if (vobi>0)
		caption += wxString::Format(_T("-%d"), vobi+1);
	int sw, sh;
	dc.GetTextExtent(caption, &sw, &sh);
	int tx = x + (m_tWidth-sw)/2;
	int ty = y + (titleHeight-sh)/2;
	dc.DrawText(caption, tx, ty);
	// draw caption
	for (int i=0; i<thumb.GetCaptionLinesCount(m_tWidth-m_tCaptionBorder); i++) {
		wxString caption = thumb.GetCaption(i);
		dc.GetTextExtent(caption, &sw, &sh);
		tx = x + (m_tWidth-sw)/2;
		ty = y + m_tHeight + i*m_tTextHeight + (m_tTextHeight-sh)/2;
		dc.DrawText(caption, tx, ty);
	}

	dc.SelectObject(wxNullBitmap);
}

void TitlesetManager::Paint(wxPaintDC& dc) {
	wxThumbnails::Paint(dc);
	if (m_dvd->GetTitlesets().size() == 1 && m_dvd->GetTitlesets()[0]->GetTitles().size() == 0) {
		dc.SetPen(*wxBLACK_PEN);
		dc.SetBrush(wxBrush(GetBackgroundColour(), wxTRANSPARENT));
		dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
		int col = m_items.GetCount() % m_cols;
		dc.DrawText(_("Drag here your video files from file browser"),
				col * (m_tWidth + m_tBorder) + 2 * m_tBorder, m_tBorder + m_tHeight / 2);
	}
}

int TitlesetManager::GetRowHeight(unsigned int begRow, unsigned int count) {
	int capHeight = 0;
	for (unsigned int i=begRow; i<begRow+count; i++)
		if (i < m_tCaptionHeight.GetCount())
			capHeight += m_tCaptionHeight[i]>0 ? m_tCaptionHeight[i] : 1;
	return (m_tHeight + m_tBorder)*count + capHeight*m_tTextHeight;
}

void TitlesetManager::UpdateItems() {
	wxThumbnailFactory::ClearQueue(this);

	int selectedMenuId = -1; // to update m_selectedMenu
	if (m_selectedMenu>=0 && m_selectedMenu<(int)m_items.Count())
		selectedMenuId = m_items[m_selectedMenu]->GetId();
	WX_CLEAR_ARRAY(m_items)
	Menus* menus = &m_dvd->GetVmgm();
	for (int pgci = 0; pgci<(int)menus->Count(); pgci++) {
		Pgc* pgc = (*menus)[pgci];
		if (!pgc->GetVobs().Count())
			continue;
		wxThumb* thumb = new wxThumb(_T(""));
		thumb->SetId(DVD::MakeId(-1, pgci, 0, true));
		m_items.Add(thumb);
	}
	for (int tsi = 0; tsi<(int)m_dvd->GetTitlesets().Count(); tsi++) {
		Titleset* ts = m_dvd->GetTitlesets()[tsi];
		menus = &ts->GetMenus();
		for (int pgci = 0; pgci<(int)menus->Count(); pgci++) {
			Pgc* pgc = (*menus)[pgci];
			if (!pgc->GetVobs().Count())
				continue;
			wxThumb* thumb = new wxThumb(_T(""));
			thumb->SetId(DVD::MakeId(tsi, pgci, 0, true));
			m_items.Add(thumb);
		}
		for (int pgci = 0; pgci<(int)ts->GetTitles().Count(); pgci++) {
			Pgc* pgc = ts->GetTitles()[pgci];
			for (int vobi = 0; vobi<(int)pgc->GetVobs().Count(); vobi++) {
				Vob* vob = pgc->GetVobs()[vobi];
				wxString fname;
				wxString caption;
				if (vob->GetSlideshow()) {
					if (vob->GetSlideshow()->GetCount() > 1)
						caption = wxString::Format(_("Slideshow (%d images)"), vob->GetSlideshow()->GetCount());
					else
						caption = _("Slideshow (1 image)");
				} else {
					fname = vob->GetFilename();
					caption = vob->GetFilenameDisplay().AfterLast(wxFILE_SEP_PATH);
					if (caption.Find(wxT('.')) > 0)
						caption = caption.BeforeLast(wxT('.'));
				}
				wxThumb* thumb = new wxThumb(fname, caption);
				thumb->SetId(DVD::MakeId(tsi, pgci, vobi, false));
				m_items.Add(thumb);
			}
		}
	}
	if (selectedMenuId>=0) // update m_selectedMenu
		m_selectedMenu = GetIndex(selectedMenuId);
	UpdateProp();
	Refresh();
	wxSplitterWindow* p = (wxSplitterWindow*) GetParent(); 
	p->SetSashPosition(p->GetSashPosition() + (m_rows > 1 ? 0 : 1000));
}

int TitlesetManager::GetIndex(int tsi, int pgci, int vobi, bool menu) {
	return GetIndex(DVD::MakeId(tsi, pgci, vobi, menu));
}

int TitlesetManager::GetIndex(int id) {
	for (int i=0; i<(int)m_items.Count(); i++) {
		if (m_items[i]->GetId() == id)
			return i;
	}
	return -1;
}

bool TitlesetManager::AddVideo(const wxString& fname, bool createTitle, int tsi) {
	Vob* vob = new Vob(_T(""));
	if (!vob->SetFilename(fname))
		return false;
	Stream* videoStream = vob->GetVideoStream();
	if (videoStream == NULL) {
		wxLogError(_("%s doesn't contain video stream."), fname.c_str());
		return false;
	}
		
	int videoBitrateOld = m_dvd->GetVideoBitrate();
	
	if (m_dvd->GetTitlesets().Count() == 0)
		m_dvd->GetTitlesets().Add(new Titleset);
	if (tsi < 0)
		tsi = m_dvd->GetTitlesets().GetCount() - 1;
	PgcArray& pgcs = m_dvd->GetTitlesets()[tsi]->GetTitles();
	
	if (pgcs.Count() == 0) {
		// first title => choose aspect ratio
		pgcs.GetVideo().SetAspect(videoStream->GetSourceAspectRatio() > 1.4 ? ar16_9 : ar4_3);
		// check DVD video format
		if (m_dvd->GetTitlesets().Count() == 1) {
			int fps = lroundf(videoStream->GetSourceFps());
			if (isNTSC(m_dvd->GetVideoFormat()) && fps == 25) {
				if (wxMessageBox(_("This file contains video in PAL format. Do you want to change DVD format to PAL too?"),
						_("Burn"), wxYES_NO|wxICON_QUESTION, this) == wxYES)
					m_dvd->SetVideoFormat(vfPAL, true);
			} else if (!isNTSC(m_dvd->GetVideoFormat()) && (fps == 24 || fps == 30)) {
				if (wxMessageBox(_("This file contains video in NTSC format. Do you want to change DVD format to NTSC too?"),
						_("Burn"), wxYES_NO|wxICON_QUESTION, this) == wxYES)
					m_dvd->SetVideoFormat(vfNTSC, true);
			}
		}
	} else if (createTitle && pgcs.Count() >= 99) {
		wxLogError(_("Maximal 99 titles are allowed for one titleset."));
		return false;
	}
	if (pgcs.GetVideo().GetFormat() <= vfCOPY)
		pgcs.GetVideo().SetFormat(m_dvd->GetVideoFormat());
	// check if reencoding is needed
	for (unsigned int i=0; i<vob->GetStreams().GetCount(); i++) {
		Stream* stream = vob->GetStreams()[i];
		if (stream->GetType() == stVIDEO) {
			// set destination format (default COPY) if codec is not mpeg2
			if (!stream->GetSourceCodecName().StartsWith(wxT("mpeg2video"))
					|| stream->GetSourceVideoFormat() != pgcs.GetVideo().GetFormat()
					|| fabs(stream->GetSourceAspectRatio()
							- GetFrameAspectRatio(vfPAL, pgcs.GetVideo().GetAspect())) >= 0.15)
				stream->SetDestinationFormat(pgcs.GetVideo().GetFormat());
			else if (vob->GetAudioFilenames().Count() == 0 && MPEG::HasNavPacks(vob->GetFilename()))
				vob->SetDoNotTranscode(true);  // do not remultiplex/transcode if the video is already a VOB
		} else if (stream->GetType() == stAUDIO) {
			// set destination format (default COPY) if codec is not mp2/ac3 or sample rate is not 48 kHz
			if (stream->GetSourceAudioFormat() != m_dvd->GetAudioFormat()
					|| stream->GetSourceSampleRate() != 48000) {
				stream->SetDestinationFormat(m_dvd->GetAudioFormat());
				vob->SetDoNotTranscode(false);
			}
		} else if (stream->GetType() == stSUBTITLE) {
			stream->SetDestinationFormat(sfNONE);
		}
	}
	// set chapters
	int n = 10;
	if (vob->GetDuration() > 0) {
		int duration = (int) vob->GetDuration() / 60;
		n = duration / s_config.GetDefChapterLength();
		if (n > 0 && duration - s_config.GetDefChapterLength()*n < s_config.GetDefChapterLength()/5)
			n--;
	}
	if (n>100)
		n = 100;
	
	Pgc* pgc = pgcs.Count()>0 ? pgcs.Last() : NULL;
	if (!createTitle && pgc != NULL)
		vob->GetCells().push_back(new Cell(0));
		long t = s_config.GetDefChapterLength();
		for (int i = 0; i < n; i++) {
		vob->GetCells().push_back(new Cell(t * 60000));
		t += s_config.GetDefChapterLength();
	}
	
	// add vob
	if (createTitle || pgc == NULL) {
		pgc = new Pgc;
		pgc->SetPostCommands(m_dvd->GetDefPostCommandStr());
		pgc->GetVobs().Add(vob);
		pgcs.Add(pgc);
		if (!m_dvd->UpdateButtonImageFor(tsi, pgcs.GetCount() - 1) && pgcs.GetCount() > 4) {
			// find menu to duplicate
			PgcArray& menus = m_dvd->GetPgcArray(tsi, true);
			for (unsigned int menuIdx = 0; menuIdx < menus.GetCount(); menuIdx++) {
				Menu* menu = menus[menuIdx]->GetMenu();
				if (menu && menu->GetButtonByJumpAction(-2, pgcs.GetCount() - 2)
						&&  menu->GetButtonByJumpAction(-2, pgcs.GetCount() - 3)) {
					DuplicateMenu(tsi, menuIdx);
					break;
				}
			}
		}
	} else
		pgc->GetVobs().Add(vob);
	
	// set aspect ratio if it isn't set
	if (pgcs.GetVideo().GetAspect() == arAUTO) {
		float aspect = vob->GetVideoStream()->GetSourceAspectRatio();
		if (aspect > 0)
			pgcs.GetVideo().SetAspect(aspect < 1.4 ? ar4_3 : ar16_9);
		else
			pgcs.GetVideo().SetAspect(m_dvd->GetAspectRatio());
	}
	
	UpdateItems();
	SendDvdChangedEvent();
	
	if (videoBitrateOld >= 2000 && m_dvd->GetVideoBitrate() < 2000) {
		wxMessageBox(_("The video bitrate is reduced below 2 MB/s to fit your video on DVD. \
This can produce DVD with bad video quality."), wxT("DVDStyler"), wxOK | wxICON_WARNING, this);
	}
	
	return true;
}

bool TitlesetManager::AddAudio(const wxString& fname) {
	if (m_dvd->GetTitlesets().Count() == 0)
		m_dvd->GetTitlesets().Add(new Titleset);

	PgcArray& pgcs = m_dvd->GetTitlesets().Last()->GetTitles();
	if (pgcs.GetCount() == 0 || pgcs.Last()->GetVobs().GetCount() == 0) {
		wxLogError(_("Please add a video track first."));
		return false;
	}
	Vob* vob = pgcs.Last()->GetVobs().Last();
	vob->AddAudioFile(fname);
	vob->SetDoNotTranscode(false);
	// check if reencoding is needed
	Stream* stream = vob->GetStreams()[vob->GetStreams().GetCount()-1];
	if (vob->GetStreams().GetCount() == 2 && stream->GetSourceAudioFormat() != m_dvd->GetAudioFormat())
		stream->SetDestinationFormat(m_dvd->GetAudioFormat());
	else if (stream->GetSourceAudioFormat() != afMP2 && stream->GetSourceAudioFormat() != afAC3)
		stream->SetDestinationFormat(m_dvd->GetAudioFormat());
	else if (stream->GetSourceSampleRate() != 48000)
		stream->SetDestinationFormat(stream->GetSourceAudioFormat());
	UpdateItems();
	SendDvdChangedEvent();
	return true;
}

bool TitlesetManager::AddSubtitles(const wxString& fname) {
	if (m_dvd->GetTitlesets().Count() == 0)
		m_dvd->GetTitlesets().Add(new Titleset);

	PgcArray& pgcs = m_dvd->GetTitlesets().Last()->GetTitles();
	if (pgcs.Count()==0 || pgcs.Last()->GetVobs().Count()==0) {
		wxLogError(_("Please add a video track first."));
		return false;
	}
	Vob* vob = pgcs.Last()->GetVobs().Last();
	vob->GetSubtitles().Add(new TextSub(fname));
	UpdateItems();
	SendDvdChangedEvent();
	return true;
}

void TitlesetManager::AddImage(const wxString& fname, bool createTitle) {
	if (m_dvd->GetTitlesets().Count() == 0)
		m_dvd->GetTitlesets().Add(new Titleset);

	PgcArray& pgcs = m_dvd->GetTitlesets().Last()->GetTitles();
	Pgc* pgc = pgcs.Count()>0 ? pgcs.Last() : NULL;

	if (createTitle || pgc == NULL) {
		pgc = new Pgc;
		pgc->SetPostCommands(_T("call last menu;"));
		pgcs.Add(pgc);
	}

	Slideshow* slideshow = pgc->GetSlideshow();
	if (slideshow == NULL) {
		if (pgcs.GetVideo().GetAspect() == arAUTO)
			pgcs.GetVideo().SetAspect(m_dvd->GetAspectRatio());
		slideshow = new Slideshow(m_dvd->GetVideoFormat(), pgcs.GetVideo().GetAspect());
		Vob* vob = new Vob(slideshow);
		pgc->GetVobs().Add(vob);
	}
	slideshow->AddSlide(new Slide(fname));

	UpdateItems();
	SendDvdChangedEvent();
}

void TitlesetManager::SelectMenu(int tsi, int pgci) {
	m_selectedMenu = GetIndex(tsi, pgci, 0, true);
	SendMenuSelectedEvent();
}

void TitlesetManager::SelectFirstMenu() {
	m_selectedMenu = -1;
	for (int tsi=-1; tsi<(int)m_dvd->GetTitlesets().Count(); tsi++) {
		if (m_dvd->GetMenu(tsi, 0)) {
			m_selectedMenu = GetIndex(tsi, 0, 0, true);
			break;
		}
	}
	SendMenuSelectedEvent();
}

bool TitlesetManager::IsBeginOfTitleset(int tsi, int pgci, int vobi, bool isMenu) {
	if (tsi<0 || (m_dvd->GetTitlesets().Count()<=1 && m_dvd->GetVmgm().Count()==0))
		return false;
	return pgci == 0 && vobi == 0
		&& (isMenu || m_dvd->GetTitlesets()[tsi]->GetMenus().Count() == 0);
}

bool TitlesetManager::IsEndOfTitleset(int tsi, int pgci, int vobi, bool isMenu) {
	if (tsi<0 || m_dvd->GetTitlesets().Count() < 2
			|| tsi == (int)m_dvd->GetTitlesets().Count()-1)
		return false;
	Titleset* ts = m_dvd->GetTitlesets()[tsi];
	return (isMenu && ts->GetTitles().Count() == 0
				&& pgci == (int)ts->GetMenus().Count()-1
				&& vobi == (int)ts->GetMenus()[pgci]->GetVobs().Count()-1)
		|| (!isMenu && pgci == (int)ts->GetTitles().Count()-1
				&& vobi == (int)ts->GetTitles()[pgci]->GetVobs().Count()-1);
}

void TitlesetManager::SendMenuSelectedEvent() {
	wxCommandEvent evt(EVT_COMMAND_MENU_SELECTED, this->GetId());
	GetEventHandler()->ProcessEvent(evt);
}

void TitlesetManager::SendDvdChangedEvent() {
	wxCommandEvent evt(EVT_COMMAND_DVD_CHANGED, this->GetId());
	GetEventHandler()->ProcessEvent(evt);
}

void TitlesetManager::OnMouseUp(wxMouseEvent& event) {
	wxThumbnails::OnMouseUp(event);
	// Popup menu
	if (event.RightUp())
		return;
	// select menu
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	if (DVD::IsMenu(id)) {
		SelectMenu(DVD::GetTsi(id), DVD::GetPgci(id));
		Refresh();
	}
}

void TitlesetManager::OnMouseDClick(wxMouseEvent& WXUNUSED(event)) {
	wxCommandEvent evt;
	OnProps(evt);
}

void TitlesetManager::OnMouseMove(wxMouseEvent &event) {
	wxThumbnails::OnMouseMove(event);
	// -- drag & drop --
	if (event.Dragging() && m_selected >= 0 && m_beginDrag) {
		DVDAction action;
		action.SetTsi(DVD::GetTsi(GetSelectedId()));
		action.SetPgci(DVD::GetPgci(GetSelectedId()));
		action.SetMenu(DVD::IsMenu(GetSelectedId()));
		int vobi = DVD::GetVobi(GetSelectedId());
		if (vobi > 0) {
			Pgc* pgc = m_dvd->GetPgc(action.GetTsi(), action.IsMenu(), action.GetPgci());
			Vob* vob = pgc->GetVobs()[vobi];
			action.SetChapter(pgc->GetChapterCount(vobi) + (vob->GetChapters().length() ? 0 : -1));
		}
		wxString actionStr = action.Serialize();
		wxDataFormat format(DATAFORMAT_ACTION);
		wxCustomDataObject dataObj(format);
		dataObj.SetData(actionStr.length(), actionStr.ToAscii());
		wxDropSource source(dataObj, this);
		source.DoDragDrop(wxDrag_DefaultMove);
		m_beginDrag = false;
	}
}

void TitlesetManager::AddMenu() {
	int tsi = m_dvd->GetTitlesets().Count()-1;
	if (GetSelected()>=0)
		tsi = DVD::GetTsi(GetSelectedId());
	int pgci = m_dvd->AddMenu(tsi);
	UpdateItems();
	SelectMenu(tsi, pgci);
	SendDvdChangedEvent();
}

void TitlesetManager::AddVmMenu() {
	int tsi = -1;
	int pgci = m_dvd->AddMenu(tsi);
	UpdateItems();
	SelectMenu(tsi, pgci);
	SendDvdChangedEvent();
}

void TitlesetManager::AddTitleset() {
	m_dvd->AddTitleset();
	AddMenu();
}

bool TitlesetManager::DuplicateMenu(int tsi, int pgci) {
	Vob* vob = m_dvd->GetVob(tsi, true, pgci, 0);
	if (!vob || !vob->GetMenu())
		return false;
	wxSvgXmlNode* menuXml = vob->GetMenu()->GetXML(DVDSTYLER_XML);
	// add new menu
	int pgci2 = m_dvd->AddMenu(tsi);
	Menu* menu = m_dvd->GetMenu(tsi, pgci2);
	menu->PutXML(menuXml);
	delete menuXml;
	// fix jump actions
	int actionPgci = m_dvd->GetPgcArray(tsi, false).GetCount() - 1;
	for (unsigned int obji = 0; obji < menu->GetObjectsCount(); obji++) {
		MenuObject* obj = menu->GetObject(obji);
		if (obj->IsButton() && !obj->GetAction().IsCustom() && obj->GetAction().GetTsi() == -2) {
			if (!obj->GetAction().IsMenu() && !obj->GetAction().IsPlayAll()) {
				obj->GetAction().SetPgci(actionPgci);
				menu->UpdateButtonImageFor(-2, actionPgci, m_dvd);
				actionPgci++;
			} else if (obj->GetAction().IsMenu()) {
				if (obj->GetAction().GetPgci() == pgci2)
					obj->GetAction().SetPgci(pgci2 + 1);
				else if (obj->GetAction().GetPgci() == 100)
					obj->GetAction().SetPgci(pgci2 - 1);
				else if (pgci2 > 2 && obj->GetAction().GetPgci() == pgci2 - 2)
					obj->GetAction().SetPgci(pgci2 - 1);
			}
		}
	}
	return true;
}

void TitlesetManager::FixVobChapters(Pgc* pgc, int vobi, int vobi2) {
	wxString chapters = pgc->GetVobs()[vobi]->GetChapters();
	wxString chapters2 = pgc->GetVobs()[vobi2]->GetChapters();
	if (!(chapters + wxT(",")).StartsWith(wxT("0:00,")) && (chapters2 + wxT(",")).StartsWith(wxT("0:00,"))) {
		chapters = (chapters.length() ? wxT("0:00,") : wxT("0:00")) + chapters;
		pgc->GetVobs()[vobi]->SetChapters(chapters);
		chapters2 = chapters2.length() > 4 ? chapters2.substr(5) : wxT("");
		pgc->GetVobs()[vobi2]->SetChapters(chapters2);
	}
}

void TitlesetManager::MoveSelectedTo(int idx) {
	if (GetSelected() < 0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	int vobi = DVD::GetVobi(id);
	PgcArray& pgcs = m_dvd->GetPgcArray(tsi, isMenu);
	
	int id2 = -1;
	if (idx < 0) {
		int tsi2 = m_dvd->GetTitlesets().size() - 1;
		int pgci2 = m_dvd->GetPgcArray(tsi2, false).size() - 1;
		id2 = DVD::MakeId(tsi2, pgci2 >= 0 ? pgci2 : 0, 0, false);
	} else
		id2 = GetItem(idx)->GetId();
	if (id == id2 || id2 < 0)
		return;
	int tsi2 = DVD::GetTsi(id2);
	int pgci2 = DVD::GetPgci(id2);
	bool isMenu2 = DVD::IsMenu(id2);
	int vobi2 = DVD::GetVobi(id2);
	
	if (!isMenu && isMenu2) {
		pgci2 = 0;
		isMenu2 = false;
		if (tsi2 == -1)
			return;
	} else if (isMenu && !isMenu2) {
		PgcArray& pgcs2 = m_dvd->GetPgcArray(tsi2, true);
		pgci2 = pgcs2.size() > 0 ? pgcs2.size() - 1 : 0;
		isMenu2 = true;
	}
	if (tsi2 == tsi) {
		if (pgci2 > pgci) {
			int pgciTmp = pgci;
			while (pgciTmp != pgci2) {
				Pgc* pgc = pgcs[pgciTmp];
				pgcs[pgciTmp] = pgcs[pgciTmp + 1];
				pgcs[++pgciTmp] = pgc;
			}
			if (isMenu) {
				SelectMenu(tsi2, pgci2);
			} else {
				for (int i = pgci; i <= pgci2; i++)
					m_dvd->UpdateButtonImageFor(tsi, i);
			}
		} else if (pgci2 < pgci) {
			int pgciTmp = pgci;
			while (pgciTmp != pgci2) {
				Pgc* pgc = pgcs[pgciTmp];
				pgcs[pgciTmp] = pgcs[pgciTmp - 1];
				pgcs[--pgciTmp] = pgc;
			}
			if (isMenu) {
				SelectMenu(tsi2, pgci2);
			} else {
				for (int i = pgci2; i <= pgci; i++)
					m_dvd->UpdateButtonImageFor(tsi, i);
			}
		} else if (vobi2 > vobi) {
			Pgc* pgc = pgcs[pgci];
			if (vobi == 0)
				FixVobChapters(pgc, vobi, vobi2);
			int vobiTmp = vobi;
			while (vobiTmp != vobi2) {
				Vob* vob = pgc->GetVobs()[vobiTmp];
				pgc->GetVobs()[vobiTmp] = pgc->GetVobs()[vobiTmp + 1];
				pgc->GetVobs()[++vobiTmp] = vob;
			}
			if (!isMenu)
				m_dvd->UpdateButtonImageFor(tsi, pgci);
		} else if (vobi2 < vobi) {
			Pgc* pgc = pgcs[pgci];
			if (vobi2 == 0)
				FixVobChapters(pgc, vobi2, vobi);
			int vobiTmp = vobi;
			while (vobiTmp != vobi2) {
				Vob* vob = pgc->GetVobs()[vobiTmp];
				pgc->GetVobs()[vobiTmp] = pgc->GetVobs()[vobiTmp - 1];
				pgc->GetVobs()[--vobiTmp] = vob;
			}
			if (!isMenu)
				m_dvd->UpdateButtonImageFor(tsi, pgci);
		} else
			return;
	} else {
		Pgc* pgc = pgcs[pgci];
		pgcs.RemoveAt(pgci);
		PgcArray& pgcs2 = m_dvd->GetPgcArray(tsi2, isMenu2);
		pgcs2.Insert(pgc, pgci2);
		if (!isMenu) {
			for (unsigned int i = pgci; i < pgcs.size(); i++)
				m_dvd->UpdateButtonImageFor(tsi, i);
			for (unsigned int i = pgci2; i < pgcs2.size(); i++)
				m_dvd->UpdateButtonImageFor(tsi2, i);
		} else {
			SelectMenu(tsi2, pgci2);
		}
		if (tsi >= 0 && m_dvd->GetPgcArray(tsi, false).size() == 0
				&& m_dvd->GetPgcArray(tsi, true).size() == 0) {
			delete m_dvd->GetTitlesets()[tsi];
			m_dvd->GetTitlesets().RemoveAt(tsi);
		}
	}
	UpdateItems();
	SendDvdChangedEvent();
}

void TitlesetManager::OnAddTitlesetUpdateUI(wxUpdateUIEvent& event) {
	int cnt = m_dvd->GetTitlesets().Count();
	event.Enable(cnt>0 && m_dvd->GetTitlesets()[cnt-1]->GetTitles().Count());
}

void TitlesetManager::OnMoveTitleLeft(wxCommandEvent& WXUNUSED(event)) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	PgcArray& pgcs = m_dvd->GetPgcArray(tsi, isMenu);
	if (pgci == 0)
		return;
	Pgc* pgc = pgcs[pgci];
	pgcs[pgci] = pgcs[pgci-1];
	pgcs[pgci-1] = pgc;
	if (isMenu)
		SelectMenu(tsi, pgci-1);
	UpdateItems();
	SendDvdChangedEvent();
	m_dvd->UpdateButtonImageFor(tsi, pgci - 1);
	m_dvd->UpdateButtonImageFor(tsi, pgci);
}

void TitlesetManager::OnMoveTitleRight(wxCommandEvent& WXUNUSED(event)) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	PgcArray& pgcs = m_dvd->GetPgcArray(tsi, isMenu);
	if (pgci == (int)pgcs.Count()-1)
		return;
	Pgc* pgc = pgcs[pgci];
	pgcs[pgci] = pgcs[pgci+1];
	pgcs[pgci+1] = pgc;
	if (isMenu)
		SelectMenu(tsi, pgci+1);
	UpdateItems();
	SendDvdChangedEvent();
	m_dvd->UpdateButtonImageFor(tsi, pgci);
	m_dvd->UpdateButtonImageFor(tsi, pgci + 1);
}

void TitlesetManager::OnMoveTitleLeftUpdateUI(wxUpdateUIEvent& event) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int pgci = DVD::GetPgci(id);
	event.Enable(pgci>0);
}

void TitlesetManager::OnMoveTitleRightUpdateUI(wxUpdateUIEvent& event) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	PgcArray& pgcs = m_dvd->GetPgcArray(tsi, isMenu);
	event.Enable(pgci<(int)pgcs.Count()-1);
}

void TitlesetManager::OnDelete(wxCommandEvent& WXUNUSED(event)) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	int vobi = DVD::GetVobi(id);
	if (s_config.GetShowTitleDeletePrompt()) {
		wxString caption = isMenu ? _("Menu") : _T("Title");
		caption += wxString::Format(wxT(" %d"), pgci+1);
		if (vobi>0)
			caption += wxString::Format(_T("-%d"), vobi+1);
		MessageDlg msgDlg(this, wxString::Format(_("Do you really want to delete %s?"), caption.c_str()),
				_("Titleset Manager"), wxYES_NO|wxICON_QUESTION);
		if (msgDlg.ShowModal() != wxYES)
			return;
		s_config.SetShowTitleDeletePrompt(!msgDlg.DontShowAgain());
	}
	if (isMenu) {
		Titleset* ts= NULL;
		if (tsi>=0)
			ts = m_dvd->GetTitlesets()[tsi];
		Menus* menus = &m_dvd->GetVmgm();
		if (ts)
			menus = &ts->GetMenus();
		if (pgci>=(int)menus->GetCount())
			return;
		Pgc* pgc = (*menus)[pgci];
		if (vobi>=(int)pgc->GetVobs().Count())
			return;
		delete pgc->GetVobs()[vobi];
		pgc->GetVobs().RemoveAt(vobi);
		// remove pgc if need
		if (pgc->GetVobs().Count() == 0) {
			delete pgc;
			menus->RemoveAt(pgci);
		}
		// remove titleset if need
		if (ts && ts->GetTitles().Count() == 0
				&& ts->GetMenus().Count() == 0
				&& m_dvd->GetTitlesets().Count() > 1) {
			delete ts;
			m_dvd->GetTitlesets().RemoveAt(tsi);
		}
		// select next menu
		if (GetSelected() == m_selectedMenu) {
			if (pgci >= (int)menus->Count())
				pgci = menus->Count()-1;
			if (pgci>=0)
				SelectMenu(tsi, pgci);
			else
				SelectFirstMenu();
		} else if (m_selectedMenu>=0) {
			wxThumb* mThumb = m_items[m_selectedMenu];
			int mTsi = (mThumb->GetId()>>24)-1;
			int mPgci = ((mThumb->GetId()>>8) & 0xFFFF) / 2;
			if (mTsi == tsi && mPgci > pgci)
				m_selectedMenu--;
		}
	} else { // title
		if (tsi >= (int)m_dvd->GetTitlesets().Count())
			return;
		Titleset* ts = m_dvd->GetTitlesets()[tsi];

		//title
		if (pgci>=(int)ts->GetTitles().GetCount())
			return;
		Pgc* pgc = ts->GetTitles()[pgci];
		if (vobi>=(int)pgc->GetVobs().Count())
			return;
		// remove vob
		delete pgc->GetVobs()[vobi];
		pgc->GetVobs().RemoveAt(vobi);
		// remove title if need
		if (pgc->GetVobs().Count() == 0) {
			delete pgc;
			ts->GetTitles().RemoveAt(pgci);
			for (unsigned int pgci2 = pgci; pgci2 <= ts->GetTitles().GetCount(); pgci2++) {
				m_dvd->UpdateButtonImageFor(tsi, pgci2);
			}
		}
		// remove titleset if need
		if (ts->GetTitles().Count() == 0
				&& ts->GetMenus().Count() == 0
				&& m_dvd->GetTitlesets().Count() > 1) {
			delete ts;
			m_dvd->GetTitlesets().RemoveAt(tsi);
		}
	}
	SetSelected(-1);
	UpdateItems();
	SendDvdChangedEvent();
}

void TitlesetManager::OnProps(wxCommandEvent& WXUNUSED(event)) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	int vobi = DVD::GetVobi(id);
	if (isMenu) { // menu
		MenuPropDlg propDlg(this->GetParent(), m_dvd, tsi, pgci);
		if (propDlg.ShowModal() == wxID_OK) {
			SendMenuSelectedEvent(); // update MenuEditor
			SendDvdChangedEvent();
			UpdateItems();
		}
	} else { //title
		TitlePropDlg propDlg(this->GetParent(), m_dvd, tsi, pgci, vobi);
		if (propDlg.ShowModal() == wxID_OK) {
			m_dvd->UpdateButtonImageFor(tsi, pgci);
			SendMenuSelectedEvent(); // update MenuEditor
			SendDvdChangedEvent();
			UpdateItems();
		}
	}
	m_pointed = -1;
	Refresh();
}

void TitlesetManager::OnCopy(wxCommandEvent& WXUNUSED(event)) {
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	int vobi = DVD::GetVobi(id);
	Vob* vob = m_dvd->GetVob(tsi, DVD::IsMenu(id), pgci, vobi);
	if (!vob || !vob->GetMenu())
		return;
	CopyXmlToClipboard(vob->GetMenu()->GetXML(DVDSTYLER_XML), DATAFORMAT_MENU);
}

void TitlesetManager::OnPaste(wxCommandEvent& WXUNUSED(event)) {
	int tsi = GetSelected() >= 0 ? DVD::GetTsi(GetSelectedId()) : m_dvd->GetTitlesets().Count() - 1;
	int pgci = m_dvd->AddMenu(tsi);
	Menu* menu = m_dvd->GetMenu(tsi, pgci);
	wxSvgXmlDocument doc;
	if (!GetXmlFromClipboard(DATAFORMAT_MENU, doc))
		return;
	menu->PutXML(doc.GetRoot());
	UpdateItems();
	SelectMenu(tsi, pgci);
	SendDvdChangedEvent();
}

void TitlesetManager::OnUpdateUIObjectPaste(wxUpdateUIEvent& event) {
	event.Enable(wxTheClipboard->IsSupported(wxDataFormat(DATAFORMAT_MENU)));
}

