/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Jean Le Feuvre
 *			Copyright (c) Telecom ParisTech 2000-2012
 *					All rights reserved
 *
 *  This file is part of GPAC / Osmo4 wxWidgets GUI
 *
 *  GPAC is gf_free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 */


#include "wxOsmo4.h"
#include "wxGPACControl.h"
#include "fileprops.h"
#include <wx/image.h>
#include <gpac/modules/service.h>
#include <gpac/network.h>
#include <gpac/constants.h>
#include <gpac/options.h>


IMPLEMENT_APP(wxOsmo4App)

#include "osmo4.xpm"

#include <wx/dnd.h>





#include <wx/filename.h>
#include <wx/clipbrd.h>

#include "toolbar.xpm"

#include "Playlist.h"

#ifdef WIN32
#define	FRAME_H	140
#else
#define	FRAME_H	110
#endif


wxString get_pref_browser(GF_Config *cfg)
{
	const char *sOpt = gf_cfg_get_key(cfg, "General", "Browser");
	if (sOpt) return wxString(sOpt, wxConvUTF8);
	return wxEmptyString;
	/*
	#ifdef __WXMAC__
		return wxT("safari");
	#else
	#ifdef WIN32
		return wxT("explorer.exe");
	#else
		return wxT("mozilla");
	#endif
	#endif*/
}


IMPLEMENT_DYNAMIC_CLASS(wxGPACEvent, wxEvent )

wxGPACEvent::wxGPACEvent(wxWindow* win)
{
	SetEventType(GPAC_EVENT);
	SetEventObject(win);
	gpac_evt.type = 0;
	to_url = wxT("");
}
wxEvent *wxGPACEvent::Clone() const
{
	wxGPACEvent *evt = new wxGPACEvent((wxWindow *) m_eventObject);
	evt->to_url = to_url;
	evt->gpac_evt = gpac_evt;
	return evt;
}

#include <wx/wx.h>

/*open file dlg*/
BEGIN_EVENT_TABLE(OpenURLDlg, wxDialog)
	EVT_BUTTON(ID_URL_GO, OpenURLDlg::OnGo)
END_EVENT_TABLE()

OpenURLDlg::OpenURLDlg(wxWindow *parent, GF_Config *cfg)
	: wxDialog(parent, -1, wxString(wxT("Enter remote presentation location")))
{
#ifndef WIN32
	SetSize(430, 35);
#else
	SetSize(430, 55);
#endif
	Centre();
	m_url = new wxComboBox(this, -1, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_DROPDOWN);
	m_url->SetSize(0, 2, 340, 18, wxSIZE_AUTO);
	m_go = new wxButton(this, ID_URL_GO, wxT("Go !"));
#ifndef WIN32
	m_go->SetSize(344, 2, 20, 18, wxSIZE_AUTO);
#else
	m_go->SetSize(364, 2, 30, 18, wxSIZE_AUTO);
#endif
	m_urlVal = wxT("");

	m_cfg = cfg;

	const char *sOpt;
	u32 i=0;

	while (1) {
		sOpt = gf_cfg_get_key_name(m_cfg, "RecentFiles", i);
		if (!sOpt) break;
		m_url->Append(wxString(sOpt, wxConvUTF8) );
		i++;
	}
}

#define MAX_LAST_FILES		20
void UpdateLastFiles(GF_Config *cfg, const char *URL)
{
	u32 nb_entries;
	gf_cfg_set_key(cfg, "RecentFiles", URL, NULL);
	gf_cfg_insert_key(cfg, "RecentFiles", URL, "", 0);
	/*remove last entry if needed*/
	nb_entries = gf_cfg_get_key_count(cfg, "RecentFiles");
	if (nb_entries>MAX_LAST_FILES) {
		gf_cfg_set_key(cfg, "RecentFiles", gf_cfg_get_key_name(cfg, "RecentFiles", nb_entries-1), NULL);
	}
}

void OpenURLDlg::OnGo(wxCommandEvent& event)
{
	m_urlVal = m_url->GetValue();
	UpdateLastFiles(m_cfg, m_urlVal.mb_str(wxConvUTF8));
	EndModal(wxID_OK);
}
/*end open file dlg*/

#ifdef WIN32
u32 get_sys_col(int idx)
{
	u32 res;
	DWORD val = GetSysColor(idx);
	res = (val)&0xFF;
	res<<=8;
	res |= (val>>8)&0xFF;
	res<<=8;
	res |= (val>>16)&0xFF;
	return res;
}
#endif

static void wxOsmo4_progress_cbk(const void *usr, const char *title, u64 done, u64 total)
{
	if (!total) return;
	wxOsmo4Frame *app = (wxOsmo4Frame *)usr;
	s32 prog = (s32) ( (100 * (u64)done) / total);
	if (app->m_last_prog < prog) {
		app->m_last_prog = prog;

		if (prog<100) {
			/*appears to crash wxWidgets / X11 when refreshing the text too often*/
			if (app->m_LastStatusTime + 200 > gf_sys_clock()) return;
			char msg[1024];
			sprintf(msg, "%s %02d %%)", title, prog);
			//app->SetStatus(wxString(msg, wxConvUTF8));
		} else {
			app->SetStatus(wxT("Ready"));
			app->m_last_prog = -1;
		}
	}
}

Bool GPAC_EventProc(void *ptr, GF_Event *evt)
{
	wxCommandEvent event;
	wxOsmo4Frame *app = (wxOsmo4Frame *)ptr;

	switch (evt->type) {
	case GF_EVENT_DURATION:
		app->m_duration = (u32) (evt->duration.duration*1000);
		app->m_can_seek = evt->duration.can_seek;
		if (app->m_duration<1100) app->m_can_seek = 0;
		app->m_pProg->Enable(app->m_can_seek ? 1 : 0);
		app->m_pPlayList->SetDuration((u32) evt->duration.duration);
		break;
	case GF_EVENT_MESSAGE:
	{
		const char *servName;
		if (!evt->message.service || !strcmp(evt->message.service, app->m_pPlayList->GetURL().mb_str(wxConvUTF8))) {
			servName = "main service";
		} else {
			servName = evt->message.service;
		}
		if (!evt->message.message) return 0;

		if (evt->message.error) {
			app->SetStatus(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(")") );
			if (!app->m_connected) app->m_pPlayList->SetDead();
		}
		else if (!app->m_console_off) {
			if (strstr(evt->message.message, "100 %")) {
				app->SetStatus(wxT(""));
			} else {
				app->SetStatus(wxString(evt->message.message, wxConvUTF8) );
			}
		}

#if 0
		/*log*/
		if (evt->message.error)
			::wxLogMessage(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(") ") + wxString(gf_error_to_string(evt->message.error), wxConvUTF8) );
		else
			::wxLogMessage(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(")"));
#endif
	}
	break;
	case GF_EVENT_PROGRESS:
	{
		const char *sTitle;
		if (evt->progress.progress_type==0) sTitle = (char *)"Buffer";
		else if (evt->progress.progress_type==1) sTitle = (char *)"Download";
		else if (evt->progress.progress_type==2) sTitle = (char *)"Import";
		gf_set_progress(sTitle, evt->progress.done, evt->progress.total);
	}
	break;
	case GF_EVENT_KEYDOWN:
		if (app->m_can_seek && (evt->key.flags & GF_KEY_MOD_ALT)) {
			s32 res;
			switch (evt->key.key_code) {
			case GF_KEY_LEFT:
				res = gf_term_get_time_in_ms(app->m_term) - 5*app->m_duration/100;
				if (res<0) res=0;
				gf_term_play_from_time(app->m_term, res, 0);
				break;
			case GF_KEY_RIGHT:
				res = gf_term_get_time_in_ms(app->m_term) + 5*app->m_duration/100;
				if ((u32) res>=app->m_duration) res = 0;
				gf_term_play_from_time(app->m_term, res, 0);
				break;
			case GF_KEY_DOWN:
				res = gf_term_get_time_in_ms(app->m_term) - 60000;
				if (res<0) res=0;
				gf_term_play_from_time(app->m_term, res, 0);
				break;
			case GF_KEY_UP:
				res = gf_term_get_time_in_ms(app->m_term) + 60000;
				if ((u32) res>=app->m_duration) res = 0;
				gf_term_play_from_time(app->m_term, res, 0);
				break;
			}
		} else if (evt->key.flags & GF_KEY_MOD_CTRL) {
			switch (evt->key.key_code) {
			case GF_KEY_LEFT:
				app->m_pPlayList->PlayPrev();
				break;
			case GF_KEY_RIGHT:
				app->m_pPlayList->PlayNext();
				break;
			}
		} else {
			switch (evt->key.key_code) {
			case GF_KEY_HOME:
				gf_term_set_option(app->m_term, GF_OPT_NAVIGATION_TYPE, 1);
				break;
			case GF_KEY_ESCAPE:
				if (gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN))
					gf_term_set_option(app->m_term, GF_OPT_FULLSCREEN, 0);
				break;
			default:
			{
				wxGPACEvent wxevt(app);
				wxevt.gpac_evt = *evt;
				app->AddPendingEvent(wxevt);
			}
			break;
			}
		}
		break;

	case GF_EVENT_CONNECT:
	{
		wxGPACEvent wxevt(app);
		wxevt.gpac_evt.type = GF_EVENT_CONNECT;
		wxevt.gpac_evt.connect.is_connected = evt->connect.is_connected;
		if (!evt->connect.is_connected) app->m_duration = 0;
		app->AddPendingEvent(wxevt);
	}
	break;
	case GF_EVENT_NAVIGATE:
	{
		wxGPACEvent wxevt(app);
		wxevt.to_url = wxString(evt->navigate.to_url, wxConvUTF8);
		wxevt.gpac_evt.type = evt->type;
		app->AddPendingEvent(wxevt);
	}
	return 1;
	case GF_EVENT_SET_CAPTION:
	{
		wxGPACEvent wxevt(app);
		wxevt.to_url = wxString(evt->caption.caption, wxConvUTF8);
		wxevt.gpac_evt.type = evt->type;
		app->AddPendingEvent(wxevt);
	}
	return 1;

	case GF_EVENT_QUIT:
	case GF_EVENT_VIEWPOINTS:
	case GF_EVENT_STREAMLIST:
	case GF_EVENT_SCENE_SIZE:
//	case GF_EVENT_SIZE:
	{
		wxGPACEvent wxevt(app);
		wxevt.gpac_evt = *evt;
		app->AddPendingEvent(wxevt);
	}
	break;
	case GF_EVENT_DBLCLICK:
		gf_term_set_option(app->m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN));
		return 0;
	case GF_EVENT_MOUSEDOWN:
		if (!gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN)) {
#ifdef __WXGTK__
			app->m_pVisual->SetFocus();
#else
			app->m_pView->SetFocus();
#endif
		}
		break;
	case GF_EVENT_AUTHORIZATION:
	{
		wxGPACEvent wxevt(app);
		wxTextEntryDialog user_d (0,
		                          wxT("Please set the user name for connection"),
		                          wxString(evt->auth.site_url, wxConvUTF8),
		                          wxString(evt->auth.user, wxConvUTF8));
		if (user_d.ShowModal() != wxID_OK)
			return 0;
		strncpy(evt->auth.user, user_d.GetValue().mb_str(wxConvUTF8), 50);
		wxPasswordEntryDialog passwd_d(0,
		                               wxT("Please enter password"),
		                               wxString(evt->auth.site_url, wxConvUTF8),
		                               wxString(evt->auth.password, wxConvUTF8));
		if (passwd_d.ShowModal() != wxID_OK)
			return 0;
		strncpy(evt->auth.password, passwd_d.GetValue().mb_str(wxConvUTF8), 50);
		return 1;
	}
	case GF_EVENT_SYS_COLORS:
#ifdef WIN32
		evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER);
		evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION);
		evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE);
		evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND);
		evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE);
		evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT);
		evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW);
		evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT);
		evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT);
		evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT);
		evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT);
		evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT);
		evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER);
		evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION);
		evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT);
		evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK);
		evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT);
		evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU);
		evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT);
		evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR);
		evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW);
		evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE);
		evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT);
		evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT);
		evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW);
		evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW);
		evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME);
		evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT);
		return 1;
