WxMac Issues

From WxWiki

Jump to: navigation, search

See also the wxMac FAQ, and Getting Started on OS X

Contents

[edit] Adding Mac-specific features

Mac users want your program to behave like every other Mac application. There's nothing that Mac users hate more than applications which look like they were ported from Windows or Unix. Unfortunately wxWidgets on Mac OS X isn't quite there yet (as of 2.4.2), and many minor issues, from misplaced menu items to one-pixel layout errors to use of the wrong widget, make wxWidgets applications look like exactly that.

Since wxMac uses all native controls, your program will automatically take on the Aqua look-and-feel when you run it on Mac OS X (See http://wxwidgets.org/screen03.htm for an example). In theory, you shouldn't have any spacing issues at all as long as you use sizers. Never make any assumptions about spacing - whenever possible, let wxWidgets make all of the spacing decisions for you with its automatic layout tools. Hopefully spacing issues will be improved in future versions.

[edit] My app can't be brought to the front!

This is a common problem met by people new to wxMac. Simply building a wxWidgets app from terminal and producing a raw executable is not enough on wxMac. You need to build an application bundle (there is a section about this below). Another possible way to solve this quickly is to add the following lines of code to your app :

#include <ApplicationServices/ApplicationServices.h>
 
ProcessSerialNumber PSN;
GetCurrentProcess(&PSN);
TransformProcessType(&PSN,kProcessTransformToForegroundApplication);

[edit] The Mac OS menu bar

All versions of Mac OS have a single menu bar at the top of the screen. While pop-up menus and context-sensitive menus are supported and are appropriate under some circumstances, Mac windows never have their own menu bar.

wxMac handles this automatically by displaying the menu bar associated with each wxFrame at the top of the screen whenever that frame is in front. If your program has multiple frames and the user brings a different frame to the front, that frame's menu bar will be switched in.

Warning: be careful of modeless windows which do not have menu bars. (Modal dialogs are okay.) If a modeless window gets brought to the front and it doesn't have a menu bar, the last menu bar will stay at the top of the screen. When the user selects something from one of these menus, the selection might never get handled, since the active frame doesn't recognize the command. You have two choices: either handle your commands in your wxApp subclass (since the commands will eventually get sent there), or create a degenerate wxMenuBar for all of your frames (maybe with only a Quit item).

On the Mac, the name of the item which exits the program is traditionally called "Quit" instead of "Exit". wxMac handles this for you - just name the item "Exit" and wxMac will change it for you.

Keyboard shortcuts use the command-key (the cloverleaf or open-apple key on the keyboard) instead of the control key. wxMac lets you specify keyboard shortcuts MS Windows style, and they are automatically translated. So Open should be specified as "&Open\tCtrl+O", and on the Mac the accelerator will be removed and the Ctrl will be replaced with a cloverleaf in the menu.

The layout of Mac menus is slightly different, and here is where you will have to add a line of code to your program to accomodate it. On the Mac, the "About" menu item should go in a particular place (in the Apple menu on Mac OS 8/9, or in the program menu on Mac OS X). wxMac will automatically move it for you if you tell it the ID of your About item:

#ifdef __WXMAC__
wxApp::s_macAboutMenuItemId = AboutID;
#endif 

On Mac OS X, the Preferences item goes in a special place, too. To tell it the ID of your Preferences item:

#ifdef __WXMAC__
wxApp::s_macPreferencesMenuItemId = PreferencesID;
#endif 

Look at defs.h for other such items.

Remark: You might have to tell wxMac the name of your help menu as well, since it assumes that it's "&Help". For example add the following lines

#ifdef __WXMAC__
wxApp::s_macHelpMenuTitleName = "Help";
#endif 

if your help menu has the name "Help".

You can achieve similar effects by using standard widget IDs. Here is a list of the Mac IDs that might have to be tweaked if you don't use their standard names (from defs.h): wxID_ABOUT, wxID_EXIT, wxID_PREFERENCES, wxID_HELP.

See also the wxMac FAQ.

[edit] When to close the program

On all versions of the Mac OS, it is okay for a program to be open but to have no windows open at all. This is similar to how a MS Windows MDI program can have its MDI Parent Frame open with no documents open within it, but on the Mac it has no open frames at all. The only clue that the program is active is that the menu bar changes.

If your program is dialog-based or otherwise just has one main frame, don't worry about this. When the user closes the main program or dialog, your program can just exit.

On the other hand, if your program is document-based, a Mac user will be confused if he/she closes a frame and the whole program exits. The user may have been intending to close one document and then open another.

If you want to support this behavior, there is a standard wxApp call, SetExitOnFrameDelete(), which tells the program not to exit when its last top-level frame is closed. To setup the menubar to be displayed when no frames are open, there is a wxMac-only static function in wxMenuBar, MacSetCommonMenuBar().

Using the common menubar
bool myApp::OnInit() {
  wxApp::SetExitOnFrameDelete(false);
  wxMenuBar *menubar = new wxMenuBar;
  // add open, new, etc options to your menubar.
  wxMenuBar::MacSetCommonMenuBar(menubar);
}

Alternatively, you may employ a little trick: create an offscreen frame with a menu bar (give it top-left coordinates of [5000, 5000] or something like that, but don't hide it using wxFrame::Hide). Give the frame a style of wxFRAME_NO_TASKBAR, so that the frame does not appear under the "Window" menu. When all of your other frames are closed, this frame will be the frontmost, and its menu bar will appear. This menu bar should include items like Open, Quit, and maybe Preferences.

On OSX, creating a window of wxSize(0,0) and window style 0 also works to create an invisible window - however this does not work under Classic.

[edit] Icons

Long ago, you used to have to do many complicated things to give a program an icon on the Mac. Starting with Mac OS 8, any Mac file can have a "custom icon" which is stored in the file's resource fork. There are many 3rd-party utilities which let you modify a file's custom icon, and this will be more than adequate for 90% of wxMac programs. One good program is Iconographer.

The one exception is if your program needs to be able to create documents which are associated with your application. In other words, you have an application called WixWriter which creates documents of type "Wix". You want to make sure that ".wix" documents get an appropriate icon and that your application is opened whenever someone double-clicks on a ".wix" file.

To do this, you will need to either create an Info.plist file inside the application's bundle folder (if you are creating a bundle folder for your app), or give your application a BNDL resource and a signature (if you are creating a standalone CFM or Mach-O executable with a resource fork). The details of this are beyond the scope of this document, but you can find resources on the web (esp. in Apple's ADC pages) which describe everything you need to do. Also, see samples/docview which has .r files which you can largely copy.

[edit] Saving files with types and creators

Even if your application doesn't need to create its own custom documents, you may want to be able to get or set the Type and Creator of files. On the Mac, every file has a four-character Type code which specifies the type of the file independent of its three-character extension (which is not required), and also a four-character Creator code which matches the "signature" of the application that created it.

There are methods in wxFilename which allow you to get or set these codes:

bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator )
bool wxFileName::MacSetDefaultTypeAndCreator()
bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )

Note that types and creators are defined as wxUint32 instead of a string or character type because all Mac compilers allow you to specify types and creators as: 'TEXT', 'APPL', 'ttro', 'R*ch', etc. - where the four characters are packed into one 32-bit int.

If you want to set the type of a file (for example before distributing it), you can do something like this:

/Developer/Tools/Rez -t HTBD -o myfile.htb < /dev/null

This sets the wxWidgets HTML help file 'myfile.htb' to the appropriate type to invoke the HelpView application (see utils/helpview/src, CVS HEAD).

If you are creating an application bundle folder on Mac OSX, you can also list file types in your application's Info.plist. See Apple developer documentation for more information.

[edit] Opening files by double-clicking and/or drag-and-drop

In order to have your application handle files that are dropped on the application icon, and respond to doubleclicking on the documents that it created, override virtual void wxApp::MacOpenFile(const wxString &fileName)

This is handled automatically in wxMac-2.8.4 (osx)

[edit] Installing programs under Mac

Basically under Mac, users expect a simple copy install. I.e. you deliver them a disk image or a compressed folder, and they simply drag it to the place they like. You can of course also use installers:

http://www.mindvision.com/products.html#IVISE

or StuffIt InstallerMaker from http://www.aladdinsys.com

If you go OSX, you can use the free package manager:

http://developer.apple.com/techpubs/macosx/Essentials/SystemOverview/InstallIntegrate/_Installing_Application.html

One reason why you may not need an installer on the Mac is that file associations are effectively set up by the application itself, which sets the creator type for the documents it creates so that Finder knows to launch your application.

[edit] Getting Correct Icons for your application (no document icons)

If you don't have any special documents which your application is creating, then you don't need any of the information below, the easiest thing (assuming you already have your applications icon in another format, like .ico or .xpm) is to use Thorsten Lemke's GraphicConverter and convert it to Format 'Apple File Icon'). Eg if you have a file called mondrian.ico you will get a new file mondrian which already shows the icon. Add a .rsrc to that file, making it mondrian.rsrc. Before doing so, please change the Preferences/Save/Custom Icon settings to not adding a black border. If you are creating an icon for an application you will also want to uncheck the 'dog-ear' option, but leave it on for document icons. If you are having an .ico file, you can also use Iconographer (see above, section Icons).

