This file is the start of a wxMac Developers Guide. Written by Cecil Coupe (ccoupe@simplot.com) with additions from Bill Hale and Tomaso Paoletti, and Matthew Flatt

I. Introduction

What is wxWindows and wxMac

wxWindows is a cross platform GUI development library with supporting applications and demos. wxMac is a port of wxWindows for the Macintosh.

Who is this document for?

Part II of this document will help you install and *use* wxWindows for the Macintosh. Part III and beyond are intended for alpha and beta testers and for those who want to add to or fix the wxMac code

II. Installation

Hardware and Software requirements

MacOS - System 7 or greater

Color QuickDraw

32 bit addressing - so you require a 68030/68040 or PowerPC.

Floating point hardware will help (probably a lot).

Enough memory for your C++ compiler to handle large programs.

You will want 20MB of free space in order to run the sample applications, 40MB free would be easier to deal with.

Archives and compression

There are several methods for bundling up a directory tree into a single file and compressing that file to reduce its size. Depending on how and which distribution you got you may need additional helper programs to create a real Macintosh folder(s).

1. If you are lucky, you downloaded a self extracting stuffit archive. Simply double click on the icon and StuffIt will ask where to put the Folders and files.

2. If you don't have the .sea or .sit versions of the distribution, but instead have a file ending in .tar.Z, .tar.gz, or .ZIP (or .zip), you need to acquire the following programs for your Macintosh:

Also note, that tar may not preserve some of the Mac special characters, in particular the 'pi' character is the Option-p keys, and needs to be placed in project files. Parentheses are replaced with '+', which is only really a problem for Symantec.

3. If you don't understand both steps 1 and 2, (and even if you don't need #2) you will have a extremely steep learning curve ahead of you. I'm sorry, but that is how it is in the cross platform world.

Chose your compiler/environment

Note: Symantec Projects and MPW makefiles are out of date. CodeWarrior is the only supported compilers.

By now you've untarred/unzipped/unstuffed the archive, you might have read the README files, and you've already tried to build the system with CodeWarrior or Think-C++. MPW aficionados just modified the Makefile and tried it. You probably wouldn't be reading this unless you had a problem. Lets try it again, from the archive/zip/tar file (you did make a copy, right).

You should decide whether you want to use MPW, CodeWarrior, or ThinkC++. They can all coexist in the same set of folders, but life will be so much better and easier if you chose one now and stick with it throughout your wxWindows exploration.

Structure of distribution directories

There will be a lot of directories/folders and files. The ones you want to deal with are "samples", "src" and "include" in the top-level directory, plus the project files and/or Makefile and hopefully the README's and Documentation. IGNORE the others. You'll have plenty of time later after you get a decent install.

Compiling a wxWindows program with Metrowerks C++ (v7)

The wxWindows library (used by all samples and any programs you write) has to be built first. In the top level directory, along with the 'src' and "include" directories should be a wxMac161-PPC.[[pi]] or wxMac161-68K[[pi]] project. Double click, select Make from the "Project" menu and cross your fingers. CodeWarrior may complain that some path's are not available, typically this is the path to wherever you installed Codewarrior. This error message is corrected by setting the project preferences (Edit->Preferences->Paths) to your CW installation Folder.

For 68k compilation, you must make sure that the library and all applications are compiled with the same prefs settings. As distributed, the projects build for 4 byte ints, 8 byte doubles, and no floating pt HW (that is 4i/8d). If you wish to change this you will also have to add the appropriate libraries into the project in place of the 4i/8d's that are there.

If all works well (no fatal compile errors) you can try the sample programs located in the "samples" directories. Drill down into samples:hello. Loaded it - I happen to like using the Finder to open the :samples:hello: folder and double clicking on the hello.ppc.[[pi]].

Although, the 'minimal' program is even simpler than 'hello', if hello doesn't run for you, well its a long and winding road...

Compiling a wxWindows program with Symantec C++ or MPW

Symantec and MPW is no longer supported with the alpha-4 snapshot. I'm not saying that you couldn't make it work, only that I no longer care to spend the copious amounts of time watching MPW compile. Its large enough under CodeWarrior as it is!

III. WxMac Internals

Aside from curiosity, there are only three reasons to be reading this section of the document:

A. Philosophy and Goals of the wxMac Project [and participants]

The first developer and instigator of the Macintosh port was Bill Hale who wanted to learn C++ by porting a "major" piece of code. The second to join was Tomaso Paoletti who wanted (?) The third was Cecil Coupe who just wanted a cross-platform GUI for the python language and got sucked into the effort. Bill created the Mac specific part of Julian Smarts (jacs@aiai.ed.ac.uk) wxWindows.