#else
		memset(evt->sys_cols.sys_colors, 0, sizeof(u32)*28);
		return 1;
#endif
	}
	return 0;
}



bool wxOsmo4App::OnInit()
{
#ifdef __WXGTK__
	XSynchronize((Display *) wxGetDisplay(), 1);
#endif
	wxFrame *frame = new wxOsmo4Frame();
	frame->Show(TRUE);
	SetTopWindow(frame);
	return true;
}


class myDropfiles : public wxFileDropTarget
{
public:
	myDropfiles() : wxFileDropTarget() {}
	virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames);
	wxOsmo4Frame *m_pMain;
};

bool myDropfiles::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
{
	u32 count = filenames.GetCount();

	if (count==1) {
		const char *ext = strrchr(filenames.Item(0).mb_str(wxConvUTF8) , '.');
		/*if playing and sub d&d, open sub in current presentation*/
		if (m_pMain->m_connected && ext && ( !stricmp(ext, ".srt") || !stricmp(ext, ".sub") || !stricmp(ext, ".ttxt") || !stricmp(ext, ".xml") ) ) {
			m_pMain->AddSubtitle(filenames.Item(0).mb_str(wxConvUTF8) , 1);
			return TRUE;
		}
	}

	for (u32 i=0; i<count; i++)
		m_pMain->m_pPlayList->QueueURL(filenames.Item(i));

	m_pMain->m_pPlayList->RefreshList();
	m_pMain->m_pPlayList->PlayNext();
	return TRUE;
}

bool GPACLogs::OnFrameClose(wxFrame *frame)
{
	Show(FALSE);
	return 0;
}

void wxOsmo4Frame::ShowViewWindow(Bool do_show)
{
	m_pView->Show(do_show ? 1 : 0);
#ifdef __WXGTK__
	//m_pView->Show(0);
#endif
}

#ifdef __WXGTK__
extern "C" {
#ifdef __WXGTK20__
	int gdk_x11_drawable_get_xid( void * );
	void *gdk_x11_drawable_get_xdisplay( void * );
#endif
	void *gtk_widget_get_parent_window( void * );
}
#endif


void wxOsmo4Frame::CheckVideoOut()
{
	const char *sOpt = gf_cfg_get_key(m_user.config, "Video", "DriverName");
	void *os_handle = NULL;
	void *os_display = NULL;
	/*build a child window for embed display*/
	if (sOpt && stricmp(sOpt, "SDL Video Output")) {
		if (m_user.os_window_handler) return;
		m_bExternalView = 0;

#ifdef __WXGTK__
		GtkWidget* widget = m_pVisual->GetHandle();

#ifdef __WXGTK20__
		os_handle = (void *) gdk_x11_drawable_get_xid(gtk_widget_get_parent_window(widget));
#else
		os_handle = (void *)*(int *)( (char *)gtk_widget_get_parent_window(widget) + 2 * sizeof(void *) );
#endif

#elif defined (WIN32)
		os_handle = m_pView->GetHandle();
#endif
		if (os_handle) {
			m_user.os_window_handler = os_handle;
			m_user.os_display = os_display;
			ShowViewWindow(1);
			m_pView->SetSize(320, 240);
			SetSize(wxSize(320, 240+FRAME_H));
			SetWindowStyle(wxDEFAULT_FRAME_STYLE);
			DoLayout(320, 240);
			return;
		}
	}
	/*we're using SDL, don't use SDL hack*/
	m_bExternalView = 1;
	m_user.os_window_handler = 0;
	m_user.os_display = NULL;
	SetSize(wxSize(320,FRAME_H));
	m_pView->SetSize(0, 0);
	ShowViewWindow(0);
	DoLayout();
	SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER));
}

static void wxOsmo4_do_log(void *cbk, u32 level, u32 tool, const char *fmt, va_list list)
{
	wxOsmo4Frame *osmo = (wxOsmo4Frame *)cbk;

	if (osmo->m_logs) {
		vfprintf(osmo->m_logs, fmt, list);
		fflush(osmo->m_logs);
	} else {
		::wxVLogMessage(wxString(fmt, wxConvUTF8), list);
	}
}


Bool wxOsmo4Frame::LoadTerminal()
{
	m_term = NULL;
	memset(&m_user, 0, sizeof(GF_User));

	/*locate exec dir for cfg file*/
	wxPathList pathList;
	wxString currentDir(wxGetCwd());
	wxString abs_gpac_path = wxT("");
	char *gpac_cfg, *sep;

	::wxLogMessage(wxT("Looking for GPAC configuration file"));

	/*load config*/
	Bool first_launch = 0;
	m_user.config = gf_cfg_init(NULL, &first_launch);

	if (!m_user.config) {
		wxMessageDialog(NULL, wxT("Cannot open GPAC configuration file"), wxT("Init error"), wxOK);
		return 0;
	}

	gpac_cfg = gf_cfg_get_filename(m_user.config);
	sep = strrchr(gpac_cfg, '/');
	if (!sep) sep = strrchr(gpac_cfg, '\\');
	if (sep) sep[0] = 0;
	strcpy(szAppPath, gpac_cfg);
	if (sep) sep[0] = '/';
	gf_free(gpac_cfg);

	/*check log file*/
	const char *str = gf_cfg_get_key(m_user.config, "General", "LogFile");
	if (str) m_logs = gf_fopen(str, "wt");
	gf_log_set_callback(this, wxOsmo4_do_log);

	/*set log level*/
	gf_log_set_tools_levels( gf_cfg_get_key(m_user.config, "General", "Logs") );

	gf_sys_init(0);

	::wxLogMessage(wxT("GPAC configuration file opened - looking for modules"));

	m_user.modules = gf_modules_new(str, m_user.config);
	/*initial launch*/
	if (!m_user.modules || !gf_modules_get_count(m_user.modules)) {
		wxMessageDialog(NULL, wxT("No modules available - system cannot work"), wxT("Fatal Error"), wxOK).ShowModal();
		if (m_user.modules) gf_modules_del(m_user.modules);
		gf_cfg_del(m_user.config);
		m_user.config = NULL;
		return 0;
	}

	if (first_launch) {
		u32 i;
		for (i=0; i<gf_modules_get_count(m_user.modules); i++) {
			GF_InputService *ifce = (GF_InputService *) gf_modules_load_interface(m_user.modules, i, GF_NET_CLIENT_INTERFACE);
			if (!ifce) continue;
			if (ifce) {
				ifce->CanHandleURL(ifce, "test.test");
				gf_modules_close_interface((GF_BaseInterface *) ifce);
			}
		}
	}



	::wxLogMessage(wxT("%d modules found:"), gf_modules_get_count(m_user.modules));
	for (u32 i=0; i<gf_modules_get_count(m_user.modules); i++) {
		::wxLogMessage(wxT("\t") + wxString(gf_modules_get_file_name(m_user.modules, i), wxConvUTF8) );
	}

	::wxLogMessage(wxT("Starting GPAC Terminal"));
	/*now load terminal*/
	m_user.opaque = this;
	m_user.EventProc = GPAC_EventProc;

	CheckVideoOut();

	m_term = gf_term_new(&m_user);
	if (!m_term) {
		wxMessageDialog(NULL, wxT("Fatal Error"), wxT("Cannot load GPAC Terminal"), wxOK).ShowModal();
		return 0;
	} else {
		::wxLogMessage(wxT("GPAC Terminal started") );
	}
	return 1;
}



wxOsmo4Frame::wxOsmo4Frame() :
	wxFrame((wxFrame *) NULL, -1, wxT("Osmo4 - GPAC"), wxPoint(-1, -1), wxSize(320, FRAME_H), //wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER)
	        wxDEFAULT_FRAME_STYLE
	       )

