posted by Jean-Louis Villecroze on Wed 8th Dec 2004 21:40 UTC

"Zinzala, Page 11/11"
cDConsoleView

In the method ReSkinL() of our console class, we perform something similar to what we have done in ConstructL(). We are going to instantiate all the bitmaps we need from the skin, then replace the old ones by the new ones. However, because the method can leave anytime, we only need to replace the old bitmaps once all the new ones have been created. This implies that we must use the cleanup stack to insure that all the bitmaps loaded before will be destroyed if we leave when we are instantiating the last bitmap of the skin. It also requires us to keep the newly created bitmap in some temporary array until they replace the old ones.

Here's the method implementation:

void cDConsoleView::ReSkinL(cDSkin &aSkin)
{
   cBitmap *lBackground;
   cBitmap *lBStates[kStatesCount];
   cBitmap *lBLabelsN[kLabelsCount];
   cBitmap *lBLabelsD[kLabelsCount];

   // Leave if the widget is faulty
   sEnv::LeaveIfError(iFault);

   // Instantiate the background bitmap
   lBackground             = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResBackground),false);

   // Instantiate the button bitmaps
   lBStates[kStateNormal]  = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResNormal));
   lBStates[kStateDimmed]  = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResDimmed));
   lBStates[kStatePressed] = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResPressed));
   lBStates[kStateActive]  = lBStates[kStateNormal]; // use same bitmap as the normal state
   lBStates[kStateActive1] = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResAnimate1));
   lBStates[kStateActive2] = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResAnimate2));
   lBStates[kStateActive3] = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResAnimate3));
   // Instantiate the label bitmaps
   lBLabelsN[kLabelBwd]    = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResBackwardN));
   lBLabelsN[kLabelFwd]    = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResForwardN));
   lBLabelsN[kLabelStop]   = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResStopN));
   lBLabelsN[kLabelPlay]   = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResPlayN));
   lBLabelsN[kLabelPause]  = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResPauseN));
   lBLabelsD[kLabelBwd]    = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResBackwardD));
   lBLabelsD[kLabelFwd]    = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResForwardD));
   lBLabelsD[kLabelStop]   = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResStopD));
   lBLabelsD[kLabelPlay]   = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResPlayD));
   lBLabelsD[kLabelPause]  = CreateBitmapFromResourceLC(aSkin.GetResourceL(eResPauseD));

   // delete all the bitmap we currently use
   // and assign the newly loaded bitmaps
   delete iBackground;
   iBackground = lBackground;

   for(tUint8 i=0;i<kStatesCount;i++)
   {
      if(iBStates[i] && i!=kStateActive) // Active and Normal states share the same bitmap
         delete iBStates[i];
      iBStates[i] = lBStates[i];
   }

   for(tUint8 i=0;i<kLabelsCount;i++)
      if(iBLabelsN[i])
      {
         delete iBLabelsN[i];
         iBLabelsN[i] = lBLabelsN[i];
      }

   for(tUint8 i=0;i<kLabelsCount;i++)
      if(iBLabelsD[i])
      {
         delete iBLabelsD[i];
         iBLabelsD[i] = lBLabelsD[i];
      }
	
   // remove the bitmaps from the cleanupstack
   sCleanupStack::PopL(17,lBackground);

   Render(kButtonsCount,true);
}

Instead of using the method CreateBitmapFromResourceL that we implemented earlier, we are using one that keeps the created bitmap on the cleanup stack even if there is no exception. Once we have replaced all the old bitmaps by the new ones, at the end of ReSkinL(), we pop all the 17 objects we have placed on the cleanup stack. The method PopL() verifies that the last object to remove is the background bitmap. If this is not the case, something is wrongx and the method will throw an exception. If everything goes according to plan, we can then redraw the whole console.

The method CreateBitmapFromResourceLC isn't much different from the one we did earlier. It simply pushes the bitmap on the cleanup stack:

cBitmap *CreateBitmapFromResourceLC(const tBitmapResource &aRes,tBool aTransparent = true)
{
   tErr     lErr;
   cBitmap *lBitmap = NULL;

   lBitmap  = new cBitmap(aRes.iWidth,aRes.iHeight,eSpaceColor8Bit,true,aRes.iColors);
   lErr     = cBitmap::VerifyD(lBitmap);

   if(!lErr)
   {	
      sCleanupStack::PushL(lBitmap);
      lBitmap->SetPalette(aRes.iPalette);
      lBitmap->SetBits(aRes.iBits,aRes.iLength,0);
      if(aTransparent)
         lBitmap->MakeTransparent(aRes.iIndex);
      return lBitmap;
   }
   else
      throw uException(lErr,"CreateBitmapFromResourceLC()");
}

