/* Contextual Menu Workshop version 1.5 June 23, 2003 Copyright (C) 2002-2003 Abracode Inc http://free.abracode.com/cmworkshop/ */ ===================== WHAT'S NEW IN 1.5 ======================= Addition of Cocoa text selection code thanks to Jayson Adams. Look Common/CMHelper/ Moved OnMyCommandCM project to Project Builder to take advantage of new Cocoa text selection code Updated source code for freeware projects Changed absolute paths in some sample Project Builder projects to relative ones ===================== WHAT'S NEW IN 1.4 ======================= Code improvements and changes in many places. ProjectBuilder project for FileSampleCM Added instructions for building a new CM with ProjectBuilder (look below) OnMyCommandCM plugin is in quite mature form. QuickAccessSetup application with sources added: an ObjC++ project with Cocoa interface and some C++ classes reused from QuickAccessCM plug-in Started using some of the ACCELA open source Mac OS Toolbox C++ wrappers by David Catmull More info at: http://sourceforge.net/projects/accela ===================== WHAT'S NEW IN 1.3.1 ======================= Started OnMyCommandCM plugin. Fixed installer problem on Mac OS 10.2. Fixed a memory leak in FileUtilsCM module "Copy Path to Clipboard" GrimRipperCM in 3 flavors. ===================== WHAT'S NEW IN 1.3 ======================= A new project added: QuickAccessCM. Simple launcher and move/copy. GrimRipperCM menu now shows only when a file does have a resource fork. Revised and cleaned some code. Added MoreFiles & MoreFilesX to source distribution. ===================== WHAT'S NEW IN 1.2 ======================= A new project added: GrimRipperCM. Rips off resource forks. Added FileInfo submenu to FileUtilsCM plugin. AppleScript installer takes advantage of OS X-only "path to" command. ===================== ABSTRACT ======================= "Contextual Menu Workshop" is a mini environment for creating Contextual Menu plugins for Mac OS X. The idea is to simplify the creation of new CM plugins by providing a code skeleton and using C++ techniques without really using C++ for base class. ===================== LITERATURE ======================= If you want to understand the technology of CoreFoundation plugins, read: file://localhost/Developer/Documentation/CoreFoundation/PluginServices/pluginservices_carbon.html or http://developer.apple.com/techpubs/macosx/CoreFoundation/PluginServices/pluginservices_carbon.html ======================= TOOLS ======================== CodeWarrior This code has been compiled with CodeWarrior Pro 7.2 and Universal Interfaces 3.4.1. All projects compiled with CodeWarrior are CFM (I am an old school guy ;-)) but I guess it should be easy to compile them as Mach-O. ProjectBuilder A sample project for this environment is provided for EmptyCM and FileSampleCM. Also some helper applications written in Cocoa are compiled with ProjectBuilder. ================= WORKSHOP CONTENTS =================== "CFAbstractCMPlugin.cp" I wrote CFAbstractCMPlugin.cp by modifying CFPluginMenu.cp from Apple sample code. This file contains the base frame for CM plugin, taking care of COM interface and communication with a host (this is the "base class" in C++ terminology). This code will rarely need to be changed and the implementation of a concrete plugin can be done in separate file. "CFAbstractCMPlugin.h" Header file for abstract plugin. The structure "AbstractCMPluginType" is defined here (this structure forms the member data of our pseudo class). "AbstractCMPluginType" contains "CMPluginImplData" field which can be defined in "CMPluginImpl.h". The structure data is allocated by abstract plugin and is passed to plugin implementation in "thisInstance" argument (look functions below). Abstract plugin is responsible for releasing the data on "destruction". This file also provides the prototypes for the following methods which must be implemented by plugin ("pure virtual functions" in C++ terminology). Abstract plugin does not provide implementation of these methods. OSStatus CMPluginExamineContext( void *thisInstance, const AEDesc *inContext, AEDescList *outCommandPairs ); OSStatus CMPluginHandleSelection( void *thisInstance, AEDesc *inContext, SInt32 inCommandID ); void CMPluginPostMenuCleanup( void *thisInstance ); "CMPluginImpl.h" Concrete plugin implementation has to provide a file named "CMPluginImpl.h". This file is included by CFAbstractCMPlugin.h and individual constants and defines are set here. These defines are used later by abstract plugin. This is kind of workaround for lack of inheritance mechanisms of plain C. kCMImplPluginFactoryID Unique kCMImplPluginFactoryID can be easily generated with "Generate UUID" application provided in Workshop. CM_IMPL_PLUGIN_BUNDLE_INDENTIFIER CM_IMPL_PLUGIN_BUNDLE_INDENTIFIER is used by abstract plugin to obtain bundle reference which is stored in bundleRef field of AbstractCMPluginType structure. Bundle reference may be used by a plugin to access its resources. CMPluginImplData The structure CMPluginImplData is included in "parent" structure of AbstractCMPluginType which is allocated by abstract plugin. CMPluginImplData can be later used by implementation plugin ("class member data" in C++ terminology). "CMUtils.cp" The framework provides CMUtils - a class of static utility functions for CMs. Most functions here are file related and most come in two flavors: FSSpec & FSRef. If you are porting a plug-in from Mac OS 9 it is a good idea to start with FSSpec version and make sure everything works fine. If you want to take full advantage of Mac OS X it is better to use FSRef versions. This class will hopefully grow with new plugins. "EmptyCM" EmptyCM plugin project is provided as a starting point for new plug-ins. "FileSampleCM" Sample CM dealing with files or folders. Another starting point for new plug-ins. "Preferences" Helper application written in AppleScript Studio to handle preferences of CMs. A CM may call this app to modify and save its prefs. Usage demonstrated in InternetLocationCM and FileUtilsCM. "Installer" A simple AppleScript CM installer has been written to ease the installation task for end user. ================= 17 EASY STEPS TO CREATE A NEW PLUG-IN WITH CW PRO =================== Assume we want to create a new plugin named "MyPluginCM". 1. Copy "EmptyCM" project folder and rename it to "MyPluginCM". 2. Open "EmptyCM.prj" and export project to XML. 3. Open newly generated XML project and replace all "EmptyCM" occurances with "MyPluginCM" 4. Rename "EmptyCM.cp" to "MyPluginCM.cp" 5. Rename "EmptyCM.plugin" to "MyPluginCM.plugin" 6. Rename "Install EmptyCM" to "Install MyPluginCM" 7. Run "Generate UUID" application and leave its window open 8. Open "MyPluginCM.plugin" package, clean it and and edit "Info.plist" changing required data (There is a bug in Finder that after a copy sometimes you might not be able to open "Contents" folder because it is marked as "busy". Very irritating. Logout and login back to open it). 9. Replace "FEEDBABE-054E-11D6-B5DB-00050289EC9F" in "Info.plist" (2 occurances) with ID generated by "Generate UUID" app. 10. Edit "CMPluginImpl.h" and replace kCMImplPluginFactoryID constant with ID generated. 11. Change CM_IMPL_PLUGIN_BUNDLE_INDENTIFIER to match your bundle identifier set in "Info.plist". 12. Import XML project created in step 2 and save as "MyPluginCM.prj" in "MyPluginCM" folder. 13. Open the project, compile and link. 14. Open "Install MyPluginCM" file with ScriptEditor and change: property objectName : "EmptyCM.plugin" to property objectName : "MyPluginCM.plugin" 15. Clean your project folder to get rid of the remainings from "Empty CM" 16. Write your code in "MyPluginCM.cp" 17. There is no step 17 :-) ================= 17 EASY STEPS TO CREATE A NEW PLUG-IN WITH PROJECT BUILDER =================== Assume we want to create a new plugin named "MyPluginCM". 1. Copy "EmptyCM" project folder and rename it to "MyPluginCM". 2. Rename "EmptyCM.cp" to "MyPluginCM.cp" 3. Rename "Install EmptyCM" to "Install MyPluginCM" 4. Rename "EmptyCM.pbproj" to "MyPluginCM.pbproj" 5. Ctrl-click on MyPluginCM.pbproj and choose "Show Package Contents" 6. Open "project.pbxproj" inside "MyPluginCM.pbproj" with some text editor 7. Find all occurances of "EmptyCM" string and replace with "MyPluginCM" string 8. Scroll to the place where plist content is and edit CFBundleIdentifier 9. Run "Generate UUID" application and leave its window open, go back to the edited "project.pbxproj" and replace "FEEDBABE-054E-11D6-B5DB-00050289EC9F" there (2 occurances) with ID generated by "Generate UUID" app. Now you may close the project file. 10. Edit InfoPlist.strings in "English.lproj" folder. 11. Edit "CMPluginImpl.h" and replace kCMImplPluginFactoryID constant with ID generated by "Generate UUID". 12. Change CM_IMPL_PLUGIN_BUNDLE_INDENTIFIER to match your bundle identifier set in plist text (in step 8). 13. Open the project, compile and link - if you are lucky, you should have no errors. 14. Open "Install MyPluginCM" file with ScriptEditor and change: property objectName : "EmptyCM.plugin" to property objectName : "MyPluginCM.plugin" 15. Clean your project folder to get rid of the remainings from "Empty CM" 16. Write your code in "MyPluginCM.cp" 17. There is no step 17 :-) ======================= Mac OS 10.2 Jaguar notes ======================== Directly from: "Mac OS X 10.2 Developer Release Notes: High Level Toolbox" http://developer.apple.com/techpubs/macosx/ReleaseNotes/HIToolbox.html Contextual Menu Manager Features and Enhancements The Contextual Menu Manager now supports two new AE keywords for CMM plugin commands, keyContextualMenuAttributes and keyContextualMenuModifiers. These keywords allow specification of the menu item attributes and menu item modifiers for a menu item in a contextual menu. Using the attribute and modifier parameters, a CMM plugin may now: prevent menu item command text that starts with a dash from being interpreted as a separator (by specifing the kMenuItemAttrIgnoreMeta menu item attribute); create dynamic menu items that change according to the modifier keys pressed by the user (by specifying appropriate modifier key combinations). Support for the Attributes and Modifiers keys in CMM plugin AERecords is indicated by the gestaltContextualMenuHasAttributeAndModifierKeys bit in the gestaltContextualMenuAttr selector. The Contextual Menu Manager now supports more data types for the keyAEName parameter in a CM plugin command. Previously, the CMM supported typeChar and typeIntlText. Now, the CMM also supports typeStyledText, typeAEText, typeUnicodeText, and typeCFStringRef. Support for these additional types is indicated by the presence of the gestaltContextualMenuHasUnicodeSupport bit in the gestaltContextualMenuAttr result. Since Mac OS X 10.0, the Contextual Menu Manager has supported a new value, kCMHelpItemRemoveHelp, for the inHelpType parameter to ContextualMenuSelect. This value causes the Help item to be removed from the contextual menu entirely. The symbolic constant for this value is now included in Menus.h. Added the keyContextualMenuCommandID and keyContextualMenuSubmenu constants from the Contextual Menu SDK to Menus.h. Implementation Changes In general, the Contextual Menu Manager implementation has been cleaned up and modernized; it now internally stores menu item text with CFStringRefs instead of Str255s, and uses modern Menu Manager APIs.