[haiku] Some Be programming documentation

  • From: Jimmy <jimmy@xxxxxxxxxxxxxxxx>
  • To: haiku@xxxxxxxxxxxxx
  • Date: Wed, 15 Feb 2012 02:40:10 +0000 (GMT)

Hi all, found this doc I had DL'd from the be website back in the day.
Maybe one of the webmasters should post it..

shaka

Beginning BeOS Programming by Eric Shepherd

"Developers' Workshop" is a new weekly feature that provides answers to our 
developers' questions. Each week, a 
Be technical support or documentation professional will choose a question (or 
two) sent in by an actual developer and 
provide an answer. 

We've created a new section on our web site. Please send us your Newsletter 
topic suggestions by visiting the web 
site at: http://www.be.com/developers/suggestion_box.html. 

The sample code for this article is on the Be FTP Server 
ftp://ftp.be.com/pub/samples/preview/intro/helloworld_article.zip 

As we draw closer to the first release of BeOS for Intel, a whole new 
generation of BeOS programmers is preparing 
to enter unknown territory. In order to help introduce them to writing BeOS 
applications (and to help PowerPC 
programmers get started with the BeOS), my next few articles for the Be 
Newsletter will comprise a brief introductory 
course on BeOS programming.
 
This week, we're going to build a very basic application. Can you say "Hello, 
world?" This article and the sample 
application we'll create will show you how to do that.
 
Yes, that's right, we'll be writing "Hello World" as a BeOS application. We'll 
create application, window, and view objects, 
and draw text into a view. This article won't tell you everything you'll ever 
need to know, but it should at least get you up 
and running with your first application so you can start experimenting. In 
fact, you should keep a copy of the Be 
Developer's Guide or the online Be Book handy as you read this article. 

For this project, you'll need the following header files included in your 
source code: 

#include <Application.h>
#include <Window.h> 
#include <View.h> 

Let's begin at the beginning: The BApplication class. This class defines a BeOS 
application. In essence, the BApplication 
class represents the link between your application and the Application Server. 
The Application Server handles 
messaging, imaging, and other good stuff that most applications need to do. So 
before you can draw on the screen or 
accept or transmit messages, you have to create a BApplication object.
 
Typically, you'll do this by creating your own class, derived from 
BApplication, so you can augment or override certain 
methods to customize the behavior of your application. In our Hello World 
program, we'll be creating a class called 
HelloApp to serve this purpose. 

An application's main() function's primary duty is to create the BApplication 
(or an object of a class derived from 
BApplication) and to invoke it. Before we create HelloApp, let's look at a 
typical main() function (in fact, this is the main() 
our Hello World program will use):
 
void main(void) 
{
 HelloApp *theApp; // Pointer to our application object
 theApp = new(HelloApp);
 theApp->Run();
 delete theApp;
} 

All main() does here is create a new HelloApp object, which is derived from 
BApplication, as we'll see in a few moments. 
Once that's done, the application's Run() method is invoked. The Run() method 
is the application's event loop. It won't 
return until the application is terminated.

Once the Run() method returns, we delete the application object, since our 
program is about to terminate, so we don't 
need the object anymore.

It's useful to remember that the global variable be_app always points to your 
application's BApplication object. That 
means you don't have to keep a pointer to your application object around if you 
don't want to, although if you call any 
custom methods added to the object, you'll need to cast be_app to your class.
 
Now let's look at the HelloApp class: 

class HelloApp : public BApplication 
{
 public:
 HelloApp();
 private:
 HelloWindow *theWindow;
};

As mentioned earlier, HelloApp is derived from BApplication. We only need the 
constructor to get done what we need to 
do, so that's the only public method we define. A single private variable, 
theWindow, is used to store a pointer to our 
application's window. 

Let's check out the HelloApp constructor: 

HelloApp::HelloApp()
: BApplication("application/x-vnd.Be-HelloWorld") 
{
 BRect windowRect;
 windowRect.Set(50,50,200,200);
 theWindow = new HelloWindow(windowRect);
}

The HelloApp constructor defers to the BApplication constructor, passing the 
string "application/x-vnd.Be-HelloWorld". 
This odd-looking string is the application's signature. It specifies that the 
application is an application, and that the 
vendor is Be, Incorporated. The application's name is "HelloWorld". This is the 
standard way we identify applications; 
this string can be used by other programs to locate your application and pass 
messages to it. 

Once the object has been constructed, we create a BRect. BRect is a rectangle 
object (described in the Interface Kit 
chapter of the Be Developer's Guide). It's used to define a rectangular area on 
the screen, given the top, left, right, and 
bottom edges of the rectangle, and it has several methods for setting and 
retrieving the position and size of the 
rectangle. 

One of these methods is Set(), which we use to set the rectangle to occupy the 
area from (50,50) to (200,200) in space. 
This establishes a rectangle 150 pixels wide and 150 pixels tall. The arguments 
to Set() are in the order: left, top, right, 
bottom. 

That BRect object is then passed to the HelloWindow constructor when we create 
our window object; this rectangle is 
used to position and size the window on the screen when it is created. Note 
that we save the window pointer in the 
HelloApp's theWindow field. 

That must mean it's time to look over the HelloWindow class: 

class HelloWindow : public BWindow 
{
 public:
 HelloWindow(BRect frame);
 virtual bool QuitRequested();
};

The HelloWindow class has a constructor, which accepts a BRect object as an 
argument (as discussed previously), and a 
QuitRequested() method. Note that HelloWindow is derived from BWindow, which is 
the class from which all windows 
are derived on the BeOS. 

HelloWindow::HelloWindow(BRect frame) : BWindow(frame, "Hello World", 
B_TITLED_WINDOW, 
B_NOT_RESIZABLE|B_NOT_ZOOMABLE) 
{ 
 AddChild(new HelloView(Bounds())); 
 Show(); 
}

The constructor, shown above, defers to the BWindow constructor to set up the 
window. The frame rectangle specified 
as the argument to the constructor is passed through, and the window's name is 
specified by the string "Hello World". 
B_TITLED_WINDOW is a flag indicating the type of window to create: A standard 
window with a tab at the top where the 
title and close box should draw. 

B_NOT_RESIZABLE and B_NOT_ZOOMABLE are flags that indicate that the window 
can't be zoomed (it won't have a 
zoom box) or resized. 