Now, let's add some extra scripting abilities to the console widget itself by supporting invocations of any of the console's buttons. By doing so, we allow remote control and automated testing capabilities, since the console can be invoked from anywhere:

#> yo hexaZen/Custom tell window.console invoke play

Because by default the widgets do not allow scripting, we are going to allow it in the window ConstructL() method:

void cDWindow::ConstructL(cDSkin &aSkin)
{
   sEnv::LeaveIfError(iFault);

   iConsole = cMyConsoleView::NewL(aSkin);
   iConsole->AllowScripting(eLocal);
   AddChild(iConsole);
}

Then, we will be declaring the method ScriptingMessageReceived() in the console class and implementing it. Here it is:

void cDConsoleView::ScriptingReceived(cMessage *aMessage,cMessage *aReply)
{	
   tUint32 lCmd;

   aReply->SetBool(kScriptItemDone,true);

   if(!aMessage->GetUint32(kScriptItemCommand,&lCmd))
   {
      switch(lCmd)
      {
         case kScriptCmdInvoke:
         {
            const tChar *lArg;

	    // if a button is already pressed, we will ignore this command
            if(iPressedButton == kButtonsCount)
            {
               if(!aMessage->GetString(kScriptItemArgs,&lArg))
               {
                  if(!strcmp(lArg,kScrButtonPlay) || !strcmp(lArg,kScrButtonStop))
                  {
                     Pressed(kButtonPlay);
                     Invoked();
                  }
                  else
                  if(!strcmp(lArg,kScrButtonPause))
                  {
                     Pressed(kButtonPause);
                     Invoked();
                  }
                  else
                  if(!strcmp(lArg,kScrButtonBwd))
                  {
                     Pressed(kButtonBwd);
                     Invoked();
                  }
                  else
                  if(!strcmp(lArg,kScrButtonFwd))
                  {
                     Pressed(kButtonFwd);
                     Invoked();
                  }
                  else
                  {
                     aReply->SetBool(kScriptItemDone,false);
                     aReply->SetUint8(kScriptItemReason,kScriptErrIncorrect);
                  }
               }
               else
               {
                  aReply->SetBool(kScriptItemDone,false);
                  aReply->SetUint8(kScriptItemReason,kScriptErrMissingArg);
               }
            }
			
            break;
         }
         default:
            cView::ScriptingReceived(aMessage,aReply);	
      }
   }
   else
   {
      aReply->SetBool(kScriptItemDone,false);
      aReply->SetUint8(kScriptItemReason,kScriptErrMalformed);
   }
}

If the command in the scripting message is kScriptCmdInvoke, we retrieve its argument and compare it to the label of the console's buttons. Here's the declaration of the constants we are using to distinguish the different buttons invoked:

const tChar *kScrButtonPlay   = "play";
const tChar *kScrButtonStop   = "stop";
const tChar *kScrButtonBwd    = "bwd";
const tChar *kScrButtonFwd    = "fwd";
const tChar *kScrButtonPause  = "pause";

If we were to add scriptability to all the widgets of the UI, we could then support automated testing of the applications. This will allow for a more consistent approach to testing since it is fully documented, repeatable and very flexible. Saving developers from painful manual testing also helps to keep their focus on more rewarding activities. Because it is repeatable, regression testing can be performed on a regular basis. In short, scriptability can help your product achieve a better quality in a relatively painless way. It can also ease the integration of an application in a network of collaborative applications.

6. Conclusion

By using object-oriented programming, we were able to quickly build a custom widget. Inheritance permits the use of the widget in many applications, saving time and effort. The Zinzala SDK can leverage the quality of your embedded systems, while making developers lives easier. If you are developing set-top boxes, using object-oriented techniques will help you lower the time to market of your product. A goal worth the cost of the little overhead that C++ will bring.

7. Acknowledgment

The author would like to thank Chris McKillop and Eugenia Loli-Queru for reviewing this article and giving valuable feedback. Thanks also goes out to Susan for providing the various graphics and for proofreading this article.


If you would like to see your thoughts or experiences with technology published, please consider writing an article for OSNews.
Table of contents
  1. "Zinzala, Page 1/11"
  2. "Zinzala, Page 2/11"
  3. "Zinzala, Page 3/11"
  4. "Zinzala, Page 4/11"
  5. "Zinzala, Page 5/11"
  6. "Zinzala, Page 6/11"
  7. "Zinzala, Page 7/11"
  8. "Zinzala, Page 8/11"
  9. "Zinzala, Page 9/11"
  10. "Zinzala, Page 10/11"
  11. "Zinzala, Page 11/11"
e p (0)    12 Comment(s)

Technology White Papers

See More