Then drag that file onto your CodeWarrior Project Window (Files Tab). And set the following additional things in each of your target's settings dialogs: - under Target:TargetSettings change the Post-linker to 'Output Flags Post-Linker' - under Linker:Output Flags check the 'Has custom icon' box

After your next linkage you should have an application with the proper icon.

Another way to make Mac icon files is to use Iconographer, which can open a Windows .ico file and save it as a .rsrc file. If you have both 32x32 and 16x16 icons in this icon file (using, for example, IconEdit32 to create them) , then Iconographer will pull out both of these and save it in the .rsrc file.

Mac OSX developer tools also contain a tool to create a set of icons from source image files, which can then be converted and added to a standalone executable's resource fork, or included in the application bundle folder (depending on which way you choose to package your application).

Most Mac icon-editing tools save in binary .rsrc format, and it's best if you convert to a .r file as detailed below.

[edit] Converting .rsrc files into .r files

If you are copying your project files onto non Apple Volumes, or storing them into a cvs, you may prefer to convert the binary .rsrc files into textual representations (like the .rc file on msw). You can do this via the DeRez terminal tool. This tool is available under OSX via /Developer/Tools/DeRez , or if you are on MacOS Classic only via MPW. Type DeRez followed by the full pathname to your .rsrc file, followed by a redirect output >> to your target .r filename.

You can now commit this .r file to your cvs and add it to your CodeWarrior Project instead of the .rsrc file.

To DeRez your file using CodeWarrior, make sure the RSRC file mapping in your project settings has Rez as the compiler and .rsrc as the extension. Also create a mapping with the same settings but a lower-case rsrc type instead of RSRC, to be on the safe side. Using ResEdit and showing the Info for your icon, make sure the type is set to RSRC (and not, for example, Icon). Now add your .rsrc file and select the Project Disassemble menu item to get a text version of the file, which you can then save as a .r file and add back to your project. Then clear the .rsrc file from your project. Phew! Do a Google Groups search on "DeRez CodeWarrior" for more detail on this.

Julian Smart writes: the above is a bit overcomplicated, so here's my own recipe for getting icons working on MacOS X using CodeWarrior.

1) Prepare PNG images, with transparency, on a Windows machine or other system of your choice using (for example) Paint Shop Pro or Gimp. Prepare 16x16, 32x32, 64x64 and 128x128 PNGs with 16 or 256 colours (the 128x128 could and should have an alpha channel but so far I've just been working with simple transparency). Copy these to the Mac.

2) On MacOS X, create a new icon in Icon Composer (see the Developer folder for this utility). In Finder, open each PNG in turn, copy the image to the clipboard, then select the relevant icon outline in Icon Composer, and paste the icon into it, choosing to create the mask automatically when prompted. Do not attempt to import the PNG from a file since this can crash Icon Composer. Now save the icns file.

3) Launch Iconographer, a shareware icon editor, and open the icns file you previous saved. For the missing icons listed in various bit depths, copy one of the others of the same dimensions and paste into the blanks. Note that pasting from the Finder doesn't seem to preserve masks in Iconographer (or at least in the version I have), hence the use of Icon Composer as a first step. Save as a Mac OS Universal (Resources) file when you're done.

4) Now run DeRez on the file, e.g. /Developer/Tools/DeRez myicons > myicons.r.

5) Open myicons.r in a text editor, and do a global substitution changing the negative number specified in each icon resource to e.g. 128 as specified in your bundle .r file (see samples/docview/docview.r for an example bundle). If you don't have seperate icons for application and document, you can make a copy of the icons in myicons.r and change e.g. 128 to 129.

6) Add myicons.r to your project.

[edit] Getting Correct Icons for your application & documents

For app bundles : You will need to set the information correctly in the info.plist file inside your app bundle. Easiest way is probably to open the info.plist file of some small app that does file associatons, it's XML and rather easy to figure out.

legacy information : Please follow the steps below in order to create proper icons also for your application's documents, make double clicking your documents open your app etc.

You must first get a unique identifier for your application, a so called creator, a four letter constant. All you need to know about this step is at http://developer.apple.com/dev/cftype/

In the end you will have a creator code, this creator must now be entered in your CodeWarrior Target Settings Dialog under PPC Target in the field Creator (bearing a wildcard ???? if not defined)

TBD

[edit] Building a MacOSX application bundle

MacOSX introduces a new way of putting together an application. Instead of adding a resource fork to the executable file, you can simply create a special directory (folder). This is the preferred method for OSX.

