TweetFollow Us on Twitter

First Steps Volume Number: 17 (2001)
Issue Number: 4
Column Tag: PowerPlant Workshop

First Steps

By Aaron Montgomery

How one goes about writing a PowerPlant application

My Intended Audience

This series of articles is intended to be an introduction into the use of the PowerPlant application framework. It is intended to be a gentler introduction than The PowerPlant Book supplied on the CodeWarrior CD and more elementary than previous MacTech articles (in particular, those by John C. Daub). We will start by building a very basic application in this article and we will add features to it in later articles (upcoming articles are planned on the topics of: debugging, windows, files, dialogs, preferences, and panes).

PowerPlant is big and it would be hard (if not impossible) to try to provide a simple linear introduction to the topic. As a result, these articles are likely to introduce some aspect of the framework without fully explaining how it works. In some cases, the series will make a concerted effort to return to the topic but in other cases the reader will be expected to do a little reading on their own. One consequence of this is that it will probably be worthwhile to re-read earlier articles after finishing with the later articles. Another consequence is that often I will introduce a term without explanation and you may need to read more of the article before it makes sense.

While writing this article, I've assumed that the readers are familiar with the C++ programming language and basic Macintosh API programming topics. In addition to these programming skills, readers are also assumed to be familiar with the CodeWarrior IDE (including the use of the class browser and debugger).

Myself

Before you start reading this article, it is best to know where I learned what I know. I program as a hobby (not as a profession) and have been using PowerPlant in my projects for the past three years. Although I consider myself fairly adept with the framework, I don't claim to know all the details. I encourage those who know the framework better to contact me if I make any errors or miss any topics. Probably the best forum for this is the PowerPlant newsgroup and I will also post errata at my web site (see by-line).

Why Learn PowerPlant

PowerPlant is an application framework. You can find a detailed discussion of what this means in The PowerPlant Book, but basically, this means that it provides much of the routine code needed for a Macintosh application (in particular, the interface) and allows you to focus on the details of your specific program. At the time that I am writing this, a natural question arises: why bother with PowerPlant when you could use Cocoa? Personally, I decided to continue to use PowerPlant for two reasons. The first of which is my own personal familiarity with C++. Since I only program as a hobby, I don't have the time (or the need) to build that level of familiarity with Objective C and would like to continue to use a language I know. A second reason is that many of the programs I write for educational purposes will be used in environments where OS X is not going to be available for a number of years. PowerPlant allows me to use a single code-base to support a wide variety of platforms (from 68K to G4).

The Tools