{
	int ws[3];
	m_Address = NULL;
	m_pView = NULL;
	m_term = NULL;
	SetIcon(wxIcon(osmo4));
	m_bExternalView = 0;
	m_last_prog = -1;
	m_num_chapters = 0;
	m_chapters_start = NULL;
	m_bViewRTI = 0;
	m_logs = NULL;
	m_bStartupFile = 0;
	gf_set_progress_callback(this, wxOsmo4_progress_cbk);

	myDropfiles *droptarget = new myDropfiles();
	droptarget->m_pMain = this;
	SetDropTarget(droptarget);
	m_pLogs = new GPACLogs(this);
	m_bGrabbed = 0;

	/*new menu bar*/
	wxMenuBar *b = new wxMenuBar();
	/*file*/
	wxMenu *menu = new wxMenu();
	menu->Append(GWX_FILE_OPEN, wxT("&Open File\tCtrl+O"), wxT("Open local presentation"));
	menu->Append(GWX_FILE_OPEN_URL, wxT("&Open URL\tCtrl+U"), wxT("Open remote presentation"));
	menu->AppendSeparator();
	menu->Append(FILE_PROPERTIES, wxT("&Properties\tCtrl+I"), wxT("Show presentation properties"));
	menu->Enable(FILE_PROPERTIES, 0);
	wxMenu *smenu = new wxMenu();
	smenu->Append(ID_MCACHE_ENABLE, wxT("&Enable"), wxT("Turns Recorder On/Off"));
	smenu->Append(ID_MCACHE_STOP, wxT("&Stop"), wxT("Stops recording and saves"));
	smenu->Append(ID_MCACHE_ABORT, wxT("&Abort"), wxT("Stops recording and discards"));
	menu->Append(0, wxT("&Streaming Cache"), smenu);
	menu->AppendSeparator();
	menu->Append(FILE_COPY, wxT("&Copy\tCtrl+C"), wxT("Copy selected text"));
	menu->Append(FILE_PASTE, wxT("&Paste\tCtrl+V"), wxT("Copy selected text"));
	menu->AppendSeparator();
	menu->Append(FILE_QUIT, wxT("E&xit"), wxT("Quit the application"));
	b->Append(menu, wxT("&File"));
	/*view*/
	menu = new wxMenu();
	vp_list = new wxMenu();
	menu->Append(0, wxT("&Viewpoint"), vp_list);
	smenu = new wxMenu();
	smenu->Append(ID_HEADLIGHT, wxT("Headlight"), wxT("Turns headlight on/off"), wxITEM_CHECK);
	smenu->AppendSeparator();
	smenu->Append(ID_NAVIGATE_NONE, wxT("None"), wxT("Disables Navigation"), wxITEM_CHECK);
	smenu->Append(ID_NAVIGATE_WALK, wxT("Walk"), wxT("Walk Navigation"), wxITEM_CHECK);
	smenu->Append(ID_NAVIGATE_FLY, wxT("Fly"), wxT("Fly Navigation"), wxITEM_CHECK);
	smenu->Append(ID_NAVIGATE_EXAMINE, wxT("Examine"), wxT("Examine Navigation"), wxITEM_CHECK);
	smenu->Append(ID_NAVIGATE_PAN, wxT("Pan"), wxT("Pan Navigation"), wxITEM_CHECK);
	smenu->Append(ID_NAVIGATE_SLIDE, wxT("Slide"), wxT("Slide Navigation"), wxITEM_CHECK);
	smenu->Append(ID_NAVIGATE_ORBIT, wxT("Orbit"), wxT("Orbit Navigation"), wxITEM_CHECK);
	smenu->Append(ID_NAVIGATE_GAME, wxT("Game"), wxT("Game Navigation"), wxITEM_CHECK);
	smenu->AppendSeparator();
	wxMenu *ssmenu = new wxMenu();
	ssmenu->Append(ID_COLLIDE_NONE, wxT("None"), wxT("No Collision detection"), wxITEM_CHECK);
	ssmenu->Append(ID_COLLIDE_REG, wxT("Regular"), wxT("Regular Collision detection"), wxITEM_CHECK);
	ssmenu->Append(ID_COLLIDE_DISP, wxT("Displacement"), wxT("Collision detecion with camera displacement"), wxITEM_CHECK);
	smenu->Append(0, wxT("&Collision"), ssmenu);
	smenu->Append(ID_GRAVITY, wxT("Gravity"), wxT("Turns gravity on/off"), wxITEM_CHECK);
	smenu->AppendSeparator();
	smenu->Append(ID_NAVIGATE_RESET, wxT("Reset"), wxT("Reset Navigation"));

	menu->Append(0, wxT("&Navigation"), smenu);
	menu->AppendSeparator();
	menu->Append(VIEW_FULLSCREEN, wxT("&Fullscreen"), wxT("Toggles Fullscreen"), wxITEM_CHECK);
	menu->Append(VIEW_ORIGINAL, wxT("&Original Size"), wxT("Restore original size"));
	smenu = new wxMenu();
	smenu->Append(VIEW_AR_KEEP, wxT("Keep Original\tCtrl+1"), wxT("Keep original aspect ratio"), wxITEM_CHECK);
	smenu->Append(VIEW_AR_FILL, wxT("Fill Screen\tCtrl+2"), wxT("Stretch presentation to fill screen"), wxITEM_CHECK);
	smenu->Append(VIEW_AR_43, wxT("Ratio 4/3\tCtrl+3"), wxT("Force aspect ratio to 4/3"), wxITEM_CHECK);
	smenu->Append(VIEW_AR_169, wxT("Ratio 16/9\tCtrl+4"), wxT("Force aspect ratio to 16/9"), wxITEM_CHECK);
	menu->Append(0, wxT("&Aspect Ratio"), smenu);
	smenu->Check(VIEW_AR_KEEP, 1);
	menu->AppendSeparator();
	menu->Append(VIEW_OPTIONS, wxT("&Options"), wxT("View Options"));
	menu->AppendSeparator();
	menu->Append(VIEW_RTI, wxT("&Resource Usage"), wxT("View Resource Usage"), wxITEM_CHECK);
	menu->Append(VIEW_LOGS, wxT("&Logs"), wxT("View GPAC logs"));
	b->Append(menu, wxT("&View"));

	/*play*/
	menu = new wxMenu();
	sel_menu = new wxMenu();
	sel_menu->Append(0, wxT("&Audio"), new wxMenu());
	sel_menu->Append(0, wxT("&Video"), new wxMenu());
	sel_menu->Append(0, wxT("&Subtitles"), new wxMenu());
	sel_menu->AppendSeparator();
	sel_menu->Append(ID_ADD_SUB, wxT("&Add Subtitle"), wxT("Adds subtitle"));
	menu->Append(ID_STREAM_MENU, wxT("&Streams Selection"), sel_menu);
	chap_menu = new wxMenu();
	menu->Append(ID_CHAPTER_MENU, wxT("&Chapters"), chap_menu);

	menu->AppendSeparator();
	menu->Append(VIEW_PLAYLIST, wxT("&Playlist\tCtrl+L"), wxT("Show navigation history as playlist"), wxITEM_CHECK);
	menu->Append(ID_CLEAR_NAV, wxT("&Clear History"), wxT("Clear navigation history"));
	menu->AppendSeparator();
	menu->Append(FILE_PLAY, wxT("&Play/Pause\tCtrl+P"), wxT("Play/Pause/Resume Presentation"));
	menu->Append(FILE_STEP, wxT("&Step-by-Step\tCtrl+S"), wxT("Play/Pause/Resume Presentation"));
	menu->Append(FILE_STOP, wxT("&Stop"), wxT("Stop Presentation"));
	menu->AppendSeparator();
	menu->Append(FILE_RELOAD_CONFIG, wxT("&Reload Config\tCtrl+R"), wxT("Reload Configuration File"));
	menu->Append(FILE_RELOAD, wxT("&Reload File\tCtrl+R"), wxT("Reload Presentation"));
	b->Append(menu, wxT("&Play"));

	menu = new wxMenu();
	menu->Append(APP_SHORTCUTS, wxT("&Shortcuts"), wxT("Show keyboard shortcuts"));
	menu->Append(APP_NAV_KEYS, wxT("&Navigation Keys"), wxT("Show navigation keys"));
	menu->AppendSeparator();
	menu->Append(APP_ABOUT, wxT("&About"), wxT("Display information and copyright"));
	b->Append(menu, wxT("&?"));

	SetMenuBar(b);

	m_pStatusbar = CreateStatusBar(1, 0, -1, wxT("statusBar"));
	ws[0] = 60;
	ws[1] = 70;
	ws[2] = -1;
	m_pStatusbar->SetFieldsCount(3, ws);

	SetStatusBarPane(2);
	wxColour foreCol = m_pStatusbar->GetBackgroundColour();
	SetBackgroundColour(foreCol);


	m_pTimer = new wxTimer();
	m_pTimer->SetOwner(this, ID_CTRL_TIMER);
	m_bGrabbed = 0;

	/*create toolbar*/
	m_pToolBar = CreateToolBar(wxTB_FLAT|wxTB_HORIZONTAL);
	m_pOpenFile = new wxBitmap(tool_open_file);
	m_pPrev = new wxBitmap(tool_prev);
	m_pNext = new wxBitmap(tool_next);
	m_pPlay = new wxBitmap(tool_play);
	m_pPause = new wxBitmap(tool_pause);
	m_pStep = new wxBitmap(tool_step);
	m_pStop = new wxBitmap(tool_stop);
	m_pInfo = new wxBitmap(tool_info);
	m_pConfig = new wxBitmap(tool_config);
	m_pSW2D = new wxBitmap(tool_sw_2d);
	m_pSW3D = new wxBitmap(tool_sw_3d);

	m_pToolBar->AddTool(GWX_FILE_OPEN, wxT(""), *m_pOpenFile, wxT("Open File"));
	m_pToolBar->AddSeparator();
	m_pPrevBut = new wxMenuButton(m_pToolBar, FILE_PREV, *m_pPrev);
	m_pPrevBut->SetToolTip(wxT("Previous Location"));
	m_pToolBar->AddControl(m_pPrevBut);
	m_pNextBut = new wxMenuButton(m_pToolBar, FILE_NEXT, *m_pNext);
	m_pNextBut->SetToolTip(wxT("Next Location"));
	m_pToolBar->AddControl(m_pNextBut);

	m_pToolBar->AddSeparator();
	m_pToolBar->AddTool(FILE_PLAY, wxT(""), *m_pPlay, wxT("Play/Pause File"));
	m_pToolBar->AddTool(FILE_STEP, wxT(""), *m_pStep, wxT("Step-by-Step Mode"));
	m_pToolBar->AddTool(FILE_STOP, wxT(""), *m_pStop, wxT("Stop File"));
	m_pToolBar->AddSeparator();
	m_pToolBar->AddTool(FILE_PROPERTIES, wxT(""), *m_pInfo, wxT("Show File Information"));
	m_pToolBar->AddSeparator();
	m_pToolBar->AddTool(VIEW_OPTIONS, wxT(""), *m_pConfig, wxT("GPAC Configuration"));
	m_pToolBar->AddTool(SWITCH_RENDER, wxT(""), *m_pSW3D, wxT("Switch 2D/3D Renderers"));

	m_pToolBar->Realize();

	m_Address = new wxMyComboBox(this, ID_ADDRESS, wxT(""), wxPoint(50, 0), wxSize(80, 20));
	wxStaticText *add_text = new wxStaticText(this, -1, wxT("URL"), wxPoint(0, 0), wxSize(40, 20));
	add_text->SetBackgroundColour(foreCol);

	m_pAddBar = new wxBoxSizer(wxHORIZONTAL);
	m_pAddBar->Add(add_text, 0, wxALIGN_TOP);
	m_pAddBar->Add(m_Address, 2, wxALIGN_CENTER|wxEXPAND|wxADJUST_MINSIZE);
	m_pAddBar->SetMinSize(80, 32);
	m_pAddBar->Layout();

	m_pProg = new wxSlider(this, ID_SLIDER, 0, 0, 1000, wxPoint(0, 22), wxSize(80, 22), wxSL_HORIZONTAL|wxSUNKEN_BORDER);
	m_pProg->Enable(0);
	m_pProg->Show();
	m_pProg->SetBackgroundColour(foreCol);

	m_pView = new wxWindow(this, -1, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
	m_pView->SetBackgroundColour(wxColour(wxT("BLACK")));
#ifdef __WXGTK__
	m_pVisual = new wxWindow(m_pView, -1, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
	m_pVisual->SetBackgroundColour(wxColour(wxT("BLACK")));
#endif

	m_pPlayList = new wxPlaylist(this);
	m_pPlayList->SetIcon(wxIcon(osmo4));
	m_pPlayList->Hide();
	Raise();
	Show();

	m_connected = 0;
	if (!LoadTerminal()) {
		Close(TRUE);
		return;
	}



	if (m_bExternalView) SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER));
	DoLayout(320, 240);
	UpdateRenderSwitch();

	const char *sOpt = gf_cfg_get_key(m_user.config, "General", "ConsoleOff");
	m_console_off = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0;
	sOpt = gf_cfg_get_key(m_user.config, "General", "Loop");
	m_loop = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0;
	sOpt = gf_cfg_get_key(m_user.config, "General", "LookForSubtitles");
	m_lookforsubs = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0;
	gf_term_set_option(m_term, GF_OPT_AUDIO_VOLUME, 100);

	ReloadURLs();
	Raise();
	m_pStatusbar->SetStatusText(wxT("Ready"), 2);
	m_LastStatusTime = 0;

	m_pPrevBut->Refresh();
	m_pNextBut->Refresh();

	wxOsmo4App &app = wxGetApp();
	if (app.argc>1) {
		m_pPlayList->QueueURL(wxString(app.argv[1]));
		m_pPlayList->RefreshList();
		m_pPlayList->PlayNext();
	} else {
		char sPL[GF_MAX_PATH];
		strcpy((char *) sPL, szAppPath);
#ifdef WIN32
		strcat(sPL, "gpac_pl.m3u");
#else
		strcat(sPL, ".gpac_pl.m3u");
#endif
		m_pPlayList->OpenPlaylist(wxString(sPL, wxConvUTF8) );
		const char *sOpt = gf_cfg_get_key(m_user.config, "General", "PLEntry");
		if (sOpt) {
			m_pPlayList->m_cur_entry = atoi(sOpt);
			if (m_pPlayList->m_cur_entry>=(s32)gf_list_count(m_pPlayList->m_entries))
				m_pPlayList->m_cur_entry = -1;
		}

		sOpt = gf_cfg_get_key(m_user.config, "General", "StartupFile");
		if (sOpt) {
			gf_term_connect(m_term, sOpt);
			m_bStartupFile = 1;
		}
	}

	sOpt = gf_cfg_get_key(m_user.config, "Audio", "DriverName");

	if (!strcmp(sOpt, "No Audio Output Available")) {
		::wxLogMessage(wxT("WARNING: no audio output availble - make sure no other program is locking the sound card"));
		SetStatus(wxT("No audio ouput available"));

	} else {
		SetStatus(wxT("Ready"));
	}
}

wxOsmo4Frame::~wxOsmo4Frame()
{
	vp_list = NULL;
	sel_menu = NULL;

	if (m_user.modules) gf_modules_del(m_user.modules);
	gf_sys_close();
	if (m_user.config) gf_cfg_del(m_user.config);

	if (m_chapters_start) gf_free(m_chapters_start);
	if (m_pView) delete m_pView;

	//m_pToolBar->RemoveTool(FILE_PREV);
	//m_pToolBar->RemoveTool(FILE_NEXT);

	delete m_pPrevBut;
	delete m_pNextBut;
	delete m_pPlayList;
	delete m_pTimer;
	delete m_pOpenFile;
	delete m_pPrev;
	delete m_pNext;
	delete m_pPlay;
	delete m_pPause;
	delete m_pStep;
	delete m_pStop;
	delete m_pInfo;
	delete m_pConfig;
	delete m_pSW2D;
	delete m_pSW3D;
}