Louis Birk, Matthew Flatt, Scott Maxwell and Greg Whitehead joined the project, each with real applications that they wanted to port. Each contributed serious development effort for wxMac.

Given the different perspectives it should be no surprise that there are different stylistic tactics taken in various wxMac modules. So be it, We offer no apologies.

B. Necessary [useful] skills and reference material

See the wxMac_Status document. The bug you found may be known. Also, sometimes the wxWindows documentation differs from the actual implementation.

C. How the classes are laid out (file/project point of view)

Here is the list of .c and .cc files needed. This could be used to determine which files to load into another development environment.
Project Name: "Prog:wxmac161d-a4:wxMac161-PPC."
Precompiled Headers
wxGWin.pch
Base
DebugNew.cp
wb_dc.cc
wb_data.cc
wb_canvs.cc
wb_dialg.cc
wb_gdi.cc
wb_frame.cc
wb_hash.cc
wb_help.cc
wb_ipc.cc
wb_item.cc
wb_list.cc
wb_main.cc
wb_mgstr.cc
wb_obj.cc
wb_panel.cc
wb_ps.cc
wb_mf.cc
wb_stdev.cc
wb_sysev.cc
wb_text.cc
wb_timer.cc
wb_types.cc
wb_utils.cc
wb_win.cc
wxstring.cc
xfspline.cc
Mac Core
wx_app.cc
wx_area.cc
wx_buttn.cc
wx_canvs.cc
wx_check.cc
wx_choic.cc
wx_clipb.cc
wx_dc.cc
wx_dccan1.cc
wx_dccan2.cc
wx_dccan3.cc
wx_dcmem.cc
wx_dialg.cc
wx_frame.cc
wx_gdi.cc
wx_ipc.cc
wx_item.cc
wx_main.cc
wx_lbox.cc
wx_menu.cc
wx_messg.cc
wx_mnuit.cc
wx_mtxt.cc
wx_panel.cc
wx_rbox.cc
wx_rbut.cc
wx_sbar.cc
wx_screen.cc
wx_slidr.cc
wx_text.cc
wx_timer.cc
wx_txt.cc
wx_util.cc
wx_win.cc
Mac Helpers
wxBorder.cc
wx_comparestrings.c
wx_mac_utils.cc
wxBorderArea.cc
wxGetTxt.cc
wxLabelArea.cc
wxMacDC.cc
wxMacObj.cc
wxMacPPC.c
wxMargin.cc
wxButtonBorder.cc
wxRectBorder.cc
wxScroll.cc
wxScrollArea.cc
wxScrollData.cc
Unfinished Core
wx_group.cc
wx_mf.cc
Printing
wx_print.cc
wx_dcpr1.cc
wx_dcpr2.cc
wx_dcpr3.cc
wxExtend
exdc.cc
exdde.cc
extbar.cc
exutils.cc
exitems.cc
exmisc.cc
exgdi.cc
exwin.cc
wxImage
wximgfil.cc
wx_image.cc
wx_xbm.cc
wx_bmp.cc
libxpm
crbuffri.c
crdatfri.c
create.c
crifrbuf.c
crifrdat.c
data.c
hashtab.c
misc.c
parse.c
rdftodat.c
rdftoi.c
rgb.c
scan.c
simx.c
wrffrdat.c
wrffri.c
Note: you don't really need the wxExtend unless you want to compile a Mac version of WxPython in which case you should contact me (ccoupe@simplot.com) to get the "latest" wxPython known to work with the Mac (version 3, FYI)

D. How the classes really interact (debugging point of view)

The Macintosh platform derives dialogs from wxFrame class, while the other platforms derive dialogs from wxPanel class (however, the usage for dialogs should be the same). This is no long true since release 'a4p2'.

E. Mac-specific classes, methods, techniques and constraints

1. Internally generated Mac Menu Id's start at 129

128 is used for the Apple Menu, and that value is hard coded in some places (wx_menu.cc, wx_app.cc)

2. wxApp, CreateApp(), wx_main.cc, and your program.

There is a bug, exhibited in some wxWindows applications, that causes the the wxTheApp global to not get set. This problem started with Symantec C++(6.x) and shows up in some applications compiled with Code Warrior 6. There is a work-around (from Bill Hale) that involves changing your application code slightly:

You must define a class that is publicly derived from wxApp. You can name this derived class whatever you want. For the following, I will name it "MyApp". In this class, you must declare a public method which will override the pure virtual method "OnInit" that is declared in the class wxApp. Then, at the minimum, we will have the following in a header file called "MyApp.h":

	#include "wx_main.h"   // should really be called "wx_app.h"
	class MyApp: public wxApp
	{
		public:
			wxFrame* OnInit(void);
	};

You must implement the publicly derived class of wxApp that you have just defined. At the minimum, we will have the following in a source file called "MyApp.cc":

	#include "MyApp.h"
	wxFrame* MyApp::OnInit(void)
	{
		...
		...
		...
	}

You must finally declare and define a procedure "CreateApp" which the main program will call to create the object that you have derived from the class wxApp. You must use the name "CreateApp". It should be placed into the source file created in step B) above. The code that you need is as follows (where you should replace "MyApp" with the name of your derived class):
	void CreateApp(void);
	void CreateApp(void)
	{
		new MyApp;
	}

As distributed, this workaround is not enabled. If you need it, in wx_main.cc, in the main() function is a line

	// CreateApp();	// This procedure initializes the whole application
Simply uncomment this, and apply the work around above to your application.

3. How do these "Areas" work? What are Margins?

From: Bill Hale <hale@mailhost.tcs.tulane.edu>

The purpose of the wxArea objects is to allow a parent wxWindow object to have different origins for its children wxWindow objects.

A wxWindow object contains wxArea objects. These wxArea objects are nested. Each wxArea object has margin values (left, top, right, and bottom) which specify where the next wxArea object is situated. Each wxArea object contains wxWindow objects. These wxWindow objects are positioned within the wxArea by an offset value (X, Y). The wxArea object is clipped by the inner sibling wxArea objects (not coded yet). The child wxWindow objects are clipped by the parent wxArea object.

The width and height of a wxArea object is calculated from the width and height of its parent wxWindow and from the margins of the outer sibling wxAreas.

Example:

                        width 200
    ----------------------------------------------
    |           area A             2              | wxWindow parent
    |  |---------------------------------------|  |
    |  |          area B          20           |  |
    |  |                                       |  |
    |2 |  |--------------------------------|   | 2|
    |  |10|           area C               |16 |  | height 100
    |  |  |                                |   |  |
    |  |  |--------------------------------|   |  |
    |  |                           5           |  |
    |  |---------------------------------------|  |
    |                              2              |
    |---------------------------------------------|

Suppose the wxWindow parent has width 200 and height 100.

Suppose the area A has (l, t, r, b) = (2, 2, 2, 2).

Suppose the area B has (l, t, r, b) = (10, 20, 16, 5).

Let area C be the automatically generated client area, with (l, t, r, b) = (0, 0, 0, 0).

Then, the width and height of area A is 200 and 100. The width and height of area B is 196 and 96. The width and height of area C is 170 and 71.

Children wxWindow objects of area A are positioned with respect to area A origin; children wxWindow objects of area B objects are positioned with respect to area B origin; and, likewise, for area C. In effect, these children wxWindow objects of the parent wxWindow have different origins within the wxWindow parent.

You can look at the code for wxText (best) and wxFrame for examples of creating areas for a wxWindow object in addition to the automatic client area.

So, wxWindows contain areas and pointers to areas. Some Areas contain pointers to wxWindows. Be aware that the order of destructors is somewhat confusing and problematical.

4. The Magic 32k Limit

Matthew Flatt provided the wxMac changes neccesary to implement wxMedia which handles the scrolling limits at the wxWindows level:

Up until wxMedia, wxCanvas always managed the scrollbars for you. If your drawing extended far enough, the scroll bars kicked in, and scroll bar actions were automatically excuted on the drawing area.

Unfortunately, this doesn't work well enough for editing text documents. The first reason is that scroll steps need to be correlated with lines of text. The second reaosn is that the size of a text document can easily exceed the short-integer-based drawing areas of many platforms.

SetScrollbars is supposed to configure properties of the scrollbar that are used for automatic scrolling. It is also the way to turn scrollbars on or off. So, we added the extra Bool flag to SetScrollbars to turn the scrollbars on without turning on automatic management.

The new methods - SetScrollPos, SetScrollRange, etc. - were added to give direct control over the scrollbars. Why wasn't SetScrollbars sufficient? I don't remember.

Finally, OnScroll was added to allow manual handling of scrollbar events.

5. The Apple Menu Hack.

In wx_frame.cc, the Mac menubar is cleared and recreated through the wxWindows menu objects. The wxWindows for Macintosh Apple Menu Extension (AKA Apple Menu hack) is to:

6. The About Menu Item hack.

This is a true hack. It is fragile. It has very specific requirements before it kicks in. If you do something close enought to trigger it, you must provide the code it calls.

In simple terms, this hack detects that your program creates a "Help" menu with a wxMenuItem of "About" and that you added this menu to the menubar with something like: menubar->Append(help_menu, "Help"); Yes, this is not inter-nationalized, just English.

