Home | History | Annotate | Download | only in app
      1 //
      2 // Copyright 2005 The Android Open Source Project
      3 //
      4 // Main window, menu bar, and associated goodies.
      5 //
      6 
      7 // For compilers that support precompilation, include "wx/wx.h".
      8 #include "wx/wxprec.h"
      9 
     10 // Otherwise, include all standard headers
     11 #ifndef WX_PRECOMP
     12 # include "wx/wx.h"
     13 #endif
     14 #include "wx/button.h"
     15 #include "wx/help.h"
     16 #include "wx/filedlg.h"
     17 #include "wx/slider.h"
     18 #include "wx/textctrl.h"
     19 
     20 #include "MainFrame.h"
     21 #include "MyApp.h"
     22 #include "Resource.h"
     23 #include "PhoneCollection.h"
     24 #include "PhoneData.h"
     25 #include "PhoneWindow.h"
     26 #include "DeviceWindow.h"
     27 #include "UserEventMessage.h"
     28 #include "PrefsDialog.h"
     29 
     30 #include "SimRuntime.h"
     31 
     32 
     33 static wxString kStatusNotRunning = wxT("Idle");
     34 static wxString kStatusRunning = wxT("Run");
     35 
     36 static wxString kDeviceMenuString = wxT("&Device");
     37 
     38 static const wxString gStdJavaApps[] = {
     39     wxT(""),
     40     wxT("com.android.testharness.TestList"),
     41     wxT("com.android.apps.contacts.ContactsList"),
     42     wxT("mikeapp")
     43 };
     44 
     45 
     46 BEGIN_EVENT_TABLE(MainFrame::MainFrame, wxFrame)
     47     EVT_CLOSE(MainFrame::OnClose)
     48     EVT_TIMER(kHalfSecondTimerId, MainFrame::OnTimer)
     49     //EVT_IDLE(MainFrame::OnIdle)
     50 
     51     EVT_ACTIVATE(MainFrame::OnActivate)
     52     EVT_ACTIVATE_APP(MainFrame::OnActivate)
     53     EVT_COMBOBOX(IDC_MODE_SELECT, MainFrame::OnComboBox)
     54     EVT_COMBOBOX(IDC_JAVA_VM, MainFrame::OnComboBox)
     55     EVT_CHECKBOX(IDC_USE_GDB, MainFrame::OnCheckBox)
     56     EVT_CHECKBOX(IDC_USE_VALGRIND, MainFrame::OnCheckBox)
     57     EVT_CHECKBOX(IDC_CHECK_JNI, MainFrame::OnCheckBox)
     58     EVT_CHECKBOX(IDC_OVERLAY_ONION_SKIN, MainFrame::OnCheckBox)
     59     EVT_TEXT(IDC_JAVA_APP_NAME, MainFrame::OnText)
     60     EVT_TEXT_ENTER(IDC_ONION_SKIN_FILE_NAME, MainFrame::OnTextEnter)
     61     EVT_BUTTON(IDC_ONION_SKIN_BUTTON, MainFrame::OnButton)
     62     EVT_COMMAND_SCROLL(IDC_ONION_SKIN_ALPHA_VAL, MainFrame::OnSliderChange)
     63 
     64     EVT_MENU(IDM_FILE_PREFERENCES, MainFrame::OnFilePreferences)
     65     EVT_MENU(IDM_FILE_EXIT, MainFrame::OnFileExit)
     66     EVT_MENU(IDM_RUNTIME_START, MainFrame::OnSimStart)
     67     EVT_UPDATE_UI(IDM_RUNTIME_START, MainFrame::OnUpdateSimStart)
     68     EVT_MENU(IDM_RUNTIME_STOP, MainFrame::OnSimStop)
     69     EVT_UPDATE_UI(IDM_RUNTIME_STOP, MainFrame::OnUpdateSimStop)
     70     EVT_MENU(IDM_RUNTIME_RESTART, MainFrame::OnSimRestart)
     71     EVT_UPDATE_UI(IDM_RUNTIME_RESTART, MainFrame::OnUpdateSimRestart)
     72     EVT_MENU(IDM_RUNTIME_KILL, MainFrame::OnSimKill)
     73     EVT_UPDATE_UI(IDM_RUNTIME_KILL, MainFrame::OnUpdateSimKill)
     74     EVT_MENU_RANGE(IDM_DEVICE_SEL0, IDM_DEVICE_SELN,
     75         MainFrame::OnDeviceSelected)
     76     EVT_MENU(IDM_DEVICE_RESCAN, MainFrame::OnDeviceRescan)
     77     EVT_UPDATE_UI(IDM_DEBUG_SHOW_LOG, MainFrame::OnUpdateDebugShowLog)
     78     EVT_MENU(IDM_DEBUG_SHOW_LOG, MainFrame::OnDebugShowLog)
     79     EVT_MENU(IDM_HELP_CONTENTS, MainFrame::OnHelpContents)
     80     EVT_MENU(IDM_HELP_ABOUT, MainFrame::OnHelpAbout)
     81 
     82     EVT_USER_EVENT(MainFrame::OnUserEvent)
     83 END_EVENT_TABLE()
     84 
     85 
     86 /*
     87  * Main window constructor.
     88  *
     89  * Creates menus and status bar.
     90  */
     91 MainFrame::MainFrame(const wxString& title, const wxPoint& pos,
     92     const wxSize& size, long style)
     93     : wxFrame((wxFrame *)NULL, -1, title, pos, size, style),
     94       mSimRunning(false),
     95       mRestartRequested(false),
     96       mpPhoneWindow(NULL),
     97       mPhoneWindowPosn(wxDefaultPosition),
     98       mTimer(this, kHalfSecondTimerId)
     99 {
    100     mSimAssetPath = ((MyApp*)wxTheApp)->GetSimAssetPath();
    101     mSimAssetPath += wxT("/simulator/default/default");
    102 
    103     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    104     int val;
    105 
    106     val = mPhoneWindowPosn.x;
    107     pPrefs->GetInt("window-device-x", &val);
    108     mPhoneWindowPosn.x = val;
    109     val = mPhoneWindowPosn.y;
    110     pPrefs->GetInt("window-device-y", &val);
    111     mPhoneWindowPosn.y = val;
    112 
    113     /*
    114      * Create main menu.
    115      */
    116     ConstructMenu();
    117 
    118     /*
    119      * Create the status bar.
    120      */
    121     int widths[2] = { -1, 50 };
    122     CreateStatusBar(2, wxFULL_REPAINT_ON_RESIZE);   // no wxST_SIZEGRIP
    123     SetStatusWidths(2, widths);
    124     SetStatusText(wxT("Ready"));
    125     SetStatusText(kStatusNotRunning, 1);
    126 
    127     /*
    128      * Create main window controls.
    129      */
    130     ConstructControls();
    131 
    132 #if 0
    133     /*
    134      * Use the standard window color for the main frame (which usually
    135      * has a darker color).  This has a dramatic effect under Windows.
    136      */
    137     wxColour color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
    138     SetOwnBackgroundColour(color);
    139 #endif
    140 
    141     /*
    142      * Create the log window.
    143      */
    144     wxRect layout = LogWindow::GetPrefWindowRect();
    145     mpLogWindow = new LogWindow(this);
    146     mpLogWindow->Move(layout.GetTopLeft());
    147     mpLogWindow->SetSize(layout.GetSize());
    148     bool showLogWindow = true;
    149     pPrefs->GetBool("window-log-show", &showLogWindow);
    150     if (showLogWindow)
    151         mpLogWindow->Show();
    152 
    153     /*
    154      * Set up a frequent timer.  We use this to keep our "run/idle"
    155      * display up to date.  (Ideally this will go away.)
    156      */
    157     mTimer.Start(400);      // arg is delay in ms
    158 
    159     /*
    160      * Handle auto-power-on by sending ourselves an event.  That way it
    161      * gets handled after window initialization finishes.
    162      */
    163     bool autoPowerOn = false;
    164     pPrefs->GetBool("auto-power-on", &autoPowerOn);
    165     if (autoPowerOn) {
    166         printf("Sim: Auto power-up\n");
    167         wxCommandEvent startEvent(wxEVT_COMMAND_MENU_SELECTED, IDM_RUNTIME_START);
    168         this->AddPendingEvent(startEvent);
    169     }
    170 
    171     /*
    172      * wxThread wants these to be on the heap -- it will call delete on the
    173      * object when the thread exits.
    174      */
    175     mExternalRuntimeThread = new ExternalRuntime();
    176     mExternalRuntimeThread->StartThread();
    177     mPropertyServerThread = new PropertyServer();
    178     mPropertyServerThread->StartThread();
    179 }
    180 
    181 /*
    182  * Construct the main menu.  Called from the constructor.
    183  */
    184 void MainFrame::ConstructMenu(void)
    185 {
    186     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    187 
    188     /*
    189      * Scan for available phones.
    190      */
    191     PhoneCollection* pCollection = PhoneCollection::GetInstance();
    192     pCollection->ScanForPhones(mSimAssetPath.ToAscii());
    193 
    194     /*
    195      * Create the "File" menu.
    196      */
    197     wxMenu* menuFile = new wxMenu;
    198 
    199     menuFile->Append(IDM_FILE_PREFERENCES, wxT("&Preferences..."),
    200         wxT("Edit simulator preferences"));
    201     menuFile->AppendSeparator();
    202     menuFile->Append(IDM_FILE_EXIT, wxT("E&xit\tCtrl-Q"),
    203         wxT("Stop simulator and exit"));
    204 
    205     /*
    206      * Create the "Runtime" menu.
    207      */
    208     wxMenu* menuRuntime = new wxMenu;
    209     menuRuntime->Append(IDM_RUNTIME_START, wxT("&Power On\tCtrl-G"),
    210         wxT("Start the device"));
    211 //    menuRuntime->Append(IDM_RUNTIME_STOP, wxT("Power &Off"),
    212 //        wxT("Stop the device"));
    213     menuRuntime->AppendSeparator();
    214 //    menuRuntime->Append(IDM_RUNTIME_RESTART, wxT("&Restart"),
    215 //        wxT("Restart the device"));
    216     menuRuntime->Append(IDM_RUNTIME_KILL, wxT("&Kill\tCtrl-K"),
    217         wxT("Kill the runtime processes"));
    218 
    219     /*
    220      * Create "Device" menu.
    221      */
    222     wxString defaultDevice = wxT("Sooner");
    223     pPrefs->GetString("default-device", /*ref*/ defaultDevice);
    224     wxMenu* menuDevice = CreateDeviceMenu(defaultDevice.ToAscii());
    225 
    226     /*
    227      * Create "Debug" menu.
    228      */
    229     wxMenu* menuDebug = new wxMenu;
    230     menuDebug->AppendCheckItem(IDM_DEBUG_SHOW_LOG, wxT("View &Log Output"),
    231         wxT("View log output window"));
    232 
    233     /*
    234      * Create the "Help" menu.
    235      */
    236     wxMenu* menuHelp = new wxMenu;
    237     menuHelp->Append(IDM_HELP_CONTENTS, wxT("&Contents...\tF1"),
    238         wxT("Simulator help"));
    239     menuHelp->AppendSeparator();
    240     menuHelp->Append(IDM_HELP_ABOUT, wxT("&About..."),
    241         wxT("See the fabulous 'about' box"));
    242 
    243     /*
    244      * Create the menu bar.
    245      */
    246     wxMenuBar *menuBar = new wxMenuBar;
    247     menuBar->Append(menuFile, wxT("&File"));
    248     menuBar->Append(menuDevice, kDeviceMenuString);
    249     menuBar->Append(menuRuntime, wxT("&Runtime"));
    250     menuBar->Append(menuDebug, wxT("&Debug"));
    251     menuBar->Append(menuHelp, wxT("&Help"));
    252 
    253     SetMenuBar(menuBar);
    254 
    255 }
    256 
    257 /*
    258  * Construct the "device" menu from our phone collection.
    259  */
    260 wxMenu* MainFrame::CreateDeviceMenu(const char* defaultItemName)
    261 {
    262     wxMenu* menuDevice = new wxMenu;
    263     PhoneCollection* pCollection = PhoneCollection::GetInstance();
    264     int defaultModel = 0;
    265 
    266     for (int i = 0; i < pCollection->GetPhoneCount(); i++) {
    267         PhoneData* pPhoneData = pCollection->GetPhoneData(i);
    268         assert(pPhoneData != NULL);
    269 
    270         menuDevice->AppendRadioItem(IDM_DEVICE_SEL0 + i,
    271             wxString::FromAscii(pPhoneData->GetTitle()));
    272 
    273         // use this one as default if the string matches
    274         if (strcasecmp(pPhoneData->GetName(), defaultItemName) == 0)
    275             defaultModel = i;
    276     }
    277 
    278     menuDevice->Check(IDM_DEVICE_SEL0 + defaultModel, true);
    279 
    280     menuDevice->AppendSeparator();
    281     menuDevice->Append(IDM_DEVICE_RESCAN, wxT("Re-scan"));
    282 
    283     return menuDevice;
    284 }
    285 
    286 /*
    287  * Create some controls in the main window.
    288  *
    289  * The main frame doesn't use the normal background color that you find
    290  * in dialog windows, so we create a "panel" and put all the controls
    291  * on that.
    292  */
    293 void MainFrame::ConstructControls(void)
    294 {
    295     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    296     wxPanel* base = new wxPanel(this, wxID_ANY);
    297     wxBoxSizer* masterSizer = new wxBoxSizer(wxVERTICAL);
    298     wxBoxSizer* tmpSizer;
    299     wxStaticBoxSizer* displayOptSizer;
    300     wxStaticBoxSizer* runtimeOptSizer;
    301     wxStaticBoxSizer* onionSkinOptSizer;
    302     wxComboBox* pModeSelection;
    303     wxCheckBox* pUseGDB;
    304     wxCheckBox* pUseValgrind;
    305     wxCheckBox* pCheckJni;
    306     wxCheckBox* pOverlayOnionSkin;
    307 
    308     displayOptSizer = new wxStaticBoxSizer(wxHORIZONTAL, base,
    309         wxT("Configuration"));
    310     runtimeOptSizer = new wxStaticBoxSizer(wxVERTICAL, base,
    311         wxT("Runtime Options"));
    312     onionSkinOptSizer = new wxStaticBoxSizer(wxVERTICAL, base,
    313         wxT("Onion Skin Options"));
    314 
    315     /*
    316      * Set up the configuration sizer (nee "display options").
    317      */
    318     tmpSizer = new wxBoxSizer(wxHORIZONTAL);
    319     displayOptSizer->Add(tmpSizer);
    320     tmpSizer->Add(
    321             new wxStaticText(base, wxID_ANY, wxT("Device mode:"),
    322             wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT), 0, wxALIGN_CENTER_VERTICAL);
    323     pModeSelection = new wxComboBox(base, IDC_MODE_SELECT, wxT(""),
    324             wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY);
    325     tmpSizer->AddSpacer(kInterSpacing);
    326     tmpSizer->Add(pModeSelection);
    327 
    328     displayOptSizer->AddSpacer(kInterSpacing);
    329 
    330     /*
    331      * Configure the runtime options sizer.
    332      */
    333     wxComboBox* pJavaAppName;
    334     tmpSizer = new wxBoxSizer(wxHORIZONTAL);
    335     pUseGDB = new wxCheckBox(base, IDC_USE_GDB, wxT("Use &debugger"));
    336     tmpSizer->Add(pUseGDB);
    337     tmpSizer->AddSpacer(kInterSpacing);
    338     pUseValgrind = new wxCheckBox(base, IDC_USE_VALGRIND, wxT("Use &valgrind"));
    339     tmpSizer->Add(pUseValgrind);
    340     tmpSizer->AddSpacer(kInterSpacing);
    341     pCheckJni = new wxCheckBox(base, IDC_CHECK_JNI, wxT("Check &JNI"));
    342     tmpSizer->Add(pCheckJni);
    343 
    344     pJavaAppName = new wxComboBox(base, IDC_JAVA_APP_NAME, wxT(""),
    345         wxDefaultPosition, wxSize(320, -1), NELEM(gStdJavaApps), gStdJavaApps,
    346         wxCB_DROPDOWN);
    347     wxBoxSizer* javaAppSizer = new wxBoxSizer(wxHORIZONTAL);
    348     javaAppSizer->Add(
    349             new wxStaticText(base, wxID_ANY,
    350                 wxT("Java app:"),
    351                 wxDefaultPosition, wxDefaultSize,
    352                 wxALIGN_LEFT),
    353             0, wxALIGN_CENTER_VERTICAL);
    354     javaAppSizer->AddSpacer(kInterSpacing);
    355     javaAppSizer->Add(pJavaAppName);
    356 
    357     runtimeOptSizer->Add(tmpSizer);
    358 
    359     runtimeOptSizer->AddSpacer(kInterSpacing);
    360     runtimeOptSizer->Add(javaAppSizer);
    361     runtimeOptSizer->AddSpacer(kInterSpacing);
    362 
    363     wxString tmpStr;
    364     SetCheckFromPref(pUseGDB, "debug", false);
    365     SetCheckFromPref(pUseValgrind, "valgrind", false);
    366     SetCheckFromPref(pCheckJni, "check-jni", false);
    367     if (pPrefs->GetString("java-app-name", /*ref*/ tmpStr))
    368         pJavaAppName->SetValue(tmpStr);
    369 
    370     /*
    371      * Configure the onion skin options sizer.
    372      */
    373     wxTextCtrl* pOnionSkinFileNameText;
    374     wxButton* pOnionSkinFileButton;
    375     wxSlider* pOnionSkinAlphaSlider;
    376     tmpSizer = new wxBoxSizer(wxHORIZONTAL);
    377     pOverlayOnionSkin = new wxCheckBox(base,
    378         IDC_OVERLAY_ONION_SKIN, wxT("Overlay &onion skin"));
    379     tmpSizer->Add(pOverlayOnionSkin);
    380 
    381     pOnionSkinFileNameText = new wxTextCtrl(base,
    382         IDC_ONION_SKIN_FILE_NAME, wxT(""),
    383         wxDefaultPosition, wxSize(250, -1),
    384         wxTE_PROCESS_ENTER);
    385     pOnionSkinFileButton = new wxButton(base, IDC_ONION_SKIN_BUTTON,
    386         wxT("Choose"));
    387 
    388     wxBoxSizer* onionSkinFileNameSizer = new wxBoxSizer(wxHORIZONTAL);
    389     onionSkinFileNameSizer->Add(
    390         new wxStaticText(base, wxID_ANY,
    391             wxT("Filename:"),
    392             wxDefaultPosition, wxDefaultSize,
    393             wxALIGN_LEFT),
    394         0, wxALIGN_CENTER_VERTICAL);
    395     onionSkinFileNameSizer->AddSpacer(kInterSpacing);
    396     onionSkinFileNameSizer->Add(pOnionSkinFileNameText);
    397     onionSkinFileNameSizer->Add(pOnionSkinFileButton);
    398 
    399     wxBoxSizer * onionSkinAlphaSizer = new wxBoxSizer(wxHORIZONTAL);
    400     int initialAlphaVal = 127;
    401     pPrefs->GetInt("onion-skin-alpha-value", &initialAlphaVal);
    402     pOnionSkinAlphaSlider = new wxSlider(base, IDC_ONION_SKIN_ALPHA_VAL,
    403         initialAlphaVal, 0, 255, wxDefaultPosition, wxSize(150, 20));
    404     onionSkinAlphaSizer->Add(
    405         new wxStaticText(base, wxID_ANY,
    406             wxT("Transparency:"),
    407             wxDefaultPosition, wxDefaultSize,
    408             wxALIGN_LEFT),
    409         0, wxALIGN_CENTER_VERTICAL);
    410     onionSkinAlphaSizer->AddSpacer(kInterSpacing);
    411     onionSkinAlphaSizer->Add(pOnionSkinAlphaSlider, 1, wxCENTRE | wxALL, 5);
    412 
    413     onionSkinOptSizer->Add(tmpSizer);
    414     onionSkinOptSizer->AddSpacer(kInterSpacing);
    415     onionSkinOptSizer->Add(onionSkinFileNameSizer);
    416     onionSkinOptSizer->Add(onionSkinAlphaSizer);
    417 
    418     wxString tmpStr2;
    419     SetCheckFromPref(pOverlayOnionSkin, "overlay-onion-skin", false);
    420     if (pPrefs->GetString("onion-skin-file-name", /*ref*/ tmpStr2))
    421         pOnionSkinFileNameText->SetValue(tmpStr2);
    422 
    423     /*
    424      * Add the various components to the master sizer.
    425      */
    426     masterSizer->Add(displayOptSizer);
    427     masterSizer->AddSpacer(kInterSpacing * 2);
    428     masterSizer->Add(runtimeOptSizer);
    429     masterSizer->AddSpacer(kInterSpacing * 2);
    430     masterSizer->Add(onionSkinOptSizer);
    431     //masterSizer->AddSpacer(kInterSpacing);
    432 
    433     /*
    434      * I don't see a way to guarantee that the window is wide enough to
    435      * show the entire menu bar, so just throw some pixels at it.
    436      */
    437     wxBoxSizer* minWidthSizer = new wxBoxSizer(wxVERTICAL);
    438     minWidthSizer->Add(300, kEdgeSpacing);       // forces minimum width
    439     minWidthSizer->Add(masterSizer);
    440     minWidthSizer->AddSpacer(kInterSpacing * 2);
    441 
    442     /* move us a few pixels in from the left */
    443     wxBoxSizer* indentSizer = new wxBoxSizer(wxHORIZONTAL);
    444     indentSizer->AddSpacer(kEdgeSpacing);
    445     indentSizer->Add(minWidthSizer);
    446     indentSizer->AddSpacer(kEdgeSpacing);
    447 
    448     base->SetSizer(indentSizer);
    449 
    450     indentSizer->Fit(this);
    451     indentSizer->SetSizeHints(this);
    452 }
    453 
    454 /*
    455  * Set the value of a checkbox based on a value from the config file.
    456  */
    457 void MainFrame::SetCheckFromPref(wxCheckBox* pControl, const char* prefStr,
    458     bool defaultVal)
    459 {
    460     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    461     assert(pPrefs != NULL);
    462 
    463     bool val = defaultVal;
    464     pPrefs->GetBool(prefStr, &val);
    465 
    466     pControl->SetValue(val);
    467 }
    468 
    469 /*
    470  * Destructor.
    471  */
    472 MainFrame::~MainFrame(void)
    473 {
    474     PhoneCollection::DestroyInstance();
    475 
    476     delete mExternalRuntimeThread;
    477     delete mPropertyServerThread;
    478 
    479     // don't touch mpModeSelection -- child of window
    480 }
    481 
    482 /*
    483  * File->Quit or click on close box.
    484  *
    485  * If we want an "are you sure you want to quit" box, add it here.
    486  */
    487 void MainFrame::OnClose(wxCloseEvent& event)
    488 {
    489     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    490 
    491 /*
    492     if (event.CanVeto())
    493         printf("Closing (can veto)\n");
    494     else
    495         printf("Closing (mandatory)\n");
    496 */
    497 
    498     /*
    499      * Generally speaking, Close() is not guaranteed to close the window.
    500      * However, we want to use it here because (a) our windows are
    501      * guaranteed to close, and (b) it provides our windows an opportunity
    502      * to tell others that they are about to vanish.
    503      */
    504     if (mpPhoneWindow != NULL)
    505         mpPhoneWindow->Close(true);
    506 
    507     /* save position of main window */
    508     wxPoint pos = GetPosition();
    509     pPrefs->SetInt("window-main-x", pos.x);
    510     pPrefs->SetInt("window-main-y", pos.y);
    511 
    512     /* save default device selection */
    513     int idx = GetSelectedDeviceIndex();
    514     if (idx >= 0) {
    515         PhoneCollection* pCollection = PhoneCollection::GetInstance();
    516         PhoneData* pPhoneData = pCollection->GetPhoneData(idx);
    517         pPrefs->SetString("default-device", pPhoneData->GetName());
    518     }
    519 
    520     if (mpLogWindow != NULL)
    521         mpLogWindow->Close(true);
    522     Destroy();
    523 }
    524 
    525 /*
    526  * File->Preferences
    527  */
    528 void MainFrame::OnFilePreferences(wxCommandEvent& WXUNUSED(event))
    529 {
    530     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    531     PrefsDialog dialog(this);
    532     int result;
    533 
    534     result = dialog.ShowModal();
    535     if (result == wxID_OK) {
    536         /*
    537          * The dialog handles writing changes to Preferences, so all we
    538          * need to deal with here are changes that have an immediate
    539          * impact on us. (which is currently nothing)
    540          */
    541     }
    542 }
    543 
    544 /*
    545  * File->Exit
    546  */
    547 void MainFrame::OnFileExit(wxCommandEvent& WXUNUSED(event))
    548 {
    549     Close(FALSE);       // false means "allow veto"
    550 }
    551 
    552 /*
    553  * Decide whether Simulator->Start should be enabled.
    554  */
    555 void MainFrame::OnUpdateSimStart(wxUpdateUIEvent& event)
    556 {
    557     if (IsRuntimeRunning())
    558         event.Enable(FALSE);
    559     else
    560         event.Enable(TRUE);
    561 }
    562 
    563 /*
    564  * Simulator->Start
    565  */
    566 void MainFrame::OnSimStart(wxCommandEvent& WXUNUSED(event))
    567 {
    568     // keyboard equivalents can still get here even if menu item disabled
    569     if (IsRuntimeRunning())
    570         return;
    571 
    572     int id = GetSelectedDeviceIndex();
    573     if (id < 0) {
    574         fprintf(stderr, "Sim: could not identify currently selected device\n");
    575         return;
    576     }
    577 
    578 #if 0
    579     static int foo = 0;
    580     foo++;
    581     if (foo == 2) {
    582         Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    583 
    584         pPrefs->SetBool("debug", true);
    585     }
    586 #endif
    587 
    588     SetupPhoneUI(id, NULL);
    589     if (mpPhoneWindow != NULL)
    590         mpPhoneWindow->GetDeviceManager()->StartRuntime();
    591 }
    592 
    593 /*
    594  * Decide whether Simulator->Stop should be enabled.
    595  */
    596 void MainFrame::OnUpdateSimStop(wxUpdateUIEvent& event)
    597 {
    598     if (IsRuntimeRunning())
    599         event.Enable(TRUE);
    600     else
    601         event.Enable(FALSE);
    602 }
    603 
    604 /*
    605  * Simulator->Stop
    606  */
    607 void MainFrame::OnSimStop(wxCommandEvent& WXUNUSED(event))
    608 {
    609     if (!IsRuntimeRunning())
    610         return;
    611     assert(mpPhoneWindow != NULL);
    612     mpPhoneWindow->GetDeviceManager()->StopRuntime();
    613 }
    614 
    615 /*
    616  * Decide whether Simulator->Restart should be enabled.
    617  */
    618 void MainFrame::OnUpdateSimRestart(wxUpdateUIEvent& event)
    619 {
    620     if (IsRuntimeRunning())
    621         event.Enable(TRUE);
    622     else
    623         event.Enable(FALSE);
    624 }
    625 
    626 /*
    627  * Simulator->Restart - stop then start the device runtime.
    628  */
    629 void MainFrame::OnSimRestart(wxCommandEvent& WXUNUSED(event))
    630 {
    631     if (!IsRuntimeRunning())
    632         return;
    633 
    634     printf("Restart requested\n");
    635     mpPhoneWindow->GetDeviceManager()->StopRuntime();
    636 
    637     mRestartRequested = true;
    638 }
    639 
    640 /*
    641  * Decide whether Simulator->Kill should be enabled.
    642  */
    643 void MainFrame::OnUpdateSimKill(wxUpdateUIEvent& event)
    644 {
    645     if (IsRuntimeKillable())
    646         event.Enable(TRUE);
    647     else
    648         event.Enable(FALSE);
    649 }
    650 
    651 /*
    652  * Simulator->Kill
    653  */
    654 void MainFrame::OnSimKill(wxCommandEvent& WXUNUSED(event))
    655 {
    656     if (!IsRuntimeKillable())
    657         return;
    658     assert(mpPhoneWindow != NULL);
    659     mpPhoneWindow->GetDeviceManager()->KillRuntime();
    660 }
    661 
    662 
    663 /*
    664  * Device->[select]
    665  */
    666 void MainFrame::OnDeviceSelected(wxCommandEvent& event)
    667 {
    668     wxBusyCursor busyc;
    669     int id = event.GetId() - IDM_DEVICE_SEL0;
    670 
    671     SetupPhoneUI(id, NULL);
    672 }
    673 
    674 /*
    675  * Device->Rescan
    676  */
    677 void MainFrame::OnDeviceRescan(wxCommandEvent& event)
    678 {
    679     wxBusyCursor busyc;
    680     wxMenuBar* pMenuBar;
    681     PhoneCollection* pCollection;
    682     wxMenu* pOldMenu;
    683     wxMenu* pNewMenu;
    684     const char* curDevName = NULL;
    685     int idx;
    686 
    687     /* figure out the current device name */
    688     pCollection = PhoneCollection::GetInstance();
    689     idx = GetSelectedDeviceIndex();
    690     if (idx >= 0) {
    691         PhoneData* pPhoneData;
    692 
    693         pPhoneData = pCollection->GetPhoneData(idx);
    694         curDevName = pPhoneData->GetName();
    695         printf("--- device name is '%s'\n", (const char*) curDevName);
    696     }
    697 
    698     /* reconstruct device menu with new data */
    699 #ifdef BEFORE_ASSET
    700     pCollection->ScanForPhones(mSimAssetPath);
    701 #else
    702     pCollection->ScanForPhones(NULL);
    703 #endif
    704 
    705     pMenuBar = GetMenuBar();
    706     idx = pMenuBar->FindMenu(kDeviceMenuString);
    707     if (idx == wxNOT_FOUND) {
    708         fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii());
    709         return;
    710     }
    711 
    712     pNewMenu = CreateDeviceMenu(curDevName);
    713 
    714     pOldMenu = pMenuBar->Replace(idx, pNewMenu, kDeviceMenuString);
    715     delete pOldMenu;
    716 
    717     /* tell the PhoneWindow about it; may cause runtime to exit */
    718     if (mpPhoneWindow != NULL)
    719         mpPhoneWindow->DevicesRescanned();
    720 }
    721 
    722 /*
    723  * Set checkbox on menu item.
    724  */
    725 void MainFrame::OnUpdateDebugShowLog(wxUpdateUIEvent& event)
    726 {
    727     if (mpLogWindow == NULL) {
    728         event.Enable(false);
    729     } else {
    730         event.Enable(true);
    731         event.Check(mpLogWindow->IsShown());
    732     }
    733 }
    734 
    735 /*
    736  * Debug->ShowLog toggle.
    737  */
    738 void MainFrame::OnDebugShowLog(wxCommandEvent& WXUNUSED(event))
    739 {
    740     mpLogWindow->Show(!mpLogWindow->IsShown());
    741 }
    742 
    743 /*
    744  * Help->Contents
    745  */
    746 void MainFrame::OnHelpContents(wxCommandEvent& WXUNUSED(event))
    747 {
    748     ((MyApp*)wxTheApp)->GetHelpController()->DisplayContents();
    749 }
    750 
    751 /*
    752  * Help->About
    753  */
    754 void MainFrame::OnHelpAbout(wxCommandEvent& WXUNUSED(event))
    755 {
    756     wxMessageBox(wxT("Android Simulator v0.1\n"
    757                      "Copyright 2006 The Android Open Source Project"),
    758         wxT("About..."), wxOK | wxICON_INFORMATION, this);
    759 }
    760 
    761 /*
    762  * Sent from phonewindow or when activated
    763  */
    764 void MainFrame::OnActivate(wxActivateEvent& event)
    765 {
    766 #if 0
    767     if (event.GetActive())
    768     {
    769         if (mpPhoneWindow != NULL &&
    770             mpPhoneWindow->GetDeviceManager()->RefreshRuntime())
    771         {
    772             wxString msg;
    773             int sel;
    774 
    775             msg = wxT("Newer runtime executable found. Would you like to reload the device?");
    776 
    777             sel = wxMessageBox(msg, wxT("Android Safety Patrol"),
    778                 wxYES | wxNO | wxICON_QUESTION, mpPhoneWindow);
    779             //printf("BUTTON was %d (yes=%d)\n", sel, wxYES);
    780             if (sel == wxYES)
    781             {
    782                 mpPhoneWindow->GetDeviceManager()->StopRuntime();
    783                 mpPhoneWindow->Close();
    784                 mpPhoneWindow = NULL;
    785                 mRestartRequested = true;
    786             }
    787             else
    788             {
    789                 mpPhoneWindow->GetDeviceManager()->UserCancelledRefresh();
    790             }
    791         }
    792     }
    793 #endif
    794 
    795     // let wxWidgets do whatever it needs to do
    796     event.Skip();
    797 }
    798 
    799 
    800 /*
    801  * Device mode selection box.
    802  */
    803 void MainFrame::OnComboBox(wxCommandEvent& event)
    804 {
    805     const char* pref;
    806     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    807     assert(pPrefs != NULL);
    808 
    809     if (IDC_MODE_SELECT == event.GetId())
    810     {
    811         int id = GetSelectedDeviceIndex();
    812         if (id < 0)
    813             return;
    814         //printf("--- mode selected: '%s'\n", (const char*) event.GetString().ToAscii());
    815 
    816         /*
    817          * Call the phone window's setup function.  Don't call our SetupPhoneUI
    818          * function from here -- updating the combo box from a combo box callback
    819          * could cause problems.
    820          */
    821         if (mpPhoneWindow != NULL) {
    822             mpPhoneWindow->SetCurrentMode(event.GetString());
    823             mpPhoneWindow->Setup(id);
    824         }
    825     } else if (event.GetId() == IDC_JAVA_VM) {
    826         wxComboBox* pBox = (wxComboBox*) FindWindow(IDC_JAVA_VM);
    827         pPrefs->SetString("java-vm", pBox->GetValue().ToAscii());
    828     }
    829 }
    830 
    831 /*
    832  * One of our option checkboxes has been changed.
    833  *
    834  * We update the prefs database so that the settings are retained when
    835  * the simulator is next used.
    836  */
    837 void MainFrame::OnCheckBox(wxCommandEvent& event)
    838 {
    839     const char* pref;
    840 
    841     switch (event.GetId()) {
    842     case IDC_USE_GDB:               pref = "debug";                 break;
    843     case IDC_USE_VALGRIND:          pref = "valgrind";              break;
    844     case IDC_CHECK_JNI:             pref = "check-jni";             break;
    845     case IDC_OVERLAY_ONION_SKIN:    pref = "overlay-onion-skin";    break;
    846     default:
    847         printf("Sim: unrecognized checkbox %d in OnCheckBox\n", event.GetId());
    848         return;
    849     }
    850 
    851     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    852     assert(pPrefs != NULL);
    853 
    854     pPrefs->SetBool(pref, (bool) event.GetInt());
    855     //printf("--- set pref '%s' to %d\n", pref, (bool) event.GetInt());
    856     if (event.GetId() == IDC_OVERLAY_ONION_SKIN) {
    857         BroadcastOnionSkinUpdate();
    858     }
    859     if (event.GetId() == IDC_CHECK_JNI) {
    860         const char* val = "0";
    861         if ((bool) event.GetInt())
    862             val = "1";
    863         mPropertyServerThread->SetProperty(PropertyServer::kPropCheckJni, val);
    864 
    865     }
    866 }
    867 
    868 void MainFrame::BroadcastOnionSkinUpdate() {
    869     if (mpPhoneWindow != NULL) {
    870         // broadcast a user event indicating an onion skin update
    871         UserEvent uev(0, (void*) -1);
    872         mpPhoneWindow->GetDeviceManager()->BroadcastEvent(uev);
    873     }
    874 }
    875 
    876 /*
    877  * A text control on the main page is being updated.
    878  *
    879  * The current implementation updates the preferences database on every
    880  * change, which is a bit silly but is easy to do.
    881  */
    882 void MainFrame::OnText(wxCommandEvent& event)
    883 {
    884     const char* pref;
    885 
    886     switch (event.GetId()) {
    887     case IDC_JAVA_APP_NAME:     pref = "java-app-name"; break;
    888     default:
    889         printf("Sim: unrecognized textctrl %d in OnText\n", event.GetId());
    890         return;
    891     }
    892 
    893     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    894     assert(pPrefs != NULL);
    895 
    896     // event.GetString() does not work on Mac -- always blank
    897     //pPrefs->SetString(pref, event.GetString());
    898     assert(event.GetId() == IDC_JAVA_APP_NAME); // fix if we add more
    899     wxComboBox* pBox;
    900     pBox = (wxComboBox*) FindWindow(IDC_JAVA_APP_NAME);
    901     pPrefs->SetString(pref, pBox->GetValue().ToAscii());
    902     //printf("--- set pref '%s' to '%s'\n", pref,(const char*)pBox->GetValue());
    903 }
    904 
    905 /*
    906  * A user pressed enter in a text control on the main page.
    907  *
    908  * The current implementation updates the preferences database on every
    909  * change, which is a bit silly but is easy to do.
    910  */
    911 void MainFrame::OnTextEnter(wxCommandEvent& event)
    912 {
    913     const char* pref;
    914 
    915     switch (event.GetId()) {
    916     case IDC_ONION_SKIN_FILE_NAME:
    917         pref = "onion-skin-file-name";
    918         break;
    919     default:
    920         printf("Sim: unrecognized textctrl %d in OnTextEnter\n", event.GetId());
    921         return;
    922     }
    923 
    924     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    925     assert(pPrefs != NULL);
    926 
    927     assert(event.GetId() == IDC_ONION_SKIN_FILE_NAME); // fix if we add more
    928     wxTextCtrl* pTextCtrl;
    929     pTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME);
    930     wxString onionSkinFileNameWxString = pTextCtrl->GetValue();
    931     char* onionSkinFileName = "";
    932     if (onionSkinFileNameWxString.Len() > 0) {
    933         onionSkinFileName = android::strdupNew(onionSkinFileNameWxString.ToAscii());
    934     }
    935     pPrefs->SetString(pref, onionSkinFileName);
    936     BroadcastOnionSkinUpdate();
    937 }
    938 
    939 /*
    940  * A user pressed a button on the main page
    941  *
    942  */
    943  void MainFrame::OnButton(wxCommandEvent& event)
    944  {
    945     wxWindow* base;
    946     wxFileDialog* pOnionSkinFileChooser;
    947     int retVal;
    948     switch (event.GetId()) {
    949     case IDC_ONION_SKIN_BUTTON:
    950         base = FindWindow(IDC_ONION_SKIN_BUTTON)->GetParent();
    951         pOnionSkinFileChooser = new wxFileDialog(base,
    952             wxT("Choose the onion skin image file."),
    953             wxT(""), wxT(""), wxT("*.*"),
    954             wxOPEN | wxFILE_MUST_EXIST);
    955         retVal = pOnionSkinFileChooser->ShowModal();
    956         if (retVal == pOnionSkinFileChooser->GetAffirmativeId()) {
    957             Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    958             assert(pPrefs != NULL);
    959             wxString fileNameWxString = pOnionSkinFileChooser->GetPath();
    960             const char* fileName = android::strdupNew(fileNameWxString.ToAscii());
    961             wxTextCtrl* fileTextCtrl = (wxTextCtrl*) FindWindow(IDC_ONION_SKIN_FILE_NAME);
    962             fileTextCtrl->SetValue(fileNameWxString);
    963             pPrefs->SetString("onion-skin-file-name", fileName);
    964             BroadcastOnionSkinUpdate();
    965         }
    966         break;
    967     default:
    968         printf("Sim: unrecognized button %d in OnButton\n", event.GetId());
    969         return;
    970     }
    971  }
    972 
    973  /*
    974   * The user moved a slider on the main page
    975   */
    976  void MainFrame::OnSliderChange(wxScrollEvent& event)
    977  {
    978     wxSlider* pSlider;
    979     Preferences* pPrefs;
    980     switch (event.GetId()) {
    981     case IDC_ONION_SKIN_ALPHA_VAL:
    982         pSlider = (wxSlider*) FindWindow(IDC_ONION_SKIN_ALPHA_VAL);
    983         pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
    984         assert(pPrefs != NULL);
    985         pPrefs->SetInt("onion-skin-alpha-value", pSlider->GetValue());
    986         BroadcastOnionSkinUpdate();
    987         break;
    988     default:
    989         printf("Sim: unrecognized scroller or slider %d in OnSliderChange\n", event.GetId());
    990         return;
    991     }
    992  }
    993 
    994 #if 0
    995 /*
    996  * Idle processing.  Under wxWidgets this only called once after UI
    997  * activity unless you call event.RequestMore().
    998  */
    999 void MainFrame::OnIdle(wxIdleEvent& event)
   1000 {
   1001     event.Skip();       // let base class handler do stuff
   1002 }
   1003 #endif
   1004 
   1005 /*
   1006  * Handle the timer.
   1007  *
   1008  * This is being called in the main thread, so multithreading with the
   1009  * rest of MainFrame isn't a concern here.
   1010  */
   1011 void MainFrame::OnTimer(wxTimerEvent& event)
   1012 {
   1013     bool status;
   1014 
   1015     /*
   1016      * Check to see if the runtime died without telling us.  This can only
   1017      * happen if we forcibly kill our thread.  We shouldn't really be
   1018      * doing that anymore, but keep this in for now just in case.
   1019      */
   1020     status = IsRuntimeRunning();
   1021 
   1022     if (mSimRunning != status) {
   1023         if (!status) {
   1024             printf("Sim: fixed mSimRunning=%d actual=%d\n",
   1025                 mSimRunning, status);
   1026             mSimRunning = status;
   1027 
   1028             if (!status)
   1029                 HandleRuntimeStop();
   1030         } else {
   1031             /*
   1032              * This was happening when we were shutting down but the
   1033              * device management thread hadn't completely gone away.  The
   1034              * simple IsRunning test passes, so we get a false positive.
   1035              * Ignore it.
   1036              */
   1037         }
   1038     }
   1039 
   1040     if (gWantToKill) {
   1041         if (IsRuntimeRunning()) {
   1042             printf("Sim: handling kill request\n");
   1043             mpPhoneWindow->GetDeviceManager()->KillRuntime();
   1044         }
   1045         gWantToKill = false;
   1046 
   1047         /* see if Ctrl-C should kill us too */
   1048         Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
   1049         bool die = false;
   1050 
   1051         pPrefs->GetBool("trap-sigint-suicide", &die);
   1052         if (die) {
   1053             printf("Sim: goodbye cruel world!\n");
   1054             exit(0);
   1055         }
   1056     }
   1057 }
   1058 
   1059 /*
   1060  * Determine whether or not the simulator is running.
   1061  */
   1062 bool MainFrame::IsRuntimeRunning(void)
   1063 {
   1064     bool result;
   1065 
   1066     if (mpPhoneWindow == NULL)
   1067         result = false;
   1068     else if (!mpPhoneWindow->IsReady())
   1069         result = false;
   1070     else
   1071         result = mpPhoneWindow->GetDeviceManager()->IsRunning();
   1072 
   1073     return result;
   1074 }
   1075 
   1076 /*
   1077  * Determine whether or not the runtime can be killed.
   1078  */
   1079 bool MainFrame::IsRuntimeKillable(void)
   1080 {
   1081     bool result;
   1082 
   1083     result = IsRuntimeRunning();
   1084     if (result)
   1085         result = mpPhoneWindow->GetDeviceManager()->IsKillable();
   1086 
   1087     return result;
   1088 }
   1089 
   1090 /*
   1091  * Determine whether two devices are sufficiently compatible.
   1092  */
   1093 bool MainFrame::CompatibleDevices(PhoneData* pData1, PhoneData* pData2)
   1094 {
   1095     int displayCount;
   1096 
   1097     displayCount = pData1->GetNumDisplays();
   1098     if (pData2->GetNumDisplays() != displayCount)
   1099         return false;
   1100 
   1101     for (int i = 0; i < displayCount; i++) {
   1102         PhoneDisplay* pDisplay1 = pData1->GetPhoneDisplay(i);
   1103         PhoneDisplay* pDisplay2 = pData2->GetPhoneDisplay(i);
   1104 
   1105         if (!PhoneDisplay::IsCompatible(pDisplay1, pDisplay2))
   1106             return false;
   1107     }
   1108 
   1109     return true;
   1110 }
   1111 
   1112 /*
   1113  * (Re-)arrange the UI for the currently selected phone model.
   1114  *
   1115  * If the simulator is running, and the set of displays for the current
   1116  * device are incompatible with the new device, we need to restart the
   1117  * runtime.  We need to ask for permission first though.
   1118  */
   1119 void MainFrame::SetupPhoneUI(int idx, const char* defaultMode)
   1120 {
   1121     PhoneCollection* pCollection;
   1122     PhoneData* pPhoneData;
   1123     wxString* choices = NULL;
   1124     int numChoices = 0;
   1125     int numKeyboards = 0;
   1126     bool haveDefaultMode = false;
   1127     wxCharBuffer currentMode;
   1128     int i;
   1129 
   1130     pCollection = PhoneCollection::GetInstance();
   1131     pPhoneData = pCollection->GetPhoneData(idx);
   1132     if (pPhoneData == NULL) {
   1133         fprintf(stderr, "ERROR: device index %d not valid\n", idx);
   1134         goto bail;
   1135     }
   1136 
   1137     /*
   1138      * We have a window up.  If the displays aren't compatible, we'll
   1139      * need to recreate it.
   1140      */
   1141     if (mpPhoneWindow != NULL) {
   1142         PhoneData* pCurData = mpPhoneWindow->GetPhoneData();
   1143 
   1144         if (!CompatibleDevices(pCurData, pPhoneData)) {
   1145             /*
   1146              * We need to trash the window.  This will also kill the
   1147              * runtime.  If it's running, ask permission.
   1148              */
   1149             if (IsRuntimeRunning()) {
   1150                 wxString msg;
   1151                 int sel;
   1152 
   1153                 msg =  wxT("Switching to the new device requires restarting the");
   1154                 msg += wxT(" runtime.  Continue?");
   1155 
   1156                 sel = wxMessageBox(msg, wxT("Android Safety Patrol"),
   1157                     wxOK | wxCANCEL | wxICON_QUESTION, this);
   1158                 printf("BUTTON was %d (ok=%d)\n", sel, wxOK);
   1159                 if (sel == wxCANCEL)
   1160                     goto bail;
   1161 
   1162                 /* shut it down (politely), ask for an eventual restart */
   1163                 mpPhoneWindow->GetDeviceManager()->StopRuntime();
   1164                 mpPhoneWindow->Close();
   1165                 mpPhoneWindow = NULL;
   1166                 mRestartRequested = true;
   1167                 goto bail;
   1168             } else {
   1169                 /* not running, just trash the window and continue */
   1170                 mpPhoneWindow->Close();
   1171                 mpPhoneWindow = NULL;
   1172             }
   1173         }
   1174     }
   1175 
   1176     /*
   1177      * Figure out the set of available modes.
   1178      */
   1179 
   1180     numChoices = pPhoneData->GetNumModes();
   1181     if (numChoices > 0) {
   1182         choices = new wxString[numChoices];
   1183         for (i = 0; i < numChoices; i++) {
   1184             PhoneMode* pPhoneMode;
   1185             pPhoneMode = pPhoneData->GetPhoneMode(i);
   1186             choices[i] = wxString::FromAscii(pPhoneMode->GetName());
   1187             if (defaultMode != NULL &&
   1188                 strcmp(defaultMode, pPhoneMode->GetName()) == 0)
   1189             {
   1190                 haveDefaultMode = true;
   1191             }
   1192         }
   1193     }
   1194 
   1195     if (choices == NULL) {
   1196         /* had a failure earlier; configure UI with default stuff */
   1197         choices = new wxString[1];
   1198         choices[0] = wxT("(none)");
   1199     }
   1200 
   1201     if (!haveDefaultMode) {
   1202         /*
   1203          * Default mode wasn't found.  If we specify it as the default
   1204          * in the wxComboBox create call it shows up in the combo box
   1205          * under Linux, even if it doesn't exist in the list.  So, we
   1206          * make sure that it doesn't get used if we can't find it.
   1207          */
   1208         if (defaultMode != NULL) {
   1209             printf("Sim: HEY: default mode '%s' not found in list\n",
   1210                 defaultMode);
   1211         }
   1212         currentMode = choices[0].ToAscii();
   1213     } else {
   1214         currentMode = defaultMode;
   1215     }
   1216 
   1217 
   1218     /*
   1219      * Create the window if necessary.
   1220      */
   1221     if (mpPhoneWindow == NULL) {
   1222         // create, setup, and then show window
   1223         mpPhoneWindow = new PhoneWindow(this, mPhoneWindowPosn);
   1224         mpPhoneWindow->SetCurrentMode((const char*)currentMode);
   1225         if (!mpPhoneWindow->Setup(idx)) {
   1226             delete mpPhoneWindow;
   1227             mpPhoneWindow = NULL;
   1228         }
   1229         if (mpPhoneWindow != NULL) {
   1230             mpPhoneWindow->Show();
   1231             //mpPhoneWindow->CheckPlacement();
   1232         }
   1233     } else {
   1234         // just set up for new device
   1235         mpPhoneWindow->SetCurrentMode((const char*)currentMode);
   1236         if (!mpPhoneWindow->Setup(idx)) {
   1237             // it's in an uncertain state, blow it away
   1238             delete mpPhoneWindow;
   1239             mpPhoneWindow = NULL;
   1240         }
   1241     }
   1242 
   1243     /*
   1244      * Reconfigure mode selection box.
   1245      */
   1246     wxComboBox* pModeSelection;
   1247     pModeSelection = (wxComboBox*)FindWindow(IDC_MODE_SELECT);
   1248     pModeSelection->Clear();
   1249     for (i = 0; i < numChoices; i++)
   1250         pModeSelection->Append(choices[i]);
   1251     pModeSelection->SetSelection(0);
   1252     pModeSelection->Enable(numChoices > 1);
   1253 
   1254     /*
   1255      * configure qwerty keyboard attribute
   1256      */
   1257     numKeyboards = pPhoneData->GetNumKeyboards();
   1258     if (numKeyboards > 0) {
   1259         // only use the first keyboard for now
   1260         PhoneKeyboard* pPhoneKeyboard;
   1261         pPhoneKeyboard = pPhoneData->GetPhoneKeyboard(0);
   1262         if (pPhoneKeyboard->getQwerty()) {
   1263             printf("Sim: set 'qwerty' env\n");
   1264             setenv("qwerty", "true", true);
   1265         }
   1266     }
   1267 
   1268 bail:
   1269     delete[] choices;
   1270 }
   1271 
   1272 /*
   1273  * Figure out which device is currently selected.
   1274  *
   1275  * The easiest way to do this is just run down the list of possible IDs
   1276  * and stop when something claims to be checked.
   1277  *
   1278  * Returns -1 if it can't find a checked item (which can happen if no
   1279  * device layouts were found).
   1280  */
   1281 int MainFrame::GetSelectedDeviceIndex(void)
   1282 {
   1283     wxMenuBar* pMenuBar;
   1284     wxMenu* pMenu;
   1285     int idx;
   1286 
   1287     pMenuBar = GetMenuBar();
   1288     idx = pMenuBar->FindMenu(kDeviceMenuString);
   1289     if (idx == wxNOT_FOUND) {
   1290         fprintf(stderr, "Sim: couldn't find %s menu\n", (const char*) kDeviceMenuString.ToAscii());
   1291         return -1;
   1292     }
   1293 
   1294     pMenu = pMenuBar->GetMenu(idx);
   1295 
   1296     //printf("Menu.MenuItemCount = %d\n", pMenu->GetMenuItemCount());
   1297     for (int j = pMenu->GetMenuItemCount() -1; j >= 0; j--) {
   1298         wxMenuItem* pItem;
   1299 
   1300         pItem = pMenu->FindItemByPosition(j);
   1301         //printf("ITEM %d: %s\n", j, (const char*) pItem->GetLabel());
   1302         if (pItem->IsChecked()) {
   1303             printf("Sim: selected device is '%s'\n",
   1304                 (const char*) pItem->GetLabel().ToAscii());
   1305             return j;
   1306         }
   1307     }
   1308 
   1309     return -1;
   1310 }
   1311 
   1312 /*
   1313  * Receive a status message from the runtime thread.
   1314  */
   1315 void MainFrame::OnUserEvent(UserEvent& event)
   1316 {
   1317     UserEventMessage* pUem;
   1318 
   1319     pUem = (UserEventMessage*) event.GetData();
   1320     assert(pUem != NULL);
   1321 
   1322     switch (pUem->GetType()) {
   1323     case UserEventMessage::kRuntimeStarted:
   1324         printf("Sim: runtime thread started!\n");
   1325         HandleRuntimeStart();
   1326         break;
   1327     case UserEventMessage::kRuntimeStopped:
   1328         printf("Sim: runtime thread stopped!\n");
   1329         HandleRuntimeStop();
   1330         break;
   1331     case UserEventMessage::kErrorMessage:
   1332         {
   1333             wxString msg = pUem->GetString();
   1334             wxMessageBox(msg, wxT("Android Runtime Error"),
   1335                 wxOK | wxICON_WARNING, this);
   1336         }
   1337         break;
   1338     case UserEventMessage::kLogMessage:
   1339         mpLogWindow->AddLogMessage(pUem->GetLogMessage());
   1340         break;
   1341     case UserEventMessage::kExternalRuntime:
   1342         HandleExternalRuntime(pUem->GetReader(), pUem->GetWriter());
   1343         break;
   1344     default:
   1345         printf("Sim: MESSAGE: unknown UserEventMessage rcvd (type=%d)\n",
   1346             pUem->GetType());
   1347         break;
   1348     }
   1349 
   1350     delete pUem;
   1351 }
   1352 
   1353 /*
   1354  * The device management thread is up, so the runtime should be fully
   1355  * running shortly.
   1356  */
   1357 void MainFrame::HandleRuntimeStart(void)
   1358 {
   1359     mSimRunning = true;
   1360 
   1361     SetStatusText(kStatusRunning, 1);
   1362 }
   1363 
   1364 /*
   1365  * The device management thread is exiting, so the runtime must be dead.
   1366  */
   1367 void MainFrame::HandleRuntimeStop(void)
   1368 {
   1369     mSimRunning = false;
   1370 
   1371     SetStatusText(kStatusNotRunning, 1);
   1372 
   1373     if (mRestartRequested) {
   1374         printf("Sim: restarting runtime\n");
   1375         mRestartRequested = false;
   1376         SetupPhoneUI(GetSelectedDeviceIndex(), NULL);
   1377         if (mpPhoneWindow != NULL)
   1378             mpPhoneWindow->GetDeviceManager()->StartRuntime();
   1379     }
   1380 }
   1381 
   1382 /*
   1383  * Handle a connection from an external runtime.
   1384  */
   1385 void MainFrame::HandleExternalRuntime(android::Pipe* reader,
   1386     android::Pipe* writer)
   1387 {
   1388     android::MessageStream msgStream;
   1389     android::Message msg;
   1390 
   1391     if (IsRuntimeRunning()) {
   1392         /*
   1393          * Tell the new guy to go away.
   1394          */
   1395         if (!msgStream.init(reader, writer, true)) {
   1396             fprintf(stderr, "Sim: WARNING: unable to talk to remote runtime\n");
   1397             goto bail;
   1398         }
   1399 
   1400         printf("Sim: telling external runtime to go away\n");
   1401         msg.setCommand(android::Simulator::kCommandGoAway, 0);
   1402         msgStream.send(&msg);
   1403     } else {
   1404         printf("Sim: new external runtime wants to talk to us\n");
   1405 
   1406         /*
   1407          * Launch the pieces necessary to talk to this guy.
   1408          */
   1409         int id = GetSelectedDeviceIndex();
   1410         if (id < 0) {
   1411             fprintf(stderr,
   1412                 "Sim: could not identify currently selected device\n");
   1413             goto bail;
   1414         }
   1415 
   1416         /* kill existing window, so it pops up and reclaims focus */
   1417         if (mpPhoneWindow != NULL) {
   1418             Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
   1419             bool okay;
   1420 
   1421             if (pPrefs->GetBool("refocus-on-restart", &okay) && okay) {
   1422                 printf("Sim: inducing phone window refocus\n");
   1423                 mpPhoneWindow->Close(TRUE);     // no veto
   1424                 mpPhoneWindow = NULL;
   1425             }
   1426         }
   1427 
   1428         SetupPhoneUI(id, NULL);
   1429         if (mpPhoneWindow != NULL) {
   1430             mpPhoneWindow->GetDeviceManager()->StartRuntime(reader, writer);
   1431         } else {
   1432             fprintf(stderr, "Sim: ERROR: unable to get runtime going\n");
   1433             goto bail;
   1434         }
   1435 
   1436         // we don't own these anymore
   1437         reader = writer = NULL;
   1438     }
   1439 
   1440 bail:
   1441     delete reader;
   1442     delete writer;
   1443 }
   1444 
   1445 /*
   1446  * The phone window is about to destroy itself.  Get rid of our pointer
   1447  * to it, and record its last position so we can create the new one in
   1448  * the same place.
   1449  */
   1450 void MainFrame::PhoneWindowClosing(int x, int y)
   1451 {
   1452     Preferences* pPrefs = ((MyApp*)wxTheApp)->GetPrefs();
   1453 
   1454     mpPhoneWindow = NULL;
   1455 
   1456     mPhoneWindowPosn.x = x;
   1457     mPhoneWindowPosn.y = y;
   1458 
   1459     pPrefs->SetInt("window-device-x", x);
   1460     pPrefs->SetInt("window-device-y", y);
   1461 }
   1462 
   1463