BREW & J2ME: Let's Be Friends!, Page 3
What's a List?
BREW doesn't provide a List, but a MenuCtl. The short answer is "a List is a IDisplayable exposing MenuCtl functionality."
template <class T,
class P=ListStrategy,
class E = ErrorHandler>
class ListImpl : public IDisplayable
{
typedef bool (T::*FNC)(ListImpl<T,P,E>*);
public:
static ListImpl* getList(
const WString& title, T* t)
{
ListImpl* l = createList(t);
if (l)
{
l->setTitle(title);
l->setFullScreen();
}
return l;
}
virtual ~ListImpl()
{
if (list_) IMENUCTL_Release(list_);
}
void append(const WString& item)
{
if (!rr_)
return;
int id = rr_->getNextAvailableID();
IMENUCTL_AddItem(list_, 0, 0, id,
const_cast<AECHAR*>
(item.toCharArray()), 0);
indxs_.append(id);
}
void append(const WString items[]s, int sz)
{
for(int i=0; i < sz; ++i)
{
append(items[i]);
}
}
virtual IControl* getControl() const
{
return reinterpret_cast<IControl*>(list_);
}
bool onCmd()
{
if (fnc_ && t_)
return (*t_.*fnc_)(this);
return false;
}
int getSelectedIndex() const
{
return selected_;
}
virtual bool containsItem(int ix)
{
selected_ = getIDImpl(ix);
return (selected_ != INDEX_OUT_OF_BOUNDS);
}
virtual int getID() const
{
return id_;
}
void setCbk(FNC f)
{
fnc_ = f;
}
void setSelection(int id)
{
int lid = IMENUCTL_GetItemID(list_, id);
IMENUCTL_SetSel(list_, lid);
}
int size() const
{
return indxs_.size();
}
void setTitle(const WString& title)
{
if (title.length())
IMENUCTL_SetTitle(list_, NULL, 0, const_cast<AECHAR*>
(title.toCharArray()));
}
private:
ListImpl( T* t, AEECLSID cid ): list_(0), t_(t) , rr_(0),
shell_(getShell()), selected_(0)
{
if (shell_)
ISHELL_CreateInstance(shell_, cid, (void **)&list_);
if (!list_)
{
E::onMemAllocError(WString(__FILE__),
WString((long)__LINE__));
return;
}
rr_ = t_->getDisplayable();
if (rr_)
id_ = rr_->registerResource(this);
}
static ListImpl* createList( T* t, AEECLSID cid =
AEECLSID_MENUCTL)
{
ListImpl* l = new ListImpl( t, cid);
if (!l->list_)
{
delete l;
return 0;
}
return l;
}
void setFullScreen();
int getIDImpl(int ix);
private:
IMenuCtl * list_;
FNC fnc_;
T* t_;
DisplayableRegistry* rr_;
int selected_;
int id_;
IShell * shell_;
BrewVector<int> indxs_;
};
The next concern is event handling. Apparently, there are important differences between BREW and Java: a unique ID, event loop mechanism versus an implicit, observer-based one. I say "apparently" because one can move freely from one mechanism to the other. Even more: J2ME, for example, shares one of the weaknesses of BREW—the command listener implementation is a close relative of the BREW event loop—usually a hard-to-maintain, huge "switch." On top of this, listeners are weak-typed constructs, exposing a Displayable interface that has to be cast by the user. Our framework offers a safer approach with type safe, J2SE style listeners. For example, the ListImpl class defines a listener:
typedef bool (T::*FNC)(ListImpl<T,P,E>*);
bool myListUsage( List* l)
{
int pos = l->getSelectedIndex();
return (pos == INDEX_OUT_OF_BOUNDS) ?
false : BuildCommand(pos), true;
}
and registered like this:
l->setCommandListener(myListUsage);
Java has a more elegant getSelected() mechanism instead of unique labels to be passed to IMENUCTL_AddItem. This can be easily implemented by using the ubiquitous DisplayableRegistry, this time generating unique numbers to be used internally as IDs. Please note the use of a policy to faster retrieve the IDs in some particular cases.
A string implementation supporting AECHARs and CHARs was discussed extensively in [3].
A striking difference between BREW and Java is error handling, when RTTI based exception handling is not available or might be considered too expensive to be used in BREW. Emulating a try/catch mechanism using setjmp/longjmp is not directly applicable due to problems in destructing auto objects, but other techniques are available (see [4], [5]). As a convenience, we provided an ErrorHandler as a policy—a way to gracefully provide error tracking information.