BEGIN_EVENT_TABLE(wxOsmo4Frame, wxFrame)
	EVT_CLOSE(wxOsmo4Frame::OnCloseApp)
	EVT_MENU(GWX_FILE_OPEN, wxOsmo4Frame::OnFileOpen)
	EVT_MENU(GWX_FILE_OPEN_URL, wxOsmo4Frame::OnFileOpenURL)
	EVT_MENU(FILE_RELOAD_CONFIG, wxOsmo4Frame::OnFileReloadConfig)
	EVT_MENU(FILE_RELOAD, wxOsmo4Frame::OnFileReload)
	EVT_MENU(FILE_PROPERTIES, wxOsmo4Frame::OnFileProperties)
	EVT_MENU(FILE_QUIT, wxOsmo4Frame::OnFileQuit)
	EVT_MENU(VIEW_FULLSCREEN, wxOsmo4Frame::OnFullScreen)
	EVT_MENU(VIEW_OPTIONS, wxOsmo4Frame::OnOptions)
	EVT_MENU(VIEW_AR_KEEP, wxOsmo4Frame::OnViewARKeep)
	EVT_MENU(VIEW_AR_FILL, wxOsmo4Frame::OnViewARFill)
	EVT_MENU(VIEW_AR_169, wxOsmo4Frame::OnViewAR169)
	EVT_MENU(VIEW_AR_43, wxOsmo4Frame::OnViewAR43)
	EVT_MENU(VIEW_ORIGINAL, wxOsmo4Frame::OnViewOriginal)
	EVT_MENU(VIEW_PLAYLIST, wxOsmo4Frame::OnPlaylist)
	EVT_UPDATE_UI(VIEW_PLAYLIST, wxOsmo4Frame::OnUpdatePlayList)
	EVT_MENU(FILE_COPY, wxOsmo4Frame::OnFileCopy)
	EVT_UPDATE_UI(FILE_COPY, wxOsmo4Frame::OnUpdateFileCopy)
	EVT_MENU(FILE_PASTE, wxOsmo4Frame::OnFilePaste)
	EVT_UPDATE_UI(FILE_PASTE, wxOsmo4Frame::OnUpdateFilePaste)

	EVT_MENU(ID_CLEAR_NAV, wxOsmo4Frame::OnClearNav)
	EVT_UPDATE_UI(ID_STREAM_MENU, wxOsmo4Frame::OnUpdateStreamMenu)
	EVT_UPDATE_UI(ID_CHAPTER_MENU, wxOsmo4Frame::OnUpdateChapterMenu)
	EVT_MENU(ID_ADD_SUB, wxOsmo4Frame::OnAddSub)

	EVT_MENU(ID_MCACHE_ENABLE, wxOsmo4Frame::OnCacheEnable)
	EVT_UPDATE_UI(ID_MCACHE_ENABLE, wxOsmo4Frame::OnUpdateCacheEnable)
	EVT_MENU(ID_MCACHE_STOP, wxOsmo4Frame::OnCacheStop)
	EVT_MENU(ID_MCACHE_ABORT, wxOsmo4Frame::OnCacheAbort)
	EVT_UPDATE_UI(ID_MCACHE_STOP, wxOsmo4Frame::OnUpdateCacheAbort)
	EVT_UPDATE_UI(ID_MCACHE_ABORT, wxOsmo4Frame::OnUpdateCacheAbort)


	EVT_MENU(APP_SHORTCUTS, wxOsmo4Frame::OnShortcuts)
	EVT_MENU(APP_NAV_KEYS, wxOsmo4Frame::OnNavInfo)
	EVT_MENU(APP_ABOUT, wxOsmo4Frame::OnAbout)
	EVT_GPACEVENT(wxOsmo4Frame::OnGPACEvent)
	EVT_TIMER(ID_CTRL_TIMER, wxOsmo4Frame::OnTimer)
	EVT_COMMAND_SCROLL(ID_SLIDER, wxOsmo4Frame::OnSlide)
	EVT_MENU(VIEW_LOGS, wxOsmo4Frame::OnLogs)
	EVT_MENU(VIEW_RTI, wxOsmo4Frame::OnRTI)

	EVT_MENUBUTTON_OPEN(FILE_PREV, wxOsmo4Frame::OnFilePrevOpen)
	EVT_MENUBUTTON_OPEN(FILE_NEXT, wxOsmo4Frame::OnFileNextOpen)
	EVT_MENU(FILE_PREV, wxOsmo4Frame::OnNavPrev)
	EVT_UPDATE_UI(FILE_PREV, wxOsmo4Frame::OnUpdateNavPrev)
	EVT_MENU_RANGE(ID_NAV_PREV_0, ID_NAV_PREV_9, wxOsmo4Frame::OnNavPrevMenu)
	EVT_MENU(FILE_NEXT, wxOsmo4Frame::OnNavNext)
	EVT_UPDATE_UI(FILE_NEXT, wxOsmo4Frame::OnUpdateNavNext)
	EVT_MENU_RANGE(ID_NAV_NEXT_0, ID_NAV_NEXT_9, wxOsmo4Frame::OnNavNextMenu)

	EVT_TOOL(FILE_PLAY, wxOsmo4Frame::OnFilePlay)
	EVT_TOOL(FILE_STEP, wxOsmo4Frame::OnFileStep)
	EVT_TOOL(FILE_STOP, wxOsmo4Frame::OnFileStop)
	EVT_TOOL(SWITCH_RENDER, wxOsmo4Frame::OnRenderSwitch)

	EVT_COMBOBOX(ID_ADDRESS, wxOsmo4Frame::OnURLSelect)

	EVT_MENU_RANGE(ID_SELSTREAM_0, ID_SELSTREAM_9, wxOsmo4Frame::OnStreamSel)
	EVT_UPDATE_UI_RANGE(ID_SELSTREAM_0, ID_SELSTREAM_9, wxOsmo4Frame::OnUpdateStreamSel)

	EVT_MENU_RANGE(ID_SETCHAP_FIRST, ID_SETCHAP_LAST, wxOsmo4Frame::OnChapterSel)
	EVT_UPDATE_UI_RANGE(ID_SETCHAP_FIRST, ID_SETCHAP_LAST, wxOsmo4Frame::OnUpdateChapterSel)

	EVT_MENU_RANGE(ID_VIEWPOINT_FIRST, ID_VIEWPOINT_LAST, wxOsmo4Frame::OnViewport)
	EVT_UPDATE_UI_RANGE(ID_VIEWPOINT_FIRST, ID_VIEWPOINT_LAST, wxOsmo4Frame::OnUpdateViewport)

	EVT_MENU_RANGE(ID_NAVIGATE_NONE, ID_NAVIGATE_GAME, wxOsmo4Frame::OnNavigate)
	EVT_UPDATE_UI_RANGE(ID_NAVIGATE_NONE, ID_NAVIGATE_GAME, wxOsmo4Frame::OnUpdateNavigation)
	EVT_MENU(ID_NAVIGATE_RESET, wxOsmo4Frame::OnNavigateReset)

	EVT_MENU_RANGE(ID_COLLIDE_NONE, ID_COLLIDE_DISP, wxOsmo4Frame::OnCollide)
	EVT_UPDATE_UI_RANGE(ID_COLLIDE_NONE, ID_COLLIDE_DISP, wxOsmo4Frame::OnUpdateCollide)

	EVT_MENU(ID_HEADLIGHT, wxOsmo4Frame::OnHeadlight)
	EVT_UPDATE_UI(ID_HEADLIGHT, wxOsmo4Frame::OnUpdateHeadlight)
	EVT_MENU(ID_GRAVITY, wxOsmo4Frame::OnGravity)
	EVT_UPDATE_UI(ID_GRAVITY, wxOsmo4Frame::OnUpdateGravity)

	EVT_UPDATE_UI(FILE_PROPERTIES, wxOsmo4Frame::OnUpdateNeedsConnect)
	EVT_UPDATE_UI(FILE_RELOAD, wxOsmo4Frame::OnUpdateNeedsConnect)
	EVT_UPDATE_UI(FILE_PLAY, wxOsmo4Frame::OnUpdatePlay)
	EVT_UPDATE_UI(FILE_STOP, wxOsmo4Frame::OnUpdateNeedsConnect)
	EVT_UPDATE_UI(FILE_STEP, wxOsmo4Frame::OnUpdateNeedsConnect)
	EVT_UPDATE_UI(VIEW_ORIGINAL, wxOsmo4Frame::OnUpdateNeedsConnect)
	EVT_UPDATE_UI(VIEW_FULLSCREEN, wxOsmo4Frame::OnUpdateFullScreen)
	EVT_UPDATE_UI(VIEW_AR_KEEP, wxOsmo4Frame::OnUpdateAR)
	EVT_UPDATE_UI(VIEW_AR_FILL, wxOsmo4Frame::OnUpdateAR)
	EVT_UPDATE_UI(VIEW_AR_169, wxOsmo4Frame::OnUpdateAR)
	EVT_UPDATE_UI(VIEW_AR_43, wxOsmo4Frame::OnUpdateAR)

	EVT_SIZE(wxOsmo4Frame::OnSize)
END_EVENT_TABLE()

void wxOsmo4Frame::DoLayout(u32 v_width, u32 v_height)
{
	wxPoint pos;
	if (!m_Address || !m_pProg) return;

	int t_h = m_pToolBar->GetSize().y;
	int a_h = m_pAddBar->GetSize().y;
	int p_h = m_pProg->GetSize().y;

	if (m_bExternalView) {
		if (v_width && v_height) {
			m_orig_width = v_width;
			m_orig_height = v_height;
		}
		SetClientSize(320, a_h+p_h+t_h);
		m_pAddBar->SetDimension(0,0, 320, a_h);
		m_pProg->SetSize(0, t_h+a_h, 320, p_h, 0);
		return;
	}

	if (v_width && v_height) {
		m_orig_width = v_width;
		m_orig_height = v_height;
		v_height += a_h + p_h + t_h;
		SetClientSize(v_width, v_height);
		m_pView->SetSize(0, a_h+t_h, v_width, v_height, 0);
		m_pAddBar->SetDimension(0, t_h, v_width, a_h);
		m_pProg->SetSize(0, v_height - p_h, v_width, p_h, 0);
	}
	wxSize s = GetClientSize();
	s.y -= a_h + p_h + t_h;
	if (m_pView) {
		m_pView->SetSize(0, a_h+t_h, s.x, s.y, 0);
		m_pAddBar->SetDimension(0, 0, s.x, a_h);
		m_pAddBar->SetDimension(0, 0, s.x, a_h);
		m_pAddBar->Layout();
		m_pProg->SetSize(0, s.y+t_h+a_h, s.x, p_h, 0);
		if (m_term) gf_term_set_size(m_term, s.x, s.y);
	}
}

void wxOsmo4Frame::OnSize(wxSizeEvent &event)
{
	DoLayout();
}

void wxOsmo4Frame::OnCloseApp(wxCloseEvent &WXUNUSED(event))
{
	if (m_term) gf_term_del(m_term);
	m_term = NULL;
	Destroy();
}


wxString wxOsmo4Frame::GetFileFilter()
{
	u32 keyCount, i;
	wxString sFiles, sSupportedFiles, sExts;

	/*force MP4 and 3GP files at beginning to make sure they are selected (Win32 bug with too large filters)*/
	sSupportedFiles = wxT("All Known Files|*.m3u;*.pls;*.mp4;*.3gp;*.3g2");
	sExts = wxT("");
	sFiles = wxT("");
	keyCount = gf_cfg_get_key_count(m_user.config, "MimeTypes");
	for (i=0; i<keyCount; i++) {
		Bool first = 1;
		const char *sMime;
		char *sKey;
		const char *opt;
		char szKeyList[1000], sDesc[1000];
		sMime = gf_cfg_get_key_name(m_user.config, "MimeTypes", i);
		if (!sMime) continue;
		opt = gf_cfg_get_key(m_user.config, "MimeTypes", sMime);
		/*remove module name*/
		strcpy(szKeyList, opt+1);
		sKey = strrchr(szKeyList, '\"');
		if (!sKey) continue;
		sKey[0] = 0;
		/*get description*/
		sKey = strrchr(szKeyList, '\"');
		if (!sKey) continue;
		strcpy(sDesc, sKey+1);
		sKey[0] = 0;
		sKey = strrchr(szKeyList, '\"');
		if (!sKey) continue;
		sKey[0] = 0;

		/*if same description for # mime types skip (means an old mime syntax)*/
		if (sFiles.Find(wxString(sDesc, wxConvUTF8) )>=0) continue;
		/*if same extensions for # mime types skip (don't polluate the file list)*/
		if (sExts.Find(wxString(szKeyList, wxConvUTF8) )>=0) continue;

		sExts += wxString(szKeyList, wxConvUTF8);
		sExts += wxT(" ");
		sFiles += wxString(sDesc, wxConvUTF8);
		sFiles += wxT("|");

		wxString sOpt = wxString(szKeyList, wxConvUTF8);
		while (1) {
			wxString ext = sOpt.BeforeFirst(' ');
			if (ext.Find('.')<0) {
				if (first) first = 0;
				else sFiles += wxT(";");
				sFiles += wxT("*.");
				sFiles += ext;
				wxString sext = ext;
				sext += wxT(";");
				if (sSupportedFiles.Find(sext)<0) {
					sSupportedFiles  += wxT(";*.");
					sSupportedFiles += ext;
				}
			}
			if (sOpt==ext) break;
			wxString rem = ext + wxT(" ");
			sOpt.Replace(rem, wxT(""), TRUE);
		}
		sFiles += wxT("|");
	}
	sSupportedFiles += wxT("|");
	sSupportedFiles += sFiles;
	sSupportedFiles += wxT("M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|All Files|*.*||");
	return sSupportedFiles;
}

