// // Copyright 2005 The Android Open Source Project // // Main window, menu bar, and associated goodies. // // For compilers that support precompilation, include "wx/wx.h". #include "wx/wxprec.h" // Otherwise, include all standard headers #ifndef WX_PRECOMP # include "wx/wx.h" #endif #include "wx/button.h" #include "wx/help.h" #include "wx/filedlg.h" #include "wx/slider.h" #include "wx/textctrl.h" #include "MainFrame.h" #include "MyApp.h" #include "Resource.h" #include "PhoneCollection.h" #include "PhoneData.h" #include "PhoneWindow.h" #include "DeviceWindow.h" #include "UserEventMessage.h" #include "PrefsDialog.h" #include "SimRuntime.h" static wxString kStatusNotRunning = wxT("Idle"); static wxString kStatusRunning = wxT("Run"); static wxString kDeviceMenuString = wxT("&Device"); static const wxString gStdJavaApps[] = { wxT(""), wxT("com.android.testharness.TestList"), wxT("com.android.apps.contacts.ContactsList"), wxT("mikeapp") }; BEGIN_EVENT_TABLE(MainFrame::MainFrame, wxFrame) EVT_CLOSE(MainFrame::OnClose) EVT_TIMER(kHalfSecondTimerId, MainFrame::OnTimer) //EVT_IDLE(MainFrame::OnIdle) EVT_ACTIVATE(MainFrame::OnActivate) EVT_ACTIVATE_APP(MainFrame::OnActivate) EVT_COMBOBOX(IDC_MODE_SELECT, MainFrame::OnComboBox) EVT_COMBOBOX(IDC_JAVA_VM, MainFrame::OnComboBox) EVT_CHECKBOX(IDC_USE_GDB, MainFrame::OnCheckBox) EVT_CHECKBOX(IDC_USE_VALGRIND, MainFrame::OnCheckBox) EVT_CHECKBOX(IDC_CHECK_JNI, MainFrame::OnCheckBox) EVT_CHECKBOX(IDC_OVERLAY_ONION_SKIN, MainFrame::OnCheckBox) EVT_TEXT(IDC_JAVA_APP_NAME, MainFrame::OnText) EVT_TEXT_ENTER(IDC_ONION_SKIN_FILE_NAME, MainFrame::OnTextEnter) EVT_BUTTON(IDC_ONION_SKIN_BUTTON, MainFrame::OnButton) EVT_COMMAND_SCROLL(IDC_ONION_SKIN_ALPHA_VAL, MainFrame::OnSliderChange) EVT_MENU(IDM_FILE_PREFERENCES, MainFrame::OnFilePreferences) EVT_MENU(IDM_FILE_EXIT, MainFrame::OnFileExit) EVT_MENU(IDM_RUNTIME_START, MainFrame::OnSimStart) EVT_UPDATE_UI(IDM_RUNTIME_START, MainFrame::OnUpdateSimStart) EVT_MENU(IDM_RUNTIME_STOP, MainFrame::OnSimStop) EVT_UPDATE_UI(IDM_RUNTIME_STOP, MainFrame::OnUpdateSimStop) EVT_MENU(IDM_RUNTIME_RESTART, MainFrame::OnSimRestart) EVT_UPDATE_UI(IDM_RUNTIME_RESTART, MainFrame::OnUpdateSimRestart) EVT_MENU(IDM_RUNTIME_KILL, MainFrame::OnSimKill) EVT_UPDATE_UI(IDM_RUNTIME_KILL, MainFrame::OnUpdateSimKill) EVT_MENU_RANGE(IDM_DEVICE_SEL0, IDM_DEVICE_SELN, MainFrame::OnDeviceSelected) EVT_MENU(IDM_DEVICE_RESCAN, MainFrame::OnDeviceRescan) EVT_UPDATE_UI(IDM_DEBUG_SHOW_LOG, MainFrame::OnUpdateDebugShowLog) EVT_MENU(IDM_DEBUG_SHOW_LOG, MainFrame::OnDebugShowLog) EVT_MENU(IDM_HELP_CONTENTS, MainFrame::OnHelpContents) EVT_MENU(IDM_HELP_ABOUT, MainFrame::OnHelpAbout) EVT_USER_EVENT(MainFrame::OnUserEvent) END_EVENT_TABLE() /* * Main window constructor. * * Creates menus and status bar. */ MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame((wxFrame *)NULL, -1, title, pos, size, style), mSimRunning(false), mRestartRequested(false), mpPhoneWindow(NULL), mPhoneWindowPosn(wxDefaultPosition), mTimer(this, kHalfSecondTimerId) { mSimAssetPath = ((MyApp*)wxTheApp)->GetSimAssetPath(); mSimAssetPath += wxT("/simulator/default/default"); Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); int val; val = mPhoneWindowPosn.x; pPrefs->GetInt("window-device-x", &val); mPhoneWindowPosn.x = val; val = mPhoneWindowPosn.y; pPrefs->GetInt("window-device-y", &val); mPhoneWindowPosn.y = val; /* * Create main menu. */ ConstructMenu(); /* * Create the status bar. */ int widths[2] = { -1, 50 }; CreateStatusBar(2, wxFULL_REPAINT_ON_RESIZE); // no wxST_SIZEGRIP SetStatusWidths(2, widths); SetStatusText(wxT("Ready")); SetStatusText(kStatusNotRunning, 1); /* * Create main window controls. */ ConstructControls(); #if 0 /* * Use the standard window color for the main frame (which usually * has a darker color). This has a dramatic effect under Windows. */ wxColour color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetOwnBackgroundColour(color); #endif /* * Create the log window. */ wxRect layout = LogWindow::GetPrefWindowRect(); mpLogWindow = new LogWindow(this); mpLogWindow->Move(layout.GetTopLeft()); mpLogWindow->SetSize(layout.GetSize()); bool showLogWindow = true; pPrefs->GetBool("window-log-show", &showLogWindow); if (showLogWindow) mpLogWindow->Show(); /* * Set up a frequent timer. We use this to keep our "run/idle" * display up to date. (Ideally this will go away.) */ mTimer.Start(400); // arg is delay in ms /* * Handle auto-power-on by sending ourselves an event. That way it * gets handled after window initialization finishes. */ bool autoPowerOn = false; pPrefs->GetBool("auto-power-on", &autoPowerOn); if (autoPowerOn) { printf("Sim: Auto power-up\n"); wxCommandEvent startEvent(wxEVT_COMMAND_MENU_SELECTED, IDM_RUNTIME_START); this->AddPendingEvent(startEvent); } /* * wxThread wants these to be on the heap -- it will call delete on the * object when the thread exits. */ mExternalRuntimeThread = new ExternalRuntime(); mExternalRuntimeThread->StartThread(); mPropertyServerThread = new PropertyServer(); mPropertyServerThread->StartThread(); } /* * Construct the main menu. Called from the constructor. */ void MainFrame::ConstructMenu(void) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); /* * Scan for available phones. */ PhoneCollection* pCollection = PhoneCollection::GetInstance(); pCollection->ScanForPhones(mSimAssetPath.ToAscii()); /* * Create the "File" menu. */ wxMenu* menuFile = new wxMenu; menuFile->Append(IDM_FILE_PREFERENCES, wxT("&Preferences..."), wxT("Edit simulator preferences")); menuFile->AppendSeparator(); menuFile->Append(IDM_FILE_EXIT, wxT("E&xit\tCtrl-Q"), wxT("Stop simulator and exit")); /* * Create the "Runtime" menu. */ wxMenu* menuRuntime = new wxMenu; menuRuntime->Append(IDM_RUNTIME_START, wxT("&Power On\tCtrl-G"), wxT("Start the device")); // menuRuntime->Append(IDM_RUNTIME_STOP, wxT("Power &Off"), // wxT("Stop the device")); menuRuntime->AppendSeparator(); // menuRuntime->Append(IDM_RUNTIME_RESTART, wxT("&Restart"), // wxT("Restart the device")); menuRuntime->Append(IDM_RUNTIME_KILL, wxT("&Kill\tCtrl-K"), wxT("Kill the runtime processes")); /* * Create "Device" menu. */ wxString defaultDevice = wxT("Sooner"); pPrefs->GetString("default-device", /*ref*/ defaultDevice); wxMenu* menuDevice = CreateDeviceMenu(defaultDevice.ToAscii()); /* * Create "Debug" menu. */ wxMenu* menuDebug = new wxMenu; menuDebug->AppendCheckItem(IDM_DEBUG_SHOW_LOG, wxT("View &Log Output"), wxT("View log output window")); /* * Create the "Help" menu. */ wxMenu* menuHelp = new wxMenu; menuHelp->Append(IDM_HELP_CONTENTS, wxT("&Contents...\tF1"), wxT("Simulator help")); menuHelp->AppendSeparator(); menuHelp->Append(IDM_HELP_ABOUT, wxT("&About..."), wxT("See the fabulous 'about' box")); /* * Create the menu bar. */ wxMenuBar *menuBar = new wxMenuBar; menuBar->Append(menuFile, wxT("&File")); menuBar->Append(menuDevice, kDeviceMenuString); menuBar->Append(menuRuntime, wxT("&Runtime")); menuBar->Append(menuDebug, wxT("&Debug")); menuBar->Append(menuHelp, wxT("&Help")); SetMenuBar(menuBar); } /* * Construct the "device" menu from our phone collection. */ wxMenu* MainFrame::CreateDeviceMenu(const char* defaultItemName) { wxMenu* menuDevice = new wxMenu; PhoneCollection* pCollection = PhoneCollection::GetInstance(); int defaultModel = 0; for (int i = 0; i < pCollection->GetPhoneCount(); i++) { PhoneData* pPhoneData = pCollection->GetPhoneData(i); assert(pPhoneData != NULL); menuDevice->AppendRadioItem(IDM_DEVICE_SEL0 + i, wxString::FromAscii(pPhoneData->GetTitle())); // use this one as default if the string matches if (strcasecmp(pPhoneData->GetName(), defaultItemName) == 0) defaultModel = i; } menuDevice->Check(IDM_DEVICE_SEL0 + defaultModel, true); menuDevice->AppendSeparator(); menuDevice->Append(IDM_DEVICE_RESCAN, wxT("Re-scan")); return menuDevice; } /* * Create some controls in the main window. * * The main frame doesn't use the normal background color that you find * in dialog windows, so we create a "panel" and put all the controls * on that. */ void MainFrame::ConstructControls(void) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); wxPanel* base = new wxPanel(this, wxID_ANY); wxBoxSizer* masterSizer = new wxBoxSizer(wxVERTICAL); wxBoxSizer* tmpSizer; wxStaticBoxSizer* displayOptSizer; wxStaticBoxSizer* runtimeOptSizer; wxStaticBoxSizer* onionSkinOptSizer; wxComboBox* pModeSelection; wxCheckBox* pUseGDB; wxCheckBox* pUseValgrind; wxCheckBox* pCheckJni; wxCheckBox* pOverlayOnionSkin; displayOptSizer = new wxStaticBoxSizer(wxHORIZONTAL, base, wxT("Configuration")); runtimeOptSizer = new wxStaticBoxSizer(wxVERTICAL, base, wxT("Runtime Options")); onionSkinOptSizer = new wxStaticBoxSizer(wxVERTICAL, base, wxT("Onion Skin Options")); /* * Set up the configuration sizer (nee "display options"). */ tmpSizer = new wxBoxSizer(wxHORIZONTAL); displayOptSizer->Add(tmpSizer); tmpSizer->Add( new wxStaticText(base, wxID_ANY, wxT("Device mode:"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), 0, wxALIGN_CENTER_VERTICAL); pModeSelection = new wxComboBox(base, IDC_MODE_SELECT, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); tmpSizer->AddSpacer(kInterSpacing); tmpSizer->Add(pModeSelection); displayOptSizer->AddSpacer(kInterSpacing); /* * Configure the runtime options sizer. */ wxComboBox* pJavaAppName; tmpSizer = new wxBoxSizer(wxHORIZONTAL); pUseGDB = new wxCheckBox(base, IDC_USE_GDB, wxT("Use &debugger")); tmpSizer->Add(pUseGDB); tmpSizer->AddSpacer(kInterSpacing); pUseValgrind = new wxCheckBox(base, IDC_USE_VALGRIND, wxT("Use &valgrind")); tmpSizer->Add(pUseValgrind); tmpSizer->AddSpacer(kInterSpacing); pCheckJni = new wxCheckBox(base, IDC_CHECK_JNI, wxT("Check &JNI")); tmpSizer->Add(pCheckJni); pJavaAppName = new wxComboBox(base, IDC_JAVA_APP_NAME, wxT(""), wxDefaultPosition, wxSize(320, -1), NELEM(gStdJavaApps), gStdJavaApps, wxCB_DROPDOWN); wxBoxSizer* javaAppSizer = new wxBoxSizer(wxHORIZONTAL); javaAppSizer->Add( new wxStaticText(base, wxID_ANY, wxT("Java app:"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), 0, wxALIGN_CENTER_VERTICAL); javaAppSizer->AddSpacer(kInterSpacing); javaAppSizer->Add(pJavaAppName); runtimeOptSizer->Add(tmpSizer); runtimeOptSizer->AddSpacer(kInterSpacing); runtimeOptSizer->Add(javaAppSizer); runtimeOptSizer->AddSpacer(kInterSpacing); wxString tmpStr; SetCheckFromPref(pUseGDB, "debug", false); SetCheckFromPref(pUseValgrind, "valgrind", false); SetCheckFromPref(pCheckJni, "check-jni", false); if (pPrefs->GetString("java-app-name", /*ref*/ tmpStr)) pJavaAppName->SetValue(tmpStr); /* * Configure the onion skin options sizer. */ wxTextCtrl* pOnionSkinFileNameText; wxButton* pOnionSkinFileButton; wxSlider* pOnionSkinAlphaSlider; tmpSizer = new wxBoxSizer(wxHORIZONTAL); pOverlayOnionSkin = new wxCheckBox(base, IDC_OVERLAY_ONION_SKIN, wxT("Overlay &onion skin")); tmpSizer->Add(pOverlayOnionSkin); pOnionSkinFileNameText = new wxTextCtrl(base, IDC_ONION_SKIN_FILE_NAME, wxT(""), wxDefaultPosition, wxSize(250, -1), wxTE_PROCESS_ENTER); pOnionSkinFileButton = new wxButton(base, IDC_ONION_SKIN_BUTTON, wxT("Choose")); wxBoxSizer* onionSkinFileNameSizer = new wxBoxSizer(wxHORIZONTAL); onionSkinFileNameSizer->Add( new wxStaticText(base, wxID_ANY, wxT("Filename:"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), 0, wxALIGN_CENTER_VERTICAL); onionSkinFileNameSizer->AddSpacer(kInterSpacing); onionSkinFileNameSizer->Add(pOnionSkinFileNameText); onionSkinFileNameSizer->Add(pOnionSkinFileButton); wxBoxSizer * onionSkinAlphaSizer = new wxBoxSizer(wxHORIZONTAL); int initialAlphaVal = 127; pPrefs->GetInt("onion-skin-alpha-value", &initialAlphaVal); pOnionSkinAlphaSlider = new wxSlider(base, IDC_ONION_SKIN_ALPHA_VAL, initialAlphaVal, 0, 255, wxDefaultPosition, wxSize(150, 20)); onionSkinAlphaSizer->Add( new wxStaticText(base, wxID_ANY, wxT("Transparency:"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), 0, wxALIGN_CENTER_VERTICAL); onionSkinAlphaSizer->AddSpacer(kInterSpacing); onionSkinAlphaSizer->Add(pOnionSkinAlphaSlider, 1, wxCENTRE | wxALL, 5); onionSkinOptSizer->Add(tmpSizer); onionSkinOptSizer->AddSpacer(kInterSpacing); onionSkinOptSizer->Add(onionSkinFileNameSizer); onionSkinOptSizer->Add(onionSkinAlphaSizer); wxString tmpStr2; SetCheckFromPref(pOverlayOnionSkin, "overlay-onion-skin", false); if (pPrefs->GetString("onion-skin-file-name", /*ref*/ tmpStr2)) pOnionSkinFileNameText->SetValue(tmpStr2); /* * Add the various components to the master sizer. */ masterSizer->Add(displayOptSizer); masterSizer->AddSpacer(kInterSpacing * 2); masterSizer->Add(runtimeOptSizer); masterSizer->AddSpacer(kInterSpacing * 2); masterSizer->Add(onionSkinOptSizer); //masterSizer->AddSpacer(kInterSpacing); /* * I don't see a way to guarantee that the window is wide enough to * show the entire menu bar, so just throw some pixels at it. */ wxBoxSizer* minWidthSizer = new wxBoxSizer(wxVERTICAL); minWidthSizer->Add(300, kEdgeSpacing); // forces minimum width minWidthSizer->Add(masterSizer); minWidthSizer->AddSpacer(kInterSpacing * 2); /* move us a few pixels in from the left */ wxBoxSizer* indentSizer = new wxBoxSizer(wxHORIZONTAL); indentSizer->AddSpacer(kEdgeSpacing); indentSizer->Add(minWidthSizer); indentSizer->AddSpacer(kEdgeSpacing); base->SetSizer(indentSizer); indentSizer->Fit(this); indentSizer->SetSizeHints(this); } /* * Set the value of a checkbox based on a value from the config file. */ void MainFrame::SetCheckFromPref(wxCheckBox* pControl, const char* prefStr, bool defaultVal) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); assert(pPrefs != NULL); bool val = defaultVal; pPrefs->GetBool(prefStr, &val); pControl->SetValue(val); } /* * Destructor. */ MainFrame::~MainFrame(void) { PhoneCollection::DestroyInstance(); delete mExternalRuntimeThread; delete mPropertyServerThread; // don't touch mpModeSelection -- child of window } /* * File->Quit or click on close box. * * If we want an "are you sure you want to quit" box, add it here. */ void MainFrame::OnClose(wxCloseEvent& event) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); /* if (event.CanVeto()) printf("Closing (can veto)\n"); else printf("Closing (mandatory)\n"); */ /* * Generally speaking, Close() is not guaranteed to close the window. * However, we want to use it here because (a) our windows are * guaranteed to close, and (b) it provides our windows an opportunity * to tell others that they are about to vanish. */ if (mpPhoneWindow != NULL) mpPhoneWindow->Close(true); /* save position of main window */ wxPoint pos = GetPosition(); pPrefs->SetInt("window-main-x", pos.x); pPrefs->SetInt("window-main-y", pos.y); /* save default device selection */ int idx = GetSelectedDeviceIndex(); if (idx >= 0) { PhoneCollection* pCollection = PhoneCollection::GetInstance(); PhoneData* pPhoneData = pCollection->GetPhoneData(idx); pPrefs->SetString("default-device", pPhoneData->GetName()); } if (mpLogWindow != NULL) mpLogWindow->Close(true); Destroy(); } /* * File->Preferences */ void MainFrame::OnFilePreferences(wxCommandEvent& WXUNUSED(event)) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); PrefsDialog dialog(this); int result; result = dialog.ShowModal(); if (result == wxID_OK) { /* * The dialog handles writing changes to Preferences, so all we * need to deal with here are changes that have an immediate * impact on us. (which is currently nothing) */ } } /* * File->Exit */ void MainFrame::OnFileExit(wxCommandEvent& WXUNUSED(event)) { Close(FALSE); // false means "allow veto" } /* * Decide whether Simulator->Start should be enabled. */ void MainFrame::OnUpdateSimStart(wxUpdateUIEvent& event) { if (IsRuntimeRunning()) event.Enable(FALSE); else event.Enable(TRUE); } /* * Simulator->Start */ void MainFrame::OnSimStart(wxCommandEvent& WXUNUSED(event)) { // keyboard equivalents can still get here even if menu item disabled if (IsRuntimeRunning()) return; int id = GetSelectedDeviceIndex(); if (id < 0) { fprintf(stderr, "Sim: could not identify currently selected device\n"); return; } #if 0 static int foo = 0; foo++; if (foo == 2) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); pPrefs->SetBool("debug", true); } #endif SetupPhoneUI(id, NULL); if (mpPhoneWindow != NULL) mpPhoneWindow->GetDeviceManager()->StartRuntime(); } /* * Decide whether Simulator->Stop should be enabled. */ void MainFrame::OnUpdateSimStop(wxUpdateUIEvent& event) { if (IsRuntimeRunning()) event.Enable(TRUE); else event.Enable(FALSE); } /* * Simulator->Stop */ void MainFrame::OnSimStop(wxCommandEvent& WXUNUSED(event)) { if (!IsRuntimeRunning()) return; assert(mpPhoneWindow != NULL); mpPhoneWindow->GetDeviceManager()->StopRuntime(); } /* * Decide whether Simulator->Restart should be enabled. */ void MainFrame::OnUpdateSimRestart(wxUpdateUIEvent& event) { if (IsRuntimeRunning()) event.Enable(TRUE); else event.Enable(FALSE); } /* * Simulator->Restart - stop then start the device runtime. */ void MainFrame::OnSimRestart(wxCommandEvent& WXUNUSED(event)) { if (!IsRuntimeRunning()) return; printf("Restart requested\n"); mpPhoneWindow->GetDeviceManager()->StopRuntime(); mRestartRequested = true; } /* * Decide whether Simulator->Kill should be enabled. */ void MainFrame::OnUpdateSimKill(wxUpdateUIEvent& event) { if (IsRuntimeKillable()) event.Enable(TRUE); else event.Enable(FALSE); } /* * Simulator->Kill */ void MainFrame::OnSimKill(wxCommandEvent& WXUNUSED(event)) { if (!IsRuntimeKillable()) return; assert(mpPhoneWindow != NULL); mpPhoneWindow->GetDeviceManager()->KillRuntime(); } /* * Device->[select] */ void MainFrame::OnDeviceSelected(wxCommandEvent& event) { wxBusyCursor busyc; int id = event.GetId() - IDM_DEVICE_SEL0; SetupPhoneUI(id, NULL); } /* * Device->Rescan */ void MainFrame::OnDeviceRescan(wxCommandEvent& event) { wxBusyCursor busyc; wxMenuBar* pMenuBar; PhoneCollection* pCollection; wxMenu* pOldMenu; wxMenu* pNewMenu; const char* curDevName = NULL; int idx; /* figure out the current device name */ pCollection = PhoneCollection::GetInstance(); idx = GetSelectedDeviceIndex(); if (idx >= 0) { PhoneData* pPhoneData; pPhoneData = pCollection->GetPhoneData(idx); curDevName = pPhoneData->GetName(); printf("--- device name is '%s'\n", (const char*) curDevName); } /* reconstruct device menu with new data */ #ifdef BEFORE_ASSET pCollection->ScanForPhones(mSimAssetPath); #else pCollection->ScanForPhones(NULL); #endif pMenuBar = GetMenuBar(); idx = pMenuBar->FindMenu(kDeviceMenuString); if (idx == wxNOT_FOUND) { fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii()); return; } pNewMenu = CreateDeviceMenu(curDevName); pOldMenu = pMenuBar->Replace(idx, pNewMenu, kDeviceMenuString); delete pOldMenu; /* tell the PhoneWindow about it; may cause runtime to exit */ if (mpPhoneWindow != NULL) mpPhoneWindow->DevicesRescanned(); } /* * Set checkbox on menu item. */ void MainFrame::OnUpdateDebugShowLog(wxUpdateUIEvent& event) { if (mpLogWindow == NULL) { event.Enable(false); } else { event.Enable(true); event.Check(mpLogWindow->IsShown()); } } /* * Debug->ShowLog toggle. */ void MainFrame::OnDebugShowLog(wxCommandEvent& WXUNUSED(event)) { mpLogWindow->Show(!mpLogWindow->IsShown()); } /* * Help->Contents */ void MainFrame::OnHelpContents(wxCommandEvent& WXUNUSED(event)) { ((MyApp*)wxTheApp)->GetHelpController()->DisplayContents(); } /* * Help->About */ void MainFrame::OnHelpAbout(wxCommandEvent& WXUNUSED(event)) { wxMessageBox(wxT("Android Simulator v0.1\n" "Copyright 2006 The Android Open Source Project"), wxT("About..."), wxOK | wxICON_INFORMATION, this); } /* * Sent from phonewindow or when activated */ void MainFrame::OnActivate(wxActivateEvent& event) { #if 0 if (event.GetActive()) { if (mpPhoneWindow != NULL && mpPhoneWindow->GetDeviceManager()->RefreshRuntime()) { wxString msg; int sel; msg = wxT("Newer runtime executable found. Would you like to reload the device?"); sel = wxMessageBox(msg, wxT("Android Safety Patrol"), wxYES | wxNO | wxICON_QUESTION, mpPhoneWindow); //printf("BUTTON was %d (yes=%d)\n", sel, wxYES); if (sel == wxYES) { mpPhoneWindow->GetDeviceManager()->StopRuntime(); mpPhoneWindow->Close(); mpPhoneWindow = NULL; mRestartRequested = true; } else { mpPhoneWindow->GetDeviceManager()->UserCancelledRefresh(); } } } #endif // let wxWidgets do whatever it needs to do event.Skip(); } /* * Device mode selection box. */ void MainFrame::OnComboBox(wxCommandEvent& event) { const char* pref; Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); assert(pPrefs != NULL); if (IDC_MODE_SELECT == event.GetId()) { int id = GetSelectedDeviceIndex(); if (id < 0) return; //printf("--- mode selected: '%s'\n", (const char*) event.GetString().ToAscii()); /* * Call the phone window's setup function. Don't call our SetupPhoneUI * function from here -- updating the combo box from a combo box callback * could cause problems. */ if (mpPhoneWindow != NULL) { mpPhoneWindow->SetCurrentMode(event.GetString()); mpPhoneWindow->Setup(id); } } else if (event.GetId() == IDC_JAVA_VM) { wxComboBox* pBox = (wxComboBox*) FindWindow(IDC_JAVA_VM); pPrefs->SetString("java-vm", pBox->GetValue().ToAscii()); } } /* * One of our option checkboxes has been changed. * * We update the prefs database so that the settings are retained when * the simulator is next used. */ void MainFrame::OnCheckBox(wxCommandEvent& event) { const char* pref; switch (event.GetId()) { case IDC_USE_GDB: pref = "debug"; break; case IDC_USE_VALGRIND: pref = "valgrind"; break; case IDC_CHECK_JNI: pref = "check-jni"; break; case IDC_OVERLAY_ONION_SKIN: pref = "overlay-onion-skin"; break; default: printf("Sim: unrecognized checkbox %d in OnCheckBox\n", event.GetId()); return; } Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); assert(pPrefs != NULL); pPrefs->SetBool(pref, (bool) event.GetInt()); //printf("--- set pref '%s' to %d\n", pref, (bool) event.GetInt()); if (event.GetId() == IDC_OVERLAY_ONION_SKIN) { BroadcastOnionSkinUpdate(); } if (event.GetId() == IDC_CHECK_JNI) { const char* val = "0"; if ((bool) event.GetInt()) val = "1"; mPropertyServerThread->SetProperty(PropertyServer::kPropCheckJni, val); } } void MainFrame::BroadcastOnionSkinUpdate() { if (mpPhoneWindow != NULL) { // broadcast a user event indicating an onion skin update UserEvent uev(0, (void*) -1); mpPhoneWindow->GetDeviceManager()->BroadcastEvent(uev); } } /* * A text control on the main page is being updated. * * The current implementation updates the preferences database on every * change, which is a bit silly but is easy to do. */ void MainFrame::OnText(wxCommandEvent& event) { const char* pref; switch (event.GetId()) { case IDC_JAVA_APP_NAME: pref = "java-app-name"; break; default: printf("Sim: unrecognized textctrl %d in OnText\n", event.GetId()); return; } Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); assert(pPrefs != NULL); // event.GetString() does not work on Mac -- always blank //pPrefs->SetString(pref, event.GetString()); assert(event.GetId() == IDC_JAVA_APP_NAME); // fix if we add more wxComboBox* pBox; pBox = (wxComboBox*) FindWindow(IDC_JAVA_APP_NAME); pPrefs->SetString(pref, pBox->GetValue().ToAscii()); //printf("--- set pref '%s' to '%s'\n", pref,(const char*)pBox->GetValue()); } /* * A user pressed enter in a text control on the main page. * * The current implementation updates the preferences database on every * change, which is a bit silly but is easy to do. */ void MainFrame::OnTextEnter(wxCommandEvent& event) { const char* pref; switch (event.GetId()) { case IDC_ONION_SKIN_FILE_NAME: pref = "onion-skin-file-name"; break; default: printf("Sim: unrecognized textctrl %d in OnTextEnter\n", event.GetId()); return; } Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); assert(pPrefs != NULL); assert(event.GetId() == IDC_ONION_SKIN_FILE_NAME); // fix if we add more wxTextCtrl* pTextCtrl; pTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME); wxString onionSkinFileNameWxString = pTextCtrl->GetValue(); char* onionSkinFileName = ""; if (onionSkinFileNameWxString.Len() > 0) { onionSkinFileName = android::strdupNew(onionSkinFileNameWxString.ToAscii()); } pPrefs->SetString(pref, onionSkinFileName); BroadcastOnionSkinUpdate(); } /* * A user pressed a button on the main page * */ void MainFrame::OnButton(wxCommandEvent& event) { wxWindow* base; wxFileDialog* pOnionSkinFileChooser; int retVal; switch (event.GetId()) { case IDC_ONION_SKIN_BUTTON: base = FindWindow(IDC_ONION_SKIN_BUTTON)->GetParent(); pOnionSkinFileChooser = new wxFileDialog(base, wxT("Choose the onion skin image file."), wxT(""), wxT(""), wxT("*.*"), wxOPEN | wxFILE_MUST_EXIST); retVal = pOnionSkinFileChooser->ShowModal(); if (retVal == pOnionSkinFileChooser->GetAffirmativeId()) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); assert(pPrefs != NULL); wxString fileNameWxString = pOnionSkinFileChooser->GetPath(); const char* fileName = android::strdupNew(fileNameWxString.ToAscii()); wxTextCtrl* fileTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME); fileTextCtrl->SetValue(fileNameWxString); pPrefs->SetString("onion-skin-file-name", fileName); BroadcastOnionSkinUpdate(); } break; default: printf("Sim: unrecognized button %d in OnButton\n", event.GetId()); return; } } /* * The user moved a slider on the main page */ void MainFrame::OnSliderChange(wxScrollEvent& event) { wxSlider* pSlider; Preferences* pPrefs; switch (event.GetId()) { case IDC_ONION_SKIN_ALPHA_VAL: pSlider = (wxSlider*) FindWindow(IDC_ONION_SKIN_ALPHA_VAL); pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); assert(pPrefs != NULL); pPrefs->SetInt("onion-skin-alpha-value", pSlider->GetValue()); BroadcastOnionSkinUpdate(); break; default: printf("Sim: unrecognized scroller or slider %d in OnSliderChange\n", event.GetId()); return; } } #if 0 /* * Idle processing. Under wxWidgets this only called once after UI * activity unless you call event.RequestMore(). */ void MainFrame::OnIdle(wxIdleEvent& event) { event.Skip(); // let base class handler do stuff } #endif /* * Handle the timer. * * This is being called in the main thread, so multithreading with the * rest of MainFrame isn't a concern here. */ void MainFrame::OnTimer(wxTimerEvent& event) { bool status; /* * Check to see if the runtime died without telling us. This can only * happen if we forcibly kill our thread. We shouldn't really be * doing that anymore, but keep this in for now just in case. */ status = IsRuntimeRunning(); if (mSimRunning != status) { if (!status) { printf("Sim: fixed mSimRunning=%d actual=%d\n", mSimRunning, status); mSimRunning = status; if (!status) HandleRuntimeStop(); } else { /* * This was happening when we were shutting down but the * device management thread hadn't completely gone away. The * simple IsRunning test passes, so we get a false positive. * Ignore it. */ } } if (gWantToKill) { if (IsRuntimeRunning()) { printf("Sim: handling kill request\n"); mpPhoneWindow->GetDeviceManager()->KillRuntime(); } gWantToKill = false; /* see if Ctrl-C should kill us too */ Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); bool die = false; pPrefs->GetBool("trap-sigint-suicide", &die); if (die) { printf("Sim: goodbye cruel world!\n"); exit(0); } } } /* * Determine whether or not the simulator is running. */ bool MainFrame::IsRuntimeRunning(void) { bool result; if (mpPhoneWindow == NULL) result = false; else if (!mpPhoneWindow->IsReady()) result = false; else result = mpPhoneWindow->GetDeviceManager()->IsRunning(); return result; } /* * Determine whether or not the runtime can be killed. */ bool MainFrame::IsRuntimeKillable(void) { bool result; result = IsRuntimeRunning(); if (result) result = mpPhoneWindow->GetDeviceManager()->IsKillable(); return result; } /* * Determine whether two devices are sufficiently compatible. */ bool MainFrame::CompatibleDevices(PhoneData* pData1, PhoneData* pData2) { int displayCount; displayCount = pData1->GetNumDisplays(); if (pData2->GetNumDisplays() != displayCount) return false; for (int i = 0; i < displayCount; i++) { PhoneDisplay* pDisplay1 = pData1->GetPhoneDisplay(i); PhoneDisplay* pDisplay2 = pData2->GetPhoneDisplay(i); if (!PhoneDisplay::IsCompatible(pDisplay1, pDisplay2)) return false; } return true; } /* * (Re-)arrange the UI for the currently selected phone model. * * If the simulator is running, and the set of displays for the current * device are incompatible with the new device, we need to restart the * runtime. We need to ask for permission first though. */ void MainFrame::SetupPhoneUI(int idx, const char* defaultMode) { PhoneCollection* pCollection; PhoneData* pPhoneData; wxString* choices = NULL; int numChoices = 0; int numKeyboards = 0; bool haveDefaultMode = false; wxCharBuffer currentMode; int i; pCollection = PhoneCollection::GetInstance(); pPhoneData = pCollection->GetPhoneData(idx); if (pPhoneData == NULL) { fprintf(stderr, "ERROR: device index %d not valid\n", idx); goto bail; } /* * We have a window up. If the displays aren't compatible, we'll * need to recreate it. */ if (mpPhoneWindow != NULL) { PhoneData* pCurData = mpPhoneWindow->GetPhoneData(); if (!CompatibleDevices(pCurData, pPhoneData)) { /* * We need to trash the window. This will also kill the * runtime. If it's running, ask permission. */ if (IsRuntimeRunning()) { wxString msg; int sel; msg = wxT("Switching to the new device requires restarting the"); msg += wxT(" runtime. Continue?"); sel = wxMessageBox(msg, wxT("Android Safety Patrol"), wxOK | wxCANCEL | wxICON_QUESTION, this); printf("BUTTON was %d (ok=%d)\n", sel, wxOK); if (sel == wxCANCEL) goto bail; /* shut it down (politely), ask for an eventual restart */ mpPhoneWindow->GetDeviceManager()->StopRuntime(); mpPhoneWindow->Close(); mpPhoneWindow = NULL; mRestartRequested = true; goto bail; } else { /* not running, just trash the window and continue */ mpPhoneWindow->Close(); mpPhoneWindow = NULL; } } } /* * Figure out the set of available modes. */ numChoices = pPhoneData->GetNumModes(); if (numChoices > 0) { choices = new wxString[numChoices]; for (i = 0; i < numChoices; i++) { PhoneMode* pPhoneMode; pPhoneMode = pPhoneData->GetPhoneMode(i); choices[i] = wxString::FromAscii(pPhoneMode->GetName()); if (defaultMode != NULL && strcmp(defaultMode, pPhoneMode->GetName()) == 0) { haveDefaultMode = true; } } } if (choices == NULL) { /* had a failure earlier; configure UI with default stuff */ choices = new wxString[1]; choices[0] = wxT("(none)"); } if (!haveDefaultMode) { /* * Default mode wasn't found. If we specify it as the default * in the wxComboBox create call it shows up in the combo box * under Linux, even if it doesn't exist in the list. So, we * make sure that it doesn't get used if we can't find it. */ if (defaultMode != NULL) { printf("Sim: HEY: default mode '%s' not found in list\n", defaultMode); } currentMode = choices[0].ToAscii(); } else { currentMode = defaultMode; } /* * Create the window if necessary. */ if (mpPhoneWindow == NULL) { // create, setup, and then show window mpPhoneWindow = new PhoneWindow(this, mPhoneWindowPosn); mpPhoneWindow->SetCurrentMode((const char*)currentMode); if (!mpPhoneWindow->Setup(idx)) { delete mpPhoneWindow; mpPhoneWindow = NULL; } if (mpPhoneWindow != NULL) { mpPhoneWindow->Show(); //mpPhoneWindow->CheckPlacement(); } } else { // just set up for new device mpPhoneWindow->SetCurrentMode((const char*)currentMode); if (!mpPhoneWindow->Setup(idx)) { // it's in an uncertain state, blow it away delete mpPhoneWindow; mpPhoneWindow = NULL; } } /* * Reconfigure mode selection box. */ wxComboBox* pModeSelection; pModeSelection = (wxComboBox*)FindWindow(IDC_MODE_SELECT); pModeSelection->Clear(); for (i = 0; i < numChoices; i++) pModeSelection->Append(choices[i]); pModeSelection->SetSelection(0); pModeSelection->Enable(numChoices > 1); /* * configure qwerty keyboard attribute */ numKeyboards = pPhoneData->GetNumKeyboards(); if (numKeyboards > 0) { // only use the first keyboard for now PhoneKeyboard* pPhoneKeyboard; pPhoneKeyboard = pPhoneData->GetPhoneKeyboard(0); if (pPhoneKeyboard->getQwerty()) { printf("Sim: set 'qwerty' env\n"); setenv("qwerty", "true", true); } } bail: delete[] choices; } /* * Figure out which device is currently selected. * * The easiest way to do this is just run down the list of possible IDs * and stop when something claims to be checked. * * Returns -1 if it can't find a checked item (which can happen if no * device layouts were found). */ int MainFrame::GetSelectedDeviceIndex(void) { wxMenuBar* pMenuBar; wxMenu* pMenu; int idx; pMenuBar = GetMenuBar(); idx = pMenuBar->FindMenu(kDeviceMenuString); if (idx == wxNOT_FOUND) { fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii()); return -1; } pMenu = pMenuBar->GetMenu(idx); //printf("Menu.MenuItemCount = %d\n", pMenu->GetMenuItemCount()); for (int j = pMenu->GetMenuItemCount() -1; j >= 0; j--) { wxMenuItem* pItem; pItem = pMenu->FindItemByPosition(j); //printf("ITEM %d: %s\n", j, (const char*) pItem->GetLabel()); if (pItem->IsChecked()) { printf("Sim: selected device is '%s'\n", (const char*) pItem->GetLabel().ToAscii()); return j; } } return -1; } /* * Receive a status message from the runtime thread. */ void MainFrame::OnUserEvent(UserEvent& event) { UserEventMessage* pUem; pUem = (UserEventMessage*) event.GetData(); assert(pUem != NULL); switch (pUem->GetType()) { case UserEventMessage::kRuntimeStarted: printf("Sim: runtime thread started!\n"); HandleRuntimeStart(); break; case UserEventMessage::kRuntimeStopped: printf("Sim: runtime thread stopped!\n"); HandleRuntimeStop(); break; case UserEventMessage::kErrorMessage: { wxString msg = pUem->GetString(); wxMessageBox(msg, wxT("Android Runtime Error"), wxOK | wxICON_WARNING, this); } break; case UserEventMessage::kLogMessage: mpLogWindow->AddLogMessage(pUem->GetLogMessage()); break; case UserEventMessage::kExternalRuntime: HandleExternalRuntime(pUem->GetReader(), pUem->GetWriter()); break; default: printf("Sim: MESSAGE: unknown UserEventMessage rcvd (type=%d)\n", pUem->GetType()); break; } delete pUem; } /* * The device management thread is up, so the runtime should be fully * running shortly. */ void MainFrame::HandleRuntimeStart(void) { mSimRunning = true; SetStatusText(kStatusRunning, 1); } /* * The device management thread is exiting, so the runtime must be dead. */ void MainFrame::HandleRuntimeStop(void) { mSimRunning = false; SetStatusText(kStatusNotRunning, 1); if (mRestartRequested) { printf("Sim: restarting runtime\n"); mRestartRequested = false; SetupPhoneUI(GetSelectedDeviceIndex(), NULL); if (mpPhoneWindow != NULL) mpPhoneWindow->GetDeviceManager()->StartRuntime(); } } /* * Handle a connection from an external runtime. */ void MainFrame::HandleExternalRuntime(android::Pipe* reader, android::Pipe* writer) { android::MessageStream msgStream; android::Message msg; if (IsRuntimeRunning()) { /* * Tell the new guy to go away. */ if (!msgStream.init(reader, writer, true)) { fprintf(stderr, "Sim: WARNING: unable to talk to remote runtime\n"); goto bail; } printf("Sim: telling external runtime to go away\n"); msg.setCommand(android::Simulator::kCommandGoAway, 0); msgStream.send(&msg); } else { printf("Sim: new external runtime wants to talk to us\n"); /* * Launch the pieces necessary to talk to this guy. */ int id = GetSelectedDeviceIndex(); if (id < 0) { fprintf(stderr, "Sim: could not identify currently selected device\n"); goto bail; } /* kill existing window, so it pops up and reclaims focus */ if (mpPhoneWindow != NULL) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); bool okay; if (pPrefs->GetBool("refocus-on-restart", &okay) && okay) { printf("Sim: inducing phone window refocus\n"); mpPhoneWindow->Close(TRUE); // no veto mpPhoneWindow = NULL; } } SetupPhoneUI(id, NULL); if (mpPhoneWindow != NULL) { mpPhoneWindow->GetDeviceManager()->StartRuntime(reader, writer); } else { fprintf(stderr, "Sim: ERROR: unable to get runtime going\n"); goto bail; } // we don't own these anymore reader = writer = NULL; } bail: delete reader; delete writer; } /* * The phone window is about to destroy itself. Get rid of our pointer * to it, and record its last position so we can create the new one in * the same place. */ void MainFrame::PhoneWindowClosing(int x, int y) { Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs(); mpPhoneWindow = NULL; mPhoneWindowPosn.x = x; mPhoneWindowPosn.y = y; pPrefs->SetInt("window-device-x", x); pPrefs->SetInt("window-device-y", y); }