When I was dating my now wife Anita way back when, she bet me that I couldn't stop making jokes for 30 minutes. I buttoned up my wit and buckled down for a half hour of witless conversation. At the end of the allotted time, I burst forth with a stream of jokes and witticisms like an uncorked fire hydrant. Well, I haven't written a Newsletter article for quite a few months now and I'm brimming with so many ideas I hardly know where to start...
I've been training for a triathlon for the past few weeks, and a couple of times I've taken my daughter Yasmin along on 20 mile bike rides. Let me tell you, that's no easy feat! Up and down hills, huffing and puffing, Yasmin poking me in the butt, kicking the backs of my feet, making it harder. But just when I think I have no more energy left, the end is in sight. I make it up the last hill and look back across the landscape of miles, puff out my chest and crow with the knowledge that at least Yasmin thinks I'm a major stud muffin. Speaking of stud muffins, have you been programming the BeOS and using threads lately?
If you've done any BeOS programming, you've probably been using threads
whether you intended to or not. A much-utilized mechanism for
communicating actions in a BeOS program is the
BLooper
object. BLooper
greatly simplifies life, and in conjunction with BHandler
, is the basis
of our messaging and scripting capabilities. You have probably seen or
used a number of times some code that looks like this:
BMessage
msg
('doit');aLooper
->PostMessage
(&msg
);
Also, in the looper or handler of your choice, there is similar code in
)
that does something interesting in
response to the particular message. Using MessageReceived
(BMessage
* msg
BLooper
and posting messages is
a good thing to do in many situations because it keeps the threading and
deadlocking model pretty straight. No fuss, no muss, no dirty semaphores.
But as our mantra goes, the BeOS offers pervasive multithreading,
symmetrical multiprocessing, etc. So aren't you anxious to spread your
threads and bring your machine to its processing knees cleanly?
Many times when you program with BLooper
you're engaging in a design
pattern known as observer/observable. That is, you have some data that
changes from time to time, and you want various other components to
notice the changes and react accordingly. I have found it useful to
explicitly name this interface and create a couple of classes to
implement it. First, the BObservable
class:
I've left out the constructor and a couple other things to save space.
virtual status_tSendNotificationOf
(constint32notice
, void *data
); status_tAddObserver
(BObserver
*, void *data
); status_tRemoveObserver
(BObserver
*, void *data
);
A BObservable
is any object that wants other objects to know what it's
doing. In the Model/View/Controller world of SmallTalk, this would
probably be the Model. One example of an observable object might be a TV
tuner. Whenever the channel changes, some other object, notably a display
object, might be interested so that it can display a new channel number.
The Observable would call its SendNotification()
method and the BObserver
will have its ReceiveNotice()
method called.
Speaking of BObservable
,
here is its interface:
virtual boolIsInterestedIn
(constint32notice
,BObservable
*observable
); virtual voidReceiveNotice
(constint32notice
,BObservable
*observable
, void *data
);
The primary thing to note is the ReceiveNotice()
method. This is similar
to a message filter in that a single integer value is sent. One
difference from a BMessageFilter
is
that a BObserver
can tell a
BObservable
what messages it wants by using the
InterestedIn()
method. The
BObservable
can call this at any time, and
the BObserver
can report true
or false
as to whether it is currently interested in receiving such
notifications. Another difference from a BMessageFilter
is that
BObserver
s can be hooked up to as many BObservable objects as they want.
You'll notice that one of the parameters to ReceiveNotification()
is the
BObserver
that sent the message. Using
this, the BObserver
can perform
appropriate actions based on which BObservable
sent the message.
The interfaces of BObservable
and
BObserver
say absolutely nothing about
threading. Where and when threads are used is completely left up to the
individual implementations. Is this good or bad? Well, it gives you some
choices, and with choices come responsibilities. Here's the scissors --
please don't run with them.
I have a CD Player. There is one object, BCDROMDrive
, which can act as
the Observable model. It will send out notices whenever a disk changes,
tracks change, or any of the other transport commands are activated.
enum ECDNotices {CD_STOPPED
,CD_PLAY
,CD_FASTFORWARD
,CD_REWIND
,CD_CHANGED
... };
There is no visual interface within this object. Then there is the
BCDROMObserver
, which is responsible for displaying up to date disk
information. I don't want the Observer to slow down the Observable in any
way, so I've decided that when I receive notifications, I'll just put
them in a local queue, and return immediately so the Observable can carry
on with what it was doing:
voidBCDROMObserver
::ReceiveNotice
(constint32notice
,BObservable
*observable
, void *data
) {fMessageQueue
.push
(notice
); }
Separately, I have a thread that is running in a loop, pulling messages off the queue:
voidBCDROMObserver
::Run
() { while(!timeToQuit
) {fMessageQueue
.wait
(); int32aMsg
=fMessageQueue
.pop
(); // Massive switch statement to do things // based on what the message is. } }
There are two things of note here. My message queue implementation is
thread safe, so I don't have to explicitly lock and unlock the queue to
do the push and pop. Also, there is other code that makes the Run()
method occur in its own thread.
In essence, this gives you behavior similar to that of BLooper
, with a
little bit more control of what type of messages you send around, and
where you use threads, ports, and semaphores. In this particular case my
messages are always just a 32-bit integer. Speed and memory management
are a concern, so I wanted tighter control of when and where things are
allocated and how much data is being passed around. Also, this
application does not receive messages from the outside world, nor does it
try to send them outside, so leaving BLooper
is a reasonable thing to do.
Some sample code that uses this technique can be found at: ftp://ftp.be.com/pub/samples/R3/support_kit/observable.zip
There's the BObserver
/able
code, some threading code, some thread safe
queue code, and a little sample app that actually uses it. I would love
to write more, but as any good comedian knows, get off the stage while
they're still laughing.
But before I go, I've been working on some lightweight UI type things. If you are a developer who has an interface which is highly customized with lots of artwork, and you're wondering how to make nice sliders, buttons, and other doohickeys, and keep it all fast and responsive, please drop me e-mail, we may have something you can use: wadams@be.com.
That's all the news from the front.
Here is another in my series on making money by developing products for the BeOS. Getting press is an important subject so I have promised not to include weird jokes, not to knock Apple, and not to use the word "skank."
In order for you to make money selling your software, your potential customers need to find out about your product. Aside from placing a demo of your product on the BeWare web site, the most effective and least costly way to get info about your product out to customers is through magazine articles and reviews, TV and radio mentions, newspaper articles, and articles in trade journals and targeted publications in specific market segments. An example of an article in a targeted publication would be an article about a new BeOS MIDI sequencer in a publication of interest to musicians rather than a general computer magazine.
I can't encourage you strongly enough to make an effort to reach the press. When the customer gets information from the press it comes with greater credibility than when it comes from you. And if you are sending press releases this helps bring attention to the BeOS in general as well as letting the world know that there are lots of great applications available. The more the BeOS becomes well known the better that helps you with your product sales.
This article is for freeware and shareware authors as well as commercial publishers. Freeware authors want press because you can put it on your resume. Shareware and commercial publishers will benefit because increased exposure means increased sales. And Be benefits when you get press attention as long as they spell our name right (which isn't hard to do).
To get press attention you send press releases to editors and reporters, and follow up.
Press releases follow a standard format. At the top you should put contact information, name, phone and e-mail. If you are sending information that can be released immediately, you should put "For Immediate Release" at the top.
You should have a headline, and possibly a sub-headline. The content of the press release consists of several paragraphs of information, often ending with general information about your company. The bottom of the first page should either have a centered "MORE" if there is another page, or "###" if it is the end of the press release. The top of the next page should have the title and page number.
Your press release serves two purposes. It is your chance to get the interest of a busy editor or reporter, and it provides the necessary information while framing the story.
Think of a press release as a form of direct mail advertising but not as an ad. The structure and requirements are actually very similar to a direct mail piece. Like a direct mail piece, your press release is going to be quickly skimmed and then a decision is made whether to write a story. The editor/reporter is reading and thinking "Is this a story that will interest my readers?" But...
You want to provide information, not do a hard sell. It is bad form to try to do a sell job on the press. Working with the press is about getting accurate information to the reader, and editors and reports will appreciate that.
When viewed in this direct mail context you can see that the headline can be the most important part of your press release. It must entice the editor or reporter to read further, while accurately conveying your message. The headline should be a very short and to-the-point statement telling what the press release is about while maintaining the interest of the reader. You should think of it as a headline in a newspaper—as the way you want your story to appear in the publication.
You can also have a sub-headline that conveys more information or additional information.
Note: Use "BeOS" somewhere in the headline. The BeOS is hot right now; editors are interested in it and they know their readers are interested in it. Normally you would be competing with hundreds of press releases for attention. Putting "BeOS" in the title should help you break through the clutter.
The first paragraph has to contain the important information of interest to the reader. If the headline is interesting enough for the reporter/editor to read further you want to clinch it with the 1st paragraph. It begins with a location and a date in parenthesis. "Menlo Park, CA, (March 31, 1998)." You should tell your story quickly, briefly and interestingly while providing necessary information. You've got interest if they're reading this paragraph and you need to turn the interest into a desire to write a story.
If it is decided that a story will be written, the press release will be read more closely and will serve to frame the story. The content must satisfy the customer who is interested in what you're selling and reading. It no longer needs to be enticing, just informative. If the reader is looking at this part at all it's likely an article will be forthcoming.
Be sure that your press release contains all the necessary information. Does it tell the version and price of the product? Do you make clear the availability—when and where and how to order or get more information. Is there a demo available? Special hardware requirements? Be sure to include your web address and please mention Be's BeWare page address.
The BeOS is new, so not everyone is going to know what you're writing about when you say your product runs on the BeOS. Along with "BeOS" in the title you should include a paragraph describing the BeOS, where to get more info about it, and where to get it. And it certainly doesn't hurt if you describe the various ways your application runs better on the BeOS.
The BeOS (available from Be, Inc. at the be web site ) is a new, modern, high-performance, media-optimized personal computer operating system that runs on Intel Pentium-based and PowerPC desktop computers. The BeOS incorporates pervasive multithreading, symmetric multiprocessing, protected memory, a 64-bit journaling file system, client-server based architecture and an object-oriented API. The BeOS delivers the performance required for multimedia applications, with unprecedented user responsiveness and enabling real-time manipulation & feedback capabilities in applications.
Often press releases end with general information about your company itself. When was the company established, markets, products, personnel, etc...
Send the press release to the publications where you hope to see articles about your product. Remember to include special-interest publications as well as computer publications. If you have a fishing product send the press release to Field & Stream as well as ALL the computer magazines.
There are several approaches to this. One approach is to get a general press list and send the press release to everyone on the list. DP Directory is an excellent source of press lists and I have included their URL below. You can get lists for e-mailing or postal mailing from them. We have used these lists and we recommend them.
Another way is to purchase a copy of each publication you wish to target and locate the masthead. The masthead is a page that lists the editors and reporters that put the publication together. Often they list the area of specialty for each editor. You can send the press release to everyone on the list or only to selected people. You can call the publication and ask who the right editor would be for your information.
Some PR experts suggest that you send press releases to only one person at each news source, others say you it is a good idea to make sure everyone knows about you. My feeling is that the more people that know about your products the better. They will be writing articles in the future and might remember your product, they might even move on to new publications and introduce your product to new people there.
After the press release goes out be sure you are available in case you get calls. Give priority to editors who say they are "on deadline."
You can wait and be pleasantly surprised by any articles that appear or you can be proactive and take command of the effort. Set up a press tracking chart listing each publication and contact, and call them to find out what is scheduled with what publication dates. (The call can also serve as a nudge to please put you on the schedule.)
You can also assign yourself certain important targets that you deem necessary for your PR effort. These targets can then be contacted until you can get a commitment for something to appear in the publication.
After a while a picture will emerge of what press attention is coming over time. With the knowledge of what articles will appear and when they will be published you can coordinate your marketing efforts, such as what months you want to have your product advertised in software catalogs, etc...
Honest information is the Big Kahuna of press relations. If you mislead the press you lose your credibility and they will not forgive you. This is not the way to handle problems that come up. Don't try to cover up problems, just admit them and talk about what you plan to do about them. Don't try to be clever. Remember that the press is about getting accurate information to the readers. If you are going to be in business for the long term you have to keep the respect of the press.
The press release should tell who editors should contact to obtain review copies of your product.
When sending out review copies it's a good idea to provide a reviewer's guide. A reviewer's guide is a document describing important features, what you think the customer will use the product for, and other things you want to be sure a reviewer knows. It might contain a script for use of the product, like a demo script
A review is usually objective and fair. And sometimes a review turns out to be an opportunity for you to learn new ways that your product needs improvement. :-) A fair reviewer will call you if problems come up and give you an opportunity to explain a possible misunderstanding.
Often when an article is underway you will be asked if you can provide the names of some customers. Writers like to get the customer's perspective and some good quotations because the reader can relate to other users.
If you are new to this, you don't need to be afraid to ask editors questions about how to talk to them, and what you should do to get articles scheduled. This is about building a relationship with the press. But here's some tips. After you have sent the press release it's useful to follow up with a quick call—but respect deadlines. When a deadline approaches everyone at a publication gets extremely busy. If you call someone and they say they are under deadline don't try to press your point. Ask them when to call and then get off the phone.
Learn a publication's deadlines. It is useful to know when information needs to arrive at a publication and the lead time of the publication. Magazines can have lead times of several months. Weeklies have a cutoff day the preceding week—nothing gets into the publication after that day. If you want something in the next week's issue you need to know what day the info needs to be there.
If you want to make a more serious effort to get press attention you should schedule a "demo tour." This is where you schedule to visit a publication. Contact the appropriate editor and ask if you can schedule to come visit and demonstrate your product and discuss it with one or more members of the staff of the publication. Often you'll find yourself with several people in attendance. This is a good way of establishing a good personal relationship with members of the press.
Finally, you should establish a press section of your web site. For ideas on how to set this up check out Be's press page located at: www.be.com/aboutbe/pressreleases/index.html and Microsoft's press page located at: www.microsoft.com/corpinfo/todaynews.htm
Here are some URLs of good information about working with the press:
http://www.dpdirectory.com—A great place to get press lists!
http://www.dpdirectory.com/prtab1.htm—Tutorial on writing press releases.
http://www.newstips.com/newsrlse.htm—A sample press release from Newstips, a PR newsletter.
http://www.smalloffice.com/guru/archive/mgrescre2.htm—A sample press release with some comments. There are other interesting articles on marketing at this site.
http://www.prweb.com/coach/970722a.htm—Press release how-to's.
http://www.smartbiz.com/sbs/cats/pr.htm—Links to LOTS of "How To" PR and marketing sites.
http://www.smartbiz.com/sbs/arts/bly6.htm—Good article on how to PR.
http://www.prweb.com/—A very good site, check out their "PR Coach" page.
In my last article, "BeOS Programming Basics, Part 1", we looked at creating a basic BeOS application with a single window containing a single view. This week, we'll expand on that a bit by adding a menu bar to our window and showing how to handle user selections in menus.
Before we get to that, though, I thought I'd begin by clarifying a point that often confuses people, and briefly review the coordinate system the BeOS uses. If you have a rectangle:
rect
.Set
(0, 0, 5, 7);
This rectangle's top-left corner is at (0, 0) and its bottom-right corner is at (5,7). It looks something like this:
012345 ******0 ******1 ******2 ******3 ******4 ******5 ******6 ******7
Because the coordinates you specify are inclusive, the top row of the rectangle contains the points (0,0), (1,0), (2,0), (3,0), (4,0), and (5,0). If you add those up, you can see that the rectangle is six pixels wide and eight pixels high.
If you call Width()
on this rectangle, the result will be 5 (5-0).
Height()
returns 7 (7-0). This is the source of most of the confusion. If
you want to know the actual width or height in pixels of a rectangle, you
need to add one to the result of these two functions.
Now on to this week's project: Menu World. Menu World is based on the Hello World code from my last article; in fact, if you haven't read that article, I recommend you visit the Be web site and read it (once again, that's at: www.be.com/aboutbe/benewsletter/volume_II/Issue7.html#Workshop. You might also want to download the source code for Hello World and have a look at that as well: ftp://ftp.be.com/pub/samples/intro/obsolete/helloworld_article.zip
Also, before we get started, I should point out that this article uses
BMessage
s (which is how user interactions are reported to your
application) without going into a lot of detail on how they work. The
next article in this series will discuss messaging in more detail.
Here are the includes for Menu World:
#include <Application.h> #include <Window.h> #include <View.h> #include <MenuBar.h> #include <Menu.h> #include <MenuItem.h> #include <string.h>
Let's start with main()
, which is very simple and self-explanatory. The
only reason I'm including it is because it's slightly different from what
it was when I wrote the previous article—it now allocates the HelloApp
object on the stack, which is the C++-approved way of doing things (I
love progress). Note that because it's allocated on the stack, we don't
have to remember to delete it, because it's done automatically when
main()
returns.
voidmain
(void) {HelloApp
theApp
; // The application objecttheApp
.Run
(); }
We also use the following constants for the BMessage
command codes for
each of the menu items that Menu World will provide:
constuint32MENU_FILE_NEW
= 'MFnw'; constuint32MENU_FILE_OPEN
= 'MFop'; constuint32MENU_FILE_CLOSE
= 'MFcl'; constuint32MENU_FILE_SAVE
= 'MFsv'; constuint32MENU_FILE_SAVEAS
= 'MFsa'; constuint32MENU_FILE_PAGESETUP
= 'MFps'; constuint32MENU_FILE_PRINT
= 'MFpr'; constuint32MENU_FILE_QUIT
= 'MFqu'; constuint32MENU_OPT_HELLO
= 'MOhl';
Now let's take a look at the HelloView
class, which has one new method
and some private data added since last time. This isn't very exciting
stuff yet, but we'll experiment with menus a bit when we get to that
point:
classHelloView
: publicBView
{ public:HelloView
(BRect
frame
); virtual voidDraw
(BRect
updateRect
); voidSetString
(const char *s
); private: charmessage
[128]; };
The SetString()
function has been added to let you configure what message
the HelloView
class draws. The message is stored in the
message
string,
as follows:
voidHelloView
::SetString
(const char *s
) { if (strlen
(s
) < 127) {strcpy
(message
,s
); } }
SetString()
just makes sure the string isn't too long, then copies it
into the message field.
The Draw()
function has to be changed
to use the message
string:
voidHelloView
::Draw
(BRect
updateRect
) {MovePenTo
(BPoint
(20,75)); // Move penDrawString
(message
); }
Finally, the HelloView
constructor has to be updated to initialize the
message string:
HelloView
::HelloView
(BRect
frame
) :BView
(frame
, "HelloView",B_FOLLOW_ALL_SIDES
,B_WILL_DRAW
) {SetString
(STRING_HELLO
); }
Things start to get really interesting when we look at the updated
HelloWindow
class:
classHelloWindow
: publicBWindow
{ public:HelloWindow
(BRect
frame
); virtual boolQuitRequested
(); virtual voidMessageReceived
(BMessage
*message
); private:BMenuBar
*menubar
;HelloView
*helloview
; };
In the HelloWindow
class, the
MessageReceived()
function is new since
last time. Notice also that we now have fields for stashing pointers to
the menu bar and the HelloView
attached to the window.
Let's go through these methods one by one. The constructor has been updated to create and install the menu bar. Here it is:
HelloWindow
::HelloWindow
(BRect
frame
) :BWindow
(frame
, "MenuWorld",B_TITLED_WINDOW
,B_NOT_RESIZABLE
|B_NOT_ZOOMABLE
) {BRect
r
;BMenu
*menu
;BMenuItem
*item
; // Add the drawing viewr
=Bounds
();r
.top
= 20;AddChild
(helloview
= newHelloView
(r
)); // Add the menu barr
.top
= 0;r
.bottom
= 19;menubar
= newBMenuBar
(r
, "menu_bar");AddChild
(menubar
);
This isn't as complicated as it looks at first glance, since most of the content is very repetitive.
We start by getting the bounds window rectangle, just as we did for Hello
World. We want to create our HelloView
. However, to make room for the
menu bar, we set the top edge of the rectangle to 20 (the menu bar will
be 20 pixels tall and placed above the HelloView
). Then we create and add
the HelloView
to the window. Notice that we also store a pointer to the
view in the member variable helloview
.
Then we create the menu bar and add it to the window. We set r.top
to 0
and r.bottom
to 19, to indicate that the menu bar should take up the top
20 pixels of the window, and create a BMenuBar
named menu_bar
. A
pointer to the menu bar is stored in the variable menubar
. Then the menu
bar is added to the window by calling AddChild()
.
Note that at this point, our window has two views in it: one is the
HelloView
, and one is a BMenuBar
(which is derived from BMenu
, which is
derived from BView
).
The File menu is created next:
menu
= newBMenu
("File");
This line just creates an empty menu named "File." Then it's time to add the items to the menu, as follows:
menu
->AddItem
(newBMenuItem
("New", newBMessage
(MENU_FILE_NEW
), 'N'));
This line of code adds the "New" option (with the keyboard shortcut key
Command+N)
to the menu by creating a new BMenuItem
object and adding it
to the menu using the AddItem()
call.
When the user selects a menu item, a BMessage
is sent to the window
containing the menu item (the target for the message can be changed, but
let's not get ahead of ourselves). The message that's sent to the window
is created by copying the model message specified when you create the
BMenuItem
.
In this case, we specify a model message with the command code
MENU_FILE_NEW
. We'll look at messages in more detail in my next article.
We continue adding the rest of the menu items. Note that the keyboard
shortcut argument to the BMenuItem
constructor is optional; if you don't
specify it, the menu item won't have a keyboard shortcut. Also, we use
the AddSeparatorItem()
function to add dividing lines here and there, to
make the menu easier to read:
menu
->AddItem
(newBMenuItem
("Open"B_UTF8_ELLIPSIS
, newBMessage
(MENU_FILE_OPEN
), 'O'));menu
->AddItem
(newBMenuItem
("Close", newBMessage
(MENU_FILE_CLOSE
), 'W'));menu
->AddSeparatorItem
();menu
->AddItem
(newBMenuItem
("Save", newBMessage
(MENU_FILE_SAVE
), 'S'));menu
->AddItem
(newBMenuItem
("Save as" B_UTF8_ELLIPSIS, newBMessage
(MENU_FILE_SAVEAS
)));menu
->AddSeparatorItem
();menu
->AddItem
(newBMenuItem
("Page Setup" B_UTF8_ELLIPSIS, newBMessage
(MENU_FILE_PAGESETUP
)));menu
->AddItem
(newBMenuItem
("Print" B_UTF8_ELLIPSIS, newBMessage
(MENU_FILE_PRINT
), 'P'));menu
->AddSeparatorItem
();menu
->AddItem
(newBMenuItem
("Quit", newBMessage
(MENU_FILE_QUIT
), 'Q'));
Once all the items have been added to the menu, we add the menu to the
menu bar by calling the BMenuBar
class'
AddItem()
function:
menubar
->AddItem
(menu
);
Then we create the Options menu, which only has one item in it.
menu
= newBMenu
("Options");item
= newBMenuItem
("Say Hello", newBMessage
(MENU_OPT_HELLO
));
The "Say Hello" menu item is used to toggle between two strings that
might appear in the HelloView
in our window:
"Hello World" and "Goodbye
World." This menu item has a check mark next to it if "Hello World" is
displayed, and no check mark if "Goodbye World" is displayed. Let's make
"Hello World" the default and make sure the "Say Hello" item is initially
checked:
item
->SetMarked
(true
);
Passing true
to SetMarked()
puts a check mark in front of the menu item,
while passing false
removes the check mark.
Then we add the "Say Hello" item to the menu and add the Options menu to the menu bar.
menu
->AddItem
(item
);menubar
->AddItem
(menu
);
Finally, we make the window visible by calling Show()
.
Show
();
}
QuitRequested()
is unchanged from Hello World.
MessageReceived()
is where the heavy lifting of handling and dispatching
user selections in the menu bar is done. When the user selects a menu
option, an appropriate BMessage
is created and sent to the window
containing the menu. The window's MessageReceived()
function receives the
message and handles it:
voidHelloWindow
::MessageReceived
(BMessage
*message
) { switch(message->what
) { caseMENU_OPT_HELLO
: /* see below for the code that goes here */ break; default:BWindow
::MessageReceived
(message
); break; } }
One of the public fields of a BMessage
object is called what
; it
contains the command code that was specified when creating the BMessage
.
We can therefore tell what kind of message the BMessage
represents by
looking at the value of the what
field.
For now, the only message we handle is MENU_OPT_HELLO
, which is sent when
the user selects the "Say Hello" item in the Options menu (if you look in
the HelloWindow
constructor, the model
BMessage
for the "Say Hello" item
has this command code). All other messages are forwarded to
BWindow::MessageReceived()
for further processing.
The "Say Hello" item toggles the text displayed in the view between "Hello World" and "Goodbye World." It also adds a checkbox to the menu item if "Hello World" is displayed, and removes it if "Goodbye World" is displayed. Let's have a look at the code.
caseMENU_OPT_HELLO
: {BMenuItem
*item
; const char *s
; boolmark
;message
->FindPointer
("source", (void **) &item
); if (item
->IsMarked
()) {s
=STRING_GOODBYE
;mark
=false
; } else {s
=STRING_HELLO
;mark
=true
; }helloview
->SetString
(s
);item
->SetMarked
(mark
);helloview
->Invalidate
(); } break;
The BMessage
that's received by your
MessageReceived()
function when the
user selects a menu item is the model message specified when the menu
item was created, with three additional fields added to it:
The when
field contains a
B_INT64_TYPE
value that specifies the time
the item was selected, measured in terms of the number of microseconds
since 12:00:00 AM on January 1, 1970.
The source
field contains a B_POINTER_TYPE
value that's a pointer to
the BMenuItem
object itself.
The index
field contains a
B_INT32_TYPE
value that specifies the
ordinal position of the selected menu item within the menu, where 0 is
the first item in the menu.
In order to toggle the check mark next to the menu item, we need a
pointer to its BMenuItem
object. We could have cached it in our window
object, but it's more interesting from a sample code standpoint to fetch
it from the "source" field in the
BMessage
, so that's what we do. We use
the FindPointer()
function to get the pointer out of the message and
store it in the variable called item
.
We then call
to determine whether the menu item is
currently checked or not. If it's checked, item
->IsMarked()
IsMarked()
returns true
(which
means the text in the view is currently "Hello World." We set up a
pointer to the string "Goodbye World," and set the variable mark to
false
. If IsMarked()
returns false
(meaning that "Goodbye World" is
displayed), we set the string pointer to "Hello World"
and mark
to true
.
Then we call the HelloView
's
SetString()
function to change the text
being displayed, and call
to turn on or off the check
mark; if item
->SetMark()
mark
is false
, the check mark is
removed. If mark
is true
, the
check mark is added.
Then we invalidate the HelloView
. Invalidating a view causes the
application server to Draw()
the entire view. This is necessary to
refresh the view's contents.
The BMenuItem
class has some other functions you might want to experiment
with, such as SetEnabled()
and
IsEnabled()
, which let you control whether
or not a menu item is disabled (dimmed and unselectable), or SetLabel()
and Label()
, which let you set and retrieve the current text displayed
for the menu item when it's drawn.
To make life easier, you can download the complete source code for this week's project: ftp://ftp.be.com/pub/samples/intro/obsolete/menuworld_article.zip
Since this week's article has opened the BMessage
can of worms, I think
it's only proper that part three of the BeOS Programming Basics series
cover message services on the BeOS in a little more detail. For now, play
with the BMenuBar
, BMenu
,
and BMenuItem
classes and see where you can go
with it. See you again in six weeks or so.
Two weeks ago, we were preparing for our BeDC, our developers' conference in Santa Clara, and we were unusually nervous. What was unusual was that we felt reasonably well-prepared this time, unlike our August '97 conference in Boston, for instance. And, happily, most everything worked this time, with very few glitches.
I won't go into details but I'd like to thank those participants who gave us feedback and suggestions for improving the formula or the execution. Speaking of which, it felt a little strange, at first, having court reporters in the sessions, but their purpose was non-prosecutorial. Transcripts of the presentations will be preserved for posterity on our site, with appropriate beautification from our embalming editors, I hope.
I had a good time. Developer conferences motivate me, because they are a great opportunity to meet the most important people in our life -- developers. True blue capitalists will argue that shareholders are the most important constituency we serve, they have a point, but without developers, shares in an operating system aren't worth much.
In any event, we value the opportunity to stand in front of a jury of our peers and present our work and our plans for the future. At the same time, one of the high points for me at the BeDC was watching the Masters Awards ceremony. I remember the old joke in the early difficult days of the Macintosh: there are three forms of lying, action, omission, and there is software for the Macintosh.
Seeing the quality and abundance of applications rewarded, not just submitted, I feel (cautiously) optimistic about our prospects. Please point your browser to www.be.com/aboutbe/pressreleases/98-03-19_beos_masters.html for details. And please don't ask me which program I prefer. I've excluded myself from the judging because I'm a little too enthusiastic. I also remember the first part of 1985 when everyone was so down on the Macintosh, and I got a nice job out of the common and unwarranted pessimism.
Back at the office, we got a good dose of cold water. Early orders for Release 3 were coming in at a nice clip, so nice, in fact, that it quickly revealed an embarrassing bug in our order taking system. Somehow, we managed to drop the last digit of credit card numbers and we were faced with having to call over fifteen hundred customers, apologize for our mistake, expose our mastery of computer technology, and sheepishly ask for their credit card number again.
Fortunately, Ron Theis, our Web Master, found an algorithm to recalculate this last digit. It worked for about 70% of cases. For the other 30%, this turned into an opportunity to deal with another issue: hardware configurations. Even if we take precautions on our Web site, in the documentation, and in any medium we can find, if our customer orders the BeOS and can't run it on his/her machine, we're going to create frustrations and bad word-of-mouth.
So, as we called customers, we checked, and the good news is a very high proportion indeed have hardware we support at this time and are clearly of the geek persuasion. We know we need to broaden the range of configurations we support and we acknowledge we need to make the installation process smoother, so we were relieved to see our early customers were quite patient with our imperfections. Our thanks to them, and our apologies to those we didn't satisfy as much and as quickly as they expected. We now have a clear picture of what we have to do.