void wxOsmo4Frame::OnFileOpen(wxCommandEvent & WXUNUSED(event))
{
	wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), GetFileFilter(), wxOPEN | wxMULTIPLE | wxCHANGE_DIR /*| wxHIDE_READONLY*/);

	if (dlg.ShowModal() != wxID_OK) return;

	wxArrayString stra;
	dlg.GetPaths(stra);
	if (stra.GetCount() == 1) {
		m_pPlayList->Truncate();
	} else {
		m_pPlayList->Clear();
	}
	for (u32 i=0; i<stra.GetCount(); i++)
		m_pPlayList->QueueURL(stra[i]);

	m_pPlayList->RefreshList();
	m_pPlayList->PlayNext();
}

void wxOsmo4Frame::OnFileOpenURL(wxCommandEvent & WXUNUSED(event))
{
	OpenURLDlg dlg(this, m_user.config);
	if (dlg.ShowModal()==wxID_OK) {
		m_pPlayList->Truncate();
		m_pPlayList->QueueURL(dlg.m_urlVal);
		m_pPlayList->RefreshList();
		m_pPlayList->PlayNext();
	}
}

void wxOsmo4Frame::OnFileProperties(wxCommandEvent & WXUNUSED(event))
{
	wxFileProps dlg(this);
	dlg.SetIcon(wxIcon(osmo4));
	dlg.ShowModal();
}

void wxOsmo4Frame::OnFileReload(wxCommandEvent & WXUNUSED(event))
{
	gf_term_disconnect(m_term);
	m_connected = 0;
	DoConnect();
}

void wxOsmo4Frame::OnFileReloadConfig(wxCommandEvent & WXUNUSED(event))
{
	gf_term_set_option(m_term, GF_OPT_RELOAD_CONFIG, 1);
}

void wxOsmo4Frame::OnFileQuit(wxCommandEvent & WXUNUSED(event))
{
	Close(FALSE);
}

void wxOsmo4Frame::OnViewOriginal(wxCommandEvent & WXUNUSED(event))
{
	if (!m_bExternalView) {
		DoLayout(m_orig_width, m_orig_height);
	} else {
		gf_term_set_option(m_term, GF_OPT_ORIGINAL_VIEW, 1);
	}
}

void wxOsmo4Frame::OnOptions(wxCommandEvent & WXUNUSED(event))
{
	wxGPACControl dlg(this);
	dlg.SetIcon(wxIcon(osmo4));
	dlg.ShowModal();
}

void wxOsmo4Frame::DoConnect()
{
	//if (m_connected) { gf_term_disconnect(m_term); m_connected = 0; }

	wxString url = m_pPlayList->GetURL();
	m_Address->SetValue(url);
#ifdef __WXGTK__
	m_pVisual->SetFocus();
#else
	m_pView->SetFocus();
#endif
	wxString txt = wxT("Osmo4 - ");
	txt += m_pPlayList->GetDisplayName();
	SetTitle(txt);
	m_bStartupFile = 0;
	gf_term_connect(m_term, url.mb_str(wxConvUTF8));
}

void wxOsmo4Frame::OnLogs(wxCommandEvent & WXUNUSED(event))
{
	m_pLogs->Show();
}

void wxOsmo4Frame::OnUpdateNeedsConnect(wxUpdateUIEvent &event)
{
	event.Enable(m_connected ? 1 : 0);
}

void wxOsmo4Frame::OnUpdatePlay(wxUpdateUIEvent &event)
{
	event.Enable( (m_connected || m_pPlayList->HasValidEntries()) ? 1 : 0);
}

void wxOsmo4Frame::OnUpdateFullScreen(wxUpdateUIEvent &event)
{
	if (m_connected) {
		event.Enable(1);
		event.Check(gf_term_get_option(m_term, GF_OPT_FULLSCREEN) ? 1 : 0);
	} else {
		event.Enable(0);
	}
}

void wxOsmo4Frame::OnFullScreen(wxCommandEvent & WXUNUSED(event))
{
	Bool isFS = gf_term_get_option(m_term, GF_OPT_FULLSCREEN) ? 1 : 0;
	gf_term_set_option(m_term, GF_OPT_FULLSCREEN, isFS ? 0 : 1);
}

void wxOsmo4Frame::OnViewARKeep(wxCommandEvent & WXUNUSED(event))
{
	gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP);
}
void wxOsmo4Frame::OnViewARFill(wxCommandEvent & WXUNUSED(event))
{
	gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN);
}
void wxOsmo4Frame::OnViewAR169(wxCommandEvent & WXUNUSED(event))
{
	gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9);
}
void wxOsmo4Frame::OnViewAR43(wxCommandEvent & WXUNUSED(event))
{
	gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3);
}

void wxOsmo4Frame::OnUpdateAR(wxUpdateUIEvent &event)
{
	if (!m_connected) {
		event.Enable(0);
		return;
	}
	event.Enable(1);
	u32 val = gf_term_get_option(m_term, GF_OPT_ASPECT_RATIO);
	if ((event.GetId() == VIEW_AR_FILL) && (val==GF_ASPECT_RATIO_FILL_SCREEN))
		event.Check(1);
	else if ((event.GetId() == VIEW_AR_KEEP) && (val==GF_ASPECT_RATIO_KEEP))
		event.Check(1);
	else if ((event.GetId() == VIEW_AR_169) && (val==GF_ASPECT_RATIO_16_9))
		event.Check(1);
	else if ((event.GetId() == VIEW_AR_43) && (val==GF_ASPECT_RATIO_4_3))
		event.Check(1);
	else event.Check(0);
}

void wxOsmo4Frame::OnShortcuts(wxCommandEvent & WXUNUSED(event))
{
	wxMessageDialog dlg(this,
	                    wxT("Shortcuts with focus on main frame:\n")
	                    wxT("Open File: Ctrl + O\n")
	                    wxT("Show File Information: Ctrl + I\n")
	                    wxT("Reload File: Ctrl + R\n")
	                    wxT("Pause/Resume File: Ctrl + P\n")
	                    wxT("Step by Step: Ctrl + S\n")
	                    wxT("Fullscreen On/Off: Alt + Return\n")
	                    wxT("View Playlist: Ctrl + L\n")
	                    wxT("Aspect Ratio Normal: Ctrl + 1\n")
	                    wxT("Aspect Ratio Fill: Ctrl + 2\n")
	                    wxT("Aspect Ratio 4/3: Ctrl + 3\n")
	                    wxT("Aspect Ratio 16/9: Ctrl + 4\n")
	                    wxT("\n")
	                    wxT("Shortcuts with focus on video frame:\n")
	                    wxT("Seek +5% into presentation: Alt + right arrow\n")
	                    wxT("Seek -5% into presentation: Alt + left arrow\n")
	                    wxT("Seek +1min into presentation: Alt + up arrow\n")
	                    wxT("Seek -1min into presentation: Alt + down arrow\n")
	                    wxT("Next Playlist Entry: Ctrl + right arrow\n")
	                    wxT("Prev Playlist Entry: Ctrl + left arrow\n")

	                    , wxT("Shortcuts Available on Osmo4")
	                    , wxOK);

	dlg.ShowModal();
}

void wxOsmo4Frame::OnNavInfo(wxCommandEvent & WXUNUSED(event))
{
	wxMessageDialog dlg(this,
	                    wxT("* Walk & Fly modes:\n")
	                    wxT("\tH move: H pan - V move: Z-translate - V move+CTRL or Wheel: V pan - Right Click (Walk only): Jump\n")
	                    wxT("\tleft/right: H pan - left/right+CTRL: H translate - up/down: Z-translate - up/down+CTRL: V pan\n")
	                    wxT("* Pan mode:\n")
	                    wxT("\tH move: H pan - V move: V pan - V move+CTRL or Wheel: Z-translate\n")
	                    wxT("\tleft/right: H pan - left/right+CTRL: H translate - up/down: V pan - up/down+CTRL: Z-translate\n")
	                    wxT("* Slide mode:\n")
	                    wxT("\tH move: H translate - V move: V translate - V move+CTRL or Wheel: Z-translate\n")
	                    wxT("\tleft/right: H translate - left/right+CTRL: H pan - up/down: V translate - up/down+CTRL: Z-translate\n")
	                    wxT("* Examine & Orbit mode:\n")
	                    wxT("\tH move: Y-Axis rotate - H move+CTRL: No move - V move: X-Axis rotate - V move+CTRL or Wheel: Z-translate\n")
	                    wxT("\tleft/right: Y-Axis rotate - left/right+CTRL: H translate - up/down: X-Axis rotate - up/down+CTRL: Y-translate\n")
	                    wxT("* VR mode:\n")
	                    wxT("\tH move: H pan - V move: V pan - V move+CTRL or Wheel: Camera Zoom\n")
	                    wxT("\tleft/right: H pan - up/down: V pan - up/down+CTRL: Camera Zoom\n")
	                    wxT("* Game mode (press END to escape):\n")
	                    wxT("\tH move: H pan - V move: V pan\n")
	                    wxT("\tleft/right: H translate - up/down: Z-translate\n")
	                    wxT("\n")
	                    wxT("* All 3D modes: CTRL+PGUP/PGDOWN will zoom in/out camera (field of view) \n")
	                    wxT("\n")
	                    wxT("*Slide Mode in 2D:\n")
	                    wxT("\tH move: H translate - V move: V translate - V move+CTRL: zoom\n")
	                    wxT("\tleft/right: H translate - up/down: V translate - up/down+CTRL: zoom\n")
	                    wxT("*Examine Mode in 2D (3D renderer only):\n")
	                    wxT("\tH move: Y-Axis rotate - V move: X-Axis rotate\n")
	                    wxT("\tleft/right: Y-Axis rotate - up/down: X-Axis rotate\n")
	                    wxT("\n")
	                    wxT("HOME: reset navigation to last viewpoint (2D or 3D navigation)\n")
	                    wxT("SHIFT key in all modes: fast movement\n")

	                    , wxT("3D navigation keys (\'H\'orizontal and \'V\'ertical) used in GPAC")
	                    , wxOK );
	dlg.ShowModal();
}


/*open file dlg*/
class AboutDlg : public wxDialog {
public:
	AboutDlg(wxWindow *parent);

private:
	wxStaticText *m_info;
	wxButton *m_close;
	void OnClose(wxCommandEvent& event);
	DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(AboutDlg, wxDialog)
	EVT_BUTTON(ID_ABOUT_CLOSE, AboutDlg::OnClose)
END_EVENT_TABLE()

AboutDlg::AboutDlg(wxWindow *parent)
	: wxDialog(parent, -1, wxString(wxT("GPAC/Osmo4 V ")wxT(GPAC_FULL_VERSION)))
{
	SetSize(220, 320);
	Centre();
	wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);