Once the window is constructed, we need to add a view to the window. Views are 
containers for other views. This is an 
interesting concept, and it's an important one to understand. Windows are 
containers for views. You typically can't draw 
directly into a window. You have to create a view and attach it to the window 
first. 

A window can have as many or as few views attached to it as you want, and each 
view can have subviews. We'll develop 
this further in my next article. For now, our application's window has a single 
view. 

In our case, we want the view to be the exact size of the window, and fill the 
window completely. So when we instantiate 
our HelloView (which we'll look at shortly), we pass the BRect returned by the 
Bounds() method. Bounds() is a method 
of the BWindow class that returns the "bounds rectangle" of the window. 

The bounds rectangle is a rectangle that indicates the area occupied by a 
window, but in coordinates local to the 
window. This is another important concept to grasp. 

If your computer has a 640 x 480 screen, the top-left corner of the screen has 
the coordinates (0,0) and the bottom-right 
corner of the screen is at (640,480). These are called "screen coordinates." 
When you create a window, you specify the 
location of the window on the screen using screen coordinates. 

Once the window has been created, anything you do inside that window is done in 
"local coordinates." Just as screen 
coordinates have the top-left corner of the screen at (0,0), local coordinates 
have the top-left corner of the window at 
(0,0). 

The Bounds() method in the BWindow class returns the window's rectangle in 
local coordinates, so in our program's 
case, this rectangle will be (0,0)-(150,150), which is exactly what we want. 

We pass this BRect to the HelloView constructor so the view will fill the 
entire window. We'll study the HelloView class 
next:

The HelloView pointer that's returned by new is then passed to BWindow's 
AddChild() method. As we already 
learned, windows are containers for views. The views that a window contains are 
called "children." The AddChild() 
method, then, adds a view to a window, thereby making that view a child of the 
window. Once a view is added to a
window, any drawing done inside the view is visible in the window (assuming 
both the view and the window are visible, 
but we'll get into that another time). 

Once the child view has been added to the window, we call Show(). This BWindow 
method makes the window visible 
-- all BWindows are hidden until you call this method.
 
The other method we define for the HelloWindow class, QuitRequested(), is 
called whenever someone attempts to close 
our window. This happens if the user clicks the close box, for example. Here's 
the code: 

bool HelloWindow::QuitRequested() 
{
 be_app->PostMessage(B_QUIT_REQUESTED);
 return true;
}

This is very simple. We simply post a B_QUIT_REQUESTED message to the 
application. By default, when a BApplication 
object receives this message, its Run() method simply exits (which then returns 
control to our main() function, which 
deletes the application object and exits). 

Then we return true to indicate that we're granting permission to close the 
window. If QuitRequested() returns false, this 
indicates to the caller that for whatever reason, they shouldn't close the 
window. An obvious application of this would be 
to present an alert reminding the user they haven't saved their work yet, with 
the option for the user cancel whatever 
operation was about to close the window. 

Finally, let's see the HelloView class, which is derived from BView. Views are 
the contexts in which all drawing is done. 

class HelloView : public BView 
{
 public:
 HelloView(BRect frame);
 virtual void Draw(BRect updateRect);
};

Our HelloView class has a constructor, which accepts a BRect identifying the 
size and position the view should occupy 
within its parent, and a Draw() method, which is called by the Application 
Server to update the contents of the view. 

The constructor looks like this: 

HelloView::HelloView(BRect frame)
: BView(frame, "HelloView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW) 
{
}

As you can tell, we don't do anything in the constructor: We simply defer to 
the BView constructor, passing through the 
frame rectangle. The view's name is "HelloView". We specify the 
B_FOLLOW_ALL_SIDES flag for our resizing mode, 
which indicates that the view will always fill the complete window, even if the 
window is moved or resized, and the 
B_WILL_DRAW flag, which tells the Application Server that we're implementing a 
Draw() method that needs to be called 
for updates. 

The Application Server calls Draw() whenever the window's contents need to be 
updated. This occurs if another window 
passes in front of ours, or when the window is initially shown. A BRect is 
passed to the method, indicating the area of the 
view that needs to be redrawn. 

void HelloView::Draw(BRect updateRect) 
{
 MovePenTo(BPoint(20,75)); // Move pen
 DrawString("Hello, world!");
}  

As you can see, in Hello World, we're ignoring the updateRect. We position the 
pen at coordinates (20,75) by creating a 
BPoint object (which represents a point on the screen) with those coordinates, 
then passing that point to the 
MovePenTo() method. 

Once the pen has been positioned where we want, we call DrawString() to draw 
the message "Hello, world!" into the view. 

The Next tutorial investigates how to employ multiple views in a window, and 
begins looking at some basic user 
interface features. For now, poke through the Be Developer's Guide and the 
Application.h, Window.h, and View.h 
header files and do some exploring on your own.   


//////////////////////////////////////////////////////////////////////
///             SOURCE CODE FOR PART 1          //////////////////////
//////////////////////////////////////////////////////////////////////

//
// Hello World 2000
//
// Copyright 1998, Be Incorporated
//
// Written by: Eric Shepherd
//

#include <Application.h>
#include <Window.h>
#include <View.h>

//
// HelloView class
//
// This class defines the view in which the "Hello World"
// message will be drawn.
//
class HelloView : public BView 
{
 public:
 HelloView(BRect frame);
 virtual void Draw(BRect updateRect);
};

//
// HelloView::HelloView
//
// Constructs the view we'll be drawing in.
// As you see, it doesn't do much.
//
HelloView::HelloView(BRect frame)
: BView(frame, "HelloView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW) 
{
}

//
// HelloView::Draw
//
// This function is called whenever our view
// needs to be redrawn.  This happens only because
// we specified B_WILL_DRAW for the flags when
// we created the view (see the constructor).
//
// The updateRect is the rectangle that needs to be
// redrawn.  We're ignoring it, but you can use it to
// speed up your refreshes for more complex programs.
//
void HelloView::Draw(BRect updateRect) 
{
 MovePenTo(BPoint(20,75));                      // Move pen
 DrawString("Hello, world!");
}

//
// HelloWindow class
//
// This class defines the hello world window.
//
class HelloWindow : public BWindow 
{
 public:
 HelloWindow(BRect frame);
 virtual bool   QuitRequested();
};

//
// HelloWindow::HelloWindow
//
// Constructs the window we'll be drawing into.
//
HelloWindow::HelloWindow(BRect frame)
: BWindow(frame, "Hello World", B_TITLED_WINDOW, 
B_NOT_RESIZABLE|B_NOT_ZOOMABLE) 
{
 AddChild(new HelloView(Bounds()));
 Show();
}

//
// HelloWindow::QuitRequested
//
// When the window is closed (either by the user clicking
// the close box or by some other means),
// this function is called.  You can add code to this
// to give the user the opportunity to save their document,
// or perform other such activities.
//
// Here we just give permission to close the window.
//
bool HelloWindow::QuitRequested() 
{
 be_app->PostMessage(B_QUIT_REQUESTED);
 return true;
}

//
// HelloApp class
//
// This class, derived from BApplication, defines the
// Hello World application itself.
//
class HelloApp : public BApplication 
{
 public:
 HelloApp();
 private:
 HelloWindow *theWindow;
};

//
// HelloApp::HelloApp
//
// The constructor for the HelloApp class.  This
// will create our window.
//
HelloApp::HelloApp()
: BApplication("application/x-vnd.Be-HelloWorld") 
{
 BRect windowRect;
        
 windowRect.Set(50,50,200,200);
 theWindow = new HelloWindow(windowRect);
}

//
// main
//
// The main() function's only real job in a basic BeOS
// application is to create the BApplication object,
// run it, and delete it when it terminates.
//
int main(void) 
{
 HelloApp *theApp;              // Pointer to our application object
 theApp = new(HelloApp);
 theApp->Run();
 delete theApp;
}

//////////////////////////////////////////////////////////////////////
///////////     END     //////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////


Beginning BeOS Programming by Eric Shepherd: Part 2

"Developers' Workshop" is a new weekly feature that provides answers to our 
developers' questions. Each week, 
a Be technical support or documentation professional will choose a question (or 
two) sent in by an actual developer 
and provide an answer. 

We've created a new section on our web site. Please send us your Newsletter 
topic suggestions by visiting the 
web site at: http://www.be.com/developers/suggestion_box.html. 

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. 

This article's source is available at 
ftp://ftp.be.com/pub/samples/preview/intro/menuworld_article.zip

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 site and do so. 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/preview/intro/helloworld_article.zip
 
Also, before we get started, I should point out that this article uses 
BMessages (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. 

void main(void) 
{
 HelloApp theApp; // The application object
 theApp.Run();
}

We also use the following constants for the BMessage command codes for each of 
the menu items that Menu World will 
provide: 

const uint32 MENU_FILE_NEW = 'MFnw';
const uint32 MENU_FILE_OPEN = 'MFop';
const uint32 MENU_FILE_CLOSE = 'MFcl';
const uint32 MENU_FILE_SAVE = 'MFsv';
const uint32 MENU_FILE_SAVEAS = 'MFsa';
const uint32 MENU_FILE_PAGESETUP = 'MFps';
const uint32 MENU_FILE_PRINT = 'MFpr';
const uint32 MENU_FILE_QUIT = 'MFqu';
const uint32 MENU_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: 

class HelloView : public BView 
{
 public:
  HelloView(BRect frame);
  virtual void Draw(BRect updateRect);
  void SetString(const char *s);

 private:
  char message[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: 

void HelloView::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: 

void HelloView::Draw(BRect updateRect) 
{
 MovePenTo(BPoint(20,75)); // Move pen
 DrawString(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: 

class HelloWindow : public BWindow 
{
 public:
 HelloWindow(BRect frame);
 virtual bool QuitRequested();
 virtual void MessageReceived(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 view

 r = Bounds();
 r.top = 20;
 AddChild(helloview = new HelloView(r));

 // Add the menu bar

 r.top = 0;
 r.bottom = 19;
 menubar = new BMenuBar(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 = new BMenu("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(new BMenuItem("New", new BMessage(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(new BMenuItem("Open" B_UTF8_ELLIPSIS,
new BMessage(MENU_FILE_OPEN), 'O'));
menu->AddItem(new BMenuItem("Close",
new BMessage(MENU_FILE_CLOSE), 'W'));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("Save",
new BMessage(MENU_FILE_SAVE), 'S'));
menu->AddItem(new BMenuItem("Save as" B_UTF8_ELLIPSIS,
new BMessage(MENU_FILE_SAVEAS)));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("Page Setup" B_UTF8_ELLIPSIS,
new BMessage(MENU_FILE_PAGESETUP)));
menu->AddItem(new BMenuItem("Print" B_UTF8_ELLIPSIS,
new BMessage(MENU_FILE_PRINT), 'P'));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("Quit",
new BMessage(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 = new BMenu("Options"); 
item = new BMenuItem("Say Hello",
new BMessage(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: 

void HelloWindow::MessageReceived(BMessage *message) 
{
 switch(message->what) 
 {
 case MENU_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. 

case MENU_OPT_HELLO:
{
 BMenuItem *item;
 const char *s;
 bool mark;

 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 item->IsMarked() to determine whether the menu item is currently 
checked or not. If it's checked, 
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 item->SetMark() to turn on 
or off the check mark; if 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. 

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.   








//////////////////////////////////////////////////////////////////////
///             SOURCE CODE FOR PART 2          //////////////////////
//////////////////////////////////////////////////////////////////////





// Menu World
//
// A sample program demonstrating the construction
// of a menu bar in a window.
//
// Copyright 1998, Be Incorporated
//
// Written by: Eric Shepherd
//

#include <Application.h>
#include <Window.h>
#include <View.h>
#include <MenuBar.h>
#include <Menu.h>
#include <MenuItem.h>
#include <string.h>

// Application's signature

const char *APP_SIGNATURE = "application/x-vnd.Be-MenuWorld";

// Messages for menu commands

const uint32 MENU_FILE_NEW = 'MFnw';
const uint32 MENU_FILE_OPEN = 'MFop';
const uint32 MENU_FILE_SAVE     = 'MFsv';
const uint32 MENU_FILE_SAVEAS = 'MFsa';
const uint32 MENU_FILE_PAGESETUP = 'MFps';
const uint32 MENU_FILE_PRINT = 'MFpr';

const uint32 MENU_OPT_HELLO     = 'MOhl';

const char *STRING_HELLO = "Hello World!";
const char *STRING_GOODBYE = "Goodbye World!";

//
// HelloView class
//
// This class defines the view in which the "Hello World"
// message will be drawn.
//
class HelloView : public BView 
{
 public:
 HelloView(BRect frame);
 virtual void Draw(BRect updateRect);
 void SetString(const char *s);
        
 private:
 char message[128];
};


//
// HelloView::HelloView
//
// Constructs the view we'll be drawing in.
// As you see, it doesn't do much.
//
HelloView::HelloView(BRect frame)
: BView(frame, "HelloView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW) 
{
 SetString(STRING_HELLO);
}


//
// HelloView::SetString
//
// Sets the message to draw in the view.
//
void HelloView::SetString(const char *s) 
{
 if (strlen(s) < 127) 
 {
  strcpy(message, s);
 }
}

//
// HelloView::Draw
//
// This function is called whenever our view
// needs to be redrawn.  This happens only because
// we specified B_WILL_DRAW for the flags when
// we created the view (see the constructor).
//
// The updateRect is the rectangle that needs to be
// redrawn.  We're ignoring it, but you can use it to
// speed up your refreshes for more complex programs.
//
void HelloView::Draw(BRect updateRect) 
{
 MovePenTo(BPoint(20,75));                      // Move pen
 DrawString(message);
}


//
// HelloWindow class
//
// This class defines the hello world window.
//
class HelloWindow : public BWindow 
{
 public:
 HelloWindow(BRect frame);
 virtual bool QuitRequested();
 virtual void MessageReceived(BMessage *message);
        
 private:               
 BMenuBar *menubar;
 HelloView *helloview;
};


//
// HelloWindow::HelloWindow
//
// Constructs the window we'll be drawing into.
//
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 view

 r = Bounds();
 r.top = 20;
 AddChild(helloview = new HelloView(r));
        
 // Add the menu bar
        
 r.top = 0;
 r.bottom = 19;
 menubar = new BMenuBar(r, "menu_bar");
 AddChild(menubar);
        
 // Add File menu to menu bar
        
 menu = new BMenu("File");
 menu->AddItem(new BMenuItem("New", new BMessage(MENU_FILE_NEW), 'N'));
 menu->AddItem(new BMenuItem("Open" B_UTF8_ELLIPSIS, 
        new BMessage(MENU_FILE_OPEN), 'O'));
 menu->AddItem(new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED), 'W'));
 menu->AddSeparatorItem();
 menu->AddItem(new BMenuItem("Save", new BMessage(MENU_FILE_SAVE), 'S'));
 menu->AddItem(new BMenuItem("Save as" B_UTF8_ELLIPSIS,
        new BMessage(MENU_FILE_SAVEAS)));
 menu->AddSeparatorItem();
 menu->AddItem(new BMenuItem("Page Setup" B_UTF8_ELLIPSIS,
        new BMessage(MENU_FILE_PAGESETUP)));
 menu->AddItem(new BMenuItem("Print" B_UTF8_ELLIPSIS,
        new BMessage(MENU_FILE_PRINT), 'P'));
 menu->AddSeparatorItem();
 menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'));
 menubar->AddItem(menu);

 // Add Options menu to menu bar
        
 menu = new BMenu("Options");
 item=new BMenuItem("Say Hello", new BMessage(MENU_OPT_HELLO));
 item->SetMarked(true);
 menu->AddItem(item);
 menubar->AddItem(menu);
        
 Show();
}


//
// HelloWindow::MessageReceived
//
// Called when a message is received by our
// application.
//
void HelloWindow::MessageReceived(BMessage *message) 
{
 switch(message->what) 
 {
  case MENU_OPT_HELLO:
  {
   BMenuItem *item;
   const char *s;
   bool mark;
                        
   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;
                
  default:
  BWindow::MessageReceived(message);
  break;
 }
}


//
// HelloWindow::QuitRequested
//
// Here we just give permission to close the window.
//
bool HelloWindow::QuitRequested() 
{
 be_app->PostMessage(B_QUIT_REQUESTED);
 return true;
}


//
// HelloApp class
//
// This class, derived from BApplication, defines the
// Hello World application itself.
//
class HelloApp : public BApplication 
{
 public:
 HelloApp();
};


//
// HelloApp::HelloApp
//
// The constructor for the HelloApp class.  This
// will create our window.
//
HelloApp::HelloApp()
: BApplication(APP_SIGNATURE) 
{
 BRect windowRect;
 windowRect.Set(50,50,349,399);

 new HelloWindow(windowRect);
}


//
// main
//
// The main() function's only real job in a basic BeOS
// application is to create the BApplication object
// and run it.
//
int main(void) 
{
        HelloApp theApp;                // The application object
        theApp.Run();
}


//////////////////////////// THE END //////////////////////////////////////

DEVELOPERS' WORKSHOP: BeOS Programming Basics: Part 3 
By Eric Shepherd

"Developers' Workshop" is a new weekly feature that provides answers to our 
developers' 
questions. Each week, a Be technical support or documentation professional will 
choose a 
question (or two) sent in by an actual developer and provide an answer. 

We've created a new section on our web site. Please send us your Newsletter 
topic 
suggestions by visiting the web site at: 
http://www.be.com/developers/suggestion_box.html. 

You can download the source code for this week's project from the Be FTP site:
ftp.be.com/pub/samples/R3/intro/messageworld_article.zip

Last time, we created a simple application with a menu bar that the user could 
play with. 
We also poked a toe into the waters of the BMessage class, but I skimmed over 
the details, 
leaving you high and dry until this week's exciting episode. If you haven't 
read parts 1 and 2 
of this series, you really ought to do so: 

http://www.be.com/aboutbe/benewsletter/volume_II/Issue7.html#Workshop 
http://www.be.com/aboutbe/benewsletter/volume_II/Issue13.html#Workshop 

As promised, this week we're going to delve a little more deeply into messaging 
on the 
BeOS. In particular, we're going to add support for multiple windows to the 
MenuWorld 
application from last time, using messages to keep track of how many windows 
are open, 
and to ensure that no two untitled windows get the same name. 

To accomplish this magnificent feat of software engineering, we're going to 
extend the 
HelloApp class to serve as a registry for our windows. Whenever a new window is 
opened 
within the MessageWorld application, that window tells the HelloApp object that 
a new 
window has been opened. HelloApp, in return, lets the new window know what its 
name 
should be. 

As always, you might want to have your Web browser aimed at the Be Book so you 
can read 
the more detailed descriptions of the functions discussed here; we won't be 
covering 
anything in complete detail, but just taking a look at how things might be 
done, to give you 
a place to start. 

We begin with the MenuWorld application from last time. Before getting into 
messaging, 
let's take a look at a minor change to the HelloWindow constructor. It was 
pointed out on 
the BeDevTalk mailing list that MenuWorld was assuming that the menu bar was 
always 
going to be 20 pixels tall, which is not a safe assumption to make. 

Additionally, there are a couple of minor changes needed so the window can 
register itself 
with the application object. 

So the HelloWindow constructor now does the following: 

HelloWindow::HelloWindow(BRect frame) 
: BWindow(frame, "Untitled ", B_TITLED_WINDOW, B_NOT_RESIZABLE|B_NOT_ZOOMABLE) 
{ 
  BRect r; 
  BMenu *menu; 
  BMenuItem *item; 

  // Add the menu bar 

  r = Bounds(); 
  menubar = new BMenuBar(r, "menu_bar"); 
  AddChild(menubar); 

  // Add File menu to menu bar 

  /* same code as last time */ 

  // Add Options menu to menu bar 

  /* same code as last time */ 

  // Add the drawing view 

  r.top = menubar->Bounds().bottom+1; 
  AddChild(helloview = new HelloView(r)); 

  Register(true); 
  Show(); 
} 

The menu bar is now added using the full bounds rectangle of the window. This 
is safe to do 
because, as described on page 467 of the Be Developer's Guide, the BMenuBar 
constructor 
resizes the height of a menu bar to the correct value. Then we add all the 
menus to the menu 
bar. Once that's done, the drawing view is created. The frame rectangle of the 
drawing view 
is determined by taking the bounds rectangle of the window, and setting the top 
edge of 
that rectangle to one greater than the bottom edge of the menu bar's bounds 
rectangle
 -- this results in a view that fills the remainder of the window. 

We've also added a call to the new Register() function (which we'll look at 
later). This 
registers the new window's existence with the application, and causes the 
application to 
assign the new window a number. 

Note also that we've changed the title of the HelloWindow to "Untitled ". This 
isn't 
important, but it seemed like a good idea. 

There are three basic classes in the BeOS messaging system. These are: 

 BMessage. The BMessage is the class that represents a bundle of information 
that is 
 sent from one place to another. This class includes functions for adding, 
changing, 
 and removing named fields of data. 

 BMessenger. The BMessenger class handles transmitting BMessage objects from 
one 
 place to another. Its central function, SendMessage(), does the actual job of 
 transmitting messages. Upon construction, the BMessenger's target (the 
BHandler 
 that will receive messages it transmits) is specified. The target can't be 
changed. 

 BHandler. The BHandler class receives BMessage objects that are sent to it, 
and can 
 optionally reply to those messages. It can't initiate a transaction. 

When a message is sent, the application server delivers it to a BHandler by 
calling that 
BHandler's MessageReceived() function. The message is passed into 
MessageReceived(), 
which interprets the message and acts upon it as necessary. The BHandler may 
choose to 
reply to the message by calling the message's SendReply() function, but this 
isn't necessary. 

The BView class is derived from BHandler; a view can receive and respond to 
messages. 
That's how user commands are received -- messages indicating mouse and keyboard 
activity 
are delivered to the view, which interprets and acts upon them. 

If you flip through the Be Book for a while, you'll notice that BLooper is 
derived from 
BHandler. BLooper is a handy class that establishes a thread that runs a 
message loop 
repeatedly until the loop's Quit() function is called. But the really good 
stuff (at least, as 
far as we're concerned today) happens when you look at some of the classes 
derived from 
BLooper, such as BApplication and BWindow. 

A BApplication object is instantiated, then you call its Run() function. Once 
you've done 
that, it doesn't return until its Quit() function is called. Its 
MessageReceived() function, 
which is inherited from BHandler, processes incoming messages. 

Likewise, a BWindow loops, handling incoming messages in its MessageReceived() 
function, until its Quit() function is called. The only difference is that you 
don't call a 
Run() function; this is done implicitly for you. It's still a BLooper, and it 
can still receive 
messages, just like anything derived from BHandler. 

So now we know that BApplication and BWindow are both capable of handling 
messages 
(in fact, we saw this last time, when we added code to our HelloWindow class to 
process 
incoming messages indicating that the menu bar was being used). 

Let's add the registry code to the HelloApp class. We'll need to add the 
MessageReceived() function, as well as two new private variables: 


class HelloApp : public BApplication 
{ 
 public: 
  HelloApp(); 
  virtual void MessageReceived(BMessage *message); 

 private: 
  int32 window_count; 
  int32 next_untitled_number; 
}; 

The window_count variable keep tracks of how many windows our application has 
open 
(there's actually a BApplication function that does this, but we're going to 
keep track of it 
ourselves for educational purposes). next_untitled_number tracks the number to 
use when 
naming a new untitled window (such as "Untitled 42"). This number always 
increases, so 
we'll never see the same number twice. 

The HelloApp constructor needs to be updated to initialize these variables: 

HelloApp::HelloApp() 
: BApplication(APP_SIGNATURE) 
{ 
  BRect windowRect; 
  windowRect.Set(50,50,349,399); 

  window_count = 0;      // No windows yet 
  next_untitled_number = 1;    // Next window is "Untitled 1" 
  new HelloWindow(windowRect); 
} 

When the application is first run, there aren't any open windows, and the first 
window is 
called "Untitled 1". 

The window registry -- the mechanism by which the application counts and names 
windows -
- is handled entirely by accepting and responding to two application-defined 
messages: 

const uint32 WINDOW_REGISTRY_ADD    = 'WRad'; 
const uint32 WINDOW_REGISTRY_SUB    = 'WRsb'; 

WINDOW_REGISTRY_ADD messages will be sent to the HelloApp object when a new
window is opened and needs to be added to the registry. WINDOW_REGISTRY_SUB 
messages will be sent to remove a window from the registry as it's being 
closed. 

The HelloApp needs to be able to reply to WINDOW_REGISTRY_ADD messages so that 
it can tell the window what number to use when giving itself a new "Untitled" 
name. So we 
need to define a command code for this reply message: 

const uint32 WINDOW_REGISTRY_ADDED = 'WRdd'; 

Now let's look at HelloApp's MessageReceived() function. This will be called by 
the 
application server whenever a message is delivered to the HelloApp object: 

void HelloApp::MessageReceived(BMessage *message) 
{ 
 switch(message->what) 
 { 

The function begins by looking at the BMessage's command code. This 32-bit 
value is located 
in the public "what" variable in the BMessage class, and specifies what type of 
message has 
been received. If it's a WINDOW_REGISTRY_ADD message, we do the following: 

    case WINDOW_REGISTRY_ADD: 
      { 
        bool need_id = false; 

        if (message->FindBool("need_id", &need_id) == B_OK) 
        { 
          if (need_id) 
        { 
            BMessage reply(WINDOW_REGISTRY_ADDED); 
            reply.AddInt32("new_window_number", next_untitled_number); 
            message->SendReply(&reply); 
            next_untitled_number++; 
          } 
          window_count++; 
        } 
        break; 
      } 

Before handing the WINDOW_REGISTRY_ADD function, we look at the "need_id" field 
of the 
message. If it's true, the window needs to be assigned an ID number (since the 
window is 
untitled). If need_id is false, the window doesn't need an ID. This will be 
used when we add 
the ability to open existing documents, which will already have names, and 
won't need to 
have an "Untitled" name. 

If need_id is false, a new BMessage, called reply, is created, with the command 
code 
WINDOW_REGISTRY_ADDED. This is the message we'll send back to the HelloWindow 
object 
as a reply once we've processed the WINDOW_REGISTRY_ADD message. 

We then add a new field to the reply message by calling AddInt32(). The field 
is given the 
name "new_window_number" and its value is set to the value of 
next_untitled_number. We 
could add as many named fields as we want to this message, but for now, this is 
all we need. 

Then the reply is sent by calling the received BMessage's SendReply() function. 
This 
directs the reply message to the appropriate BHandler object (in this case, the 
HelloWindow that sent the WINDOW_REGISTRY_ADD message). 

Finally, the next_untitled_number variable is incremented, so that the next new 
window's 
number will be one greater than the previous window's. 

The window count is then incremented (whether a reply was sent or not), since 
there's a 
new window registered. We always want to count up every new window, even if 
it's not 
untitled. 

    case WINDOW_REGISTRY_SUB: 
      window_count--; 
      if (!window_count) 
      { 
        Quit(); 
      } 
      break; 

If the message is a WINDOW_REGISTRY_SUB message, the window count is 
decremented, 
thereby indicating that one less window is open. If the count becomes zero, the 
application's 
Quit() function is called. This causes the application to terminate when there 
aren't any 
open windows left. 

    default: 
      BApplication::MessageReceived(message); 
      break; 
  } 
} 

All other messages are routed back to the inherited BApplication 
MessageReceived() 
function. 

Now let's look at the changes needed to the HelloWindow class: 

class HelloWindow : public BWindow 
{ 
  public: 
    HelloWindow(BRect frame); 
    ~HelloWindow(); 
    virtual bool  QuitRequested(); 
    virtual void  MessageReceived(BMessage *message); 

  private: 
    void  Register(void); 
    void  Unregister(void); 

    BMenuBar   *menubar; 
    HelloView  *helloview; 
}; 

The updated HelloWindow class needs a customized destructor, so that the window 
can be 
automatically unregistered whenever it's closed. Two private functions are 
added as well: 
Register(), which registers the window with the application, and Unregister(), 
which 
unregisters the window. 

The destructor is really simple -- it just calls the Unregister() function: 

HelloWindow::~HelloWindow() 
{ 
  Unregister(); 
} 

The Register() function sends a WINDOW_REGISTRY_ADD message to the HelloApp 
object. 
To accomplish this, we need to create a BMessenger object to deliver messages 
to the 
application. This is done by passing the application's signature to the 
BMessenger 
constructor; this creates a BMessenger that delivers messages to the HelloApp. 

The WINDOW_REGISTRY_ADD message is created on the stack, and the need_id 
argument 
is added to it by calling AddBool(). The message is then sent to the 
application by calling 
BMessenger::SendMessage(). 

SendMessage() lets you specify the handler to which replies should be directed. 
Since we 
want replies sent to the HelloWindow object, we specify the object (this) as 
the reply 
handler; by default, replies are sent to the BApplication object, which isn't 
what we want. 


void HelloWindow::Register(bool need_id) 
{ 
 BMessenger messenger(APP_SIGNATURE); 
 BMessage   message(WINDOW_REGISTRY_ADD); 

 message.AddBool("need_id", need_id); 
 messenger.SendMessage(&message, this); 
} 

The Unregister() function is nearly identical, except that it sends a 
WINDOW_REGISTRY_SUB message, and, since no replies are expected, it doesn't 
specify 
a reply handler. 

void HelloWindow::Unregister(void) 
{ 
 BMessenger messenger(APP_SIGNATURE); 
 messenger.SendMessage(new BMessage(WINDOW_REGISTRY_SUB)); 
} 

HelloWindow's MessageReceived() function needs to be augmented a bit. We're 
going to 
implement the New, Close, and Quit options in the File menu, and we need to 
handle the 
WINDOW_REGISTRY_ADDED message (as you recall, this is the reply sent by the 
HelloApp 
object when a window is successfully registered). 

Here are the constants for the message codes we're adding support for: 

const uint32 MENU_FILE_NEW    = 'MFnw'; 
const uint32 MENU_FILE_CLOSE  = 'MFcl'; 
const uint32 MENU_FILE_QUIT   = 'MFqu'; 

The code is as follows: 

void HelloWindow::MessageReceived(BMessage *message) 
{ 
  switch(message->what) 
  { 
    case WINDOW_REGISTRY_ADDED: 
      { 
        char s[22]; 
        int32 id = 0; 
        if (message->FindInt32("new_window_number",  &id) == B_OK) 
       { 
         sprintf(s, "Untitled %ld", id); 
         SetTitle(s); 
       } 
      } 
      break; 

When a WINDOW_REGISTRY_ADDED message is received, we extract the window's 
number 
from the message's "new_window_number" field by calling FindInt32(). This value 
is 
passed to the ANSI C sprintf() function to create a string to be used as the 
window's 
title, such as "Untitled 1" or "Untitled 42". That string is then passed to the 
window's 
SetTitle() function to change the window's title. This ensures that every 
window has a 
unique name. 

    case MENU_FILE_NEW: 
      { 
        BRect r; 
        r = Frame(); 
        r.OffsetBy(20,20); 
        new HelloWindow(r); 
      } 
      break; 

The File menu's New option creates a new HelloWindow, whose frame rectangle is 
the same 
as the current window's, but offset by 20 pixels down and to the left. This 
staggering effect 
helps keep the windows orderly on the screen (although in real life you want to 
be sure the 
windows don't eventually creep off the bottom or right edge of the screen!). 

    case MENU_FILE_CLOSE: 
      Quit(); 
      break; 

The Close option simply calls the HelloWindow::Quit() function to close the 
window. 
Because the HelloWindow destructor calls Unregister(), the window will be 
unregistered 
from the application (thereby reducing the window count). 

    case MENU_FILE_QUIT: 
      be_app->PostMessage(B_QUIT_REQUESTED); 
      break; 

The Quit option posts a B_QUIT_REQUESTED message to the application object. 
This 
causes the application to close all the windows and terminate itself. 

    case MENU_OPT_HELLO: 
      /* this is the same as last time */ 
      break; 

    default: 
      BWindow::MessageReceived(message); 
      break; 
  } 
} 

The rest of the code in HelloWindow::MessageReceived() is unchanged since last 
time. 

Finally, because we don't want closing the window to automatically quit the 
application, we 
rewrite the HelloWindow::QuitRequested() function to simply return true -- it's 
always 
okay to close the window. We no longer want to request that the application 
quit as well: 


bool HelloWindow::QuitRequested() 
{ 
  return true; 
} 

This has been a very brief overview of messaging on the BeOS. Because messaging 
is so 
pervasive throughout the entire operating system, we'll learn more about it as 
we go on. But 
this will give you the background needed to follow along as we continue to 
explore. Try 
adding code to insert more data into the registration messages, or make the 
WINDOW_REGISTRY_SUB message send a reply. 

Although this sample project's messages are mostly transmitted within the 
application, 
there's no reason you can't write two applications and send messages back and 
forth 
between them (try this -- just remember to use the appropriate signature when 
creating a 
BMessenger object for sending messages to the other application).
 
In about six weeks, we'll try making the HelloWindow class do something more 
useful than 
simply display a string in a window. We'll look at the BTextView class and 
start down the 
road toward turning our sample application into a text editor. 







//////////////////////////////////////////////////////////////////////
///             SOURCE CODE FOR PART 3          //////////////////////
//////////////////////////////////////////////////////////////////////



//
// Menu World
//
// A sample program demonstrating the basics of using
// the BMessage and BMessenger classes.
//
// Copyright 1998, Be Incorporated
//
// Written by: Eric Shepherd
//

#include <Application.h>
#include <Messenger.h>
#include <Message.h>
#include <Roster.h>
#include <Window.h>
#include <View.h>
#include <MenuBar.h>
#include <Menu.h>
#include <MenuItem.h>
#include <string.h>
#include <stdio.h>

// Application's signature

const char *APP_SIGNATURE = "application/x-vnd.Be-MessageWorld";

// Messages for window registry with application

const uint32 WINDOW_REGISTRY_ADD = 'WRad';
const uint32 WINDOW_REGISTRY_SUB = 'WRsb';
const uint32 WINDOW_REGISTRY_ADDED = 'WRdd';

// Messages for menu commands

const uint32 MENU_FILE_NEW = 'MFnw';
const uint32 MENU_FILE_OPEN = 'MFop';
const uint32 MENU_FILE_CLOSE = 'MFcl';
const uint32 MENU_FILE_SAVE     = 'MFsv';
const uint32 MENU_FILE_SAVEAS = 'MFsa';
const uint32 MENU_FILE_PAGESETUP = 'MFps';
const uint32 MENU_FILE_PRINT = 'MFpr';
const uint32 MENU_FILE_QUIT     = 'MFqu';

const uint32 MENU_OPT_HELLO     = 'MOhl';

const char *STRING_HELLO = "Hello World!";
const char *STRING_GOODBYE = "Goodbye World!";

//
// HelloView class
//
// This class defines the view in which the "Hello World"
// message will be drawn.
//
class HelloView : public BView 
{
        public:
                                                HelloView(BRect frame);
                virtual void    Draw(BRect updateRect);
                void                    SetString(const char *s);
        
        private:
                char                    message[128];
};


//
// HelloView::HelloView
//
// Constructs the view we'll be drawing in.
// As you see, it doesn't do much.
//
HelloView::HelloView(BRect frame)
: BView(frame, "HelloView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW) 
{
        SetString(STRING_HELLO);
}


//
// HelloView::SetString
//
// Sets the message to draw in the view.
//
void HelloView::SetString(const char *s) 
{
        if (strlen(s) < 127) 
        {
                strcpy(message, s);
        }
}

//
// HelloView::Draw
//
// This function is called whenever our view
// needs to be redrawn.  This happens only because
// we specified B_WILL_DRAW for the flags when
// we created the view (see the constructor).
//
// The updateRect is the rectangle that needs to be
// redrawn.  We're ignoring it, but you can use it to
// speed up your refreshes for more complex programs.
//
void HelloView::Draw(BRect updateRect) 
{
        MovePenTo(BPoint(20,75));                       // Move pen
        DrawString(message);
}


//
// HelloWindow class
//
// This class defines the hello world window.
//
class HelloWindow : public BWindow 
{
        public:
                                                HelloWindow(BRect frame);
                                                ~HelloWindow();
                virtual bool    QuitRequested();
                virtual void    MessageReceived(BMessage *message);
        
        private:
                void                    Register(bool need_id);
                void                    Unregister(void);
                
                BMenuBar                *menubar;
                HelloView               *helloview;
};


//
// HelloWindow::HelloWindow
//
// Constructs the window we'll be drawing into.
//
HelloWindow::HelloWindow(BRect frame)
: BWindow(frame, "Untitled ", B_TITLED_WINDOW, B_NOT_RESIZABLE|B_NOT_ZOOMABLE) 
{
        BRect r;
        BMenu *menu;
        BMenuItem *item;
        
        // Add the menu bar
        
        r = Bounds();
        menubar = new BMenuBar(r, "menu_bar");
        AddChild(menubar);

        // Add File menu to menu bar
        
        menu = new BMenu("File");
        menu->AddItem(new BMenuItem("New", new BMessage(MENU_FILE_NEW), 'N'));
        menu->AddItem(new BMenuItem("Open" B_UTF8_ELLIPSIS,
                                        new BMessage(MENU_FILE_OPEN), 'O'));
        menu->AddItem(new BMenuItem("Close", new BMessage(MENU_FILE_CLOSE), 
'W'));
        menu->AddSeparatorItem();
        menu->AddItem(new BMenuItem("Save", new BMessage(MENU_FILE_SAVE), 'S'));
        menu->AddItem(new BMenuItem("Save as" B_UTF8_ELLIPSIS,
                                        new BMessage(MENU_FILE_SAVEAS)));
        menu->AddSeparatorItem();
        menu->AddItem(new BMenuItem("Page Setup" B_UTF8_ELLIPSIS,
                                        new BMessage(MENU_FILE_PAGESETUP)));
        menu->AddItem(new BMenuItem("Print" B_UTF8_ELLIPSIS,
                                        new BMessage(MENU_FILE_PRINT), 'P'));
        menu->AddSeparatorItem();
        menu->AddItem(new BMenuItem("Quit", new BMessage(MENU_FILE_QUIT), 'Q'));
        menubar->AddItem(menu);

        // Add Options menu to menu bar
        
        menu = new BMenu("Options");
        item=new BMenuItem("Say Hello", new BMessage(MENU_OPT_HELLO));
        item->SetMarked(true);
        menu->AddItem(item);
        menubar->AddItem(menu);
        
        // Add the drawing view
        
        r.top = menubar->Bounds().bottom+1;
        AddChild(helloview = new HelloView(r));
        
        // Tell the application that there's one more window
        // and get the number for this untitled window.
        
        Register(true);
        Show();
}


//
// HelloWindow::~HelloWindow
//
// Destruct the window.  This calls Unregister().
//
HelloWindow::~HelloWindow() 
{
        Unregister();
}


//
// HelloWindow::MessageReceived
//
// Called when a message is received by our
// application.
//
void HelloWindow::MessageReceived(BMessage *message) 
{
        switch(message->what) 
        {
                case WINDOW_REGISTRY_ADDED:
                        {
                                char s[22];
                                int32 id = 0;
                                if (message->FindInt32("new_window_number", 
&id) == B_OK) 
                                {
                                        sprintf(s, "Untitled %ld", id);
                                        SetTitle(s);
                                }
                        }
                        break;
                        
                case MENU_FILE_NEW:
                        {
                                BRect r;
                                r = Frame();
                                r.OffsetBy(20,20);
                                new HelloWindow(r);
                        }
                        break;
                case MENU_FILE_CLOSE:
                        Quit();
                        break;
                case MENU_FILE_QUIT:
                        be_app->PostMessage(B_QUIT_REQUESTED);
                        break;
                
                case MENU_OPT_HELLO:
                        {
                                BMenuItem *item;
                                const char *s;
                                bool mark;
                                
                                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;
                
                default:
                        BWindow::MessageReceived(message);
                        break;
        }
}


//
// HelloWindow::Register
//
// Since MessageWorld can have multiple windows and
// we need to know when there aren't any left so the
// application can be shut down, this function is used
// to tell the application that a new window has been
// opened.
//
// If the need_id argument is true, we'll specify true
// for the "need_id" field in the message we send; this
// will cause the application to send back a
// WINDOW_REGISTRY_ADDED message containing the window's
// unique ID number.  If this argument is false, we won't
// request an ID.
//
void HelloWindow::Register(bool need_id) 
{
        BMessenger messenger(APP_SIGNATURE);
        BMessage message(WINDOW_REGISTRY_ADD);
        
        message.AddBool("need_id", need_id);
        messenger.SendMessage(&message, this);
}


//
// HelloWindow::Unregister
//
// Unregisters a window.  This tells the application that
// one fewer windows are open.  The application will
// automatically quit if the count goes to zero because
// of this call.
//
void HelloWindow::Unregister(void) 
{
        BMessenger messenger(APP_SIGNATURE);
        
        messenger.SendMessage(new BMessage(WINDOW_REGISTRY_SUB));
}


//
// HelloWindow::QuitRequested
//
// Here we just give permission to close the window.
//
bool HelloWindow::QuitRequested() 
{
        return true;
}


//
// HelloApp class
//
// This class, derived from BApplication, defines the
// Hello World application itself.
//
class HelloApp : public BApplication 
{
        public:
                                                HelloApp();
                virtual void    MessageReceived(BMessage *message);
        
        private:
                int32                   window_count;
                int32                   next_untitled_number;
};


//
// HelloApp::HelloApp
//
// The constructor for the HelloApp class.  This
// will create our window.
//
HelloApp::HelloApp()
: BApplication(APP_SIGNATURE) 
{
        BRect windowRect;
        windowRect.Set(50,50,349,399);
        
        window_count = 0;                       // No windows yet
        next_untitled_number = 1;       // Next window is "Untitled 1"
        new HelloWindow(windowRect);
}


//
// HelloApp::MessageReceived
//
// Handle incoming messages.  In particular, handle the
// WINDOW_REGISTRY_ADD and WINDOW_REGISTRY_SUB messages.
//
void HelloApp::MessageReceived(BMessage *message) 
{
        switch(message->what) 
        {
                case WINDOW_REGISTRY_ADD:
                        {
                                bool need_id = false;
                                
                                if (message->FindBool("need_id", &need_id) == 
B_OK) 
                                {
                                        if (need_id) 
                                        {
                                                BMessage 
reply(WINDOW_REGISTRY_ADDED);

                                                
reply.AddInt32("new_window_number", next_untitled_number);
                                                message->SendReply(&reply);
                                                next_untitled_number++;
                                        }
                                        window_count++;
                                }
                                break;
                        }
                case WINDOW_REGISTRY_SUB:
                        window_count--;
                        if (!window_count) 
                        {
                                Quit();
                        }
                        break;
                default:
                        BApplication::MessageReceived(message);
                        break;
        }
}


//
// main
//
// The main() function's only real job in a basic BeOS
// application is to create the BApplication object
// and run it.
//
int main(void) 
{
        HelloApp theApp;                // The application object
        theApp.Run();
        return 0;
}


///////////////////////////// THE END ///////////////////////////////

Other related posts: