| // | 
 | // 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); | 
 | } | 
 |  |