Home | History | Annotate | Download | only in app
      1 //
      2 // Copyright 2005 The Android Open Source Project
      3 //
      4 // Simulated device data.
      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/image.h"   // needed for Windows build
     15 
     16 
     17 #include "PhoneData.h"
     18 #include "PhoneButton.h"
     19 #include "PhoneCollection.h"
     20 #include "MyApp.h"
     21 
     22 #include "utils.h"
     23 #include <utils/AssetManager.h>
     24 #include <utils/String8.h>
     25 
     26 #include "tinyxml.h"
     27 
     28 #include <stdlib.h>
     29 #include <unistd.h>
     30 #include <stdio.h>
     31 #include <string.h>
     32 #include <assert.h>
     33 
     34 using namespace android;
     35 
     36 /* image relative path hack */
     37 static const char* kRelPathMagic = "::/";
     38 
     39 
     40 /*
     41  * ===========================================================================
     42  *      PhoneKeyboard
     43  * ===========================================================================
     44  */
     45 
     46 /*
     47  * Load a <keyboard> chunk.
     48  */
     49 bool PhoneKeyboard::ProcessAndValidate(TiXmlNode* pNode)
     50 {
     51     //TiXmlNode* pChild;
     52     TiXmlElement* pElem;
     53     int qwerty = 0;
     54 
     55     assert(pNode->Type() == TiXmlNode::ELEMENT);
     56 
     57     pElem = pNode->ToElement();
     58     pElem->Attribute("qwerty", &qwerty);
     59     const char *kmap = pElem->Attribute("keycharmap");
     60 
     61     if (qwerty == 1) {
     62         printf("############## PhoneKeyboard::ProcessAndValidate: qwerty = true!\n");
     63         mQwerty = true;
     64     }
     65 
     66     if (kmap != NULL) {
     67         printf("############## PhoneKeyboard::ProcessAndValidate: keycharmap = %s\n", kmap);
     68         mKeyMap = strdup(kmap);
     69     }
     70 
     71     return true;
     72 }
     73 
     74 
     75 /*
     76  * ===========================================================================
     77  *      PhoneDisplay
     78  * ===========================================================================
     79  */
     80 
     81 /*
     82  * Load a <display> chunk.
     83  */
     84 bool PhoneDisplay::ProcessAndValidate(TiXmlNode* pNode)
     85 {
     86     //TiXmlNode* pChild;
     87     TiXmlElement* pElem;
     88     const char* name;
     89     const char* format;
     90 
     91     assert(pNode->Type() == TiXmlNode::ELEMENT);
     92 
     93     /*
     94      * Process attributes.  Right now they're all mandatory, but some of
     95      * them could be defaulted (e.g. "rotate").
     96      *
     97      * [We should do some range-checking here.]
     98      */
     99     pElem = pNode->ToElement();
    100     name = pElem->Attribute("name");
    101     if (name == NULL)
    102         goto missing;
    103     if (pElem->Attribute("width", &mWidth) == NULL)
    104         goto missing;
    105     if (pElem->Attribute("height", &mHeight) == NULL)
    106         goto missing;
    107     if (pElem->Attribute("refresh", &mRefresh) == NULL)
    108         goto missing;
    109     format = pElem->Attribute("format");
    110     if (format == NULL)
    111         goto missing;
    112 
    113     delete[] mName;
    114     mName = strdupNew(name);
    115 
    116     if (strcasecmp(format, "rgb565") == 0) {
    117         mFormat = android::PIXEL_FORMAT_RGB_565;
    118     } else {
    119         fprintf(stderr, "SimCFG: unexpected value for display format\n");
    120         return false;
    121     }
    122 
    123     return true;
    124 
    125 missing:
    126     fprintf(stderr,
    127         "SimCFG: <display> requires name/width/height/format/refresh\n");
    128     return false;
    129 }
    130 
    131 
    132 /*
    133  * Returns "true" if the two displays are compatible, "false" if not.
    134  *
    135  * Compatibility means they have the same resolution, format, refresh
    136  * rate, and so on.  Anything transmitted to the runtime as part of the
    137  * initial configuration setup should be tested.
    138  */
    139 /*static*/ bool PhoneDisplay::IsCompatible(PhoneDisplay* pDisplay1,
    140     PhoneDisplay* pDisplay2)
    141 {
    142     return (pDisplay1->mWidth == pDisplay2->mWidth &&
    143             pDisplay1->mHeight == pDisplay2->mHeight &&
    144             pDisplay1->mFormat == pDisplay2->mFormat &&
    145             pDisplay1->mRefresh == pDisplay2->mRefresh);
    146 }
    147 
    148 
    149 /*
    150  * ===========================================================================
    151  *      PhoneView
    152  * ===========================================================================
    153  */
    154 
    155 /*
    156  * Load a <view> chunk.
    157  */
    158 bool PhoneView::ProcessAndValidate(TiXmlNode* pNode, const char* directory)
    159 {
    160     TiXmlNode* pChild;
    161     TiXmlElement* pElem;
    162     int rotate;
    163     const char* displayName;
    164 
    165     assert(pNode->Type() == TiXmlNode::ELEMENT);
    166 
    167     /*
    168      * Process attributes.  Right now they're all mandatory, but some of
    169      * them could be defaulted (e.g. "rotate").
    170      *
    171      * [We should do some range-checking here.]
    172      */
    173     pElem = pNode->ToElement();
    174     displayName = pElem->Attribute("display");
    175     if (displayName == NULL)
    176         goto missing;
    177     if (pElem->Attribute("x", &mXOffset) == NULL)
    178         goto missing;
    179     if (pElem->Attribute("y", &mYOffset) == NULL)
    180         goto missing;
    181     if (pElem->Attribute("rotate", &rotate) == NULL)
    182         goto missing;
    183 
    184     switch (rotate) {
    185     case 0:     mRotation = kRot0;      break;
    186     case 90:    mRotation = kRot90;     break;
    187     case 180:   mRotation = kRot180;    break;
    188     case 270:   mRotation = kRot270;    break;
    189     default:
    190                 fprintf(stderr, "SimCFG: unexpected value for rotation\n");
    191                 mRotation = kRotUnknown;
    192                 return false;
    193     }
    194 
    195     delete[] mDisplayName;
    196     mDisplayName = android::strdupNew(displayName);
    197 
    198     /*
    199      * Process elements.
    200      */
    201     for (pChild = pNode->FirstChild(); pChild != NULL;
    202         pChild = pChild->NextSibling())
    203     {
    204         if (pChild->Type() == TiXmlNode::COMMENT)
    205             continue;
    206 
    207         if (pChild->Type() == TiXmlNode::ELEMENT) {
    208             if (strcasecmp(pChild->Value(), "image") == 0) {
    209                 if (!ProcessImage(pChild, directory))
    210                     return false;
    211             } else if (strcasecmp(pChild->Value(), "button") == 0) {
    212                 if (!ProcessButton(pChild, directory))
    213                     return false;
    214             } else {
    215                 fprintf(stderr,
    216                     "SimCFG: Warning: unexpected elements in <display>\n");
    217             }
    218         } else {
    219             fprintf(stderr, "SimCFG: Warning: unexpected stuff in <display>\n");
    220         }
    221     }
    222 
    223     return true;
    224 
    225 missing:
    226     fprintf(stderr,
    227         "SimCFG: <view> requires display/x/y/rotate\n");
    228     return false;
    229 }
    230 
    231 /*
    232  * Handle <image src="zzz" x="123" y="123"/>.
    233  */
    234 bool PhoneView::ProcessImage(TiXmlNode* pNode, const char* directory)
    235 {
    236     TiXmlNode* pChild;
    237     TiXmlElement* pElem;
    238     int x, y;
    239     const char* src;
    240     LoadableImage tmpLimg;
    241     android::String8 fileName;
    242 
    243     pChild = pNode->FirstChild();
    244     if (pChild != NULL) {
    245         fprintf(stderr, "SimCFG: <image> is funky\n");
    246         return false;
    247     }
    248 
    249     /*
    250      * All attributes are mandatory.
    251      */
    252     pElem = pNode->ToElement();
    253     src = pElem->Attribute("src");
    254     if (src == NULL)
    255         goto missing;
    256     if (pElem->Attribute("x", &x) == NULL)
    257         goto missing;
    258     if (pElem->Attribute("y", &y) == NULL)
    259         goto missing;
    260 
    261     if (strncmp(src, kRelPathMagic, strlen(kRelPathMagic)) == 0) {
    262         fileName = src + strlen(kRelPathMagic);
    263     } else {
    264         fileName = directory;
    265         fileName += "/";
    266         fileName += src;
    267     }
    268 
    269     tmpLimg.Create(fileName, x, y);
    270     mImageList.push_back(tmpLimg);
    271 
    272     return true;
    273 
    274 missing:
    275     fprintf(stderr, "SimCFG: <image> requires src/x/y\n");
    276     return false;
    277 }
    278 
    279 /*
    280  * Handle <button keyCode="zzz" src="zzz" x="123" y="123"/> and
    281  * <button keyCode="zzz"/>.
    282  */
    283 bool PhoneView::ProcessButton(TiXmlNode* pNode, const char* directory)
    284 {
    285     TiXmlNode* pChild;
    286     TiXmlElement* pElem;
    287     int x, y;
    288     const char* keyCode;
    289     const char* src;
    290     PhoneButton tmpButton;
    291     android::String8 fileName;
    292 
    293     pChild = pNode->FirstChild();
    294     if (pChild != NULL) {
    295         fprintf(stderr, "SimCFG: button is funky\n");
    296         return false;
    297     }
    298 
    299     /*
    300      * Only keyCode is mandatory.  If they specify "src", then "x" and "y"
    301      * are also required.
    302      */
    303     pElem = pNode->ToElement();
    304     keyCode = pElem->Attribute("keyCode");
    305     if (keyCode == NULL)
    306         goto missing;
    307 
    308     src = pElem->Attribute("src");
    309     if (src != NULL) {
    310         if (pElem->Attribute("x", &x) == NULL)
    311             goto missing;
    312         if (pElem->Attribute("y", &y) == NULL)
    313             goto missing;
    314     }
    315 
    316     if (src == NULL)
    317         tmpButton.Create(keyCode);
    318     else {
    319         if (strncmp(src, kRelPathMagic, strlen(kRelPathMagic)) == 0) {
    320             fileName = src + strlen(kRelPathMagic);
    321         } else {
    322             fileName = directory;
    323             fileName += "/";
    324             fileName += src;
    325         }
    326         tmpButton.Create(keyCode, fileName, x, y);
    327     }
    328 
    329     mButtonList.push_back(tmpButton);
    330 
    331     return true;
    332 
    333 missing:
    334     fprintf(stderr, "SimCFG: <button> requires keycode and may have src/x/y\n");
    335     return false;
    336 }
    337 
    338 
    339 /*
    340  * Load all resources associated with the display.
    341  */
    342 bool PhoneView::LoadResources(void)
    343 {
    344     typedef List<LoadableImage>::iterator LIter;
    345     typedef List<PhoneButton>::iterator BIter;
    346 
    347     for (LIter ii = mImageList.begin(); ii != mImageList.end(); ++ii)
    348         (*ii).LoadResources();
    349     for (BIter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii)
    350         (*ii).LoadResources();
    351     return true;
    352 }
    353 
    354 /*
    355  * Unload all resources associated with the display.
    356  */
    357 bool PhoneView::UnloadResources(void)
    358 {
    359     typedef List<LoadableImage>::iterator LIter;
    360     typedef List<PhoneButton>::iterator BIter;
    361 
    362     for (LIter ii = mImageList.begin(); ii != mImageList.end(); ++ii)
    363         (*ii).UnloadResources();
    364     for (BIter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii)
    365         (*ii).UnloadResources();
    366     return true;
    367 }
    368 
    369 
    370 /*
    371  * Get the #of images.
    372  */
    373 int PhoneView::GetBkgImageCount(void) const
    374 {
    375     return mImageList.size();
    376 }
    377 
    378 /*
    379  * Return the Nth entry.
    380  */
    381 const LoadableImage* PhoneView::GetBkgImage(int idx) const
    382 {
    383     typedef List<LoadableImage>::const_iterator Iter;
    384 
    385     for (Iter ii = mImageList.begin(); ii != mImageList.end(); ++ii) {
    386         if (!idx)
    387             return &(*ii);
    388         --idx;
    389     }
    390 
    391     return NULL;
    392 }
    393 
    394 
    395 /*
    396  * Find the first button that covers the specified coordinates.
    397  *
    398  * The coordinates must be relative to the upper left corner of the
    399  * phone image.
    400  */
    401 PhoneButton* PhoneView::FindButtonHit(int x, int y)
    402 {
    403     typedef List<PhoneButton>::iterator Iter;
    404 
    405     for (Iter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii) {
    406         if ((*ii).CheckCollision(x, y))
    407             return &(*ii);
    408     }
    409 
    410     return NULL;
    411 }
    412 
    413 /*
    414  * Find the first button with a matching key code.
    415  */
    416 PhoneButton* PhoneView::FindButtonByKey(int32_t keyCode)
    417 {
    418     typedef List<PhoneButton>::iterator Iter;
    419 
    420     for (Iter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii) {
    421         if ((*ii).GetKeyCode() == keyCode)
    422             return &(*ii);
    423     }
    424 
    425     return NULL;
    426 }
    427 
    428 
    429 /*
    430  * ===========================================================================
    431  *      PhoneMode
    432  * ===========================================================================
    433  */
    434 
    435 /*
    436  * Process a <mode name="zzz"> chunk.
    437  */
    438 bool PhoneMode::ProcessAndValidate(TiXmlNode* pNode, const char* directory)
    439 {
    440     TiXmlNode* pChild;
    441     const char* name;
    442 
    443     assert(pNode->Type() == TiXmlNode::ELEMENT);
    444 
    445     name = pNode->ToElement()->Attribute("name");
    446     if (name == NULL) {
    447         fprintf(stderr, "SimCFG: <mode> requires name attrib\n");
    448         return false;
    449     }
    450     SetName(name);
    451 
    452     for (pChild = pNode->FirstChild(); pChild != NULL;
    453         pChild = pChild->NextSibling())
    454     {
    455         if (pChild->Type() == TiXmlNode::COMMENT)
    456             continue;
    457 
    458         if (pChild->Type() == TiXmlNode::ELEMENT &&
    459             strcasecmp(pChild->Value(), "view") == 0)
    460         {
    461             PhoneView tmpDisplay;
    462             bool result;
    463 
    464             result = tmpDisplay.ProcessAndValidate(pChild, directory);
    465             if (!result)
    466                 return false;
    467 
    468             mViewList.push_back(tmpDisplay);
    469         } else {
    470             fprintf(stderr, "SimCFG: Warning: unexpected stuff in <mode>\n");
    471         }
    472     }
    473 
    474     if (mViewList.size() == 0) {
    475         fprintf(stderr, "SimCFG: no <view> entries found\n");
    476         return false;
    477     }
    478 
    479     return true;
    480 }
    481 
    482 
    483 /*
    484  * Load all resources associated with the phone.
    485  */
    486 bool PhoneMode::LoadResources(void)
    487 {
    488     typedef List<PhoneView>::iterator Iter;
    489 
    490     for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii)
    491         (*ii).LoadResources();
    492     return true;
    493 }
    494 
    495 /*
    496  * Unload all resources associated with the phone.
    497  */
    498 bool PhoneMode::UnloadResources(void)
    499 {
    500     typedef List<PhoneView>::iterator Iter;
    501 
    502     for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii)
    503         (*ii).UnloadResources();
    504     return true;
    505 }
    506 
    507 
    508 /*
    509  * Return the Nth entry.  [make this a Vector?]
    510  */
    511 PhoneView* PhoneMode::GetPhoneView(int viewNum)
    512 {
    513     typedef List<PhoneView>::iterator Iter;
    514 
    515     for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii) {
    516         if (viewNum == 0)
    517             return &(*ii);
    518         --viewNum;
    519     }
    520     return NULL;
    521 }
    522 
    523 
    524 /*
    525  * ===========================================================================
    526  *      PhoneData
    527  * ===========================================================================
    528  */
    529 
    530 
    531 /*
    532  * Look for a "layout.xml" in the specified directory.  If found, parse
    533  * the contents out.
    534  *
    535  * Returns "true" on success, "false" on failure.
    536  */
    537 bool PhoneData::Create(const char* directory)
    538 {
    539     android::String8 fileName;
    540 
    541     SetDirectory(directory);
    542 
    543     fileName = directory;
    544     fileName += "/";
    545     fileName += PhoneCollection::kLayoutFile;
    546 
    547 #ifdef BEFORE_ASSET
    548     TiXmlDocument doc(fileName);
    549     if (!doc.LoadFile())
    550 #else
    551     android::AssetManager* pAssetMgr = ((MyApp*)wxTheApp)->GetAssetManager();
    552     TiXmlDocument doc;
    553     android::Asset* pAsset;
    554     bool result;
    555 
    556     pAsset = pAssetMgr->open(fileName, Asset::ACCESS_STREAMING);
    557     if (pAsset == NULL) {
    558         fprintf(stderr, "Unable to open asset '%s'\n", (const char*) fileName);
    559         return false;
    560     } else {
    561         //printf("--- opened asset '%s'\n",
    562         //    (const char*) pAsset->getAssetSource());
    563     }
    564 
    565     /* TinyXml insists that the buffer be NULL-terminated... ugh */
    566     char* buf = new char[pAsset->getLength() +1];
    567     pAsset->read(buf, pAsset->getLength());
    568     buf[pAsset->getLength()] = '\0';
    569 
    570     delete pAsset;
    571     result = doc.Parse(buf);
    572     delete[] buf;
    573 
    574     if (!result)
    575 #endif
    576     {
    577         fprintf(stderr, "SimCFG: ERROR: failed parsing '%s'\n",
    578             (const char*) fileName);
    579         if (doc.ErrorRow() != 0)
    580             fprintf(stderr, "    XML: %s (row=%d col=%d)\n",
    581                 doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
    582         else
    583             fprintf(stderr, "    XML: %s\n", doc.ErrorDesc());
    584         return false;
    585     }
    586 
    587     if (!ProcessAndValidate(&doc)) {
    588         fprintf(stderr, "SimCFG: ERROR: failed analyzing '%s'\n",
    589             (const char*) fileName);
    590         return false;
    591     }
    592 
    593     printf("SimCFG: loaded data from '%s'\n", (const char*) fileName);
    594 
    595     return true;
    596 }
    597 
    598 /*
    599  * TinyXml has loaded and parsed the XML document for us.  We need to
    600  * run through the DOM tree, pull out the interesting bits, and make
    601  * sure the stuff we need is present.
    602  *
    603  * Returns "true" on success, "false" on failure.
    604  */
    605 bool PhoneData::ProcessAndValidate(TiXmlDocument* pDoc)
    606 {
    607     bool deviceFound = false;
    608     TiXmlNode* pChild;
    609 
    610     assert(pDoc->Type() == TiXmlNode::DOCUMENT);
    611 
    612     for (pChild = pDoc->FirstChild(); pChild != NULL;
    613         pChild = pChild->NextSibling())
    614     {
    615         /*
    616          * Find the <device> entry.  There should be exactly one.
    617          */
    618         if (pChild->Type() == TiXmlNode::ELEMENT) {
    619             if (strcasecmp(pChild->Value(), "device") != 0) {
    620                 fprintf(stderr,
    621                     "SimCFG: Warning: unexpected element '%s' at top level\n",
    622                     pChild->Value());
    623                 continue;
    624             }
    625             if (deviceFound) {
    626                 fprintf(stderr, "SimCFG: one <device> per customer\n");
    627                 return false;
    628             }
    629 
    630             bool result = ProcessDevice(pChild);
    631             if (!result)
    632                 return false;
    633             deviceFound = true;
    634         }
    635     }
    636 
    637     if (!deviceFound) {
    638         fprintf(stderr, "SimCFG: no <device> section found\n");
    639         return false;
    640     }
    641 
    642     return true;
    643 }
    644 
    645 /*
    646  * Process a <device name="zzz"> chunk.
    647  */
    648 bool PhoneData::ProcessDevice(TiXmlNode* pNode)
    649 {
    650     TiXmlNode* pChild;
    651     const char* name;
    652 
    653     assert(pNode->Type() == TiXmlNode::ELEMENT);
    654 
    655     name = pNode->ToElement()->Attribute("name");
    656     if (name == NULL) {
    657         fprintf(stderr, "SimCFG: <device> requires name attrib\n");
    658         return false;
    659     }
    660     SetName(name);
    661 
    662     /*
    663      * Walk through the children and find interesting stuff.
    664      *
    665      * Might be more correct to process all <display> entries and
    666      * then process all <view> entries, since <view> has "pointers"
    667      * to <display>.  We're deferring the lookup until later, though,
    668      * so for now it doesn't really matter.
    669      */
    670     for (pChild = pNode->FirstChild(); pChild != NULL;
    671         pChild = pChild->NextSibling())
    672     {
    673         bool result;
    674 
    675         if (pChild->Type() == TiXmlNode::COMMENT)
    676             continue;
    677 
    678         if (pChild->Type() == TiXmlNode::ELEMENT &&
    679             strcasecmp(pChild->Value(), "title") == 0)
    680         {
    681             result = ProcessTitle(pChild);
    682             if (!result)
    683                 return false;
    684         } else if (pChild->Type() == TiXmlNode::ELEMENT &&
    685             strcasecmp(pChild->Value(), "display") == 0)
    686         {
    687             PhoneDisplay tmpDisplay;
    688 
    689             result = tmpDisplay.ProcessAndValidate(pChild);
    690             if (!result)
    691                 return false;
    692 
    693             mDisplayList.push_back(tmpDisplay);
    694         } else if (pChild->Type() == TiXmlNode::ELEMENT &&
    695             strcasecmp(pChild->Value(), "keyboard") == 0)
    696         {
    697             PhoneKeyboard tmpKeyboard;
    698             result = tmpKeyboard.ProcessAndValidate(pChild);
    699             if (!result)
    700                 return false;
    701 
    702             mKeyboardList.push_back(tmpKeyboard);
    703         } else if (pChild->Type() == TiXmlNode::ELEMENT &&
    704             strcasecmp(pChild->Value(), "mode") == 0)
    705         {
    706             PhoneMode tmpMode;
    707 
    708             result = tmpMode.ProcessAndValidate(pChild, mDirectory);
    709             if (!result)
    710                 return false;
    711 
    712             mModeList.push_back(tmpMode);
    713         } else {
    714             fprintf(stderr, "SimCFG: Warning: unexpected stuff in <device>\n");
    715         }
    716     }
    717 
    718     if (mDisplayList.size() == 0) {
    719         fprintf(stderr, "SimCFG: no <display> entries found\n");
    720         return false;
    721     }
    722     if (mModeList.size() == 0) {
    723         fprintf(stderr, "SimCFG: no <mode> entries found\n");
    724         return false;
    725     }
    726 
    727     return true;
    728 }
    729 
    730 /*
    731  * Handle <title>.
    732  */
    733 bool PhoneData::ProcessTitle(TiXmlNode* pNode)
    734 {
    735     TiXmlNode* pChild;
    736 
    737     pChild = pNode->FirstChild();
    738     if (pChild->Type() != TiXmlNode::TEXT) {
    739         fprintf(stderr, "SimCFG: title is funky\n");
    740         return false;
    741     }
    742 
    743     SetTitle(pChild->Value());
    744     return true;
    745 }
    746 
    747 
    748 /*
    749  * Load all resources associated with the phone.
    750  */
    751 bool PhoneData::LoadResources(void)
    752 {
    753     typedef List<PhoneMode>::iterator Iter;
    754 
    755     for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii)
    756         (*ii).LoadResources();
    757     return true;
    758 }
    759 
    760 /*
    761  * Unload all resources associated with the phone.
    762  */
    763 bool PhoneData::UnloadResources(void)
    764 {
    765     typedef List<PhoneMode>::iterator Iter;
    766 
    767     for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii)
    768         (*ii).UnloadResources();
    769     return true;
    770 }
    771 
    772 
    773 /*
    774  * Return the PhoneMode entry with the matching name.
    775  *
    776  * Returns NULL if no match was found.
    777  */
    778 PhoneMode* PhoneData::GetPhoneMode(const char* modeName)
    779 {
    780     typedef List<PhoneMode>::iterator Iter;
    781 
    782     for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii) {
    783         if (strcmp((*ii).GetName(), modeName) == 0)
    784             return &(*ii);
    785     }
    786     return NULL;
    787 }
    788 
    789 /*
    790  * Return the Nth phone mode entry.
    791  */
    792 PhoneMode* PhoneData::GetPhoneMode(int idx)
    793 {
    794     typedef List<PhoneMode>::iterator Iter;
    795 
    796     for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii) {
    797         if (!idx)
    798             return &(*ii);
    799         --idx;
    800     }
    801     return NULL;
    802 }
    803 
    804 
    805 /*
    806  * Return the PhoneDisplay entry with the matching name.
    807  *
    808  * Returns NULL if no match was found.
    809  */
    810 PhoneDisplay* PhoneData::GetPhoneDisplay(const char* dispName)
    811 {
    812     typedef List<PhoneDisplay>::iterator Iter;
    813 
    814     for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) {
    815         if (strcmp((*ii).GetName(), dispName) == 0)
    816             return &(*ii);
    817     }
    818     return NULL;
    819 }
    820 
    821 /*
    822  * Return the Nth phone mode entry.
    823  */
    824 PhoneDisplay* PhoneData::GetPhoneDisplay(int idx)
    825 {
    826     typedef List<PhoneDisplay>::iterator Iter;
    827 
    828     for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) {
    829         if (!idx)
    830             return &(*ii);
    831         --idx;
    832     }
    833     return NULL;
    834 }
    835 
    836 /*
    837  * Find the PhoneDisplay entry with the matching name, and return its index.
    838  *
    839  * Returns -1 if the entry wasn't found.
    840  */
    841 int PhoneData::GetPhoneDisplayIndex(const char* dispName)
    842 {
    843     typedef List<PhoneDisplay>::iterator Iter;
    844     int idx = 0;
    845 
    846     for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) {
    847         if (strcmp((*ii).GetName(), dispName) == 0)
    848             return idx;
    849         idx++;
    850     }
    851     return -1;
    852 }
    853 
    854 
    855 /*
    856  * Return the Nth phone keyboard entry.
    857  */
    858 PhoneKeyboard* PhoneData::GetPhoneKeyboard(int idx)
    859 {
    860     typedef List<PhoneKeyboard>::iterator Iter;
    861 
    862     for (Iter ii = mKeyboardList.begin(); ii != mKeyboardList.end(); ++ii) {
    863         if (!idx)
    864             return &(*ii);
    865         --idx;
    866     }
    867     return NULL;
    868 }
    869