	m_info = new wxStaticText(this, -1, wxT("http://gpac.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE);
	sizer->Add(m_info, 1, wxEXPAND|wxADJUST_MINSIZE, 0);
	m_close = new wxButton(this, ID_ABOUT_CLOSE, wxT("Close"), wxDefaultPosition, wxSize(120, 20));
	sizer->Add(m_close, 0, wxEXPAND, 0);

	SetIcon(wxIcon(osmo4));
	m_info->SetLabel(
	    wxT("Osmo4 Player\n")
	    wxT("GPAC Multimedia Framework\n")
	    wxT("\n")
	    wxT("This program is gf_free software and may\n")
	    wxT("be distributed according to the terms\n")
	    wxT("of the GNU Lesser General Public License\n")
	    wxT("\n")
	    wxT("Authors: Jean Le Feuvre\n")
	    wxT("(c) Telecom ParisTech 2005-2012\n")
	    wxT("All Rights Reserved\n")
	    wxT("http://gpac.sourceforge.net\n")
	    wxT("\n")
	    wxT(" ** With Many Thanks To ** \n\n")
	    wxT("Mozilla SpiderMonkey (JavaScript)\n")
	    wxT("The FreeType Project\n")
	    wxT("The PNG Group, The I.J.G.\n")
	    wxT("FFMPEG, FAAD, XVID, MAD\n")
	);

	SetSizer(sizer);
	sizer->Fit(this);
}
void AboutDlg::OnClose(wxCommandEvent& WXUNUSED(event))
{
	Close(FALSE);
}

void wxOsmo4Frame::OnAbout(wxCommandEvent & WXUNUSED(event))
{
	AboutDlg dlg(this);
	dlg.ShowModal();
}


void wxOsmo4Frame::OnGPACEvent(wxGPACEvent &event)
{
	wxString cmd;
	wxCommandEvent evt;
	if (!m_term) return;

	switch (event.gpac_evt.type) {
	case GF_EVENT_NAVIGATE:
		if (gf_term_is_supported_url(m_term, event.to_url.mb_str(wxConvUTF8), 1, 0)) {
			char *str = gf_url_concatenate(m_pPlayList->GetURL().mb_str(wxConvUTF8), event.to_url.mb_str(wxConvUTF8));
			if (str) {
				m_pPlayList->Truncate();
				m_pPlayList->QueueURL(wxString(str, wxConvUTF8));
				m_pPlayList->RefreshList();
				gf_free(str);
				m_pPlayList->PlayNext();
			}
			return;
		}
		cmd = get_pref_browser(m_user.config);
		if (cmd.IsEmpty()) {
			cmd += wxT(" ");
			cmd += event.to_url;
			wxExecute(cmd);
		} else {
#ifdef wxLaunchDefaultBrowser
			wxLaunchDefaultBrowser(event.to_url);
#endif
		}
		break;
	case GF_EVENT_QUIT:
		Close(TRUE);
		break;
	case GF_EVENT_SET_CAPTION:
		SetTitle(event.to_url);
		break;
	case GF_EVENT_CONNECT:
		BuildStreamList(0);
		ConnectAcknowledged(event.gpac_evt.connect.is_connected);
		break;
	case GF_EVENT_KEYDOWN:
		if (!(event.gpac_evt.key.flags & GF_KEY_MOD_CTRL)) return;
		switch (event.gpac_evt.key.key_code) {
		case GF_KEY_R:
			gf_term_set_option(m_term, GF_OPT_REFRESH, 1);
			break;
		case GF_KEY_P:
			OnFilePlay(evt);
			break;
		case GF_KEY_S:
			OnFileStep(evt);
			break;
		}
		break;
	case GF_EVENT_SCENE_SIZE:
		m_orig_width = event.gpac_evt.size.width;
		m_orig_height = event.gpac_evt.size.height;
	case GF_EVENT_SIZE:
		if (! gf_term_get_option(m_term, GF_OPT_FULLSCREEN)) {
			DoLayout(event.gpac_evt.size.width, event.gpac_evt.size.height);
		}
		break;
	case GF_EVENT_VIEWPOINTS:
		BuildViewList();
		break;
	case GF_EVENT_STREAMLIST:
		BuildStreamList(0);
		break;
	}
}


static wxString format_time(u32 duration, u32 timescale)
{
	u32 h, m, s;
	Float time = duration;
	time /= timescale;
	time *= 1000;
	h = (u32) (time / 1000 / 3600);
	m = (u32) (time / 1000 / 60 - h*60);
	s = (u32) (time / 1000 - h*3600 - m*60);
	return wxString::Format(wxT("%02d:%02d:%02d"), h, m, s);
}

void wxOsmo4Frame::SetStatus(wxString str)
{
	//m_pStatusbar->SetStatusText(str, 2);
	m_LastStatusTime = gf_sys_clock();
}

#define RTI_REFRESH_MS		500
void wxOsmo4Frame::OnRTI(wxCommandEvent & event)
{
	m_bViewRTI = event.IsChecked();
	if (m_bViewRTI) {
		if (!m_pTimer->IsRunning()) m_pTimer->Start(RTI_REFRESH_MS, 0);
	} else if (!m_connected && m_pTimer->IsRunning()) {
		m_LastStatusTime = 0;
		m_pStatusbar->SetStatusText(wxT("Ready"), 2);
		m_pTimer->Stop();
	}
}

void wxOsmo4Frame::OnTimer(wxTimerEvent& WXUNUSED(event))
{
	wxString str;
	u32 now;
	if (m_LastStatusTime) {
		now = gf_sys_clock();
		if (now > 1000+m_LastStatusTime) {
			m_LastStatusTime = 0;
			m_pStatusbar->SetStatusText(wxT("Ready"), 2);
		}
	}

	if (m_bViewRTI) {
		GF_SystemRTInfo rti;
		if (!gf_sys_get_rti(RTI_REFRESH_MS, &rti, 0)) return;
		if (rti.gpac_memory) rti.process_memory = rti.gpac_memory;

		str = wxString::Format(wxT("CPU %02d (%02d) - Mem %d kB" ),
		                       rti.total_cpu_usage, rti.process_cpu_usage, rti.gpac_memory/1024);

		m_pStatusbar->SetStatusText(str, 2);
	}
	if (!m_connected) return;

	now = gf_term_get_time_in_ms(m_term);
	if (!now) return;

	if (!m_duration) {
		str = format_time(now, 1000);
		m_pStatusbar->SetStatusText(str);
		str = wxString::Format(wxT("FPS %.2f"), gf_term_get_framerate(m_term, 0));
		m_pStatusbar->SetStatusText(str, 1);
		return;
	}
#ifdef __WXGTK__
	if (m_bGrabbed) {
		u32 now = gf_sys_clock() - m_last_grab_time;
		if (now>200) {
			m_bGrabbed = 0;
			Double res = (Double) m_last_grab_pos;
			res /= 1000;
			res *= m_duration;
			if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) {
				gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING);
				m_bToReset = 0;
			}
			gf_term_play_from_time(m_term, (u32) res, 0);
			return;
		}
	}
#endif

	if (!m_bGrabbed) {
		if ((now >= m_duration + 500) && gf_term_get_option(m_term, GF_OPT_IS_FINISHED)) {
			m_pPlayList->PlayNext();
		} else {
			Double val = now * 1000;
			val /= m_duration;
			m_pProg->SetValue((val<=1000) ? (u32) val : 1000);

			if (0) {
				str = format_time(m_duration-now, 1000);
			} else {
				str = format_time(now, 1000);
			}
			m_pStatusbar->SetStatusText(str);
			str = wxString::Format(wxT("FPS %.2f"), gf_term_get_framerate(m_term, 0));
			m_pStatusbar->SetStatusText(str, 1);
		}
	}
}

void wxOsmo4Frame::ConnectAcknowledged(Bool bOk)
{
	if (bOk) {
		m_pTimer->Start(RTI_REFRESH_MS, 0);
		m_connected = 1;
		m_bToReset = 0;
		UpdatePlay();
		BuildChapterList(0);
	} else {
		BuildChapterList(1);
		if (!m_connected) {
			UpdatePlay();
			m_pTimer->Stop();
			//m_pProg->Enable(0);
		}
	}
}

void wxOsmo4Frame::OnFilePlay(wxCommandEvent & WXUNUSED(event))
{
	wxCommandEvent evt;
	if (m_connected) {
		if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) {
			gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING);
			if (m_bToReset) {
				m_pTimer->Start(100, 0);
				gf_term_play_from_time(m_term, 0, 0);
			}
			m_bToReset = 0;
			UpdatePlay();
		} else {
			gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED);
			UpdatePlay();
		}
	} else {
		m_pPlayList->Play();
	}
}

void wxOsmo4Frame::OnFileStep(wxCommandEvent & WXUNUSED(event))
{
	wxCommandEvent evt;
	gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE);
	UpdatePlay();
}

void wxOsmo4Frame::OnFileStop(wxCommandEvent &WXUNUSED(event))
{
	Stop();
}

void wxOsmo4Frame::Stop()
{
	if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) {
		gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED);
	}
	m_bToReset = 1;
	m_pTimer->Stop();
	m_pProg->SetValue(0);
	UpdatePlay();
}

void wxOsmo4Frame::OnSlide(wxScrollEvent &event)
{
	if (!m_duration) return;

	/*wxSlider on GTK is buggy, so track a release timeout*/
#ifdef __WXGTK__
	m_last_grab_time = gf_sys_clock();
	m_bGrabbed = 1;
	m_last_grab_pos = event.GetPosition();
	Double now = (Double) m_last_grab_pos;
	now /= 1000;
	now *= m_duration;
	wxString str = format_time((u32) (now), 1000);
	m_pStatusbar->SetStatusText(str);
	if (!m_pTimer->IsRunning()) m_pTimer->Start(100, 0);
#else
	s32 type = event.GetEventType();
	if (type == wxEVT_SCROLL_THUMBTRACK) {
		m_bGrabbed = 1;
		Double now = (Double) event.GetPosition();
		now /= 1000;
		now *= m_duration;
		wxString str = format_time((u32) (now), 1000);
		m_pStatusbar->SetStatusText(str);
	}
	else if (m_bGrabbed) {
		m_bGrabbed = 0;
		Double res = (Double) m_pProg->GetValue();
		res /= 1000;
		res *= m_duration;
		if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) {
			gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING);
			m_bToReset = 0;
			if (!m_pTimer->IsRunning()) m_pTimer->Start(100, 0);
		}
		gf_term_play_from_time(m_term, (u32) res, 0);
	}
#endif
}


void wxOsmo4Frame::BuildViewList()
{
	if (!vp_list || !m_connected) return;

	while (vp_list->GetMenuItemCount()) {
		wxMenuItem* it = vp_list->FindItemByPosition(0);
		vp_list->Delete(it);
	}

	s32 id = ID_VIEWPOINT_FIRST;
	nb_viewpoints = 0;
	while (1) {
		const char *szName;
		Bool bound;
		GF_Err e = gf_term_get_viewpoint(m_term, nb_viewpoints+1, &szName, &bound);
		if (e) break;
		if (szName) {
			vp_list->AppendCheckItem(id+nb_viewpoints, wxString(szName, wxConvUTF8) );
		} else {
			vp_list->AppendCheckItem(id+nb_viewpoints, wxString::Format(wxT("Viewpoint #%d"), nb_viewpoints+1) );
		}
		nb_viewpoints++;
	}
}

void wxOsmo4Frame::OnViewport(wxCommandEvent & event)
{
	u32 ID = event.GetId() - ID_VIEWPOINT_FIRST;
	gf_term_set_viewpoint(m_term, ID+1, NULL);
}

void wxOsmo4Frame::OnUpdateViewport(wxUpdateUIEvent & event)
{
	u32 ID = event.GetId() - ID_VIEWPOINT_FIRST;
	const char *szName;
	Bool bound;
	gf_term_get_viewpoint(m_term, ID+1, &szName, &bound);
	event.Enable(1);
	if (bound) event.Check(1);
}