This article will assume that you are using CodeWarrior 6 with the net-update to IDE 4.1.0.3 and no other modifications. In particular, it assumes the use of Universal Headers 3.3.2 and no modified PowerPlant files. I believe much of the code I write should also run on CodeWarrior 5 or with newer Universal Headers without much trouble (other than that caused by the Metrowerks' PowerPlant source). If you are trying to use these articles as a guide and run into trouble with a particular configuration, let me know and I will try to suggest a solution.

Stationary Options

The first thing to do with any project is to create a new project file from the Metrowerks supplied stationary. There are four options: Basic, Appearance, Document or Advanced. The Basic option is the simplest and all other options are built on top of it. The Document and Appearance options have identical library source file and include everything in the Basic option as well as the Appearance Classes, the Graphic Utilities, the Grayscale Classes and the Page Controller. The difference between the two versions is that the sample code for the Document option is set up for a basic application supporting the use of documents. The Advanced option contains everything in the Document and Appearance options as well as the Context Menu, the Debugging Classes, the Table Classes and the Threads Classes. This is the option I choose most frequently (the Debugging Classes are so good, they will be the topic of the second article in this series).

For this project, I started with the Advanced option, moved some unused classes out of the regular targets to a side-target (Other Stuff). We'll add them back to the build targets when I start using them in later projects. I have also made some stylistic changes (e.g., requiring explicit use of the PowerPlant namespace and adding a separate file to contain main()). Finally, I removed some code so that I will be able to focus on the basics. Much of the removed code will reappear in later articles.

Naming Conventions

Before examining any source code, the PowerPlant naming conventions deserve mention. All PowerPlant types defined by typedef macros end with a T. All PowerPlant classes begin with L (for library), U (for utility) or St (for stack-based). Class methods begin with uppercase letters and use capitalization as a word-break. Class data (static data) begins with the letter s and object data begins with the letter m. Calls to the Macintosh API are indicated by a global scope operator (::). Local variables are indicated by the prefix the. You can find more conventions in The PowerPlant Book.

User classes (those that you write) will typically start with a C (for class), I also use Ut (for utility) and Au (for automatic, i.e., stack-based) classes. Class data for my classes begins with our and object data begins with my. This helps me distinguish between data I am responsible for and data coming from the framework.

Browsing the Initialization Code

The first code we will discuss is the function main() in main.cp. One effective way to learn about PowerPlant is to use the class browser to examine a function's definition and commentary. In order to follow along, you will need to make the project with the class browser activated (see the Build Extras preference panel for the target). I will be using the PPC Debug target although you may want to use a different target depending on your setup. (I have removed all comments for listing in the magazine.)

main() in main.cp

int main()
{
   InitializeHeap(5);
   UQDGlobals::InitializeToolbox();
   ::FlushEvents(everyEvent, nil);
   UEnvironment::InitEnvironment();

   CDocumentApp   theApp;
   theApp.Run();
   
   return 0;
}

We'll quickly run through the initialization code in main() using the browser. Selecting the InitializeHeap() (e.g., by double-clicking the word) and then using Find Definition in the Search (or by using command-'), we can get the source for the function. PowerPlant commentary is good and the comments before the definition of InitializeHeap() describe its purpose: it expands the heap and calls the appropriate version of MoreMasters(). The next method is InitializeToolbox() and looking at its definition (and code commentary) you can see that it contains the standard Macintosh Toolbox initialization code. Following the toolbox initialization is a ::FlushEvents() call. This isn't called in the InitializeToolbox() method, but commentary in the PowerPlant stationary indicates that failure to call it may lead to problems and so we place it here. The last call in this sequence is InitEnvironment(). Looking at its definition (and code commentary) indicates that it will check for things like the existence of the Appearance Manager. Even if you do not use the UEnvironment class yourself, PowerPlant uses it and so you need to insure that it is initialized.

We have now reached the class CDocumentApp. This class represents the Macintosh application and the object theApp represents this instantiation of the Macintosh application. Again, we can use the browser to examine the constructor.

CDocumentApp() in CDocumentApp.cp

CDocumentApp::CDocumentApp()
{
   if (UEnvironment::HasFeature(env_HasAppearance))
   {
      ::RegisterAppearanceClient();
   }

   CTextDocument::RegisterClasses();
}

The HasFeature() determines if the Appearance Manager is installed. If it is installed, the application registers itself with the operating system. The second statement is a call to the static method RegisterClasses() of CTextDocument. This is necessary to build the window structure from the resource file and will be discussed in the third article.

Stepping Through Run()

The browser is good if the code is very linear, once the code becomes complicated, stepping through the source with the debugger can be a great aid in understanding. In order to do this, add a breakpoint on the line CDocApplication.Run() call in main.cp and run the application (with the debugger enabled). When the debugger halts at this breakpoint, step into the Run() method. I will describe, but won't list, the code from the PowerPlant source. The first thing that occurs is a sequence of initializations for the application: creating a menu bar, setting up the AppleEvent handlers and then calling Initialize(). This virtual method should handle initialization routines that need to happen after the menu bar and AppleEvent handlers have been initialized (and hence cannot occur in the constructor). Later we will use this method to create a dynamic menu, for now we fall back on the inherited method. PowerPlant then calls ForceTargetSwitch() (described below), initializes the cursor, updates the menu and starts handling events.

Event Processing

We have now reached the main event loop. There are two types of events to which the application needs to respond: AppleEvents and menu-commands. The PowerPlant framework handles the dispatching of some basic AppleEvents to virtual methods in the LApplication class. The only ones we need to implement here are the methods associated with Open Application and Reopen Application events. PowerPlant dispatches these to the StartUp() and ReopenApp() methods respectively.

StartUp() and ReopenApp() in CDocumentApp.cp
void
CDocumentApp::StartUp()
{
   ObeyCommand(cmd_New, nil);
}