When wxMenuBar::Append sees this, it saves away the wxMenu ptr and the Mac Item Number. These are used in the wxApp::doMacInMenuBar() [wx_app.cc] to trigger whatever callback function you set up to handle Help/About. I'm actually proud of this, but I know that it is weak. You can disable a couple of lines in wx_app.cc to remove its effects, although the additional memory lingers.

7. wxBitmap implementation

The internal Mac object for a wxBitmap is a Mac GWorld (and pixmap)"Picture". This makes for easy and quick execution during Update events and for handling offscreen bitmaps (wxDCMEM). It is not the clearest of code however. Several people have helped me debug this. I also depend on implementing parts of the wxImage class (to handle GIF, XPM, XBM, and BMP). USE_XPM_IN_MAC can be toggled off in wx_setup.h if you do not need some of these formats. Most of the code is in wx_gdi.cc and wxImage.cc.

8. Mac Resources.

In each of the sample directories, you should find a resource file to include into the project. There is an ALRT and DITL which are required for error messages. The 'MENU' resource is no longer used, but it doesn't hurt to keep it. There may be PICT resources which are used for bitmapped buttons. Anything else is left over from other tests and maybe deleted.

9. Memory Managment and PowerPC oddities

The memory manager on PowerMacs is a little more sensitive to some errors. I compile and link with Code Warriors DebugNew routines because it helps track down some of these errors.

If you really want to find leaks (and they are there), and if you have access to MPW's canon tool, you can replace calls to 'new' with calls to DebugNew's 'NEW', set the appropropriate #define and recompile. You should do this on a copy of the project, because your doing a global search and replace and they can be troublesome to undo. See the CodeWarrior Doc for DebugNew (probably on the CD).

There is a library project, wxMac161 PPC (debug) that can be used for leak testing. It uses a different .pch file, 'wxDebugHeaders.pch) which builds the library and one of two precompiled headers, either 'wxDebugHeadersPPC' or 'wxDebugHeaders68K'. Then in the project for the application set preferences/C++ Language/prefix file: to be wxDebugHeadersPPC (or 68K). 12 leaks is normal for minimal.cc and 25 leaks for hello.cc To help track drown other leaks I've included a file of patches to make to Metroworks DebugNew. See file DebugNew.patch and Hello.cc which has lots of code that trys force and or find leaks. Ugly but effective.

10. SetSize, OnSize - OnClientAreaDSize.

First off, OnSize() is only defined for wxFrame's and wxbWindow (in regular 16.3 its also defined for wb_item). Since release 'a4p3', you do not need a OnClientAreaDSize() in your application. Myframe::OnSize works fine. You app's OnSize will be called when wxApp::OnInit returns to the system. From then on it will only be called when the window is maximized/minimized, the growicon is dragged or your app calls SetSize() on the app's frame. In fact you do not want both OnSize() and OnClientAreaDSize() defined for your app's frames (if so they may both be called).

SetSize() for wxWindows and its subclasses is a little mysterious and has significant implications if you want to write or understand the controls (wxButtons, wxListBoxs ...) The only SetSize() you need is in wx_win.cc (for class wxWindow). wxItems (buttons, etc.) should not define this function!!! Instead a window or item needs to define an OnClientAreaDSize() method. It works like this - When an application like hello.cc calls SetSize(), the method in wxWindow is used (because no one overrides it). wxWindow::SetSize() ventures off into the wxArea's code and eventually the Window's or Item's OnClientAreaDSize() method is called. This is where the control (ie wxItem) needs to modify its position and size according to the semantics of wxMac and OnClientAreaDSize().

11. OnPaint and Paint - when to use

OnPaint is defined for wxbWindow, wxWindow and wxPreviewCanvas (sort of) and is overridden as needed by your applications classes for canvas's. OnPaint() is called when wxMac wants your app to redraw the contents (ex. in repsonse to the Mac's Update event).

Paint() is a method of many classes. By default (wb_win.h,) Paint() calls OnPaint() so for the application developer, Paint() is just a way to trigger OnPaint(). Starting with the 'a5' release, there should not be any methods named Paint() either inside wxMac or your app either. Inside wxmac the various wxItems' OnPaint() methods depend upon being able to call wx_win.cc/OnPaint() and they do so with an explicit scope operation.

12. wxDirectorySelector() - New

This function is called like wxFileSelector, but instead of selecting files it allows the user to select a Directory (not unlike CodeWarriors Preferences->Path dialog). This function is also called from wxFileSelector() is the proper flag values are passed in (by default they are not). If I remember this is in wx_utils.cc. The code is not mine - proper accredidation is in the source code. This function also requires a DLOG and appropriate DITL's resources which had better be there when this is called. See xpmshow for and example of how this is used.