void wxOsmo4Frame::OnNavigate(wxCommandEvent & event)
{
	switch (event.GetId()) {
	case ID_NAVIGATE_NONE:
		gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE);
		break;
	case ID_NAVIGATE_WALK:
		gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_WALK);
		break;
	case ID_NAVIGATE_FLY:
		gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_FLY);
		break;
	case ID_NAVIGATE_EXAMINE:
		gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_EXAMINE);
		break;
	case ID_NAVIGATE_PAN:
		gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_PAN);
		break;
	case ID_NAVIGATE_SLIDE:
		gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_SLIDE);
		break;
	case ID_NAVIGATE_ORBIT:
		gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_ORBIT);
		break;
	case ID_NAVIGATE_GAME:
		gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_GAME);
		break;
	}
}
void wxOsmo4Frame::OnNavigateReset(wxCommandEvent & WXUNUSED(event))
{
	gf_term_set_option(m_term, GF_OPT_NAVIGATION_TYPE, 0);
}
void wxOsmo4Frame::OnUpdateNavigation(wxUpdateUIEvent & event)
{
	u32 ID = event.GetId();
	event.Enable(0);
	if (!m_connected) return;
	u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE);
	bool enable = type ? 1 : 0;

	u32 mode = gf_term_get_option(m_term, GF_OPT_NAVIGATION);
	/*common 2D/3D modes*/
	if (ID==ID_NAVIGATE_NONE) {
		event.Enable(enable);
		event.Check(mode ? 0 : 1);
	}
	else if (ID==ID_NAVIGATE_EXAMINE) {
		event.Enable(enable);
		event.Check((mode==GF_NAVIGATE_EXAMINE) ? 1 : 0);
	}
	else if (ID==ID_NAVIGATE_SLIDE) {
		event.Enable(enable);
		event.Check((mode==GF_NAVIGATE_SLIDE) ? 1 : 0);
	}

	if (type==GF_NAVIGATE_TYPE_2D) return;
	event.Enable(enable);
	if (ID==ID_NAVIGATE_WALK) event.Check((mode==GF_NAVIGATE_WALK) ? 1 : 0);
	else if (ID==ID_NAVIGATE_FLY) event.Check((mode==GF_NAVIGATE_FLY) ? 1 : 0);
	else if (ID==ID_NAVIGATE_PAN) event.Check((mode==GF_NAVIGATE_PAN) ? 1 : 0);
	else if (ID==ID_NAVIGATE_ORBIT) event.Check((mode==GF_NAVIGATE_ORBIT) ? 1 : 0);
	else if (ID==ID_NAVIGATE_GAME) event.Check((mode==GF_NAVIGATE_GAME) ? 1 : 0);
}

void wxOsmo4Frame::OnRenderSwitch(wxCommandEvent &WXUNUSED(event))
{
	const char *opt = gf_cfg_get_key(m_user.config, "Compositor", "ForceOpenGL");
	Bool use_gl = (opt && !stricmp(opt, "yes")) ? 1 : 0;
	gf_cfg_set_key(m_user.config, "Compositor", "ForceOpenGL", use_gl ? "no" : "yes");

	gf_term_set_option(m_term, GF_OPT_USE_OPENGL, !use_gl);

	UpdateRenderSwitch();
}

void wxOsmo4Frame::UpdateRenderSwitch()
{
	const char *opt = gf_cfg_get_key(m_user.config, "Compositor", "ForceOpenGL");
	m_pToolBar->RemoveTool(SWITCH_RENDER);
	if (opt && !stricmp(opt, "yes"))
		m_pToolBar->InsertTool(12, SWITCH_RENDER, *m_pSW2D, wxNullBitmap, FALSE, NULL, wxT("2D Rasterizer"));
	else
		m_pToolBar->InsertTool(12, SWITCH_RENDER, *m_pSW3D, wxNullBitmap, FALSE, NULL, wxT("OpenGL Rendering"));

#ifdef WIN32
	/*there's a display bug with the menubtn, remove and reinsert*/
	m_pToolBar->RemoveTool(FILE_PREV);
	m_pToolBar->RemoveTool(FILE_NEXT);
	m_pToolBar->InsertControl(2, m_pPrevBut);
	m_pToolBar->InsertControl(3, m_pNextBut);
#endif
	m_pToolBar->Realize();
}

void wxOsmo4Frame::UpdatePlay()
{
	m_pToolBar->RemoveTool(FILE_PLAY);
	if (m_connected) {
		if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED)
			m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPlay, wxNullBitmap, FALSE, NULL, wxT("Pause File"));
		else
			m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPause, wxNullBitmap, FALSE, NULL, wxT("Play File"));
	} else {
		m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPlay, wxNullBitmap, FALSE, NULL, wxT("Pause File"));
	}

#ifdef WIN32
	/*there's a display bug with the menubtn, remove and reinsert*/
	m_pToolBar->RemoveTool(FILE_PREV);
	m_pToolBar->RemoveTool(FILE_NEXT);
	m_pToolBar->InsertControl(2, m_pPrevBut);
	m_pToolBar->InsertControl(3, m_pNextBut);
#endif
	m_pToolBar->Realize();
}

void wxOsmo4Frame::OnCollide(wxCommandEvent & event)
{
	u32 ID = event.GetId();
	if (ID==ID_COLLIDE_NONE) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_NONE);
	else if (ID==ID_COLLIDE_REG) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_NORMAL);
	else if (ID==ID_COLLIDE_DISP) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_DISPLACEMENT);
}
void wxOsmo4Frame::OnUpdateCollide(wxUpdateUIEvent & event)
{
	u32 ID = event.GetId();
	event.Enable(0);
	if (!m_connected) return;
	event.Enable(1);
	u32 mode = gf_term_get_option(m_term, GF_OPT_COLLISION);
	if (ID==ID_COLLIDE_NONE) {
		event.Check((mode==GF_COLLISION_NONE) ? 1 : 0);
	}
	else if (ID==ID_COLLIDE_REG) {
		event.Check((mode==GF_COLLISION_NORMAL) ? 1 : 0);
	}
	else if (ID==ID_COLLIDE_DISP) {
		event.Check((mode==GF_COLLISION_DISPLACEMENT) ? 1 : 0);
	}
}

void wxOsmo4Frame::OnHeadlight(wxCommandEvent &WXUNUSED(event))
{
	Bool val = !gf_term_get_option(m_term, GF_OPT_HEADLIGHT);
	gf_term_set_option(m_term, GF_OPT_HEADLIGHT, val);
}
void wxOsmo4Frame::OnUpdateHeadlight(wxUpdateUIEvent & event)
{
	event.Enable(0);
	if (!m_connected) return;
	u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE);
	if (type!=GF_NAVIGATE_TYPE_3D) return;

	event.Enable(1);
	event.Check(gf_term_get_option(m_term, GF_OPT_HEADLIGHT) ? 1 : 0);
}
void wxOsmo4Frame::OnGravity(wxCommandEvent & WXUNUSED(event))
{
	Bool val = gf_term_get_option(m_term, GF_OPT_GRAVITY) ? 0 : 1;
	gf_term_set_option(m_term, GF_OPT_GRAVITY, val);
}
void wxOsmo4Frame::OnUpdateGravity(wxUpdateUIEvent & event)
{
	event.Enable(0);
	if (!m_connected) return;
	u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE);
	if (type!=GF_NAVIGATE_TYPE_3D) return;
	type = gf_term_get_option(m_term, GF_OPT_NAVIGATION);
	if (type != GF_NAVIGATE_WALK) return;
	event.Enable(1);
	event.Check(gf_term_get_option(m_term, GF_OPT_GRAVITY) ? 1 : 0);
}


BEGIN_EVENT_TABLE(wxMyComboBox, wxComboBox)
	EVT_KEY_UP(wxMyComboBox::OnKeyUp)
END_EVENT_TABLE()

void wxMyComboBox::OnKeyUp(wxKeyEvent &event)
{
	if (event.GetKeyCode()==WXK_RETURN) {
		event.Skip();
		wxCommandEvent evt;
		evt.SetEventType(wxEVT_COMMAND_COMBOBOX_SELECTED);
		evt.SetEventObject(this);
		evt.SetId(GetId());
		GetParent()->AddPendingEvent(evt);
	}
}


void wxOsmo4Frame::ReloadURLs()
{
	const char *sOpt;
	u32 i=0;

	m_Address->Clear();
	while (1) {
		sOpt = gf_cfg_get_key_name(m_user.config, "RecentFiles", i);
		if (!sOpt) break;
		m_Address->Append(wxString(sOpt, wxConvUTF8) );
		i++;
	}
}

void wxOsmo4Frame::SelectionReady()
{
	wxString urlVal = m_Address->GetValue();
	if (urlVal.Find(wxT("://"))>0) {
		UpdateLastFiles(m_user.config, urlVal.mb_str(wxConvUTF8));
		ReloadURLs();
	}
	m_pPlayList->Truncate();
	m_pPlayList->QueueURL(urlVal);
	m_pPlayList->RefreshList();
	m_pPlayList->PlayNext();
}

void wxOsmo4Frame::OnURLSelect(wxCommandEvent &WXUNUSED(event))
{
	SelectionReady();
}

void wxOsmo4Frame::OnPlaylist(wxCommandEvent &WXUNUSED(event))
{
	assert(m_pPlayList);
	m_pPlayList->Show(m_pPlayList->IsShown() ? 0 : 1);
}

void wxOsmo4Frame::OnUpdatePlayList(wxUpdateUIEvent & event)
{
	event.Enable(1);
	event.Check(m_pPlayList->IsShown() ? 1 : 0);
}

void wxOsmo4Frame::OnFilePrevOpen(wxNotifyEvent & event)
{
	u32 count = gf_list_count(m_pPlayList->m_entries);
	u32 start = m_pPlayList->m_cur_entry - 1;
	wxMenu *popup = new wxMenu();

	for (u32 i=0; i<10; i++) {
		if (i > start) break;
		if (start - i >= count) break;
		PLEntry *ple = (PLEntry *) gf_list_get(m_pPlayList->m_entries, start - i);
		popup->Append(ID_NAV_PREV_0 + i, wxString(ple->m_disp_name, wxConvUTF8) );
	}
	m_pPrevBut->AssignMenu(popup);
}

void wxOsmo4Frame::OnFileNextOpen(wxNotifyEvent & event)
{
	u32 count = gf_list_count(m_pPlayList->m_entries);
	wxMenu *popup = new wxMenu();
	u32 start = m_pPlayList->m_cur_entry + 1;
	for (u32 i=0; i<10; i++) {
		if (start + i >= count) break;
		PLEntry *ple = (PLEntry *) gf_list_get(m_pPlayList->m_entries, start + i);
		popup->Append(ID_NAV_NEXT_0 + i, wxString(ple->m_disp_name, wxConvUTF8) );
	}
	m_pNextBut->AssignMenu(popup);
}

void wxOsmo4Frame::OnNavPrev(wxCommandEvent &WXUNUSED(event))
{
	if (m_pPlayList->m_cur_entry<=0) return;
	m_pPlayList->PlayPrev();
}
void wxOsmo4Frame::OnUpdateNavPrev(wxUpdateUIEvent & event)
{
	if (m_pPlayList->m_cur_entry<=0) event.Enable(0);
	else event.Enable(TRUE);
}
void wxOsmo4Frame::OnNavPrevMenu(wxCommandEvent &event)
{
	u32 ID = event.GetId() - ID_NAV_PREV_0;
	s32 prev = m_pPlayList->m_cur_entry - ID;
	if (prev>=0) {
		m_pPlayList->m_cur_entry = prev;
		m_pPlayList->PlayPrev();
	}
}
void wxOsmo4Frame::OnNavNext(wxCommandEvent &WXUNUSED(event))
{
	/*don't play if last could trigger playlist loop*/
	if ((m_pPlayList->m_cur_entry<0) || (gf_list_count(m_pPlayList->m_entries) == 1 + (u32) m_pPlayList->m_cur_entry)) return;
	m_pPlayList->PlayNext();
}
void wxOsmo4Frame::OnUpdateNavNext(wxUpdateUIEvent & event)
{
	if (m_pPlayList->m_cur_entry<0) event.Enable(0);
	else if ((u32) m_pPlayList->m_cur_entry + 1 == gf_list_count(m_pPlayList->m_entries) ) event.Enable(0);
	else event.Enable(1);
}

void wxOsmo4Frame::OnNavNextMenu(wxCommandEvent &event)
{
	u32 ID = event.GetId() - ID_NAV_NEXT_0;
	s32 next = m_pPlayList->m_cur_entry + ID;
	if (next < (s32) gf_list_count(m_pPlayList->m_entries) ) {
		m_pPlayList->m_cur_entry = next;
		m_pPlayList->PlayNext();
	}
}

void wxOsmo4Frame::OnClearNav(wxCommandEvent &WXUNUSED(event))
{
	m_pPlayList->ClearButPlaying();
}


void wxOsmo4Frame::BuildStreamList(Bool reset_only)
{
	u32 nb_subs;
	wxMenu *pMenu;

	pMenu = sel_menu->FindItemByPosition(0)->GetSubMenu();
	while (pMenu->GetMenuItemCount()) {
		wxMenuItem* it = pMenu->FindItemByPosition(0);
		pMenu->Delete(it);
	}
	pMenu = sel_menu->FindItemByPosition(1)->GetSubMenu();
	while (pMenu->GetMenuItemCount()) {
		wxMenuItem* it = pMenu->FindItemByPosition(0);
		pMenu->Delete(it);
	}
	pMenu = sel_menu->FindItemByPosition(2)->GetSubMenu();
	while (pMenu->GetMenuItemCount()) {
		wxMenuItem* it = pMenu->FindItemByPosition(0);
		pMenu->Delete(it);
	}

	if (reset_only) {
		m_bFirstStreamListBuild = 1;
		return;
	}

	if (!gf_term_get_option(m_term, GF_OPT_CAN_SELECT_STREAMS)) return;

	nb_subs = 0;
	GF_ObjectManager *root_od = gf_term_get_root_object(m_term);
	if (!root_od) return;
	u32 count = gf_term_get_object_count(m_term, root_od);

	for (u32 i=0; i<count; i++) {
		char szLabel[1024];
		GF_MediaInfo info;
		GF_ObjectManager *odm = gf_term_get_object(m_term, root_od, i);
		if (!odm) return;

		if (gf_term_get_object_info(m_term, odm, &info) != GF_OK) break;

		if (info.owns_service) {
			char *szName = (char *) strrchr(info.service_url, '\\');
			if (!szName) szName = (char *) strrchr(info.service_url, '/');
			if (!szName) szName = (char *) info.service_url;
			else szName += 1;
			strcpy(szLabel, szName);
			szName = strrchr(szLabel, '.');
			if (szName) szName[0] = '\0';
		}
		switch (info.od_type) {
		case GF_STREAM_AUDIO:
			pMenu = sel_menu->FindItemByPosition(0)->GetSubMenu();
			if (!info.owns_service) sprintf(szLabel, "Audio #"LLU, (u64)pMenu->GetMenuItemCount() + 1);
			pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8));
			break;
		case GF_STREAM_VISUAL:
			pMenu = sel_menu->FindItemByPosition(1)->GetSubMenu();
			if (!info.owns_service) sprintf(szLabel, "Video #"LLU, (u64)pMenu->GetMenuItemCount() + 1);
			pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8));
			break;
		case GF_STREAM_TEXT:
			nb_subs ++;
			pMenu = sel_menu->FindItemByPosition(2)->GetSubMenu();
			if (!info.owns_service) sprintf(szLabel, "Subtitle #"LLU, (u64)pMenu->GetMenuItemCount() + 1);
			pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8));
			break;
		}
	}
	if (m_bFirstStreamListBuild) {
		m_bFirstStreamListBuild = 0;
		if (!nb_subs && m_lookforsubs) LookForSubtitles();
	}
}

void wxOsmo4Frame::OnStreamSel(wxCommandEvent & event)
{
	GF_ObjectManager *root_od = gf_term_get_root_object(m_term);
	if (!root_od) return;
	u32 ID = event.GetId() - ID_SELSTREAM_0;
	GF_ObjectManager *odm = gf_term_get_object(m_term, root_od, ID);
	gf_term_select_object(m_term, odm);
}

void wxOsmo4Frame::OnUpdateStreamSel(wxUpdateUIEvent & event)
{
	GF_ObjectManager *root_od = gf_term_get_root_object(m_term);
	if (!root_od) return;
	u32 ID = event.GetId() - ID_SELSTREAM_0;

	GF_ObjectManager *odm = gf_term_get_object(m_term, root_od, ID);
	if (!odm) return;

	GF_MediaInfo info;
	gf_term_get_object_info(m_term, odm, &info);
	event.Enable(1);
	event.Check(info.status ? 1 : 0);
}

void wxOsmo4Frame::OnUpdateStreamMenu(wxUpdateUIEvent & event)
{
	if (!m_connected || !gf_term_get_option(m_term, GF_OPT_CAN_SELECT_STREAMS)) {
		event.Enable(0);
	} else {
		event.Enable(1);
	}
}

void wxOsmo4Frame::OnAddSub(wxCommandEvent &WXUNUSED(event))
{
	wxFileDialog dlg(this, wxT("Add Subtitle"), wxT(""), wxT(""), wxT("All Subtitles|*.srt;*.ttxt|SRT Subtitles|*.srt|3GPP TimedText|*.ttxt|"), wxOPEN | wxCHANGE_DIR /* | wxHIDE_READONLY*/);

	if (dlg.ShowModal() == wxID_OK) {
		AddSubtitle(dlg.GetPath().mb_str(wxConvUTF8), 1);
	}

}

void wxOsmo4Frame::AddSubtitle(const char *fileName, Bool auto_play)
{
	gf_term_add_object(m_term, fileName, auto_play);
}

static Bool subs_enum_dir_item(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
{
	wxOsmo4Frame *_this = (wxOsmo4Frame*)cbck;
	_this->AddSubtitle(item_path, 0);
	return 0;
}

void wxOsmo4Frame::LookForSubtitles()
{
	char dir[GF_MAX_PATH];
	const char *url = m_pPlayList->GetURL().mb_str(wxConvUTF8);
	strcpy(dir, url);
	char *sep = strrchr(dir, '\\');
	if (!sep) strcpy(dir, ::wxGetCwd().mb_str(wxConvUTF8));
	else sep[0] = 0;

	gf_enum_directory(dir, 0, subs_enum_dir_item, this, "ttxt;srt");
}

void wxOsmo4Frame::OnCacheEnable(wxCommandEvent &WXUNUSED(event))
{
	u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE);
	if (state==GF_MEDIA_CACHE_DISABLED) {
		gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_ENABLED);
	} else if (state==GF_MEDIA_CACHE_DISABLED) {
		gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED);
	}
}

void wxOsmo4Frame::OnCacheStop(wxCommandEvent &WXUNUSED(event))
{
	gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED);
}

void wxOsmo4Frame::OnCacheAbort(wxCommandEvent &WXUNUSED(event))
{
	gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISCARD);
}

void wxOsmo4Frame::OnUpdateCacheEnable(wxUpdateUIEvent & event)
{
	u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE);
	switch (state) {
	case GF_MEDIA_CACHE_ENABLED:
		event.Enable(1);
		event.SetText(wxT("Enabled"));
		break;
	case GF_MEDIA_CACHE_RUNNING:
		event.SetText(wxT("Running"));
		event.Enable(0);
		break;
	case GF_MEDIA_CACHE_DISABLED:
		event.SetText(wxT("Disabled"));
		break;
	}
}

void wxOsmo4Frame::OnUpdateCacheAbort(wxUpdateUIEvent & event)
{
	u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE);
	event.Enable( (state==GF_MEDIA_CACHE_RUNNING) ? 1 : 0);
}



void wxOsmo4Frame::BuildChapterList(Bool reset_only)
{
	GF_MediaInfo odi;

	while (chap_menu->GetMenuItemCount()) {
		wxMenuItem* it = chap_menu->FindItemByPosition(0);
		chap_menu->Delete(it);
	}
	if (m_chapters_start) gf_free(m_chapters_start);
	m_chapters_start = NULL;
	m_num_chapters = 0;
	if (reset_only) return;

	GF_ObjectManager *root_od = gf_term_get_root_object(m_term);
	if (!root_od) return;
	if (gf_term_get_object_info(m_term, root_od, &odi) != GF_OK) return;

	u32 count = gf_list_count(odi.od->OCIDescriptors);
	m_num_chapters = 0;
	for (u32 i=0; i<count; i++) {
		char szLabel[1024];
		GF_Segment *seg = (GF_Segment *) gf_list_get(odi.od->OCIDescriptors, i);
		if (seg->tag != GF_ODF_SEGMENT_TAG) continue;

		if (seg->SegmentName && strlen((const char *)seg->SegmentName)) {
			strcpy(szLabel, (const char *) seg->SegmentName);
		} else {
			sprintf(szLabel, "Chapter %02d", m_num_chapters+1);
		}
		chap_menu->AppendCheckItem(ID_SETCHAP_FIRST + m_num_chapters, wxString(szLabel, wxConvUTF8));

		m_chapters_start = (Double *) gf_realloc(m_chapters_start, sizeof(Double)*(m_num_chapters+1));
		m_chapters_start[m_num_chapters] = seg->startTime;
		m_num_chapters++;
	}

	/*get any service info*/
	NetInfoCommand com;
	if (!m_bStartupFile && gf_term_get_service_info(m_term, root_od, &com) == GF_OK) {
		wxString title = wxT("");
		if (com.track_info) {
			title.Format(wxT("%02d "), (u32) (com.track_info>>16) );
		}
		if (com.artist) {
			title.Append(wxString(com.artist, wxConvUTF8));
			title += wxT(" ");
		}
		if (com.name) {
			title.Append(wxString(com.name, wxConvUTF8));
			title += wxT(" ");
		}
		if (com.album) {
			title += wxT("(");
			title.Append(wxString(com.album, wxConvUTF8));
			title += wxT(")");
		}

		if (title.length()) SetTitle(title);
	}

}

void wxOsmo4Frame::OnChapterSel(wxCommandEvent & event)
{
	GF_ObjectManager *root_od = gf_term_get_root_object(m_term);
	if (!root_od) return;
	u32 ID = event.GetId() - ID_SETCHAP_FIRST;
	gf_term_play_from_time(m_term, (u32) (1000*m_chapters_start[ID]), 0);
}

void wxOsmo4Frame::OnUpdateChapterSel(wxUpdateUIEvent & event)
{
	Double now;
	Bool is_current;
	u32 ID = event.GetId() - ID_SETCHAP_FIRST;

	now = gf_term_get_time_in_ms(m_term);
	now /= 1000;

	is_current = 0;
	if (m_chapters_start[ID]<=now) {
		if (ID+1<m_num_chapters) {
			if (m_chapters_start[ID+1]>now) is_current = 1;
		} else {
			is_current = 1;
		}
	}
	event.Enable(1);
	event.Check(is_current ? 1 : 0);
}

void wxOsmo4Frame::OnUpdateChapterMenu(wxUpdateUIEvent & event)
{
	if (!m_connected || !m_num_chapters) {
		event.Enable(0);
	} else {
		event.Enable(1);
	}
}

void wxOsmo4Frame::OnFileCopy(wxCommandEvent &event)
{
	const char *text = gf_term_get_text_selection(m_term, 0);
	if (!text) return;
	if (!wxTheClipboard->Open()) return;

	wxTheClipboard->SetData( new wxTextDataObject( wxString(text, wxConvUTF8)) );
	wxTheClipboard->Close();
}

void wxOsmo4Frame::OnUpdateFileCopy(wxUpdateUIEvent &event)
{
	if (gf_term_get_text_selection(m_term, 1)!=NULL) {
		event.Enable(1);
	} else {
		event.Enable(0);
	}
}

void wxOsmo4Frame::OnFilePaste(wxCommandEvent &event)
{
	if (!wxTheClipboard->Open()) return;
	if (wxTheClipboard->IsSupported( wxDF_TEXT )) {
		wxTextDataObject data;
		wxTheClipboard->GetData(data);
		gf_term_paste_text(m_term, data.GetText().mb_str(wxConvUTF8), 0);
	}
	wxTheClipboard->Close();
}

void wxOsmo4Frame::OnUpdateFilePaste(wxUpdateUIEvent &event)
{
	Bool ok = 0;
	if (wxTheClipboard->Open()) {
		if (wxTheClipboard->IsSupported( wxDF_TEXT )) {
			if (gf_term_paste_text(m_term, NULL, 1)==GF_OK) {
				ok = 1;
			}
		}
		wxTheClipboard->Close();
	}
	event.Enable(ok ? 1 : 0);
}