void
CDocumentApp::DoReopenApp()
{   
   if ((UDesktop::FetchTopRegular() == nil)
         && (UDesktop::FetchTopModal() == nil))
   {
      ObeyCommand(cmd_New, nil);
   }
}

The basic idea of both methods is the same: pass the responsibility to ObeyCommand(). There only difference is that in ReopenApp(), the ObeyCommand() is only called if no other windows are open. In the actual source file, there is a long commentary about why this is the appropriate behavior.

The method ObeyCommand() is a virtual method of the LCommander class (from which LApplication inherits). Each LCommander has a supercommander (well, not quite, the top commander will not have a supercommander). The collection of commanders generated by following supercommanders up to the top commander is called the command chain. At any given time, exactly one LCommander object has control of the application, this object is said to be on duty. The call to ForceTargetSwitch() that we saw above placed the application object on duty. When ObeyCommand() is called, the object on duty should either obey the command or pass it to its base class. A basic example of this in this application is the cmd_New when no windows are open. To see how this is handled, place a breakpoint at the top of the ObeyCommand() method in LDocApplication (you can find this by typing command-' when no text is selected and typing ObeyCommand). Once the breakpoint is set, switch to the application (or start it from the debugger) and select Open from the File menu. You should step into the SendAECreateDocument() and MakeNewDocument() methods, the second is supplied by the CDocumentApp class.

MakeNewDocument() in CDocumentApp.cp
LModelObject*
CDocumentApp::MakeNewDocument()
{
   return new CTextDocument(this);
}

That was simple, we just pass all the work off to the CTextDocument class. We will leave CDocumentApp for a moment and explore the CTextDocument class.

CTextDocument() from CTextDocument.cp

CTextDocument::CTextDocument(
   LCommander*      inSuper)
   : LSingleDoc(inSuper)
{
   mWindow = LWindow::CreateWindow(PPob_TextWindow, this );
   
   myTextView =
      static_cast<LTextEditView*>(
         mWindow->FindPaneByID(kTextView));
   mWindow->SetLatentSub(myTextView);

   NameNewDoc();
   
   mWindow->Show();
}

Much of the code in the constructor handles the user interface and will be discussed in greater detail in the third article. We focus on those calls that concern the command chain. The CreateWindow() method creates a LWindow from data in the resource fork of the application. LWindow is derived from LCommander and the second argument in the CreateWindow() method sets the window object's supercommander. The window resource contains a LTextEditView and LTextEditView also inherits from LCommander and its supercommander will be set to the LWindow being created. The next two lines find the LTextEditView and then establish it as being the on duty when the window opens. The remaining two calls are mundane: NameNewDoc() uses a very naive method to generate unique window names (which will be improved in the third article) and Show() makes the window visible.

Now that we have a non-trivial command chain, we can demonstrate what happens when an object cannot obey a command. In this case, the command works it way up to the ObeyCommand() implementation in LCommander and you should place a breakpoint in that method. Now make sure a window is open in the application and select the Close item from the File menu. The first time you stop in LCommander's ObeyCommand(), a LTextEditView has received the command and is passing it upward to its supercommander. Running the debugger will cause the window to close and the application to continue. This means that the LWindow object was able to handle the command.

A good question arises here: If the LWindow handled the close event, what happened to the CTextDocument? To determine this, place a break point in the CTextDocument destructor and close another window. Before looking at the destructor code, examine the stack. By selecting on various locations of the stack you can see that the LDocument object's Close() method is called which causes the CTextDocument object to be destructed.

~CTextDocument() in CTextDocument.cp

CTextDocument::~CTextDocument()
{
   try
   {
      TakeOffDuty();
   }   
   catch (...)
   {
   }
}

The destructor is very simple and only calls TakeOffDuty() which is inherited from LCommander. This call takes the CTextDocument off duty (a good thing since its memory is about to be reclaimed).

To see a more complicated demonstration of the command chain, make sure a breakpoint is still set in LCommander's implementation of ObeyCommand(). Now make sure a window is open and chose Quit from the File menu. This command will need to work up many levels in the command chain. The LTextEditView object passes it to the LWindow object which passes it to the LDocument object which passes it on to the LApplication object which causes the application to quit.

Menu Manipulation

The other virtual method that you will need to override when you add (or remove) commands is FindCommandStatus(). We will discuss this more fully in a later article when we handle menu management, but for now we only need to worry about the first two parameters. The first, inCommand, is of type CommandT and is the command whose status is being determined. The second, outEnabled, is of type Boolean& and will be set to true if the LCommander object can handle the command. Each time the menus are displayed, the method FindCommandStatus() is called in a manner analogous to ObeyCommand(). If any LCommander in the command chain can handle the command, the method returns true and the menu item is enabled. If no LCommander in the command chain can handle the command, the method will return false and the menu item will be disabled. Because they are called in identical ways, ObeyCommand() and FindCommandStatus() need to be consistent in each class.

FindCommandStatus() in CDocumentApp.cp

void
CDocumentApp::FindCommandStatus(
   CommandT   inCommand,
   Boolean&   outEnabled,
   Boolean&   outUsesMark,
   UInt16&   outMark,
   Str255      outName)
{
   switch (inCommand)
{
      case cmd_Open:
{
outEnabled = false;
}
break;
      
      default:
{
         LDocApplication::FindCommandStatus(
inCommand, outEnabled, outUsesMark,
outMark, outName);
      }
break;
   }
}

The PowerPlant class LDocApplication assumes the application will have the ability to open documents. Since we aren't going to implement this until we discuss files (in the fourth article), we need to disable the command in the File menu. We simply set outEnabled to indicate that the command is never handled and the item will remain disabled. Similarly, the CTextDocument overrides its FindCommandStatus() method to disable the cmd_Save, cmd_SaveAs and cmd_Print commands. You can trace this method in exactly the same way as we did for ObeyCommand(), but that will be left as an exercise for the reader.

Concluding Remarks

If you've followed along so far, you should be able to start examining some of the command chains in the sample projects that are provided on the CodeWarrior CD. You might also want to start reading through the references I present at the end of this article. The next article will discuss PowerPlant's debugging classes. Following that we will expand on PowerPlant's document classes (windows in the third article and files in the fourth article).

PowerPlant References

The PowerPlant Book on the CodeWarrior CD is an introduction to PowerPlant provided by Metrowerks. Unlike this series of articles, The PowerPlant Book focuses on the interface building aspects of PowerPlant first. If you need to obtain a high level of proficiency with PowerPlant, this is the book to work from cover to cover. The PowerPlant Advanced Topics book on the CodeWarrior CD continues where The PowerPlant Book leaves off. It covers more complicated classes such as the Drag & Drop Classes, Thread Classes and Networking Classes to name a few. The topics are presented so that you will be able to read each chapter as you need it (and not necessarily in a sequential order). Together these books constitute the primary texts for learning PowerPlant.

The PowerPlant Reference on the CodeWarrior CD provides documentation for most of the PowerPlant classes. I found this useful for a short period of time while learning PowerPlant, but after a few months I began to rely more on the class browser, the actual source, and the commentary in the source instead of the reference book.

If you have access to old MacTech articles, the "From the Factory Floor" articles in the October, November and December 1998 issues discuss PowerPlant. The first two of these start with a "How would you learn PowerPlant" question. John C. Daub also wrote a series of MacTech articles from December 1998 through June 1999 which cover individual components of PowerPlant (but do not cover the basic classes). The MacTech issues from 1999 are available online, but the 1998 articles were not up when I checked. You will want to search the article archives at <www.mactech.com/>.

Finally you might be able to find a PowerPlant class at Metrowerk's CodeWarrior U site <http://www.codewarrior.com/>. These courses tend to follow The PowerPlant Book.


Aaron teaches in the Mathematics Department at Central Washington University in Ellensburg, WA. Outside of his job, he spends time riding his mountain bike, watching movies and entertaining his wife and two sons. You can email him at montgoaa@cwu.edu, try to catch his attention in the newsgroup comp.sys.mac.oop.powerplant or visit his web site at http://mac69108.math.cwu.edu:8080/.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizon’s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: – 128GB... Read more

Jobs Board

Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.