File: jan_tutorial.tar.gz
       Author: Be Developer Support (devsupport@be.com)
      Release: 2.0
Compatibility: DR8
     Location: Samples
  Description: Tutorial covering BeOS programming basics
        Notes:
        
Scope
=====

This tutorial is intended for the beginning Be developer.
Knowledge of C++ is assumed. The following topics will be
covered:

* Creating projects
* Basic application framework
* Creating windows, and views within windows (view hierarchy)
* Graphics and dynamically resizable views

Note: Reading/reviewing The BeBook while reading this tutorial 
will enhance your learning experience.....

Introduction
============

The January program displays the calendar for the month of January 
in a window. In addition, the calendar dynamically redraws as the 
window is resized.


Source File description
-----------------------

	* GMCGraphics.cpp
		Defines an object, BTSMonthGraphic, which can be 
		displayed by inserting it into a view.
		
	* MonthView.cpp
		A view that contains an object of type BTSMonthGraphic.
		
	* January.cpp
		Contains the main routine and definition of the 
		application window, BTSApplication window.
		
	* StringLabel.cpp
		Utility class, BTSSTringLabel, for displaying a string, 
		using centering and styling.
	
	* January.rsrc
		Resource file containing Icon and other information 
		about January.	
		

Projects
========
The BeOS development environment allows developers to create
project files that can be used in conjunction with the IDE, for
a graphical presentation of the project and compilation 
information and commands. For those from the UNIX world, 
makefiles and command-line compiling are also supported. The
January project gives examples of both.

CodeWarrior users should open the "January.proj" file at this
time, while UNIX fans should open up a terminal and cd to the
January directory.


Application Framework
=====================

The base application class is BApplication. Every application must
instantiate an instance of BApplication. A reference to this object
is automatically placed in the the global variable be_app, for convenience.
In the case of January, it's not necessary to override the default
application class behavior. Also, we want a window to open 
automatically when we start the application. So, our main routine 
looks like this:

main()
{	
	BTSApplicationWindow		*aWindow;
	BRect						aRect;

	// We can use a standard BApplication object, because
	// we don't have anything special to add to what it does
	// by default.
	BApplication *myApplication;

	myApplication = new BApplication('JANR');
	
	// set up a rectangle and instantiate a new window
	aRect.Set(20, 20, 200, 200);
	aWindow = new BTSApplicationWindow(aRect);

	// Once everything is setup, start running the application.
	myApplication->Run();

	// Delete the application object before exiting.	
	delete(myApplication);
	return(0);
}

Application Specific Windows
----------------------------

Any application that creates windows will have to subclass 
the BWindow class. In this case, our window class is 
BTSApplicationWindow. The definition of the class looks like
this:

class BTSApplicationWindow : public BWindow 
{

public:
				BTSApplicationWindow(BRect frame); 
virtual	bool	QuitRequested();
};

Only two methods are defined. Overriding the constructor allows
us to insert the code that creates the calendar within the
window, as follows:

BTSApplicationWindow::BTSApplicationWindow(BRect frame)
	: BWindow(frame, "January", B_TITLED_WINDOW, 0)
{
	
	BRect			aRect = frame; // rect same size as window.
	BTSMonthView	*aView;

	// Move rect so top left is a (0,0)
	aRect.OffsetTo(B_ORIGIN);
	aView = new BTSMonthView(aRect, "MonthView");
	
	// Lock the window before altering contents.
	Lock();
	// add view to window
	AddChild(aView);
	// Unlock after done altering window contents.
	Unlock();
	
	// make window visible
	Show();
}

We create an object of type BTSMonthView, making it the same size
as the window, then insert it into the window. Every view can be
thought of as a container which can hold other views. This is 
called a view hierarchy. At the root of every view hierarchy is
an object that is either a BWindow or a subclass of BWindow. 
While the BWindow itself is not a view, it has a private member
that is a view. This view is not accessible to programmers.
So, you do not draw directly into a window, but rather you create
a view of your own, draw into it, and insert it into the window's
view hierarchy. And that's exactly what is done here.

The other unusual thing is the Lock() and Unlock() calls around 
the AddChild() call. It is necessary to Lock a window whenever
you change its internal state. Examples of this are: changing the
view hierarchy, calling Draw() explicitly, or resizing the window.
These calls are necessary to keep other threads from also changing
the window at the same time, resulting in unpredictable results.
However, functions that are called directly by the system (Draw(),
WindowActivated(), MessageReceived() ) don't need to perform a 
Lock(), as the system does it before calling the function. Everone
should read the section in BView chapter of the Interface Kit
documentation entitled "Locking the Window".