In it's most basic form, on OSX application bundle is a set of nested folders, with your executable in the "MacOS" folder:

  • YourApp.app
    • Contents
      • MacOS
        • YourApp (this is your executable file)
      • Resources
        • ...

Instead of being embeded in a resource fork, your application's resources can be placed as seperate files in "Contents/Resources". Additional metadata about your application can be contained in "Contents/Info.plist" and "Contents/version.plist". Locale/Language-specific data is contained in "Contents/Resources/<Language>.lproj". Dependent libraries and frameworks (e.g. wxWidgets) can also be contained in the bundle folders.

For more information on the file formats and layout of an application bundle folder, consult the Apple developer website. For example: http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFBundles/CFBundles.html

If you are using XCode or ProjectBuilder, those tools will create an application bundle for you with metadata plist files based on values you enter in the project settings. See the wxWidgets example programs for a method to create an application bundle folrder from makefiles. A basic bundle folder can be created from "make" with the following rule:

YourApp.app: Info.plist YourApp version.plist InfoPlist.strings YourAppMacIcons.icns AnotherResource.txt  
   -mkdir YourApp.app    
   -mkdir YourApp.app/Contents
   -mkdir YourApp.app/Contents/MacOS
   -mkdir YourApp.app/Contents/Resources
   -mkdir YourApp.app/Contents/Resources/English.lproj
   cp Info.plist YourApp.app/Contents/
   cp version.plist YourApp.app/Contents/
   cp InfoPlist.strings YourApp.app/Contents/Resources/English.lproj/
   echo -n 'APPL????' > YourApp.app/Contents/PkgInfo
   cp YourApp YourApp.app/Contents/MacOS/YourApp
   cp YourAppMacIcons.icns AnotherResource.txt YourApp.app/Contents/Resources/

Then you can run "make YourApp.app", or you can add "YourApp.app" as a dependency of "all". In my applications, I use autoconf, and generate Info.plist, version.plist, etc. from templates using configure.

[edit] Universal Binaries

As of wxWidgets 2.6.3, there is support for building "universal binaries" (i.e., binaries that will run on both PPC and Intel Macs). There are two approaches:

  • Pass --enable-universal_binary to configure when building wxWidgets. This will easily create a library that will work with Tiger (Mac OS X 10.4) and the latest Panther release (Mac OS X 10.3.9).
  • If you use something older : build the library and your application once for PPC and once for Intel, and lipo them together:
   % lipo -create hello-intel hello-ppc -output hello              

More details on the lipo technique can be found near the bottom of this mailing list message

[edit] Fink Caveats

  • If you have Fink installed, make sure there are no CFLAGS or LDFLAGS set in your .profile. Why? Fink doesn't build Universal binaries. The wxWidgets configure script will attempt to use the Fink versions of certain libraries (in particular libiconv) instead of the ones shipped with the Apple Developer Tools, resulting in link errors when the libraries are built. Unsetting the CFLAGS and LDFLAGS lines in your .profile and reopening Terminal/xterm should work. Note that just running Fink's init.sh won't interfere with wxWidgets; these are lines that have to be added to your .profile manually during the Fink setup process.

[edit] OS X 10.5 Leopard issues

If you get errors like:

ld: in /Developer/SDKs/MacOSX10.4u.sdk/usr/local/lib/libPng.dylib, file is not of required architecture for architecture ppc

You might need to add this to your configure line: --with-macosx-sdk=/Developer/SDKs/MacOSX10.5.sdk --with-macosx-version-min=10.4

Someone should verify if this can work if used with a lower min version than 10.4.

[edit] Legacy ways of compiling for Mac OS

  1. A Classic application will run on Mac OS 8.6 - 9.2. It must be compiled

using MetroWerks CodeWarrior 5.2 or later. These applications will only run on Mac OS X in "classic" mode. There is very little reason to compile one of these applications anymore unless for some reason your application does not run properly as a Carbon app (there are a few Classic functions which are not implemented in Carbon).

  1. A CFM Carbon application will run on both Mac OS 8 or 9 (with CarbonLib

installed) and natively on Mac OS X. CFM stands for "Code Fragment Manager", and it refers to the binary format of the executable file which is understood by all versions of the Mac OS. This application must be compiled using CodeWarrior also; Apple's Developer Tools does not compile CFM applications.

  1. A stand-alone Mach-O Carbon executable will run only on Mac OS X. It can be

compiled either using CodeWarrior (7.0 or later) or with Apple's Developer Tools, which are based on gcc.

Please note that all these methods are deprecated and the correct way to build wxWidgets apps on OS X is Mac-O binaries in app bundles.

Personal tools