All of the drawing actually occurs within the BTSMonthView. Let's
ignore that for now. The only other method in BTSApplicationWindow
is QuitRequested:

bool 
BTSApplicationWindow::QuitRequested()
{
	// Tell the application to quit!
	be_app->PostMessage(B_QUIT_REQUESTED);
	return(TRUE);
}

When a user closes a window by any means, the BWindow object's
hook function QuitRequested() is called. In this case, the 
proper response is to quit the program entirely, by posting
the "B_QUIT_REQUESTED" message to the application. There are
a lot of other hook functions like this for common window
activities;check the "Responding to the User" section of the
Interface Kit documentation.

Any application that displays a user interface should exit when
the last window closes. If this is not done, the application's
main menu will disappear, and the user will be unable to quit
the program in an acceptable way. If you need this type of functionality, 
you should create a "dummy" BWindow object and hide it, only deleting 
it when the program exits. If you have multiple windows, you need to 
keep track of the window count and exit the program when the count 
gets to 0. This problem will disappear in a later release of the
BeOS.

At this point, we have a basic application framework created; 
these same definitions of main and of the application window
could be used for any single-window application. The only
thing that would need to be changed is the view that we would
be inserting into the window in the window's constructor.


Graphics and dynamically resizing views
=======================================


The graphics work is all done within the BTSMonthView object.
Here's the constructor:

BTSMonthView::BTSMonthView(BRect frame, char *name) 
	: BView(frame, name, B_FOLLOW_ALL, 
		B_FULL_UPDATE_ON_RESIZE|B_WILL_DRAW|B_FRAME_EVENTS) 	
{	
	fBackgroundGraphic = new BTSMonthGraphic(this);	
	SetViewColor(B_TRANSPARENT_32_BIT);
}

All that needs to be done in the constructor is to create 
the graphics object representing the calendar, BTSMonthGraphic.
Note that we pass BTSMonthGraphic a 'this' pointer to the
view. This is because BTSMonthGraphic is not a view subclass
itself, so it needs a graphics context in order to draw. This
is given by the view. In many cases, it is more convenient
to just make the graphics object itself a subclass of BView.
However, for simple graphics that don't interact with the user
(such as text labels), subclassing from BView is unnecessary,
and the approach of passing the object a drawing context is
faster, more efficient, and makes the graphics object more
general purpose.

Note the flags passed at the end of the BView constructor. There
are a lot of flags that can be passed to a view to customize its
behavior; see the interface kit documentation for more information.

The particular flags used here specify the following:

FOLLOW_ALL - When the view is resized from any direction
				grow the view in that direction.
				
FULL_UPDATE_ON_RESIZE - Invalidate the entire view and not just the
				freshly exposed portion.  We want this because otherwise
				we would only redraw new portions, when in our case we
				want the entire view to be drawn if the size changes.
				
FRAME_EVENTS -  We want to be notified when the view's size changes.
				If we don't use this flag, then the FrameResized method
				will not be called and we'll never know when to change
				the size of the graphic.
WILL_DRAW -   	The view wants to have its Draw() function called in
				response to an update event.
				


Dynamic resizing is easy; since we specified FRAME_EVENTS, as the window 
is resized, we get a stream of calls to the view's FrameResized() 
hook function, and in turn we resize the embedded graphic:

void		
BTSMonthView::FrameResized(float new_width, float new_height)
{
	fBackgroundGraphic->SetSize(BPoint(new_width, new_height));
}
 

Drawing
-------

Finally, drawing! the calendar graphic is represented by a grid. As
the window resizes, the grid spacing changes. Based on the new
grid spacing, the font size and text for the various labels on
the calendar are selected. We can follow all the action starting
with the SetSize() method, which is called by BTSMonthView's
Draw() method:

void BTSMonthGraphic::SetSize(BPoint size)
{
	fSize = size;
	Invalidate();
}

void BTSMonthGraphic::Invalidate()
{	
	// Do the resizing magic
	// Re-calculate the various size things
	CalculateParameters();
	CalculateDayLabels();
	
	// Make sure the view draws itself
	fImageNeedsUpdate = 1;
	
}

OK...without going into too much detail, CalculateParameters()
determines the new grid size and spacing. CalculateDayLabels()
determines what the labels should be for the days of the week;
if the grid spacing is large, "Sunday" will work; if it's small,
it might be just "Su". fImageNeedsUpdate is a flag that tells
the Draw() function of the graphic that it needs to do some work.
With views, the Draw() function is called automatically by the
enclosing container when an update message is received that 
includes some or all of the region occupied by the view. In
this case, our graphic object is not a view, so we must maintain
this information ourselves.

The Draw() method itself is fairly straightforward. The background
is drawn, the calendar outline is drawn, the month and day names
are drawn, and the days of the month are drawn. All text is
represented by StringLabel objects which were created in the Init()
method. The StringLabel object, like BTSMonthGraphic, is not a
view, but rather knows how to draw itself within a view. 

Drawing on the BeOS is not done by the application itself, but by
the application server. The app server is a separate process, 
one copy of which is always running. (You can see this by opening up 
a terminal window and entering the command "ps | grep app_server". 
All drawing commands are communicated to the app server for execution. 
To speed this up, line drawing commands can be bundled in arrays
so that the app server can execute them in a more rapid fashion.
This is done by using the BeginLineArray(), AddLine(), and EndLineArray() 
methods. After calling BeginLineArray(), calls to AddLine() add commands 
to the array. Calling EndLineArray() informs the app server that the
entire array of line drawing commands should be executed. The following 
piece of code from BTSMonthGraphic::Draw() illustrates this:


	fView->BeginLineArray(7);	
	for (counter = 1; counter < 7; counter++)
	{
		BPoint startPoint(xStart+(counter*GridCellSize.x)-2,yStart);
		BPoint endPoint(xStart+(counter*GridCellSize.x)-2,yEnd);
		fView->AddLine(startPoint, endPoint, blackColor);
	}
	fView->EndLineArray();

The code above results in only one large communication with the app 
server, instead of 7 small ones. However, calling AddLine() too many
times without calling EndLineArray() may result in worse performance,
due to the memory overhead of the array. (The BeBook
recommends no more than 256).

The separation of the actual drawing from the drawing commands 
brings up an important problem; how does the programmer know when
a drawing command has actually executed? In many cases the app
server may not execute drawing commands as soon as they are received.
In order to force the app server to complete all drawing requests
for a given view, the Sync() method of the view should be called.
This method is synchronous, and when it returns you can be sure that
all draw commands for the view have been executed. This is particularly
important when doing interactive drawing; for example, if the user is
dragging a graphic around within a window, if Sync() is not called
after drawing the graphic, the dragging may appear jumpy. 


BTSStringLabel
--------------

BTSStringLabel is an object that can remember the font parameters 
necessary to draw it, including position within the view, font
name, size, shear, rotation, and color.Rather than have separate views 
for each piece of text, the BTSStringLabel is associated with a view. 
When it comes time to draw, the BTSStringLabel object changes the view's 
font parameter to match its own parameters,then draws its associated 
string:

void
BTSStringLabel::Draw(BView* aView)
{
	if (fNeedsCalculation)
		Recalculate(aView);
		
	// Setting font info
	aView->SetFontName(fFontInfo.name);
	aView->SetFontSize(fFontInfo.size);
	aView->SetFontShear(fFontInfo.shear);
	aView->SetFontRotation(fFontInfo.rotation);
	
	aView->SetHighColor(fColor);
	aView->MovePenTo(fStartPoint);
	aView->DrawString(fString);
}


IconWorld
=========

IconWorld is used to do basic resource configuration for your app.
The IconWorld section in the BeOS User's Guide
describes it very well. For mac developers, think of it as ResEdit
for the BeOS, although it has far fewer resources.

Open January.rsrc using IconWorld. Note under the "App Info" menu
that the flag "Single Launch" is set. This means that one copy of
the application can be running per executable file, as opposed to
"Exclusive Launch", which means one application can be running on
the system, period. Of course "Multiple Launch" means the same
app can be launched multiple times on the same system. In this case,
the selection of "Single Launch" was arbitrary, but for some apps
it may be very important.

Note the icon's type is 'BAPP', which means it's associated with the
application. If this app had documents associated with it, we could
create another icon to represent the documents, and give the icon
a type that matched the signature of the document files.


Close IconWorld.


Building and Running the Project
================================
You can refer to the BeOS User's Guide and Metrowerks documentation for
complete details on building applications. Below is a short summary
of the steps.

You now have a basic application framework that you can use as a
jumping off point for your own application. No employees of Be or
their children were involved in vomiting episodes (projectile or
otherwise) during the writing of this tutorial. We hope the same
is true for you.

For UNIX users
--------------
in the January directory, type "make". If you want, you can open up the
makefile and check it out. The only thing that may look unfamiliar is
the lines:

	copyres $(RESOURCE_FORK) $@
	setfile $@

The first line just appends the resource information to the end of the 
file. The second line informs the BeOS that the newly created program is
an executable file.


For CodeWarrior users
---------------------

Choose "Make" from the project menu. Choose "Run" if you want to run it.