Objective Toolkit User`s Guide
Transcription
Objective Toolkit User`s Guide
Objective Toolkit User’s Guide Stingray® Studio Version 12.0.1 OBJECTIVE TOOLKIT USER’S GUIDE PRODUCT TEAM Development: Terry Crook, Clayton Dean, Boris Meltreger, David Noi Documentation: Marc Betz, Shelley Hoose Development Manager: Clayton Dean Product Manager: Ben Gomez Support: Terry Crook, Boris Meltreger THIS MANUAL © Copyright 1997-2012 Rogue Wave Software, Inc. All Rights Reserved. Rogue Wave and Stingray are registered trademarks of Rogue Wave Software, Inc. in the United States and other countries. All other trademarks are the property of their respective owners. ACKNOWLEDGMENTS This documentation, and the information contained herein (the "Documentation"), contains proprietary information of Rogue Wave Software, Inc. Any reproduction, disclosure, modification, creation of derivative works from, license, sale, or other transfer of the Documentation without the express written consent of Rogue Wave Software, Inc., is strictly prohibited. The Documentation may contain technical inaccuracies or typographical errors. Use of the Documentation and implementation of any of its processes or techniques are the sole responsibility of the client, and Rogue Wave Software, Inc., assumes no responsibility and will not be liable for any errors, omissions, damage, or loss that might result from any use or misuse of the Documentation ROGUE WAVE SOFTWARE, INC., MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THE DOCUMENTATION. THE DOCUMENTATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. ROGUE WAVE SOFTWARE, INC., HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS WITH REGARD TO THE DOCUMENTATION, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL ROGUE WAVE SOFTWARE, INC., BE LIABLE, WHETHER IN CONTRACT, TORT, OR OTHERWISE, FOR ANY SPECIAL, CONSEQUENTIAL, INDIRECT, PUNITIVE, OR EXEMPLARY DAMAGES IN CONNECTION WITH THE USE OF THE DOCUMENTATION. The Documentation is subject to change at any time without notice. ROGUE WAVE SOFTWARE, INC. Address: 5500 Flatiron Parkway, Boulder, CO 80301 USA Product Information: Fax: Web: (303) 473-9118 (800) 487-3217 (303) 473-9137 http://www.roguewave.com CONTENTS 1Chapter 1 Introduction to Objective Toolkit 1.1 Welcome to Objective Toolkit 1 1.2 Product Features 2 1.2.1 MFC Extension Classes 2 1.2.2 Full Source Code 3 1.2.3 Compatibility and Build Options 3 1.3 Location of Samples 4 1.4 Supported Platforms 4 1.5 Getting Help 4 1.5.1 Documentation 4 1.5.2 Knowledge Base 5 1.5.3 Professional Services 5 1.5.4 Technical Support 5 1.6 Licensing Restrictions 5 2Chapter 2 Objective Toolkit Quick Start 2.1 Overview 7 2.2 Installation Notes 7 2.3 Building Objective Toolkit 9 2.3.1 Using the Build Wizard 9 2.3.1.1 To build a custom configuration version of the library 10 2.3.2 DLL naming issues 10 2.3.3 Compiler Flags 10 2.3.4 Build Target Naming Conventions 11 2.3.5 Build Configuration Options 12 2.3.6 To build the 32-bit or 64-bit libraries 12 2.3.7 Miscellaneous Build Issues 13 2.3.7.1 Using Multiple Library Configurations 13 2.3.7.2 Make Files and Building Directly with nmake 13 Contents iii 2.3.7.3 Components Requiring RTTI Support 13 2.3.8 Building the Samples 14 2.3.9 Automatic linking 14 2.3.10 To incorporate Objective Toolkit into your application 15 2.4 Distributing Objective Toolkit Applications 17 2.5 Basic Tutorial—MaskEdit Control 18 2.6 Using Component Headers to Increase Application Build Performance 22 2.6.1 The Component Headers 22 2.6.2 To use the component headers in your project 24 3Chapter 3 The Objective Toolkit AppWizard 3.1 Overview 27 3.2 Creating a Skeleton Application 29 4Chapter 4 Simple Controls 4.1 Overview 35 4.2 Browse Edit Controls 35 4.2.1 Browse Edit Classes 36 4.2.1.1 SECBrowseEditBase 36 4.2.1.2 SECBrowseFileEdit 36 4.2.1.3 SECBrowseDirEdit 36 4.2.2 Using the Browse Edit Classes 37 4.2.2.1 To incorporate the SECBrowseEdit classes into your code 37 4.2.3 Customizing the Browse Edit Control 38 4.3 Browse Edit Sample 38 4.4 Button Controls 39 4.5 Button Class Hierarchy 40 4.5.1 SECOwnerDrawButton 40 4.5.2 Using SECOwnerDrawButton 40 4.5.2.1 To attach an SECOwnerDrawButton to a button resource in a dialog 40 4.5.2.2 To create SECOwnerDrawButtons without using a button dialog resource 41 4.5.3 Customizing SECOwnerDrawButton 41 4.5.3.1 To extend the SECOwnerDrawButton class 41 4.5.4 SECBitmapButton 41 4.5.5 Using SECBitmapButton 41 4.5.5.1 To attach an SECBitmap Button to a dialog resource 41 4.5.5.2 To create SECBitmapButtons without using a button dialog resource 42 4.5.6 SECBitmapButton alignment 42 4.5.7 SECMenuButton 42 iv Contents 4.5.8 Using SECMenuButton 42 4.5.8.1 To attach an SECMenuButton to a dialog resource 43 4.5.8.2 To create SECMenuButtons without using a button dialog resource 43 4.5.9 SECMenuButton Menu Placement 43 4.5.10 SECWellButton 43 4.5.11 Using SECWellButton 44 4.5.11.1 To attach an SECWellButton to a dialog resource 44 4.5.11.2 To create SECWellButtons without using a button dialog resource 44 4.5.12 Customizing SECWellButton 44 4.6 Calculator Control 45 4.6.1 SECCalculator 45 4.6.2 SECPopupCalculator 46 4.6.3 Using SECCalculator 46 4.6.4 Customizing SECCalculator 46 4.6.5 Calculator Sample 47 4.7 Calendar Control 48 4.7.1 To incorporate the SECCalendar class into your code 48 4.7.2 SECCalendar Key Methods 49 4.7.3 Customizing SECCalendar 50 4.7.4 SECCalendar Sample 50 4.8 Color Well Control 51 4.8.1 SECColorWell 52 4.8.1.1 Using SECColorWell 52 4.8.1.2 Customizing SECColorWell 53 4.8.2 SECPopupColorWell 53 4.8.2.1 To incorporate SECPopupColorWell into your code 53 4.8.3 ColorWell Sample 54 4.9 Currency Edit Control 55 4.9.1 SECDropEdit 55 4.9.2 SECCurrencyEdit 55 4.9.3 Using SECCurrencyEdit 55 4.9.4 SECCurrencyEdit::Format 56 4.9.5 SECCurrencyEdit Messages 57 4.9.6 SECCurrencyEdit Sample 58 4.10 Date/Time Edit Control 59 4.10.1 SECDateTimeCtrl 60 4.10.1.1 SECDateTimeCtrl styles 60 4.10.1.2 SECDateTimeCtrl messages 60 4.10.2 SECDTGadget 60 4.10.3 Date Formats 61 4.10.3.1 Predefined Format Types 61 4.10.3.2 Format Strings 61 4.10.4 Null Data Entry Mode 62 4.10.5 Using SECDateTimeCtrl 63 4.10.6 Date/Time Edit Control Sample 65 Contents v 4.11 List Box Edit Control 66 4.11.1 SECListBoxEditor 67 4.11.2 SECListBoxFileEditor 67 4.11.3 SECListBoxDirEditor 67 4.11.4 Using the List Box Edit Classes 68 4.11.5 Customizing the List Box Edit Classes 68 4.11.6 Extending the Editable List Box Classes 69 4.11.7 Editable List Box Sample 70 4.12 Marquee Control 71 4.12.1 Using SECMarquee 71 4.12.2 Customizing SECMarquee 72 4.12.3 Marquee Sample 73 4.13 Masked Edit Control 74 4.13.1 SECMaskEdit 74 4.13.2 Using SECMaskEdit 74 4.13.3 Creating a Mask to Use with SECMaskEdit 75 4.13.4 Mask Edit Sample 76 4.14 Extended Progress Control 77 4.14.1 Using SECProgressCtrl 77 4.14.2 Customizing SECProgressCtrl 78 4.14.3 Extending SECProgressCtrl 78 4.15 Enhanced ComboBox with AutoComplete 79 4.15.1 The Enhanced ComboBox Class 79 4.15.2 Using SECComboBoxEx 79 5Chapter 5 Look and Feel Styles 5.1 Overview 81 5.2 Microsoft Vista Classic Style 82 5.3 Visual Studio .NET/Office XP Style 83 5.3.1 Enabling .NET/Office XP Styles 84 5.4 Microsoft Office 2003 Style 85 5.4.1 Enabling Office 2003 Styles 90 6Chapter 6 Customizable Toolbars 6.1 Overview 91 6.2 The Customizable Toolbar Classes 93 6.2.1 SECCustomToolBar 93 vi Contents 6.2.2 SECToolBarManager 94 6.2.3 SECToolBarsBase 94 6.2.4 SECToolBarsDlg 94 6.2.5 SECNewToolBar 95 6.2.6 SECToolBarsPage 95 6.2.7 SECToolBarCmdPage 95 6.2.8 SECToolBarSheet 95 6.3 The Toolbar Button Classes 96 6.3.1 SECStdBtn 96 6.3.2 SECStdMenuBtn 97 6.3.3 SECTwoPartBtn 97 6.3.4 SECTBTextBtn 97 6.3.5 SECWndBtn 97 6.3.6 SECComboBtn 98 6.4 Comparing SECToolbar to SECCustomToolBar 98 6.5 Toolbar Button Map 99 6.5.1 STD_BUTTON 99 6.5.2 STD_MENU_BUTTON 100 6.5.3 TEXT_BUTTON 100 6.5.4 TEXT_BUTTON_EX 101 6.5.5 TWOPART_BUTTON 101 6.5.6 COMBO_BUTTON 101 6.6 Toolbar Button Styles 102 6.6.1 Button style macros 102 6.6.2 Button State Macros 102 6.7 Creating New Button Types 103 6.8 Customization Dialogs 104 6.8.1 Creating SECCustomToolBars—Arguments and Cautions 105 6.8.2 The Effect of Modifying Toolbars on Persistence 105 6.9 Using the Customizable Toolbar Classes 106 6.9.1 To incorporate customizable toolbars into your application 106 6.9.2 To implement toolbars with the flat cool look with a toolbar manager 107 6.9.3 To implement toolbars with the flat cool look without a toolbar manager 108 6.9.4 To implement button groups 108 6.9.5 To use multiple toolbar bitmap resources with the toolbar manager 109 6.9.6 To find a button on a customizable toolbar 109 6.9.7 To use the button map 109 6.9.8 To implement a text button 110 6.9.9 To implement a text button with styles 110 6.9.10 To implement a combo button 111 6.9.11 To implement a twopart button 111 6.9.12 To implement a bitmap button using styles 111 Contents vii 6.9.13 To make a customizable toolbar dockable 112 6.9.14 To reposition customizable toolbars at run time 112 6.9.15 To obtain a pointer to a specific customizable toolbar 112 6.9.16 To iterate the customizable toolbars 112 6.9.17 To implement the toolbar customization dialog 113 6.9.18 To invoke the toolbar customization dialog with a toolbar button 114 6.9.19 To hide and show customizable toolbars 114 6.9.20 To set the docking order of customizable toolbars 114 6.9.21 To changing the font for text buttons 114 6.9.22 To save and restore customizable toolbars 115 6.9.23 To draw owner-draw controls embedded in a vertically docked toolbar 115 7Chapter 7 Menu Bars 7.1 Overview 117 7.2 Menu Bar Classes 119 7.2.1 SECMenuBar 119 7.2.2 SECMDIMenuBar 119 7.3 Customizing the Display of Menu Pop-ups 120 7.4 Menu Button Map Macros 121 7.5 WM_EXTENDCONTEXTMENU 122 7.6 Using the Menu Bar Classes 123 7.6.1 To Incorporate Objective Toolkit Menubars Into Your Code—Simple Case 123 7.6.2 To Incorporate Objective Toolkit Menubars Into Your Code--Advanced Case 124 7.6.3 To Implement SECMenuBar Or SECMDIMenuBar Without a Toolbar Manager 127 7.6.4 To remove the close button from a floating menu 128 7.6.5 To switch between menus 128 7.6.6 To use bitmap menus without the cool-look toolbars 129 7.6.7 To use bitmap menus in context menus 130 7.7 MenuBar Sample 132 8Chapter 8 Docking Windows 8.1 Overview 133 8.2 Features of Docking Windows 134 8.3 Flat-Style Drawing 136 8.4 Auto-Hide Docking Windows 137 viii Contents 8.4.1 Features 137 8.4.1.1 Vertical Frame Docking 137 8.4.1.2 Auto-Hide Pins 138 8.4.1.3 Vertical/Horizontal Text 138 8.4.1.4 Floating Grippers 138 8.4.1.5 Icons 138 8.4.1.6 Active Windows 138 8.4.2 Creating Auto-Hide Docking Windows 139 8.5 The Docking Window Classes 140 8.6 Docking Window Frame Classes 141 8.6.1 SECFrameWnd 141 8.6.2 SECMDIFrameWnd 141 8.6.3 SECMDIChildWnd 141 8.7 Docking Window Control Bar Classes 142 8.7.1 SECControlBar 142 8.7.2 SECDialogBar 142 8.7.3 SECControlBarManager 142 8.7.4 SECDockState 142 8.8 Message Routing Issues 143 8.9 Extended ControlBar Styles 144 8.10 Embedding CViews in Docking Windows 146 8.11 Using the Docking Window Architecture 147 8.11.1 To create an application with Objective Toolkit docking windows 147 8.11.2 To incorporate Objective Toolkit docking windows into an existing MDI application 147 8.11.3 To incorporate Objective Toolkit docking windows into an existing SDI application 148 8.11.4 To use Objective Toolkit docking windows inside an OLE IP server 148 8.11.5 To create a docking window based on a dialog resource 150 8.11.6 To create a docking window not based on a dialog resource 151 8.11.7 To set the style of a docking window 151 8.11.8 To make a docking window dockable 151 8.11.9 To create a non-dockable control bar 152 8.11.10 To dock a docking window that is floating 152 8.11.11 To float a docking window that is docked 153 8.11.12 To make an SECDialogBar size diagonally when floated 153 8.11.13 To receive notifications when the docked state of a docking window changes 153 8.11.14 To hide a docking window 154 8.11.15 To control the docking location of a docking window 154 8.11.16 To determine where a docking window is docked 154 8.11.17 To determine the row and column of a docked window 155 8.11.18 To modify a control bar’s context menu 155 Contents ix 8.11.19 To add a toolbar to a control bar 156 8.11.20 To access controls in the docking window inside a message handler 156 8.11.21 To route messages to the client area of SECControlBar 156 8.12 Customizing Objective Toolkit Docking Windows 157 8.12.1 Key Extended Control Bar Members 157 8.13 Docking Windows Sample 157 9Chapter 9 Image Classes 9.1 Overview 159 9.2 The Image Classes 160 9.2.1 SECImage 160 9.2.2 SECDib 161 9.2.3 SECGif 161 9.2.4 SECJpeg 161 9.2.5 SECPcx 161 9.2.6 SECTarga 161 9.2.7 SECTiff 161 9.3 SECImage Format 162 9.4 Using the Image Classes 163 9.4.1 To read an image from a file 163 9.4.2 To view GIF/TIFF images 163 9.4.3 To display an image 163 9.4.4 To convert an image 164 9.4.5 To copy an image 165 9.4.6 To manipulate an image 165 9.4.7 To write an image to a file 166 9.4.8 To convert to a CBitmap object 166 9.4.9 To convert from a CBitmap object 166 9.4.10 To create from a CDC object 167 9.4.11 To load an image from a resource 167 9.4.12 To stream image data 168 9.5 Key Image Methods 170 9.6 Image Sample 170 10Chapter 10 MDI Alternatives 10.1 Overview 171 10.2 Benefits of MDI Alternatives 172 10.2.1 Multiple Top-level Interface (MTI) 172 x Contents 10.2.2 MTI Class – SECToplevelFrame 174 10.2.2.1 Using MTI 175 10.2.2.2 To convert an existing SDI application to MTI 175 10.2.2.3 To convert an existing MDI application to MTI 175 10.2.2.4 To create a new MTI-based application 175 10.2.2.5 Customizing MTI 175 10.2.2.6 To load the document into the initial, empty frame 176 10.2.2.7 Message Dispatching in an MTI Application 176 10.2.2.8 Other Uses For SECToplevelFrame 176 10.2.2.9 MTI Sample 177 10.2.3 Floating Document Interface (FDI) 177 10.2.3.1 Differences between FDI and MTI 178 10.2.3.2 FDI Classes 178 10.2.3.3 SECFDIChildWnd 178 10.2.3.4 SECFDIFrameWnd 178 10.2.3.5 Using FDI 178 10.2.3.6 To convert an existing SDI application to FDI 178 10.2.3.7 To convert an existing MDI application to FDI 179 10.2.3.8 To create a new FDI-based application 179 10.2.3.9 FDI Sample 179 10.2.4 Workbook Document Interface (WDI) 179 10.2.4.1 Adding Flat Style Drawing to the Workbook Window 180 10.2.4.2 Adding Flat Style Drawing to the Shortcut Window 181 10.2.4.3 Adding Flat Style Drawing to the 3D Tab Control Window 181 10.2.4.4 WDI Classes 182 10.2.4.5 SECWorkbookWnd 182 10.2.4.6 SECWorksheetWnd 183 10.2.4.7 SECWorkbookClientWnd 183 10.2.4.8 To convert an existing MDI application to WDI 183 10.2.4.9 Customizing WDI 183 10.2.4.10 To change the tab display order 183 10.2.4.11 To draw a different worksheet tab label 183 10.2.4.12 To change the icon 184 10.2.4.13 To change the appearance of the tabs 184 10.2.4.14 Key WDI Methods and Data Members 184 11Chapter 11 Shortcut Bar 11.1 Overview 187 11.2 The Shortcut Bar Classes 189 11.2.1 SECShortcutBar 189 11.2.2 SECBar 190 11.2.3 SECListBar 190 11.2.4 SECShortcutListCtrl 190 11.3 Shortcut Bar Styles 191 11.4 Shortcut Bar Notification Messages 192 11.5 Shortcut Bar Callbacks 192 Contents xi 11.6 Using SECShortcutBar 193 11.6.1 To incorporate an SECShortcutBar into your application 193 11.6.2 To add selectable icons to a shortcut bar 193 11.6.3 To embed a window in a shortcut bar 194 11.6.4 To change the way the bars are drawn 194 11.6.5 To allow shortcut bars to behave like buttons 195 11.6.6 To enable context menus in a shortcut bar 195 11.6.7 To have the shortcut bars display the focus rectangle 196 11.6.8 To enable/disable animated scrolling 196 11.6.9 To receive notifications when an icon in the SECShortcutListCtrl is clicked 196 11.6.10 To determine which icon is clicked in an SECListBar window 197 11.6.11 To change the orientation of the shortcut bar at run time 198 11.6.12 To embed an SECShortcutBar into a splitter pane 198 11.7 Shortcut Bar Samples 198 12Chapter 12 Framework-Tailored Shortcut Bars 12.1 Overview 199 12.2 The Shortcut Bar Classes 201 12.2.1 ATL 201 12.2.2 MFC 201 12.3 Shortcut Bar Styles 202 12.4 Using the Shortcut Bar 203 12.4.1 Using the Windowed Shortcut Bar 203 12.4.2 Using the Non-Windowed Shortcut Bar 203 12.4.3 Setting visual aspects of the shortcut bar 204 12.4.4 Adding a context menu to the shortcut bar 204 12.5 Shortcut Bar Sample 205 13Chapter 13 Tabbed Windows 13.1 Overview 207 13.2 The Tabbed Window Classes 209 13.2.1 SECTabControlBase 209 13.2.2 SECTabControl 209 13.2.3 SEC3DTabControl 210 13.2.4 SECTabWndBase 210 13.2.5 SECTabWnd 210 13.2.6 SEC3DTabWnd 211 13.3 Tabbed Window Styles 212 xii Contents 13.4 Tab Control Notification Messages 214 13.5 Using SECTabWnd and SEC3DTabWnd 215 13.5.1 To add SECTabWnd or SEC3DTabWnd to a frame window 215 13.5.2 To add a tabbed window to a dialog 216 13.5.3 Removing the 2D Tab Scroll Buttons 216 13.5.4 To put 3D tabs on the side or top of the tabbed window 216 13.5.5 To enable scroll bars in the 2D tabbed window 217 13.5.6 To add keyboard accelerator support 217 13.5.7 To add a window to the tabbed window 218 13.5.8 To create and add a view to the tabbed window 218 13.5.9 To remove a tab 219 13.5.10 To access the CWnd associated with a tab 219 13.5.11 To change the font of a tab 220 13.5.12 To receive user event notifications from the tabbed window 220 13.5.13 To get a pointer to the SECTabWnd from a contained view 220 13.5.14 To insert a splitter window into a tabbed window 221 13.5.15 Problem with Tabbed Windows in Docking Views 221 13.6 Tabbed Window Sample 222 14Chapter 14 Tree Control & Tree View 14.1 Overview 223 14.2 The Tree Control Classes 224 14.2.1 SECTreeCtrl 224 14.2.2 SECListCtrl 224 14.3 The Tree View Classes 225 14.3.1 SECTreeView 225 14.3.2 SECListView 225 14.4 Tree Control Data Structures 226 14.4.1 TV_ITEM 226 14.4.2 NM_TREEVIEW 226 14.4.3 TV_HITTESTINFO 227 14.5 Tree Item States 228 14.6 Tree Control/Tree View Styles 230 14.7 Tree Control Notifications 234 14.8 Using the Tree Control Classes 236 14.8.1 To create a tree control in a dialog 236 14.8.2 To create a tree control dynamically 236 14.8.3 To add a tree item 237 14.8.4 To create multiple columns 237 Contents xiii 14.8.5 To set the text on subitems of multi-column trees 238 14.8.6 To create a standard image list for tree items 238 14.8.7 To create a state image list for tree items 238 14.8.8 To create a tree item with a state image 238 14.8.9 To change a state image on a tree item 239 14.8.10 To add an overlay image to a tree item 240 14.8.11 To find out which items are selected 240 14.8.12 To specify different colors for tree items 240 14.8.13 To specify different fonts for tree items 241 14.8.14 To update the tree control 241 14.8.15 To incorporate SECTreeCtrl into an application already using CtreeCtrl 242 14.9 Tree Control Samples 243 15Chapter 15 User Interface Extensions 15.1 Overview 245 15.2 Bitmapped Dialog 245 15.2.1 Using SECBitmapDialog 246 15.2.1.1 To incorporate the SECBitmapDialog class into your code 246 15.2.1.2 To set the image used by the SECBitmapDialog class 246 15.2.2 Customizing SECBitmapDialog 246 15.3 Gradient Caption Extension 247 15.3.1 The Gradient Caption Classes 247 15.3.2 SECFrameWnd 247 15.3.3 SECMDIFrameWnd 248 15.3.4 Using the Gradient Caption Feature 248 15.4 Keyboard Shortcuts 249 15.4.1 The Keyboard Shortcut Classes 249 15.4.2 SECShortcutTable 249 15.4.3 SECCommandList 249 15.4.4 SECShortcutDlg 249 15.4.5 Using the Keyboard Shortcut Classes 250 15.4.5.1 To incorporate keyboard shortcuts into your application 250 15.4.5.2 To update menus 250 15.4.5.3 To allow or disallow certain keyboard combinations 250 15.4.5.4 Setting Up Commands 251 15.4.5.5 Excluded IDs 252 15.4.5.6 Saving the Shortcuts 253 15.4.5.7 Keyboard Shortcut Notes 253 15.4.6 Keyboard Shortcut Sample 253 15.5 Splash Window 254 15.5.1 The SECSplashWnd Class 254 15.5.2 Using SECSplashWnd 255 15.5.3 SECSplashWnd Samples 255 xiv Contents 15.6 Custom Status Bar 256 15.6.1 Using SECCustomStatusBar 256 15.6.1.1 To incorporate SECCustomStatusBar into your code 256 15.6.1.2 To display the progress bar over the status bar 258 15.6.2 Customizing SECCustomStatusBar 259 15.6.2.1 To customize panes with the PANEINFOEX structure 259 15.6.2.2 To extend the SECCustomStatusBar class 260 15.7 Thumbnail Classes 261 15.7.1 The Thumbnail Classes 261 15.7.1.1 SECTNBitmap 262 15.7.1.2 SECTNDC 262 15.7.1.3 SECTNDocument 262 15.7.1.4 SECTNFileDialog 262 15.7.1.5 SECTNView 262 15.7.1.6 SECTNWinApp 263 15.7.2 Using the Thumbnail Classes 263 15.7.3 Thumbnail Sample 263 15.8 Tip of the Day Dialog 264 15.8.1 The SECTipOfDay Class 264 15.8.2 SECTipOfDay Resource IDs 264 15.8.3 Using SECTipOfDay 265 15.8.3.1 To create a modal tip of the day dialog 265 15.8.3.2 To create a modeless tip of the day dialog 265 15.8.3.3 To change the caption of the tip of the day dialog 266 15.8.3.4 To hide buttons on the tip of the day dialog 266 15.8.4 SECTipOfDay Sample 267 15.9 Tray Icon Class 268 15.9.1 To incorporate the Tray Icon Class into your application 268 15.9.2 To add notification handlers for mouse events 269 15.9.3 To animate a tray icon 270 15.9.4 Tray Icon Sample 270 15.10User-Tools Menu 271 15.10.1 The User-Tools Menu Classes 271 15.10.1.1 SECUserTool 271 15.10.1.2 SECUserToolsDlg 272 15.10.2 Using the User-Tools Menu Classes 272 15.10.3 User-Tool Menu Sample 273 15.11Workspace Manager 273 15.11.1 The Workspace Manager Classes 273 15.11.1.1 SECWorkspaceManager versus SECWorkspaceManagerEx 274 15.11.1.2 Support for Dynamically Created Controlbars 274 15.11.1.3 Multiple Views per Document, Multiple Views Per Frame 275 15.11.2 Using SECWorkspaceManagerEx 275 15.11.2.1 To add workspace management to your application 275 15.11.2.2 To load and store the workspace state automatically 276 Contents xv 15.11.2.3 To add application specific information to the workspace state 276 15.11.2.4 To store the WDI workbook state in the workspace state 278 15.11.3 Using SECWorkspaceManager 280 15.11.4 Workspace Manager Samples 281 15.12Full-Screen View 282 15.12.1 The Full-Screen View Class 282 15.12.2 SECFullScreenView Styles 283 15.12.3 Using the SECFullScreenView Class 283 15.12.3.1 To incorporate the SECFullScreenView class into your application 283 15.12.3.2 To set or retrieve the full-screen view mode styles 284 15.12.3.3 To terminate full-screen view mode 284 15.12.3.4 To use full-screen view mode with docking windows 284 15.12.3.5 To change the default full-screen view toolbar styles 284 15.12.4 SECFullScreenView Sample 285 16Chapter 16 Utility Classes 16.1 Overview 287 16.2 Compressed File I/O 287 16.2.1 SECCompressFile 287 16.2.2 Using SECCompressFile 288 16.2.3 SECCompressFile Sample 288 16.3 Encrypted File I/O 289 16.3.1 Electronic Codebook (ECB) 289 16.3.2 Output Feedback Mode (OFB) 289 16.3.3 The SECCryptoFile Classes 289 16.3.3.1 SECCryptoFile 289 16.3.3.2 SECBlackBox 290 16.3.4 The Encryption Algorithm 290 16.3.4.1 Password Transformation 290 16.3.4.2 The Stream Cipher 290 16.3.4.3 The Cipher Modes 291 16.3.5 Limitations 292 16.3.6 Using SECCryptoFile 292 16.3.6.1 To encrypt data to a file 292 16.3.6.2 To read an encrypted file 293 16.3.7 SECCryptoFile Sample 293 16.4 Safe Multi-Threaded Trace Output 294 16.4.1 Use of the Multi-Threaded Logging Class 294 16.5 File System Access 296 16.5.1 SECFileSystem 296 16.5.2 Using SECFileSystem 296 16.5.2.1 To copy a file 296 16.5.2.2 To copy multiple files 296 xvi Contents 16.5.2.3 To compare two files 296 16.5.2.4 To delete a file 297 16.5.2.5 To delete multiple files 297 16.5.2.6 To get information about a file 297 16.5.2.7 To parse filename information on a file 297 16.5.2.8 To get a list of files in a directory 298 16.5.2.9 To create a directory 298 16.5.2.10 To change the working directory 298 16.5.3 SECFileSystem Sample 298 16.6 Random Number Generation 299 16.6.1 The SECRandom Class 299 16.6.2 Using SECRandom 300 16.6.2.1 To generate an unsigned random number (0 to 32767) 300 16.6.2.2 To set the range of the random numbers generated 300 16.6.2.3 To generate weighted random values 300 16.6.2.4 Key SECRandom Methods 301 16.6.3 SECRandom Sample 301 16.7 Formula Engine 302 16.7.1 Features of the Formula Scanner 302 16.7.2 Use of the Formula Engine Class 302 16.8 Win32 Registry Access 304 16.8.1 The SECRegistry Class 304 16.8.2 Using SECRegistry 304 16.8.2.1 To open the registry 305 16.8.2.2 To open a subkey 305 16.8.2.3 To enumerate registry keys 305 16.8.2.4 To enumerate values within a key 305 16.8.2.5 To read a value 306 16.8.2.6 To create a key 306 16.8.2.7 To delete a key 306 16.8.2.8 To recursively delete keys 307 16.8.2.9 To close a registry connection 307 16.8.3 SECRegistry Sample 307 17Chapter 17 Data Extraction Classes 17.1 Overview 309 17.1.1 Building the Libraries 309 17.1.2 Using Regular Expression Libraries 311 17.2 Data Extraction Framework 312 17.3 Example: Setting Up a Scanner 313 17.3.1 Introduction to the Deals Sample 313 17.3.2 Declaring the Processor 313 17.3.3 Implementation Details 315 17.3.3.1 Token Definitions 316 Contents xvii 17.3.3.2 States 316 17.3.3.3 Sub Expressions 317 17.3.3.4 Defining the Listener 318 17.3.3.5 Optional Overrides 319 17.3.3.6 OnMatch() Implementation 320 17.4 Note on Exceptions 322 18Chapter 18 View Classes 18.1 Overview 323 18.2 The Zoom and Pan View Classes 323 18.3 SECZoomView 324 18.3.1 SECPanView 324 18.3.2 SECPanWnd 324 18.4 Zoom Modes 325 18.5 Using the View Classes 326 18.5.1 To incorporate zooming support into an application 326 18.5.2 To incorporate panning support into an application 326 18.5.3 To incorporate a panning overview window to an application 327 18.5.4 Key Zooming Methods 327 18.5.5 Key Panning Methods 328 18.6 Zooming/Panning Sample 330 19Chapter 19 ActiveScript Hosting Framework 19.1 Overview 331 19.2 Overview of JavaScript 332 19.3 VBScript 332 19.4 Hosting an Active Script 332 19.5 ActiveScript Classes 333 19.5.1 SECAAppObj 333 19.5.2 SECAFormObj 333 19.5.3 SECAScriptHost 333 19.5.4 SECAScriptOccManager 334 19.5.5 ActiveScriptErrorHandler 334 19.6 Using the ActiveScript Framework 335 19.6.1 ActiveScript and Type-libraries 335 19.6.2 To prevent the Objective Toolkit library from automatically including xviii Contents ScriptHost.tlb as resource 335 19.6.3 To incorporate scripting into your application 335 19.7 ActiveScript Sample 337 20Chapter 20 ActiveHost Form Scripting and Editing Framework 20.1 Overview 339 20.2 ActiveHost Classes 340 20.2.1 SECScriptHostDoc 340 20.2.2 SECScriptHostView 340 20.2.3 SECAFloatDocTemplate 340 20.2.4 SECADlgFrame 340 20.3 Using the ActiveHost Form Editing Framework 341 20.3.1 To incorporate ActiveHost into your application 341 20.4 The ActiveHost Sample 341 21Chapter 21 Advanced Docking Windows 21.1 Overview 343 21.2 Advanced Docking Windows Architecture 344 21.3 Advanced Docking Windows Features 345 21.3.1 Docking Inside an MDI Child Frame 345 21.3.2 Floating MultiDock Mode 345 21.3.3 Realtime Drag Mode 345 21.3.4 Alternate Border Layout Logic 345 21.3.5 Advanced Docking Configurations 346 21.4 Advanced Docking Windows Splitter Styles 347 21.5 Using the Advanced Docking Windows Architecture 349 21.5.1 To incorporate advanced docking windows into your application 349 21.5.2 To create dockable device context nodes 352 21.5.3 To use docking insertion constraints 353 21.5.4 To adjust the border sizing 355 21.5.5 To use ‘real-time’ drag mode 356 21.5.6 To use floating multidock mode 356 21.5.7 To use alternate border layout logic 356 21.5.8 To integrate a dockable node inside an MDI child frame 358 21.6 Advanced Docking Windows Examples 360 22Chapter 22 Docking Views Contents xix 22.1 Overview 361 22.2 Features of Docking Views 362 22.3 Issues when Docking a CView 363 22.4 Docking Views Options 364 22.5 The Docking Views Classes 365 22.5.1 SECDockableFrame 365 22.5.2 SECFrameBar 365 22.5.3 SECMDIChildWnd 366 22.5.4 SECMultiDocTemplate 366 22.6 Architectural Overview 367 22.7 Docking Views Containment Model 368 22.8 WM_SYSCOMMANDEX 370 22.9 Docking Views and MDI Alternatives 371 22.10Using the Docking Views Architecture 371 22.10.1 To incorporate docking views into your application 371 22.10.2 To set docking view styles 371 22.10.3 To toggle the presence of the docking button on a dockable view frame 371 22.10.4 To disable right mouse double-clicks on the docking view caption bar 373 22.10.5 To create an initially docked view 373 22.10.6 To start an application with no initial view 374 22.10.7 To put a splitter in a docking view 374 22.10.8 To dock a view 375 22.10.9 To float a view as an MDI child 375 22.10.10 To obtain a pointer to the view 375 22.10.11 To control the initial size and position of a docking view as it is docked 376 23Chapter 23 Layout Manager Framework 23.1 Overview 377 23.2 Issues with Resizable Windows 378 23.3 Benefits of the Objective Toolkit Layout Manager 379 23.3.1 Objective Toolkit Layout Manager: MFC Integration 379 23.3.2 Objective Toolkit Layout Manager: Strong Architecture 379 23.3.3 Objective Toolkit Layout Manager: Ease of Integration 379 23.4 Layout Manager Architecture 381 23.4.1 Layout Nodes 381 23.4.2 Window Listeners 384 xx Contents 23.4.3 Layout Factory 384 23.4.4 Splitter Node 384 23.5 Layout Algorithms 385 23.5.1 Alignment Layout 385 23.5.2 Scale Layout 385 23.5.3 Grid Layout 386 23.5.4 GridBag Layout 386 23.5.5 Relative Layout 387 23.6 Using the Layout Manager 389 23.6.1 Adding Layout Management to Your Applications 389 23.7 Layout Manager Examples 390 23.7.1 Scale Layout in a Dialog 390 23.7.2 Relative Layout in a Dialog 390 23.7.3 Grid Layout, Alignment Layout and Splitter in a Formview 391 23.7.4 To specify min/max sizes for layout nodes 392 23.7.5 To implement a custom layout manager 392 23.8 Layout Manager Sample 392 24Chapter 24 Microsoft Agent Extensions 24.1 Overview 393 24.2 Overview of Microsoft Agent Technology 393 24.3 Agent Extension Classes 394 24.3.1 SECAgentCharacterExPtr 394 24.3.2 IAgentApp 394 24.3.3 SECAgentApp 394 24.3.4 SECAgentCharAct 395 24.3.5 SECAgentNotifySink 395 24.4 Using the Agent Extension Classes In Your Applications 396 24.4.1 Agent Extensions Sample 396 25Chapter 25 Namespace Extension Wizard 25.1 Overview 397 25.2 Installing Stingray Namespace Extension Wizard 397 25.3 Creating a Skeleton Namespace Extension 398 25.4 Selecting Namespace Options 398 25.4.1 Show Up 398 Contents xxi 25.4.2 Register For 398 25.4.3 Support UI Object 399 25.5 Tutorial 400 25.5.1 Create the Skeleton Namespace Extension Project 400 25.5.2 Work Through the Generated Code 401 25.5.3 Change The Data Structure For PIDL 403 25.5.4 Implementing CreateEnumIDList() 403 25.5.5 Modify CNSExtCompView::InitList() 405 25.5.6 Give Each Node an Informative Name 409 25.5.7 Change the Default Context Menu Handling 410 25.5.8 Give Node An Icon 412 26Chapter 26 The Hyperlink Classes 26.1 Overview 415 26.2 Using the Hyperlink Classes 416 26.3 Customizing the Hyperlink Control 417 26.4 Sample 417 27Chapter 27 Web Browser Extensions 27.1 Overview 419 27.2 Feature List 419 27.2.1 Getting the IWebBrowser2 Interface in IE 419 27.2.1.1 IWebBrowser2 from a NEW IE instance 419 27.2.1.2 Grab IE under HWND 420 27.2.2 Init IWebBrowser2 with HTML in Memory 420 27.2.3 Retrieve HTML in IWebBrowser2 420 27.2.4 CHTMLView Extensions 420 27.2.5 Miscellaneous Utility Functions 421 27.3 Sample 421 28Chapter 28 The APP ATL Object 28.1 Overview 423 28.1.1 Overview of Asynchronous Pluggable Protocol 423 28.2 Objective Toolkit APP ATL Object Classes 424 28.2.1 SECPlugProt 424 28.2.2 SECPlugProtImp 424 28.2.3 FileDownloadInfo 424 28.2.4 SECWorkerThreadFetchObject 424 xxii Contents 28.2.5 WorkerThreadMain 424 28.3 Using the Objective Toolkit APP ATL Object in Your Applications 425 28.4 Sample 425 29Chapter 29 ATL and Objective Toolkit Components 29.1 Overview 427 29.2 Wrapping Objective Toolkit Components in an ATL ActiveX Control 428 29.3 An Example: An ATL ActiveX Control Built From SECTreeCtrl 429 29.3.1 Pre-Build Set-Up 433 29.3.2 Building Your Control 435 29.3.3 Testing Your Control 435 29.4 Further Extensions 441 29.5 Sample Code 442 30Chapter 30 Introduction to Objective Toolkit for ATL 30.1 Overview 443 30.2 Features and Benefits 444 30.2.1 Features of Objective Toolkit for ATL 444 30.3 COM Collection Classes 445 30.4 Threading Classes 447 30.5 Interface Token Class 448 30.6 Functors 449 30.6.1 Constructing Functors 449 30.6.2 Invoking Functors 449 30.6.3 Interface Tokens and Functors 450 30.6.4 Threads and Functors 450 30.7 SAFEARRAY Classes 451 30.7.1 COtlSimpleSafeArray 451 30.8 RGSEdit 453 30.8.1 Changing What Gets Registered 453 30.8.2 Editing a Registry Script 454 30.8.3 Adding Keys 454 30.8.4 Editing Keys 454 30.8.5 Deleting Keys 454 Contents xxiii 30.8.6 Adding Values 454 30.8.7 Editing Values 455 30.8.8 Managing Categories 455 30.8.9 Launching RGSEdit from the IDE 456 30.9 Microsoft Message Queue Class 457 30.9.1 Requirements 457 30.9.2 Creating a queue 457 30.9.3 Opening a queue 457 30.9.4 Receiving Messages 458 30.9.5 Sending Messages to a Queue 458 30.9.6 Cleanup 459 30.10XML Helpers 460 30.10.1 Creating an XML Document From Scratch 460 30.10.2 Opening an Existing XML Document 460 30.10.3 Adding Tags 460 30.10.4 Saving the XML Document to a File 460 30.10.5 Reading Tag Values 461 30.10.6 Trace Output 461 30.11Internet Explorer Band Object Wizard and Classes 462 30.11.1 Changing Size Constraints 462 30.11.2 Context Menu Commands 462 30.11.2.1 Parameters 463 30.12Desktop Application Toolbar Class and Object Wizard 464 30.12.1 Creating an AppBar 464 30.12.2 Docking and Layout 464 30.12.3 Appbar Tab Window Control 464 30.12.4 Creation 465 30.12.5 Message Reflection 465 30.12.6 Image List 465 30.12.7 Selection Notification 465 30.13Window Layout Manager for Composite Controls 466 30.13.1 Layout Manager Algorithms 466 30.13.2 Scale Layout 466 30.13.3 Relative Layout 466 30.13.4 Adding Layout Management to Your Applications 467 30.14COM Task Allocator Memory Debugging Tools 468 30.14.1 Using the Task Allocator Debugger 468 30.15Marshaling Classes 469 30.16Software Requirements 471 30.17Distributing Objective Toolkit for ATL Applications 471 xxiv Contents Index 473 Contents xxv xxvi Contents Chapter 1 Introduction to Objective Toolkit 1.1 Welcome to Objective Toolkit Objective Toolkit is a set of MFC extension classes that enhance your current Visual C++/Microsoft Foundation Class programs. Objective Toolkit provides support for a variety of graphical user interface controls, views, and utilities. You can extend its object-oriented classes quickly and easily. Unlike other C++ class libraries, Objective Toolkit classes are completely compatible with the Microsoft Foundation Class (MFC) classes. The Objective Toolkit classes work seamlessly with the MFC classes and, in many cases, inherit from existing classes such as CView or CWnd. Objective Toolkit is fully compatible with the latest 32-bit and 64-bit releases of Microsoft Visual Studio (see Section 1.3, “Supported Platforms.”). Objective Toolkit components enable you to dedicate your efforts to creating a viable application, instead of modifying the GUI, by providing you with extension classes for common user-interface features. Chapter 1 Introduction to Objective Toolkit 1 1.2 Product Features The following sections describe some of the major features of Objective Toolkit. 1.2.1 MFC Extension Classes Simple Control Classes. Objective Toolkit consists of a variety of powerful classes that provide advanced GUI components, such as: Owner-draw, bitmap, menu, and well buttons. Color well, pop-up color well, masked edit, browse edit, and editable list box controls. 2-D and 3-D tab controls/tabbed windows. Calculator, calendar, currency, and date/time edit controls. Custom status bar, custom toolbar, and a tree control with enhanced functionality. The source code for every window and control class is in the Src\Toolkit\Controls subdirectory. User Interface Extensions. Objective Toolkit includes a number of user-interface extensions that address high-level UI design issues. A user-interface extension is a class or set of classes that enhances the look, configuration capability, or information content of your user interface. The source code for all MDI alternatives and enhancements is in the Src\Toolkit\UI subdirectory. Image Classes. Objective Toolkit contains a group of classes that let you read, write, convert between, and manipulate popular image formats. Supported formats include DIB, GIF, JPEG, PCX, TGA, and TIFF. The source code for each image class is in the Src\Toolkit\Image subdirectory. Docking Windows Architecture. The extended control bar architecture is a set of MFC extensions that augment the docking window features available in MFC version 4.x. There are two categories of extended control bar classes: control bar derivatives and frame window derivatives. Control bar classes include: Control bar and control bar manager Dialog bar, toolbar and toolbar manager Status bar MDI Alternatives and Enhancements. Objective Toolkit implements several MDI alternatives and enhancements. The Multiple Top-level Interface (MTI) and the Floating Document Interface (FDI) are alternatives to MDI. The Workbook Document Interface (WDI) and Gradient Caption class enhance MDI. The source code for all alternatives and enhancements is in the Src\Toolkit\MDI directory. Objective Toolkit provides replacements for MFC’s frame window classes that add significant functionality. The Frame window classes include SECFrameWnd, SECMDIChildWnd, and SECMDIFrameWnd. 2 Toolbar Classes. The Stingray toolbar replacement for CToolBar supports the enhanced docking window features. It also gives you the ability to resize the toolbar when it is docked and maintain compatibility with our enhanced docking windows implementation. These classes also support drag-and-drop customization, large/small icon view modes, and an Microsoft Office look-and-feel. Utility Classes. Objective Toolkit also provides classes that are not related to the user interface. For example: SECRegistry provides a sophisticated interface to the registry. SECRandom supports random number generation. SECFileSystem provides an encapsulation of the run-time access to the file system. SECCryptoFile is a CFile derivative that provides encryption. SECCompressFile is a CFile derivative that provides compression. The source code for all utility classes is in the Src\Toolkit\Utility subdirectory. View Classes. The Objective Toolkit view classes are CView extensions that provide features such as advanced zooming and panning. The zooming feature lets the user zoom in and out of a view and automatically handles all mapping mode changes. Panning is a popular scrolling extension used in Windows applications like Delrina WinFax. The source code for all view classes is in the Src\Toolkit\Views subdirectory. 1.2.2 Full Source Code The complete source code for Objective Toolkit is included with the product. The source code is indispensable for debugging, using, and inheriting from an MFC extension class. The source code is in the \Include\Toolkit and \Src\Toolkit directories. File names reflect the classes they contain. 1.2.3 Compatibility and Build Options For maximum flexibility, you can use Objective Toolkit with the latest Microsoft Visual Studio compilers (see Section 1.3, “Supported Platforms.”). Moreover, you can use Objective Toolkit as a static library with MFC static, as a static library with MFC as a DLL, or as a DLL with MFC as a DLL. Objective Toolkit supports Unicode. Unicode and non-Unicode variants exist for all static and DLL builds. Once the Objective Toolkit libraries are built, these configurations are transparent to you. Chapter 1 Introduction to Objective Toolkit 3 1.3 Location of Samples Stingray Studio ships the most basic and commonly used samples with the product itself. The less commonly used and more specialized samples have been removed from the product distribution, but are available from the Rogue Wave web site. If you are looking for a sample and it does not appear under <stingrayinstalldir>\Samples\Toolkit\<sample-name>, you can download the sample bundle from the Knowledge Base on the Rogue Wave Web site, as described in Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 1.4 Supported Platforms For a list of supported operating systems and compilers, see http://www.roguewave.com/products/stingray.aspx, then click on the link “Supported Platforms” to download a PDF. 1.5 Getting Help Several avenues of help are available to you when working with Objective Toolkit. 1.5.1 Documentation Documentation is located in the Docs subdirectory of your Objective Toolkit directory. The following documents are available: User's Guide - This manual. The User's Guide provides an introduction to Objective Toolkit and a foundation for using Objective Toolkit “out-of-the-box.” Several tutorials help new Objective Toolkit users learn how to create Objective Toolkit applications quickly. This manual assumes that you are familiar with Visual C++ and the Microsoft Foundation Classes (MFC). This document is available in two formats: HTML Help (otug.chm) and Portable Document Format (otug.pdf). Reference Guide- The reference document (otref.chm) is a detailed description of the properties, methods, and events in Objective Toolkit. ReadMe file - The latest information about the product, Toolkitreadme.htm located in the Readme directory under your Stingray installation directory. For more information on the documentation, including all Stingray documentation, an index to the Help files, and document type conventions, see Section 1.4, “Product Documentation,” in the Stingray Studio Getting Started Guide. 4 1.5.2 Knowledge Base The Rogue Wave Knowledge Base contains a large body of useful information created by the Support Services team. This information is available to any user of the Rogue Wave Web site, and no login or registration is required. http://www.roguewave.com/support/knowledge-base.aspx. 1.5.3 Professional Services The Rogue Wave Professional Services offers training and mentoring for all levels of project development, from analysis and design to implementation. For more information, see Section 1.5, “Professional Services,” in the Stingray Studio Getting Started Guide. 1.5.4 Technical Support Technical support for Objective Toolkit products is provided through the Rogue Wave Web site. For more information on registering with the support system, and the type of support you may receive, see Section 1.6, “Technical Support,” in the Stingray Studio Getting Started Guide. 1.6 Licensing Restrictions Please read the license agreement that was shipped with this package. You are bound by the licensing restrictions contained in that document. Do not use this product unless you can accept all the terms of the license agreement. You can use all the files accompanying this product for development of an application. You can distribute the Objective Toolkit Dynamic Link Libraries (DLLs) according to the terms of the license agreement. Your applications can also statically link to Objective Toolkit, in which case you do not need to redistribute any Objective Toolkit files—except any required language configuration files. Chapter 1 Introduction to Objective Toolkit 5 6 Chapter 2 Objective Toolkit Quick Start 2.1 Overview The following sections describe how to build Objective Toolkit and setup your application to utilize its features. 2.2 Installation Notes Please note the following warnings, which may help you avoid or repair problems during the installation process. During installation, Objective Toolkit adds several entries to the registry and environmental variables. The following circumstances can impede installation: An environment variable exceeds the maximum length. If this occurs, you need to shorten its directory names. You have directories that are outdated, unused, or both. To remove them, you need to delete their PATH, LIB, INCLUDE strings. Some directory names may have to be shortened. In Windows 2000 and subsequent versions, the environment strings can be edited on the Control Panel | System | Advanced tab. Ensure that Visual Studio is closed before you begin installing Objective Toolkit. If you inadvertently leave Visual Studio open during installation, you need to exit and restart it for the new settings to take effect. Windows must be restarted after installation to establish the paths to the new Objective Toolkit DLLs. The HTML help files of Objective Toolkit and the other Stingray Studio products are automatically integrated into Visual Studio's MSDN Help system. The first time you open MSDN Help after installing Objective Toolkit MSDN slowly rebuilds its index database. Please be patient and allow the process to complete. Chapter 2 Objective Toolkit Quick Start 7 8 The Help file integration has been problematic for some customers. The details of MSDN integration change frequently. If installing Objective Toolkit deactivates your version of MSDN, we recommend that you copy hhsetup.DLL from the MSDN CD and then paste it into the Stingray Studio directory to overwrite our version. If copying the new DLL over our DLL doesn’t reactivate MSDN, we have additional solutions posted to our Knowledge Base (kb.roguewave.com/kb). 2.3 Building Objective Toolkit You can build Objective Toolkit in many different ways to support a variety of operating systems and VC++/MFC configurations. For example, you can build Objective Toolkit for any of the latest Visual Studio compilers. (For a full support matrix, go to the Stingray product page on the Rogue Wave web site, www.roguewave.com/products/stingray.aspx, and click on Supported Platforms.) In addition, your build configuration may specify any combination of build flags such as debug or release, Unicode or ANSI, static or DLL. You can obtain prebuilt versions of the libraries by request to Rogue Wave technical support. Libraries are available for Windows XP and Vista with the currently supported compilers. We recommend, however, that you build the libraries yourself. The prebuilt libraries are built with a particular instance of Visual Studio and the Windows operating system. Building the libraries yourself ensures that they are compatible with your version of the compiler and the operating system they are built on. A Build Configuration Wizard is included with Objective Toolkit (utils\ToolkitBuildWiz.exe). The Build Wizard is a powerful tool that allows you to build various versions of the Objective Toolkit library with different configurations. For example, you can build a version of the library that only includes the features that pertain to your project. This is an effective technique for reducing the binary size of the resulting Objective Toolkit libraries and DLLs. Objective Toolkit is distributed with build files for the default configuration that includes all the Toolkit features. You do not need to run the Build Wizard to build the default configuration. The SRC subdirectory contains build files for every version of Visual Studio that Objective Toolkit supports. To build Objective Toolkit, open the appropriate build file. After you load it, examine the different build configurations that specify the settings such as debug or release, Unicode or ANSI, static linking or DLL, and more. You can choose a particular configuration and build only that library variant, or select the All configuration and build every variant of the Objective Toolkit library in one build session. To choose a build configuration, select an item from the Set Active Configuration combo box in Visual Studio or start a Batch Build and select several variants to be built at once. 2.3.1 Using the Build Wizard The Build Configuration Wizard is a wizard dialog that allows you to select every feature you want to include in the resulting library. After you answer a few simple questions, the Build Wizard automatically generates a custom tailored makefile that you can build to create the Objective Toolkit library in the configuration you specified. You can customize the names of the libraries to avoid any potential name collisions or versioning problems. You can generate multiple library configurations with the Build Wizard. For example, you could generate one with just a tree control and another with only the docking windows code. Several different configurations can coexist on your hard disk simultaneously. The Wizard dialogs describe this procedure. Give the target libraries of different build configurations different target names to avoid name collisions. Chapter 2 Objective Toolkit Quick Start 9 The Objective Toolkit components are now built into two libraries: Stingray Foundation Library (SFL) and OT. If you want to create a set of libraries with a special configuration, you should run the Build Wizard for both libraries, specifying the same configuration name. 2.3.1.1 To build a custom configuration version of the library 1. Run the SFL Library Build Wizard. 2. On the second panel, specify the configuration name (for example, MyConfig). Use the Build Wizard to specify custom library names for the common library. 3. Run the Objective Toolkit library Build Wizard. After you enter the configuration name (for example, MyConfig), select the components you want to include in the build of the library, and then enter names for your custom library. 4. Rebuild the Objective Toolkit library to automatically build the Objective Toolkit and SFL libraries. The Build Wizards automatically generate custom linking header files that you can include in your project. These header files are located in the include\toolkit\config directory for the Toolkit library and include\foundation\config directory for the SFL library. The Build Wizard created these files when you rebuilt the library. They have the same name as the library to which they link, with the added prefix sfl_ or ot_ . To link to the custom configuration in your application, insert the following lines into stdafx, before you include toolkit\secall.h or any Objective Toolkit component headers. #include "foundation\config\sfl_MyConfig.h" #include "toolkit\config\ot_MyConfig.h" #include "toolkit\secall.h" If you are working with the default configuration, you do not need to include the xxx_Default.h header files. 2.3.2 DLL naming issues When you build a custom configuration of Objective Toolkit, you must specify a unique DLL target name. When you build a subset of the Objective Toolkit features or make a change to the Objective Toolkit source or header files, the signature of the library changes. So, when you build a DLL that incorporates a subset of Objective Toolkit features or your own changes, you need to treat the target DLL like a completely unique DLL. If you change the signature of Objective Toolkit and do not specify a new DLL target name, other applications that link to the Objective Toolkit DLL may fail. 2.3.3 Compiler Flags The Objective Toolkit build files and header files use #ifdef’s on several compiler flags to define the configuration variants described above. You can use the standard symbols, _DEBUG, _UNICODE, and _AFXDLL, to select support for debugging, Unicode characters, and linking to MFC as a DLL. 10 The flags in Table 1 are specific to Objective Toolkit. Table 1 – Objective Toolkit flags Flag Definition _SECDLL Links to the Objective Toolkit library as a DLL. _SECNOAUTOLIB Prevents the Objective Toolkit autolink mechanism from working. If this is defined, you need to explicitly list the Objective Toolkit libraries you want to link to in your project's link settings. _SECNOMSG Prevents the output of Objective Toolkit related messages when you build. SEC_NO_TLB Objective Toolkit automatically includes its ScriptHost.tlb as resource 1 when the ActiveScript headers are included in an application. This can conflict with applications containing ActiveX components. Defining SEC_NO_TLB prevents this problem from occurring. 2.3.4 Build Target Naming Conventions The library naming conventions are illustrated in Figure 1. Figure 1 – Build Configurations Chapter 2 Objective Toolkit Quick Start 11 2.3.5 Build Configuration Options The following table shows the default library names for the various configurations, where <ver> stands for the current product version number. Refer to the naming convention described above. Library name Objective Toolkit library configuration MFC configuration Unicode supported Build Type OT<ver> Static Static No Release OT<ver>d Static Static No Debug OT<ver>a Static DLL No Release OT<ver>ad Static DLL No Debug OT<ver>as DLL DLL No Release OT<ver>asd DLL DLL No Debug OT<ver>u Static Static Yes Release OT<ver>ud Static Static Yes Debug OT<ver>au Static DLL Yes Release OT<ver>aud Static DLL Yes Debug OT<ver>asu DLL DLL Yes Release OT<ver>asu d DLL DLL Yes Debug Objective Toolkit makefiles place .lib, .dll and .pdb files into the directory OT\lib\<VCVer>\<Plat>\ (where <VCVer> is either vc71, vc8, or vc9, and <Plat> is x86 or x64). Add this directory to your executable path, or manually copy the DLLs to your Windows directory. 2.3.6 To build the 32-bit or 64-bit libraries 1. Start Visual Studio. 2. Check the Visual Studio environment directory paths for both Win32 and x64 settings to ensure that the Include, Source, Library and Executable paths contain the appropriate Stingray Studio include, source, library and executable directory paths. Please refer to Section 2.7.3, “Check Visual Studio Paths,” in the Stingray Studio Getting Started Guide for pathing details. 3. Open the solution file appropriate to the compiler version you are using. 4. Choose the platform, i.e. Win32 or x64, and then choose the build configuration to build. 5. Start the build. Intermediate files use approximately 250 Mb of space, and library files use approximately 80 Mb of space. 12 2.3.7 Miscellaneous Build Issues 2.3.7.1 Using Multiple Library Configurations Whenever you run the Build Wizard, it overwrites sflversion.h in the SFL (include\foundation) include directory or secver.h in the Objective Toolkit (include\toolkit) include directory with the names of the libraries. This information is used to link the libraries to your project automatically. If you are maintaining more than one library configuration on your system, this can cause problems. Your projects link to whatever configuration was last set up by the Build Wizard. To avoid problems when switching library configurations, run the Build Wizard specifying the desired configuration for each of the Objective Toolkit libraries. 2.3.7.2 Make Files and Building Directly with nmake When you build the Stingray libraries in Visual Studio, Visual Studio invokes make files that ship with the product. For information on these underlying make files, and how to build the libraries by invoking nmake on these files directly, see Section 2.3, “Building from the Command Line with nmake,” in the Stingray Studio Getting Started Guide. This section also discusses the issue of building the libraries with 1-byte structure alignment rather than the default 8-byte structure alignment. 2.3.7.3 Components Requiring RTTI Support Run-Time Type Information (RTTI) support is required for some components. When you run the Build Wizard, RTTI support must be enabled if you include: Layout Manager Advanced docking windows (ADW) components Full Screen view components Outlook bar SECMultiDocTemplate and related code Microsoft Agent To learn more about this requirement, see our Knowledge Base at (kb.roguewave.com/kb/). Chapter 2 Objective Toolkit Quick Start 13 2.3.8 Building the Samples Each sample project has a variety of build options to show you how to use the various build configurations. All 32-bit and 64-bit samples build with the following targets: Table 2 – Build Configurations for Samples Build Configuration Description WIN32 or x64 – Lib MFC Lib Debug Objective Toolkit as a static library, MFC as a static library, ANSI, Debug WIN32 or x64 – Lib MFC Lib Release Objective Toolkit as a static library, MFC as a static library, ANSI, Release WIN32 or x64 – Lib MFC DLL Debug Objective Toolkit as a static library, MFC as a DLL, ANSI, Debug WIN32 or x64 – Lib MFC DLL Release Objective Toolkit as a static library, MFC as DLL, ANSI, Release WIN32 or x64 – DLL MFC DLL Debug Objective Toolkit as a DLL, MFC as a DLL, ANSI, Debug WIN32 or x64 – DLL MFC DLL Release Objective Toolkit as a DLL, MFC as a DLL, ANSI, Release WIN32 or x64 – Lib MFC Lib Uni Debug Objective Toolkit as a static library, MFC as a static library, Unicode, Debug WIN32 or x64 – Lib MFC Lib Uni Release Objective Toolkit as a static library, MFC as a static library, Unicode, Release WIN32 or x64 – Lib MFC DLL Uni Debug Objective Toolkit as a static library, MFC as a DLL, Unicode, Debug WIN32 or x64 – Lib MFC DLL Uni Release Objective Toolkit as a static library, MFC as a DLL, Unicode, Release WIN32 or x64 – DLL MFC DLL Uni Debug Objective Toolkit as a DLL, MFC as a DLL, Unicode, Debug WIN32 or x64 – DLL MFC DLL Uni Release Objective Toolkit as a DLL, MFC as a DLL, Unicode, Release To build the samples, you need to build the appropriate Objective Toolkit library configuration first. 2.3.9 Automatic linking If you check the Project Settings|C/C++ tab|Preprocessor Definitions for several target configurations, you can see how the compiler flags link to the appropriate Objective Toolkit library variant automatically. 14 You indicate exactly which Objective Toolkit library you want to link by defining a combination of _DEBUG, _AFXDLL, _UNICODE, and _SECDLL in your project’s preprocessor definitions. 2.3.10 To incorporate Objective Toolkit into your application After you build the libraries, you can start using Objective Toolkit classes in your own applications. Follow the steps below to add Objective Toolkit to an existing application. 1. Load your project into Visual Studio. 2. Add the following the line to the end of your stdafx.h header file. Because stdafx.h is normally included in all your source files, this makes the Objective Toolkit classes available throughout your project. You can optimize your application’s build speed by using the Objective Toolkit component-specific headers instead of secall.h. #include “toolkit\secall.h” If you are linking to a custom library configuration, include the custom headers before the secall.h include. For example: #include "foundation\config\sfl_MyConfig.h" #include "toolkit\config\ot_MyConfig.h" #include “toolkit\secall.h” Finally, add includes for these header files, common to all Stingray Studio products: Add #include <SupportedPlatforms.h> at the top of the includes. The conditional platform information is displayed in the application's output window. The output information is helpful when developing and deploying across one or more platforms. Add #include <ManifestDefs.h> at the end of the includes. This file facilitates the inclusion of manifest definitions for Windows Visual Styles. 3. Open the View|Resource Includes dialog and add the following line to the list of readonly symbol directives. #include “toolkit\secres.h” 4. Add the next line to the list of compile-time directives. #include “toolkit\secres.rc” Add a combination of _DEBUG, _UNICODE, _AFXDLL, and _SECDLL to the Project Settings|C/C++ tab|Preprocessor Definitions. This automatically causes the correct variant of the Objective Toolkit library to be included in your project. _AFXDLL and _DEBUG may be defined automatically by other project settings. If you have any problems using a specific class, refer to the online Objective Toolkit Class Reference. Each section of the Class Reference discusses one class or a group of related classes and includes an overview, step-by-step instructions, and important member functions. We also cite Objective Toolkit samples that demonstrate the use and capabilities of the class. Chapter 2 Objective Toolkit Quick Start 15 For information on creating an application with our AppWizard, see Chapter 3, “The Objective Toolkit AppWizard.” For information on using component headers, see Section 2.6.1. 16 2.4 Distributing Objective Toolkit Applications Please read the license agreement that was shipped with this package. You are bound by the licensing restrictions contained in this document. Do not use this product unless you can accept all the terms of the license agreement. You can use all the files accompanying this product for development of an application. You can distribute the with Objective Toolkit Dynamic Link Libraries (DLLs) according to the terms of the license agreement. Your applications can also statically link to Objective Toolkit, in which case you do not need to redistribute any Objective Toolkit files. You can also distribute the MFC DLLS with your application. Look in the compiler's root directory where you installed Visual Studio (or a previous version of VC++).You should find a document named redist.txt. This document gives details about which MFC files need to be distributed with various configurations. Chapter 2 Objective Toolkit Quick Start 17 2.5 Basic Tutorial—MaskEdit Control The CEdit control that MFC provides is often insufficient for data entry purposes. The application can only verify the value the user entered after it is fully typed. In addition, CEdit provides no ability to break the edit field into sub-fields that indicate what text is expected. SECMaskEdit addresses these problems. SECMaskEdit is derived from CEdit and it adds member functions for specifying a mask. This tutorial uses the built-in functionality of Objective Toolkit’s SECMaskEdit class to create a masked edit field for a telephone number. Follow the steps below to create a simple dialog-based application using the AppWizard. An SECMaskEdit object, added as a member of the dialog, displays an entry field that is formatted for a telephone number. Along the way, the application is given access to all the Objective Toolkit classes. 1. From the File menu, choose New... (Name your project application MaskPhone.) 2. From the main wizard dialog, select MFC AppWizard (exe). 18 3. Click the Dialog based radio button. 4. Click Finish. The MFC AppWizard finishes creating the project for you. You must follow the two additional steps below if you are going to generate your own sample programs. 5. The next thing we need to do is make sure all of the resources in Objective Toolkit are available to your project. Find the stdafx.h file for the project and add the following line: #include <toolkit\secall.h> Make sure the stdafx.h file reads: // // // // stdafx.h : include file for standard system include files, or project specific include files that are used frequently, but are changed infrequently #if !defined(AFX_STDAFX_H__DC17B08B_39B3_11D1_8889_006097BFD99B__INCLUDED_) #define AFX_STDAFX_H__DC17B08B_39B3_11D1_8889_006097BFD99B__INCLUDED_ #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000 #define VC_EXTRALEAN// Exclude rarely-used stuff from Windows headers #include <afxwin.h> // #include <afxext.h> // #include <afxdisp.h> // #ifndef _AFX_NO_AFXCMN_SUPPORT #include <afxcmn.h> // #endif // MFC core and standard components MFC extensions MFC OLE automation classes MFC support for Windows Common Controls _AFX_NO_AFXCMN_SUPPORT // if linking to a custom library configuration // include the configuration headers #include "foundation\config\sfl_MyConfig.h" #include "toolkit\config\ot_MyConfig.h" Chapter 2 Objective Toolkit Quick Start 19 #include <toolkit\secall.h>// Objective Toolkit headers //{{AFX_INSERT_LOCATION}} // Microsoft Visual Studio will insert additional // declarations immediately before the previous line. #endif //!defined(AFX_STDAFX_H__DC17B08B_39B3_11D1_8889_006097BFD99B__INCLUDED_) 6. Click View | Resource Includes… and add the following line to the Read-Only symbol directives list box: #include “toolkit\secres.h” In the same dialog, add the following line to the compile-time directives list box: #include “toolkit\secres.rc” The list boxes should look like the following: 7. Click OK. A message box with a warning appears on the screen. The wording may vary depending on the version of Visual Studio, but the appearance of the warning is standard. 20 Create the dialog box for the control. In the Resources view, open the Dialog folder. It holds IDD_MASKPHONE_DIALOG. Open it and remove the static text that is already there. Replace the text with an Edit box from the Controls palette. Right-click the edit box and open the properties dialog. Name the edit box IDC_PHONE_NUMBER. 8. Add a member variable of SECMaskEdit to the dialog class. Open up the header file for MaskPhoneDlg and look up OnInitDialog(). Add the following line: SECMaskEdit m_editPhone; 9. Open MaskPhoneDlg.cpp, look up OnInitDialog(), and the add the following code to it under the TODO comment. // TODO: Add extra initialization here: m_editPhone.AttachEdit(IDC_PHONE_NUMBER, this); // attach the mask to the edit box m_editPhone.SetMask(_T(“(###)###-#### ext. ####”)); 10. Build the project and see the final dialog. Chapter 2 Objective Toolkit Quick Start 21 2.6 Using Component Headers to Increase Application Build Performance One factor that can significantly affect the build speed of your project is the size of the precompiled header generated for your project. The larger the precompiled header becomes, the slower each source file compiles. Every header file that is included in the precompiled header contributes to its size and increases the time it takes to compile your project. The standard mechanism for including the Objective Toolkit headers into your project is to insert a single line into stdafx.h: #include “toolkit\secall.h” Although this makes integrating Objective Toolkit easy, it can also potentially pull every Objective Toolkit header file into your project. This can result in large precompiled headers and long compile times. Each and every source file included in your project is affected even if you only use a single control. Although this increase in build time may be tolerable in small projects, it is unwieldy for a larger project. One solution is to limit the number of components built into the library with the Objective Toolkit Build Wizard; however, a better solution is to include a number of component-specific header files in place of secall.h. There is a component-specific header file for each component that you can select using the Build Wizard. You can use these headers individually or they can be combined as needed to include only the components that you require. 2.6.1 The Component Headers The component-specific header files are listed below: Table 3 – Component Specific Header Files 22 Component Header file 2D/3D tabbed windows toolkit\ot_tabwnd.h Bitmapped dialog toolkit\ot_bitmapdlg.h Browse edit control toolkit\ot_browedit.h Button classes toolkit\ot_buttons.h Calculator edit control toolkit\ot_calculator.h Calendar control toolkit\ot_calendar.h Color listbox control toolkit\ot_colorlistbox.h Color well classes toolkit\ot_colorwell.h Compressed file class toolkit\ot_compressfile.h Currency edit control toolkit\ot_currency.h Custom status bar toolkit\ot_statusbar.h Table 3 – Component Specific Header Files (Continued) Component Header file Customizable toolbar/menubar toolkit\ot_toolbar.h Date/time edit control toolkit\ot_datetime.h Design patterns framework toolkit\ot_patterns.h DIB image support toolkit\ot_secdib.h Docking windows toolkit\ot_dockingwindows.h Drop edit control toolkit\ot_dropedit.h Editable listbox control toolkit\ot_listboxedit.h Encrypted file class toolkit\ot_encryptfile.h Enhanced ComboBox toolkit\ot_combobox.h File system class toolkit\ot_filesystem.h Floating Document Interface (FDI) toolkit\ot_fdi.h Full Screen View class toolkit\ot_fullscreenview.h GIF image support toolkit\ot_secgif.h Gradient caption classes toolkit\ot_gradientcaption.h JPEG image support toolkit\ot_secjpeg.h Keyboard shortcut classes toolkit\ot_keyshortcut.h Marquee control toolkit\ot_marquee.h Masked edit control toolkit\ot_maskedit.h Multiple Top Level Interface (MTI) toolkit\ot_mti.h PCX image support toolkit\ot_secpcx.h Progress control toolkit\ot_progress.h Random number class toolkit\ot_random.h Registry class toolkit\ot_registry.h Shortcut bar toolkit\ot_shortcutbar.h Splash window classes toolkit\ot_splashwnd.h Targa image support toolkit\ot_sectga.h Thumbnail classes toolkit\ot_thumbnail.h TIFF image support toolkit\ot_sectiff.h Tray icon class toolkit\ot_trayicon.h Chapter 2 Objective Toolkit Quick Start 23 Table 3 – Component Specific Header Files (Continued) Component Header file Tip of the day class toolkit\ot_tipoftheday.h Tree control toolkit\ot_treectrl.h User tools menu class toolkit\ot_usertools.h View classes (pan, zoom) toolkit\ot_views.h Workbook Document Interface (WDI) toolkit\ot_wdi.h Workspace manager toolkit\ot_workspacemgr.h ActiveScript toolkit\ot_activescript.h Advanced docking windows toolkit\ot_advdockingwindows.h Docking views toolkit\ot_dockingviews.h Layout manager toolkit\ot_layoutmgr.h MVC architecture toolkit\ot_mvc.h 2.6.2 To use the component headers in your project You can use the component headers in the same way that you use toolkit\secall.h. 1. Using Build Wizard, build the Objective Toolkit library with the required components. If you are linking to Objective Toolkit as a static library, you do not need to build a special version of the library. You can improve the performance by using the component-specific headers and the default library with all components. If you are linking to Objective Toolkit as a DLL, carefully select the components you build into the library, as this will affect the size of the DLL that you ship. 2. Remove or comment out the line in stdafx.h that includes secall.h. For example: //#include <toolkit\secall.h> Add one or more of the component headers to stdafx.h, depending on which components your application requires. For example: #include <toolkit\ot_dockingwindows.h> // Docking Windows #include <toolkit\ot_maskedit.h> // Masked edit control // alternative to above: if you want to include ALL // Objective Toolkit headers, uncomment the following line to use // the classic Objective Toolkit inclusion method (will increase // build time) //#include <toolkit\secall.h> 24 3. Build your project. If you experience build errors, there are two likely causes: A required component header was not included. The library was built with one of the required components missing. If you are having problems using component headers, revert to using toolkit\secall.h. Chapter 2 Objective Toolkit Quick Start 25 26 Chapter 3 The Objective Toolkit AppWizard 3.1 Overview The Objective Toolkit AppWizard enables developers to generate MFC projects with functional built-in Objective Toolkit options. The AppWizard includes every standard MFC AppWizard step that you need to create a ready-to-build program template in addition to the steps you need to enable the most popular features of Objective Toolkit. These features include: Cool Look toolbar Customizable toolbar Menu Bar Bitmap Menus WorkBook interface Workspace management (persistence) Docking Windows (tree control, 2D and 3D tab windows, Shortcut Bar, or ownerdraw). Docking Views Layout Manager ActiveScript Model View Controller Architecture In addition, the resulting project automatically includes all the necessary Objective Toolkit header and resource files. The Objective Toolkit AppWizard is available for all supported versions of the Visual Studio compiler. Based on the project type (SDI, MDI, or dialog), certain features may or may not be available. Run-Time Type Information (RTTI) is enabled for all projects generated by the AppWizard. Figure 2 shows an example of an AppWizard-generated project with a docked shortcut bar and the workbook interface. Chapter 3 The Objective Toolkit AppWizard 27 Figure 2 – Sample Application Generated Using AppWizard 28 3.2 Creating a Skeleton Application To create a skeleton application using Objective Toolkit AppWizard: 1. In Visual Studio, click File | New, and then select the Projects tab. This opens a list of project types. The Objective Toolkit installation added a new project type called Objective Toolkit AppWizard to your Visual Studio environment. Select this project type and then complete the dialog. Once you finish selecting options in the New Projects dialog, the OK button becomes active. Click this button to start the wizard. Figure 3 – Using the Objective Toolkit AppWizard The first six panels of the AppWizard look like the standard panels for the Win32 Application AppWizard. The Objective Toolkit-specific portions of the AppWizard appear after you complete the Finished panel. Chapter 3 The Objective Toolkit AppWizard 29 Figure 4 – Step 6 of the AppWizard 2. Instead of clicking Finish, click Next to view the remaining panels and choose options specific to Objective Toolkit. Figure 5 – Choosing options specific to Objective Toolkit 30 3. In this step, you can link to the Objective Toolkit libraries as either a static library or as a DLL. By default, you link statically. Choosing the DLL option adds a #define _SECDLL statement to your stdafx.h before #include “secall.h”. Later, if you decide to link statically, remove the #define _SECDLL statement. Select Enable Toolbar Customization to add an SECCustomToolBar with the two-bar gripper as your main toolbar. By selecting Enable Toolbar Customization, you also enable the toolbar manager and customization dialog. A Tools | Customize menu item is added to your application automatically for easy access to customization features. For more information, see Chapter 6, “Customizable Toolbars.” The Menu Bar option adds our dockable menu class, SECMenuBar. This class can be enabled independently of bitmap menus; however, you can also use them together or in a ReBar. Figure 6 – Dockable menu You can enable the rebar control using the standard MFC steps. 4. The next step of the AppWizard brings in more Objective Toolkit features like the tabbed workbook interface and the workspace manager, which can save the positions of all the windows and toolbars in an application. You also have the option of adding a docking window with a selection of child windows to place inside the docking window. Chapter 3 The Objective Toolkit AppWizard 31 32 5. This step is only applicable to Objective Toolkit. In this step, you select an architecture and advanced features for your application. If you want to use Model View Controller with the standard MFC Document/View architecture, check Model View Controller. If you only want to support MVC, you can uncheck the Document View support box in step one of the standard MFC steps. You can use docking views in combination with any other options to provide a convenient mechanism for docking CView derivatives contained in MDI child windows. The Layout Manager is available in dialog-based applications to provide scaling and positioning algorithms for controls in a dialog that are independent of screen resolution. By default, a scale algorithm is applied to the dialog. ActiveScript is integrated in your application as a separate document type. Consequently, this option is only available if you chose an MDI project type. 6. Click Finish. Chapter 3 The Objective Toolkit AppWizard 33 7. Click OK. The AppWizard closes. You can examine the new project in Visual Studio. 8. If you are using Visual Studio 2010, please refer to Section 2.7.4, “Microsoft Visual Studio 2010 Changes,” of the Stingray Studio Getting Started Guide to add property sheet(s) with Stingray Studio paths to the project. 9. Compile and run the application. The skeleton application can now be used as a template for further customization. 34 Chapter 4 Simple Controls 4.1 Overview The simple controls in Objective Toolkit enable you to implement popular GUI controls quickly and easily. 4.2 Browse Edit Controls A browse edit control is a Windows edit control that includes a browse button positioned immediately to its right. A browse button is a push button with the label .... When the user presses the browse button, a modal dialog appears. Figure 7 – Example Browse Edit Control The modal dialog contains values that the user can select to add to the edit field. After the user chooses a value from this dialog, the text appears in the edit field. If the user knows the value, he can type it directly in the edit field without referring to the modal dialog. The purpose of the browse button is to help the user find the value he wants to enter. Chapter 4 Simple Controls 35 4.2.1 Browse Edit Classes The class hierarchy for the browse edit controls is as follows: Figure 8 – Objective Toolkit Browse Edit Class Hierarchy CEdit SECBrowseEditBase SECBrowseFileEdit SECBrowseDirEdit 4.2.1.1 SECBrowseEditBase SECBrowseEditBase is an abstract base class that provides the interface and some of the functionality of a browse edit control. 4.2.1.2 SECBrowseFileEdit The SECBrowseFileEdit class provides the functionality for a Filename Edit control. A filename edit is a browse edit that is suited for entering a filename. With a filename edit, the user can type in a filename directly or he can pick a filename from a dialog. When the user presses the browse button, a modal file selection dialog appears. After the user selects a filename from the dialog, the full filename is automatically entered into the edit field. 4.2.1.3 SECBrowseDirEdit The SECBrowseDirEdit class provides the functionality for a Directory Edit control. A directory edit is a browse edit in which a user can enter a directory name. With a directory edit control, the user can type a directory name directly into an edit field or pick a directory from a dialog. When the user presses the browse button, a modal directory selection dialog appears. 36 Figure 9 – Browse Edit file dialog After the user selects a directory from the dialog, its path is automatically entered into the edit field. 4.2.2 Using the Browse Edit Classes You can use a browse edit class in a dialog or create one as a child of a window. The steps for creating a browse edit control are the same whether you’re using SECBrowseFileEdit or SECBrowseDirEdit. You can only use the SECBrowseFileEdit and SECBrowseDirEdit classes in your applications. SECBrowseEditBase is abstract and cannot be instantiated. 4.2.2.1 To incorporate the SECBrowseEdit classes into your code 1. Use AppStudio to create a dialog template. Drop the edit control on the dialog where you want to place the File or Directory Browse Edit. 2. Create a new dialog class and attach it to the dialog template you just created. 3. Add an SECBrowseFileEdit (or SECBrowseDirEdit) as a member variable to the dialog class you just created. 4. Override the OnInitDialog() member of the dialog class. In your override, call SECBrowseFileEdit::Initialize() or SECBrowseDirEdit::Initialize() and pass the window ID of the edit control you want to convert to a browse edit. The following code below is an example of the override: Chapter 4 Simple Controls 37 BOOL MyDialog::OnInitDialog() { CDialog::OnInitDialog(); #ifndef WIN32 CenterWindow(); #endif m_editFilename.Initialize(IDC_FILENAME, this); m_editPath.Initialize(IDC_PATH, this); .. } 4.2.3 Customizing the Browse Edit Control You can create your own browse edit controls by deriving a class from SECBrowseEditBase and then overriding the OnBrowse() method. The SECBrowseEditBase base class creates and positions the text field and browse button for you. Whenever the user presses the browse button, the OnBrowse() method is automatically called. Your derived class can define the response to a browse button press by overriding the OnBrowse() method and coding your response. For example, you can display one of the common file dialogs. SECBrowseFileEdit and SECBrowseDirEdit are examples of two classes that derive from SECBrowseEditBase. Refer to their implementations for examples of how to do this. 4.3 Browse Edit Sample The Objective Toolkit toolmenu sample in the Samples\Toolkit\MFC\UIExt\toolmenu directory demonstrates the use of the file and directory browse edits in a dialog. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 38 4.4 Button Controls The Objective Toolkit button classes provide useful buttons that have more features and are easier to use than those provided by MFC. The buttons types available are as follows: Figure 10 – Bitmap Buttons Figure 11 – Menu Buttons Figure 12 – Colorwell Buttons Chapter 4 Simple Controls 39 4.5 Button Class Hierarchy The button class hierarchy is as follows: Figure 13 – Objective Toolkit Button Class Hierarchy CButton SECOwnerDrawButton SECBitmapButton SECMenuButton SECWellButton 4.5.1 SECOwnerDrawButton The SECOwnerDrawButton class is an abstract base class that simplifies creating an owner-draw button. To create an owner-draw button, provide a method to draw the face of the button and a focus rectangle on the face of the button. You do not need to consider the state of the button (up, down, disabled, etc.) or draw the borders around the edge of the button. 4.5.2 Using SECOwnerDrawButton You can attach objects instantiated from SECOwnerDrawButton-derived classes to dialog resources, or you can create them dynamically. 4.5.2.1 To attach an SECOwnerDrawButton to a button resource in a dialog 1. Create a derived class from SECOwnerDrawButton. You must provide an implementation for the pure virtual methods DrawFocus() and DrawSpecific(). 2. Create a button resource in the resource editor. The button resource must have the BS_OWNERDRAW style in its Properties dialog. 3. Instantiate an object from the SECOwnerDrawButton-derived class in your dialog. This object must be in scope when the dialog appears. 4. Attach the SECOwnerDrawButton-derived class to the dialog resource using the AttachButton() method. 40 4.5.2.2 To create SECOwnerDrawButtons without using a button dialog resource 1. Create a class derived from SECOwnerDrawButton. You must provide an implementation for the pure virtual methods DrawFocus() and DrawSpecific(). 2. Create a unique control ID for the button. In Visual Studio, you can create a control ID in the Resource Includes dialog. 3. Instantiate an object of the SECOwnerDrawButton-derived class for each button you want to create. 4. Create the button using your SECOwnerDrawButton-derived by calling the Create() method. 4.5.3 Customizing SECOwnerDrawButton The SECOwnerDrawButton class has overridable methods that allow you to customize the drawing of the button. Note that the DrawFocus() and DrawSpecific() are pure virtual methods that must be implemented before any derived class can be instantiated. 4.5.3.1 To extend the SECOwnerDrawButton class DrawFocus(). Must be overridden to draw a focus rectangle on the face of the button. DrawSpecific(). Must be overridden to draw the face of the button. PreDrawButton(). Override this method to perform any initialization of the device context required before any drawing of the button is performed. PostDrawButton(). Override this method to undo any initialization of the device context performed in PreDrawButton(). 4.5.4 SECBitmapButton The SECBitmapButton class encapsulates the behavior of a bitmap button, displaying a bitmap and an optional text caption on the face of the button. 4.5.5 Using SECBitmapButton You can attach objects instantiated from the SECBitmapButton to dialog resources or create them dynamically. 4.5.5.1 To attach an SECBitmap Button to a dialog resource 1. Create a button resource in the resource editor. The button resource must have the BS_OWNERDRAW style set in its Properties dialog. Chapter 4 Simple Controls 41 2. Instantiate an SECBitmapButton object in your dialog class. This object must be in scope when the dialog is displayed. 3. Attach the SECBitmapButton class to the dialog resource using the AttachButton() method. 4.5.5.2 To create SECBitmapButtons without using a button dialog resource 1. Create a unique control ID for each button you want. In Visual Studio, you can create a control ID with the Resource Includes dialog in Visual Studio. 2. For each button you want to create, instantiate an SECBitmapButton object. 3. Create the button by calling the Create() method. 4.5.6 SECBitmapButton alignment The placement of the bitmap and a caption (if present) is specified during initialization of the SECBitmapButton using either the AttachButton() or Create() method. There are five alignment modes for the placement of the bitmap and the placement of the caption. Table 4 – Alignment Mode Flags Alignment mode flag Alignment of bitmap and caption SECBitmapButton::Al_Left Bitmap on left, caption on right SECBitmapButton::Al_Right Bitmap on right, caption on left SECBitmapButton::Al_Top Bitmap on top, caption underneath SECBitmapButton::Al_Bottom Bitmap underneath, caption on top SECBitmapButton::Al_Center Bitmap in center of button, no caption. 4.5.7 SECMenuButton The SECMenuButton class provides a simple button that displays a pop-up menu when you click it. The application can display this pop-up menu either to the right or below the button. 4.5.8 Using SECMenuButton You can attach objects instantiated from the SECMenuButton class to dialog resources or create them dynamically. 42 4.5.8.1 To attach an SECMenuButton to a dialog resource 1. Create a button resource in the resource editor. The button resource must have the BS_OWNERDRAW style set in the button resource Properties. 2. Instantiate an SECMenuButton object in your dialog class. This object must be in scope when the dialog is displayed. 3. Attach the SECMenuButton class to the dialog resource using the AttachButton() method. Specify a menu handle and the placement of the menu through parameters, or call the SetMenu() and SetDirection() methods for altering the menu and its placement dynamically. 4.5.8.2 To create SECMenuButtons without using a button dialog resource 1. Create an unique control ID for each button you want. In Visual Studio, you can create a control ID in the Resource Includes dialog. 2. For each button you want to create, instantiate an SECMenuButton object. 3. Create the button by calling the Create() method. Specify a menu handle and the placement of the menu through parameters, or call the SetMenu() and SetDirection() methods for altering the menu and its placement dynamically. 4.5.9 SECMenuButton Menu Placement You can specify the placement of the menu with respect to the button using direction flags, which are parameters to the AttachButton(), Create(), or SetDirection() methods. These flags are as follows. Table 5 – Direction Flags Direction flag Placement of menu SECMenuButton::DT_Down Menu drops down from the button SECMenuButton::DT_Right Menu appears to the right of the button 4.5.10 SECWellButton The SECWellButton class includes a Color Selection button. The face of the button displays the currently selected color. When the user clicks the button, a color palette (or color well) appears below the button. The user can select a color by clicking it. The SECWellButton class sends a CWN_COLOR_CHANGE message to its parent window when you select a new color. If you are using AttachButton(), ensure that the original BS_OWNERDRAW button style is defined. Chapter 4 Simple Controls 43 4.5.11 Using SECWellButton You can attach objects instantiated from the SECWellButton class to dialog resources, or create them dynamically. 4.5.11.1To attach an SECWellButton to a dialog resource 1. Create a button resource in the resource editor. The button resource must have the BS_OWNERDRAW style set in the button resource Properties. 2. Instantiate an SECWellButton object in your dialog class. This object must be in scope when the dialog is displayed. 3. Attach the SECWellButton class to the button resource using the AttachButton() method. 4. Use the SetColor() and GetColor() methods to set and obtain the currently selected color in the control. You can also use the DDX_Color() function as part of a DoDataExchange() method. 4.5.11.2To create SECWellButtons without using a button dialog resource 1. Create a unique control ID for each button you need. In Visual Studio, you can create a control ID in the Resource Includes dialog. 2. For each button you want to create, instantiate an SECWellButton object. 3. Create the button by calling the Create() method. 4. Use the SetColor() and GetColor() methods to set and obtain the currently selected color in the control. You can also use the DDX_Color() function as part of a DoDataExchange() method. 4.5.12 Customizing SECWellButton The SECWellButton provides the following virtual methods so you can customize its behavior: SetOtherButton(). Enable or disable the button that enables a user to select from more than the twenty system colors. When this button is clicked, a common Color Selection dialog is displayed. 44 SetPaletteRealization(). Enable or disable palette realization of the currently selected color before drawing. SetPopup(). Enable a non-standard pop-up color palette (SECPopupColorWell). GetColorDialogFlags(). Override to return the flags for displaying the common color selection dialog when the Other button is clicked. Refer to the Objective Toolkit Class Reference for more information on the SECWellButton::SetOtherButton() method. 4.6 Calculator Control Objective Toolkit provides a calculator control that the user can use to perform basic arithmetic. You can use the calculator control independently or in association with a drop edit control. Figure 14 – Objective Toolkit Calculator Class Hierarchy CWnd SECCalculator SECPopupCalculator 4.6.1 SECCalculator The SECCalculator class implements a calculator edit control capable of decimal arithmetic and other standard operations. The calculator queries the current locale information for formatting the numerical output and determines the decimal character to display on the decimal button. Figure 15 – Example of SECCalculator Chapter 4 Simple Controls 45 4.6.2 SECPopupCalculator The SECPopupCalculator class creates a pop-up window for the calculator that is destroyed whenever the < = > key is pressed. Figure 16 – Example using SECPopupCalculator with SECDropEdit 4.6.3 Using SECCalculator After instantiating an SECCalculator object, the application can create the control dynamically by calling the Create() method. For example: m_calc.Create(WS_CHILD|WS_VISIBLE|WS_BORDER| WS_TABSTOP, sz.cx, sz.cy, this, 200); It is important that the SECCalculator object have sufficient scope so that it exists as long as the control exists. To set and query the value currently displayed by the calculator, use the SetValue() and GetValue() methods. You can control and query the number of decimal places displayed with the SetDecimalPlaces() and GetDecimalPlaces() methods. You can reset the calculator control with the Reset() method. 4.6.4 Customizing SECCalculator You can customize the behavior of the SECCalculator class with the following virtual methods: CreateBtns(). Creates the calculator’s buttons. CreatePanel(). Creates the calculator’s LCD panel. HandleEvent(). Handles events related to the calculator’s operations (add, subtract, etc.). 46 LoadDecSeparator(). Override to change the decimal separator displayed. 4.6.5 Calculator Sample The calc sample project (in Samples\Toolkit\MFC\Controls\calc) demonstrates the use of an SECCalculator control in a dialog. It demonstrates how to use a class derived from SECDropEdit that creates an SECPopupCalculator. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 4 Simple Controls 47 4.7 Calendar Control SECCalendar implements a standard calendar control for date entry and date representation. You can invoke methods to highlight and select days on the calendar. You can embed an SECCalendar instance into a dialog or implement it as a pop-up control through the derived class SECPopupCalendar. SECPopupCalendar allows the user to view the calendar control as a dropdown window, which saves screen space. The calendar class hierarchy is as follows: Figure 17 – Objective Toolkit Calendar Class Hierarchy CWnd SECCalendar SECPopupCalendar Figure 18 – SECCalendar Display 4.7.1 To incorporate the SECCalendar class into your code 1. Create an instance of SECCalendar or SECPopupCalendar. 2. Call the SetPage() method to display the appropriate month for a given date. For 32-bit versions of Objective Toolkit, pass in either a COleDateTime reference or a CTime reference. For example: 48 COleDateTime date = COleDateTime::GetCurrentTime(); // bRedraw MUST be FALSE when presetting a page. m_theCalendar.SetPage(date, FALSE); The CTime class encapsulates the run-time time_t data type. it represents absolute time values only in the range January 1, 1970 to January 18, 2038, inclusive. 3. Call the SetBehaviorMode() and SetDrawMode() methods. For example: long lBehaMode = SECBM_DEFAULT_DIALOG_BEHAVIOR; long lDrawMode = SECDM_DEFAULT_DIALOG_DRAW; m_theCalendar.SetBehaviorMode(lBehaMode); m_theCalendar.SetDrawMode(lDrawMode); You can override these methods to customize the behavior mode and drawing mode. 4. Call the Create() method. For example: m_theCalendar.Create( WS_VISIBLE|WS_BORDER, rect, this, IDC_CALENDAR_FRAME ); If you are using SECPopupCalendar, WS_POPUP is a valid window style. 4.7.2 SECCalendar Key Methods SetDrawMode(). Sets the drawing mode of the calendar with the flags specified in the following table. Table 6 – Drawing Mode Flags for SECCalendar Draw Flag Description SECDM_FULL_MONTH_NAMES Uses full month names. SECDM_FULL_DAY_NAMES Uses full day names. SetBehaviorMode(). Sets the behavior mode of the calendar with the flags specified in the following table: Table 7 – Behavior Mode Flags for SECCalendar Behavior Flag Description SECBM_AUTOSIZE_FONT Automatically scales the font to fit the current size of the control. SECBM_AUTOSIZE_RECT Automatically scales the calendar’s bounding rectangle to fit the parent window. SECBM_SINGLE_DATE Allows the user to select a single date. SECBM_MONTH_BUTTONS Displays the buttons to scroll months. Chapter 4 Simple Controls 49 Table 7 – Behavior Mode Flags for SECCalendar (Continued) Behavior Flag Description SECBM_YEAR_BUTTONS Displays the buttons to scroll years. SECBM_AUTO_HIDE Hides the calendar automatically when the user is done. SECBM_KEYBOARD_CONTROL Allows navigation of the calendar with the keyboard. SECBM_MONTH_FROZEN Freezes the currently displayed month. SetPage(). Sets the displayed calendar page based on the CTime or COleDateTime parameter passed in. HighlightDate(). Sets the highlighted mode of the specified date. Multiple dates can be highlighted at one time. SelectDate(). Selects the date based on the CTime or COleDateTime parameter passed in. The user can only select one date at any given time. ToggleSelectDate(). Toggles the selected mode of the specified date. ToggleHighlightDate(). Toggles the highlighted mode of the specified date. Multiple dates can be highlighted at one time. AdvanceDay(), AdvanceWeek(), AdvanceMonth(), AdvanceYear(). Advances the calendar by the given amount. RetreatDay(), RetreatWeek(), RetreatMonth(), RetreatYear(). Retreats the calendar by the given amount. 4.7.3 Customizing SECCalendar Most of the SECCalendar operation methods (AdvanceDay(), AdvanceWeek(), AdvanceMonth(), and more) are virtual functions that you can override to add custom behavior to a derived class. In addition, you can override the InitColors() method to specify colors for the various parts of the calendar. The default implementation initializes the colors based on the current system colors. 4.7.4 SECCalendar Sample The use of SECCalendar and SECPopupCalendar is demonstrated in the caltest sample in the Samples\Toolkit\MFC\Controls\calendar directory. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 50 4.8 Color Well Control The Objective Toolkit Color Well classes provide a sophisticated Color Selection control. This control displays the 20-system colors in a five by four grid. Figure 19 – Color Well Control Figure 20 – Color Well Control with Other Button The user can select a color by clicking a cell in the grid. The user can select from a wider range of colors by clicking the Other button. Figure 21 – Common Color Selection Dialog The hierarchy diagram for the Objective Toolkit color well classes is as follows: Chapter 4 Simple Controls 51 Figure 22 – Objective Toolkit Color Well Class Hierarchy CWnd SECColorWell SECPopupColorWell 4.8.1 SECColorWell The SECColorWell class is a CWnd derivative designed for embedding in a dialog window or a CFormView. The color well automatically sizes to fit its content, and it can have a 3D-raised border. SECColorWell also supports palette realization of the selected color. In other words, it maps entries from the current logical palette to the system palette. The SECColorWell sends a CWN_COLOR_CHANGE message to its parent window when a new color is selected. 4.8.1.1 Using SECColorWell To incorporate SECColorWell into your code: 1. Create the SECColorWell window using the Create() method. Remember to specify whether the control should have an Other button. If you want to embed the color well in a dialog, specify the initial x and y coordinates in dialog base units. 2. Set the initially selected color with the SetColor() method. 3. Enable or disable palette realization of the colors before drawing with the SetPaletteRealization() method. 4. Enable or disable the ability to select a color by passing the mouse over a cell with the SetMouseTracking() method. 5. Call GetColor() to obtain the currently selected color. 6. After you create the SECColorWell window, you can use the DDX_Color() function as part of a DoDataExchange() method. 52 4.8.1.2 Customizing SECColorWell You can customize the SECColorWell class with the following virtual methods: Table 8 – Virtual Methods for SECColorWell Method Description DrawCell() Override to customize the drawing of an unselected cell. DrawSelectedCell() Override to customize the drawing of a selected cell. SetGridSize() Override to change the size of the grid. Set the m_nCols and m_nRows member variables. InitAdditionalColors() Override in conjunction with SetGridSize() to initialize any extra color cells defined in the grid. HasFocusRectangle() Override to define whether the color well has a focus rectangle, an extra 1-pixel-wide border drawn around the edge of the window, when it has focus. GetColorDialogFlags() Override to specify the flags with which the common color selection dialog is opened when the Other button is clicked. 4.8.2 SECPopupColorWell The SECPopupColorWell class works with the SECWellButton class. It provides a pop-up color selection window. After the application creates the window, it self-destructs when it loses focus or a color is selected. When the user selects a new color or clicks the Other button, the SECWellButton sends CWN_COLOR_CHANGE and CWN_CUSTOM_COLOR messages to a specified window. 4.8.2.1 To incorporate SECPopupColorWell into your code 1. Create the pop-up color well using the Create() method. 2. Set the window to receive color change notifications using the SetNotifyWindow() method. 3. Set any other required options as you would for an SECColorWell window. Chapter 4 Simple Controls 53 4.8.3 ColorWell Sample The use of SECColorWell and SECPopupColorWell is demonstrated in the Objective Toolkit COLOR sample, located at Samples\Toolkit\MFC\Controls\colrwell. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 54 4.9 Currency Edit Control The Objective Toolkit currency edit control classes provide a specialized edit control for the display and entry of currency data. The control includes a bitmap button for dropping down a calculator. Figure 23 – Objective Toolkit’s Currency Edit Class Hierarchy CWnd SECDropEdit SECCurrencyEdit 4.9.1 SECDropEdit The SECDropEdit class adds a combo-like drop-down button to an edit control. SECCurrencyEdit uses the special SECDropEdit capabilities to display a bitmap button that activates a calculator to help in the data entry. 4.9.2 SECCurrencyEdit SECCurrencyEdit provides an extensible class for entering and displaying custom-formatted currency data. In addition, it includes a context menu and an optional bitmap button. The methods of the Format class, a helper class, provide a comprehensive set of custom formatting options. If you need implement behavior that is not addressed by Format’s methods, you can customize the input data parsing and output display formatting by descending new classes from SECCurrencyEdit::Format and SECCurrencyEdit. 4.9.3 Using SECCurrencyEdit To attach an SECBitmap Button to a dialog resource: 1. Add an edit control to your dialog using the resource editor. 2. Instantiate an SECCurrencyEdit object in your dialog class. This object must be in scope when the dialog is displayed. 3. In OnInitDialog(), replace the edit control with your SECCurrencyEdit object by calling the Initialize() method. 4. Create and configure an SECCurrencyEdit::Format object to specify formatting options. Call SetFormat() to start using the new formatting information. Use GetValue() and SetValue() to obtain and recover the current value in the control. You can also use the DDX_Currency() method in your DoDataExchange() function. Call SetBitmap(IDB_CALC) to display the calculator drop-down button. Chapter 4 Simple Controls 55 4.9.4 SECCurrencyEdit::Format Format is a nested helper class that provides the core currency formatting and parsing methods used by an SECCurrencyEdit control. As the following list of methods suggests, the Format class provides extensive control over the appearance of the currency data. void EnableLeadingZero(BOOL b); void EnableDecimalSeparatorLine(BOOL b); void SetMonetarySymbol(LPCTSTR p); void SetThousandSeparator(TCHAR c); void EnableLeadingThousSeparator (BOOL b); void SetDecimalSeparator(TCHAR c); BOOL SetGrouping(LPCTSTR x); BOOL SetPaddingCharacter(TCHAR c); void SetPositiveFormat(int i); void SetNegativeFormat(int i); void SetDecimalDigits(int i); void SetFractionalDigits(int i); void SetBackgroundColor(COLORREF cr); void SetNegativeColor(COLORREF cr); void SetPositiveColor(COLORREF cr); void SetDecimalSeparatorLineColor(COLORREF cr); Consider the following facts before you use the currency display options: 56 Setting the thousands separator to the null character (‘\0’) prevents its use. With EnableLeadingThousSeparator, the thousands separator will present ‘5555555’ as ‘$ , 5,555,555.00’ as opposed to ‘$ 5,555,555.00’. When you set decimal digits to negative one (-1) the application uses every digit that is necessary to display the number. If the number of decimal digits is greater than is required, the output is padded with the padding character. You can use the values in the table below with the SetNegativeFormat() and SetPositiveFormat() methods. These values are taken directly from Microsoft’s documentation regarding the international section of WIN.INI. Negative Format Positive Format 0 ($1) 0 $1 1 -$1 1 1$ 2 $-1 2 $1 Negative Format Positive Format 3 $1- 3 4 (1$) 5 -1$ 6 1-$ 7 1$- 8 -1 $ 9 -$ 1 10 $ 1- 1$ The ParseValue() and FormatValue() methods convert between a numeric and a string representation. If you need to use a format that is not supported by the basic Format class, derive your own Format class and override those methods. Then, derive your own version of SECCurrencyEdit and override its CreateFormatObject() method to provide an object of your descendant class. For example, the following code: SECCurrencyEdit m_Edit; // fmt(TRUE) will load the default system settings. SECCurrencyEdit::Format fmt(FALSE); fmt.EnableLeadingZero(TRUE); fmt.EnableDecimalSeparatorLine(FALSE); fmt.SetMonetarySymbol(_T("$")); fmt.SetThousandSeparator(_T(',')); fmt.SetDecimalSeparator(_T('.')); fmt.SetGrouping(_T("3;0")); // 3 digits per group repeated. fmt.SetPaddingCharacter(_T(' ')); fmt.SetPositiveFormat(0); fmt.SetNegativeFormat(0); fmt.SetDecimalDigits(10); fmt.SetFractionalDigits(2); fmt.SetNegativeColor(RGB(255,0,0)); fmt.SetPositiveColor(RGB(0,0,255)); m_Edit.SetFormat(fmt); will present "5555555" as "$ , 5,555,555.00" in blue color; and "-5555555" as "($ , 5,555,555.00)" in red color. 4.9.5 SECCurrencyEdit Messages The SECCurrencyEdit class supports some of the WM_* windows messages and EM_* edit control messages. Chapter 4 Simple Controls 57 The supported messages are as follows: Table 9 – Windows Messages Supported by SECCurrencyEdit Windows Message Description WM_COPY Copies the current selection to the clipboard. WM_CUT Deletes or cuts the current selection, if any, in the control and then copies the deleted text to the clipboard. WM_GETFONT Retrieves the font with which the control is currently drawing its text. WM_PASTE Copies the current content on the clipboard to the control. WM_SETFONT Specifies the font that the control uses when drawing text. WM_SETREDRAW Allows changes in the control to be redrawn or prevents changes in the control from being redrawn. WM_SETTEXT Sets the text of the control. WM_UNDO Undoes the last operation. Table 10 – Edit Control Messages Supported by SECCurrencyEdit Edit Control Message Description EM_CANUNDO Determines whether the user can undo an operation. EM_EMPTYUNDOBUFFER Resets the undo flag of the control. EM_GETSEL Gets the starting and ending character positions of the current selection in the control. EM_SETREADONLY Sets or removes the read-only style (ES_READONLY) of the control. EM_SETSEL Selects a range of characters in the control. EM_UNDO Undoes the last edit control operation. 4.9.6 SECCurrencyEdit Sample The use of SECCurrencyEdit and its supporting classes is demonstrated in the Objective Toolkitcurrency sample in the Samples\Toolkit\MFC\Controls\currency directory. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 58 4.10 Date/Time Edit Control The date/time edit control implements an edit field that allows users to edit date information in various display formats using either keyboard or mouse with optional spin buttons. The date is internally represented with a COleDateTime object, limiting it to 32-bit environments. Some features of the date time control include: Default and customizable display formats Null date entry mode Fast entry mode (automatic cursor advancement as data is entered) Minimum/maximum range restriction Spinner buttons Popup calendar Y2K compliance Figure 24 – Example Date Time Control with Default Display Formats Figure 25 – Example Date Time Control with a Custom Display Format The SECDateTimeCtrl class implements the date/time control. The entry field is divided into a number of subfields, depending on the specified format. Each subfield or component is controlled by a gadget class specialized for the input and display of that particular subfield. Each gadget class is based on SECDTGadget. Figure 26 – The Date/time Control Class Hierarchy CWnd SECDateTimeCtrl CObject SECDTGadget Chapter 4 Simple Controls 59 4.10.1 SECDateTimeCtrl SECDateTimeCtrl is a date/time editing control class. SECDateTimeCtrl manages several gadget objects to display the data in a specified format. 4.10.1.1SECDateTimeCtrl styles You can set the following extended styles when the control is: Created. Attached to an edit control in a dialog resource using AttachDateTimeCtrl(). You can set the styles with the Create() or CreateEx() methods. The Y2K styles determine which century is added to a two-digit year. Table 11 – Extended Styles for SECDateTimeCtrl SEC_DTS_CALENDAR Adds calendar drop-down button. SEC_DTS_UPDOWN Adds spinner control. SEC_DTS_Y2K_NOFIX Ignores Y2K fix (for backward compatibility). The current century is used to expand two-digit years.If not defined, the century is chosen so that the resulting date is within 50 years of either the current date or the date previously stored in the control, depending on SEC_DTS_Y2K_CONTEXT. SEC_DTS_Y2K_CONTEXT If SEC_DTS_Y2K_NOFIX is not defined, the century is based on the date previously stored in the control. 4.10.1.2SECDateTimeCtrl messages SECDateTimeCtrl only supports the following edit control messages. SECDTN_CHANGED or EN_CHANGE SECDTN_KILLFOCUS or EN_KILLFOCUS SECDTN_SETFOCUS or EN_SETFOCUS 4.10.2 SECDTGadget SECDTGadget is an abstract base class that handles the input and display of data in the subfields of a date/time control. SECDateTimeCtrl uses several subclasses of SECDTGadget including: 60 SECDTStaticGadget. Implements a non-editable text gadget. SECDTNumericGadget. Implements an editable numeric gadget. SECDTListGadget. Implements a text gadget with a defined list of possible strings. SECDTButtonGadget. Implements a simple push button gadget. SECDTSpinGadget. Implements a spin button gadget. 4.10.3 Date Formats SECDataTimeCtrl’s SetFormat() method specifies how the date or time data is displayed. The format determines which subfields or gadgets are shown. Several predefined format types are available. In addition, you can specify custom formats using codes in a user-defined string. 4.10.3.1Predefined Format Types The table below lists the format types that the application can pass as a parameter to SetFormat(). To specify a custom format, the user-defined string is passed to SetFormat(); don’t pass SECDateTimeCtrl::Custom. Table 12 – Supported Format Types for SetFormat() Format type Description Example SECDateTimeCtrl::Time Locale time format 11:54 SECDateTimeCtrl::ShortDate Locale short date format 7/21/98 SECDateTimeCtrl::LongDate Locale long date format Tuesday July 21, 1998 SECDateTimeCtrl::Custom A user supplied date/time format string July 21, 1998 (Tuesday) 4.10.3.2Format Strings You can use the codes in the following table to build a custom format string. Table 13 – Format Strings Supported by SECDateTimeCtrl Format string Description h Hours, 12 hour format, no leading zero hh Hours, 12-hour format with leading zero H Hours, 24-hour format, no leading zero HH Hours, 24-hour format with leading zero m Minutes, no leading zero mm Minutes with leading zero s Seconds, no leading zero Chapter 4 Simple Controls 61 Table 13 – Format Strings Supported by SECDateTimeCtrl (Continued) Format string Description ss Seconds with leading zero t Abbreviated AM/PM designator tt Full AM/PM designator d Numeric day dd Numeric day with leading zero ddd Abbreviated day name dddd Full day name M Month MM Month with leading zero MMM Abbreviated month MMMM Full month name y Year without century yy Year with leading zero, without century yyyy Year including century gg Era (ignored) ‘text' Quoted text passed straight through 4.10.4 Null Data Entry Mode SECDateTimeCtrl supports a null date entry mode. This mode allows you to enter date time information in an empty control. You can specify a character to fill the fields of the control so that the user can see them. The following figure is an example date time control that has been put into null date entry mode. Figure 27 – Example Date Time Controls in Null Data Entry Mode The user can now begin entering data. The control remains in null entry mode until all of the required fields are completed. 62 Figure 28 – Using Date Time Controls in Null Data Entry Mode When not in null date entry mode, GetDateTime() always returns a COleDateTime object containing the currently displayed date/time. When the application is in null date entry mode, GetDateTime() returns one of the following: A COleDateTime object with a status of COleDateTime::null if the date is incomplete. A COleDateTime object with a status of COleDateTime::invalid if the date is complete, but invalid (out of range). A COleDateTime object containing the entered date/time. 4.10.5 Using SECDateTimeCtrl The following sections give tips on using the SECDateTimeCtrl class. To use the date/time control in a dialog: 1. Create an edit control on a dialog resource in the resource editor. 2. Add a data member to the dialog class for the date time control. For example: SECDateTimeCtrl m_dateCtrl; 3. In the OnInitDialog() method of the dialog, set the display format for the date time object with the SetFormat() method. For example: m_dateCtrl.SetFormat(SECDateTimeCtrl::Time); 4. After setting the display format, attach the resource edit control to the date time object with the AttachDateTimeCtrl() method. For example: VERIFY(m_dateCtrl.AttachDateTimeCtrl(IDC_TIME, this, SEC_DTS_UPDOWN)); To set the date or time for the control: Call the SetDateTime(), SetDate(), or SetTime() methods. For example: m_dateCtrl.SetDateTime(minDate); To change a date/time control to the null date entry mode: Call the SetNull() method. This method accepts a character that is used to blank out the control. For example: m_dateCtrl.SetNull('_'); Chapter 4 Simple Controls 63 To make the control always remain in the null date entry mode: m_dateCtrl.SetNull('_', TRUE); To clear the control using the delete key: 1. Derive a class from SECDateTimeCtrl. 2. Override the OnKeyDown() method. In this override, test for the VK_DELETE key, and if detected, call the SetNull() method. For example: void CMyDateTimeCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default SECDateTimeCtrl::OnKeyDown(nChar, nRepCnt, nFlags); if (VK_DELETE == nChar) { SetNull('_'); } } To set focus back to the first gadget of the control: 1. Derive a class from SECDateTimeCtrl. 2. Create a method, or in a method override, use the following code to locate the first gadget index: // Find first gadget int nGadget; for(nGadget = m_nFixed; nGadget < m_gadgets.GetSize() && !(m_gadgets[nGadget]->GetStyle() & SECDTGadget::WantFocus); nGadget++); 3. When you locate the first gadget index, call the Enable() method on the gadget, and then call the BringIntoView() method. For example: if(nGadget >= m_nFixed && nGadget < m_gadgets.GetSize()) { m_gadgets[m_nCurGadget = nGadget]->Enable(TRUE); BringIntoView(m_nCurGadget); } To change the color of the text in the control: 1. In the parent window, override OnCtlColor() and set the text and background colors using the SetBkColor() and SetTextColor() methods. For example: HBRUSH CDatetimeDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CPropertyPage::OnCtlColor(pDC, pWnd, nCtlColor); if (pWnd-> IsKindOf(RUNTIME_CLASS(SECDateTimeCtrl)) && nCtlColor == CTLCOLOR_EDIT) 64 { //pDC->SetBkMode(OPAQUE); pDC->SetBkColor(RGB(75,75,255)); pDC->SetTextColor(RGB(255,255,0)); } // for all other windows, use the default brush from the // base class else hbr = CPropertyPage::OnCtlColor(pDC, pWnd, nCtlColor); return hbr; } 4.10.6 Date/Time Edit Control Sample See the datetime sample in the Samples\Toolkit\MFC\Controls\datetime directory for a demonstration of how to use the SECDateTimeCtrl classes. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 4 Simple Controls 65 4.11 List Box Edit Control An editable list box is a Windows list box that contains items that the user can modify. For example, the user can modify the order of the list box items, create and delete items, and edit the content of individual items. The editable list box is similar to Visual Studio’s Include and Library path editors, which is accessible by selecting Options from the Tools menu. Figure 29 – Objective Toolkit Editable Listbox Class Hierarchy CWnd SECListBoxEditor SECListBoxFileEditor SECListBoxDirEditor There are two ways to edit a list box item. You can either edit in-place by selecting the text of the item and typing or, if the class supports browsing, you can browse for a value by pressing the browse button to the right of the item. A browse button is a push button labeled with ellipses (...), which is only visible after the user double-clicks an item. When the browse button is pressed, a dialog appears to allow the user to choose from a list or tree of possible values. After the user selects a new value and closes the dialog, the selected item is automatically replaced. To support the creation, deletion, and reordering of list box items, SECListBoxEditor adds several child controls, known as command buttons, above the list box. Figure 30 – Editable List Box Command Buttons The command buttons and their actions are as follows: Table 14 – Command Buttons for SECListBoxEditor 66 Click the: To: New Button Create a new list box item at the end of the list. Delete Button Remove the selected list box item. Move-Up Button Move up selected list box item one place. Move-Down Button Move down selected list box item one place. 4.11.1 SECListBoxEditor The SECListBoxEditor class implements an editable list box that supports item creation, deletion, reordering, and in-place text editing. Figure 31 – Example SECListBoxEditor The SECListBoxEditor does not support browsing. If you need to support browsing, derive a class from SECListBoxEditor and override its OnBrowse() method. See Section 4.11.6 later in this chapter for more information. 4.11.2 SECListBoxFileEditor The SECListBoxFileEditor class is an editable list box that is specialized for entering a list of filenames. An SECListBoxFileEditor object allows users to enter a filename by typing it or by selecting it from a file selection dialog. SECListBoxFileEditor inherits most of its functionality from SECListBoxEditor. In fact, its only method is an override of OnBrowse(), which launches a file selection dialog when the user presses a browse button. 4.11.3 SECListBoxDirEditor The SECListBoxDirEditor class is an editable list box that is specialized for entering a list of directories. Figure 32 – Example SECListBoxDirEditor Chapter 4 Simple Controls 67 An SECListBoxDirEditor object allows you to enter a directory name by typing it or by selecting it from a directory selection dialog. SECListBoxDirEditor inherits most of its functionality from SECListBoxEditor. In fact, its only method is an override of OnBrowse(), which launches a directory selection dialog when a browse button is pressed. 4.11.4 Using the List Box Edit Classes The SECListBoxEditor-based classes are intended to replace existing list box controls in a parent window. Typically, the parent window is a dialog box, but the parent can be any CWnd with a list box control as a child. The steps for creating a list box edit control are the same whether you are using SECListBoxEditor, SECListBoxFileEditor, or SECListBoxDirEditor. To incorporate an editable list box into a dialog window: 1. Use the resource editor to create a dialog template. Drop a list box control on the dialog. 2. Create a new dialog class and attach it to the dialog template created in the last step. 3. Add a member variable of type SECListBoxEditor to the class created in the last step. 4. Override the OnInitDialog() member of the dialog you created earlier. In your override, call SECListBoxEditor::Initialize() and pass the window ID of the list box control you want to convert to an editable list box. For example: BOOL MyDialogBox::OnInitDialog() { CDialog::OnInitDialog(); m_LBEditor.Initialize(this, IDC_LIST); m_LBEditor.SetWindowText("My Editable List Box:"); .. } After the control is initialized, use the GetListBoxPtr() method to obtain a pointer to the CListBox-based helper object for manipulating and querying the list box control. To incorporate an editable list box into an arbitrary CWnd: 1. Construct a CListBox object and then create the control in the parent window with the Create() method, assigning it a unique control ID. 2. After the list box control is created, construct an SECListBoxEditor object and call the Initialize() method, using the control ID you just created. It is important that both the CListBox and SECListBoxEditor objects have sufficient scope to exist while the control lives in the parent window. 4.11.5 Customizing the List Box Edit Classes The command buttons are optional. A style flag passed into the initialization of the editable list box controls their creation. These style flags allow you to select editing features for the list box. 68 The style flags and their definitions are as follows. Table 15 – Style Flags for the List Box Edit Classes Style Flag Definition LBE_BROWSE Editable list box has a browse button for each item. LBE_AUTOEDIT Alphanumeric key starts in-place edit of selected item. LBE_BUTTONS Enable the command buttons. The presence of the individual buttons is determined by the next four styles. LBE_NEWBUTTON Displays new pushbutton. LBE_DELBUTTON Display delete pushbutton. LBE_UPBUTTON Display up pushbutton. LBE_DOWNBUTTON Display Down pushbutton. LBE_TOOLTIPS Supply tool tips for the command buttons. LBE_SHORTCUTS Allow keyboard shortcuts for New, Delete, Move, etc. LBE_DRAGNDROP Allow items to be drag-and-drop to and from the list. LBE_NOTRAILBLANK Does not add the trailing blank item. LBE_DEFAULT Combination of all of the preceding style flags. Use these style flags with the SECListBoxEditor::Initialize() method by passing a style flag to its third parameter. Note that the styles for displaying the individual command buttons (LBE_NEWBUTTON, etc.) must be combined with LBE_BUTTONS. 4.11.6 Extending the Editable List Box Classes The SECListBoxEditor and SECListBoxEditor-derived classes have a number of virtual methods that you can override to modify the standard behaviors. Some of the virtual callbacks provided by these classes are as follows: Table 16 – Virtual Callbacks for Edit List Box Classes OnEditingJustStarted() Called when editing has started. OnEditingStopped() Called when editing has stopped. OnItemRenamed() Called after an item has been renamed. OnItemAdded() Called after an item has been added. Chapter 4 Simple Controls 69 Table 16 – Virtual Callbacks for Edit List Box Classes (Continued) OnItemMoved() Called after an item has been moved. OnItemDelete() Called before an item is deleted. The SECListBoxEditor class implements an editable list box into which the user can enter arbitrary text. It does not support browsing. To support browsing, the control must understand the nature of the text contained in the list box. This is why the SECListBoxFileEditor and SECListBoxDirEditor classes exist. These classes inherit the functionality of an editable list box from SECListBoxEditor and add the ability to browse for a file or directory name. If you need to create an editable list box that supports browsing, derive a class from SECListBoxEditor and override its OnBrowse() method. Typically, an overridden OnBrowse() method opens an application-specific modal dialog, accepts the user’s choice and writes the new value back to the selected list box item. Refer to the implementations of OnBrowse() in SECListBoxDirEditor and SECListBoxFileEditor for examples of how to do this. 4.11.7 Editable List Box Sample The Objective Toolkit LBEdit sample in the Samples\Toolkit\MFC\Controls\LBEdit directory shows how to use SECListBoxEditor and SECListBoxDirEditor in a dialog. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 70 4.12 Marquee Control The SECMarquee implements a marquee control that scrolls text across its window. You can configure the fonts, colors, scrolling speed, and wrapping mode. In addition, you can easily change them at run time. Because it is CWnd-derived, you can embed SECMarquee in a view or dialog like any other control. Figure 33 – SECMarquee Window Embedded in an SECStatusBar Figure 34 – Objective Toolkit Marquee Class Hierarchy CStatic SECMarquee 4.12.1 Using SECMarquee You can use objects instantiated from SECMarquee anywhere a CStatic can be used. You can attach the object to an existing CStatic control via the AttachStatic() method or create it dynamically via the Create() method. To attach an SECMarquee instance to an existing CStatic instance: 1. Call the AttachStatic() method with the control ID of the static control to attach the SECMarquee instance. For example: m_wndMarquee.AttachStatic(IDC_MARQUEE_STATIC,this); To create an SECMarquee instance dynamically: 1. Create a unique control ID for the control. In Visual Studio, you can create a control ID in the Resource Includes dialog. 2. Call the Create() method. For example: Rect rect(0,0,100,50); // initial rectangle m_wndMarquee.Create(rect,this,strMarqueeText); To start and stop the SECMarquee: The Scroll() method controls the scrolling of the marquee. Chapter 4 Simple Controls 71 4.12.2 Customizing SECMarquee Table 17 lists the five methods that allow you to customize the text, the text font, scrolling speed, foreground/background colors, and wrapping mode at run time. Table 17 – Customization Methods for SECMarquee Method Description SetScrollDelay() Sets scrolling delay. SetTextWrap() Sets text wrapping mode. SetColors() Sets foreground and background colors. SetWindowText() Sets/resets marquee message text. SetFont() Set the marquee font. For more information about these methods, see the Objective Toolkit Class Reference. To modify the SECMarquee behaviors: Nearly all the public and protected methods of SECMarquee are virtual so you can override them. Some of the virtual callbacks provided by this class are as follows: Table 18 – Virtual Callbacks for SECMarquee Method Behavior OnScrollStart() Called when a new message is about to start scrolling. OnScrollComplete() Called when an existing message is about to finish. OnInitMarquee() Called when the marquee is initializing. OnScrollMarquee() Called when the marquee is scrolling one frame. Some of the methods that you can override to alter the appearance of the SECMarquee class include: Table 19 – Methods to alter the appearance of SECMarquee 72 Method Behavior Draw3Dborder() Draws a 3D border around the marquee control. DoPaint() Paints the marquee control. 4.12.3 Marquee Sample The statbar sample in the Samples\Toolkit\MFC\UIExt\statbar directory demonstrates how to the use the marquee control in an SECStatusBar. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 4 Simple Controls 73 4.13 Masked Edit Control The masked edit control implements an edit field that filters characters as they are typed. One of the primary limitations of Dynamic Data Validation (DDV) is that the user must type all the data and dismiss the dialog before the errors in input are found. The masked edit control in Objective Toolkit provides a solution to this problem by giving the user immediate feedback if an invalid character is typed. The class hierarchy is as follows: Figure 35 – Masked Edit Class Hierarchy CWnd CEdit SECMaskEdit 4.13.1 SECMaskEdit SECMaskEdit provides a subclassed CEdit that you can use to specify the format and restrictions for entered data. For example, if you wanted a user to enter a telephone number, you would set the mask to (###) ###-####. SECMaskEdit would restrict cursor movement and data input to the location of the pound signs (#). Figure 36 – Example SECMaskEdit Control SECMaskEdit supports cut, copy, paste, and clear operations in addition to the insert mode, which the user can toggle on and off by pressing the VK_INSERT key. 4.13.2 Using SECMaskEdit SECMaskEdit attaches to an existing CEdit control and lets you add formatted input capabilities to it. To incorporate an SECMaskEdit object into your code: Attach an SECMaskEdit object to an existing CEdit using the AttachEdit() method. To set a mask that defines the format for the data the user enters: Construct a string containing the special mask codes and pass it to the control with the SetMask() method. For more information, see Section 4.13.3. 74 To retrieve the data from the masked edit control: Call GetData() to retrieve the data without the mask or GetRawData() to retrieve the entire contents of the SECMaskEdit. To use DDX (dynamic data exchange) with an SECMaskEdit: Use the DDX_Mask() function instead of DDX_Control() function in your dialog’s DoDataExchange() method. 4.13.3 Creating a Mask to Use with SECMaskEdit Mask strings consist of mask characters and literals. Literals are characters that appear unchanged in the mask. Mask characters specify a spot in the mask that accepts certain characters. The valid mask characters are as follows: Table 20 – Mask Characters for SECMaskEdit Mask Character Input allowed for that field # Numeric data (0-9) A Alpha-numeric data (0-9 and a-Z) & Any ASCII character ? Alphabetic data (a-Z) U Accepts a-Z, forces to A-Z (uppercase) L Accepts a-Z, forces to a-z (lowercase) \ Escape character. Use this character if you want the application to interpret one of the preceding character literally (in other words, shown in the mask). The ES_LOWERCASE and ES_UPPERCASE styles have precedence over the ‘U’ and ‘L’ masks. Here are some of more popular masks: Table 21 – Popular Masks for SECMaskEdit Description Mask Example Date ##/##/## 12/24/98 Time ##:## UU 12:35 AM Soc. Sec. Number ###-##-#### 148-92-1532 Phone (###) ###-#### [####] (919) 933-0867 [7] Zip code + 4 #####-#### 27858-1203 First Name ???????????????? Bartholomew Chapter 4 Simple Controls 75 At run time, a prompt character replaces the mask characters. By default, the prompt character is a space. You can change the prompt character via the SetPromptChar() method, e.g., in the case that the space is used as a valid input character. In this version of SECMaskEdit, validation is not performed on the various types of data. For example, 12/35/95 is invalid for a US date format, but would fit the requirements of the mask. It is the SECMaskEdit user’s task to detect that 12/35/95 is an invalid date in such a circumstance. 4.13.4 Mask Edit Sample The Objective Toolkit masktest sample in the Samples\Toolkit\MFC\Controls\masktest directory demonstrates the creation and capabilities of SECMaskEdit, including DDX (dynamic data exchange). This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 76 4.14 Extended Progress Control Derived from MFC's CProgressCtrl, SECProgressCtrl offers several enhancements to the existing progress control, including Smooth fill with percentages. Multidirectional progressions (left to right, right to left, bottom to top, or top to bottom). Fully configurable foreground and background coloring. Custom status text with configurable fonts. The progress control also offers powerful virtual hooks for you to provide your own customizations easily. In addition, SECProgressCtrl supports the automatic advancing of the progress status in response to timer events. Refer to StepOnTimer() in the Objective Toolkit Class Reference for more information. Figure 37 – Example Progress Control The class hierarchy for the progress control is as follows: Figure 38 – Objective Toolkit SECProgressCtrl Class Hierarchy CProgressCtrl SECProgressCtrl 4.14.1 Using SECProgressCtrl You can use objects instantiated from SECProgressCtrl anywhere you can use CProgressCtrl. You can attach an SECProgressCtrl object to an existing CProgressCtrl control via the AttachProgress() method or you can create the control dynamically via the Create() method. To attach an SECProgressCtrl instance to an progress control resource: Call the AttachProgress() method with the control ID of the progress control you want to attach. m_wndProgress.AttachProgress(IDC_PROGRESS,this); To create an SECProgressCtrl instance dynamically: 1. Create a unique control ID for the control. In Visual Studio, you can create a control ID in the Resource Includes dialog. 2. Call the Create() method. CRect rect(0,0,100,50); // initial rectangle m_wndProgress.Create(WS_CHILD|WS_VISIBLE,rect, this,IDC_PROGRESS1); Chapter 4 Simple Controls 77 4.14.2 Customizing SECProgressCtrl You can use the following extended styles to customize the progress control. Table 22 – Extended Styles for SECProgressCtrl Extended style flag Description SEC_EX_PROGRESS_VERT Bar progresses vertically SEC_EX_PROGRESS_RIGHT_TO_LEFT If horizontal, bar progresses right to left SEC_EX_PROGRESS_TOP_TO_BOTTOM If vertical, bar progresses top to bottom SEC_EX_PROGRESS_SHOWPERCENT Show text percentages on bar SEC_EX_PROGRESS_SHOWTEXT Show text on bar SEC_EX_PROGRESS_TEXT_ALIGN_LEFT If text shown, align text left SEC_EX_PROGRESS_TEXT_ALIGN_RIGHT If text shown, align text right SEC_EX_PROGRESS_TEXT_ALIGN_CENTER If text shown, center the text SEC_EX_PROGRESS_ COMMCTRL32 Has look and feel of CProgressCtrl SEC_EX_PROGRESS_DEFAULTS SEC_EX_PROGRESS_SHOWPERCENT |SEC_EX_PROGRESS_TEXT_ALIGN _CENTER You can specify these styles as parameters to the Create() and SetExStyle() methods. By default, the progress bar is horizontal and progresses left to right with centered percentages. You can use the preceding flags to modify the default behavior. In addition, the application can configure the font, foreground, and background color at run time. 4.14.3 Extending SECProgressCtrl Almost every method in SECProgressCtrl is virtual so you can override them. Some of the virtual callbacks provided by this class include: Table 23 – Virtual Callbacks in SECProgressCtrl 78 Method Description OnDisplayStr() Override to alter a progress string before display. OnPaintBarFill() Paint the filled portion of the progress bar. OnPaintBarEmpty() Paint the empty portion of the progress bar. OnPaintBarText() Paint the progress text, if any. 4.15 Enhanced ComboBox with AutoComplete The SECComboBoxEx class extends MFC’s CComboBoxEx class by providing a type ahead or autocomplete feature. The Address combobox in MS Internet Explorer provides the same functionality. As the user types, the control searches through its list of entries for a match. If the control finds a match to the partial entry, it substitutes the text in the edit control with first matching entry and highlights the characters to the right the caret. Figure 39 – Example Enhanced Combobox Control The autocomplete feature also skips ahead when the user types in an entry that includes a regularly repeating delimiter strings sequence, such as the “.” in an IP address or the slash character (/) in an URL. When the user presses the delimiter key once, the entry appears as follows: Figure 40 – Example Entry with Repeating Delimiters When the user presses the delimiter key twice, the entry appears as follows: When the user presses the delimiter key three times, the entry appears as follows: The user can always click the button to the right to drop down the list of items as well. 4.15.1 The Enhanced ComboBox Class The SECComboBoxEx class inherits directly from CComboBoxEx. Figure 41 – Enhanced Combobox Class Hierarchy CComboBoxEx SECComboBoxEx 4.15.2 Using SECComboBoxEx The following procedures describe how to use an enhanced combo box in your application. To add SECComboBoxEx to a window: Use the CComboBox::Create() method. For example: Chapter 4 Simple Controls 79 m_combo.Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, CRect(10,10,350,100), this, ID_COMBOBOX); To add string items to the combo box: Use the CComboBox::InsertItem() method. For example: COMBOBOXEXITEM comboItem; memset(&comboItem, 0, sizeof(comboItem)); comboItem.mask = CBEIF_TEXT; comboItem.iItem = 0; comboItem.pszText = _T("<stingray-installdir>\\Include"); m_combo.InsertItem(&comboItem); where <stingray-installdir> refers to the top-level directory in your Stingray Studio installation. To set the case sensitivity for the autocomplete feature: Call the CComboBox::SetCaseSensitive() method. m_combo.SetCaseSensitive(FALSE); The preceding call ensures that the autocomplete feature does not consider the case of the characters when it searches for a matching entry in the list. To allow users to skip repeating delimiters: Use the CComboBox::SetDelimiterKey() and SetDelimiterString() methods. For example, if the combobox contains long directory entries delimited by two backslashes, you can let the user skip the next delimiter by pressing the <TAB> key with the following calls. m_combo.SetDelimiterKey(VK_TAB); m_combo.SetDelimiterString(_T("\\")); 80 Chapter 5 Look and Feel Styles 5.1 Overview Objective Toolkit provides developers with components that simulate the Visual Studio .NET/Office XP look and feel, the Microsoft Office 2003 look and feel, and the Vista Classic look and feel. You cannot dynamically switch between look-and-feel styles without some noticeable drawing side effects with toolbar buttons. Stingray Studio now has a generalized mechanism for enabling supported look and feel styles. See Section 5.2 for a description of this mechanism. The previous mechanisms described in Section 5.3 and Section 5.4 still work. Chapter 5 Look and Feel Styles 81 5.2 Microsoft Vista Classic Style The Vista Classic style feature allows your applications to take on a look and fill similar to Windows Vista Classic. This visual style is theme-enabled, and requires Windows Vista. The Vista Classic look and feel is enabled for Objective Toolkit and Objective Grid controls. MFCderived and Stingray custom controls now have the Vista Classic look and feel. Applications and samples enable a particular drawing style with the function call RWSetVisualStyle(). Using this function requires inclusion of the header file RWUXTheme.h, which is found in the <stingray-installdir>\RWUXTheme\Include directory. RWSetVisualStyle() should be placed in the application or sample’s main InitInstance() or InitDialog() function call. The call should look like this: RWSetVisualStyle(RW_VISUALSTYLE_VISTACLASSIC); The following themes are available through this call: 82 RW_VISUALSTYLE_WINDOWSCLASSIC RW_VISUALSTYLE_DOTNET RW_VISUALSTYLE_OFFICE2003 RW_VISUALSTYLE_VISTACLASSIC. 5.3 Visual Studio .NET/Office XP Style The following are examples of the styles of Visual Studio.NET/Office XP components that Objective Toolkit offers: Figure 42 – Toolbar in Normal Mode Figure 43 – Toolbar in Mouse Hover Mode Figure 44 – Toolbar in Pressed Mode Figure 45 – Toolbar in Checked Mode Figure 46 – Menu bar in Normal Mode Figure 47 – Menu bar in Mouse Hover Mode Figure 48 – Menu bar with Open Submenu and Selected Item Chapter 5 Look and Feel Styles 83 5.3.1 Enabling .NET/Office XP Styles To enable Visual Studio.NET style toolbars and menu bars, call theBOOL RWSetDotNetStyle(BOOL bEnable=true) function from your application. This function belongs to the global namespace and can be found in Includes\Toolkit\TMenuFrm.h. The suggested place to call this function is from the InitApplication method of your CWinApp-derived class. Note that this call must be made before the creation of the Mainframe. To view sample code showing how to enable Visual Studio.NET/Office XP styles, see the CVizApp::InitInstance function implementation in VIZ.cpp, VIZ sample: RWSetDotNetStyle(TRUE); The VIZ sample demonstrates this feature, and can be found in the Samples\Toolkit\MFC\Docking directory. To fully enable the .NET style, use the Stingray Studio toolbars and menu bars. You will also need to enable ‘Cool’ look for the toolbar. For more information on ‘Cool’ look, please see Section 6.2, “The Customizable Toolbar Classes.” 84 5.4 Microsoft Office 2003 Style The Office 2003 style feature allows your applications to take on a similar look and feel to the Microsoft Office 2003 suite of products. This visual style is theme-enabled, meaning that colors will change depending on the current Windows XP theme you have enabled. (Windows XP required.) The following are examples of the styles of Microsoft Office 2003 components that Objective Toolkit offers: Figure 49 – Gradient-shaded and rounded toolbars with grippers Figure 50 – Hot toolbar buttons Figure 51 – Hot pressed toolbar buttons Figure 52 – Large toolbars Figure 53 – Vertically docked toolbars Figure 54 – Menus with grippers and hot menu buttons Chapter 5 Look and Feel Styles 85 Figure 55 – Gradient-shaded menus with hot items Figure 56 – Checks with hot and hot pressed states Figure 57 – Expanded menus Figure 58 – Gradient-shaded control bar with grippers Figure 59 – Control bars with embedded controls 86 Figure 60 – Themed shortcut bars Figure 61 – Gradient-shaded workbook/worksheet window tabs Figure 62 – Gradient-shaded 3D tab controls Figure 63 – Triple-nested tabs on bottom Chapter 5 Look and Feel Styles 87 Figure 64 – Tabs on left with double-nested tabs on bottom Figure 65 – Tabs on top with double-nested tabs on bottom 88 Figure 66 – Tabs on right with double-nested tabs on bottom Figure 67 – Themed status bars Figure 68 – Objective Toolkit’s Viz sample Chapter 5 Look and Feel Styles 89 5.4.1 Enabling Office 2003 Styles To enable Microsoft Office 2003 styles, add RWSetOfc2003Style (TRUE); in your main application class’s InitInstance(). You must also include toolkit\ot_toolbar.h in your application’s stdafx.h file to pick up the classes involved with this look-and-feel style. Theme colors will automatically update when you change the system theme via the OnSysColorsChange() call. Do not use this look-and-feel style in conjunction with the .NET/XP style, described in Section 5.3, “Visual Studio .NET/Office XP Style.” You can either remove any previous RWSetDotNetStyle() or set it to FALSE. For toolbars and menus to use this style, you must create Stingray toolbars and menus. Controls that you create should have their corresponding flat style and grippers enabled. Cool Look should also be enabled. For classes derived from SECControlBar, if you have overridden OnEraseBkgnd(), you must return SECControlBar::OnEraseBkgnd(pDC); at the end of your routine in order to have the background draw themed. 90 Chapter 6 Customizable Toolbars 6.1 Overview The customizable toolbar classes extend traditional toolbars by allowing the user to drag-and-drop toolbar buttons onto custom toolbar configurations. These classes also implement the cool toolbar look-and-feel found in Microsoft’s Visual Studio. Figure 69 – Example of Customizable Toolbars with the Cool Look The toolbar classes also provide dialogs that you can use to customize toolbars dynamically. Sometimes the toolbars on the desktop do not contain every button available. An end-user can use these dynamic dialogs to move buttons on and off the toolbars via drag-and-drop. Chapter 6 Customizable Toolbars 91 Figure 70 – Example Toolbar Customization Property Sheet 92 6.2 The Customizable Toolbar Classes The hierarchies of the customizable toolbar classes are as follows: Figure 71 – Customizable Toolbar Class Hierarchies CControlBar SECControlBar CObject SECControlBarManager SECCustomToolBar SECToolBarManager SECToolBarsBase CPropertyPage CDialog SECToolBarCmdPage SECToolBarsDlg SECWndBtn CPropertySheet SECToolBarSheet SECToolBarsPage CComboBox SECComboBtn SECStdBtn SECTwoPartBtn 6.2.1 SECCustomToolBar SECCustomToolBar lets you create a custom toolbar to which you can assign a specialized set of buttons. You can add or remove buttons from your toolbar via the Customize dialog. You can also drag-and-drop toolbar buttons by pressing the <ALT> and dragging them. In addition, you can categorize a button by applying a style to it. For example, you could reserve one set of buttons for common File operations and another set for a user-defined task. In the Toolbar dialog, you can choose from large or small buttons, enable or disable tool-tips, and select the standard appearance or the new cool look. Once a button is selected, you can drag it to any toolbar. You can also drag-and-drop buttons among toolbars. When you are dragging a toolbar, you can press the <CTRL> key to prevent docking. This allows you to float the toolbar anywhere on the desktop. If you dock a toolbar, it acquires a gripper that makes it easy to move. You can show or hide a toolbar to indicate active or inactive states. Chapter 6 Customizable Toolbars 93 Because SECCustomToolBar inherits from SECControlBar, it includes support for sizing while docked, automatic stretching when resized, and a default context menu with facilities for adding and removing menu items. Although you can use SECCustomToolBar as a replacement for CToolBar, it works best when used in conjunction with SECToolBarManager. 6.2.2 SECToolBarManager The SECToolBarManager class manages customizable toolbars and the state of an application’s main frame during customize mode, as invoked by SECToolBarsDlg, SECToolBarsPage, and SECToolBarCmdPage. This mode allows the user to create custom toolbars with buttons that are specific to a certain task. In particular, SECToolBarManager performs the following functions: Administrates all SECCustomToolBar objects. Creates/Destroys toolbars. Loads and maintains toolbar bitmap(s). Provides utility access to all toolbars. Provides a framework for the persistent state from the SECControlBarManager base class (introduced in Section 8.7.3). Because customizable toolbars are derived from SECControlBar, you cannot attach an SECCustomToolbar to an existing CToolBar/SECToolBar. Instead, you must utilize the toolbar manager (SECToolBarManager) to create and maintain the bars. 6.2.3 SECToolBarsBase SECToolBarsBase provides the common code base for SECToolBarsDlg and SECToolBarsPage, which provide a user interface for listing and manipulating all of the toolbars. In the toolbar dialog, the user can choose from large or small buttons, enable or disable tooltips, and select a look for the application. SECToolBarsBase is a protected class. Because its constructor is hidden, this class cannot be instantiated directly. 6.2.4 SECToolBarsDlg SECToolBarsDlg displays a list of toolbars and allows the user to manipulate these toolbars. SECToolBarsDlg implements the Customize dialog, which you can use to create a customized toolbar with buttons designed to perform particular tasks. On the toolbar dialog, you can choose from large or small buttons, enable or disable tooltips, and select a look for the toolbars. 94 6.2.5 SECNewToolBar SECNewToolBar constructs the New Toolbar dialog. This dialog is invoked from within the Customize dialog, which you can use to create a toolbar comprised of buttons designed to perform a particular task. Typically, SECToolBarsDlg and SECToolBarsPage use SECNewToolBar to lay the foundation for the Customize and New Toolbar dialogs. This class also prompts you to enter the title of the new toolbar. 6.2.6 SECToolBarsPage SECToolBarsPage constructs a toolbar property page for the Customize dialog implemented by SECToolBarsDlg. This dialog displays a list of toolbars that you can manipulate. In the Toolbar dialog, you choose from large or small buttons, enable or disable tooltips, and select a look for your toolbars. 6.2.7 SECToolBarCmdPage The SECToolBarCmdPage class presents you with all the available toolbar buttons. You can drag these buttons onto an existing toolbar or create a new toolbar from them. You can only use SECToolBarCmdPage in an SECToolBarSheet. Do not try to use it directly in a CPropertySheet. Use the DefineBtnGroup() function to define at least one button group. SECToolBarCmdPage must be used in conjunction with a toolbar manager. 6.2.8 SECToolBarSheet The SECToolBarSheet class constructs a property sheet that you can use with the toolbar button templates created by the SECToolBarCmdPage and SECToolBarsPage. SECToolBarSheet supports the Customize dialog, which you can use to create a toolbar comprised of buttons designed to perform a particular task. This dialog displays a list of toolbars that can be manipulated. In the Toolbar dialog, you choose from large or small buttons, enable or disable tooltips, and select a look for your toolbars. Chapter 6 Customizable Toolbars 95 6.3 The Toolbar Button Classes Objective Toolkit includes a variety of classes that allow you to add different types of buttons to customizable toolbars. The following figure is of some of these button types. Figure 72 – Example Customizable Toolbar Button Types The button classes do not derive from CWnd or CObject. SECStdBtn is the base class for all customizable toolbar buttons. The reason the buttons are not CWnd-derived is to restrict the use of Windows resources. Multiple concurrently running applications with multiple customizable toolbars per application and multiple buttons per toolbar require considerable system resources. Our mechanism allows you to add CWnd-derived controls that can be added to the toolbars on an asneeded basis. See Section 6.3.5, “SECWndBtn,”and Section 6.7, “Creating New Button Types,”for more information. Figure 73 – Hierarchies of the Toolbar Button Classes SECStdBtn SECStdMenuBtn CComboBox SECTwoPartBtn SECTBTextBtn SECWndBtn SECComboBtn 6.3.1 SECStdBtn The SECStdBtn encapsulates a single toolbar button. The SECCustomToolBar class uses it. SECStdBtn includes various operations for drawing buttons on a toolbar. Typically, the SECStdBtn member functions are called from the Customize dialog. For more information, see Section 6.2.4. You can use this dialog to create a toolbar comprised of buttons designed to perform a particular task. If the default styles offered in the Customize dialog do not match your requirements, you can invoke the New Toolbar. 96 The New Toolbar dialog allows the user to create a new toolbar with a unique set of buttons.Use STD_BUTTON in your button map to create buttons of this type. 6.3.2 SECStdMenuBtn This class encapsulates a bitmap toolbar button that does not execute a command. Instead, it displays a drop-down menu that behaves like a context menu. This menu must be part of your application’s menu resources. If you create a separate menu resource for the button to use and you want to support bitmap menus, include this menu in your call to SECToolBarManager::SetMenuInfo(). Use the STD_MENU_BUTTON macro in your button map to create buttons of this type. 6.3.3 SECTwoPartBtn The SECTwoPartBtn class constructs a button that splits its functionality into two parts. The two parts of the button are independent. The Undo/Redo buttons in Visual Studio and MS Word are examples of two part buttons. The left side is a button. The right side part can perform actions such as displaying a drop-down list. When the toolbar containing the button is vertically docked, only the left side of the button is displayed. Use the TWOPART_BUTTON in your button map to create buttons of this type. 6.3.4 SECTBTextBtn SECTBTextBtn is a specialized class that allows you to display text in toolbar buttons when large buttons are displayed. Use the TEXT_BUTTON or TEXT_BUTTON_EX macro in your button map to create buttons of this type. 6.3.5 SECWndBtn The SECWndBtn class bridges the logic between a CWnd control and SECStdBtn. By multiply inheriting from a CWnd control and SECWndBtn, you can create new CWnd-derived button types. For more information, refer to Section 6.7, “Creating New Button Types.” Chapter 6 Customizable Toolbars 97 6.3.6 SECComboBtn The SECComboBtn class constructs a button that can function like a combo box that combines a list box and an edit control. It derives multiply from CComboBox and SECWndBtn. This inheritance gives it the functionality of a CComboBox and allows it to be embedded within an SECCustomToolBar. When embedded in a vertically docked toolbar, the combo box is replaced with a simple button. Use the COMBO_BUTTON macro in your button map to create buttons of this type. 6.4 Comparing SECToolbar to SECCustomToolBar The SECCustomToolbar class is not a drop-in replacement for the CToolbar or SECToolbar classes. 98 6.5 Toolbar Button Map The toolbar manager for the customizable toolbar also allows you to register a button map. A button map lets you map specific button command IDs to non-standard toolbar buttons such as text buttons, combo box buttons, and two part buttons. You don’t need to include a button map if you only need traditional bitmap-only buttons. The button map supports the following features: Allows customization of buttons on a per-command basis (recall button instances can be cloned). Sets the button style. Sets the button class. Sets button class specific parameters. Is linked in via the toolbar manager. The button map is placed in the implementation file of your frame class, similar to the way a window message map is added. The following lines of code are an example of a button map. BEGIN_BUTTON_MAP(btnMap) STD_BUTTON(ID_CHECKED, TBBS_CHECKBOX) STD_BUTTON(ID_DISABLE, TBBS_CHECKBOX) STD_BUTTON(ID_INDETERMINATE, TBBS_INDETERMINATE) TWOPART_BUTTON(ID_UNDO, ID_DROPARROW, TBBS_CHECKBOX, 0) TWOPART_BUTTON(ID_REDO, ID_DROPARROW, 0, ID_REDO) COMBO_BUTTON(ID_FIND, IDC_COMBO_FIND, 0, CBS_DROPDOWN, 150, 40, 150) END_BUTTON_MAP() Once you define a button map, it is given to the toolbar manager. For more information, see Section 6.9.7. 6.5.1 STD_BUTTON The STD_BUTTON macro extends the standard customizable toolbar bitmap only button by introducing a style parameter. The syntax is as follows: STD_BUTTON(ButtonID, Style) Table 24 – STD_Button Macro Parameters Macro parameter Description ButtonID Command ID for the button. Style Window style (WS_*) Chapter 6 Customizable Toolbars 99 6.5.2 STD_MENU_BUTTON The STD_MENU_BUTTON macro extends the standard customizable toolbar by creating a bitmap toolbar button that drops down a menu instead of executing a command. STD_MENU_BUTTON(ButtonID, style, MenuResID, SubMenuIdx, TPMAlign) Table 25 – STD_MENU_BUTTON Macro Parameters Macro Parameter Description ButtonID The command ID for the button. This command is not actually executed when the button is pressed. style The button’s window style. This parameter is usually zero. MenuResID The resource ID for the menu resource for the drop-down menu. SubMenuIdx Zero-based index for the popup menu’s location in the menu resource—usually zero if creating a separate menu resource. TPMAlign Alignment of the menu when dropped with respect to the toolbar button. Can be either TPM_LEFTALIGN, TPM_CENTERALIGN, or TPM_RIGHTALIGN. 6.5.3 TEXT_BUTTON The TEXT_BUTTON macro implements a customizable toolbar button with text. The macro syntax is shown below. TEXT_BUTTON(ButtonID, textResourceID) 100 Macro parameter Description ButtonID Command ID for the button. TextResourceID Resource ID of the text string (or 0 to load based on the button ID). 6.5.4 TEXT_BUTTON_EX The TEXT_BUTTON_EX macro is similar to TEXT_BUTTON (it implements a customizable toolbar button with text) except it also introduces a style parameter. The syntax is as follows: TEXT_BUTTON_EX(ButtonID, TextResourceID, Style) Macro parameter Description ButtonID Command ID for the button. TextResourceID Resource ID for the text string (or 0 to load based on the button ID). Style Window style (WS_*) 6.5.5 TWOPART_BUTTON The TWOPART_BUTTON macro implements a customizable toolbar button that is divided into two bitmaps. It can issue two separate command IDs (for example, an Undo-Redo button). The syntax for this macro is as follows: TWOPART_BUTTON(ButtonID, ButtonID2, Style, DispatchID) Macro parameter Description ButtonID Obtains the bitmap resource and command ID for the left side of the button. ButtonID2 Obtains the bitmap resource for the right side of the button. Style Window style (WS_*). DispatchID The command ID for the right side of the button. 6.5.6 COMBO_BUTTON The COMBO_BUTTON macro implements a customizable toolbar button from which you can drop down a combo box. The syntax is as follows: COMBO_BUTTON(ButtonID, ComboID, Style, ComboStyle, ComboDefWidth, ComboMinWidth, ComboHeight) Macro parameter Description ButtonID Command ID for the button. ComboID Command ID for the combo button. Style Window style (WS_*). Chapter 6 Customizable Toolbars 101 6.6 ComboStyle Combo style (CBS_*). Not all combo styles are supported. ComboDefWidth Default width of combo button. ComboMinWidth Minimum width of combo button. ComboHeight Height of combo button. Toolbar Button Styles The style flags for toolbar buttons are stored in SECStdBtn::m_nStyle. In addition, you can clone and create buttons through the user interface. Applying a style to a specific button is difficult because you need to use a style template. 6.6.1 Button style macros You can use the following button style macros as parameters in the button map. They can be logically OR’ed with the button state macros. Button style macro Equivalent to Description TBBS_BUTTON TBSTYLE_BUTTON Standard button TBBS_SEPARATOR TBSTYLE_SEP Separator TBBS_CHECKBOX TBSTYLE_CHECK Auto check button TBBS_GROUP TBSTYLE_GROUP Marks the start of a group. TBBS_CHECKGROUP TBBS_GROUP | TBBS_CHECKBOX Marks the start of a group of check buttons. 6.6.2 Button State Macros You can use the following button state macros as style parameters with the button map macros. You can logically OR’ed them with the button style macros. 102 Button state macro Equivalent to Description TBBS_CHECKED TBSTATE_CHECKED Button is checked/down. TBBS_PRESSED TBSTATE_PRESSED Button is being depressed. TBBS_DISABLED TBSTATE_ENABLED Button is disabled. TBBS_INDETERMINATE TBSTATE_INDETERMINATE Third state 6.7 TBBS_HIDDEN TBSTATE_HIDDEN Button is hidden. TBBS_WRAPPED TBSTATE_WRAP Button is wrapped at this point. Creating New Button Types You can create new button types; however, the process can be tedious and does not support the cool or flat look. First you need to derive a class from SECWndBtn and the CWnd (SECComboBtn). Then you need to propagate CWnd events across the SECWndBtn bridge. Chapter 6 Customizable Toolbars 103 6.8 Customization Dialogs You can use customization dialogs to display a hidden toolbar. They have a lightweight interface and can easily be overridden to remove various buttons and checkboxes. Figure 74 – Example Toolbars Dialog Figure 75 – Example Toolbars Property Page 104 Figure 76 – Example Toolbars Command Property Page 6.8.1 Creating SECCustomToolBars—Arguments and Cautions The toolbar ID defaults to AFX_IDW_TOOLBAR +0 so that two toolbars can take the default in deftoolbar. When you’re creating a toolbar, you cannot use +2, +3, or +4 or go over +20. 6.8.2 The Effect of Modifying Toolbars on Persistence If the buttons on a toolbar are modified at run time (for example, by dynamically creating a button) persistence may fail. Chapter 6 Customizable Toolbars 105 6.9 Using the Customizable Toolbar Classes The following sections describe how to use customizable tooltips in your applications. 6.9.1 To incorporate customizable toolbars into your application 1. Generate a skeletal application with toolbars using AppWizard. If you create a Do this MDI application Replace CMDIFrameWnd with SECMDIFrameWnd. Replace CMDIChildWnd with SECMDIChildWnd. SDI application Replace CFrameWnd with SECFrameWnd. 2. Replace CToolBar or SECToolbar with SECCustomToolBar. ReplaceCStatusBar with SECStatusBar. 3. Use the Microsoft Visual Studio toolbar resource editor to create a composite bitmap of all the toolbar buttons you want to use with any of the customizable toolbars as one resource. Do NOT put any button separators inside this toolbar resource. Specify every button command ID you want to associate with a bitmap in the resource editor. 4. You can also create a copy of the previous resource and set a larger pixel height/width and redraw any bitmap images as you see fit. This is your large buttons resource. It is important that you maintain a 1-1 button ID mapping to the resource you created earlier. This is essentially a different view of the same data. 5. In your CMainFrame constructor, create the SECToolBarManager object. Use the existing m_pControlBarManager member from the frame’s base class. For example: m_pControlBarManager = new SECToolBarManager(this); 6. In CMainFrame::OnCreate(), issue a call to LoadToolBarResource to load the resource(s) created earlier. If you're only using one resource, use the same resource ID twice. For example, // large and small resources pToolBarMgr-> LoadToolBarResource(MAKEINTRESOURCE(IDR_MAINFRAME), MAKEINTRESOURCE(IDR_MAINFRAME_LG))); 106 // OR small resources only (identical views) pToolBarMgr-> LoadToolBarResource(MAKEINTRESOURCE(IDR_MAINFRAME), MAKEINTRESOURCE(IDR_MAINFRAME))); m_pControlBarManager is declared as type SECControlBarManager so you must typecast to SECToolBarManager to access the specific member function mentioned above. For example, SECToolBarManager* pToolBarMgr= (SECToolBarManager *)m_pControlBarManager; 7. Create a button group. For example: static UINT BASED_CODE fileButtons[] = { ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE, ID_FILE_SAVEALL, }; See Section 6.9.4, “To implement button groups,”for more information. 8. In CMainFrame::OnCreate(), define default toolbar resources via the DefineDefaultToolBar() method. For example, pToolBarManager->DefineDefaultToolBar(AFX_IDW_TOOLBAR, _T("File"),NUMELEMENTS(fileButtons),fileButtons, CBRS_ALIGN_ANY,AFX_IDW_DOCKBAR_TOP); Toolbar IDs AFX_IDW_TOOLBAR+1,+2,+3 are reserved by other MFC components, including the status bar, and cannot be used in this call. However, you can use +0, +4 and higher. 9. If you do not plan to load toolbar states from persistent storage, call the toolbar manager’s SetDefaultDockState() method to pre-load the default toolbars in your application. For example: pToolBarMgr->SetDefaultDockState(); See Section 6.9.7, “To use the button map,” Section 6.9.22, “To save and restore customizable toolbars,” and “Section 6.9.17, “To implement the toolbar customization dialog.” 6.9.2 To implement toolbars with the flat cool look with a toolbar manager In the OnCreate() method of your main frame class, call the EnableCoolLook() method of the toolbar manager. For example: // the boring old toolbars won't cut it, let's see something cool! pToolBarMgr->EnableCoolLook(TRUE); Chapter 6 Customizable Toolbars 107 6.9.3 To implement toolbars with the flat cool look without a toolbar manager 1. Generate a skeletal application with toolbars. If you create a Do this MDI application Replace CMDIFrameWnd with SECMDIFrameWnd. Replace CMDIChildWnd with SECMDIChildWnd. SDI application Replace CFrameWnd with SECFrameWnd. 2. Replace CToolBar or SECToolbar with SECCustomToolBar and CStatusBar with SECStatusBar. 3. Change the toolbar create syntax to that of the SECCustomToolBar create overload and specific the cool extended styles. For example: if(!m_wndToolBar.CreateEx(CBRS_EX_COOLBORDERS|CBRS_EX_GRIPPER,this) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to create toolbar\n"); return -1; // fail to create } 6.9.4 To implement button groups 1. Create the button group as a static array using the button control IDs defined with the associated bitmap resource in the Resource Editor. For example: static UINT BASED_CODE fileButtons[] = { ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE, ID_FILE_SAVEALL, }; 2. In CMainFrame::OnCreate(), define the default toolbar using the DefineDefaultToolBar() method. For example: SECToolBarManager* pToolBarMgr= (SECToolBarManager *)m_pControlBarManager; pToolBarManager-> DefineDefaultToolBar(AFX_IDW_TOOLBAR, _T("File"),NUMELEMENTS(fileButtons),fileButtons, CBRS_ALIGN_ANY,AFX_IDW_DOCKBAR_TOP); Toolbar Ids AFX_IDW_TOOLBAR+1,+2,+3 are reserved by other MFC components, including the status bar and cannot be used in this call. However, you can use +0, +4 and higher. 108 6.9.5 To use multiple toolbar bitmap resources with the toolbar manager Use the SECToolBarManager::AddToolBarResource() method in conjunction with LoadToolBarResource(). For example: // Use the following calls for multiple bitmaps // (to surpass the 2048 pixel width limit on toolbar bitmaps) pToolbarManager->AddToolBarResource( MAKEINTRESOURCE(IDR_MAINFRAME1), MAKEINTRESOURCE(IDR_MAINFRAMELARGE1)); pToolbarManager->AddToolBarResource( MAKEINTRESOURCE(IDR_MAINFRAME2), MAKEINTRESOURCE(IDR_MAINFRAMELARGE2)); VERIFY(pMgr->LoadToolBarResource()); 6.9.6 To find a button on a customizable toolbar 1. Obtain a pointer to the customizable toolbar. See Section 6.9.15, “To obtain a pointer to a specific customizable toolbar,” for more information. 2. For each customizable toolbar, use the GetBtnCount() method and the m_btns data member to access the button data. If your application contains the Customize dialog, you may have more than one copy of that button in existence. For example: // Get pointer to custom toolbar using the preceding // procedure… int nButtons=pBar->GetBtnCount(); for(int nIndex=0;nIndex<nButtons;++nIndex) { if(m_btns[nIndex]->m_nID==MY_TARGET_ID) } 6.9.7 To use the button map The button map maps specific button command IDs to non-standard toolbar buttons. 1. Create a customizable toolbar that uses a toolbar manager. See Section 6.9.1 for more information. 2. Create a button map in your frame class implementation file. For example: BEGIN_BUTTON_MAP(btnMap) STD_BUTTON(ID_CHECKED, TBBS_CHECKBOX) STD_BUTTON(ID_DISABLE, TBBS_CHECKBOX) Chapter 6 Customizable Toolbars 109 STD_BUTTON(ID_INDETERMINATE, TBBS_INDETERMINATE) TWOPART_BUTTON(ID_UNDO, ID_DROPARROW, \ TBBS_CHECKBOX, 0) TWOPART_BUTTON(ID_REDO, ID_DROPARROW, 0, \ ID_REDO) COMBO_BUTTON(ID_FIND, IDC_COMBO_FIND, 0, \ CBS_DROPDOWN, 150, 40, 150) END_BUTTON_MAP() 3. In the frame’s OnCreate() method, provide a reference to the button map using the toolbar manager’s SetButtonMap() method. For example, pMgr->SetButtonMap(btnMap); 6.9.8 To implement a text button 1. Create a customizable toolbar that uses a toolbar manager. See Section 6.9.1, “To incorporate customizable toolbars into your application,” for more information. 2. Create a button map with a TEXT_BUTTON macro entry in your frame class implementation file. For example: BEGIN_BUTTON_MAP(btnMap) TEXT_BUTTON(ID_TEXTBTN, IDS_BUTTONTEXT) END_BUTTON_MAP() 3. In the frame’s OnCreate() method, provide a reference to the button map using the toolbar manager’s SetButtonMap() method. For example: pMgr->SetButtonMap(btnMap); 6.9.9 To implement a text button with styles 1. Create a customizable toolbar that uses a toolbar manager. See Section 6.9.1, “To incorporate customizable toolbars into your application,” for more information. 2. Create a button map with a TEXT_BUTTON_EX macro entry in your frame class implementation file. For example: BEGIN_BUTTON_MAP(btnMap) TEXT_BUTTON_EX(ID_TEXTBTN, IDS_BUTTONTEXT, \ TBBS_CHECKBOX) END_BUTTON_MAP() 3. In the frame’s OnCreate() method, provide a reference to the button map using the toolbar manager’s SetButtonMap() method. For example: pMgr->SetButtonMap(btnMap); 110 6.9.10 To implement a combo button 1. Create a customizable toolbar that uses a toolbar manager. See Section 6.9.1 for more information. 2. Create a button map with a COMBO_BUTTON macro entry in your frame class implementation file. For example: BEGIN_BUTTON_MAP(btnMap) COMBO_BUTTON(ID_FIND, IDC_COMBO_FIND, 0, CBS_DROPDOWN, 150, 40, 150) END_BUTTON_MAP() 3. In the frame’s OnCreate() method, provide a reference to the button map using the toolbar manager’s SetButtonMap() method. For example: pMgr->SetButtonMap(btnMap); 6.9.11 To implement a twopart button 1. Create a customizable toolbar that uses a toolbar manager. See Section 6.9.1, “To incorporate customizable toolbars into your application,” for more information. 2. Create a button map with a TWOPART_BUTTON macro entry in your frame class implementation file. For example: BEGIN_BUTTON_MAP(btnMap) TWOPART_BUTTON(ID_UNDO, ID_DROPARROW, \ TBBS_CHECKBOX, 0) END_BUTTON_MAP() 3. In the frame’s OnCreate() method, provide a reference to the button map using the SetButtonMap() method. For example: pMgr->SetButtonMap(btnMap); 6.9.12 To implement a bitmap button using styles 1. Create a customizable toolbar that uses a toolbar manager. See Section 6.9.1, “To incorporate customizable toolbars into your application,” for more information. 2. Create a button map with a STD_BUTTON macro entry in your frame class implementation file. For example: BEGIN_BUTTON_MAP(btnMap) STD_BUTTON(ID_DISABLE, TBBS_CHECKBOX) END_BUTTON_MAP() 3. In the frame’s OnCreate() method, provide a reference to the button map using the SetButtonMap() method. For example: pMgr->SetButtonMap(btnMap); Chapter 6 Customizable Toolbars 111 6.9.13 To make a customizable toolbar dockable When you define toolbars with the toolbar manager, specify the docking behavior as parameters in the DefineDefaultToolbar() method. For example: pToolBarManager-> DefineDefaultToolBar(AFX_IDW_TOOLBAR, _T("File"),NUMELEMENTS(fileButtons),fileButtons, CBRS_ALIGN_ANY,AFX_IDW_DOCKBAR_TOP); // Call this to position the default toolbars as // configured by the DefineDefaultToolBar command // above. Don't do this if you are going use // LoadBarState/LoadState below, as these functions // will call it anyways on nonexistent state info. pToolBarMgr->SetDefaultDockState(); 6.9.14 To reposition customizable toolbars at run time 1. Obtain a pointer to the customizable toolbar. See Section 6.9.15 and Section 6.9.16. 2. After you identify a controlbar as a customizable toolbar, use the DockControlBarEx member of CFrameWnd to modify the position of the toolbar. For example: DockControlBarEx(pBar, AFX_IDW_DOCKBAR_RIGHT); 6.9.15 To obtain a pointer to a specific customizable toolbar Use the CFrameWnd::GetControlBar() method. Pass the ID of the toolbar you want to access as a parameter. For example, SECCustomToolBar* pBar = STATIC_DOWNCAST( SECCustomToolBar, GetControlBar(AFX_IDW_TOOLBAR)); 6.9.16 To iterate the customizable toolbars Use the m_listControlBars member of the SECFrameWnd or SECMDIFrameWnd class declared as a CPtrList to iterate through the configured controlbars of the frame window: CControlBar* pBar; POSITION pos=m_listControlBars.GetHeadPosition(); while(pos) { pBar=(CControlBar*) m_listControlBars.GetNext(pos); if(pBar-> IsKindOf(RUNTIME_CLASS(SECCustomToolBar))) { // do something with this custom toolbar } } 112 6.9.17 To implement the toolbar customization dialog 1. Incorporate toolbars with a toolbar manager. See Section 6.9.1, “To incorporate customizable toolbars into your application,”for more information. 2. Create a handler method in which to instantiate the Customization dialog. For example: void CMainFrame::OnCustomize() { } 3. Within the handler, instantiate a property sheet of type SECToolBarSheet. For example: SECToolBarSheet toolbarSheet; 4. If you need a toolbar page that shows and hides toolbars, create a property page of type SECToolBarsPage. Then, add it to the property sheet after specifying the toolbar manager for the page. For example: SECToolBarsPage toolbarPage; toolbarPage.SetManager((SECToolBarManager*)m_pControlBarManager); toolbarSheet.AddPage(&toolbarPage); 5. If you need a toolbar command page to drag-and-drop additional buttons onto toolbars, create a property page of SECToolBarCmdPage. Specify the toolbar manager, define the button groups, and add the page to the property sheet. For example: SECToolBarCmdPage cmdPage(SECToolBarCmdPage::IDD, IDS_COMMANDS); cmdPage.SetManager((SECToolBarManager*)m_pControlBarManager); cmdPage.DefineBtnGroup(_T("File"), NUMELEMENTS(fileButtons), fileButtons); cmdPage.DefineBtnGroup(_T("Help"), NUMELEMENTS(helpButtons), helpButtons); cmdPage.DefineBtnGroup(_T("Browse"), NUMELEMENTS(browseButtons), browseButtons); cmdPage.DefineBtnGroup(_T("Debug"), NUMELEMENTS(debugButtons), debugButtons); cmdPage.DefineBtnGroup(_T("Palette"), NUMELEMENTS(paletteButtons), paletteButtons); cmdPage.DefineBtnGroup(_T("Resource"), NUMELEMENTS(resourceButtons), resourceButtons); toolbarSheet.AddPage(&cmdPage); 6. You can add additional property pages to the property sheet. For example: // We have a dummy page to demonstrate window activation // handling when swapping to and from the toolbar page CPropertyPage dummyPage(IDD_DUMMY); toolbarSheet.AddPage(&dummyPage); Chapter 6 Customizable Toolbars 113 7. To invoke the dialog, call the DoModal() method: toolbarSheet.DoModal(); 6.9.18 To invoke the toolbar customization dialog with a toolbar button 1. Create a handler to create the toolbar Customize dialog. See Section 6.9.17, “To implement the toolbar customization dialog,” for more information. 2. Map a button event to an additional handler, which posts a message to the message queue to launch the toolbar Customization dialog. For example, void CMainFrame::OnMyDialogButton() { PostMessage(WM_COMMAND,IDC_CUSTOMIZE,NULL); } Do not use SendMessage(). The button message handler must return prior to invoking the Customization dialog. 6.9.19 To hide and show customizable toolbars 1. Obtain a pointer to the desired toolbar. See Section 6.9.15, “To obtain a pointer to a specific customizable toolbar,” for more information. 2. Use the CFrameWnd::ShowControlBar() method to show or hide the toolbar. 6.9.20 To set the docking order of customizable toolbars Specify the docking order via the nDockNextToID parameter of DefineDefaultToolBar. Set the value of this ID to the previous toolbar in the docking order. Here is the complete prototype for the method: void SECToolBarManager::DefineDefaultToolBar(UINT nID, const CString& strTitle, UINT nBtnCount, UINT* lpBtnIDs, DWORD dwAlignment /* = CBRS_ALIGN_ANY */, UINT nDockBarID /* = AFX_IDW_DOCKBAR_TOP */, UINT nDockNextToID /* = NULL */, BOOL bDocked /* = TRUE */, BOOL bVisible /* = TRUE */) 6.9.21 To changing the font for text buttons Use the static SECTBTextButton::SetTextFont() accessor method to reset the style before using the toolbar manager to create any customizable toolbars, preferably in your CMainFrame constructor. For example: 114 CMainFrame::CMainFrame() { CFont* pFont=LoadCustomFontFromSomewhere(); SECTBTextButton::SetTextFont(pFont); } 6.9.22 To save and restore customizable toolbars Method 1: SECWorkspaceManagerEx Implement the SECWorkspaceManagerEx to save the state of your toolbars, docking windows, and views. For more information, see SECWorkspaceManagerEx in the Objective Toolkit Class Reference. Method 2: LoadBarState/SaveBarState The LoadBarState()and SaveBarState() methods are members of the SECFrameWnd and SECMDIFrameWnd frame classes. For example: LoadBarState(_T("VizBarState")); Method 3: LoadState/SaveState The LoadState()/SaveState() methods are members of SECToolbarManager. For example: pToolBarMgr->LoadState(_T("VizBarState")); If you do not want to support toolbar persistence, you can use the SECToolBarManager::SetDockState() method directly to place default toolbars. 6.9.23 To draw owner-draw controls embedded in a vertically docked toolbar In the appropriate SECWndBtn-derived class, override SetMode() and always pass FALSE through to the base implementation. For example: void CMyWndBtn::SetMode(BOOL /* bVertical */) { SECWndBtn::SetMode(FALSE); } Take a look at the Toolbar sample located in \Samples\Toolkit\MFC\Docking\Toolbar. Chapter 6 Customizable Toolbars 115 116 Chapter 7 Menu Bars 7.1 Overview The Objective Toolkit menu bars implement the newer Cool Look style of dockable, flat-look menu bars. The menu bars provide you with additional functionality. You can: Drag items onto them. Rearrange their buttons. Associate iconic cues with them. Customize them. For example, you can show or hide them. Figure 77 – Sample Application with Objective Toolkit menu bars Chapter 7 Menu Bars 117 Figure 78 – Sample Application with Objective Toolkit customizable menu bars 118 7.2 Menu Bar Classes The following figure is of the class hierarchy for the menu bar classes. Figure 79 – Objective Toolkit Menu Bar Class Hierarchy SECCustomToolBar SECMenuBar SECMDIMenuBar 7.2.1 SECMenuBar SECMenuBar replaces your normal menu with a dockable bar that resembles the Visual Studio and Office 97 menus. A simple button class represents the menu bar buttons that display the appropriate HMENU when clicked. Because SECMenuBar derives from SECCustomToolBar, you can customize the menu bar when used it is used in conjunction with SECToolBarManager. Refer to Chapter 6, “Customizable Toolbars,” for more information. In addition, you can include bitmaps with menus so they look like the most current menus. Integrating this class into an existing application that uses the SECToolBarManager takes as little as three lines of code. 7.2.2 SECMDIMenuBar SECMDIMenuBar is an SECMenuBar derivative that handles the system menu and caption buttons when an MDI child window is maximized. The rest of its features are the same as the SECMenuBar class. See the three MDI samples in the samples\toolkit\MFC\docking\menubar subdirectory for information on using this class. Chapter 7 Menu Bars 119 7.3 Customizing the Display of Menu Pop-ups When a menu pop-up is displayed, a registered message WM_SECGETMENU is sent to the SECFrameWnd/SECMDIFrameWnd. The WPARAM parameter specifies the ID of the menu containing the pop-up. The LPARAM parameter specifies the index of the pop-up within that menu. The message handler needs to return the HMENU of the pop-up menu to be displayed. The default handler for this message retrieves the pop-up menu by searching the CMultiDocTemplate list for the correct menu ID. If the list does not contain the menu ID, the handler searches the frame menu resource. By providing your own handler for this message, you can customize the returned HMENU, the only restriction is that the HMENU handle must persist after this function returns. Note that both SECFrameWnd and SECMDIFrameWnd have member variables (m_hMenuFrame and m_nIDMenuResource) that contain the handle of the frame window's menu and its ID (when menu bar support is enabled). 120 7.4 Menu Button Map Macros Two macros are provided for defining menu buttons in the button map. MENU_BUTTON(id, style, menuId, popupIndex, title) MENU_BUTTON_EX(id, style, menuId, popupIndex, title, bitFlag) Table 26 – Parameters for Menu Button Map Macro parameter Description id The unique ID for this button. style The style for this button (should include SEC_TBBS_NODISABLE so that the button is not disabled if it does not have a command handle). menuId The ID of the menu resource containing the pop-up menu for this button. popupIndex The index of pop-up menu within menuId. title The title for this button. bitFlag The bit flag for this menu button. Chapter 7 Menu Bars 121 7.5 WM_EXTENDCONTEXTMENU The SECControlBar::OnContextMenu() method sends the WM_EXTENDCONTEXTMENU message to the application’s main frame after creating the default context menu. Trap this message if you need to customize the context menu. To customize the menu, simply cast the wParam parameter to an SECControlBar type and the lParam parameter to an CMenu type and modify the menu using the CMenu operations. 122 7.6 Using the Menu Bar Classes The following sections give details and tips on using the menu bar classes. 7.6.1 To Incorporate Objective Toolkit Menubars Into Your Code—Simple Case 1. Instantiate the menubar object in your mainframe constructor in addition to the SECToolBarManager object created for customizable toolbar support. If you’re creating a SDI application, use SECMenuBar. If you’re creating an MDI application, use SECMDIMenuBar. m_pMenuBar is a member variable of SECFrameWnd or SECMDIFrameWnd (base class of CMainFrame). CMainFrame::CMainFrame() { m_pMenuBar = new SECMDIMenuBar; // or SECMenuBar for SDI } 2. If you want to have bitmap support enabled for your menubar menus, include a call to the EnableBmpMenus() method in the constructor. 3. Delete the menubar in the main frame destructor. CMainFrame::~CMainFrame() { if(NULL != m_pMenuBar) { delete m_pMenuBar; m_pMenuBar = NULL; } } 4. In your mainframe OnCreate() handler, use the SetMenuInfo() method of SECToolBarManager to define the menu resources used by the application. This member takes a variable number of arguments based on the number of toolbars to autoconfigure. The syntax is: SetMenuInfo(<count>,<menu id 1>,<menu id 2>,...,<menu id n>); For example: pMgr->SetMenuInfo(4,IDR_MAINFRAME,IDR_EDITVIEW, IDR_LISTVIEW, IDR_FILEVIEW); Chapter 7 Menu Bars 123 Internally these functions assign a unique bit flag to each menu resource. The code in the preceding example assigns the bit flags in the following manner: Menu Resource Bit Mask IDR_MAINFRAME 0x00000001 IDR_EDITVIEW 0x00000002 IDR_LISTVIEW 0x00000004 IDR_FILEVIEW 0x00000008 Then, the menu bar is populated with SECTBMenuBtn instances for all of the menu resources. Each SECTBMenuBtn is assigned the bit flag for its parent menu resource. In the preceding example, all menu buttons belonging to IDR_MAINFRAME would have bit 0 set in their bit flag, menu buttons belonging to IDR_EDITVIEW would have bit 1 set, etc. When a MDI child window becomes active, the SECMenuBar::SwitchMenu() function is called with the ID of the menu resource associated with that window (taken from the document template). SwitchMenu() looks up the bit flag associated with the given ID, and shows all the SECTBMenuBtns that have that bit set and hides those that do not with the button style TBBS_HIDDEN. 5. If you want to apply the cool look to your menubar, call SECToolBarManager::EnableCoolLook() in your mainframe's OnCreate() handler For example: pMgr->EnableCoolLook(); 6. Call EnableDocking(), as you would for any standard toolbar. This step assumes that you called EnableDocking(CBRS_ALIGN_ANY) on the frame window. For example: // dock the menubar m_pMenuBar->EnableDocking(CBRS_ALIGN_ANY); 7. Dock the menu bar using either the DockControlBar() or DockControlBarEx() methods. You can float the menu bar afterward, but it must be docked for initialization. DockControlBar(m_pMenuBar); 7.6.2 To Incorporate Objective Toolkit Menubars Into Your Code--Advanced Case You can make the menu bar more efficient by using the toolbar manager (see the MDIADV sample). With the button map, you can define the bit flags that specify to which menu a particular menu button belongs. This enables you to use the toolbar manager to create a single bar that shows and hides the buttons instead of multiple bars that you need to switch between. You can still use the SwitchMenu() function; however, an additional button map allows greater customization. When you create a button map fewer buttons are created which results in a reduction of the memory required. // IDs for menu buttons #define ID_MENUBAR_FILE #define ID_MENUBAR_EDIT 124 0x80000001 0x80000002 #define #define #define #define ID_MENUBAR_VIEW ID_MENUBAR_TOOLS ID_MENUBAR_WINDOW ID_MENUBAR_HELP // View #define #define #define Bit Flags BITFLAG_MAINFRAME BITFLAG_EDITVIEW BITFLAG_ALL 0x80000003 0x80000004 0x80000005 0x80000006 0x00000001 0x00000002 BITFLAG_MAINFRAME | \ BITFLAG_EDITVIEW ////////////////////////////////////////////////////// // Define the button map // NOTE: MENU_BUTTON_EX and MENU_BUTTON are macros. Line // continuation // characters are required if the text is to be used as formatted // here. They may be removed if your macro only uses one line. BEGIN_BUTTON_MAP(btnMap) MENU_BUTTON_EX(ID_MENUBAR_FILE, SEC_TBBS_NODISABLE, \ IDR_MDIADVTYPE, 0, _T("&File"), \ BITFLAG_ALL) MENU_BUTTON_EX(ID_MENUBAR_EDIT, SEC_TBBS_NODISABLE, \ IDR_MDIADVTYPE, 1, _T("&Edit"), \ BITFLAG_EDITVIEW) MENU_BUTTON_EX(ID_MENUBAR_VIEW, SEC_TBBS_NODISABLE, \ IDR_MDIADVTYPE, 2, _T("&View"), \ BITFLAG_ALL) MENU_BUTTON_EX(ID_MENUBAR_TOOLS, SEC_TBBS_NODISABLE, \ IDR_MDIADVTYPE, 3, _T("&Tools"), \ BITFLAG_ALL) MENU_BUTTON_EX(ID_MENUBAR_WINDOW, SEC_TBBS_NODISABLE, \ IDR_MDIADVTYPE, 4, _T("&Window"), \ BITFLAG_EDITVIEW) MENU_BUTTON_EX(ID_MENUBAR_HELP, SEC_TBBS_NODISABLE, \ IDR_MDIADVTYPE, 5, _T("&Help"), \ BITFLAG_ALL) END_BUTTON_MAP() Now you have two different menus: one for when no view is available (IDR_MAINFRAME), and the other for an edit view (IDR_EDITVIEW). You have declared that the menu buttons ID_MENUBAR_FILE, ID_MENUBAR_VIEW, ID_MENUBAR_TOOLS, and ID_MENUBAR_HELP appear in both menus by specifying the BITFLAG_ALL bit flag. You set ID_MENUBAR_EDIT and ID_MENUBAR_WINDOW to appear only on the IDR_EDITVIEW menu by specifying the BITFLAG_EDITVIEW bit flag. Although BITFLAG_MAINFRAME is defined, it is only used by the BITFLAG_ALL definition. In your CMainFrame::OnCreate() method, use the SECToolBarManager::SetMenuMap() method to define how the bit flags map onto menu resource IDs, and also use the SECToolBarManager::LayoutMenuBar() to define the initial menu button layout for the menu bar. You need to create a table that specifies the order of the buttons on the menu bar. // Table mapping bitflag to menu resource // Default menu bar layout static UINT menuButtons[] = { ID_MENUBAR_FILE, ID_MENUBAR_EDIT, Chapter 7 Menu Bars 125 ID_MENUBAR_VIEW, ID_MENUBAR_TOOLS, ID_MENUBAR_WINDOW, ID_MENUBAR_HELP }; Then, you need to associate each menu type with a bit flag. static SECMenuMap menuMap[] = { { IDR_MAINFRAME, BITFLAG_MAINFRAME }, { IDR_MDIADVTYPE, BITFLAG_EDITVIEW } }; The following code is not complete. It only shows the steps necessary to implement the menu bar. The code that implements toolbars, status bars, and other objects in the main frame is not shown. This example assumes that simple persistence is implemented with storage under a key named Menu60. int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (SECFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // Verify the presence of the toolbar manager and cast it to // the correct type so that we can access the proper methods. ASSERT(m_pControlBarManager != NULL); ASSERT_KINDOF(SECToolBarManager, m_pControlBarManager); SECToolBarManager* pToolBarMgr = (SECToolBarManager*)m_pControlBarManager; // Set the menu map for the toolbar manager using our // previously defined menuMap. pToolBarMgr->SetMenuMap(menuMap, NUMELEMENTS(menuMap)); // Use the previously defined menu button layout to set the // order and presence of the default buttons. pToolBarMgr->LayoutMenuBar(NUMELEMENTS(menuButtons), menuButtons); // Allow docking on all four sides. EnableDocking(CBRS_ALIGN_ANY); // Enable the cool look. Cool look is required to obtain // the Office 2003 and Vista Classic look and feel. pToolBarMgr->EnableCoolLook(TRUE); // load old state (if any) LoadBarState(_T("Menu60")); pToolBarMgr->LoadState(_T("Menu60")); return 0; } When an SECMDIChildWnd is activated, it calls into SECMDIFrameWnd::ActivateMenu() with the ID of its menu resource. You can override this function to customize the way the menu bar is changed when views become active. You can change the state of the menu bar by calling SECMenuBar::SwitchMenu() or SECMenuBar::EnableBitFlag() to enable the buttons associated with the given menu ID or bit flag respectively. 126 SECMenuBar::SwitchMenu() is called with the menu ID of the menu you want to display. BOOL SECMenuBar::SwitchMenu(UINT nIDResource) SECMenuBar::EnableBitFlag() is called with the bitflag and a BOOL bUpdate. If you wish to have the menu bar redrawn immediately, you can pass TRUE in for bUpdate. void SECMenuBar::EnableBitFlag(DWORD dwBit, BOOL bUpdate) 7.6.3 To Implement SECMenuBar Or SECMDIMenuBar Without a Toolbar Manager The following steps provide dockable cool look menus without customization. 1. Follow the steps for enabling docking window support. This consists of replacing your frame classes (SECFrameWnd) with SECMDIFrameWnd, replacing the child frames with SECMDIChildWnd, and changing all dockable and non-dockable bar types to the Stingray equivalent. If persistence is needed, instantiate a toolbar manager. 2. Instantiate the menubar object in your mainframe constructor. If you are creating an SDI application, use SECMenuBar. If you are creating a MDI application, use SECMDIMenuBar. m_pMenuBar is a member variable of the base class. CMainFrame::CMainFrame() { m_pMenuBar = new SECMDIMenuBar; } // or SECMenuBar for SDI 3. Delete the menubar in the main frame destructor. CMainFrame::~CMainFrame() { if(NULL != m_pMenuBar) { delete m_pMenuBar; m_pMenuBar = NULL; } } 4. In MainFrame::OnCreate(), create the menubar. It is created in a manner similar to the other controlbars. For example: // SDI: if (!m_pMenuBar->CreateEx(CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER, this) || !m_pMenuBar->LoadMenu(IDR_MAINFRAME)) { TRACE0("Failed to create menubar\n"); return -1; } Chapter 7 Menu Bars 127 // MDI, use SetMenuInfo instead of LoadMenu: if (!m_pMenuBar->CreateEx(CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER, this) || !m_pMenuBar->SetMenuInfo(2, IDR_MAINFRAME, IDR_MDI2TYPE)) { TRACE("Failed to create menubar\n"); return -1; } 5. Call EnableDocking() as you would a normal toolbar. This step assumes that you called EnableDocking(CBRS_ALIGN_ANY) on the frame window. For example: // dock the menubar m_pMenuBar->EnableDocking(CBRS_ALIGN_ANY); DockControlBar(m_pMenuBar); 7.6.4 To remove the close button from a floating menu As the last statement in the OnCreate() method of your main frame, use the SECMenuBar::ModifyStyle() method to remove the WS_SYSMENU style flag. For example: m_pMenuBar->ModifyStyle(WS_SysMenu,NULL); 7.6.5 To switch between menus SDI Implementation Method 1. In the OnCreate() method of the main frame, replace the standard call to SECMenuBar::LoadMenu() with a call to SECMenuBar::SetMenuInfo(). Make sure you define every menu resource you want to use. For example, the following code defines a menubar that uses two menu resources, IDR_MAINFRAME and IDR_MENU2: if (!m_pMenuBar->CreateEx(CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER, this, WS_VISIBLE | WS_CHILD | CBRS_TOP, AFX_IDW_CONTROLBAR_LAST) || !m_pMenuBar->SetMenuInfo(2, IDR_MAINFRAME, IDR_MENU2)) { TRACE0("Failed to create menubar\n"); return -1; } 2. In the OnCreate() method of the main frame, call the SECMenuBar::SwitchMenu() function to activate the correct initial menu directly after the calls to DockControlBar(). For example, the following code activates the IDR_MAINFRAME menu: m_pMenuBar->SwitchMenu(IDR_MAINFRAME); 3. To toggle the menu at run time, call the SECFrameWnd::SwapMenu() method. For example, the following code activates the menu IDR_MENU2: SwapMenu(IDR_MENU2); 128 MDI Implementation Method 1. In the OnCreate() method of the main frame, ensure that the call to SECMenuBar::SetMenuInfo() defines all the menus that the application can use. For example, the following code defines that the menubar uses four menu resources— IDR_MAINFRAME, IDR_MAINFRAME_MENU2, IDR_MDITYPE and IDR_MDITYPE_MENU2. if (!m_pMenuBar->CreateEx(CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER, this, WS_VISIBLE | WS_CHILD | CBRS_TOP, AFX_IDW_CONTROLBAR_LAST) || !m_pMenuBar->SetMenuInfo(4, IDR_MAINFRAME, IDR_MAINFRAME_MENU2, IDR_MDITYPE, IDR_MDITYPE_MENU2)) { TRACE0("Failed to create menubar\n"); return -1; } 2. Directly after this, add a call to SECMDIFrameWnd::LoadAdditionalMenus(). This method informs the menubar about menu resources that are not loaded by the document templates or the frame window’s menu. For example, the following code loads the IDR_MAINFRAME_MENU2 and IDR_MDITYPE_MENU2 menus by default. The menu IDR_MDITYPE is loaded automatically by the document template, and IDR_MAINFRAME is loaded automatically by the frame window so they are not defined here. if(!LoadAdditionalMenus(2,IDR_MAINFRAME_MENU2, IDR_MDITYPE_MENU2)) { TRACE0("Failed to load additional menus\n"); } To toggle the menus of the frame window or MDI child windows, call the SECMDIFrameWnd::SwapMenu() and SECMDIChildWnd::SwapMenu() methods. For example, the following activates the menu IDR_MDITYPE_MENU2. SwapMenu(IDR_MDITYPE_MENU2); 7.6.6 To use bitmap menus without the cool-look toolbars 1. Replace your mainframe base class with SECFrameWnd for SDI or SECMDIFrameWnd for MDI. If applicable, change your childframe base class to SECMDIChildWnd. 2. In your mainframe constructor, call EnableBmpMenus(). CMainFrame::CMainFrame() { EnableBmpMenus(); } 3. Configure an appropriate toolbar bitmap resource to supply the bitmap resources in your CMainFrame::OnCreate(). Chapter 7 Menu Bars 129 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (SECMDIFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // All other init... // Use the IDR_MAINFRAME toolbar resource for the // bitmap menus AddBmpMenuToolBarResource(IDR_MAINFRAME); } For more information, see Section 7.6.7, “To use bitmap menus in context menus.” 7.6.7 To use bitmap menus in context menus With Main Frame Message Processing By default, bitmaps are not displayed in context popup menus because the bitmaps are added to the menus via a plug-in on the mainframe. If the WM_INITMENUPOPUP is handled by the view, there is no opportunity to add the bitmaps. To circumvent this, parent the menu to the mainframe. This allows the bitmap menu plug-in to process the WM_INITMENUPOPUP the same way it processes the menus from the menubar, which makes adding bitmap support much simpler. For example: void CMyView::OnRButtonDown(UINT nFlags, CPoint point) { CMenu menuText; // Load the menu menuText.LoadMenu(IDR_MAINFRAME); // Pick the popup you wish to use. You may add and // delete menu items at this point. CMenu* pMenuPopup = menuText.GetSubMenu(0); // This puts the menu at the right spot on the screen. ClientToScreen(&point); // Notice that this is parented to the mainframe. The // TMP_XXX style could be any of the valid ones. pMenuPopup->TrackPopupMenu( TPM_RIGHTALIGN , point.x, point.y, AfxGetMainWnd()); } Without Main Frame Message Processing Unfortunately, this has the effect of routing all context message commands directly to the mainframe. If you want to re-parent the context menu as a child of the view so you can route the commands to the parent view instead, complete the following steps. 1. Declare a pointer to an SECBmpMenuPlugIn object in your view header. protected: SECBmpMenuPlugIn* m_pBmpMenuPlugin; 2. Initialize this pointer to NULL in your class constructor. 130 3. In your CView::OnInitialUpdate(), initialize the bitmap menu plugin, defining the toolbar resource from which to load the bitmap images. CMyView::OnInitialUpdate() { // Initialize the bitmap menu plugin m_pBmpMenuPlugin=new SECBmpMenuPlugIn; m_pBmpMenuPlugin->PlugInTo(this); m_pBmpMenuPlugin->AddToolBarResource(IDR_MAINFRAME); } 4. Override WindowProc() and then add the following code to forward events to the plugin. LRESULT CMyView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { // Plug the bitmap menu plugin into this window's WNDPROC if(m_pBmpMenuPlugin) { if(message==WM_INITMENUPOPUP) m_pBmpMenuPlugin->InitMenuPopup(wParam,lParam); else { LRESULT result = 0; m_pBmpMenuPlugin->OnWndMsg( message, wParam, lParam, &result ); if( m_pBmpMenuPlugin->m_bExitMessage ) return result; } } return CView::WindowProc(message, wParam, lParam); } 5. Delete the m_pBmpMenuPlugin object in your view destructor with the following code: if(m_pBmpMenuPlugin) delete m_pBmpMenuPlugin; Chapter 7 Menu Bars 131 7.7 MenuBar Sample The viz sample project (in the samples\toolkit\MFC\docking\viz subdirectory) uses the new menubar classes. You can use the button map that is used in customizable toolbars to denote combo boxes, two-part buttons, and custom buttons to dynamically swap menus based on the currently active view. 132 Chapter 8 Docking Windows 8.1 Overview The Objective Toolkit docking windows architecture is a set of MFC extensions that gives your application the same docking windows features available in the Visual Studio IDE. Docking windows can dock to the application’s main frame and float in a frame outside the application frame. In addition, you can make docking windows in MDI applications float as MDI children. Depending on the docking location, docked windows can be resized in one or two dimensions. When they are floating, docking windows can be resized in both dimensions. During the process of docking, limited geometry management is available. The Objective Toolkit docking windows architecture extends and enhances MFC’s docking control bar architecture without altering it. There are relatively few interface changes so your knowledge of MFC’s CControlBar, CDialogBar, and other classes still applies. You can add Objective Toolkit docking window features to any application, whether it is SDI, MDI, MTI, or FDI-based. Objective Toolkit also supports embedding of Objective Toolkit’s enhanced docking windows inside an OLE IP Server. This addition allows you to use our cool look control bars and toolbars in an embeddable OLE Inplace Server so you can activate them using any OLE Container, such as Microsoft Excel. Menu bars and Docking Views are not supported in this configuration (inside an IP Server). Chapter 8 Docking Windows 133 8.2 Features of Docking Windows The Objective Toolkit docking windows architecture adds a host of features to the existing MFC control bar architecture. When docked, you can resize dockable windows using splitter bars that are along the window’s edge. The following figure shows how docking windows appear when docked. Figure 80 – Example of Objective Toolkit Docking Windows (docked) You can float docking windows in their own frames, outside the frame of the application. 134 Figure 81 – Example of Objective Toolkit Docking Windows (floating) Another significant feature added by the extended control bar architecture is the ability to float a control bar as an MDI child window and then turn it back into a docking control bar. When it is floating, a dockable window can be resized horizontally, vertically, and diagonally. When it is floating as an MDI child, the docking window is clipped to the MDI application frame. Docking windows floating outside of the main frame are not constrained. Each dockable window stretches automatically when resized. You can implement your own, alternate form of resize handling. In addition, each dockable window has a default context menu that you can access by right-clicking. The default context menu contains Hide and Allow Docking menu items by default. You can add or remove menu items from the context menu. Chapter 8 Docking Windows 135 8.3 Flat-Style Drawing To enable the flat drawing style, you will need to set the CBRS_EX_FLATSTYLE extended style while creating a docking window. Alternatively, you can set the flat drawing style later by calling SetExBarStyle (a member of the SECControlBar class). Text inside a gripper window is taken from the window caption which can be set during the creation of a window, or later in the program by calling the SetWindowText function (a member of the CWnd class). Figure 82 – Flat-Style Horizontal Gripper with Close and Expand Buttons Enabled Figure 83 – Flat-Style Vertical Gripper with Close and Expand Buttons Enabled To view sample code which shows how to enable the flat-style mode of docking windows during creation time, please see the CMainFrame::OnCreate function implementation in MAINFRM.cpp, VIZ sample. m_wndProjectWorkspace.Create(this, _T("Project Workspace"), CBRS_RIGHT | WS_VISIBLE | CBRS_SIZE_DYNAMIC, CBRS_EX_STDCONTEXTMENU | CBRS_EX_ALLOW_MDI_FLOAT | CBRS_EX_COOL | CBRS_EX_BORDERSPACE | CBRS_EX_FLATSTYLE, ID_PROJECTWORKSPACE); The VIZ sample demonstrates this feature and can be found in the Samples\Toolkit\MFC\Docking directory. 136 8.4 Auto-Hide Docking Windows Auto-hide docking windows are similar to the pinnable windows in Visual Studio. They are an extension to the Docking Windows architecture that deals specifically with the SECControlBar class. With this new feature, you can unpin a control bar, essentially hiding it while still keeping it docked to the corresponding dockbar. To unpin a control bar, simply click the pin button. When a control bar is unpinned, a new control bar (the SECAutoHideBar) is displayed adjacent to the corresponding dockbar region (i.e., top, bottom, left, right). It will display a tab along that dockbar region for the newly hidden docking window. When the mouse is over the tabs of the auto-hide bar, the corresponding docking window is displayed in floating mode next to the auto hide bar. These floating windows can be repinned by clicking the pin button. This will, in turn, redock the docking window control bar in the position it was in previous to unpinning. SECControlBars can be placed in the top dockbar region, but the top docking region is not limited to toolbars and menubars. To preserve the state of any existing SECControlBars that may be docked to the top, the auto-hide bar is docked to the very bottom of the top dockbar region. The auto-hide bar will be the last bar in the last row of control bars. This means that any SECControlBars you dock to the top dockbar region will be displayed above the Auto Hide Bar. In order to keep the behavior of the auto -hide windows consistent, we do not recommend top docking auto-hide windows, even though it’s possible to do so. If you must dock to the top and want to specify, for example, that certain derived objects cannot dock above toolbars and menubars, consider overriding OnLButtonUp() and specifying where the control bar should be docked. For more information, see the description of the SECMDIFrameWnd::DockControlBarEx() function in the Objective Toolkit Class Reference Guide. Auto Hide is used with the existing SECFrameWnd and SECMDIFrameWnd classes. 8.4.1 Features Auto Hide features include vertical frame docking, auto hide pins, vertical/horizontal text, floating grippers, auto-hide icons, and active windows. 8.4.1.1 Vertical Frame Docking You can control the order of docking, allowing control bars to take up the full frame vertically. By default, docking order priority is Top, Bottom, Left, Right, with Top and Bottom docking taking priority across the frame. You can specify the order of docking alignment by calling EnableDocking() from the frame class with all four alignment flags called separately. Calling EnableDocking with CBRS_ALIGN_ANY gives the default docking order. CBRS_ALIGN_ANY is called only once. If you specify the docking order, EnableDocking() is called exactly four times, as follows: CMainFrame::OnCreate() { //Let’s specify the docking order. EnableDocking(CBRS_ALIGN_TOP); Chapter 8 Docking Windows 137 EnableDocking(CBRS_ALIGN_LEFT); EnableDocking(CBRS_ALIGN_RIGHT); EnableDocking(CBRS_ALIGN_BOTTOM); ... } 8.4.1.2 Auto-Hide Pins Auto-hide pins are displayed in the gripper bar of control bar windows that can be docked. Vertical pins represent a docked state. 8.4.1.3 Vertical/Horizontal Text Text draws horizontally as well as vertically, depending on how the hidden control bar was previously docked. 8.4.1.4 Floating Grippers Floating, or unpinned, auto-hide windows display the gripper horizontally across the top. The auto-hide pin is displayed on its side to represent an unpinned, or hidden, state. Since auto-hide docking windows need a gripper, set the following style flags: CBRS_EX_COOL CBRS_EX_BORDERSPACE CBRS_EX_FLATSTYLE 8.4.1.5 Icons Assigning an icon to a control bar that has auto-hide properties is optional but recommended. Hidden control bars that do not have an icon associated with them, such as the Output Window, display full text in the Auto Hide Bar. To set an icon to an auto-hide docking window, use the MFC function SetIcon(). You’ll need to pass as a parameter the handle to an icon that has already been loaded, such as via LoadIcon(). The following is an example: HICON hndlYourIcon = AfxGetApp()->LoadIcon(IDI_ICON1); m_wndYourAutoHideCtrlBar.SetIcon(hndlYourIcon, FALSE); 8.4.1.6 Active Windows Active displayed windows that are unpinned expand to text in the Auto Hide Bar. Inactive windows are displayed as icons. Hidden windows that are the only bar attached to an Auto Hide Bar, or that don’t have an icon associated with them, are always displayed as full text. The delay of the active auto-hide window’s disappearance is set with SECControlBar’s SetAutoHideDelay() with a DWORD value in milliseconds. By default, it is set to 10 milliseconds. 138 8.4.2 Creating Auto-Hide Docking Windows Your frame class should be derived from SECFrameWnd if you want SDI capability or from SECMDIFrameWnd is you want MDI capability. Auto-hide docking windows are SECControlBarderived classes, with the SetAutoHide (TRUE) function called. By default, SECControlBar classes do not have Auto Hide enabled. As noted in Section 8.4.1.4, “Floating Grippers,” creation of the control bar should contain the CBRS_EX_COOL, CBRS_EX_BORDERSPACE, and CBRS_EX_FLATSTYLE style flags. Following is an example showing how to turn a SECControlBar class into an Auto Hide enabled docking window: //Project Workspace Window if (!m_wndProjectWorkspace.Create(this,_T(“Project Workspace”), CBRS_RIGHT | WS_VISIBLE | CBRS_SIZE_DYNAMIC, CBRS_EX_STDCONTEXTMENU | CBRS_EX_ALLOW_MDI_FLOAT | CBRS_EX_COOL | CBRS_EX_BORDERSPACE | CBRS_EX_FLATSTYLE, ID_PROJECTWORKSPACE)) { TRACE(_T(“Failed to create dialog bar\n”)); return -1; } m_wndProjectWorkspace.EnableDocking(CBRS_ALIGN_ANY); DockControlBarEx(&m_wndProjectWorkspace, AFX_IDW_DOCKBAR_RIGHT, 0, 0 (float)1.00, 220); HICON hicPW = AfxGetApp()->LoadIcon(IDI_ICON1); m_wndProjectWorkspace.SetIcon(hicPW, FALSE); m_wndProjectWorkspace.SetAutoHide(TRUE); To see the complete implementation of auto-hide docking windows, see the Viz sample located at <installdir>\Samples\Toolkit\MFC\Docking\Viz\. Chapter 8 Docking Windows 139 8.5 The Docking Window Classes The extended control bar classes are divided into two categories: control bar derivatives and frame window derivatives. For each MFC control bar and frame window class, there is a corresponding Objective Toolkit class with a name prefixed by SEC. To use our enhanced docking window features, you must migrate from the MFC control bar and frame window classes to Objective Toolkit’s replacements. Figure 84 – Objective Toolkit Extended Control Bar Class Hierarchy CControlBar SECControlBar SECDialogBar SECToolBar SECStatusBar SECCustomToolbar 140 CFrameWnd SECFrameWnd CMDIChildWnd SECMDIChildWnd CMDIFrameWnd SECMDIFrameWnd CObject CCmdTarget SECControlBarManager CDockState SECDockState 8.6 Docking Window Frame Classes The following sections describe the Objective Toolkit frame classes. To use the Objective Toolkit docking windows architecture, you must use the Objective Toolkit frame class. 8.6.1 SECFrameWnd The SECFrameWnd class derives from CFrameWnd and adds the implementation details supporting the docking window features. This class supports the SECControlBar class by adding members for an SECControlBar manager and the SECDockBars. It also adds improved serialization support for extension information. 8.6.2 SECMDIFrameWnd The SECMDIFrameWnd class derives from CMDIFrameWnd and adds the implementation details supporting the docking window features. This class supports the SECControlBar class by adding members for an SECControlBar manager and the SECDockBars. It also adds improved serialization support for extension information. 8.6.3 SECMDIChildWnd The SECMDIChildWnd class derives from CMDIChildWnd and adds the implementation details that support the docking window features. This class supports the SECControlBar class. It is compatible with the improved serialization support. Chapter 8 Docking Windows 141 8.7 Docking Window Control Bar Classes The following sections describe the Objective Toolkit Control bar classes. 8.7.1 SECControlBar The SECControlBar class derives from CControlBar and adds the internal state and implementation details supporting sizing when docked. It also adds a gripper bar as a private class, a gripper close button, a gripper expand button, and methods for context menu manipulation. Virtual methods are provided for advanced users to manipulate docking and other features. 8.7.2 SECDialogBar SECDialogBar is an interface equivalent replacement for CDialogBar. SECDialogBar serves as the base class for all of your dialog bars. SECDialogBar does nothing more than re-derive from SECControlBar, so that all member variables and implementation details exist to facilitate sizing docked windows and more. This class introduces no new member variables or functions. All dialog bars formerly derived from CDialogBar must be rederived from SECDialogBar. You cannot use CDialogBars with Objective Toolkit’s docking window enhancements because they lack the member variables required to perform the sizing calculations. See the viz sample in the samples\toolkit\MFC\docking\viz subdirectory for an example of how to use this class. 8.7.3 SECControlBarManager The SECControlBarManager class manages control bars as well as the state of an application’s main frame. It supports dynamic save and restore of control bars. This class derives from CCmdTarget. 8.7.4 SECDockState TheSECDockState class derives from CDockState and adds the additional states introduced by SECControlBarInfo. The docking states are stored in member variables that are not recorded by CDockState. SECDockState inherits the core docking state from CDockState and adds all the new attributes required for enhanced docking window functionality. If your application uses our docking window architecture, you must replace all occurrences of CDockState with SECDockState. 142 8.8 Message Routing Issues MFC routes command messages to the frame window, view, document, doc template, and the CWinApp instead of a control bar. Controls are not included in MFC's default message routing. If you are working strictly with MFC and CControlBar, the client of a CControlBar is not visited in the default routing of messages either. When you apply focus to a control bar, command messages are still routed to the active MDI child (or SDI frame). This is not unique to Objective Toolkit; using CControlBar yields the same result. For more information, see Section 8.11.21, “To route messages to the client area of SECControlBar.” Chapter 8 Docking Windows 143 8.9 Extended ControlBar Styles The SECControlBar class introduces extended control bar styles. Set these extended control bar style bits via the m_dwExStyle class variable of the SECControlBar::Create() method. Here is an overview of the extended control bar styles that can be set for any class derived from SECControlBar. Table 27 – Extended Control Bar Styles 144 Extended Style Flag Description CBRS_EX_STDCONTEXTMENU When the extended control bar style is set, the control bar is automatically given the standard context menu items (for example, Show/Hide and Allow Docking). CBRS_EX_STRETCH_ON_SIZE The control bar is automatically stretched when resized so that all child windows of the control bar are proportionally scaled to fit the new size exactly. If you require a custom form of resize handling, do not set this extended style and override SECControlBar::OnSize(). CBRS_EX_DRAWBORDERS Draw a border around the bar. CBRS_EX_BORDERSPACE Leave border space for ease of dragging. This style causes a very wide border around the client area of the window while floating. CBRS_EX_ALLOW_MDI_FLOAT Control bar can be re-parented by an MDI child window. Set this style if you want the control bar to have a context menu item that allows the control bar to be floated as a normal MDI child window. This style can only be used in MDI applications. CBRS_EX_SIZE_TO_FIT Size the (single) child to fit. CBRS_EX_UNIDIRECTIONAL The control bar can be sized horizontally or vertically but not diagonally (for example, a toolbar). The control bar can be sized in only one direction per sizing operation. CBRS_EX_COOLBORDERS Floating buttons, no border. This style only applies to toolbars. CBRS_EX_GRIPPER Draw the dragging gripper. CBRS_EX_GRIPPER_CLOSE Draw the close button on gripper. CBRS_EX_GRIPPER_EXPAND Expand/contract control bar button. Table 27 – Extended Control Bar Styles (Continued) Extended Style Flag Description CBRS_EX_COOL CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER | CBRS_EX_GRIPPER_CLOSE | CBRS_EX_GRIPPER_EXPAND Gives the control bar the cool look – a flat, painted look similar to the control bars seen in Microsoft Visual Studio. These extended style options allow you to customize the cool look for your application. By default, the customizable toolbars are: CBRS_EX_COOLBORDERS | CBRS_EX_GRIPPER; all other control bars are CBRS_EX_COOL. Note: Gripper requires cool- borders, and Close requires Gripper. In addition, the gripper drawing code has been made virtual, so you can easily plug in your own gripper or modify the existing gripper with just one or two overrides. CBRS_EX_FULL_ROW Control bar always occupies entire row (menubars). CBRS_EX_TRANSPARENT Toolbar buttons drawn transparently. CBRS_EX_MENU_STYLE Do not become large unless a large object is dropped on bar. The extended control bar styles are distinct from the window styles to avoid value collisions. Chapter 8 Docking Windows 145 8.10 Embedding CViews in Docking Windows Classes derived from CView pose special challenges when docking is concerned. To circumvent these challenges, the Objective Toolkit docking windows architecture only allows you to embed a class inside an SECControlBar if it is a CWnd that is not CView-derived. This is an MFC limitation inherited from the CControlBar class (for example, you cannot embed a CView inside a CControlBar). 146 8.11 Using the Docking Window Architecture The following topics describe how to use docking windows classes and handle special situations. 8.11.1 To create an application with Objective Toolkit docking windows Use the Objective Toolkit AppWizard to create a project. 8.11.2 To incorporate Objective Toolkit docking windows into an existing MDI application 1. Integrate the library into your application. For more information, see Section 2.3.10, “To incorporate Objective Toolkit into your application.” 2. From the View menu in Visual Studio, click Resource Includes… Ensure that the _AFX_NO_SPLITTER_RESOURCES is either absent or commented out. // #define _AFX_NO_SPLITTER_RESOURCES 3. Replace the base class of your CMainFrame class. Replace CMDIFrameWnd with SECMDIFrameWnd. The MDI parent frame acquires the functionality it needs to host its resizable SECControlBar children. 4. Replace the base class of all existing MDI child frames (CMDIChildWnd) with SECMDIChildWnd. 5. Replace the base class of all windows derived from CControlBar with SECControlBar. 6. Replace the base class of all windows derived from CDialogBar with SECDialogBar. 7. Replace the base class of all toolbars derived from CToolBar or SECToolBar with SECCustomToolBar. 8. Replace the base class of the status bar derived from CStatusBar with SECStatusBar. You need to re-derive any status bar derived from CStatusBar from SECStatusBar. You cannot use CStatusBars with our docking window enhancements because they do not have the member variables the class expects. 9. Replace the base class of all windows derived from CDockContext with SECDockContext. 10. Modify the OnCreate() member of your CMainFrame class so that all calls to CControlBar::Create(), CDialogBar::Create(), or CToolBar::Create() reflect the change in prototypes introduced by the re-derivations in the previous steps. In particular, the CControlBar::Create() member adds an additional parameter (dwExStyle) for passing extended style bits. For example, CBRS_EX_STRETCH_ON_SIZE is an extended style bit that you can set in your call to SECDialogBar::Create(). Chapter 8 Docking Windows 147 8.11.3 To incorporate Objective Toolkit docking windows into an existing SDI application 1. Integrate the library into your application. For more information, see Section 2.3.10, “To incorporate Objective Toolkit into your application.” 2. From the View menu in Visual Studio, click Resource Includes… Ensure that the AFX_NO_SPLITTER_RESOURCES is either absent or commented out. // #define _AFX_NO_SPLITTER_RESOURCES 3. Replace the base class of your CMainFrame class. Replace CFrameWnd with SECFrameWnd. 4. Replace the base class of all windows derived from CControlBar with SECControlBar. 5. Replace the base class of all windows derived from CDialogBar with SECDialogBar. 6. Replace the base class of all toolbars derived from CToolBar with SECToolBar. 7. Replace the base class of the status bar from CStatusBar with SECStatusBar. 8. Modify the OnCreate() member of your CMainFrame class so that all calls to CControlBar::Create(), CDialogBar::Create(), or CToolBar::Create() reflect the change in prototypes introduced by the re-derivations in the previous steps. In particular, the CControlBar::Create() member adds an additional parameter (dwExStyle) for passing extended style bits. For example, CBRS_EX_STRETCH_ON_SIZE is an extended style bit that you can set in your call to SECDialogBar::Create(). 8.11.4 To use Objective Toolkit docking windows inside an OLE IP server 1. Use AppWizard to generate a full server or check ActiveX document server support. 2. Integrate the library into your application. For more information, see Section 2.3.10, “To incorporate Objective Toolkit into your application.” 3. Replace the base class of the server document class (COleServerDoc) with SECOleServerDoc. Replace each base class invocation in the source file when it is appropriate. 4. Replace the base class of your OLE Server Item (COleServerItem) with SECOleServerItem. If you selected ActiveX document server support when you were generating the application using AppWizard, replace CDocObjectServerItem with SECDocObjectServerItem instead. This provides you with CDocIPFrameWnd support. 5. Use the OnCreateControlBars() method of your COleIPFrameWnd derived class to create your controlbars. You must cast the pWndFrame pointer from CFrameWnd to SECFrameWnd. This ensures that the proper SEC non-virtual method overrides are called— most notably, EnableDocking(). Each controlbar must set the inplace frame object as its owner, but it should be created with the pWndFrame pointer as the parent. 148 Refer to the following sample code for more information. In addition, please try secoleip in the samples\toolkit\MFC\docking\secoleip subdirectory. You need to run this sample once to register your server. Open an OLE container like Excel, select Insert Object and then select SecOle document. Embedded Objective Toolkit docking windows and toolbars appear as well as the toolbar customization dialog. Again, Objective Toolkit MenuBars and Docking Views are not supported inside an IP Server. BOOL CInPlaceFrame::OnCreateControlBars(CFrameWnd* pWndFrame, CFrameWnd* pWndDoc) { // Remove this if you use pWndDoc UNREFERENCED_PARAMETER(pWndDoc); // Must cast the pWndFrame to an SECFrameWnd to insure the // proper "SEC" nonvirtual overrides are called. This // operation should be type safe if all the docking windows // enablement steps were properly followed (please refer to // the user's guide). ASSERT_KINDOF(SECFrameWnd,pWndFrame); SECFrameWnd* pSecWndFrame=(SECFrameWnd *)pWndFrame; m_pDockingFrameWnd=pSecWndFrame; // save ptr for OnClose //handler // A toolbar manager is needed to provide toolbar // customization support. In normal Objective Toolkit docking windows, // we can simply instantiate the m_pControlBarMgr member of // SECFrameWnd/SECMDIFrameWnd. Since this is an // COleIPFrameWnd object, though, we must manually track // this ptr and clean up as needed. // Note 2: all controlbars must have pWndFrame as a parent, // but this COleIPFrameWnd as the owner for proper command // routing. // Use the second toolbar mgr constructor overload to provide // this necessary linkage. SECToolBarManager* pToolBarMgr= new SECToolBarManager(pSecWndFrame,this); m_pToolBarMgr=pToolBarMgr; // save for memory cleanup pSecWndFrame->SetControlBarManager(pToolBarMgr); // Configure the customizable toolbars VERIFY(pToolBarMgr->LoadToolBarResource( MAKEINTRESOURCE(IDR_SECOLETYPE_SRVR_IP), MAKEINTRESOURCE(IDR_SECOLETYPE_SRVR_IP_LG))); pToolBarMgr->SetButtonMap(btnMap); pToolBarMgr->DefineDefaultToolBar(AFX_IDW_TOOLBAR,_T("File"), NUMELEMENTS(fileButtons),fileButtons, CBRS_ALIGN_ANY,AFX_IDW_DOCKBAR_TOP); pToolBarMgr->DefineDefaultToolBar(AFX_IDW_TOOLBAR + 5, _T("Edit"),NUMELEMENTS(editButtons), editButtons,CBRS_ALIGN_ANY, AFX_IDW_DOCKBAR_TOP,AFX_IDW_TOOLBAR); pToolBarMgr->DefineDefaultToolBar( AFX_IDW_TOOLBAR + 6, _T("Debug"), NUMELEMENTS(debugButtons), debugButtons, CBRS_ALIGN_ANY, AFX_IDW_DOCKBAR_TOP, AFX_IDW_TOOLBAR); Chapter 8 Docking Windows 149 pToolBarMgr->EnableCoolLook(TRUE); pSecWndFrame->EnableDocking(CBRS_ALIGN_ANY); // Output Window m_wndOutput.SetOwner(this); // mandatory! if (!m_wndOutput.Create(pSecWndFrame, _T("Output Window"), CBRS_BOTTOM|WS_VISIBLE | CBRS_SIZE_DYNAMIC|CBRS_TOOLTIPS, CBRS_EX_STDCONTEXTMENU|CBRS_EX_ALLOW_MDI_FLOAT | CBRS_EX_COOL|CBRS_EX_BORDERSPACE, ID_OUTPUTWINDOW)) { TRACE(_T("Failed to create dialog bar\n")); return -1; } m_wndOutput.EnableDocking(CBRS_ALIGN_ANY); pSecWndFrame->DockControlBarEx(&m_wndOutput, AFX_IDW_DOCKBAR_BOTTOM, 0, 0, (float)0.75, 130); // Project Workspace Window // mandatory! m_wndProjectWorkspace.SetOwner(this); if (!m_wndProjectWorkspace.Create(pSecWndFrame, _T("Project Workspace"), CBRS_RIGHT | WS_VISIBLE | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS, CBRS_EX_STDCONTEXTMENU | CBRS_EX_ALLOW_MDI_FLOAT | CBRS_EX_COOL | CBRS_EX_BORDERSPACE, ID_PROJECTWORKSPACE)) { TRACE(_T("Failed to create dialog bar\n")); return -1; } m_wndProjectWorkspace.EnableDocking(CBRS_ALIGN_ANY); pSecWndFrame-> DockControlBarEx(&m_wndProjectWorkspace, AFX_IDW_DOCKBAR_RIGHT, 0, 0, (float)1.00, 180); // Load default toolbar state pSecWndFrame->LoadBarState(_T("SecOleIPBarState")); pToolBarMgr->LoadState(_T("SecOleIPBarState")); return TRUE; } 8.11.5 To create a docking window based on a dialog resource 1. Create a dialog resource for the docking window using the resource editor in Visual Studio. 2. Create an SECDialogBar-derived class to manage the client area of the docking window. 150 3. Instantiate an object of the SECDialogBar-derived class as a member of the frame in which the docking window is included (either SECFrameWnd or SECMDIFrameWnd). 4. In the frame window’s OnCreate() method, call the Create() method of the instantiated control bar to initialize it and then pass the resource ID of the dialog resource. 8.11.6 To create a docking window not based on a dialog resource 1. Ensure your frame class is either SECFrameWnd or SECMDIFrameWnd-derived. 2. Create an SECControlBar-derived class to manage the client area of the docking window. 3. As a data member of your frame class, instantiate the SECControlBar-derived object. 4. In the frame window’s OnCreate() method, call the Create() method of the instantiated control bar to initialize it. 8.11.7 To set the style of a docking window 1. Specify the CBRS_ styles either in the Create() call for the SECControlBar-derived class or call the CControlBar::SetBarStyle() method. See the Objective Toolkit Class Reference for more information on the CControlBar::SetBarStyle() method and descriptions of these styles. 2. Specify the CBRS_EX_ style extensions in the Create() call for the SECControlBar-derived class. See Section 8.9, “Extended ControlBar Styles,”for a list and a description of the extended docking windows styles. The SECControlBar::SetExBarStyle() method will set the extended bar style. 8.11.8 To make a docking window dockable Call SECControlBar::EnableDocking(), SECMDIFrameWnd::EnableDocking(), and SECMDIFrameWnd::DockControlBarEx(). This also applies to SECFrameWnd. For example: CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... EnableDocking(CBRS_ALIGN_ANY); Chapter 8 Docking Windows 151 // Create the control bar if (!m_wndControlBar.Create(this, _T("Control Bar 1"), CBRS_BOTTOM | WS_VISIBLE | CBRS_SIZE_DYNAMIC, CBRS_EX_COOL, ID_OUTPUTWINDOW)) { TRACE(_T("Failed to create control bar\n")); return -1; } m_wndControlBar.EnableDocking(CBRS_ALIGN_ANY); DockControlBarEx(&m_wndControlBar, AFX_IDW_DOCKBAR_BOTTOM, 0, 0, (float)0.75, 130); ... } 8.11.9 To create a non-dockable control bar 1. Notice the following lines in the OnCreate() method of your frame class. The following VC++ AppWizard generated comment is incorrect: // TODO: Delete these three lines if you do not // want the toolbar to be dockable m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); If you follow the directions and delete these three lines, a pseudo-docked toolbar that draws incorrectly appears. This occurs whether or not you are using Objective Toolkit. This comment is incorrect. You still need to call EnableDocking(), but you also need to pass a value of zero to create the CDockContext(). 2. Instead of deleting the lines, call SECControlBar::EnableDocking() with a parameter of zero. For example: m_wndToolBar.EnableDocking(0); 3. Call either ToggleDocking() or FloatControlBar() on the control bar. ToggleDocking() automatically computes an initial point for you. //m_wndToolBar.m_pDockContext->ToggleDocking(); FloatControlBar(&m_wndToolBar, CPoint(50,50)); 8.11.10To dock a docking window that is floating Call the DockControlBar() or DockControlBarEx() methods from your frame window class. These methods dock a control bar that is either floating or docked to another dock bar. The DockControlBarEx() method gives you more control over how and where the control bar is docked. 152 8.11.11To float a docking window that is docked Call the FloatControlBar() method, which is a member of SECFrameWnd/SECMDIFrameWnd. This method removes a bar from its dockbar and floats it. It can also move a floating bar to a new location. 8.11.12To make an SECDialogBar size diagonally when floated Conditionally typecast pFrameWnd to SECMDIFrameWnd or SECFrameWnd so that the FloatControlBar() method in SECMDIFrameWnd is called instead of CFrameWnd. For example: if (pFrameWnd-> IsKindOf(RUNTIME_CLASS(SECMDIFrameWnd))) ((SECMDIFrameWnd*)pFrameWnd->FloatControlBar(. . .); else if (pFrameWnd-> IsKindOf(RUNTIME_CLASS(SECFrameWnd))) ((SECFrameWnd*)pFrameWnd-> FloatControlBar(. . .); else pFrameWnd->FloatControlBar(. . .); 8.11.13To receive notifications when the docked state of a docking window changes 1. Create an SECControlBar-derived class. 2. Override the following callback methods. Callback method Description OnBarBeginDock() Called before a bar is docked. The default implementation calls OnBarDock(). OnBarDock() Called before a bar is docked. The default implementation does nothing. OnBarEndDock() Called after a bar is docked. The default implementation does nothing. OnBarBeginFloat() Called before a bar is floated. The default implementation calls OnBarFloat(). OnBarFloat() Called before a bar is floated. The default implementation does nothing. OnBarEndFloat() Called after a bar is floated. The default implementation does nothing. OnBarBeginMDIFloat() Called before a bar is docked as an MDI child. The default implementation calls OnBarMDIFloat(). Chapter 8 Docking Windows 153 Callback method Description OnBarMDIFloat() Called before a bar is floated as an MDI child. The default implementation does nothing. OnBarEndMDIFloat() Called after a bar is floated as an MDI child. The default implementation does nothing. 8.11.14To hide a docking window Use ShowControlBar(), which is a member of SECFrameWnd/SECMDIFrameWnd. Calling ShowWindow() has no effect on the control bar. 8.11.15To control the docking location of a docking window Call the SECControlBar::EnableDocking() method. This method controls the ability to dock the window as well as where the window can be docked on its parent window. Refer to the Objective Toolkit Class Reference for more information on this method and its parameters. 8.11.16To determine where a docking window is docked 1. Use either CFrameWnd::GetControlBar() or CDockBar::FindBar() to get a pointer to a control bar. 2. Refer to the dock bar ID map, which is defined in both SECFrameWnd and SECMDIFrameWnd. const { { { { { }; DWORD SECFrameWnd::dwSECDockBarMap[4][2] = const { { { { { }; DWORD SECMDIFrameWnd::dwSECDockBarMap[4][2] = AFX_IDW_DOCKBAR_TOP, AFX_IDW_DOCKBAR_BOTTOM, AFX_IDW_DOCKBAR_LEFT, AFX_IDW_DOCKBAR_RIGHT, AFX_IDW_DOCKBAR_TOP, AFX_IDW_DOCKBAR_BOTTOM, AFX_IDW_DOCKBAR_LEFT, AFX_IDW_DOCKBAR_RIGHT, CBRS_TOP CBRS_BOTTOM CBRS_LEFT CBRS_RIGHT }, }, }, }, CBRS_TOP }, CBRS_BOTTOM }, CBRS_LEFT }, CBRS_RIGHT }, 3. By using the information above, you can add a member to your mainframe that determines to which dockbar a particular control bar is docked. UINT CMyMainFrame::GetControlBarDockSite(...) { 154 // In case we don't find a place holder, // find a bar with the correct alignment // and keep it in pPossibleBar. CDockBar* pDockBar= NULL; for (int i = 0; i < 4; i++) { pDockBar = (CDockBar*) GetControlBar(dwSECDockBarMap[i][0]); if ((pDockBar->FindBar(pBarFind, -1) != -1) { bFound = TRUE; break; } } if (bFound) { switch (dwSECDockBarMap[i]) { case AFX_IDW_DOCKBAR_TOP: return CBRS_TOP; } } } 8.11.17To determine the row and column of a docked window Call the SECControlBar::GetBarSizePos() method. This method returns information about the control bar position. In addition, it can also return information about the dockbar position and size. 8.11.18To modify a control bar’s context menu A context (shortcut) menu is a pop-up menu that appears when the user presses the right mouse button. The SECControlBar bar defines a standard context menu and a convention for extending or modifying it. By default, the control bar is given two context menu items (Hide and Allow Docking). There are two techniques for extending the standard context menu. If the menu item applies to all control bars of a given class, override the SECControlBar::OnExtendContextMenu() method in a derived class and extend the menu through the pMenu parameter. To learn how to use this method of context menu extension, see the source file samples\toolkit\MFC\docking\viz\outbar.cpp and the OutputControlBar::OnExtendContextMenu() method. If the menu item represents a capability that the main frame window enables, handle theWM_EXTENDCONTEXTMENU message in your CMainFrame class and extend the menu through the menu handle passed via LPARAM. SECControlBar always sends this message to its parent frame window after constructing the default context menu. Chapter 8 Docking Windows 155 For an example of this form of context menu extension, see the source file src\toolkit\mdi\swinmdi.cpp and the SECMDIFrameWnd::OnExtendContextMenu() method. 8.11.19To add a toolbar to a control bar 1. Override SECDialogBar::OnCreate() and create the toolbar as a child of the dialog bar. 2. Override SECDialogBar::OnSize() and layout the toolbar and dialog template so they do not overlap. 8.11.20To access controls in the docking window inside a message handler Use the following code in your message handler: const MSG* pMsg = CWnd::GetCurrentMessage(); HWND hWnd = HWND(pMsg->lParam); ASSERT(::IsWindow(hWnd)); CComboBox* pCombo = STATIC_DOWNCAST( CComboBox, CWnd::FromHandle(hWnd) ); 8.11.21To route messages to the client area of SECControlBar Override OnCmdMsg() and check the focus window. If it is your control bar window, route the message to it. If not, let default handling occur. We have solved this problem in our Docking Views. 156 8.12 Customizing Objective Toolkit Docking Windows This section describes how you can modify the behavior of Objective Toolkit docking windows code. 8.12.1 Key Extended Control Bar Members SECControlBar::m_bOptimizedRedrawEnabled. A static boolean variable that enables the redraw optimizations that eliminate extra redraws and flicker when set to TRUE. We have tested this to ensure no redraw problems exist when redraw optimizations are in effect. However, if your control bars are not being redrawn properly, set this member to false and see if the problems persist. If necessary, please contact Professional Services, as discussed in Section 1.4.3, “Professional Services”. For more information, see Section 10.2.4.14, “Key WDI Methods and Data Members.” The same key members exist in the SECControlBar class as in SECWorkbookWnd/SECWorksheetWnd. 8.13 Docking Windows Sample The Objective Toolkit viz sample in the samples\toolkit\MFC\docking\viz subdirectory illustrates how to use docking windows in an MDI application. Chapter 8 Docking Windows 157 158 Chapter 9 Image Classes 9.1 Overview The Objective Toolkit image classes provide basic support for viewing the most popular graphic image types. With our image classes, you can create applications that read, display, write, convert, and manipulate images. The image classes are viewer helpers. Although the image classes read and display the standard image formats (and some of their variations), we do not support the multitude of possible options for each format and the intricacies of converting from one to another. Some of the known restrictions are listed in the following sections. The Objective Toolkit image classes are contained in the Stingray Foundation Library. To avoid naming conflicts, these classes are wrapped in the stingray::foundation namespace. To use these classes you must either: Add this namespace reference wherever these classes are used. or Map the entire namespace with: <pre> using namespace stingray::foundation; </pre> Exporting an entire namespace into your project is not a good way to maintain compartmentalization; it can possibly cause naming conflicts. If you include the component header files instead of secall.h, the namespace is exported to your project automatically. For example: <pre> #include “Toolkit\ot_secdib.h” </pre> The Stingray Studio Getting Started Guide discusses this topic in greater detail. Look for details about the image classes in the Foundation Class Reference instead of the Objective Toolkit Class Reference. Chapter 9 Image Classes 159 Several other “Objective Toolkit” classes are also part of the Stingray Foundation Library— including color well controls and certain parts of the Layout Manager. 9.2 The Image Classes Figure 85 shows the hierarchy of the Objective Toolkit image classes. Figure 85 – Objective Toolkit image class hierarchy CObject SECImage SECDib SECGif SECJpeg SECPcx SECTarga SECTiff 9.2.1 SECImage SECImage is an abstract base class from which the individual image file format classes derive. The SECImage class contains most of the functionality provided by Objective Toolkit’s image support such as reading, writing, format conversion, and limited image manipulation. The format-specific derivatives act as translators for reading the format-specific image bits into the internal SECImage format and writing from the internal SECImage format to the specific image format. For example, SECPcx is an SECImage derivative that can read a .PCX file, convert it to the internal SECImage format and then write the SECImage format out as a .PCX file. This architecture isolates image format-specific code to the derived image classes and places general image handling routines in the SECImage base class. Because SECImage is an abstract base class, you cannot create an instance of it. Instead, use one of the image format-specific derivative classes: SECDib, SECGif, SECJpeg, SECPcx, SECTarga, or SECTiff. 160 9.2.2 SECDib The SECDib class supports the reading and writing of the Windows Device Independent Bitmap (.DIB) format. Typically, these files have .bmp or .dib extensions. SECDib supports all color and compression formats for DIBs. 9.2.3 SECGif The SECGif class supports reading and writing for Graphic Information File (.GIF) images. The GIF format is defined by CompuServe and is currently one of the standard image types used by Internet World Wide Web (WWW) browsers. Support is also included for interlaced as well as transparent GIF images. 9.2.4 SECJpeg The SECJpeg class supports the JPEG compression scheme found in JFIF (JPEG File Interchange Format) files, which is used in many Internet World Wide Web browsers and professional image applications. SECJpeg is based on the version 1.02 JFIF standard. Objective Toolkit includes support for progressive JPEG images. 9.2.5 SECPcx The SECPcx class supports the PC eXchange (PCX) format that many Windows graphics applications use for exchanging images. The current version of SECPcx only supports writing out to 256 colors. 9.2.6 SECTarga The SECTarga class handles the TGA (TARGA Image File) format as defined by Truevision. TGA is used in video-capturing applications and supports 24-bit color. SECTarga only supports 24-bit-perpixel images. 9.2.7 SECTiff The SECTiff class can read and write Tagged Image File Format (TIFF) files. TIFF files are common in scanning applications. SECTiff supports version 6.0 of the TIFF standard and includes support for all image depths and compression schemes. Chapter 9 Image Classes 161 9.3 SECImage Format SECImage stores its image data internally in device independent bitmap (DIB) format. This has some unique advantages and disadvantages. The main disadvantage is that the image is not compressed in any way. The advantages include the ability to access data members such as m_lpSrcBits (pointer to the actual data bits) and m_lpBMI (pointer to the BITMAPINFO structure) directly. You can use these data members directly in Windows APIs such as StretchDIBits(). If you want to modify the SECImage format for editing or other purposes, we suggest you review how the image manipulation routines (for example, Rotate90() and FlipVert()) are implemented to become familiar with the internal format. 162 9.4 Using the Image Classes Typically, an image class loads the image from a file, renders the image on the display with the StretchDIBits() API, performs any number of image processing manipulations on the image, and then saves the modified image to a new file. The sections that follow explain how to perform each of these actions with the SECImage family of classes. SECImage is serializable, so if you include an SECImage instance in your document, it is serialized with the rest of your data. 9.4.1 To read an image from a file The standard method for reading images is LoadImage(). LoadImage() is a virtual function in SECImage that is implemented by each format-specific derived image class. The SECImage derivative classes read image data from a format-specific image file and translate it to the intermediate format used by the parent SECImage class. When an image is loaded, the m_pPalette member of SECImage is created, which creates a palette for the loaded image based on the loaded color map. The following code loads a .PCX file. // . . . SECPcx pcx; if (pcx.LoadImage(“check.pcx”) == FALSE) ASSERT(1); //LoadImage FAILED! //Now we have an image loaded, and it can be displayed 9.4.2 To view GIF/TIFF images Two Objective Toolkit classes use LZW compression: SECGIF and SECTiff. These classes contain stub routines where the LZW compression algorithms belong. As a result, if you attempt to load or save images of GIF or TIFF format, an error occurs at run time. GIF is a popular format for graphics to be viewed in Web browsers, while TIFF (tag-based image file format) is a digital data format compatible with a variety of scanners, faxes, and other image-processing applications. We offer a GIF/TIFF Unlock Pack (lzwcode.zip) that allows you to replace the stubbed classes with the source code of the algorithm. 9.4.3 To display an image Once an image has been successfully created or loaded from an image file, you can display it on any device context (DC) by treating the data contained by SECImage as a device independent bitmap (DIB). A DIB is typically rendered to a DC via the StretchDIBits() API. Objective Toolkit encapsulates the StretchDIBits() call through its own StretchDIBits() method. The advantage of using the Objective Toolkit encapsulated StretchDIBits() is that it increases the resolution of the image you’re manipulating to a resolution greater than the one available on your DC. In this case, Objective Toolkit automatically displays the image correctly. For example, if you Chapter 9 Image Classes 163 attempted to display a 24-bits-per-pixel image on an 8-bits-per-pixel display in the WIN32 environment, Objective Toolkit would make a call to the CreateHalftone() palette API, which creates a palette with the closest matching color values to the image in memory. The approximated palette contains the standard colors for the image. In the 16-bit environment, Objective Toolkit uses its own internal quantize routine to perform an operation similar to CreateHalftone(). The following code displays an image. void CImageView::OnDraw(CDC* pDC) { SECImage * pImage = pDoc->GetImage(); CPalette *pOldPalette; // If a palette has been created for the image, select it. if (pImage->m_pPalette) pOldPalette = pDC->SelectPalette(pImage->m_pPalette, TRUE); // Call encapsulated StretchDIBits API pImage->StretchDIBits(pDC, 0,0, pDoc->GetDocSize().cx, pDoc->GetDocSize().cy, 0,0, pImage->m_dwWidth, pImage->m_dwHeight, pImage->m_lpSrcBits, pImage->m_lpBMI, DIB_RGB_COLORS, SRCCOPY ); // Restore the palette if (pImage->m_pPalette) pDC->SelectPalette(pOldPalette, TRUE); } 9.4.4 To convert an image The SECImage family of classes allows you to convert from one image format to another easily. For example, if you wanted to convert an GIF image to an JPEG image, you would add a few lines of code using the Objective Toolkit image classes. Image format conversion is performed using the ConvertImage() method. Given a source image and a destination image, ConvertImage() can use image instances interchangeably. For example, a .PCX image can be loaded into memory through an instance of the SECPcx class and then converted with an instance of any other derived image class. Unlike CopyImage(), ConvertImage() does not duplicate image data. Once an image is converted, you can safely delete the source image class instance. The following code demonstrates how to convert an SECPcx image into an SECGif image: void TestConversion(SECPcx *pSrc) { // Create destination instance SECGif *pDest = new SECGif(); pDest->ConvertImage(pSrc); // Now pDest is a valid image, while pSrc // contains no data... pDest->SaveImage(“convert.gif”); 164 // Convert back to validate the source image again. pSrc->ConvertImage(pDest); // delete the destination, will not affect // pSrc data members at all delete(pDest); } 9.4.5 To copy an image SECImage supplies a CopyImage() routine that allows you to create a duplicate of a source image. You can use this routine to make a copy of an image before allowing your user to change it. Then, you can allow the user to revert to the original image. CopyImage() behaves the same way the ConvertImage() method does, except it does not alter the source image so the user can use it after the copy is performed. The following code copies a TIFF image to a JPEG image: void TestCopy(SECTiff *pSrc) { // create the destination image SECJpeg *pJpeg = new SECJpeg(); if (!pJpeg) return; if (!pJpeg->CopyImage(pSrc)) { delete pJpeg; return; } // Now you have two independent copies of the // same image in 2 different formats // destruction of object will destroy the // copied image data, while pSrc is left intact. delete pJpeg; } Images often require a large amount of memory. Creating a copy does not perform any compression or savings in memory. In effect, it requires twice the amount of memory. 9.4.6 To manipulate an image Once you create an image and load it into memory, you can manipulate the image through a number of SECImage methods. For example, you can flip and rotate the image. You can flip an image vertically or horizontally by calling the FlipVert() or FlipHorz() methods. You can rotate images 90 degrees counter-clockwise by calling the Rotate90() method. Chapter 9 Image Classes 165 ContrastImage() accepts a signed integer to modify the contrast of the image. A positive value increases the sharpness of the image and a negative value decreases its sharpness. CropImage() accepts coordinates of a clipping rectangle used to crop the image currently loaded in memory. Coordinates are passed in as the left, top, right and bottom positions. If positions are passed that extend beyond the maximum reach of the current image, the clipping coordinates are limited to the rightmost and bottommost positions of the image. The following code demonstrates how to use the image manipulation methods. void Manipulate(SECPcx *pSrc) { // First rotate it pSrc->Rotate90(); // Next Flip it on the horizontal axis pSrc->FlipHorz(); // Flip again on the vertical axis pSrc->FlipVert(); // Dull the image by decreasing contrast pSrc->ContrastImage(-1); // Then crop the image to a hypothetical region pSrc->CropImage(50, 50, 100, 100); } 9.4.7 To write an image to a file SECImage and derivatives allow you to save an image to a file via the SaveImage() method. SaveImage() succeeds only when a legitimate image is loaded in the image class. The following code demonstrates how to save a .PCX image to a file named new.pcx. if (pcx.SaveImage(“new.pcx”) == FALSE) ASSERT(1); //SaveImage failed! 9.4.8 To convert to a CBitmap object Using Objective Toolkit, you can create a device-specific bitmap from an SECImage instance. To do this, ensure that then image is at the same resolution as the device. Otherwise, the CBitmap is created for an incorrect display type. You can create a CBitmap object with the MakeBitmap() method, which returns a pointer to a new CBitmap object. MakeBitmap() accepts a pointer to the current device context as an argument (CDC pointer). 9.4.9 To convert from a CBitmap object When you perform GDI drawing functions to a device context (CDC object), you need to convert the image of the bitmap selected in the device context to the SECImage format. To do so, use the CreateFromBitmap() method, which accepts CBitmap and CDC pointers as arguments. The cre- 166 ated SECImage instance contains the same dimensions and depth as the CBitmap itself. If the CDC is a memory device context, ensure that you clear the bitmap with a GDI call before making the call to CreateFromBitmap(). 9.4.10 To create from a CDC object This is a two-step process: 1. Create a CBitmap object from your CDC. 2. Instantiate an SECImage object and call SECImage::CreateFromBitmap(). You can create a CBitmap with the following code: CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dc); CDC dcMem; dcMem.CreateCompatibleDC(&dc); CBitmap* pOldBitmap = dcMem.SelectObject(&bitmap); // Todo: draw to the memory dc here... dcMem.SelectObject(pOldBitmap); SECImage::CreateFromBitmap() takes a pointer to a CBitmap object and to a CDC object to copy the image data from the CBitmap. 9.4.11 To load an image from a resource The SECImage base class does not support direct loading of image data from a resource; however, you can load images indirectly. After you have imported your image file as binary data in the resource editor, do the following to load it: 1. Obtain a pointer to the resource data. 2. Attach the data to a CMemFile object. 3. Instantiate an SECImage-derived object and call LoadImage() using the CMemFile. The following section of code demonstrates this: // The image is stored as a resource in file format. HINSTANCE hinst = AfxGetInstanceHandle(); HRSRC hRes = FindResource(hinst, szResNavn, szResType); if (hRes == NULL) { TRACE2("Couldn't find restype %s resource %s!\n", szResType,szResNavn); return FALSE; } Chapter 9 Image Classes 167 // need the pointer to the image data and it's length DWORD len = SizeofResource(hinst, hRes); BYTE* lpImage = (BYTE*)LoadResource(hinst, hRes); ASSERT(lpRes); // CMemFile is CFile-derived, and will soon be used to // to load the image using SECImage::LoadImage CMemFile* pImgMemFile = new CMemFile(); SECJpeg* pJpeg; \\ let's assume I know the image will be JPEG // Attach the image data to a CMemFile, which will allocate space. // ImageBufLen is the size of the image buffer pImgMemFile->Attach(lpImage, lImageBufLen); // now use the CMemFile to load into the SECJpeg object // for manipulation or display pJpeg = new SECJpeg(); if (!pJpeg->LoadImage(pImgMemFile)) { TRACE0("Couldn't LoadImage"); return FALSE; } // can delete the CMemFile now since LoadImage allocated its own // space delete pImgMemFile; pImgMemFile = NULL; FreeResource((HANDLE)lpRes); return(str); . . . 9.4.12 To stream image data Although the SECImage class does not directly support streaming, you can save SECImage to a CMemFile and then stream the data from CMemFile. // The image is stored in the database in file format. It has // been retrieved here to a buffer called pImageInBuffer; LPBYTE lpImage = pImageInBuffer; CMemFile* pImgMemFile = new CMemFile(); \\ CFile derived class SECJpeg* pJpeg; \\ let's assume I know the image will be JPEG // Attach the image data to a CMemFile, which will allocate space. // ImageBufLen is the size of the image buffer pImgMemFile->Attach(lpImage, lImageBufLen); // now use the CMemFile to load into the SECJpeg object // for manipulation or display pJpeg = new SECJpeg(); if (!pJpeg->LoadImage(pImgMemFile)) error("Couldn't LoadImage"); 168 // can delete the CMemFile now since LoadImage allocated its // own space delete pImgMemFile; pImgMemFile = NULL; // display the image in the pJpeg and or whatever manipulations // are required to it ... // now save it back to a CMemFile pImgMemFile = new CMemFile; if (!pJpeg->SaveImage(pImgMemFile)) error("Couldn't SaveImage"); // can now get the image back to the database via the lpImage ptr // this will store the image length in lImageBufLen and the // image itself // into lpImage lImageBufLen = pImgMemFile->GetLength(); lpImage = pImageMemFile->Detach(); // probably don't want to delete the pImageMemFile until you've // copied lpImage to the database or at lease to some other buffer // since Detach() just returns a pointer to CMemFile's buffer. Ensure that you set the nGrowBytes for the CMemFile. The default is 1024. Chapter 9 Image Classes 169 9.5 Key Image Methods Here is an overview of some of the key SECImage methods. Table 28 – Methods for SECImage 9.6 SECImage method Description LoadImage() Loads an image from file. To load a GIF, use SECGif; to load a TIFF, use SECTiff. SaveImage() Saves an image to file. The format of the image is based on the type of the object through which SaveImage is called. For example, SECGif::SaveImage writes the GIF format. CopyImage() Creates a copy of an image. ConvertImage() Converts one image to another type. The conversion actually takes place during writing or reading of the image because SECImage uses a standard internal format for all image types. ConvertImage() copies the internal image and deletes the original without the overhead of actually copying the data. FlipHorz() Flips an image horizontally. FlipVert() Flips an image vertically. Rotate90() Rotates an image 90 degrees counter-clockwise. ContrastImage() Sharpens or dulls an image. NumColors() Returns the number of colors used by the image. Image Sample The Objective Toolkit imagetst sample (samples\toolkit\MFC\image\imagetst) demonstrates all of the SECImage features covered here. Be sure to load in several images to see how to print preview and print them using the MFC document/view architecture. 170 Chapter 10 MDI Alternatives 10.1 Overview Objective Toolkit offers the following MDI alternatives and enhancements: Multiple Top-level Interface (MTI) Floating Document Interface (FDI) Workbook Document Interface (WDI) Although you can easily convert your MDI application to any of our MDI alternatives, you need to consider your application users when you select an interface. Our Multiple Top-Level Interface (MTI) and Floating Document Interface (FDI) are radically different from MDI, whereas our Workbook Document Interface (WDI) augments the capabilities of MDI without changing the interface drastically. Chapter 10 MDI Alternatives 171 10.2 Benefits of MDI Alternatives With the release of Windows 95, Microsoft made it known in The Windows Interface Guidelines for Software Design that they are moving away from MDI in their Office products; however Microsoft has not provided Windows operating-system-level support or MFC support for any of the MDI alternatives described in their design guide. Currently, MFC only supports the Multiple Document Interface (MDI) and Single-Document Interface (SDI), which are waning in popularity. Objective Toolkit proves you with three additional alternatives: MTI, FDI, and WDI. 10.2.1 Multiple Top-level Interface (MTI) MTI is a combination of SDI and MDI. As in SDI, each top-level window manipulates one document. As in MDI, the user can have multiple documents open simultaneously. MTI creates a new top-level window for each new document. MTI departs from the MDI model in which one parent frame owns and contains every document window. From the user’s standpoint, an MTI application most closely resembles an SDI application because he can only associate one document with a frame. 172 Figure 86 – Example of a MTI Application However, note that when the user loads or creates document, an additional frame is created to hold the document instead of loading the document into the existing frame. Chapter 10 MDI Alternatives 173 Figure 87 – Example of an MTI Application (new document) MTI-based applications create document windows that float freely on the desktop. This approach is common in other GUI operating systems such as the OSF/Motif windowing system for UNIX. It has also become a popular Windows interface. For example, you can easily activate MTI windows from the Windows 98 taskbar. 10.2.2 MTI Class – SECToplevelFrame The SECToplevelFrame class is the basis for the MTI MDI-alternative. MTI applications derive from SECToplevelFrame whereas an MDI application would derive from CMDIChildWnd. The following figure is the hierarchy for SECToplevelFrame. Figure 88 – Objective Toolkit MTI Class Hierarchy CFrameWnd SECFrameWnd SECToplevelFrame 174 10.2.2.1Using MTI The following sections describe how to create an MTI application from a new or existing MFC project. 10.2.2.2To convert an existing SDI application to MTI 1. Replace the base class of your CFrameWnd-derived class with SECToplevelFrame. 2. Replace the instantiation of a CSingleDocTemplate in the InitInstance() method of your CWinApp-derived class to CMultiDocTemplate. 10.2.2.3To convert an existing MDI application to MTI 1. Replace the base class of each existing MDI child frame (CMDIChildWnd) with SECToplevelFrame. This ensures that the MDI child windows retain all of their previous capabilities and can float freely on the desktop instead of being confined to the MDI frame window. 2. Remove the CMainFrame class and all references to it from your application. Because MTI has no equivalent to an MDI frame window, your CMainFrame class has no role in a MTI implementation. If you have a significant amount of code in your CMainFrame class, you need to consider moving the code to a new location. 3. The next step is to modify the OnInitInstance() method of your CWinApp-derived class. The OnInitInstance() method typically includes code to instantiate a document template and the MDI frame window. You must remove the instantiation of the MDI frame window and all references to it from the application. 4. In most cases, it is not necessary to modify the code in OnInitInstance() method which instantiates the document template. The CMultiDocTemplate is still used by MTI because it does not include anything that is MDI-specific. It manages multiple documents (document/view documents, not MDI documents) and, in that capacity, remains useful for MTI without modification. 5. Override the OnCreate() method of your SECToplevelFrame-derived class or classes and create your control bars (toolbars, status bars, and more). CFrameWnd::OnCreate() is the method that usually creates these objects and SECToplevelFrame-derived classes are no exception. 10.2.2.4To create a new MTI-based application 1. Create a new MDI application using the Visual C++ | MFC App Wizard. 2. Follow the steps described in Section 10.2.2.3. 10.2.2.5Customizing MTI The following sections show you how to modify the default behavior of an MTI application and present some information about message handling. Chapter 10 MDI Alternatives 175 10.2.2.6To load the document into the initial, empty frame By default, MTI-based applications behave the same way as MDI apps. For example, when you open a MTI application one top-level frame window containing an empty, untitled document appears. If you select Open from the File menu and select a file, a new top-level window is created, and the document is displayed therein. When you open a file, you are actually opening two windows. One window is empty whereas the other window contains the document of interest. This is exactly the same behavior MDI applications exhibit. In some applications, it is preferable to load the document into the initial empty frame rather than create a new one. 1. Derive a class from CMultiDocTemplate and override its OpenDocumentFile() method. 2. You may also want to change the standard call to the OnFileNew() method in CYourApp::InitInstance() to OnFileOpen() so your application will display an open file instead of an empty document in the first top-level frame when you start it. 10.2.2.7Message Dispatching in an MTI Application There are a couple of key MTI data members that you need to consider if you’re setting up message dispatching for your MTI application: SECToplevelFrame::s_tlfList. A static list of open top-level frame windows. You may need to dispatch a message or member function call to every top-level frame window on the desktop. Only those owned by the MTI application in question are accessible. Iterate over the top-level frames in this list and dispatch messages to them individually. If you are using MTI in a DLL, use the accessor functions GetTLFList() and SetTLFList(). theApp.m_pMainWnd. In SDI and MDI applications, only one main frame window is allowed by definition. Consequently, m_pMainWnd is initialized at startup and remains constant until the application exits. With MTI, the m_pMainWnd is constantly changing to reflect the top-level frame that currently has focus. If you need to dispatch a message or member function call to the active top-level frame, reference this variable. 10.2.2.8Other Uses For SECToplevelFrame The SECToplevelFrame class can act as more than a MDI alternative in your application. Because SECToplevelFrame is only varies slightly from CFrameWnd, you can use it anywhere you would have used CFrameWnd as a base class. SECToplevelFrame contains nothing that binds it to the document/view architecture, so you can use it in applications that do not adhere to doc/view architecture. When you use this class as a specialized frame window instead of an MDI alternative, SECToplevelFrame gives you the capability to create a top-level frame with any arbitrary client. The top-level frame is given its own entry in the Windows 98 task bar, and it can remain open when other application windows are iconified. 176 10.2.2.9MTI Sample The Objective Toolkit mt_scrib sample (<stingrayinstalldir>\Samples\Toolkit\MFC\MDI\MTScribble) shows how to convert the MFC tutorial, Scribble, from MDI to MTI. 10.2.3 Floating Document Interface (FDI) FDI is like MDI except in FDI the MDI children can float freely on the desktop. In MDI, the MDI children are confined to the MDI parent window. As with MDI, you can have multiple open documents in FDI. FDI creates a new floating child window for each new document. Each FDI child window appears as a top-level window and manipulates one document. Optionally, each FDI child window can include a menu bar containing menu items specific to that window. Figure 89 – Example FDI application As for the main window, all menu items need to apply at the application level or to all FDI children; otherwise, focus becomes an issue. For example, if you activate an FDI child and then pick a menu item from the main application window, the FDI child loses activation and consequently its menu pick. FDI-based applications have a look-and-feel that is similar to Microsoft Visual Basic. Chapter 10 MDI Alternatives 177 10.2.3.1Differences between FDI and MTI Because FDI allows you to place document windows on the desktop, its interface is somewhat similar to MTI. In MTI, no main window owns all the other windows in the application. However, the concept of a main frame window remains to serve as a menu strip or primary application window. Because MTI document windows are root-level windows, they are given their own Windows 98 task bar entries. FDI document windows are not root-level windows. You can iconify MTI document windows independently of all other windows. In FDI, when the main application window is iconified, all the FDI children are iconified as well. 10.2.3.2FDI Classes FDI is implemented in two classes that both derive from SECFrameWnd. This hierarchy is as follows. Figure 90 – Objective Toolkit FDI class hierarchy CFrameWnd SECFrameWnd SECFDIChildWnd SECFDIFrameWnd 10.2.3.3SECFDIChildWnd The SECFDIChildWnd class is a document window that is similar to a MDI child window, except it can float freely on the desktop whereas a MDI child window is tied to its parent frame. FDI applications derive from SECFDIChildWnd. MDI applications derive from CMDIChildWnd. 10.2.3.4SECFDIFrameWnd The SECFDIFrameWnd class is a main frame window that adds support for the Window menu and the Windows… dialog. FDI applications derive from SECFDIFrameWnd. MDI application would derive from CMDIFrameWnd. 10.2.3.5Using FDI The following sections tell how to use FDI in new and existing projects. 10.2.3.6To convert an existing SDI application to FDI 1. First, convert your SDI application to an MDI application. This transition is typically comprehensive. It may require you to re-derive several of your application’s classes. 2. Follow the steps described in Section 10.2.2.3. 178 10.2.3.7To convert an existing MDI application to FDI 1. Replace CMDIChildWnd, the base class of all existing MDI child frames, with SECFDIChildWnd so that the MDI child windows retain all of their previous capabilities and float freely on the desktop instead of being confined to the MDI frame window. 2. Replace CMDIFrameWnd, the base class of your main frame class, with SECFDIFrameWnd. 3. If you want your main frame window to serve as a menu strip, override SECFDIFrameWnd::PreCreateWindow() and then specify the initial window size and position. 4. Override the OnCreate() method of your SECFDIChildWnd-derived class or classes and create your control bars (toolbars, status bars, and more). 10.2.3.8To create a new FDI-based application 1. Generate an MDI application using AppWizard. 2. Follow the steps in Section 10.2.3.7. 10.2.3.9FDI Sample The Objective Toolkit fdi sample (<stingray-installdir>\Samples\Toolkit\MFC\MDI\FDI) illustrates the use of the SECFDIChildWnd and SECFDIFrameWnd classes. 10.2.4 Workbook Document Interface (WDI) The WDI classes enhance MDI by allowing the user to place every document in a tabbed window. This reduces MDI modality. All of the documents are part of a workbook of worksheets. The user can open a specific worksheet by clicking a tab instead of selecting the Window menu and searching for it. Chapter 10 MDI Alternatives 179 Figure 91 – Example MDI Application Because WDI adds an optional Workbook View to MDI, there is no cost associated with converting your MDI application to WDI. WDI enhances your MDI application without losing any functionality or fundamentally changing the MDI user interface. In other words, a WDI application with workbook viewing mode deactivated is equivalent to MDI. 10.2.4.1Adding Flat Style Drawing to the Workbook Window New ‘flat’ style drawing has been added to the workbook window. In order to enable flat style drawing, you need to call the SetFlatStyleMode function (a member of the SECWorkbook class). Figure 92 – A Workbook Window with Flat Style enabled To view sample code which shows how to set the flat drawing style, please see the CMainFrame::OnCreate function implementation in MAINFRM.cpp, VIZ sample: SetFlatStyleMode(TRUE); 180 The Viz sample demonstrates this feature, and can be found at <stingrayinstalldir>\Samples\Toolkit\MFC\Docking\Viz. 10.2.4.2Adding Flat Style Drawing to the Shortcut Window New ‘flat’ style drawing has been added to the shortcut window. In order to enable flat style drawing, you need to call the SetFlatStyleFunction function (a member of the SECShortcutBar class). Figure 93 – A Shortcut Window in Mouse Hover mode with Flat Style Enabled To view sample code which shows how to set the flat drawing style, please see the CShortcutListDockBar::OnCreate function implementation in LSTDBAR.cpp, VIZ sample: m_scListBar.SetFlatStyleMode(TRUE); The Viz sample demonstrates this feature, and can be found at <stingrayinstalldir>\Samples\Toolkit\MFC\Docking\Viz. 10.2.4.3Adding Flat Style Drawing to the 3D Tab Control Window New ‘flat’ style drawing has been added to the 3D tab control window. In order to enable flat style drawing, you need to set the TWS_FLATSTYLE window style while creating the tab control window. Alternatively, you can enable flat style drawing at a later time by calling the SetTabStyle function (a member of the SEC3DTabWnd class). Figure 94 – A 3D Tab Control Window with Flat Style Enabled To view sample code which shows how to enable flat style drawing for tabbed control windows during creation time, please see the ProjectWorkspaceWnd::OnCreate function implementation in PRJBAR.cpp fo the Viz sample: m_wndTab.Create(this,WS_CHILD|WS_VISIBLE|TWS_TABS_ON_BOTTOM| TWS_FLATSTYLE); Chapter 10 MDI Alternatives 181 To view sample code which shows how to toggle flat style drawing for tabbed control windows, please see the CChildFrame2::OnSwitchFlat function implementation in CHLDFRM2.cpp of the TabDemo sample: m_bFlatStyle = !m_bFlatStyle; DWORD dwStyle = m_tabWnd.GetTabStyle(); if( !m_bFlatStyle ) dwStyle &= (~TWS_FLATSTYLE); else dwStyle |= TWS_FLATSTYLE; m_tabWnd.SetTabStyle( dwStyle ); The TabDemo sample does not ship with the product. For information on where you can obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. The Viz sample also demonstrates this feature. It can be found in at <stingrayinstalldir>\Samples\Toolkit\MFC\Docking\Viz directory. 10.2.4.4WDI Classes WDI support is provided by a combination of three MFC extensions: SECWorkbookWnd, SECWorksheetWnd, and SECWorkbookClientWnd. Figure 95 – Objective Toolkit WDI Class Hierarchy CWnd CMDIFrameWnd SECMDIFrameWnd SECWorkbookWnd CFrameWnd CMDIChildWnd SECMDIChildWnd SECWorksheetWnd SECWorksheetClientWnd 10.2.4.5SECWorkbookWnd The SECWorkbookWnd class is derived from SECMDIFrameWnd. It adds the workbook interface (WDI) enhancements. Derive your main frame window class from SECWorkbookWnd if you want the workbook interface enhancements. 182 10.2.4.6SECWorksheetWnd The SECWorksheetWnd class is derived from SECMDIChildWnd. It adds the workbook interface (WDI) enhancements. Derive your MDI child windows from SECWorksheetWnd if you want to implement the workbook interface enhancements. 10.2.4.7SECWorkbookClientWnd The SECWorkbookClientWnd subclasses the MDI client window and is implemented as a part of the workbook interface. SECWorkbookClientWnd allows the WDI framework to hook into CFrameWnd’s window layout calculation and specify a client area that allots space for drawing the border and tabs. 10.2.4.8To convert an existing MDI application to WDI 1. Replace CMDIChildWnd, the base class of all existing MDI child frames, with SECWorksheetWnd so the MDI child windows retain all of their previous functionality and become tabbed worksheets. 2. Replace CMDIFrameWnd, the base class of your CMainFrame class, with to SECWorkbookWnd. 3. Replace the base class of all toolbars derived from CToolBar with SECToolBar. 4. Replace CStatusBar, the base class of the status bar, with SECStatusBar. 5. Call SetWorkbookMode(TRUE) in CMainFrame::OnCreate(). 10.2.4.9Customizing WDI SECWorkbookWnd has overridable functions that you can use to customize several aspects of WDI including: The order in which tabs are displayed The tab label The tab icon The interface’s general appearance 10.2.4.10To change the tab display order Override the AddSheet() and RemoveSheet() member functions. These members are expected to assign values to SECWorksheetWnd::m_nPosition for every worksheet in your workbook. In your override, you can assign any tab position to newly created worksheets. 10.2.4.11To draw a different worksheet tab label Override the SECWorkbookWnd::GetTabLabel() function. Chapter 10 MDI Alternatives 183 10.2.4.12To change the icon Override the SECWorkbookWnd::GetTabIcon() function. 10.2.4.13To change the appearance of the tabs Override the SECWorkbookWnd::OnDrawTab() and SECWorkbookWnd::OnDrawTablconAndLabel() functions. 10.2.4.14Key WDI Methods and Data Members A summary of the key methods and data members that you can use to customize the WDI is presented in the table below. Table 29 – Key Methods and Data Members for WDI Member Description SECWorksheetWnd::m_nPosition A data member that specifies the position of the tab associated with this worksheet within the row of tabs in the workbook. By default, the application display the tabs by order of creation. You can change the order by assigning a value to this member. SECWorkbookWnd::AddSheet() A virtual method that is called whenever a new MDI child window is created. This member creates a new tab associated with the new MDI child and assigns it a position within the row of tabs. To change the order in which tabs are displayed, override the AddSheet() member and set the SECWorksheetWnd::m_nPosition member. 184 SECWorkbookWnd::RemoveSheet() A virtual method called whenever an MDI child window is destroyed. Override this member if you require additional work to occur whenever a sheet is removed from the workbook. SECWorkbookWnd::GetTabLabel() A virtual method that returns the label to be drawn on the tab. By default, this label is the title of the MDI child’s active document. If this label is not appropriate for your application, override this member and return the label you need. Table 29 – Key Methods and Data Members for WDI (Continued) Member Description SECWorkbookWnd::GetTabIcon() A virtual method that returns the icon to be drawn on the tab. If NULL is returned, no icon is drawn. By default, this icon is the one associated with the MDI child frame via the resource file. If you prefer a different icon, override this member and return a different icon. SECWorkbookWnd::OnDrawTab() A virtual method that draws a blank tab at the position within the row of tabs specified by SECWorksheetWnd::m_nPosition. By default, the tab has a 3D look. If you want to change the look of the tab, override this member and draw the tab with the look you prefer. SECWorkbookWnd::OnDrawTabIcon AndLabel() A virtual method that renders the icon and tab label on top of the blank tab drawn by SECWorkbookWnd::OnDrawTab(). Override this member if you require the tab’s inscription to be rendered differently. Chapter 10 MDI Alternatives 185 186 Chapter 11 Shortcut Bar 11.1 Overview The Objective Toolkit Shortcut Bar closely resembles the Microsoft Outlook Bar™. You can use it as a container class to embed any CWnd-derived objects. Because the shortcut bar is a CWnd-derived class, you can embed it anywhere. For example, you could easily embed it in a docking window, in a pane of a splitter window, or even in the entire client area of the frame window. The shortcut bar is a variant of the tab window. However, instead of displaying tabs, it displays horizontal bars that are stacked vertically. The window representing the currently activated bar is displayed directly beneath the bar. Instead of instantly tabbing to the selected window, the process of displaying a window when a bar is selected is animated. Figure 96 – Shortcut Bar Displaying a List of Icons Chapter 11 Shortcut Bar 187 The following figure is of a shortcut bar that holds a tab control that, in turn, holds a tree control. The shortcut bar is held by a docking window. Figure 97 – Example Shortcut Bar with an Embedded Window 188 11.2 The Shortcut Bar Classes The Shortcut bar is implemented in the SECShortcutBar class, which is derived from CWnd. SECShortcutBar utilizes the classes SECBar or SECListBar and SECShortcutListCtrl to display the bars and windows that it contains. Figure 98 – Objective Toolkit shortcut bar class hierarchy CWnd SECShortcutBar 11.2.1 SECShortcutBar The SECShortcutBar class derives from CWnd, and it acts as a container class for other CWndderived objects. Because it is CWnd-derived, SECShortcutBar can also be held by other CWndderived objects. The bars that are displayed in the SECShortcutBar window are either instantiations of SECBar or SECListBar, or a combination, depending on the overload of AddBar(), which was called to create them. If the SECShortcutBar is acting as a container for an arbitrary CWnd object, the bar is of type SECBar. If the SECShortcutBar is a container for a Microsoft Outlook Bar™-like list of icons, the bar is of type SECListBar and the window that displays the list of icons is of type SECShortcutListCtrl. The following figures are of these contained bars and windows. Figure 99 – Containment of SECBar in SECShortcutBar Chapter 11 Shortcut Bar 189 Figure 100 – Containment of SECListBar and SECShortcutListCtrl in SECShortcutBar 11.2.2 SECBar An SECBar object represents a single bar window that a user can select. It is displayed inside the SECShortcutBar window. An SECBar object is created when the version of AddBar() that accepts a CWnd parameter is called. A bar that contains the embedded CWnd object is added to the shortcut bar. 11.2.3 SECListBar The SECListBar class derives from SECBar and represents a single bar window that a user can select. It is displayed inside the SECShortcutBar window. Every time a bar that contains an embedded SECShortcutListCtrl (displays icons like the Microsoft Outlook Bar™) is added, an SECListBar is created. If you use an SECBar derivative class, you can mix bars of different types in the shortcut bar. For example, one bar can contain an SECListBar and another can contain an SECBar with an SECTreeCtrl associated with it. 11.2.4 SECShortcutListCtrl The SECShortcutListCtrl contains the list of labeled icons that will appear in a pane in the Shortcut bar. 190 11.3 Shortcut Bar Styles The following table lists the short bar styles that you can specify in calls to the Create() or ModifyBarStyles() methods. Table 30 – Shortcut Bar Styles Shortcut bar style Description SEC_OBS_VERT Orients the shortcut bar vertically. This is the default orientation. SEC_OBS_HORZ Orients the shortcut bar horizontally. This is not a default style. SEC_OBS_BUTTONFEEDBACK Draws a button-down look for the bar when it is pressed. This is not the default style. SEC_OBS_CONTEXTMENU Enables the display of a context menu when the user clicks the right mouse button on the shortcut bar. You can associate the displayed menu with the shortcut bar via the SECShortcutBar::SetBarMenu() method. This is not the default style.The context menu associated with this flag does not appear when an SECShortcutListCtrl window is right clicked. SECShortcutListCtrl has its own context menu that is displayed in response to a right click. SEC_OBS_ANIMATESCROLL Enables animated scrolling. This is not the default style. SEC_OBS_BUTTONFOCUS Draws the focus rectangle on the contained bars when they are given the focus. This is not the default style. SEC_DEFAULT_OUTLOOKBAR The default style for the shortcut bar when none is specified. This style is the same as specifying (WS_VISIBLE | WS_CHILD | SEC_OBS_VERT). Chapter 11 Shortcut Bar 191 11.4 Shortcut Bar Notification Messages SECShortcutBar sends the following notification messages to its parent. Table 31 – SECShortcutBar’s Notification Messages Notification message Description NM_LB_REORDERED Notification sent when items are reordered. SEC_NM_ITEMCLICKED Notification of item clicked. 11.5 Shortcut Bar Callbacks You can override the following virtual functions to modify the default behaviors. Table 32 – Callback Methods for the Shortcut Bar 192 Callback method Description OnStyleChange() Called when styles changing. OnChangeBar() Called when trying to change bar. OnRemoveBar() Called when trying to remove a bar. OnDisableBar() Called when trying to disable a bar. OnEnableBar() Called when trying to enable a bar. OnCreatePaneWnd() Called after creating CWnd for bar object. OnCreateBar() Called after creating SECBar object. 11.6 Using SECShortcutBar The following topics describe how you can use SECShortcutBar in your projects. 11.6.1 To incorporate an SECShortcutBar into your application 1. In the class for the parent window, instantiate an SECShortcutBar object. For example: SECShortcutBar m_scBar; 2. During the creation of the parent frame, call the SECShortcutBar::Create() method to create the shortcut bar window. For example: m_scBar.Create(this, WS_CHILD | WS_VISIBLE | SEC_OBS_VERT | SEC_OBS_ANIMATESCROLL, IDC_SHORTCUTBAR); 11.6.2 To add selectable icons to a shortcut bar 1. In one of your classes, create two CImageList members to hold the normal and small bitmap images. You need to create both lists for the images to ensure the icons appear correctly in the shortcut bar. For example, in the declaration file type: CImageList m_imlNormal; CImageList m_imlSmall; In the implementation file, insert: m_imlNormal.Create( 32, 32, ILC_COLOR|ILC_MASK, 8, 1); m_imlSmall.Create( 16, 16, ILC_COLOR|ILC_MASK, 8, 1); CBitmap bmpNormal; CBitmap bmpSmall; VERIFY(bmpNorm.LoadBitmap(IDB_INBOX)); VERIFY(bmpSmall.LoadBitmap(IDB_INBOX_SMALL)); m_idInbox = m_imlNormal.Add( &bmpNorm, RGB(255,0,255)); m_imlSmall.Add( &bmpSmall, RGB(255,0,255)); bmpNorm.DeleteObject(); bmpSmall.DeleteObject(); VERIFY(bmpNorm.LoadBitmap(IDB_CONTACTS)); VERIFY(bmpSmall.LoadBitmap(IDB_CONTACTS_SMALL)); m_idContacts = m_imlNormal.Add( &bmpNorm, RGB(255,0,255)); m_imlSmall.Add( &bmpSmall, RGB(255,0,255)); Chapter 11 Shortcut Bar 193 bmpNorm.DeleteObject(); bmpSmall.DeleteObject(); 2. Use the AddBar() method to create an SECListBar with a label. For example: SECListBar* pListBar = NULL; pListBar = m_wndShortcutBar.AddBar(_T(“Fill With”)); ASSERT_VALID(pListBar); When you embed CWnd objects in the shortcut bar, the bar class is SECBar, not SECListBar. 3. Add the image lists to the SECListBar. pListBar->SetImageList( &m_imgNormal, LVSIL_NORMAL ); pListBar->SetImageList( &m_imgSmall, LVSIL_SMALL ); 4. Set the notification window if you want to receive notification messages. This is important if the shortcut bar is embedded in a splitter window. pListBar->SetNotifyWnd( this ); 5. You can also set some extra information about the list bar. //Little bit of info used to figure out who I am... pListBar->SetExtraData( (void*)FillPane ); 6. Add icons using the image ID’s in the image lists using either the InsertItem() or AddItem() methods. For example: //School Data records... pListBar->InsertItem( 0, _T("School Data"), 0 ); pListBar->SetItemData( 0, (DWORD)IDS_SCHOOL_FILE ); //Factory Data records pListBar->InsertItem( 1, _T("Factory Data"), 1 ); pListBar->SetItemData( 1, (DWORD)IDS_FACTORY_FILE ); 11.6.3 To embed a window in a shortcut bar Call the overloaded AddBar() method, which accepts a CWnd parameter. For example: m_scBar.AddBar( &m_tabWnd, _T("3D Tab Wnd") ); 11.6.4 To change the way the bars are drawn Derive a class from SECBar and then override the Draw() method. For example: class MyBar : public SECBar { //Required if you want to use your own bar objects //without deriving a new class from SECShortcutBar TOOLKIT_DECLARE_DYNCREATE(MyBar) 194 public: MyBar(); protected: virtual void Draw( CDC& dc ); }; //After creating the SECShortcutBar but before adding //any bar objects, add this line. m_wndShorcutBar.SetBarClass( RUNTIME_CLASS(MyBar) ); //If you don’t want to use your new bar class //for all the bars, you could do this CRuntimeClass* pOrigBarClass = m_wndShortcutBar.GetBarClass(); m_wndShortcutBar.SetBarClass( RUNTIME_CLASS(MyBar) ); // //Add as many bars of your new class as you want // //Prepare to add standard bars m_wndShortcutBar.SetBarClass( pOrigBarClass ); // //Add standard bars For an SECListBar derivative, the call to SetBarClass()in the above code should be replaced with a call to SetListBarClass(). 11.6.5 To allow shortcut bars to behave like buttons Use the SEC_OBS_BUTTONFEEDBACK style flag as a parameter for the Create() or ModifyBarStyles() methods. When this style is specified, the bar remains pressed, like a button, giving the user additional feedback on which bar was clicked. For example: m_scBar.ModifyBarStyle( 0, SEC_OBS_BUTTONFEEDBACK); See Section 11.3 for more style flags and their descriptions. 11.6.6 To enable context menus in a shortcut bar 1. Use the SEC_OBS_CONTEXTMENU style as a parameter for the Create() or ModifyBarStyles() methods. For example: m_scBar.ModifyBarStyle( 0, SEC_OBS_CONTEXTMENU); 2. Associate a context menu with the shortcut bar by calling the SetBarMenu() method. //Grab the zoom menu CMenu * pPopupMenu = AfxGetApp()->m_pMainWnd ->GetMenu()->GetSubMenu(3); ASSERT(pPopupMenu != NULL); m_scBar.SetBarMenu(pPopupMenu); Chapter 11 Shortcut Bar 195 The context menu specified here does not appear when an SECShortcutListCtrl window is right clicked. The SECShortcutListCtrl has its own context menu that is displayed when a right click occurs. See Section 11.3 for more style flags and their descriptions. 11.6.7 To have the shortcut bars display the focus rectangle Use the SEC_OBS_BUTTONFOCUS style flag as a parameter for the Create() or ModifyBarStyles() methods. For example: m_scBar.ModifyBarStyle( 0, SEC_OBS_BUTTONFOCUS); See Section 11.3 for more style flags and their descriptions. 11.6.8 To enable/disable animated scrolling 1. Use the SEC_OBS_ANIMATESCROLL style flag as a parameter for the Create() or ModifyBarStyles() methods. For example: m_scBar.ModifyBarStyle( 0, SEC_OBS_ANIMATESCROLL); 2. To control the scrolling behavior further, call the SetAnimationSpeed() and SetAnimationStep() methods. See Section 11.3 for more style flags and their descriptions. 11.6.9 To receive notifications when an icon in the SECShortcutListCtrl is clicked 1. In your parent window class (frame window, dialog, or other), add the ON_NOTIFY_RANGE macro to your message map macro list using the SEC_NM_ITEMCLICKED notification message. For example: ON_NOTIFY_RANGE( SEC_NM_ITEMCLICKED, SEC_IDW_BARCLIENT_FIRST, SEC_IDW_BARCLIENT_LAST, OnListBarClicked ) 2. Override SECShortcutBar::OnListBarClicked(). The prototype looks like this. afx_msg void OnListBarClicked( UINT nID, NMHDR* pNMHDR, LRESULT* pResult ); See Section 11.4 for more information on notification messages. 196 11.6.10To determine which icon is clicked in an SECListBar window When the user clicks an icon, it generates an SEC_NM_ITEMCLICKED notification. Typically, you would add a message map entry like the following to your listbar’s parent window (frame window, dialog). ON_NOTIFY_RANGE( SEC_NM_ITEMCLICKED, SEC_IDW_BARCLIENT_FIRST, SEC_IDW_BARCLIENT_LAST, OnListBarClicked ) The prototype for OnListBarClicked() is as follows: afx_msg void OnListBarClicked( UINT nID, NMHDR* pNMHDR, LRESULT* pResult ); The following is an example handler. void CMainFrame::OnListBarClicked( UINT nID, NMHDR* pNMHDR, LRESULT* pResult ) { ASSERT( pNMHDR->idFrom == nID ); ASSERT( nID >= SEC_IDW_BARCLIENT_FIRST ); ASSERT( nID <= SEC_IDW_BARCLIENT_LAST ); // Get the item clicked and display it SEC_SCNMHDR* pSCNMHDR=(SEC_SCNMHDR *)pNMHDR; TCHAR szMsg[80]; SECListBar* pListBar = (SECListBar*)&(m_scListBar.GetActiveBar()); SECShortcutListCtrl* pCtrl = pListBar->GetListCtrl(); CString strLab = pCtrl->GetItemText( pSCNMHDR->iSelItem,0); wsprintf(szMsg,_T("You clicked icon number %d\n Item Label: %s\n"), pSCNMHDR->iSelItem, strLab); AfxMessageBox(szMsg); // Apply focus back to the listbar such that // the hot-tracked state is properly cleaned // up from the recent activation change. ::SetFocus(pNMHDR->hwndFrom); } Chapter 11 Shortcut Bar 197 11.6.11To change the orientation of the shortcut bar at run time Call the SetAlignStyle() method. For example: //Change the SECShortcutBar to horizontal alignment m_wndShortcutBar.SetAlignStyle( SEC_OBS_HORZ ); //change back to vertical m_wndShortcutBar.SetAlignStyle( SEC_OBS_VERT ); 11.6.12To embed an SECShortcutBar into a splitter pane 1. Derive a new class from CSplitterWnd. In the derived splitter class, introduce a handler for the WM_MOUSEWHEEL message and remove the call to the base CSplitterWnd handler. Return FALSE instead. For example: BOOL CMySplitterWnd::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { //Do not call the base class handler. // Return false instead. return FALSE; } 2. Create a splitter window using the run-time class SECShortcutBar to create the shortcut bar as a child of the splitter window. For example: // 1 row, 3 cols m_splitter.CreateStatic( this, 1, 3 ); m_splitter.CreateView( 0, 0, RUNTIME_CLASS(SECShortcutBar), CSize(0,0), pContext ); 3. Retrieve a pointer to the shortcut bar from the splitter window. For example: //m_pShortcutBar is a pointer to SECShortcutBar m_pShortcutBar = (SECShortcutBar*)m_wndSplitterWindow.GetPane(0,0); ASSERT_VALID(m_pShortcutBar); 4. Use the AddBar() method to add bars to the shortcut bar. See Section 11.6.2 and Section 11.6.3. 11.7 Shortcut Bar Samples The functionality of SECShortcutBar is demonstrated in the Viz sample at <stingrayinstalldir>\Samples\Toolkit\MFC\Docking\Viz. See also the listbar sample, which does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 198 Chapter 12 Framework-Tailored Shortcut Bars 12.1 Overview The new Shortcut Bar component is a framework (MFC, ATL) independent container that can be used for hosting regular HWND based Windows objects as well as 'visual components' that simply render themselves onto any given device context. This framework independence is achieved using the new Stingray Foundation Library (SFL). SFL, in conjunction with our existing Model View Controller (MVC) architecture, provides all the necessary plumbing that paves the way for the shortcut bar to be abstracted from the message/command mechanisms of MFC/ATL and to have a common core that can be plugged into either framework. The term 'visual component', in the MVC context, refers to an instance of any class that derives from the MvcVisualComponent base. An MVC viewport is also a visual component. Please refer to the MVC documentation for detailed information on visual components, viewports and the MvcVisualComponent class. Chapter 12 Framework-Tailored Shortcut Bars 199 Figure 101 – The Shortcut Bar The SFL and MVC combination, together with the framework independence, makes it possible to provide two distinct variations of the shortcut bar - a regular windowed control and a non-windowed control. The windowed version has its own window handle while the non-windowed control uses the window handle of the host container. For ease of use, four specialized implementations of the shortcut bar have been made available: 200 SECATLShortcutBarHosted SECATLShortcutBarWnd SECMFCShortcutBarHosted SECMFCShortcutBarWnd 12.2 The Shortcut Bar Classes Figure 102 – Shortcut bar class hierarchy SECShortcutBarComp CEventRouterMap<SECATLShortcutBarWnd > CWindowImpl <SECATLShortcutBarWnd > IVisualWindow SECATLShortcutBarWnd SECShortcutBarComp CEventRouterMap<SECATLShortcutBarHosted > SECATLShortcutBarHosted 12.2.1 ATL The SECATLShortcutBarWnd class is the windowed version of the shortcut bar that has been tailored for use in an ATL environment. In addition to the SFL base classes, SECATLShortcutBarWnd derives from SECShortcutBarComp and also ATL's CWindowImpl. CShortcutBarComp ties up the MVC triad and provides the common interface for the four variants of the shortcut bar; the CWindowImpl lineage provides the requisite windowing support. SECATLShortcutBarHosted derives from the SECShortcutBarComp base class and provides the non-windowed version of the ATL component. When using SECATLShortcutBarHosted, the parent housing the control is expected to provide the window handle; that is, it should implement the SFL IVisualWindow interface. 12.2.2 MFC Like the ATL implementations, the SECMFCShortcutBarWnd and SECMFCShortcutBarHosted classes respectively provide the windowed and non-windowed versions of the shortcut bar for use in an MFC-only environment. Chapter 12 Framework-Tailored Shortcut Bars 201 12.3 Shortcut Bar Styles Table 33 lists the bar styles that can be applied to shortcut bars. Table 33 – Shortcut bar styles 202 Shortcut bar style Description SEC_TABBAR_VERT Orients the shortcut bar vertically. This is the default orientation. SEC_TABBAR_HORZ Orients the shortcut bar horizontally. This style is yet to be implemented. SEC_TABBAR_BARCURSOR Uses a hand cursor for the bar objects, similar to MS Outlook. The default behavior is to use the arrow cursor. SEC_TABBAR_NOANIMATE By default bar switching is animated. Setting this style disables the animation. SEC_TABBAR_NOHILIGHT Moving the cursor over any of the bars in the shortcut bar will highlight it. Setting this style disables this effect. SEC_TABBAR_ CNTXTMENU When this style is set, right-clicking the shortcut bar will result in a WM_TABBAR_CNTXTMENU message being sent to the owner window. The wParam of the message is the index of the active bar while the IParam contains a menu handle that the shortcut bar displays upon return. Handling this message allows the shortcut bar's parent to customize the context menu. 12.4 Using the Shortcut Bar We provide a windowed shortcut bar and a non-windowed shortcut bar; this section discusses using each version separately. 12.4.1 Using the Windowed Shortcut Bar 1. Depending on your application's framework, include either the ot_atlshortcutbar.h or ot_mfcshortcutbar.h header to your project. 2. To the parent class add a member of type SECATLShortcutBarWnd or SECMFCShortcutBarWnd, depending on which framework you use. 3. Handle the WM_CREATE message in the parent class. Within this handler, create the shortcut bar and set its initial styles. // m_hWnd is the window handle of the parent. m_wndSCBar.Create(m_hWnd, rcBar); m_wndSCBar.SetBarStyle(m_wndSCBar.GetBarStyle() | SEC_TABBAR_CNTXTMENU); 4. Use the SECATLShortcutBarWnd::AddBarWnd() or SECATLShortcutBarWnd::AddBarVisual() methods to add either regular window-based clients or MVC visual component/viewport clients to the shortcut bar. // m_pViewport is an instance of an MVC MvcViewport_T class m_wndSCBar.AddBarVisual(&m_pViewport, _T("Canvas Viewport")); // m_wndTree is a standard C++ wrapper(CWnd/CWindow) for // a HWND m_wndSCBar.AddBarWnd(m_wndTree.m_hWnd, _T("Tree")); 5. Finally, invoke ActivateBar() to set the initially active bar. m_wndSCBar.ActivateBar(0); 12.4.2 Using the Non-Windowed Shortcut Bar When using the non-windowed version of the control, the shortcut bar is merely a visual entity that uses the device context provided by the host window to render itself. All client windows will be created as children of the hosting parent. When using the shortcut bar in this metaphor, it is up to the parent window to suitably expose a window handle in a manner that the shortcut bar understands. This is achieved through the SFL IVisualWindow interface that the shortcut bar expects the host window to implement. 1. Implement IVisualWindow in the parent class that will host the shortcut bar. 2. To the parent class add a data member of type SECATLShortcutBarHosted or SECMFCShortcutBarHosted. 3. When using ATL, plug this object into the ATL message chain using the CHAIN_MSG_MAP_MEMBER() macro. BEGIN_MSG_MAP(CScribbleFrame) Chapter 12 Framework-Tailored Shortcut Bars 203 MESSAGE_HANDLER(WM_CREATE, OnCreate) CHAIN_MSG_MAP_MEMBER(m_wndSCBar) CHAIN_MSG_MAP(baseClass) END_MSG_MAP() 4. When using MFC, override the CWnd::OnWndMsg() and CWnd::OnCmdMsg() functions in the host class and call the equivalent methods in the shortcut bar. 5. In the WM_CREATE handler, call the SECATLShortcutBarHosted::Create() method and pass in a pointer to the parent class implementing IVisualWindow. m_wndSCBar.Create((IVisualWindow*)this, rcBar); 6. Provide a handler for the WM_SIZE message and set the shortcut bar's viewport size using the SECATLShortcutBarHosted::SetSize() function. m_wndSCBar.SetSize(250, 750); When using the windowed version of the shortcut bar, all client windows must be created as children of the shortcut bar. However, while using the non-windowed version of the control, child windows share a common parent with the shortcut bar. 12.4.3 Setting visual aspects of the shortcut bar Various visual aspects of the shortcut bar such as the bar label font, background brush, back-color, bar icon, text color, text alignment etc., can be set using the appropriately titled methods that the class implements. Some of these are shown below. // Sets a hashed background brush for the bar. The second // param is the bar index. m_wndSCBar.SetBarBrush(m_hHashBrush, 0); // Sets the text color used for the bar labels m_wndSCBar.SetBarTextColor(RGB(0,0,255), 0); // Sets the bar label font to m_hfBar. Specifying -1 sets the // particular aspect for all the bars. m_wndSCBar.SetBarFont(m_hfBar, -1); // Displays, alongside the label, the IDI_ICON1 icon on bar 1. m_wndSCBar.SetBarIcon(1, IDI_ICON1); // Center aligns the bar text. The other options available are // SEC_TABBAR_LBLALIGNLEFT & SEC_TABBAR_LBLALIGNRIGHT m_wndSCBar.SetLabelAlignment(SEC_TABBAR_LBLALIGNCENTER); 12.4.4 Adding a context menu to the shortcut bar When the SEC_TABBAR_CNTXTMENU style is set, right-clicking anywhere on the bar portion causes the shortcut bar to generate a WM_TABBAR_CNTXTMENU notification message. Providing a handler for this message within the shortcut bar's notification target allows customization of the context menu. The following excerpt demonstrates the usage. 204 // ATL Message map entry MESSAGE_HANDLER(WM_TABBAR_CNTXTMENU, OnBarContextMenu) LRESULT OnBarContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // The lParam holds a pointer to the popup menu created by the // ShortcutBar. We can either directly modify this menu or, if // a menu resource is available, reinitialize this menu pointer // with a new menu. In this case, we will destroy the current // menu, create a new one based on our menu resource and reassign // lParam to refer to the new menu handle. This menu will be // destroyed later on by the Shortcut Bar. if((int)wParam != -1) m_nCurrentHit = wParam; HMENU* phMenu = (HMENU*)lParam; DestroyMenu(*phMenu); *phMenu = GetSubMenu(LoadMenu(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDR_SHORTCUTBAR)), 0); return TRUE; } 12.5 Shortcut Bar Sample The samples ATLShortcut and MFCShortcut provide a comprehensive illustration of the shortcut bar classes. These samples do not ship with the product. For information on how to obtain these samples, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 12 Framework-Tailored Shortcut Bars 205 206 Chapter 13 Tabbed Windows 13.1 Overview Objective Toolkit includes a set of tabbed window classes that you can use as an alternative to MFC’s property sheet class. You can embed any type of window in the Objective Toolkit tabbed windows. For example, you can embed any CWnd-derived object, including CDialog and CView, into a tabbed window. For more information see the note in Section 13.5.8, “To create and add a view to the tabbed window.” In addition, you can embed the tabbed window anywhere. Tabbed windows use MFC’s document/view architecture. They allow you to add multiple views for single or multiple documents as tabs. These classes provide a variety of customizable formats. There are two variants of the tabbed window classes: a two-dimensional (Excel-like) format and a three-dimensional (Visual Studio-like) format. If you use the three dimensional format to create your tabbed window, you can place the tabs on the left, right, top, or bottom side of the control. The two-dimensional tabbed window classes restrict tab placement to the bottom of the control. Figure 103 – Example of the Two-Dimensional Tabbed Windows Chapter 13 Tabbed Windows 207 Figure 104 – Example of the Three-Dimensional Tabbed Windows 208 13.2 The Tabbed Window Classes A tabbed window is a small, rectangular window that draws tabs and processes mouse events. A tabbed window contains and manages the layout of a tab control and one or more sub-windows (or pages). When the user selects a tab with the mouse, the associated page is activated. The following figure shows the relationship of the various tabbed window classes. Figure 105 – Objective Toolkit Tabbed Window Class Hierarchies CWnd CWnd SECTabControlBase SECTabWndBase SECTabControl SECTabWnd SEC 3DTabControl SEC 3DTabWnd 13.2.1 SECTabControlBase SECTabControlBase is an abstract base class that defines the interface of a tab control. SECTabControlBase does not implement the tab control’s functionality or appearance. That is the responsibility of derived classes. SECTabControlBase defines the interface for a generic tab control. The derived classes add the implementation details necessary to define a specific look-and-feel. 13.2.2 SECTabControl TheSECTabControl class implements a tab control with a two-dimensional look- and-feel that is similar to Microsoft Excel’s. This class handles drawing and activating the tabs for the tabbed window. Figure 106 – The SECTabControl Window The SECTabControl inherits its interface from SECTabControlBase and adds the implementation details that define its appearance. Typically, you would not use this class directly. It is created as a child of the SECTabWnd object, and all its operations are performed through the SECTabWnd interface. For each tab on the control, the SECTabControl references an SECTab object, which in turn references an associated CWnd. Chapter 13 Tabbed Windows 209 13.2.3 SEC3DTabControl TheSEC3DTabControl class implements a tab control with a three-dimensional look-and-feel that is similar to the Visual Studio’s tabbed windows. This class is responsible for drawing the tabs and handling tab activation. Figure 107 – The SEC3DTabControl Window You can orient tabs so that they are on the top, bottom, left, or right of the tabbed window. The SEC3DTabControl inherits its interface from SECTabControlBase and adds the implementation details that define its appearance. Like SECTabControl, this class is typically not used directly. It is created as a child of the SEC3DTabWnd object, and all its operations are performed through the SEC3DTabWnd interface. For each tab on the control, the SEC3DTabControl references an SEC3DTab object, which in turn references an associated CWnd. 13.2.4 SECTabWndBase SECTabWndBase is an abstract base class that defines the interface of a tabbed window, which supports the dynamic creation, renaming, and destruction of tabs. SECTabWndBase supports a rich set of operations that allows you to perform a number of actions on windows objects, such as adding, deleting, renaming, activating, scrolling into view, and more. In addition, you can customize the appearance of the tabs with alternative fonts and other features. This class does not implement the tabbed window’s functionality or appearance. That is the responsibility of derived classes. SECTabWndBase defines the methods that operate on a generic tabbed window. The derived classes inherit the methods common to all tabbed windows and add the implementation details necessary to define a look-and-feel. 13.2.5 SECTabWnd TheSECTabWnd class implements a tabbed window with a two-dimensional look and feel similar to the tabbed worksheet pages in Microsoft Excel. If an SECTabWnd does not have enough room to display each of its tabs, scroll buttons appear on the dialog (depending on the style settings) so the user can navigate to each of the tabs. 210 The SECTabWnd inherits its interface from SECTabWndBase and adds the implementation details that define its appearance. The SECTabWnd contains the SECTabControl, draws its own border, and activates tab-associated CWnds for display. 13.2.6 SEC3DTabWnd TheSEC3DTabWnd class implements a tabbed window with a three-dimensional appearance that is similar to the Visual Studio’s tabbed windows. You can position tabs on the top, bottom, left, or right. SEC3DTabWnd does not display scroll buttons for navigating the tabs. The SEC3DTabWnd inherits its interface from SECTabWndBase and adds the implementation details that define its appearance. The SEC3DTabWnd contains the SEC3DTabControl and is responsible for drawing its own border and activating the tab-associated CWnds for display. Only the three-dimensional tab classes support styles that allow you to position tabs on the top, left, and right. These styles are not available in the two dimensional tab classes. Chapter 13 Tabbed Windows 211 13.3 Tabbed Window Styles You can apply the following tabbed window styles using the dwStyle parameter of the SECTabWnd::Create() method. The following styles only apply to the SECTabWnd class. They have no effect on the SEC3DTabWnd class. Table 34 – Tabbed Window Styles for SECTabWnd 2D Tabbed window (SECTabWnd) Style Description TWS_LEFTRIGHTSCROLL Only the left and right scroll buttons are shown. The user can only scroll the tabs to the left and right. There are no buttons for jumping to the first tab or to the last tab. This style is only valid for SECTabWnd. TWS_FULLSCROLL All four of the scroll buttons are shown in the lower left corner of the tabbed window. These four scroll buttons allow the user to scroll the tabs in the tabbed window to the first tab, to the last tab, a few pixels to the left, and a few pixels to the right. If you do not require the <scroll to first> and <scroll to last> buttons, use the TWS_LEFTRIGHTSCROLL style instead. This style is only valid for SECTabWnd. You can apply the following tabbed window styles with the dwStyle parameter of SEC3DTabWnd::Create() method. The following styles apply only to the SEC3DTabWnd class. Table 35 – Tabbed Window Styles for SEC3DTabWnd 212 3D Tabbed window (SEC3DtabWnd) Style Description TWS_TABS_ON_BOTTOM Places tab on the bottom of the window. This style is only valid for SEC3DtabWnd. TWS_TABS_ON_TOP Places tab on the top of the window. This style is only valid for SEC3DtabWnd. TWS_TABS_ON_LEFT Places tab on the left side of the window. This style is only valid for SEC3DtabWnd. TWS_TABS_ON_RIGHT Places tab on the right side of the window. This style is only valid for SEC3DtabWnd. Table 35 – Tabbed Window Styles for SEC3DTabWnd (Continued) 3D Tabbed window (SEC3DtabWnd) Style Description TWS_NOACTIVE_TAB_ ENLARGED By default, the active tab is drawn enlarged. The TWS_NOACTIVE_TAB_ENLARGED style flag disables this feature. TWS_DRAW_STUDIO_LIKE Provides tabs in the style similar to that in Visual Studio. TWS_DRAW_3D_NORMAL Provides a normal 3D border for the client area. TWS_DYNAMIC_ARRANGE_TABS Allows drag-and-drop rearrangements of the tabs. Chapter 13 Tabbed Windows 213 13.4 Tab Control Notification Messages The tab control notification messages provide feedback information from user generated events or various method calls. See Section 13.5.12 for information on using these notifications. Table 36 – Tab Control Messages Tab Control Message Definition Description TCM_TABSEL WM_USER+1000 Sent to the parent window when a tab is selected as the active tab. This can occur as a result of a left button click on the tab or as a result of a call to the ActivateTab() or SelectTab() methods. TCM_TABDBLCLK WM_USER+1001 The tab control sends this notification to the tab window when the mouse is double-clicked on a tab. This message needs to be handled in a derived SECTabWnd/SEC3DTabWnd class. TCM_TABSELCLR WM_USER+1002 The tab control sends this notification to the tab window when the tab selection is cleared. This occurs when the ClearSelection() method, which marks all the tabs as unselected, is called. TCM_TABREACTIVATE WM_USER+1003 The tab control sends this notification to the tab window (not the parent) when the mouse is clicked on a tab that is already selected as the active tab or by a call to the ReactivateTab() method. This message can be directly handled in a derived SECTabWnd/SEC3DTabWnd class. It can also be overridden by the SECTabWndBase::OnReActivateT ab() virtual function. 214 13.5 Using SECTabWnd and SEC3DTabWnd 13.5.1 To add SECTabWnd or SEC3DTabWnd to a frame window The steps for adding SECTabWnd and SEC3DTabWnd are identical. 1. Add an SECTabWnd (orSEC3DTabWnd) data member to your CFrameWnd-derived class. For example: SECTabWnd m_tabWnd; If your application is: Then: SDI-based Add the data member to your CMainFrame class. MDI-based Add the data member to your CMDIChildWnd descendant. FDI-based Add the data member to your SECFDIChildWnd descendant. 2. Override the OnCreateClient() method of the frame class and call the tabbed window Create() method to create the tabbed window. For example: BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext) { BOOL rtn_val; lpcs; //UNUSED rtn_val = m_tabWnd.Create(this); ... } 3. In the OnCreateClient() frame class method, add the views or any CWnd-derived objects to the tabbed window object using the AddTab() method. For example, the following line of code creates a new instance of a CView-derivative and inserts it into the tabbed window. m_tabWnd.AddTab(RUNTIME_CLASS(CDemoView1), "Tab One", pContext, nID); The next line inserts any pre-existing CWnd-derived object into the tabbed window. m_tabWnd.AddTab(pWnd, "Tab Two"); Chapter 13 Tabbed Windows 215 4. As the last step in your OnCreateClient() override, activate and scroll into view the tab that you want to be selected initially. For example: m_tabWnd.ActivateTab(0); m_tabWnd.ScrollToTab(0); 13.5.2 To add a tabbed window to a dialog 1. Edit the dialog resource in the resource editor. 2. Add an arbitrary control (for example, an edit control or a static control). Position and size it where you want the tabbed window, and give the control a unique control identifier. 3. In the OnInitDialog() handler for the dialog, retrieve the rectangle for the control and window you just added. For example: CWnd* pWnd = GetDlgItem(uiControlID); CEdit* pwndEdit=(CEdit*)pWnd; // Retrieve the previous window in the tab order // and the rectangle to use for the call to create // (in parent client-area coordinates). CWnd* pwndPrev = pwndEdit->GetWindow(GW_HWNDPREV); CRect rc; pWnd->GetWindowRect(&rc); this->ScreenToClient(&rc); // Now we no longer need the original window and can // safely destroy it. pWnd->DestroyWindow(); 4. Now, call the tabbed window Create() method. Then, set the size, position, and tab order for the control. if (m_tabWnd.Create(this)) { SetWindowPos(pwndPrev, rc.TopLeft().x, rc.TopLeft().y, rc.Width(), rc.Height(), 0); } 13.5.3 Removing the 2D Tab Scroll Buttons In the call to SECTabWnd::Create(), leave off the TWS_FULLSCROLL or TWS_LEFTRIGHTSCROLL flags. 13.5.4 To put 3D tabs on the side or top of the tabbed window 1. Ensure that the font used on the tabs is a True Type font, such Arial or Times New Roman. For more information, see Section 13.5.11, “To change the font of a tab.” 216 2. In the call to SEC3DTabWnd::Create(), specify one of the following style flags: TWS_TABS_ON_BOTTOM TWS_TABS_ON_TOP TWS_TABS_ON_LEFT TWS_TABS_ON_RIGHT For example: rtn_val = m_tabWnd.Create(this, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | TWS_FULLSCROLL | TWS_TABS_ON_LEFT); 13.5.5 To enable scroll bars in the 2D tabbed window 1. The contained object for the tab must be CScrollView-derived. Override CWnd::GetScrollBarCtrl() for the contained window class. For example: CScrollBar* CMyScrollView::GetScrollBarCtrl(int nBar) const { ASSERT(GetParent()-> IsKindOf(RUNTIME_CLASS(SECTabWnd))); return ((SECTabWnd*)GetParent())-> GetScrollBar(nBar); } 2. For each tabbed window, pass WS_HSCROLL or WS_VSCROLL as required in the dwStyle parameter of the SECTabWndBase::SetScrollStyle() member function. For example: m_tabWnd.SetScrollStyle(nTab, WS_HSCROLL | WS_VSCROLL); 13.5.6 To add keyboard accelerator support 1. Derive a class from SECTabWnd/SEC3DtabWnd and add handlers for the WM_CHAR and WM_KEYDOWN messages. 2. In OnChar() or OnKeyDown() or both, provide your own tab-activation code. Within the handler, you can use the SECTabWndBase::ActivateTab() method to activate a specific tab. If the tab contains CView derived class objects: The default message routing mechanism sends the keyboard messages to the window with the keyboard focus so the active view receives the messages. Handle the message(s) in the view/control class contained in the tab to redirect the message(s) to the tabbed window. This ensures that your tab-window's handler is called. void CDemoView1::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { nChar; //UNUSED nRepCnt; //UNUSED nFlags; //UNUSED Chapter 13 Tabbed Windows 217 // Forward message to parent ASSERT(GetParent()-> IsKindOf(RUNTIME_CLASS(SECTabWndBase))); MSG msg = *GetCurrentMessage(); if (GetParent()) GetParent()->SendMessage( msg.message, msg.wParam, msg.lParam); } If the tabbed window contains CDialog/CFormView derived class objects: When a CDialog/CFormView derived object is the active window, the application sends keyboard messages to the control within the dialog that has the focus by default. You need to subclass the control and redirect the keyboard messages to the tabbed window. 13.5.7 To add a window to the tabbed window Call the overloaded SECTabWndBase::AddTab() method with the following declaration. void AddTab(CWnd* pWnd, LPCTSTR lpszLabel, HICON hIcon = NULL); If the tab is removed at run time, don’t forget to destroy the associated CWnd. See Section 13.5.9. 13.5.8 To create and add a view to the tabbed window Call the overloaded SECTabWndBase::AddTab() method with the following declaration. CWnd* AddTab(CRuntimeClass* pViewClass, LPCTSTR lpszLabel, CCreateContext* pContext = NULL, HICON hIcon = NULL, UINT nID = -1); If the tab creation is within CFrameWnd::OnCreateClient(): Use the pContext parameter passed to the OnCreateClient() method in the AddTab() method call. If the tab creation is not occurring within CFrameWnd::OnCreateClient(): Create a CCreateContext on the stack and pass it to the AddTab() method call. For example: void CMyFrameWnd::OnSheetNew() { CCreateContext context; context.m_pCurrentFrame = this; context.m_pCurrentDoc = GetDocToUse(); context.m_pNewViewClass = RUNTIME_CLASS(CMyView); context.m_pNewDocTemplate = GetDocTemplateToUse(); m_wndTab.AddTab(RUNTIME_CLASS(CMyView), &context); } 218 The implementations of the GetDocToUse() and GetDocTemplateToUse() methods are application-specific. In general, you can embed a tabbed window anywhere. This is not true if the tabbed window contains views. You cannot embed a view in a tabbed window that is, in turn, embedded in a control bar (docking window) or a dialog. However, you can embed a tabbed window containing views into a view contained in a frame using the docking views architecture. If the tab is removed at run time, don’t forget to destroy the associated CView. See Section 13.5.9. 13.5.9 To remove a tab Call the RemoveTab() method. Don’t forget to destroy the contained CWnd. For example: // Don't just delete the tab, destroy the associated // window too. if (m_tabWnd.GetTabInfo(nActiveTab, lpszLabel, bSelected, pActiveWnd, pExtra)) { pActiveWnd->ShowWindow(SW_HIDE); pActiveWnd->SendMessage(WM_CLOSE); } 13.5.10To access the CWnd associated with a tab Call the GetTabInfo() method, which is defined as follows: // Returns information about the tab with the supplied index. BOOL GetTabInfo(int nIndex, LPCTSTR& lpszLabel, BOOL& bSelected, CWnd*& pWnd, void*& pExtra); For example: m_tabWnd.GetTabInfo(nActiveTab, lpszLabel, bSelected, pActiveWnd, pExtra); Chapter 13 Tabbed Windows 219 13.5.11To change the font of a tab Call one of the following font accessor methods. Font accessor method Description Get/SetFontActiveTab() Sets an active tab's current font to the specified font. Get/SetFontInactiveTab() Sets an inactive tab's current font to the specified font. If you are specifying the font for a tab on a 3D tabbed window and the tabs are placed on either the left or right side of the tabbed window, the font must be a True Type font. 13.5.12To receive user event notifications from the tabbed window Add a message-map entry and message-handler member function to the parent class for each message. See Section 13.4. Each message-map macro entry takes the following form: ON_MESSAGE( <tab control message id>, memberFxn ) memberFxn is the name of the parent member function you wrote to handle the notification and <tab control message id> is one of the following: TCM_TABSEL TCM_TABDBLCLK TCM_TABSELCLR TCM_TABREACTIVATE The parent's function prototype is as follows: afx_msg LRESULT memberFxn(WPARAM wParam, LPARAM lParam); wParam parameter contains the index of the activated tab. If this handler is called in response to a SelectTab() event, then the wParam is the index of the currently active tab. 13.5.13To get a pointer to the SECTabWnd from a contained view Use the following code: pTabWnd = (SECTabWnd*)pView->GetParent(); ASSERT_KINDOF(SECTabWnd, pTabWnd); 220 13.5.14To insert a splitter window into a tabbed window 1. Create the tabbed window. m_tabWnd.Create(this); 2. Create the splitter window. Specify the tabbed window as its parent. m_wndSplitter.CreateStatic(&m_tabWnd, 1, 2); 3. Insert your child windows into the splitter window. m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CDemoView2), CSize(225,100), pContext); m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CDemoView2), CSize(225,100), pContext); 4. Insert the splitter window into the tabbed window. m_tabWnd.AddTab(&m_wndSplitter, "Tab Two"); There is a known limitation in SECTabWnd, which is particularly problematic if you are using the SECTabWnd class in conjunction with a splitter window. You can create the tabbed window with or without scroll bars, but they cannot be dynamically shown or hidden. If one tab requires scroll bars and another does not, the SECTabWnd class does not show and hide the scroll bars appropriately. 13.5.15Problem with Tabbed Windows in Docking Views If you are using a tabbed window in conjunction with the docking views component of Objective Toolkit, you may experience a problem with view activation. SECTabWndBase::Create() asserts if the tab window is being added as a child of a docking view that already has an ID of AFX_ID_PANE_FIRST. A tab window is expected to have this ID if the tab window consumes the client area of an MDI child frame. In other words, it is to act as a container for one or more views that would normally otherwise have this ID. The same is true if the tab window consumes the client area in the SDI scenario. When calculating the layout of the client area, MFC looks for a window with an ID of AFX_ID_PANE_FIRST. In a MDI application, the view window is typically the one with this ID. If multiple windows with this ID exist, view activation behavior becomes erratic. To fix this problem, create a unique ID for the tab window and specify it during the call to Create(). For example: VERIFY (m_wndTab.Create(this, WS_CHILD|WS_VISIBLE|TWS_TABS_ON_BOTTOM, ID_MYTAB)); // some unique id Chapter 13 Tabbed Windows 221 13.6 Tabbed Window Sample The Objective Toolkit sample tabdemo demonstrates the use of the SECTabWnd and SEC3DTabWnd classes. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 222 Chapter 14 Tree Control & Tree View 14.1 Overview The Objective Toolkit tree control and tree view classes extend the functionality of the CTreeCtrl common tree control. A tree control is a window that displays a hierarchical list of items, such as the files and directories on a disk or the entries in an index. The Win32 common tree control has some limitations. Because no source code is available, it lacks extensibility. It isn’t object-oriented and no hooks were introduced into the drawing process until Comctl32.dll. It is difficult to implement multiple selection. Additionally, CTreeCtrl is a very thin wrapper to the common tree control so it is difficult to extend as well. Objective Toolkit’s tree control adds support for multiple selection, an integrated grid with a resizable header row, adjustable cell height, word wrap, tool tips, various font and color options through an architecture designed for extensibility. Options are controlled by styles that permit features to be enabled or disabled at run time. Objective Toolkit also supports overlay images and state images. Editing labels in a multi column tree is possible if the TVS_EDITLABELS style and LVXS_HILIGHTSUBITEMS extended style is set. See the STATE sample. Objective Toolkit also includes a view class, SECTreeView. Unlike CTreeView, SECTreeView truly is a tree. You can override any of its virtual functions the same way you would an SECTreeCtrl. You can change the text color in your SECTreeView/Ctrl derived class by overriding PickTextColors(). You can change the font for a particular item in that same class by overriding PickTextFont(). The preceding functions are the same functions that you would overload if you were using SECTreeCtrl. Painting in the tree control is optimized for performance. The control is not necessarily invalidated after every insertion or deletion of items so that you can add or remove many items in a batch. See Section 14.8.14. Chapter 14 Tree Control & Tree View 223 14.2 The Tree Control Classes As the following hierarchy suggests, templatized base classes provide identical functionality to the tree control and tree view classes. Figure 108 – The Objective Toolkit Tree Control Class Hierarchy CWnd SECListClient SECListBaseC SECListCtrl SECTreeBaseC SECTreeCtrl 14.2.1 SECTreeCtrl The SECTreeCtrl class is not derived from CTreeCtrl. It is ultimately CWnd-derived. Multi-column support is provided through the SECListCtrl class. 14.2.2 SECListCtrl This class supports the SECTreeCtrl class. Although the SECTreeCtrl class manages the first column, which is the tree structure itself, the SECListCtrl class manages any additional columns in the tree control. This class is not supported outside the scope of the SECTreeCtrl class. 224 14.3 The Tree View Classes The hierarchy for the tree view contains many classes; however, typically you only need to work with SECTreeView and SECListView. Figure 109 – The Objective Toolkit Tree View Class Hierarchy CView SECListClient SECListBaseV SECListView SECTreeBaseV SECTreeView 14.3.1 SECTreeView The SECTreeView class has the same core code as SECTreeCtrl with the exception of its base classes. In fact, this class has the same API as an SECTreeCtrl with the addition of CView inherited members. Like the SECTreeCtrl class, SECTreeView multi-column support is provided through the supporting SECListView class. The SECTreeView also has print and print preview support. 14.3.2 SECListView This is a supporting class for the SECTreeView class. Although the SECTreeView class manages the first column, which is the tree structure itself, the SECListView class manages any additional columns in the tree control. This class is not supported outside the scope of the SECTreeView class. Chapter 14 Tree Control & Tree View 225 14.4 Tree Control Data Structures The SECTreeCtrl and SECTreeView classes utilize the same data structures used by the CTreeCtrl class. The use of these data structures does not promote object-oriented programming. However, they are compatible with CTreeCtrl. 14.4.1 TV_ITEM The TV_ITEM structure retrieves or specifies the attributes of a tree view item. It is defined as follows: typedef struct _TV_ITEM { tvi UINT mask; // what is valid in this structure HTREEITEM hItem; // handle of this item UINT state; // selected, expanded, drop // highlighted, // also state and overlay // image indexes // via obscure macros // like INDEXTO… // that map to bit fields UINT stateMask; // what is valid in the “state” // member LPSTR pszText; // text or LPSTR_TEXTCALLBACK int cchTextMax; // only used when modifying text int iImage; // normal image index // or I_IMAGECALLBACK int iSelectedImage; // selected image # // or I_IMAGECALLBACK int cChildren; // # of children // or I_CHILDRENCALLBACK LPARAM lParam; // 32-bit user defined data } TV_ITEM; For a complete description of the members of this structure, refer to the MSDN documentation for TV_ITEM or TVITEM, which is identical to TV_ITEM, but follows current naming conventions. 14.4.2 NM_TREEVIEW The NM_TREEVIEW structure is used within the tree control message notification mechanism. It contains information about a specific tree view notification message. A pointer to this data structure is included as a parameter accompanying the WM_NOTIFY message. It is defined as follows: typedef struct tagNMTREEVIEW { NMHDR hdr; UINT action; TVITEM itemOld; TVITEM itemNew; POINT ptDrag; } NMTREEVIEW, FAR *LPNMTREEVIEW; 226 For a complete description of the members of this structure, refer to the MSDN documentation for NM_TREEVIEW or NMTREEVIEW, which is identical to NM_TREEVIEW, but follows current naming conventions. 14.4.3 TV_HITTESTINFO This structure contains information for determining the location of a point relative to a tree view control. It is defined as follows: typedef struct _TVHITTESTINFO { POINT pt; // client coordinates of point to test UINT flags; // info about the results of hit test HTREEITEM hItem; // handle of item that occupies point } TV_HITTESTINFO, FAR *LPTV_HITTESTINFO; This structure is used as an optional parameter of the HitTest() method. For more information about the members of this structure, refer to the MSDN documentation for TV_HITTESTINFO or TVHITTESTINFO, which is identical to TV_HITTESTINFO, but follows current naming conventions. Refer to the documentation for SEC_TREECLASS::HitTest() in the Objective Toolkit Class Reference. Chapter 14 Tree Control & Tree View 227 14.5 Tree Item States The tree node states are represented by the state member of TV_ITEM. By retrieving the TV_ITEM structure for any given item, you can test the state of that item. The following figures show an example tree control and an example tree view. Figure 110 – Example Objective Toolkit Tree Control and Tree Item States 228 Figure 111 – Example Objective Toolkit Tree View and Tree Item States Chapter 14 Tree Control & Tree View 229 14.6 Tree Control/Tree View Styles The styles of the tree control are controlled by style flags. The user can modify the styles at run time. Table 37 – Tree Control Style Flags Tree Control Style Flag Description TVS_DISABLEDRAGDROP Prevents the tree view control from sending TVN_DRAGDROP notification messages. TVS_EDITLABELS Allows the user to edit the labels of tree view items. TVS_HASBUTTONS Displays plus (+) and minus (-) buttons next to parent items. The user clicks the buttons to expand or collapse a parent item's list of child items. To include buttons with items at the root of the tree view, you need to specify TVS_LINESATROOT. TVS_HASLINES Uses lines to show the hierarchy of items. TVS_LINESATROOT Uses lines to link items at the root of the tree view control. This value is ignored if TVS_HASLINES is not also specified. TVS_SHOWSELALWAYS Causes a selected item to remain selected when the tree view control loses focus. TVXS_COLUMNHEADER Displays the column header. This style removes the LVS_NOCOLUMNHEADER style so that all the columns display headers. TVXS_FLYBYTOOLTIPS Enable tooltips. TVXS_MULTISEL Enables the user to select multiple items. TVXS_WORDWRAP Enables word wrapping of text if the first column is narrow. Specifying this style automatically enables the LVXS_WORDWRAP style, affecting all columns. LVS_SINGLESEL Disables multiple selection of items. LVXS_FITCOLUMNSONSIZE The item column fills the width not occupied by subitem columns. LVXS_FLYBYTOOLTIPS Enable tooltips for additional columns. The TVXS_FLYBYTOOLTIPS style automatically enables this style. LVXS_HILIGHTSUBITEMS 230 LVXS_LINESBETWEENCOLUMNS Paints vertical lines between columns. LVXS_LINESBETWEENITEMS Paints horizontal lines between items. Table 37 – Tree Control Style Flags (Continued) Tree Control Style Flag Description LVXS_NOGROWCOLUMNONDELETE Prevents automatic resizing of column 0 when a column is deleted. LVS_NOCOLUMNHEADER Specifies that additional columns do not display column headers. This style is automatically removed by specifying the TVXS_COLUMNHEADER style, which causes all columns to display headers. LVXS_WORDWRAP Enables word wrapping of item text if the column is narrow. The TVXS_WORDWRAP style automatically enables this style. LVXS_OWNERDRAWVARIABLE Reserved. The following styles are not supported by SECTreeCtrl or SECTreeView: TVS_CHECKBOXES TVS_FULLROWSELECT (use LVXS_HILIGHTSUBITEMS) TVS_INFOTIP TVS_NONEVENHEIGHT TVS_NOSCROLL (use LVS_NOSCROLL) TVS_NOTOOLTIPS (use LVXS_FLYBYTOOLTIPS/TVXS_FLYBYTOOLTIPS) TVS_RTLREADING TVS_SINGLEEXPAND TVS_TRACKSELECT The following figure illustrates some of the standard styles that the Objective Toolkit tree control shares with CTreeCtrl in addition to TVXS_FLYBYTOOLTIPS. Chapter 14 Tree Control & Tree View 231 Figure 112 – Examples Tree Control Styles The following figure illustrates some of the extended styles. 232 Figure 113 – Examples Tree Control Extended Styles Chapter 14 Tree Control & Tree View 233 14.7 Tree Control Notifications The Objective Toolkit tree control supports a number of notifications messages sent in the form of a WM_NOTIFY message. Table 38 – Tree Control Notifications 234 Notification Description TVN_BEGINDRAG Notifies a tree view control's parent window that a drag-and-drop operation involving the left mouse button is being initiated. TVN_BEGINLABELEDIT Notifies a tree view control's parent window about the start of label editing for an item. TVN_ENDLABELEDIT Notifies a tree view control's parent window about the end of label editing for an item. TVN_ITEMEXPANDED Notifies a tree view control's parent window that a parent item's list of child items has expanded or collapsed. TVN_ITEMEXPANDING Notifies a tree view control's parent window that a parent item's list of child items is about to expand or collapse. TVN_SELCHANGED Notifies a tree view control's parent window that the selection has changed from one item to another. TVN_SELCHANGING Notifies a tree view control's parent window that the selection is about to change from one item to another. TVN_SETDISPINFO Notifies a tree view control's parent window that it must update the information it maintains about an item. TVN_GETDISPINFO Requests that a tree view control's parent window provide information needed to display or sort an item. TVN_KEYDOWN Notifies a tree view control's parent window that the user pressed a key and the tree view control has the input focus. TVN_DELETEITEM Notifies a tree view control's parent window that an item is being deleted. TVN_BEGINLABELEDIT Notifies a tree view control's parent window about the start of label editing for an item. LVN_BEGINLABELEDIT Notifies a list view control's parent window about the start of label editing for an item. Table 38 – Tree Control Notifications (Continued) Notification Description LVN_DELETEITEM Notifies a list view control's parent window that an item is about to be deleted. LVN_ENDLABELEDIT Notifies a list view control's parent window about the end of label editing for an item. LVN_GETDISPINFO Sent by a list view control to its parent window. It is a request for the parent window to provide information needed to display or sort a list view item. LVN_INSERTITEM Notifies a list view control's parent window that a new item was inserted. LVN_KEYDOWN Notifies a list view control's parent window that a key has been pressed. Chapter 14 Tree Control & Tree View 235 14.8 Using the Tree Control Classes The following sections describe how to use the tree control and tree view classes. 14.8.1 To create a tree control in a dialog 1. In the Visual Studio resource editor, create a tree control resource on the dialog resource. 2. Instantiate an SECTreeCtrl object as a member of your dialog class. 3. In the OnInitDialog() method of your dialog class, call SubclassTreeCtrlId() on the SECTreeCtrl instance. For example: m_pTreeCtrl->SubclassTreeCtrlId( IDC_TREE, this ); 14.8.2 To create a tree control dynamically 1. Create a unique control ID for the tree control. In Visual Studio, you can create an ID with the Resource Includes dialog. 2. Instantiate an SECTreeCtrl object. 3. Call the Create() method and specify the desired styles, rectangle, and parent. For example: DWORD dwStyle = TVS_SHOWSELALWAYS | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_EDITLABELS | TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP | WS_CHILD | WS_VISIBLE; DWORD dwStyleEx = TVXS_MULTISEL | TVXS_FLYBYTOOLTIPS | LVXS_HILIGHTSUBITEMS; m_pTreeCtrl->Create( dwStyle, dwStyleEx, rect, this, IDC_TREE); 4. If you are creating a tree control inside a control bar or other resizable window, remember to override the OnSize() method to resize the tree control with the parent window. void CMyControlBar::OnSize(UINT nType, int cx, int cy) { SECControlBar::OnSize(nType, cx, cy); if ( ::IsWindow(m_tree) ) m_tree.SetWindowPos( NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER ); } 236 14.8.3 To add a tree item 1. Instantiate and initialize a TV_ITEM structure for the tree item to be added. For example the following initializes a TV_ITEM structure to be inserted at the root level of the tree. TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask = TVIF_IMAGE|TVIF_TEXT|TVIF_SELECTEDIMAGE; tvi.pszText = LPSTR_TEXTCALLBACK; tvi.iImage = I_IMAGECALLBACK; tvi.iSelectedImage = I_IMAGECALLBACK; 2. Call the InsertItem() method using a TV_INSERTSTRUCT structure as a parameter. TV_INSERTSTRUCT tvis; memmove(&(tvis.item), &tvi, sizeof(TV_ITEM)); tvis.hParent = TVI_ROOT; tvis.hInsertAfter = TVI_LAST; HTREEITEM htiItem1 = m_pTreeCtrl-> InsertItem(&tvis ); 3. Alternatively, call one of the overloaded InsertItem() methods. HTREEITEM htiitem2 = m_pTreeCtrl->InsertItem(LPSTR_TEXTCALLBACK, I_IMAGECALLBACK, I_IMAGECALLBACK, TVI_ROOT, TVI_LAST ); 14.8.4 To create multiple columns 1. Call the InsertColumn() method. m_pTreeCtrl->InsertColumn( 1 /*0-based column index*/, "Attendance", LVCFMT_CENTER, 40 /*width*/ ); m_pTreeCtrl->InsertColumn( 2 /*0-based column index*/, "Grade", LVCFMT_CENTER, 40 /*width*/ ); 2. If you want, you can mark all items as needing a remeasurement when a WM_PAINT occurs, and invalidate the control. m_pTreeCtrlX->ReMeasureAllItems(); m_pTreeCtrlX->Invalidate(); Chapter 14 Tree Control & Tree View 237 14.8.5 To set the text on subitems of multi-column trees In trees with multiple columns, the objects called subitems are managed by the items of the tree (in column 0). These subitems control what is displayed in the additional columns. Method 1 1. Handle the LVN_GETDISPINFO notification. 2. In the handler for the LVN_GETDISPINFO notification, manage the subitem text storage yourself and then provide it to the control on demand via the callback. This is the default method demonstrated in the TREEDEMO sample. This method provides maximum performance in large trees with many columns of data. Method 2 1. Call StoreSubItemText(TRUE) after creation to enable the tree control to manage subitem text storage for you. 2. Call the SetItemText() or SetItemString() methods to set the text directly. This is an easier approach than using the callback method, but it is not as scalable for large trees when performance is an issue. This method is demonstrated in the STATE sample. 14.8.6 To create a standard image list for tree items Call the SetImageList() method. SetImageList( CImageList*, TVSIL_NORMAL); Use of an image list is optional. If state images are also associated with a tree item, you can assign them different widths than the standard image, but not different heights. 14.8.7 To create a state image list for tree items Call the SetImageList with the TVSIL_STATE flag. SetImageList( CImageList*, TVSIL_STATE ); A state image is an additional image drawn to the left of the normal image like a check box. State images are optional. If the standard images are also associated with a tree item, they can be different widths than the state image, but must be the same height. 14.8.8 To create a tree item with a state image 1. Create a state image list containing each state image to be displayed. See Section 14.8.7. 2. Instantiate a TV_ITEM data structure. For example, 238 TV_ITEM tvi; 3. Specify the TVIF_SELECTEDIMAGE flag in the mask data member. For example: tvi.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_STATE; 4. Specify the state image and TVIS_STATEIMAGEMASK to the state and stateMask data members, respectively. tvi.state = INDEXTOSTATEIMAGEMASK(1); tvi.stateMask = TVIS_STATEIMAGEMASK; 5. Specify any additional data members as indicated by any other flags included in the mask data member. tvi.pszText = _T("Line Species 1"); tvi.iImage = m_idiFolderClosed; tvi.iSelectedImage = m_idiFolderClosed; tvi.lParam = (LPARAM)m_secTree.m_hWnd; 6. Instantiate a TV_INSERTITEM structure and then initialize the item data member with a pointer to the TV_ITEM data structure. TV_INSERTSTRUCT tvis; 7. Fill in the remaining TV_INSERTITEM data members. tvis.item = tvi; tvis.hParent = TVI_ROOT; tvis.hInsertAfter = TVI_LAST; 8. Create the tree item with the InsertItem method. HTREEITEM htreeitem = m_tree.InsertItem(&tvis); 14.8.9 To change a state image on a tree item 1. Create a state image list containing each state image to be displayed. See Section 14.8.7, “To create a state image list for tree items.” 2. Instantiate a TV_ITEM structure and update the data members to prepare for a request for the state image of the item. For example: TV_ITEM tvi; tvi.hItem = htiItemClicked; tvi.mask = TVIF_STATE | TVIF_HANDLE; tvi.stateMask = TVIS_STATEIMAGEMASK; 3. Request the state image using the GetItem() method by passing the address of the TV_ITEM structure as a parameter. m_secTree.GetItem(&tvi); UINT state = tvi.state & TVIS_STATEIMAGEMASK; 4. Change the state image in the TV_ITEM structure to the image. int index = (state == INDEXTOSTATEIMAGEMASK(1)) ? Chapter 14 Tree Control & Tree View 239 2 : 1); // checked // not checked tvi.state = INDEXTOSTATEIMAGEMASK(index); 5. Call SetImage() to have the tree control use the new image. m_tree.SetItem(&tvi); 14.8.10To add an overlay image to a tree item 1. Obtain a HTREEITEM handle for the tree item. For example: HTREEITEM hti = m_tree.InsertItem(&tvi); 2. Instantiate a TV_ITEM structure and update the members for the desired overlay image. TV_ITEM tvi; tvi.hItem = hti; tvi.mask = TVIF_HANDLE | TVIF_STATE; tvi.stateMask = TVIS_OVERLAYMASK; tvi.state = INDEXTOOVERLAYMASK( iImage ); 3. Call the SetItem() method using the TV_ITEM as a parameter. m_secTree.SetItem( &tvi ); 14.8.11To find out which items are selected Call the GetSelectionArray() method. pArrSelected = m_secTree.GetSelectionArray(); 14.8.12To specify different colors for tree items 1. Override PickTextColor(). 2. In the PickTextColor() override, assign the rgbText member of the LvPaintContext structure supplied as a parameter. For example, the following code is in the Objective Toolkit Build Wizard. void CMyTreeView::PickTextColors(LvPaintContext* pPC) { ASSERT(pPC); SECTreeView::PickTextColors(pPC); if(pPC->lvi.iSubItem == 0) // subitem (column) number 0 { // Inside tree column, get a convenient context. TvPaintContext* pTvPC = (TvPaintContext*)pPC; // Check the properties of tree item (pTvPC->tvi), // and change color accordingly. For example, 240 // to change the color of root nodes: HTREEITEM hParent=GetParentItem(pTvPC->tvi.hItem); if(hParent == NULL) { pTvPC->rgbText= m_rootColor; if ( GetFocus()== this && (pTvPC->lvi.state & LVIS_SELECTED) ) pTvPC->rgbTextBkgnd = RGB(255, 255, 0); // highlight } } } 14.8.13To specify different fonts for tree items 1. Override the PickTextFont() method. 2. In the PickTextFont() override, assign the pFont member of the LvPaintContext structure supplied as a parameter. For example, the following code is in the Objective Toolkit Build Wizard. void CMyTreeView::PickTextFont(LvPaintContext* pPC) { ASSERT(pPC); SECTreeView::PickTextFont(pPC); if(pPC->lvi.iSubItem == 0) // subitem (column) number 0 { // Inside tree column, get a convenient context. TvPaintContext* pTvPC = (TvPaintContext*)pPC; // Check the properties of tree item (pTvPC->tvi), // and change font accordingly. For example, // to change the font of root nodes: HTREEITEM hParent=GetParentItem(pTvPC->tvi.hItem); if(hParent == NULL) pTvPC->pFont= &m_fontRoot; } } 14.8.14To update the tree control 1. Call the following methods after making changes to the tree control/view. ReMeasureAllItems(); Invalidate(); 2. To update the GUI after every insert/delete, call EnableRedrawAfterInsert(TRUE). The dialog portion of the TREEDEMO sample demonstrates this. The Expand() function has an additional BOOL parameter to control redrawing of expanded/collapsed nodes. Chapter 14 Tree Control & Tree View 241 14.8.15To incorporate SECTreeCtrl into an application already using CtreeCtrl In the header file, locate your instance of CTreeCtrl. 1. Change this from CTreeCtrl to SECTreeCtrl. 2. Population of the tree is the same as for CTreeCtrl. SECTreeCtrl is for the most part drop-in compatible with the CTreeCtrl, but not when you start changing or adding behaviors such as drag scrolling. 242 14.9 Tree Control Samples See the samples TreeDemo and DynaTree in the <stingrayinstalldir>Samples\Toolkit\MFC\TreeCtrl directory for a demonstration of these classes. See also the sample Samples\Toolkit\TreeCtrl\State. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. The DynaTree sample shows drag-and-drop using drag images and demonstrates using children on demand. The State sample shows the use of state images and overlay images. Multi-column tree controls can store the subitem text internally if the StoreSubItemText( TRUE ) function is called after creation so that you can call SetItemText() or SetItemString() on subItems without using the LVN_GETDISPINFO callback. This feature is demonstrated in this sample. This sample also shows multi-column editing. The following is a possible creation scenario for a multi-column tree that supports multiple selection, full row select, label editing, auto column sizing, and tooltips. // standard tree control and window styles go here DWORD dwStyles = TVS_SHOWSELALWAYS|TVS_HASBUTTONS | TVS_LINESATROOT|TVS_HASLINES | TVS_EDITLABELS|TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP|WS_CHILD; // Stingray extended styles go here DWORD dwStylesEx = TVXS_MULTISEL | TVXS_FLYBYTOOLTIPS | LVXS_HILIGHTSUBITEMS; m_secTree.Create( dwStyles, dwStylesEx, rect, this, IDC_SECTREE); /* make the columns resize if the window width changes. This is an alternative to having a horizontal scroll bar. */ m_secTree.ModifyListCtrlStyleEx( 0, LVXS_FITCOLUMNSONSIZE ); /* you can set the image and text foreground/background colors for selected and normal states */ COLORREF clrBack = RGB( 192, 220, 192); // change the background color m_secTree.SetBkColor( clrBack ); // change the selected icon background color m_secTree.SetSelIconBkColor( clrBack ); // change the normal icon background color m_secTree.SetIconBkColor( clrBack ); // change the normal text color m_secTree.SetTextColor( RGB( 10, 10, 10 ) ); // change the selection text background color m_secTree.SetSelTextBkColor( ::GetSysColor(COLOR_INACTIVECAPTION) ); Chapter 14 Tree Control & Tree View 243 // change the selection text color m_secTree.SetSelTextColor( RGB( 255, 255, 255 ) ); /*as an alternative, you can skip all the color initialization and simply use the default system colors. In that case, you will want system color changes to be shown in the control. Do this by calling EnableSysColorTracking( TRUE ); */ //turn on the header control m_secTree.EnableHeaderCtrl( TRUE ); // set the header text for column 0 m_secTree.SetColumnHeading(0, _T("Object") ); // set the column width to 60% of the // view client rect. m_secTree.SetColumnWidth(0, (int)(rect.Width() * .60)); // add another column. Every tree item will // have a sub item. m_secTree.InsertColumn( 1, _T("Time"), LVCFMT_LEFT, (int)(rect.Width() * .40) ); / * I don't want to use LVN_GETDISPINFO to populate my subitem text, so I must turn on subitem text storage! This option is a much requested change that allows setting subitem text directly using SetItemText( item, subItem, _T(“Text”) ) */ m_secTree.StoreSubItemText( TRUE ); 244 Chapter 15 User Interface Extensions 15.1 Overview This chapter describes a variety of Objective Toolkit components that you can use to enhance the user interface of your programs. 15.2 Bitmapped Dialog SECBitmapDialog lets you create dialogs with tiled or centered bitmaps in the background. Use SECBitmapDialog to decorate your application dialogs with 16 or 256 color bitmaps. The following figure demonstrates how you can use bitmaps in your dialogs. Figure 114 – Objective Toolkit BmpDialog32 Sample Dialog SECBitmapDialog is a direct enhancement of CDialog. Chapter 15 User Interface Extensions 245 Figure 115 – Objective Toolkit SECBitmapDialog Class Hierarchy CWnd CDialog SECBitmapDialog 15.2.1 Using SECBitmapDialog The following sections describe how you can implement Objective Toolkit’s User Interface Extensions. 15.2.1.1To incorporate the SECBitmapDialog class into your code Use the SECBitmapDialog as you would use CDialog. SECBitmapDialog adds a SetBitmap() member function. Use SetBitmap() to specify the bitmap and a display mode. The following display modes are available. Display mode flag Description SEC_BITMAP_TILE Tiles the bitmap in the dialog’s background. SEC_BITMAP_CENTER Centers the bitmap in the dialog’s background. SEC_BITMAP_FILL Fills the dialog with the specified bitmap. The following code creates a dialog with a tiled bitmap in the background. SECBitmapDialog bmpDlg(IDD_MODAL_BMPDLG); bmpDlg.SetBitmap(IDB_BRICKWALL, SEC_BITMAP_TILE); bmpDlg.DoModal(); 15.2.1.2To set the image used by the SECBitmapDialog class You can set the bitmap used in an SECBitmapDialog by calling the SetBitmap() method, which has three overloads. The first overload accepts a resource ID of a bitmap resource. The second overload accepts the filename of a bitmap file. The third overload takes a pointer to an SECImagederived object. Objective Toolkit can use any of the overloads for 256-color support. To change the bitmap at run time, the application can call SetBitmap() multiple times. Use SetNullBitmap() to remove the bitmap from the dialog. 15.2.2 Customizing SECBitmapDialog There is one overridable method in the SECBitmapDialog class: OnStaticCtlColor(). You can override this function to set the text color for Static controls. 246 SECBitmapDialog features are demonstrated in the sample DmpDialog32. SECBitmapDialog modes are demonstrated in the sample TodTest. These samples do not ship with the product. For information on how to obtain these samples, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 15.3 Gradient Caption Extension The gradient caption extension provides a simple emulation of the smooth gradient caption effects introduced in Microsoft Office for Windows 95. The extension also allows you to control the alignment of caption text. In order to match the Windows XP look and feel in a themed application, gradient caption extension will be automatically disabled if running under Windows XP and hosting application is themed. Figure 116 – Example Application with the Gradient Caption 15.3.1 The Gradient Caption Classes The gradient caption feature is incorporated into the existing SECFrameWnd and SECMDIFrameWnd. Figure 117 – Objective Toolkit Gradient Frame Class Hierarchy CFrameWnd SECFrameWnd CMDIFrameWnd SECMDIFrameWnd 15.3.2 SECFrameWnd The SECFrameWnd class derives from CFrameWnd and adds support for the gradient caption. The class also adds support for extended docking windows. Chapter 15 User Interface Extensions 247 15.3.3 SECMDIFrameWnd The SECMDIFrameWnd class derives from CMDIFrameWnd and adds support for the gradient caption and extended docking window features. 15.3.4 Using the Gradient Caption Feature To incorporate the gradient caption into your application: 1. Change the base class of your main frame window class, which is usually CMainFrame. If you’re working with an MDI application, replace the base class (CMDIFrameWnd) with SECMDIFrameWnd. If you’re working with an SDI application, replace the base class (CFrameWnd) with SECFrameWnd. 2. Enable the gradient caption by calling EnableCustomCaption() from your frame window’s OnCreate() member. int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... // Enable the gradient caption feature. EnableCustomCaption(TRUE); return 0; } To change the font of the caption text: 1. Derive a class from either SECMDIFrameWnd or SECFrameWnd. See the preceding procedure for more information. 2. Override the CreateCaptionAppFont() method or the CreateCaptionDocFont() method or both. For example, the code below shows how to italicize the document font. void CMyFrameWnd::CreateCaptionDocFont(CFont& font) { NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(ncm); VERIFY(SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0)); ncm.lfCaptionFont.lfItalic = TRUE; font.CreateFontIndirect(&ncm.lfCaptionFont); } 248 15.4 Keyboard Shortcuts The keyboard shortcut classes enable users to redefine the keyboard for your application. These classes enable end-users to update the accelerator table at run time by choosing key bindings. 15.4.1 The Keyboard Shortcut Classes The following figure is the class hierarchy for the Keyboard Shortcut Classes. Figure 118 – Keyboard Shortcut Class Hierarchy CArray<ACCEL, ACCEL&> SECShortcutTable CDialog SECShortcutDlg CArray<SECCommand,SECCommand&> SECCommandList 15.4.2 SECShortcutTable SECShortcutTable contains key bindings in the form of an array of ACCELs. 15.4.3 SECCommandList SECCommandList contains a list of every command IDs that you can assign together with a short and a long description. You can default any and all parts of SECCommandList. 15.4.4 SECShortcutDlg SECShortcutDlg is a dialog class that provides a front-end for entering application macros. It presents a standard dialog for accelerator key entry to the application. The Objective Toolkit shortcut classes only use the APIs found in Win32. Chapter 15 User Interface Extensions 249 15.4.5 Using the Keyboard Shortcut Classes The following sections describe how to use keyboard shortcut classes in your application. 15.4.5.1To incorporate keyboard shortcuts into your application 1. At the end of InitInstance(), add this code to load user-assigned shortcuts. SECShortcutTable shortcuts; if (shortcuts.Load()) { shortcuts.Apply(); } 2. To invoke the shortcut dialog, use the following code. Generally, you can handle the shortcut dialog at the main frame window level because the shortcut handling is not view or document specific. void OnAssignShortcuts() { SECCommandList commands; SECShortcutTable shortcuts; SECShortcutDlg dlg(commands, shortcuts); if (dlg.DoModal() == IDOK && dlg.m_bDirty) { shortcuts.Save(); shortcuts.Apply(); } } 15.4.5.2To update menus The application updates the menus automatically to show the keys defined in the current accelerator table. Any accelerator descriptions you put into menu items in the resource file are discarded. If you have menu items that modify themselves in their OnUpdate() method, you need to ensure that you preserve the accelerator that is currently defined. For example, the user can redefine the keystrokes for Undo. If your application changes the Undo menu to describe the last action, you need to preserve the text of the current accelerator. 15.4.5.3To allow or disallow certain keyboard combinations 1. Complete the steps in Section 15.4.5.1, “To incorporate keyboard shortcuts into your application.” 2. Before you invoke the SECShortcutDlg dialog and after you instantiate the SECCommandList object, call the SECCommandList::SetRules() method. For example: // The default, appropriate for programs // that deal with text. 250 commands.SetRules(HKCOMB_NONE|HKCOMB_S, HOTKEYF_CONTROL); // A good alternative for draw programs // that never need // character input in the main window. commands.SetRules(0,0); // Allow all keys, even // unmodified letters // Very restrictive -- only CTRL+ALT // combinations will // be allowed, and anything else will // convert to a CTRL+ALT commands.SetRules((WORD)~HKCOMB_SC, HOTKEYF_CONTROL|HOTKEYF_ALT); For more information on this method, see the documentation for CHotKeyCtrl::SetRules() in the Objective Toolkit Class Reference. 15.4.5.4Setting Up Commands A default list of command IDs is automatically generated when you declare an instance of SECCommandList. This list is generated when the application reads the main frame window and template menus to look for IDs. The name of the macro is the menu sequence (for example, File:Open). The long description is the string resource of the same ID. This string resource is typically shown in the status line when the user highlights a menu item. You can either replace or add to this list of command IDs. For example: const SECDefaultCommandId defaultCommands[] = { { ID_VIEW_TOOLBAR, IDS_MAC_VIEW_TOOLBAR, IDS_DESC_VIEW_TOOLBAR }, { ID_FILE_OPEN, 0, IDS_DESC_FILE_OPEN }, // Default name { ID_FILE_SAVE, IDS_MAC_FILE_SAVE, 0 }, // Default description { ID_FILE_PRINT } // Default both }; SECCommandList commands; SECCommandList commands; commands.ClearCommandIds(); commands.AddCommandIds(defaultCommands, sizeof(defaultCommands)/ sizeof(SECDefaultCommandId)); In the above example, the elements defined in defaultCommands comprise a custom list of IDs. These are the only command IDs that you can assign. The call to ClearCommandIds() removes each of the default IDs that you set up by default in the constructor for SECCommandList. The call to AddCommandIds() installs the custom list of IDs. This example also shows how to customize the names and descriptions that the dialog uses. Here is the definition for SECDefaultCommandId(). Chapter 15 User Interface Extensions 251 struct SECDefaultCommandId { // Id of this command, such as ID_VIEW_TOOLBAR UINT m_nID; // String ID that gives the short name of the // command. This name appears in the // "Select a macro:" listbox. If this is // zero, then the menu name or toolhelp // text is used. UINT m_nName; // String ID that gives the description of // the command. This name appears in the // "Description:" listbox. If this is zero, // then the status bar text for this // id is used. UINT m_nDescription; }; As the definition of defaultCommands in the preceding example indicates, you can leave m_nName or m_nDescription (or both) at zero, so SECCommandList uses either the menu name or the corresponding string resource to find the name or description (or both). If you remove an ID from this list during the course of development, the saved file can still use it. Select Reset All in the dialog to solve this problem. Visual Studio does perform a dependency check on resource.h. Resource.h defines the values for every string and command ID. If you change the value of an ID when you are using a custom table, Visual Studio does not automatically recompile the module that contains the table. This can result in erratic behavior in the dialog class. 15.4.5.5Excluded IDs Objective Toolkit automatically excludes some IDs even if you put them in a list because you cannot assign them or because you never would assign them. The virtual member function QueryExcludeId() in SECCommandList contains the default list of excluded IDs. When you derive your own class from SECCommandList, you can either add IDs to the list or replace this list. A new constructor is also needed for the derived class to change the default behavior of the base SECCommandList class. For example, class MyCommandList : public SECCommandList { public: MyCommandList(): SECCommandList(FALSE) { SetRules(HKCOMB_NONE|HKCOMB_S, HOTKEYF_CONTROL); DeriveDefaults(); } public: virtual BOOL QueryExcludeId (UINT nID); }; The default list of excluded IDs is as follows: 252 MRU entries on the File menu MDI child entries on the Window menu ID_NEXT_PANE and ID_PREV_PANE Although ID_HELP and ID_CONTEXT_HELP are not excluded by default, you might want to exclude them. You cannot reliably assign commands to these IDs. 15.4.5.6Saving the Shortcuts The shortcuts are automatically saved to a file named <application-name>.MAC. If possible, store this file in the same directory as the executable. Otherwise it is put in the Windows directory. The filename is generated by the member function GetDataFileName() in SECShortcutTable. This function returns a fully qualified name and path. This function is virtual and can be overridden. The second argument to this function specifies either MAIN_NAME or ALTERNATE_NAME. This is the mechanism that switches from the application directory to the Windows directory. You can also replace the storage mechanism completely by overriding Load() and Save() in SECShortcutTable. You would do this if you wanted to save the table in the registry. You can write the table to any variant of a CArchive object. The shortcuts are loaded and applied in InitInstance() of the application class. 15.4.5.7Keyboard Shortcut Notes The keyboard shortcut classes support multiple doc templates and multiple view menus. When you load and save the list of shortcuts, the shortcut classes scan every doc-template that is associated with the application object and then updates the menu text to display the shortcut key. This only occurs if each menu has an unique command ID. Overlapping command IDs are resolved in the context of the active document's menu. Menus are updated recursively through all levels of pop-up sub-menus to support cascading menus. If you create an ID that appears on a toolbar but not in a menu, the shortcut classes use the Toolhelp string resource to generate a name and description. These classes support international and multi-byte versions of Windows. These classes presume that a single accelerator table exists in the main frame window. View-specific accelerator tables are not supported. Multiple main frame windows are not supported. 15.4.6 Keyboard Shortcut Sample The Objective Toolkit Shortcut sample is called ShortCut. It shows how to add shortcuts to a standard AppWizard-generated MDI application. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 15 User Interface Extensions 253 15.5 Splash Window A splash window is a transient window that appears temporarily when the user starts the application. Generally, the pop-up window displays a copyright notice while the application is initialized. 15.5.1 The SECSplashWnd Class The SECSplashWnd class provides you with a ready-to-use splash window for your applications. All you need to do is insert a splash bitmap and text. The splash window deletes itself after a specified time or when the user clicks it, so you do not have to keep track of the splash window in your application. Figure 119 – Objective Toolkit’s Splash Window Class Hierarchy CWnd SECSplashWnd Table 39 lists and describes the SECSplashWnd methods. Table 39 – SECSplashWnd Methods Method Description AllowUserDismiss() Sets whether the user can dismiss the Splash Screen by clicking the mouse or pressing a key. Default: True. EnableTimer() You can create the Splash Screen without enabling the timer. Call this function to start the countdown. SetTaskbarTitle() The Splash Screen can include a taskbar entry. Use this function to set the Taskbar Caption. Default: No taskbar entry. SetAlwaysOnTop() You can set the Splash Screen to WS_EX_TOPMOST. Default: Not set to WS_EX_TOPMOST, unless it is created before the application’s main window. PostSplashDraw() This is an overridable method that lets you paint on top of the Splash Screen after it is rendered to the screen. Dismiss() This function dismisses the Splash Screen. DisableParent() You can disable or enable the parent of the Splash Screen when the Splash Screen is displayed. Default: Parent enabled. The SECSplashWnd constructor has an alternate constructor that takes two additional parameters— bWaitForTimer and bAlwaysOnTop. 254 SECSplashWnd(UINT UINT BOOL BOOL nNewBitmapID, nNewDuration = 2500, bWaitForTimer = FALSE, bAlwaysOnTop = FALSE); If you want to display a login or password dialog with SECSplashWnd as the parent, set the third parameter to TRUE and then call EnableTimer() when you dismiss the dialog. 15.5.2 Using SECSplashWnd To add SECSplashWnd to your application: 1. In the Visual Studio resource editor, add a splash screen bitmap resource. 2. Create an instance of SECSplashWnd on the heap. In the constructor, specify the bitmap ID and the duration for the splash window. For example: m_pSplashWnd = new SECSplashWnd(IDB_SPLASHWND,3500); 3. Call the Create() method to create the splash window. m_pSplashWnd->Create(); 15.5.3 SECSplashWnd Samples The sample Splash51 demonstrates the 32-bit implementation of SECSplashWnd. This sample also uses the SECRegistry classes to read the user’s name from the registry and display it on the splash screen during application startup. When you open the project in Visual Studio, the bitmap used by SECSplashWnd appears in the list of application resources. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 15 User Interface Extensions 255 15.6 Custom Status Bar Objective Toolkit’s custom status bar class, SECCustomStatusBar, is a Windows status bar that has more features and is easier to configure than MFC’s CStatusBar. The custom status bar allows you to configure the fonts used in status bar panes, the text alignment, and foreground and background colors. When you use CStatusBar, panes can only contain text. The custom status bar adds the ability to embed bitmaps in status bar panes. Moreover, the custom status bar allows you to assign custom cursor bitmaps to individual status bar panes. When the cursor is inside the pane, the cursor takes the form of the specified bitmap. Outside of the pane, the cursor returns to its normal shape. In addition, the custom status bar helps process mouse events inside a status bar pane. Figure 120 – Example SECCustomStatusBar In addition, the custom status bar incorporates a progress indicator that you can show programmatically in place of the status bar panes and then hide when the process finishes. Figure 121 – Example SECCustomStatusBar Progress Bar SECCustomStatusBar inherits all the functionality of a standard MFC status bar and adds the features described previously. Figure 122 – Custom Status Bar Class Hierarchy CWnd CControlBar SECControlBar SECStatusBar SECCustomStatusBar 15.6.1 Using SECCustomStatusBar The following sections describe how to use the custom status bar and its associated progress bar in your application. 15.6.1.1To incorporate SECCustomStatusBar into your code The following steps assume that you have created an application that already has an initial status bar based on CStatusBar or SECStatusBar. 256 1. Replace the class for your frame window’s status bar (CStatusBar or SECStatusBar) with SECCustomStatusBar. a. For each pane you want to add to the status bar, create a resource symbol for the new status bar pane (for example, ID_INDICATOR_EDIT). 2. Add the new resource symbols as elements in the indicators array. This array is usually declared with module scope in the implementation file for your frame class. It specifies the positions of each pane in the status bar. static UINT indicators[]= { ID_SEPARATOR, ID_INDICATOR_EDIT, ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL; }; // added here! 3. Ensure that the indicators array is passed in to SECCustomStatusBar::SetIndicators(). if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create } 4. After the status bar has been created via Create() and initialized via SetIndicators(), create the status bar panes. Status bar panes can contain bitmaps, text, or CWnd-based controls. To add bitmap or text panes, create a PANEINFOEX structure, set the appropriate data fields in the structure, and call SetPaneInfoEx(). ... // Configure a text pane. PANEINFOEX pex; pex.iIndex = 2; pex.strText = “Text”; pex.iFlags = SBP_TEXT; m_wndStatusBar.SetPaneInfoEx(&pex); // Configure a text pane. pex.iIndex = 3; pex.pBitmap = m_pBitmap; pex.iFlags = SBP_BITMAP; m_wndStatusBar.SetPaneInfoEx(&pex); ... The SBP_BITMAP and SBP_TEXT style flags shown above are mutually exclusive. To add CWnd-derived controls, create the controls and then register them with the status bar via the RegisterWndToPane() member. The CommandToIndex() member fetches the correct index parameter. For example, the following code adds an edit control to the status bar: Chapter 15 User Interface Extensions 257 ... int nPanelIndex=m_wndStatusBar.CommandToIndex( ID_INDICATOR_EDIT); m_wndPanelEdit.Create(WS_VISIBLE|ES_LEFT| ES_AUTOHSCROLL, CRect(0,0,0,0), &m_wndStatusBar, ID_INDICATOR_EDIT); m_wndStatusBar.RegisterWndToPane( nPanelIndex, SECCustomStatusBar::FitPaneToWnd); ... &m_wndPanelEdit, You’re done. The CWnd object is now automatically sized and positioned in response to SECCustomStatusBar events. Call RegisterWndToPane() with a NULL CWnd* to unregister a window. The sample \Samples\Toolkit\MFC\UIExt\statbar sampledemonstrates how to use the SECCustomStatusBar class. This sample is not shipped with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 15.6.1.2To display the progress bar over the status bar SECCustomStatusBar includes routines that allow the application to replace the status bar with a progress control bar temporarily. The progress control bar is implemented as an SECProgressCtrl object so you can use the extended styles provided by SECProgressCtrl. In addition, you can display a text string to the left of the progress bar. 1. Use the InitializeProgressControl() method to create and display the progress bar. This function sets the optional text, the range and initial value of the progress indicator, and the extended style flags. 2. Call SetProgress() or StepProgress() to update the progress indicator. 3. Call SetPaneText() for pane 0 to change the text to the left of the bar. 4. Call UninitializeProgressControl() to remove the progress bar. SECCustomStatusBar does not support the SEC_EX_PROGRESS_SHOWTEXT style for the progress bar. Nor does it provide a method for setting the text string, and the m_pProgressCtrl member is protected. To use the SEC_EX_PROGRESS_SHOWTEXT style, create a new class derived from SECCustomStatusBar. You might add a new function to your class that passes the text along to the progress bar. For example: MyCustomStatusBar::SetProgressBarText(LPCTSTR pString) { m_pProgressCtrl->SetWindowText(pString); } Then you can call SetProgressBarText() to update the text whenever you call SetProgress() or StepProgress() to update the progress indicator. 258 15.6.2 Customizing SECCustomStatusBar The following section describes how to modify the behavior or appearance of the custom status bar. 15.6.2.1To customize panes with the PANEINFOEX structure The SECCustomStatusBar class adds several configurable attributes to your frame window’s status bar. PANEINFOEX is a structure that aggregates every attribute of one pane of a custom status bar. The PANEINFOEX structure aggregates every attribute introduced by SECCustomStatusBar. It even includes attributes defined by its base class, SECStatusBar. The following code shows how to set flags for each attribute you want to apply to a status bar pane. // Configure the second pane to display // the light bitmap and // use the gripping hand cursor. PANEINFOEX pex; pex.iIndex = 2; pex.hCursor = AfxGetApp()->LoadCursor(IDC_GRIPPING_HAND); pex.pBitmap = m_pBitmap; pex.iFlags = SBP_CURSOR | SBP_BITMAP; m_wndStatusBar.SetPaneInfoEx(&pex); The flags for the iFlags member of PANEINFOEX can include the following: SBP_ID Set the ID of the pane to the uiID member of PANEINFOEX. SBP_STYLE Set the style of the pane to that of the uiStyle member of PANEINFO. SBP_WIDTH Use the cxWidth member of PANEINFO for the width. SBP_TEXT Use the text set in the strText member of PANEINFO for the pane. SBP_TEXT_ALIGN Use the iTextAlignment member of PANEINFO for text alignment. (Text alignment flags like TA_LEFT are defined in WINGDI.H.) SBP_FOREGROUND Use the COLORREF set in the crTextForeground member of PANEINFOEX for the foreground color. SBP_BACKGROUND Use the COLORREF set in the crTextBackground member of PANEINFOEX for the background color. Chapter 15 User Interface Extensions 259 SBP_BITMAP Display the CBitmap pointed to by the pBitmap member of PANEINFOEX in the pane. SBP_CURSOR Display the cursor specified by the hCursor member of PANEINFOEX when the mouse pointer is over the pane. These flags can be logically OR’d together. However, the following flags are mutually exclusive: SBP_BITMAP, SBP_TEXT, and SBP_STYLE. 15.6.2.2To extend the SECCustomStatusBar class The SECCustomStatusBar class has the following virtual member functions that allow you to customize the behavior of the class. Table 40 – Virtual Member Functions for SECCustomStatusBar 260 Method Description InitializeProgressControl() Initializes and shows the progress indicator. UninitializeProgressControl() Deletes the progress indicator and restores the status bar to its original content. SetVisibleAllRegWnd() Sets the visibility of the registered windows. ResizeAllRegWnd() Resizes all registered windows based on current attributes. 15.7 Thumbnail Classes Many Windows applications like Microsoft PowerPoint and Delrina WinFax support thumbnails. Thumbnails let the user preview a file without having to open it. Objective Toolkit provides thumbnail support through the document/view architecture. Figure 123 – Example Thumbnail A snapshot of the current view is stored at the beginning of the archive file when the document is serialized. A specialized File Open dialog displays the stored image of highlighted files to aid in file selection. The thumbnail classes are designed to display the special images stored in archive files. They cannot be used to display images from other file formats. 15.7.1 The Thumbnail Classes The following classes work together to provide Objective Toolkit thumbnail support. SECTNBitmap, SECTNDC, and SECTNFileDialog are usually transparent to your application, but you need to know how they function. Chapter 15 User Interface Extensions 261 Figure 124 – Thumbnail Class Hierarchy CWinApp SECTNWinApp CDocument SECTNDocument CView SECTNView CFileDialog SECTNFileDialog CBitmap SECTNBitmap CDC SECTNDC 15.7.1.1SECTNBitmap SECTNBitmap is a CBitmap derivative that creates, saves, and displays thumbnail images. 15.7.1.2SECTNDC SECTNDC is a CDC derivative that is passed to your SECTNView’s OnDraw() method. The view draws the thumbnail image onto the SECTNDC. Objective Toolkit converts the image to an SECTNBitmap and then saves the image. 15.7.1.3SECTNDocument Class SECTNDocument is an optional CDocument derivative that stores an SECTNView thumbnail image during serialization and bypasses the thumbnail image when reading. 15.7.1.4SECTNFileDialog SECTNFileDialog uses a CFileDialog derivative to display a thumbnail. SECTNFileDialog also automatically reads and displays the thumbnail image when the user clicks a file name in the dialog. 15.7.1.5SECTNView SECTNView is a CView derivative that can automatically generate a thumbnail by drawing onto an SECTNDC. This behavior is overridable so that applications can implement their own thumbnail drawing routines using an interface similar to CView printing. 262 15.7.1.6SECTNWinApp SECTNWinApp automatically creates an SECTNFileDialog when the user selects File|Open. 15.7.2 Using the Thumbnail Classes To enable thumbnails in your application: 1. Replace your application object’s base class with SECTNWinApp. 2. Change your CView-derived class to derive from SECTNView. Add any custom thumbnail code to this class. 3. Change your CDocument derivative to derive from SECTNDocument. Be sure to call SECTNDocument::Serialize() first in your ::Serialize() method. By default SECTNView generates a thumbnail that is the size of your entire view. SECTNView cannot infer how you want your view’s thumbnail to appear, so you need to write special thumbnailgenerating code. To generate custom thumbnails: 1. Derive a class from SECTNView and override the OnDraw() method. 2. In the OnDraw() override, you can determine if a thumbnail is being drawn by calling IsThumbNailing(). If the view is a thumbnail, draw the view’s thumbnail on the specified DC. 15.7.3 Thumbnail Sample The Objective Toolkit thumbnl sample (Samples\Toolkit\MFC\UIExt\thumbnl) demonstrates the MFC scribble tutorial application with thumbnail support. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 15 User Interface Extensions 263 15.8 Tip of the Day Dialog A tip of the day dialog is displayed when the user starts the application. It displays random but useful information about the application. Figure 125 – Example Tip of the Day 15.8.1 The SECTipOfDay Class SECTipOfDay stores tips in a plain ASCII file so you can easily create tip files (*.tip) with any editor. SECTipOfDay is designed to be fully customizable. You can specify different fonts or icons and change other attributes by deriving your own tip class from SECTipOfDay. Figure 126 – Objective Toolkit SECTipOfDay Class Hierarchy CDialog SECTipOfDay 15.8.2 SECTipOfDay Resource IDs The following resource IDs are available for the SECTipOfDay class. You can use them to display items on the dialog selectively. See Section 15.8.3.4, “To hide buttons on the tip of the day dialog.” Table 41 – Resource IDs for SECTipofDay 264 Resource ID Description IDC_TOD_OK_BUTTON The OK button ID. IDC_TOD_NEXT_BUTTON The Next Tip button ID. IDC_TOD_PREV_BUTTON The Previous Tip button ID. Table 41 – Resource IDs for SECTipofDay (Continued) Resource ID Description IDC_TOD_HELP_BUTTON The Help button ID. IDC_TOD_SHOW_CHECK The Show tips at startup check box. IDC_TOD_GROUPBOX The tip itself. 15.8.3 Using SECTipOfDay The SECTipOfDay class has the same interface as CDialog so you can make SECTipOfDay modal or modeless. The SECTipOfDay constructor takes arguments that specify the tip file, tip number, and other parameters specific to SECTipOfDay. It is the application's responsibility to store the tip number so that it can provide the user with a new tip at every application invocation. 15.8.3.1To create a modal tip of the day dialog The following changes are typically done in the InitInstance() method of the application, as part of the initialization of the application. The code is executed after the main frame has been created and displayed. 1. Load the startup status and tip from a file, registry, or other source. For example: m_nLastTip = GetProfileInt(_T("SampleTip"),_T("CurrentTip"),0); m_bShowTipAtStartup = (BOOL)GetProfileInt(_T("SampleTip"),_T("ShowAtStart"),1); 2. Instantiate an SECTipOfDay object by passing the tip and startup information to the constructor and then calling the DoModal() method. For example: if (m_bShowTipAtStartup) { SECTipOfDay MyTips(_T("todtest.tip"),++m_nLastTip); MyTips.DoModal(); } 15.8.3.2To create a modeless tip of the day dialog 1. Load the startup status and tip from a file, registry, or other source. 2. Create an SECTipOfDay object on the heap with the new operator. m_pModelessTip = new SECTipOfDay(_T("todtest.tip"),++m_nLastTip, m_bShowTipAtStartup); Chapter 15 User Interface Extensions 265 You can declare the object on the stack (for example, as the member of a class) as long as the object is not expected to go out of scope while the tip of the day window is displayed. 3. Display the dialog in a modeless manner by calling CDialog::Create() and CDialog::ShowWindow(). theApp.m_pModelessTip->Create(); theApp.m_pModelessTip->ShowWindow(SW_SHOW); 15.8.3.3To change the caption of the tip of the day dialog 1. Derive a class from SECTipOfDay. 2. Override the OnInitDialog() method. In the override, call the base class implementation and then call the CDialog::SetWindowText() method. For example: CMyTipOfDay::OnInitDialog() { SECTipOfDay::OnInitDialog(); // Change the caption to something else this->SetWindowText( _T("This is my custom tip du jour")); // FYI, by default SECTipOfDay, centers the tip, // you might want // to move it else where here. return TRUE; } 15.8.3.4To hide buttons on the tip of the day dialog 1. Derive a class from SECTipOfDay. 2. Override the OnInitDialog() method. In the override, call the base class implementation and then call ShowWindow(SW_HIDE) for the dialog items to be hidden. See Section 15.8.2 for a list of resource IDS. For example: CMyTipOfDay::OnInitDialog() { SECTipOfDay::OnInitDialog(); // Hide the “previous” button and the // “show at startup” button. CWnd * pWnd = (CWnd *)GetDlgItem(IDC_TOD_PREV_BUTTON); pWnd->ShowWindow(SW_HIDE); pWnd = (CWnd *)GetDlgItem(IDC_TOD_SHOW_CHECK); pWnd->ShowWindow(SW_HIDE); 266 // FYI, by default SECTipOfDay, centers the tip, // you might want // to move it else where here. return TRUE; } 15.8.4 SECTipOfDay Sample The Objective Toolkit todtest sample (Samples\Toolkit\MFC\UIExt\todtest) demonstrates the SECTipOfDay class and shows how to customize the class to specify a different font and icon. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 15 User Interface Extensions 267 15.9 Tray Icon Class The SECTrayIcon class helps you create applications that are invoked via the Windows tray interface. Animated icons are supported. A System Tray Icon is a user interface metaphor that was introduced by Windows 95. If you look at the rightmost edge of the Windows 95 taskbar, there is a small "tray" of application icons and a system clock. SECTrayIcon provides your application with an easy-to-use mechanism for adding your own custom icons to the system tray and providing user interface feedback such as tooltip text, context menu support, and animated icons. Figure 127 – Objective Toolkit Tray Icon Class Hierarchy CObject SECTrayIcon 15.9.1 To incorporate the Tray Icon Class into your application 1. In your main window class (main frame or dialog), add an SECTrayIcon data member for each tray icon you want to display. For example: SECTrayIcon m_TrayApp; 2. In the initialization code for your main window class, create the tray icon by calling the Create() method. m_TrayApp.Create(this); 3. In the resource editor, add an icon resource and then set the icon with the SetIcon() method. m_TrayApp.SetIcon(IDI_STINGRAY); 4. Optionally, set tooltip text with the SetTip() method. m_TrayApp.SetTip(_T("Stingray Tray Icon Demo")); 5. Display the tray icon with the Show() method. m_TrayApp.Show(TRUE); The SECTrayIcon destructor automatically cleans up the icon; however, you can still issue a Destroy() call directly. 268 15.9.2 To add notification handlers for mouse events The following steps set up a notification handler to display a context menu in response to a rightclick. 1. Complete the steps in Section 15.9.1. 2. For each tray icon, create a unique notification ID or use the GetNextNotifyID() method to generate one on the fly. m_TrayNotifyId=SECTrayIcon::GetNextNotifyID(); Add the notification ID as the second parameter to the Create() method call. m_TrayApp.Create(this, m_TrayNotifyId); 3. Add the WM_SEC_TRAYICON_NOTIFY message using the ON_MESSAGE macro in the message map. The tray icon sends notifications via this user message. You can also specify your own message ID as an optional parameter to the SECTrayIcon::Create() method call. ON_MESSAGE(WM_SEC_TRAYICON_NOTIFY, OnTrayIconNotify) Add a message handler for this message. The wParam contains the notification ID passed to the Create() method. The lParam contains the mouse message. For example: LRESULT CTrayIconDlg::OnTrayIconNotify(WPARAM wParam, LPARAM lParam) { // Use the wParam to identify which tray icon if(wParam==m_TrayNotifyId) { // lParam identifies the mouse message switch(lParam) { case WM_MOUSEMOVE: // mousemove is a good place to // change tooltip text – for example // if you were displaying time of day case WM_RBUTTONUP: // the SECTrayIcon::ShowContextMenu // static member makes it a snap to // display popup menus—-all the // positioning and message routing // is done automatically for you. SECTrayIcon::ShowContextMenu(this, IDR_MENU_NOTIFY); break; case WM_LBUTTONDBLCLK: AfxMessageBox( _T("You just double clicked me!")); break; } } return 0L; } Chapter 15 User Interface Extensions 269 15.9.3 To animate a tray icon 1. Complete the steps in Section 15.9.1. 2. In the resource editor, add icons for each frame of the animation. 3. After the SECTrayIcon::Create() method call, call the AddState() method and specify a unique ID for each state, the icon to display, the tooltip to display, and a frame delay time. The default time delay is 255 milliseconds. If you need to specify a different time delay, multiply the delay parameter by 17 to determine the time in milliseconds. m_tray.AddState(IDI_SCROLL_ON0, IDI_SCROLL_ON0, strOn, nFrameDelay); m_tray.AddState(IDI_SCROLL_ON1, IDI_SCROLL_ON1, strOn, nFrameDelay); m_tray.AddState(IDI_SCROLL_ON2, IDI_SCROLL_ON2, strOn, nFrameDelay); m_tray.AddState(IDI_SCROLL_ON3, IDI_SCROLL_ON3, strOn, nFrameDelay); m_tray.AddState(IDI_SCROLL_ON4, IDI_SCROLL_ON4, strOn, nFrameDelay); m_tray.AddState(IDI_SCROLL_ON5, IDI_SCROLL_ON5, strOn, nFrameDelay); 4. After the call to the Show() method, call the Play() method to start the animation and the Stop() to halt the animation. m_TrayAnimated.Play(IDI_SCROLL_ON0,6); 15.9.4 Tray Icon Sample Refer to the trayicon sample (\Samples\Toolkit\MFC\UIExt\TrayIcon) for more information. Animations are also demonstrated in this sample. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 270 15.10User-Tools Menu Objective Toolkit’s user-tools menu classes implement a user-configurable Tools menu like the one in Microsoft Visual Studio. A user-tool is an executable that the application can spawn programmatically. The user can add his or her own menu items to the Tools menu and specify an action for each menu item. Figure 128 – Example User-Tools Dialog 15.10.1The User-Tools Menu Classes Two classes are involved in the implementation of the user-tools menu feature. SECUserTool encapsulates the information required to execute a tool. SECUserToolsDlg allows the user to manage the list of tools. Figure 129 – User-Tool Menu Class Hierarchy CObject SECUserTool CDialog SECUserToolsDlg 15.10.1.1SECUserTool TheSECUserTool class provides an abstraction of a user-tool. An SECUserTool object encapsulates the filename, command-line arguments, and initial directory that describe how and where to run the executable. In addition, the SECUserTool interface contains an Execute() method that uses these attributes to launch the user-defined tool. Chapter 15 User Interface Extensions 271 15.10.1.2SECUserToolsDlg The SECUserToolsDlg class implements a user-tools dialog. A user-tools dialog allows the user to edit a list of user-tools, where each user-tool is represented by one SECUserTool object. Through this dialog, the user can create new user-tools, edit and delete existing user-tools, and reorder the list of user-tools. 15.10.2Using the User-Tools Menu Classes To add user-tool support to your application: 1. Add a CObArray member to your main frame class (usually CMainFrame) to store the SECUserTool objects. 2. Instantiate an SECUserTool object and add it to the CObArray. 3. Call the SetMenuText() method to set the menu text associated with the user-tool. 4. Call the SetCommand() method to set the absolute path and filename of the executable for the user-tool. 5. Call the SetArgs() method to set the command-line arguments that are passed to the usertool upon execution. 6. Call the SetDirectory() method to set the absolute path of the directory in which the usertool is initially executed. 7. Alternatively, instantiate an SECUserToolsDlg dialog and initialize it by calling SetToolsArrayPtr() with the array of SECUserTool objects. Activate the dialog, which automatically performs calls the preceding methods using the information that the user provides for each user-tool. To execute a user-tool: Call the Execute() method and pass the pReplacements parameter to run the user-tool with the specified arguments and initial directory. To make a copy of a user-tool: Call the Clone() method. To serialize an array of user-tools: Use the ReadUserToolFile() or WriteUserToolFile() global function. To append an array of user-tools to a menu: Use the AppendUserTools() function. To delete an array of user-tools: Use the EmptyUserToolArray() function. 272 15.10.3User-Tool Menu Sample The Objective Toolkit toolmenu sample (Samples\Toolkit\MFC\UIext\toolmenu) demonstrates the capabilities of the user-definable tools menu and dialog. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 15.11Workspace Manager Objective Toolkit’s workspace manager classes provide a flexible framework that includes menu and dialog resources for saving and restoring window configurations. You can save and restore workspaces to and from a file or the Win32 registry. The workspace save/restore feature is a user interface enhancement that does not require you to change or redesign your user interface. In addition, you can use the workspace save/restore interface enhancement with Stingray’s MDI, FDI, MTI, or Extended Control Bar Architecture enhancements. The extended workspace manager now provides a simple alternative to the LoadBarState() and SaveBarState() mechanisms utilized in previous releases of Objective Toolkit for automatically restoring and saving the state of your application’s window configuration. The extended workspace manager requires you to use the enhanced frame window classes included in Objective Toolkit. With WDI, the workbook mode must be saved as user data. There are also limitations in using the SECWorkspaceManagerEx class with FDI applications. For example, there is currently no direct support for saving toolbar information in the FDI child frames. 15.11.1The Workspace Manager Classes Objective Toolkit includes two workspace manager classes— SECWorkspaceManager and the more powerful SECWorkspaceManagerEx. Figure 130 – The Objective Toolkit Workspace Manager Class Hierarchy CCmdTarget SECWorkspaceManager CWnd SECWndPlugin SECWorkspaceManagerEx The next few sections describe the differences between these two classes. Chapter 15 User Interface Extensions 273 15.11.1.1SECWorkspaceManager versus SECWorkspaceManagerEx SECWorkspaceManagerEx supports all the features in SECWorkspaceManager and adds the following: Persistence of workspaces directly to either a file or the registry. Persistence of multiple views per document. Persistence of an application’s current activation state. Workspace versioning. Full support for Objective Toolkit docking windows, toolbars, and menubars. Support for Docking Views. All data persisted by the workspace manager is stored in a tree of SECPersistentTreeNode (PTN) objects. Each object can have an arbitrary number of key-value pairs to represent one piece of data. This model is similar to the windows system registry. By default, the extended workspace manager stores the application state in a preset tree where settings are grouped logically according to their role. For example, controlbar settings are in one subtree, child frame windows in another, etc. By overriding the appropriate Open() and Save() workspace manager methods, you can easily add your own child PTN objects to be included in the workspace state. See Section 15.11.2.3, “To add application specific information to the workspace state.” This class is easier to integrate into your application than the legacy SECWorkspaceManager class. SECWorkspaceManagerEx has been completely redesigned. You can easily extend it with custom configuration information and a custom persistence protocol. SECWorkspaceManagerEx is fully redesigned, and has many enhancements compared to its predecessor, SECWorkspaceManager. Unfortunately, the two classes are not code compatible with each other. You cannot replace previous instances of SECWorkspaceManager with SECWorkspaceManagerEx. We would like to encourage you to migrate to SECWorkspaceManagerEx in any future development. SECWorkspaceManager is an obsolete class that is still offered only to ensure backward compatibility. 15.11.1.2Support for Dynamically Created Controlbars Unlike the legacy SECWorkspaceManager class, the new SECWorkspaceManagerEx class can now persist controlbars that have been created dynamically (for example, controlbars created using the new operator). The SECWorkspaceManager class can only restore the states of windows that already exist (for example, those created during CMainFrame::OnCreate) whereas the new SECWorkspaceManagerEx class can actually create controlbars based on stored run-time class information and also restore their state. The extended workspace manager class, SECWorkspaceManagerEx, can also persist the run-time class information corresponding to each controlbar, which can then be used to reconstruct any dynamically created controlbars. This support is virtually seamless to your application. The only additional step required for this support is the implementation of the following two MFC serializa- 274 tion macros for each controlbar class: IMPLEMENT_SERIAL and TOOLKIT_DECLARE_SERIAL. These macros are already included in all the packaged Objective Toolkit SECControlBar-derived classes. Please consult the standard MFC references for more information on using these macros. 15.11.1.3Multiple Views per Document, Multiple Views Per Frame The extended workspace manager has prebuilt support for multiple views tied to a single document when each view occupies its own frame window. This applies to Docking Views as well. If you have multiple views sharing a single frame window (for example, inside a splitter or tab window), the workspace manager cannot predict how you want these additional views to be restored (this is an application specific function). What it can do, however, is supply the appropriate virtual overrides for you to hook in the application specific information required for this procedure. See SECWorkspaceManagerEx::SaveAdditionalViewPerFrame() and SECWorkspaceManagerEx::OpenAdditionalViewPerFrame() for more information. The workspace manager automatically stores the presence of multiple views per frame so that OpenAdditionalViewPerFrame() is automatically called with the appropriate information. It is your responsibility to supply any additional state information, and act upon that state information (for example, inserting a new tab, creating a new splitter, etc.). 15.11.2Using SECWorkspaceManagerEx The following sections describe how to use SECWorkspaceManagerEx in a variety of application types. 15.11.2.1To add workspace management to your application 1. Include support for Objective Toolkit docking windows. 2. At the end of your CMainFrame::OnCreate() handler, add a call to the InitWorkspaceMgrEx() method. For example: InitWorkspaceMgrEx( _T("Software\\MyCompany\\Sample\\v1.0"), FALSE); The first parameter is a base registry key off HKEY_CURRENT_USER to store your workspace state information. This key must be unique to your application and its version number. The second parameter toggles the workspace manager in registry mode. If TRUE, the workspace manager saves workspace state information to a registry key off the path specified in the first parameter. If FALSE, the workspace manager prompts you to save/load workspace information from a file. The registry key is still used, but it only contains a list of Most Recently Used workspace paths. 3. If you’d like, you can add the following commands to your pull-down menus to hook into the workspace manager functionality automatically. ID_WORKSPACE_NEW ID_WORKSPACE_SAVE ID_WORKSPACE_SAVEAS ID_WORKSPACE_OPEN Chapter 15 User Interface Extensions 275 ID_WORKSPACE_DELETE ID_WORKSPACE_1 (automatic insertion of “Recent Workspaces” list) 15.11.2.2To load and store the workspace state automatically 1. Follow the steps described in Section 15.11.2.1. 2. In your application’s InitInstance() method, obtain a pointer to the workspace manager with the GetWorkspaceMgrEx() method and then call the OpenWorkspace() method. Insert this code after the code that initializes the frame but before the frame is made visible. For example: // create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE;m_pMainWnd = pMainFrame; SECWorkspaceManagerEx* pWsMgr = pMainFrame->GetWorkspaceMgrEx(); if (pWsMgr) pWsMgr->OpenWorkspace(TRUE); // TRUE = open MRU, // FALSE = prompt OpenWorkspace() can take a TRUE or FALSE parameter. If TRUE, the workspace manager automatically loads the name of the last stored workspace. If FALSE, a dialog is displayed that asks the user which workspace to load. 3. In your CMainFrame::OnClose(), save the workspace using the SaveWorkspace() method. SECWorkspaceManagerEx* pWsMgr=GetWorkspaceMgrEx(); ASSERT(pWsMgr); pWsMgr->SaveWorkspace(_T(".\\SdiMgr.wsp")); GetWorkspaceMgrEx() fetches the stored value from the SEC frame window. The SaveWorkspace() call uses the appropriate parameter based on the current storage mode (registry vs. file mode). Please refer to SECWorkspaceManagerEx::SaveWorkspace() in the Objective Toolkit Class Reference for more information. 15.11.2.3To add application specific information to the workspace state 1. Follow the steps described in Section 15.11.2.1. 2. Derive a class from SECWorkspaceManagerEx. Ensure that your derived class has properly implemented the TOOLKIT_DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE macros for run-time CObject instantiation. 3. Specify the run-time class of your SECWorkspaceManagerEx-derived class as a parameter passed to the InitWorkspaceManagerEx() call in CMainFrame::OnCreate(). For example: InitWorkspaceMgrEx( _T("Software\\Stingray\\Samples\\Toolkit\\WMCust\\v1.0"), TRUE, RUNTIME_CLASS(CCustomWrkspcEx)); 276 4. Create an override for the SaveWorkspaceCustomData() method. // Override SaveWorkspaceCustomData to save data // on a per-workspace basis void CCustomWrkspcEx::SaveWorkspaceCustomData ( SECPTNFactory& nodeFactory, SECPersistentTreeNode* pRoot, const CString& strWorkspaceName) { BOOL bCustomDataSetting = GetTheCustomDataSomehow(); // The pRoot PTN object passed in is the // appropriate parent for this custom data. // Now create a child node to store additional // information... SECPersistentTreeNode* pCustomNode = nodeFactory.CreateNode( _T("MyCustomDataNode"),pRoot); ASSERT(pCustomNode); // // // // // // And add the custom data. This will automatically be saved by the recursive save operation initiated by the workspace mgr - no additional coding required. pCustomNode->AddKeyValueBool( _T("MyUniqueCustomKeyName"), bCustomDataSetting); // We could store more key-value pairs // here if we wanted. // The only restriction is // that each key must have a unique name. // Invoke the base... SECWorkspaceManagerEx::SaveWorkspaceCustomData( nodeFactory,pRoot,strWorkspaceName); } 5. Create an override for the OpenWorkspaceCustomData() method. void CCustomWrkspcEx::OpenWorkspaceCustomData( SECPersistentTreeNode* pRoot, const CString& strWorkspaceName) { // Locate the custom data PTN node // stored above... SECPersistentTreeNode* pCustomNode=pRoot-> FindChildNode(_T("MyCustomDataNode")); // it must be there, unless the workspace // was corrupt or not versioned. ASSERT(pCustomNode); Chapter 15 User Interface Extensions 277 // Now, load the custom data stored in this // object (again, we could load multiple // key-values here if they were stored...) BOOL bCustomDataSetting; pCustomNode->GetKeyValueBool( _T("MyUniqueCustomKeyName"), bCustomDataSetting); // Got it, do something DoSomethingWithTheData(bCustomDataSetting); // Invoke the base... SECWorkspaceManagerEx::OpenWorkspaceCustomData( pRoot,strWorkspaceName); } 6. To store workspace data at a finer level of granularity, you can override other SECWorkpsaceManagerEx functions. For example, by overriding OpenSpecificChildFrame() and SaveSpecificChildFrame(), you can store data on a per-view basis. This is useful if you have multiple views tied to a single document and you want to store presentation information for each of those additional views that does not logically fit into the document data stream. 15.11.2.4To store the WDI workbook state in the workspace state 1. Follow the steps described in Section 15.11.2.1 that pertain to the creation of the stub method for the SaveWorkspaceCustomData() and OpenWorkspaceCustomData() overrides. 2. In the SaveWorkspaceCustomData() override, query the workbook state and save it as one of the tree nodes in the workspace state. For example: void CCustomWrkspcEx::SaveWorkspaceCustomData( SECPTNFactory& nodeFactory, SECPersistentTreeNode* pRoot, const CString& strWorkspaceName) { // library should insure parameters are valid ASSERT(pRoot); // Just for purposes of demonstration, // we are saving the current // workbook mode setting // Get the workbook mode setting SECWorkbookWnd* pWB=(SECWorkbookWnd *)AfxGetMainWnd(); ASSERT(pWB); ASSERT_KINDOF(SECWorkbookWnd,pWB); BOOL bWorkbookMode=pWB->m_bWorkbookMode; // // // // // 278 Create a custom child node for data storage. Note, all workspace data is organized in a tree of these SECPersistentTreeNode objects,so we can insert new nodes anywhere in the tree, as long as we also override // the corresponding "Open" function to act // upon this additional data on workspace load. SECPersistentTreeNode* pCustomNode = nodeFactory.CreateNode( szCustomWorkspaceDataNode,pRoot); ASSERT(pCustomNode); // And add the custom data. This will // automatically be saved by the recursive save // operation initiated by the workspace mgr // – no additional coding required. pCustomNode-> AddKeyValueBool( szWorkbookModeKey,bWorkbookMode); // As of Objective Toolkit 5.2, this is a no-op, but it's // still a good practice to get into... SECWorkspaceManagerEx::SaveWorkspaceCustomData( nodeFactory,pRoot,strWorkspaceName); } 3. In the OpenWorkspaceCustomData(), restore the saved workbook state from the data in the workspace state. void CCustomWrkspcEx::OpenWorkspaceCustomData( SECPersistentTreeNode* pRoot, const CString& strWorkspaceName) { // Get the workbook mode setting stored by // SaveWorkspaceCustomData // First, locate the child custom data node SECPersistentTreeNode* pCustomNode = pRoot-> FindChildNode( szCustomWorkspaceDataNode); // if not, corrupt workspace! ASSERT(pCustomNode); // And load the data value in this node // (note that 1 PTN node can have an // arbitrary number of // key-value data pairs, much like how // the registry tree works. // In this example, there's only one key-value // pair). BOOL bWorkbookMode; pCustomNode-> GetKeyValueBool( szWorkbookModeKey,bWorkbookMode); // Setting loaded. Now apply the setting to the // workbook SECWorkbookWnd* pWB=(SECWorkbookWnd *)AfxGetMainWnd(); ASSERT(pWB); ASSERT_KINDOF(SECWorkbookWnd,pWB); pWB->SetWorkbookMode(bWorkbookMode); Chapter 15 User Interface Extensions 279 // As of Objective Toolkit 5.2, this is a no-op, but it's still // a good practice to get into... SECWorkspaceManagerEx::OpenWorkspaceCustomData( pRoot,strWorkspaceName); } 15.11.3Using SECWorkspaceManager The SECWorkspaceManager class is provided only for backward compatibility. You are encouraged to use SECWorkspaceManagerEx for all new development. To add workspace management to your application: 1. Add an SECWorkspaceManager pointer (for example, m_pWorkspaceMgr) to your application class or create it as a global variable if you prefer. 2. In CMyApp::OnInitInstance(), instantiate and create the SECWorkspaceManager instance, storing a pointer to it in m_pWorkspaceMgr. For example: BOOL CIDEApp::InitInstance() { LoadStdProfileSettings(); m_pDocTemplate = new CMultiDocTemplate( IDR_SOURCE_EDITOR, RUNTIME_CLASS(CSrcDoc), RUNTIME_CLASS(CSrcFrame), RUNTIME_CLASS(CSrcView)); AddDocTemplate(m_pDocTemplate); // Instantiate the workspace manager m_pWorkspaceMgr = new SECWorkspaceManager(); m_pWorkspaceMgr->Create(AfxGetApp(), CString("Software\\YourApp\\Workspaces\\") // … } 3. Override OnCmdMsg() in your application class as follows: BOOL CMyApp::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { if (CWinApp::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; // otherwise check the workspace manager if (m_pWorkspaceMgr != NULL && m_pWorkspaceMgr->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; return FALSE; } 280 4. Add the windows to the active workspace when they’re created and then remove them from the workspace when they’re destroyed. To do this, override the CFrameWnd::LoadFrame() and CFrameWnd::DestroyWindow() methods as follows: BOOL CMyChildFrame::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext) { BOOL bRetval = CMDIChildWnd::LoadFrame(nIDResource, dwDefaultStyle, pParentWnd, pContext); theWorkspaceManager.AddWindow(this); return bRetval; } BOOL CMyChildFrame::DestroyWindow() { theWorkspaceManager.RemoveWindow(this); return CMDIChildWnd::DestroyWindow(); } 5. Include the secres.h and secres.rc files in your application’s resource file. To do this, choose Resource Includes… from the Visual Studio View menu and add secres.h to the middle edit box and secres.rc to the bottom edit box. By doing this, you ensure that all resource IDs are defined for the workspace menu created in the next step. 6. Add the Workspace menu to your application’s menu. You can make the workspace menu a standalone menu or a popup from the File menu if you prefer. Regardless of where you choose to place the workspace menu, use the workspace resource IDs defined in secres.h. They are: ID_WORKSPACE_NEW ID_WORKSPACE_SAVE ID_WORKSPACE_SAVEAS ID_WORKSPACE_OPEN ID_WORKSPACE_DELETE ID_WORKSPACE_CLOSE ID_WORKSPACE_1 through ID_WORKSPACE_8 15.11.4Workspace Manager Samples The Objective Toolkit WrkSpcEx sample (<stingrayinstalldir>Samples\Toolkit\MFC\Docking\WrkspMgr\WrkSpcEx) illustrates the use of the SECWorkspaceManagerEx class and its supporting menu and dialogs. Also refer to the sample SdiMgr for an example of the workspace manager used programmatically (no dialogs or command handlers) for persistent state in a SDI application. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 15 User Interface Extensions 281 15.12Full-Screen View SECFullScreenView is a utility component that allows you to integrate full-screen display into your MDI/SDI based applications. SECFullScreenView is designed to be used as a stand-alone component. It hooks into your application and manages the full-screen activation without any need for subclassing your existing frame window classes. This plug-in design makes it simple to add the full-screen capability to existing applications. All you need to do is add an SECFullScreenView member to your main frame class and invoke the SECFullScreenView::SetFSMode() API to trigger the full-screen display. Once this mode is set, SECFullScreenView steps into your application’s activation mechanism and manages it until the user exits full-screen view mode, by pressing the ESCAPE key or clicking the full-screen toolbar. The full-screen toolbar is a dynamically created toolbar that is instantiated as either an SECCustomToolBar or CToolBar/SECToolBar, depending on the run-time class of the frame. The SECCustomToolBar component provides the same cool-look full-screen user interface that is found in Microsoft VC++ and Microsoft Office. Both the standard bitmap toolbar and text toolbar are supported. The default toolbar offers certain pre-set styles. However, you can change the default styles with the help of an application-defined callback. Figure 131 – Default Full-screen Toolbar Another feature of SECFullScreenView is the drop-down menu style that allows the temporary display of the application’s main window menu when the cursor is placed over the top portion of the screen like the Windows Taskbar. The default behavior is to hide all docking windows (control bars) except in the case of docking views when in the application is in full-screen mode. However, SECFullScreenView allows you to selectively include control bars you want to display in fullscreen mode. 15.12.1The Full-Screen View Class The SECFullScreenView class simply derives from CWnd. Figure 132 – Objective Toolkit Full-Screen View Class Hierarchy CWnd SECFullScreenView 282 15.12.2SECFullScreenView Styles The SECFullScreenView has a number of styles that control its behavior. These styles are used with the SetFSMode() method. Style flag Description SEC_FS_DROPDOWNMENU The application’s main window menu is displayed when the cursor hovers over the top section of the screen. Moving the cursor hides the menu. SEC_FS_TEXTTOOLBAR The full-screen toolbar is a text-only toolbar. The label can be set while invoking the full-screen mode. SEC_FS_TASKBARHIDE Forcibly hides the Windows task bar when full-screen mode is triggered. Exiting full-screen mode retrieves the task bar. Default is false. SEC_FS_NOCUSTTOOLBAR SECFullScreenView defaults to using SECCustomToolBar when the Objective Toolkit frame window classes are used. Setting this style creates the toolbar as an SECToolBar. 15.12.3Using the SECFullScreenView Class The following topics describe how to utilize the full-screen view in your applications. 15.12.3.1To incorporate the SECFullScreenView class into your application Add an SECFullScreenView member to your CFrameWnd-derived main frame class. From a command handler, call the SetFSMode() function with the required parameters. For example: // m_FSView is the SECFullScreenView object m_FSView.SetFSMode(); // The following call triggers the full-screen view with // drop-down // menus enabled and a text-only toolbar. The second // parameter // specifies the toolbar text. m_FSView.SetFSMode(SEC_FS_TEXTTOOLBAR|SEC_FS_DROPDOWNMENU, "Close Full Screen"); // A bitmap toolbar with the IDR_FSBITMAP resource is set m_FSView.SetFSMode( SEC_FS_DEFAULT, IDR_FSBITMAP ); Chapter 15 User Interface Extensions 283 15.12.3.2To set or retrieve the full-screen view mode styles 1. Use the SECFullScreenView::SetFSStyle() and GetFSStyle() methods. 2. SECFullScreenView::GetFSMode() returns a BOOL that specifies whether the full-screen mode is currently set. 15.12.3.3To terminate full-screen view mode Use the CloseFSMode() method to terminate the full-screen display. 15.12.3.4To use full-screen view mode with docking windows Use the SECFSBarState structure and the SetBarStateArray() method to specify the control bars to be displayed during full-screen view mode. The following excerpt from the SCREENVIEW sample demonstrates the usage: // Setting the second member of SECFSBarState to TRUE indicates // the control bar is visible only during the full-screen mode. // The ‘full screen only’ bars will be displayed when the FS mode // is set and will automatically be hidden when FS mode exits. SECFSBarState barState[2] = { { &m_wndCloudToolBar, FALSE }, { &m_wndPalette, TRUE } }; m_FSView.SetBarStateArray(barState, 2); m_FSView.SetFSMode(SEC_FS_DROPDOWNMENU); SECFullScreenView only performs show/hide operations on the respective control bars. It is your responsibility to ensure that the control bars have been created and that they remain in scope throughout the time the application is in full-screen view mode. 15.12.3.5To change the default full-screen view toolbar styles The full-screen view toolbar class is instantiated dynamically based on the application’s frame classes. So changing the default styles involves defining a callback function within your application and specifying this function pointer to the SECFullScreenView object. When the callback is invoked, you can examine the existing styles and change them as required. Declare the callback. // Callback definition within your main frame class static void CALLBACK SetToolBarStyles(UINT& dwStyle, UINT& dwStyleEx); Define the callback function. // Callback implementation. // This example creates the toolbar without the “cool-look” // double gripper and with a visible border 284 void CALLBACK CMainFrame::SetToolBarStyles(UINT& dwStyle, UINT& dwStyleEx) { dwStyle |= CBRS_BORDER_ANY; dwStyleEx &= ~CBRS_EX_GRIPPER; } Before invoking the full-screen view mode, call the SetFSToolBarStylesCB() method, providing the address of the callback function. // Specifying the callback to SECFullScreenView m_FSView.SetFSToolBarStylesCB(SetToolBarStyles); m_FSView.SetFSMode(SEC_FS_DROPDOWNMENU, "Close Full Screen"); 15.12.4SECFullScreenView Sample The Objective Toolkit sample screenview demonstrates usage of this class. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 15 User Interface Extensions 285 286 Chapter 16 Utility Classes 16.1 Overview The classes in this section solve non-user interface problems. 16.2 Compressed File I/O The compressed file class provides compression and decompression services for data written to and from a file. The class supports two modes: a compression mode that supports a subset of access methods and a regular mode that supports ordinary CFile method calls. Because the compression file class compresses its data as a whole, you cannot see the middle of a compressed data block to perform partial decompression. However, by switching back and forth between compressed mode and normal mode, you can write compressed data to a file in a different location. In addition, you can place a jump table at the beginning of the file to store seek locations of the different compressed blocks. 16.2.1 SECCompressFile SECCompressFile is a CFile derivative that provides compression and decompression services for data written to and read from a file. SECCompressFile has no logic to determine where compressed blocks begin and end. You need to treat an entire file as a single compressed block or be able to seek to the beginning block of compressed data and read in the correct number of compressed bytes. Figure 133 – Objective Toolkit’s Compressed File Class Hierarchy CFile SECCompressedFile Chapter 16 Utility Classes 287 SECCompressFile provides a SetCompressedMode() method to treat the file as a compressed file type or as a normal CFile type. The user can treat the entire file as a single compressed block of data or jump to known locations and decompress a smaller portion. 16.2.2 Using SECCompressFile The following sections describe how you can write and read compressed data. To write to a file in compressed file format: 1. Create an instance of SECCompressFile. 2. Call the WriteHuge() method to write out data in a compressed file format. 3. Close SECCompressFile. To read a compressed file: 1. Create an instance of SECCompressFile. 2. Call the ReadHuge() method to read data from the compressed file format. 3. Close SECCompressFile. To compress CDocument data: Override the CDocument::GetFile() method as follows: CFile* CMyDoc::GetFile(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError) { SECCompressFile* pFile = new SECCompressFile(lpszFileName, nOpenFlags); return pFile; } 16.2.3 SECCompressFile Sample See the Objective Toolkit CFiles sample in the Samples\Toolkit\MFC\Utility\CFiles directory for an example of how to use SECCompressFile. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 288 16.3 Encrypted File I/O Objective Toolkit provides a class to support encryption and decryption services for data written to and from a file. Objective Toolkit provides the following modes: Electronic Codebook (ECB) Output Feedback Mode (OFB)— also known as Cipher Feedback (CFB). The following sections describe the features and limitations of this encryption capability. 16.3.1 Electronic Codebook (ECB) Electronic Codebook is moderately secure and allows random access reads and writes. The application of sophisticated cryptographic methods allows easier recovery of the contents of each of two or more files encrypted with the same password. Encourage your users to change their passwords frequently or implement another layer of key management. 16.3.2 Output Feedback Mode (OFB) Output Feedback is more secure than ECB, but file access is unidirectional and sequential only. Therefore, an SECCryptoFile secured with OFB may be opened in either CFile::modeRead or CFile::modeWrite modes, but not both. Seek functions are not available in Output Feedback mode. 16.3.3 The SECCryptoFile Classes Objective Toolkit provides file encryption services through the SECCryptoFile class and its helper SECBlackBox. Figure 134 – The Objective Toolkit Encryption Class Hierarchy CFile SECCryptoFile 16.3.3.1SECCryptoFile SECCryptoFile, a CFile derivative, provides encryption and decryption for data written to and read from a file. You need to assign a password to your user and he, in turn, must choose an encryption method. The encryption algorithms are described in detail in the following sections. This class provides only medium-grade encryption. In other words, it is only intended to stop novice to intermediate hackers from accessing your data. If you require higher levels of security, patent and export laws come into play. Chapter 16 Utility Classes 289 16.3.3.2SECBlackBox The SECBlackBox class is a helper class that encapsulates the encryption algorithm itself. 16.3.4 The Encryption Algorithm The algorithm involves two or three phases: Password transformation, Stream cipher, and Output Feedback (OFB mode only). First, SECBlackBox transforms or hashes the password you selected into an array of 72 seemingly random characters that bear no resemblance to the original password. Then SECBlackBox uses this 72-byte passphrase to generate three keys – 23, 24, and 25 bytes in length. During read or write operations, the SECBlackBox class subjects the data stream to what is known as a triple-key Vigenere cipher. The Vigenere cipher is called a symmetrical cipher, because you can use the same algorithm and key to encrypt and to decrypt. 16.3.4.1Password Transformation The password provided by the user is subjected to a hashing function to transform it into a 72-byte apparently scrambled character string, regardless of its initial size. The hashing function is as follows: void SECBlackBox::HashString( char *from, int fromSize, char *to, int toSize ) { unsigned char p=0; for (int i = 0;i < toSize;i++) { p += ( 17 * ( from[i%fromSize] + i ) ); to[i] = (from[i%fromSize] * p ) + from[(i+1)%fromSize]; } } In the preceding function, the string in from, fromSize characters long, is hashed into a buffer at to, toSize bytes long. 16.3.4.2The Stream Cipher The SECBlackBox uses a triple-ranked Vigenere cipher. In this implementation, the circular buffer or mask consists of three separate submasks that are combined to produce a final mask value with which the plain text is enciphered. In other words, given buffers B1, B2, and B3, with lengths l1, l2, and l3, respectively, the mask value M[i] for the ith character of the message would be: M[i] = B1[i%l1]^B2[i%l2]^B3[i%l3]; 290 The three buffers are filled as follows: the 72-byte hashed key is split into three subkeys – 23, 24, and 25 bytes long. Each key is loaded into a circular buffer, an instance of the class CCryptoRotor. Each rotor is then set with position zero selected. To encrypt (or decrypt at the lowest, or Vigenere level, this is a symmetrical cipher) a character, the mask character for each byte of the plaintext is generated as above. The mask character used to encrypt/decrypt the next character of plaintext, and then each rotor is advanced one position (the pointer is incremented and wrapped, if necessary). The rotor sizes are relatively prime, so the three wheels do not align the same way (and produce the same mask key) until 23 x 24 x 25 encryptions or decryptions have been performed. So, the effective key length is 13,800 bytes. 16.3.4.3The Cipher Modes The stream cipher operates in either electronic codebook mode (ECB) or output feedback mode (OFB). Output feedback mode is also known as cipher feedback mode (CFB). ECB mode is the simplest and each character is treated independently. It is potentially less secure, but more tolerant of media errors in the file. You can use ECB to access data randomly. After the first XORing stage, the ciphertext byte is output, and the next plaintext byte is read. Because no information is carried from one byte to the next, any (1 byte) error in the stored file results in only one character being decrypted incorrectly. Figure 135 – Electronic Codebook (ECB) Ciphering Topology OFB adds an additional XOR to the three-step key XORing. The last character encrypted becomes part of the key for the next character. This has the effect of making the keystream at any point dependent on the message that preceded it. OFB is more secure than ECB, but if any errors are introduced into the encrypted file by an attacker or media failure, the remainder of the file becomes corrupted. This technique makes the cipher harder to crack, but as soon as a byte is read in incorrectly, the remainder of the message becomes indecipherable. Additionally, the sequential nature of the cipher limits the use to sequential access to a file. To this end, the seek-related functions are disabled in OFB mode, and they fire an ASSERT when used. Chapter 16 Utility Classes 291 Figure 136 – Cipher Feedback (OFB) Ciphering Topology 16.3.5 Limitations Most stream ciphers, particularly the Vigenere cipher, are technically less secure. The triple-ranked Vigenere cipher in ECB mode is harder to break than a short single-rank Vigenere. The tripleranked Vigenere in OFB mode is more secure than an ECB mode cipher. Be aware, though, that this algorithm does not provide a great deal of security against governments and well-funded or highly skilled cryptographers. Decryption of data files is easier for the sophisticated attacker if he or she is given two or more different files encrypted with the same key or password. However, this advantage is diminished greatly if the files are encrypted using OFB mode. This weakness might become apparent to a sophisticated attacker, but it will probably elude the average hacker. 16.3.6 Using SECCryptoFile An SECCryptoFile is used almost the same way as a CFile is used, with a few exceptions. For example, you can open an SECCryptoFile without a password. You can open an SECCryptoFile in either ECB or OFB modes. This is specified in the eModeCipherMode argument. If the SECCryptoFile is opened in OFB mode, it cannot be opened for random access or for Read and Write capability at the same time. An ASSERT fires if this mistake is made. To open a file called SECRET.DAT in read mode with cipher feedback and with password ABRACADABRA, you would create your CFile as shown in the following examples. 16.3.6.1To encrypt data to a file 1. Instantiate an SECCryptoFile object. For example: SECCryptoFile fEncrypted; 2. Open the file for writing using the Open() method, supplying a password and encryption mode. For example: 292 fEncrypted.Open(“secret.dat”, “abracadabra”, Cfile::modeRead, SECCryptoFile::OFB); 3. Call the Write() or WriteHuge() methods to write data to the file. FEncrypted.WriteHuge(pBuffer, nCount); CFile::modeReadWrite and the Seek methods are only available in ECB mode. 16.3.6.2To read an encrypted file 1. Instantiate an SECCryptoFile object. For example: SECCryptoFile fEncrypted; 2. Open the file for writing using the Open() method, supplying a password and encryption mode. fEncrypted.Open(“secret.dat”, “abracadabra”, Cfile::modeWrite, SECCryptoFile::OFB); 3. Call the Read() or ReadHuge() methods to read data from the file. NBytesRead = FEncrypted.ReadHuge(pBuffer, nCount); CFile::modeReadWrite and the Seek methods are only available in ECB mode. 16.3.7 SECCryptoFile Sample See the Objective Toolkit CFiles sample (Samples\Toolkit\MFC\Utility\CFiles) for an example of how to use SECCryptoFile. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 16 Utility Classes 293 16.4 Safe Multi-Threaded Trace Output Objective Toolkit now includes support for safe, synchronized logging of trace-type output from within multi-threaded code. Using objects derived and instantiated from the base template class MultiTrace, developers of multithreaded applications can implement synchronized output from multiple threads to a common text file. Features of this class include: Optional synchronization. The base MultiTrace class is a template that takes as one of its parameters a trait or behavior class into which the synchronization behavior has been factored. Implementations of this trait have been provided for both single and multi-threaded behaviors; so, for example, if you instantiate from base class MultiTrace using trait class SingleThreadLogTraits as a parameter, synchronization is disabled. However, if you instantiate using trait class MultiThreadLogTraits as a parameter, synchronization is enabled. In the header file, we have provided simple typedefs for each of these choices. If some other type of behavior is desired, the user has the option of deriving and using a custom trait class. Printf-type syntax. Client code invokes methods on the multithreaded logging object using familiar syntax (i.e. format string followed by a variable list). Optional and configurable tags for log output. Users have the option of specifying tags that appear before and after each piece of log output. A default tag style is provided that includes thread ID and the time (hour:minute:second:millisecond) of the output. Optional file size threshold. A size (character count) can be specified for the logging object, which represents a count after which file output restarts back at the top. This will be convenient for applications that run unattended, as it will prevent the output file from growing indefinitely. Optional base64 conversion of binary data. Binary data may be base64 converted into printable text before being written to the output stream. Optional exception handling. By default, exceptions arising from trace log operations are NOT thrown back to client code. This will probably be the preferred mode for production applications. However, exception handling can be turned on so that exceptions within the logging code are thrown back to the client. 16.4.1 Use of the Multi-Threaded Logging Class Using the logging class is straightforward. 1. Include these two files: MultiTrace.h MultiTraceException.h 2. Declare an instance of the MultiTrace class: multithreadedlogger mt; This should have file scope. Note that this declaration uses one of the typedefs from MultiTrace.h, which is synonymous with: 294 MultiTrace<MultiThreadLogTraits> mt; As mentioned above, a non-synchronizing, single-threaded version is provided also, typedefed as singlethreadedlogger. 3. Set the desired properties of the logger, and call initialize(): mt.fname=_T(“c:\logfile.txt”); mt.initialize(); 4. In your thread procedure where you desire to log output, invoke an output method: mt.LogWrite(_T(“At this point, the value of x is %i”),x); By default, this output will be bracketed in the output file by HTML-like tags, which incorporate the thread ID and the time (to the millisecond): <25c 15:24:10.0024> At this point, the value of x is 4 </25C> The MultiTrace object will perform the file I/O operations in a critical section so that I/O from multiple threads in your application will be properly sequenced in the output stream. Binary data can be logged as well (converted to text via base64 conversion) through method LogBinary(). Arguments for this method are a pointer to the item and its length. For example, assume oStructure is a data structure in memory. The following code will convert the contents of the structure via base64 conversion and output them to the log file: Mt.LogBinary((void*)&oStructure,sizeof(oStructure)); This will produce output resembling the following: <16c 15:24:09.0703>UAAZAAAAAAAHAAAAAABPABgAUAAZAA==</16c> When your MultiTrace object falls out of scope, its destructor will be called, which in turn will close the output file and free up any buffers allocated for the purpose of base64 conversion of binary data. See the MultiThreadLog sample console application (sample\toolkit\MFC\utility) included with the product for an example of the setup and use of the multithreaded logging class. This class has no dependencies on MFC. Chapter 16 Utility Classes 295 16.5 File System Access The Objective Toolkit file system class provides access to common file system functions, such as reading a directory or copying a file. The class also encapsulates parsing and CStringList methods. 16.5.1 SECFileSystem SECFileSystem is a class that allows you to access common file system functions such as retrieving general information about files and directories, reading directories, copying files, and more. Figure 137 – The Objective Toolkit File System Class Hierarchy CObject SECFileSystem 16.5.2 Using SECFileSystem SECFileSystem can help create code for the following tasks. Please see the Objective Toolkit Class Reference or online help for a full list of methods for this class. 16.5.2.1To copy a file Call the CopyFile() method. For example: SECFileSystem fs; BOOL bRetVal = fs.CopyFile("foo.txt", "a:\\bar.txt"); 16.5.2.2To copy multiple files Call the CopyFiles() method. For example: SECFileSystem fs; BOOL bRetVal = fs.CopyFiles("c:\\foo\\bar\\*.txt", "c:\\foo2"); 16.5.2.3To compare two files Call the CompareFiles() method. For example: SECFileSystem fs; BOOL bRetVal = fs.CompareFiles("foo.txt", "bar.txt", 20480); 296 16.5.2.4To delete a file Call the DeleteFile() method. For example: SECFileSystem fs; BOOL bRetVal = fs.DeleteFile("c:\\foo.txt"); 16.5.2.5To delete multiple files Call the DeleteFiles() method. For example: SECFileSystem fs; BOOL bRetVal = fs.DeleteFiles("c:\\foo\\bar\\*.txt"); 16.5.2.6To get information about a file Call one of the following methods: GetFileAttribute() GetFileStatus() GetFileModifyTime() GetFileAccessTime() GetFileSize() For example: SECFileSystem fs; BOOL bRetVal = fs.GetFileAttribute("c:\\test.txt", bAttr); 16.5.2.7To parse filename information on a file Call one of the following methods: GetFullPathName() GetBaseFileName() GetExtension() GetFileName() GetFileSystem() GetPath() For example: SECFileSystem fs; CString strPath; strPath = fs. GetFullPathName("c:\\test\\.\\..\\foo\\bar\\what.txt"); ASSERT(Path == "c:\\foo\\bar\\what.txt"); Chapter 16 Utility Classes 297 16.5.2.8To get a list of files in a directory Call the GetDirectory() method. For example: SECFileSystem fs; CStringList *pDir = fs.GetDirectory("*.txt", SECFileSystem::normal, TRUE); fs.GetDirectory("*.doc", normal, TRUE, pDir); 16.5.2.9To create a directory Call the MakeDirectory() method. For example: BOOL bRetVal = fs.MakeDirectory("c:\\foo\\bar"); 16.5.2.10To change the working directory Call the ChangeDirectory() method. For example: BOOL bRetVal = fs.ChangeDirectory("c:\\foo\\bar"); 16.5.3 SECFileSystem Sample The Objective Toolkit filedemo sample (Samples\Toolkit\MFC\Utility\filedemo) demonstrates the use of the SECFileSystem class. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. 298 16.6 Random Number Generation SECRandom is a utility class for generating random numbers. An instance of SECRandom is initialized with an integer seed value from which random numbers are derived. If a seed of zero (0) is provided, a seed based on the current system time is used. SECRandom can provide a random value within a range by specifying an upper and lower bound for the range. SECRandom can also generate a skewed random result based on an array of weights specified by the user. Each entry of the array of weights specifies an unsigned integer value (the weight) that specifies the probability that a random number will fall in that position. A higher number increase the probability that a random number will be generated for that position. For example, consider a weighted array of 4 weights with the following values. Position Weight 0 1 1 7 2 0 3 2 The sum of the weights is 10. So there is a 10 percent chance that a random number of 0 will be generated, a 70 percent chance for a random number of 1, no chance for a number of 2, and a 20 percent chance that a value of 3 will be generated. These weights can be obtained using the GetRandomWeighted() method. 16.6.1 The SECRandom Class The SECRandom class provides a method for generating a random number based on a seed value. To skew the results of the random number generation, SECRandom also supports weighted vectors. Figure 138 – The Objective Toolkit Random Number Generator Class Hierarchy CObject SECRandom SECRandom acts as a front end to the srand() run-time function. With SECRandom, you can set the effective range for the random integer you want to generate. You can also establish a weighted vector array to specify the distribution of the generated values. Chapter 16 Utility Classes 299 16.6.2 Using SECRandom The following sections show how to generate random numbers from uniform and weighted distributions. 16.6.2.1To generate an unsigned random number (0 to 32767) Create an instance of SECRandom. If you want, you can also set a seed value. Call GetRandom() to return an unsigned integer (0 to 32767). For example: SECRandom rnd(m_nSeed); UINT result = rnd.GetRandom(); If no seed value or a seed value of zero (0) is entered (default), SECRandom generates a seed based on the current time. This is useful if you don’t want to duplicate the sequence of random numbers for different trials. 16.6.2.2To set the range of the random numbers generated Call the SetUBound() and SetLBound() methods. The upper bound must always be greater than the current lower bound and the lower bound must be less than the current upper bound. An example of initializing an instance of SECRandom and setting the desired result range from 100 to 500 is shown below: void GenerateRandom() { // Initialize with seed value of 50 SECRandom *pRandom = new SECRandom(50); // Set a range of 100 to 500. pRandom->SetLBound(100); pRandom->SetUBound(500); // retrieve a random value in the desired range UINT result = pRandom->GetRange(); delete pRandom; } You can use an overload of GetRange() to set the range and return a value in one step. 16.6.2.3To generate weighted random values You can assign a weighted set of vectors to an SECRandom instance to change the distribution of the random values. For example, in a given range of 0 to 9, you might want to place a 70 percent chance of generating a result of 5, a 20 percent chance of generating a result of 2, and a 10 percent chance of generating a 1. 1. Instantiate an object of the SECRandom class. For example: SECRandom rnd(nSeed); 300 2. Initialize a weighted vector array in SECRandom with the InitWeights() method. // Initialize 10 entries in the weighted vector list pRandom->InitWeights(10); 3. Assign the desired percentages with the AddWeight() method. // Assign the weights for the probabilities: // 10% to generate a 1 // 20% to generate a 2 // 70% to generate a 5. pRandom->AddWeight(1,10); pRandom->AddWeight(2, 20); pRandom->AddWeight(5, 70); 4. To retrieve a weighted vector result, call the GetRandomWeighted() method. // Retrieve the result pRandom->GetRandomWeighted(); 16.6.2.4Key SECRandom Methods Here is an overview of some of the key SECRandom methods: SetBounds(). Sets the range of the random number to be generated. AddWeight(). Adds an entry to the weighted vector array. InitWeights(). Initializes the weighted vector array. (Specifies the number of weights to use.) GetRandom(). Returns a random number between 0 and 32767, inclusive. GetRandomWeighted(). Returns a weighted random number based on the current weight vector. GetRange(). Returns a random number within the current range. GetSeed(). Retrieves the current seed value from which random numbers are generated. 16.6.3 SECRandom Sample The Objective Toolkit randemo sample (Samples\Toolkit\MFC\Utility\randemo) illustrates how to use SECRandom. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 16 Utility Classes 301 16.7 Formula Engine Objective Toolkit now includes a Lex (Flex)-based formula evaluation engine. By instantiating objects of the toolkit-defined class SRFormulaScanner, developers can easily add formula evaluation functionality to their applications. 16.7.1 Features of the Formula Scanner The formula scanner: Supports decimal and hexadecimal representations of numeric input. Supports all common arithmetic, trigonometric, and bitwise logical functions. Supports common string operations (e.g., LEFT, MID). Tests for formula validity and error explanations. Is usable in Unicode and single-byte character mode applications. Parsing is driven by a Flex-generated, finite state, machine type, regular expression parser. 16.7.2 Use of the Formula Engine Class Use of the formula engine class is straightforward. 1. In any source file in which you wish to incorporate formula evaluation functionality, include the following toolkit-supplied header files, as follows: #include <SRFormulaScanner.h> #include <errorcodes.h> 2. Declare an instance of the formula engine class, as follows: SRFormulaScanner scan; 3. Set the formula, tell the formula engine to evaluate it, and extract the answer (or an error message), as follows: scan.lex(_T("SUM(3,4,(6 * (4+SQRT(9))))")); if (scan.IsValid()) { _TCHAR *cb = NULL; scan.GetResult(&cb); //cb now points to the answer, formatted as a string if(cb) free(cb); //since the answer string is dynamically allocated //within the formula engine, you must free it to avoid a //memory leak } 302 else { _TCHAR * errdesc = NULL; scan.GetErrDescription(&errdesc); //errdesc points to an error message string. //this string is not dynamically allocated, and //therefore does not have to be freed. } See the sample included with the product for an example of how the formula engine can be used. Chapter 16 Utility Classes 303 16.8 Win32 Registry Access Objective Toolkit provides an encapsulation of the Win32 registry APIs. Using Objective Toolkit’s registry abstraction, you can modify the registry through a common interface consistent under all supported platforms. SECRegistry encapsulates the APIs used to access and modify the Windows registry in a single class. The bulk of the SECRegistry methods are wrappers for the Win32 API. One advantage of using the class instead of the Win32 API is that you can recursively delete keys in a 32-bit environment. 16.8.1 The SECRegistry Class The SECRegistry class encapsulates the Win32 registry API. An SECRegistry instance contains a member handle to a registry key. This same key is then used as a parameter to some of the Windows registry APIs (SECRegistry shields the user from having to keep track of the handle). Figure 139 – Objective Toolkit’s Registry Class Hierarchy CObject SECRegistry 16.8.2 Using SECRegistry The following terms are used in the following descriptions: Keys. Nodes that can contain one or more keys or values. A key is like a folder that can contain other folders (keys) or items (values). Values. An item that is contained by a key that has two attributes: a name and an associated evaluation. For example: Name Value “Path” “C:\Program Files\WordView\WordView.exe” “Password” “GX325PF” Version 5.1 The following section show you how to use SECRegistry to modify registry settings. 304 16.8.2.1To open the registry Call the Connect() method. If a computer name is specified, the application attempts to open the registry remotely. There must be sufficient permissions for remote registry connections to work. For example: // connect to the local registry m_registry.Connect(HKEY_CURRENT_USER); // connect to a remote registry m_registry.Connect(HKEY_LOCAL_MACHINE, _T(“\\\\TARZAN”)); 16.8.2.2To open a subkey 1. Connect() to the registry. m_registry.Connect(HKEY_CURRENT_USER); 2. Open the subkey with the Open() method. m_registry.Open(_T(“Software”)); 16.8.2.3To enumerate registry keys 1. Connect() to the registry. m_registry.Connect(HKEY_CURRENT_USER); 2. You can also open a subkey. m_registry.Open(_T(“Environment”)); 3. Call the EnumerateKeys() method. // Enumerate all the subkeys // and add them to the listbox DWORD dwCount = 0; CString strKey; CString strClass; while (m_registry.EnumerateKeys(dwCount++, strKey, strClass)) pListBox->InsertString(-1, (LPCTSTR)strKey); 16.8.2.4To enumerate values within a key 1. Connect() to the registry. m_registry.Connect(HKEY_CURRENT_USER); 2. You can also open a subkey. m_registry.Open(_T(“Environment”)); Chapter 16 Utility Classes 305 3. Call the EnumerateValues() method. DWORD dwCount = 0; Cstring strName; SECRegistry::KeyValueTypes typeCode; while (m_registry.EnumerateValues(dwCount++, strName, typeCode)) pListBox->InsertString(-1, (LPCTSTR)strName); 16.8.2.5To read a value 1. Connect() to the registry. m_registry.Connect(HKEY_CURRENT_USER); 2. You can also open a subkey. m_registry.Open(_T(“Environment”)); 3. Call one of the following methods to retrieve the value. These methods assume you know the type of the value. See Section 16.8.2.4. GetBinaryValue() GetDoubleWordValue() GetStringArrayValue() GetStringValue() 4. You can also call one of the overloaded GetValue() methods or the QueryValue() method. 16.8.2.6To create a key 1. Connect() to the registry. m_registry.Connect(HKEY_CURRENT_USER); 2. You can also open a subkey. m_registry.Open(_T(“Software”)); 3. Call the Create() method to create the new key. m_registry.Create(_T(“MyKey”)); 16.8.2.7To delete a key 1. Connect() to the registry and open the parent of the key about to be deleted. m_registry.Connect(HKEY_CURRENT_USER); m_registry.Open(_T(“Software”)); 2. Call the DeleteKey() method to delete a particular subkey. m_registry.DeleteKey(_T(“MyKey”)); 306 DeleteKey() fails if the key being deleted contains subkeys. To delete subkeys, you need to specify a recursive delete operation. See Section 16.8.2.8. 16.8.2.8To recursively delete keys 1. Connect() to the registry, and open the parent of the key about to be deleted. m_registry.Connect(HKEY_CURRENT_USER); m_registry.Open(_T(“Software”)); 2. Call the DeleteKey() method to delete a particular subkey and specify a recursive delete. m_registry.DeleteKey(_T(“MyKey”), TRUE); // TRUE = recursive delete Be careful when recursively deleting registry keys! 16.8.2.9To close a registry connection Call the Close() method. m_registry.Close(); If this method is not called explicitly, it is called implicitly by the SECRegistry destructor. 16.8.3 SECRegistry Sample The Objective Toolkit regdemo sample (Samples\Toolkit\MFC\Utility\regdemo) illustrates how to use SECRegistry in a 32-bit environment. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. Chapter 16 Utility Classes 307 308 Chapter 17 Data Extraction Classes 17.1 Overview 17.1.1 Building the Libraries Developers can use Objective Toolkit’s data extraction classes to extract data from text streams. These classes depend on an underlying third-party regular expression library, Regex++1. The Regex++ v.2.2.4 library is distributed and installed with Objective Toolkit (version 7.1 and later) and is also available from several FTP sites. The URL for the Regex++ Web site (as of the publication date of this manual) is http://www.boost.org/doc/libs/1_40_0/libs/regex/doc/html/index.html Read the regex.htm document (located at <stingray-installidr>\Regex) that is installed as part of the Regex++ library. 1Regex++ is distributed under the following copyright, which is reproduced here as required. Regex++ Copyright © 1998-9 Dr. John Maddock Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Dr. John Maddock makes no representations about the suitability of this software for any purpose. It is provided “as is” without express or implied warranty. To build the Objective Toolkit data extraction classes: 1. Run the Objective Toolkit Build Wizard. 2. Select HTML data extractor under the Utility Classes section in Objective Toolkit. Chapter 17 Data Extraction Classes 309 Figure 140 – Select HTML data extractor when running the Objective Toolkit Build Wizard. 3. Build the Objective Toolkit libraries with the new make files generated. Regex++ is used to facilitate the new HTML export functionality. The HTML data extractor component requires the Regex library, which makes powerful regular expression matching functionality available to projects incorporating Objective Toolkit. Objective Toolkit has an optional dependency on the Regex++ library, which in turn has other dependencies. As a result, additional files (other than the Objective Toolkit DLLs themselves) may need to be distributed with your application. The Toolkit build wizard now contains a checkbox to enable or disable the requirement for Regex. This checkbox is unchecked by default. Regex library solution files have also been updated to output the Regex libraries to the appropriate MFC product library directory by platform. For example, Win32 VC++ 8.0 Regex libraries are now output to <stingray-installdir>\Lib\vc8\x86. This output pattern is consistent for all supported compilers as well as x64 editions of the libraries. This means that the IDE path for Regex is no longer needed. Please refer to Section 2.7.3, “Check Visual Studio Paths,” in the Stingray Studio Getting Started Guide. for more detailed information about these IDE paths. Also, you can find information about redistributables in Appendix 6 of regex.htm file located at <stingray-installdir>\Regex. As described in regex.htm, it is also possible to link to a static version of Regex++ and avoid installation of additional libraries. 310 17.1.2 Using Regular Expression Libraries PERL and other such languages are commonly used to solve the frequently encountered programming problem of extracting and manipulating text stream data. We can also use tools such as lex and yacc to create custom scanners. C++ has several regular expression libraries that can provide similar functionality. It is possible to extract information from text streams quite easily using regular expression libraries. Consider the following text: <HTML>City of Raleigh, NC, temp at 7 P.M. <b>52 degrees</b> </HTML> Using a regular expression like the one shown below, it is easy to extract—as data elements—the city name, state, and the temperature at a certain time. <HTML>City of ([a-z]*), ([a-z]{1,2}), temp at ([0-9]{1,2}) (P.M|A.M). <b>([09]{1,3}) degrees</b> </HTML> Chapter 17 Data Extraction Classes 311 17.2 Data Extraction Framework Since the Web has become a huge virtual database of sorts, more and more often these days we find ourselves wanting to scrape data from Web sites (although this tactic may not be legal or allowed on some sites). However, most data commonly available on the Web is in HTML format, which cannot be easily processed by software systems that do not understand HTML presentation. Most systems that extract and process data from the Web (including shopping agents, personal news bots, etc.) fall into this category. When you extract only the required data from HTML streams, software systems designed to process that data can easily consume it — without regard to its formatting. With simple HTML streams (such as the one listed above) this is not a very difficult process. Any regular expression scanner will do nicely. With more complex files though, this quickly becomes quite difficult. The following issues are the biggest stumbling blocks. An entire HTML file cannot be scanned with a single expression. To get meaningful results, several expressions need to be written and need to work in tandem—with precise control over which expression gets evaluated when. In lex this would typically be done using states. HTML data available on the Web is volatile. One reason that a scanner built using lex would be difficult to maintain is that changes will break the scanner. Each time the HTML data changes, the scanner code will have to be regenerated using lex and binary updates will need to be made to the software system. Stingray developers considered these two issues carefully when designing the Objective Toolkit library. With reference to the first issue, the system that we have in place does not provide much of an advantage over lex (or other such systems). We offer the same functionality in terms of states. However, we believe that we have a more object oriented, easily extensible solution for the following reasons: The underlying library (Regex++ by Dr. John Maddock) is very well written and maintainable. The wrapper that we have around Regex++ is also very extensible. We support Unicode. We provide easy macros that can be used to add states. Listeners can be plugged into the data scanning process as required. The library takes advantage of other C++ niceties, such as exceptions. With reference to the issue of HTML data volatility, our approach has an important advantage. With lex, a binary generation is required each time there is a change in the Web information. With our approach this is not strictly needed. 312 17.3 Example: Setting Up a Scanner With this bit of background information under our belt, let us now look at how we setup a scanner. It would help to compile the Deals sample at this point. This sample does not ship with the product. If you need this sample, please submit a request to Stingray customer support. 17.3.1 Introduction to the Deals Sample The Deals sample retrieves the main page from http://www.thedailydeals.com (a Web site that has information on Web deals). It parses the information available at the site and populates a list control. Clicking on a link in the list control will take you directly to that link. This sample can automatically run every few minutes to gather this data, parse it, and populate the list control. 17.3.2 Declaring the Processor When using the data extraction framework, you will usually need to concern yourself with only two major classes—the data processor class (CRgProcessor), which contains and drives the data extraction process, and the listener class (CRgDefListener), which acts as a stub for any feedback generated from the scanning process. In the Deals sample, Class CDealProcessor (derived from CRgProcessor) is declared as shown below: class CDealProcessor : public CRgProcessor { public: enum states { estateNormal, estateDeals, }; enum tokens { etokenDealName, etokenExpired, etokenNotExpired, etokenRef, }; // required override // put all your initialization code here using the // provided macros for easy maintenance void Initialize(); }; Chapter 17 Data Extraction Classes 313 Typically, this is how we recommend that you declare your own processors. There should be an enum declaration for tokens that will be recognized by the scanner. There should be an enum declaration for states that the scanner will traverse. The steps for writing code to scan a text stream are: 1. Identify the tokens that you want to read from the stream. 2. Define an enum and add a value for each of these tokens. 3. Consider how you will extract the information, thinking ahead about the states and sub states to use to facilitate implementation. For example, if you are looking at stock information from www.Yahoo.com you may be looking at data much like the listing below. (A series of three dots indicates where code has been deleted for brevity.) <html> . . . <td nowrap>Div Date<br>N/A</td> <td nowrap rowspan=3 valign=bottom width="1%" bgcolor=white> <a href="/q?s=RWAV&d=1y"> <img border=0 width=192 height=96 src= "http://chart.yahoo.com/c/0b/r/rwav.gif" alt="Chart"></a><br> <small>Small: [<a href="/q?s=RWAV&d=1b">1d</a> | <a href="/q?s=RWAV&d=2b">5d</a> | <b>1y</b> | <a href="/q?s=RWAV&d=nc">none</a>]<br> Big: [<a href="/q?s=RWAV&d=1d">1d</a> | <a href="/q?s=RWAV&d=5d">5d</a> | <a href="/q?s=RWAV&d=3m">3m</a> | <a href="/q?s=RWAV&d=1y">1y</a> | <a href="/q?s=RWAV&d=2y">2y</a> | <a href="/q?s=RWAV&d=5y">5y</a>]</small></td></tr> <tr align=center valign=top> <td nowrap>Day's Range<br>5 <sup>3</sup>/<sub>4</sub> - 6 <sup>3</sup>/<sub>16</sub></td> <td nowrap>Bid<br>5 <sup>3</sup>/<sub>4</sub></td> <td nowrap>Ask<br>5 <sup>7</sup>/<sub>8</sub></td> <td nowrap>Open<br>6 <sup>1</sup>/<sub>8</sub></td> <td nowrap>Avg Vol<br>49,818</td> <td nowrap>Ex-Div<br>N/A</td></tr> <tr align=center valign=top><td nowrap>52-week Range <br>3 - 12 <sup>7</sup>/<sub>8</sub></td><td nowrap>Earn/Shr<br>-0.06</td> <td nowrap>P/E<br>N/A</td><td nowrap>Mkt Cap<br>63.3M</td> <td nowrap>Div/Shr<br>N/A</td> <td nowrap>Yield<br>N/A</td></tr></table> </td></tr><tr><td><small> . . . <tr valign=top> <td nowrap>Thu</td><td nowrap>Aug</td> 314 <td align=right nowrap>10</td><td>RWAV</td> <td><a href="http://biz.yahoo.com/e/000810/rwav.html"> ROGUE WAVE SOFTWARE INC /OR/ - Quarterly Report (SEC form 10-Q)</a> <i>Other</i></td> </tr> <tr valign=top> <td nowrap>Mon</td> <td nowrap>Aug</td> <td align=right nowrap> 7</td> <td>RWAV</td> <td><a href="http://biz.yahoo.com/prnews/000807/co_rogue_w.html"> Rogue Wave Software Embraces Japanese C++ and XML Components Market</a> - <i>PR Newswire</i></td> </tr> <tr valign=top> <td nowrap>Mon</td> <td nowrap>Jul</td> <td align=right nowrap>17</td> <td>RWAV</td> <td><a href="http://biz.yahoo.com/snp/000717/rwav_dtp_i.html"> ROGUE WAVE SOFTWARE INC., posts 3Q EPS $0.02 vs. $0.04</a> <i>Standard & Poor's</i></td> </tr> </table> <p> . . . </html> In the HTML above, we have highlighted (in bold) the portions of interest—the stock quote area and the news area. To scan these areas you need different regular expressions. This usually implies that you should have these two areas denoted by different states. Within each state, you may need to extract each needed token in a different manner. On the Yahoo! page, for example, you have to extract the title heading of the news link in a different manner than you will extract the article reference. Each of these would be sub states within the encompassing state. Sub states are not important when you define the token enum values; however, thinking about the sub states ahead of time makes implementing the scanner easier. 17.3.3 Implementation Details Now that you understand the rationale for the two enum definitions, consider the data that we scan when interacting with http://www.thedailydeals.com. If you make a request to http://www.thedailydeals.com and click View|Source (or the equivalent command in your browser) you will see the entire HTML stream that returns from the server. The sample makes an HTTP request for the default page using the Microsoft Wininet library. Chapter 17 Data Extraction Classes 315 17.3.3.1Token Definitions Based on the data available at the Web site, we determined the information to extract (shown in Table 42). This should lead us to the token definitions. Therefore, the following tokens correspond to the three pieces of information we want. Table 42 – Information and token definitions Information Required Token Definition The description of the deal. etokenDealName The link to it on the Daily Deals Web site. etokenRef Whether the deal has expired or not. etokenExpired To make the interpretation of the feedback data a little easier in the sample, we have a separate token for the expired state and the not expired state. We could have passed the token as it is and then looked at the string to see if it was expired or not expired. However, because we have two different tokens, the regular expression engine will do the work for us. This leads us to define the following four tokens: enum tokens { etokenDealName, etokenExpired, etokenNotExpired, etokenRef, }; 17.3.3.2States The next part is kind of heuristic in nature; each person may come up with a different solution. Looking at the chunk of data returned from the site, we came up with the following states: { estateNormal, estateDeals, }; estateNormal is the state that we start with. When we see the start of the deal listings we go into estateDeals. If we find another deal, we continue in estateDeals. If not, we exit the scanner. The next step is to decide how we go about shifting between the states and what data we collect in each state. The conclusions of this exercise are translated into code using macros provided in the body of the Initialize() function (in the processor). Each individual state is started with the macro rg_start_state(). This macro takes two parameters—the name of the state variable and the name of the state identifier token (one of the possible values of the state enum that we defined earlier). Inside the state declaration we can add sub states as desired. Each sub state is defined by an rg_sub_state()macro. This takes a token ID (from the enum that we defined earlier for this purpose) and the regular expression that extracts the data. 316 17.3.3.3Sub Expressions Be sure to place any data that you wish to collect inside parentheses so that a sub expression is defined. See Table 43 for an example. Table 43 – Defining a sub expression General sample form... Regular expression should be... My name is John. My name is (.*) Any token that is scanned will have the sub expressions available. These can be used to initialize our data easily. You can add any number of sub states within each state. Each of these sub states will be OR’d (combined) with the others to create a composite expression for that state. This implies that you should not try to gather data using separate regular expressions (defining two sub states) when there is overlap. Instead, combine these into the same expression. For example, consider: Table 44 – Using separate regular expressions For example... Don’t do this... Do this... My name is happyface; my e-mail address is [email protected]. My name is ([a-z]*); my e-mail address is [email protected] dmy e-mail address is (.*) My name is ([a-z]*); my e-mail address is (.*) Each sub state also takes a third parameter that specifies the action that should be taken when a scan is complete. This can be one of three possible values. CRgSubState::stateContinueImplies that no change in state will take place if that expression is matched. The next scan will also be with the same effective state. CRgSubState::stateNoNextImplies that as soon as this sub state is matched the scanning process will terminate. Custom Tokens—These can be any of the state tokens that have been defined earlier. After a match is made, processing will shift to the state that is denoted by this state identifier. End the state with rg_end_state(). You can have any number of states within the processor. The Deals sample implements the required Initialize() function as shown below: void CDealProcessor::Initialize() { rg_start_processor(this) rg_start_state(stateNormal, estateNormal) rg_sub_state(CRgSubState::tokenNone, _T("(class=\"show\">This Month</a>)"), estateDeals) rg_end_state() rg_start_state(stateDeals, estateDeals) rg_match(match_any) Chapter 17 Data Extraction Classes 317 rg_sub_state(etokenRef, _T("(href=\"(.*)\")"), CRgSubState::stateContinue) rg_sub_state(etokenDealName, ,_T("(class=\"newsquick\">([^<>]*)</a>)"), CRgSubState::stateContinue) // ignore the date rg_sub_state(CRgSubState::tokenNone, ,_T("(class=\"newsquick\">([0-9/]*)</span></td></tr> <tr bgcolor=\"#FFFFFF\")"), CRgSubState::stateContinue) rg_sub_state(etokenExpired, _T("(<font color=\"#CC0033\" size=\"1\"> Expired</font></td></tr>)"), CRgSubState::stateContinue) rg_sub_state(etokenNotExpired, _T("(</td></tr>)"), CRgSubState::stateContinue) rg_sub_state(CRgSubState::tokenNone, _T("(View Deals From </font><SELECT)"), CRgSubState::stateNoNext) rg_end_state() rg_end_processor(stateNormal) } We should mention two other macros used in the Deals sample: rg_start_processor() This macro, which denotes the start of the processor information, takes a pointer to the enclosing processor class. rg_end_processor() This macro, which terminates the processor code, provides state information. When processing starts, the state provided by rg_end_processor() will be used initially. 17.3.3.4Defining the Listener The next step is to define the listener. The listener class defines the feedback interface through which we get information from the processor. For the Deals sample the listener is declared in the dealscan.h header file. The declaration is shown below: struct CDealListener : public CRgDefListener { struct DEAL { DEAL(){ } DEAL(const DEAL& deal){ m_strDealName = deal.m_strDealName; m_bExpired = deal.m_bExpired; 318 m_strRef = deal.m_strRef; } bool operator=(const DEAL& deal){ return ( m_strDealName == deal.m_strDealName && m_bExpired == deal.m_bExpired && m_strRef == deal.m_strRef ); } CRgString m_strDealName; bool m_bExpired; CRgString m_strRef; }; typedef std::list<DEAL> CDealList; CDealListener(){ } virtual ~CDealListener(){ } void Cleanup(); // overrides virtual void OnMatch(RGTOKENID tokenid, CRgString::const_iterator citbegin, CRgString::const_iterator citend, DWORD dwOffset, const CRgMatch& actualMatch, CRgProcessor* pProcess); // operations // add any additional operations here void TraceData(); void ProcessData(); const CDealList& GetDealList() const{ return m_dealList; } protected: CDealList& GetDealListRef(){ return m_dealList; } CDealList m_dealList; DEAL m_currentDeal; }; 17.3.3.5Optional Overrides The only override that is directly tied to the scanner is the OnMatch() override. After the listener has been added to the scanner, the OnMatch() function will be called when a match takes place. Other optional overrides that you can use, if necessary, are listed below (with a brief description of each one): OnMatchStart() will be called when the matching process starts. OnMatchEnd() will be called when the matching process ends. OnChangeState() will be called when a state change happened as the result of a match. Override Cleanup() to add any code that clears internal data structures specific to your application. Chapter 17 Data Extraction Classes 319 Override Destroy()as appropriate to de-allocate memory allocated to the listener. The default implementation just performs a delete this. 17.3.3.6OnMatch() Implementation Let us now look at the OnMatch() implementation for the CDealListener class in a little more detail. It looks like this: rg_start_token(tokenid) rg_assign_string(CDealProcessor::etokenDealName, m_currentDeal.m_strDealName, 1); rg_assign_string(CDealProcessor::etokenRef, m_currentDeal.m_strRef, 1); rg_end_token() You start with the rg_start_token() and rg_end_token() macros. Use the rg_assign_string() macro to assign the scanned sub expressions to strings. This macro takes a token ID and a string that is to be used for the assignment. It also takes a number that specifies the 0 based index of the sub string that you want to assign. Pass 0 if you want the entire match to be assigned. You can also check the token ID directly and write any additional code as seen in the code shown below. // do any additional work if(tokenid == CDealProcessor::etokenExpired) { m_currentDeal.m_bExpired = true; this->GetDealListRef().push_back(m_currentDeal); } Additional code in the listener is application specific and simply declares and allocates structures to deal with the data that we collect. Figure 141 shows the Deals sample in action. 320 Figure 141 – Latest deals on the Web sample Chapter 17 Data Extraction Classes 321 17.4 Note on Exceptions Data extraction classes will throw exceptions when they encounter errors. Exceptions thrown by these classes are derived from class exception and hence can be used to extract more information in the standard manner. Please refer to the Objective Toolkit Class Reference for information on CRgSubStateException, CRgStateException and CRgProcessException. These exception classes provide rich information on the error conditions that may occur. 322 Chapter 18 View Classes 18.1 Overview The Objective Toolkit view classes enhance the MFC document/view architecture by providing new features in addition to printing, print preview, and automatic updating. 18.2 The Zoom and Pan View Classes SECZoomView and SECPanView are the classes that provide enhanced zooming and panning capabilities to CScrollView. In addition, SECPanWnd displays an overview showing what part of the complete view is currently visible. Figure 142 – Objective Toolkit View Class Hierarchies CScrollView SECZoomView CWnd SECPanWnd SECPanView Chapter 18 View Classes 323 18.3 SECZoomView The SECZoomView class supports automatic zooming of a view. SECZoomView supports two modes— zoom to fit and normal zoom. In zoom to fit mode, the view is automatically zoomed to fit the current size of the window. In zoom normal mode, the application indicates when to zoom by specifying either a zoom rectangle or a zoom percentage. 18.3.1 SECPanView SECPanView builds on SECZoomView and adds panning support. Panning enhances scrolled windows by letting the user grab the view and scroll it in any direction by moving the mouse. In addition to basic panning support, SECPanView also provides an overview window, SECPanWnd, that lets the user see and manipulate the entire view. 18.3.2 SECPanWnd SECPanWnd implements an overview window that shows the user a miniature overview of the complete view. The currently visible region of the view is outlined by a dotted rectangle. The user can move the dotted rectangle to change the currently visible area. If the user updates or scrolls the view, SECPanWnd is automatically updated to reflect the changes. 324 18.4 Zoom Modes The following flags, when set with SetZoomMode(), control how the view is displayed. Table 45 – Zoom mode flags Zoom mode flag Description SEC_ZOOMOFF No zooming. Use this if you want to turn off zooming. This is the default mode. SEC_ZOOMNORMAL Enables normal zoom mode. In this mode, specify either a zoom rectangle or a zoom percentage to control zooming. SEC_ZOOMFIT Enables zoom to fit mode. The view is automatically zoomed to fit inside the client area so that no scrabblers are needed. SECPanView supports two modes of operation that determine how the view is updated when the user moves the view with the mouse. The modes listed below can be selected with SetPanMode(). Table 46 – Pan Modes Pan mode flag Description SEC_PANDELAY The view is updated only after the user completes the move and releases the mouse button. SEC_PANINSTANT The view is updated instantly for every movement of the mouse. Chapter 18 View Classes 325 18.5 Using the View Classes Because the Objective Toolkit zooming and panning classes are based on the MFC document/view paradigm, you first need to make sure that your application is using a CScrollView. If not, we suggest you generate a new project using the VisualC++ | MFC AppWizard and use the generated code to add document/views to your application. Because SECPanView is derived from SECZoomView, it also supports zooming. With this hierarchy, use SECZoomView only if you want zooming and use SECPanView if you want zooming and panning. If you want panning only, use SECPanView with zooming turned off. SECZoomView and SECPanView work correctly only if the MM_ANISOTROPIC mapping mode is set. SECZoomView sets the mapping mode to MM_ANISOTRPIC in its override of OnPrepareDC(). Any attempt to change the GDI mapping mode in the application impacts the rendering. The following section describes how to add zooming support to your MFC applications. If you want to add both panning and zooming, see Section 18.5.2, “To incorporate panning support into an application.” 18.5.1 To incorporate zooming support into an application 1. Derive your view class from SECZoomView instead of CScrollView. For example: class CMyScrollView : public SECZoomView 2. Change the inheritance specified in the IMPLEMENT_DYNCREATE() and BEGIN_MESSAGE_MAP macros. 3. In your view constructor, call SetZoomMode() to set either SEC_ZOOMNORMAL or SEC_ZOOMFIT. 4. Add a zooming user interface to your application that calls the SECZoomView zooming functions, such as ZoomPercent(), ZoomIn() and ZoomOut(). 5. If you want to display a zoom value, override SECZoomView::ZoomLevelChanged(). SECZoomView requires the MM_ANISOTROPIC mapping mode. It sets this mode in its OnPrepareDC() method. If you set a different mapping mode, problems may occur. 18.5.2 To incorporate panning support into an application 1. Derive your view class from SECPanView instead of CScrollView. For example: class CMyScrollView : public SECPanView 2. Change the inheritance specified in the IMPLEMENT_DYNCREATE()andBEGIN_MESSAGE_MAP macros. 3. If you want to have both panning and zooming support, in your view constructor add a call to SetZoomMode() with either SEC_ZOOMNORMAL or SEC_ZOOMFIT. 326 If you only want to add panning support, do not call SetZoomMode(). The default zoom mode is SEC_ZOOMOFF. When this mode is set, SECPanView performs no zooming. You can call SetPanMode() to change the panning mode from the default (SEC_PANDELAY) to SEC_PANINSTANT. 4. Add the panning and zooming user interface. To enable panning, call StartPan() with the current starting point. To continue panning in pan instant mode, call ContinuePan() with the current panning point. When the user is done panning, call EndPan(). 18.5.3 To incorporate a panning overview window to an application 1. Call ShowOverviewWnd() with a rectangle specifying the size of the overview window. 2. Because SECPanWnd is not a CView derivative, call SECPanView::UpdateOverviewWnd() whenever you call CView::UpdateAllViews(). 3. Provide an SECPanView::OnUpdateOverview() routine that is similar to your CView::OnUpdate() override. The last argument to this function is a pointer to the overview window that you can use to call CWnd routines for drawing update logic. Like print preview, the panning overview window only works with one view. If you have several views for which you want to have overview windows, you need to create an SECPanWnd derivative and implement a custom solution for your specific situation. 18.5.4 Key Zooming Methods Here’s a quick reference to some of the key SECZoomView methods. For more information, refer to the Objective Toolkit Class Reference. Table 47 – Key Methods for SECZoomView Zooming method Description GetZoomLevel() Retrieves the current zoom level. GetZoomMode() Retrieves the current zoom mode. SetZoomLevel() Sets the current zoom level. SetZoomMode() Sets the zoom mode. ZoomFit() Prompts the view to zoom to the size of the client window. ZoomIn() Increases the zoom level. This method is overloaded to accept either a rectangle or a point parameter. The ZoomIn() overload that accepts a rectangle parameter zooms the view to fit in the rectangle. The overload that accepts a point uses the point as the center of the zoom-in operation. Chapter 18 View Classes 327 Table 47 – Key Methods for SECZoomView (Continued) Zooming method Description ZoomLevelChanged() Override this member to detect when the zoom level has changed. This is useful for keeping a displayed zoom level up-to-date. ZoomOriginal() Zooms the view to the original size (100%). ZoomOut() Decreases the zoom level. This method is overloaded to accept either a rectangle or a point parameter. ZoomOutOfRange() Specifies a floor and a ceiling to restrict the zoom level. The defaults for the floor and ceiling are the maximum allowed that does not let the zoom level wrap. ZoomPercent() Zooms the view by a percent instead of a floating-point zoom level—for example, 50 = 50%. 18.5.5 Key Panning Methods The following table provides descriptions of SECPanView’s key methods. Table 48 – Key Methods for SECPanView 328 Panning mode methods Description GetPanMode() Retrieves the pan mode. SetPanMode() Sets the pan mode. StartPan() Starts panning. Takes a point that is used to calculate the amount of panning. ContinuePan() Usually called while moving the mouse to cause the view to update in pan instant mode. Takes a point which is compared to the point passed to StartPan() in order to calculate an amount to pan. EndPan() Call EndPan() when panning is over. The view will pan if the pan mode is pan delay. If pan instant mode is active, the view will pan, but only the amount specified since the last call to ContinuePan(). IsPanning() You can call IsPanning() at any time to determine if the view is between a StartPan()/EndPan() sequence. For example, you could use this if you wanted your view’s OnDraw() to behave differently during panning. Table 49 – Key Methods for the Overview Window Overview window methods Description ShowOverviewWnd() Displays the overview window. HideOverviewWnd() Hides the overview window. UpdateOverviewWnd() Call to update the overview window. This member function is usually called near calls to CView::UpdateAllViews(). IsOverviewShown() Call to determine if the overview window is visible. Chapter 18 View Classes 329 18.6 Zooming/Panning Sample The Objective Toolkit Cloud sample (in the Samples\Toolkit\MFC\Views\Cloud directory) demonstrates all of the SECZoomView/SECPanView features described in this chapter and many more. The Cloud sample also includes several typical zooming/panning user interface options. For example, you can zoom by using a combobox in the toolbar, buttons on the toolbar, or menu items. This sample does not ship with the product. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide. To use this sample to its fullest capability, select Edit|Auto cloud to populate the view with clouds (numbered circles). Then, you can pan the view by holding the CTRL key and moving the mouse. Be sure to note the differences between pan instant and pan delay mode. In addition, examine the panning overview window. 330 Chapter 19 ActiveScript Hosting Framework 19.1 Overview Objective Toolkit includes an MFC-compatible ActiveScript engine that gives MFC applications complete access to Microsoft’s ActiveScript technology so they can host scripts. With Microsoft’s ActiveX scripting technology (Internet Client SDK), you can host VBScript or any other compliant scripting language in your own application. Internet Explorer includes DLLs that implement a VBScript and JavaScript scripting engine. By implementing several required COM interfaces, your C++ code can expose automation objects that you can control with a VBScript script. Objective Toolkit includes MFC extension classes that provide a default implementation of these COM interfaces and wrap these interfaces as an MFC extension. This makes it easier to incorporate scripting into your own MFC-based applications. Objective Toolkit’s ActiveScript framework supports two scripting languages– JavaScript and VBScript. Chapter 19 ActiveScript Hosting Framework 331 19.2 Overview of JavaScript JavaScript is a compact, cross-platform, object-oriented scripting language developed by Netscape that enables the creation of interactive web pages. A JavaScript enabled browser, such as Internet Explorer or Netscape, interprets JavaScript statements embedded in an HTML page. This enables you to create server-based applications similar to Common Gateway Interface (CGI) programs. The JavaScript language resembles Java in that it supports most of Java’s expression and basic control-flow constructs. However, there are some fundamental differences between Java and JavaScript. JavaScript lacks Java’s static typing and strong type checking and supports a smaller number of data types. For more information about JavaScript, we recommend the following Web page: http://docs.sun.com/source/816-6408-10/. 19.3 VBScript Visual Basic Script is a scripting language that is upwardly compatible with Visual Basic for Applications (VBA). VBA currently ships with Microsoft Office and Visual Basic. Microsoft Internet Explorer includes a COM server that implements a VBScript engine. Unlike VBA, you can add VBScript to your existing applications without having to pay licensing or royalty charges. Aside from language syntax and semantics, a key difference between VBScript and JScript is that VBScript has a complete server-side role and JScript does not. For example, VBScript can be integrated with the ISAPI component of Microsoft’s Internet Information Server (IIS) to run a variety of back-end applications. For a complete description of VBScript, we suggest the article, Visual Basic Script, by Keith Pleas in the Spring 1996 issue of Microsoft Interactive Developer magazine (MIND). In addition, Microsoft has a wealth of information about VBScript at the following URL: http://msdn.microsoft.com/enus/library/t0aew7h6(VS.85).aspx. 19.4 Hosting an Active Script Before you can make your application scriptable, you need to determine the application’s object model. The object model is a specification of the objects that are available to the scripting engine through OLE automation. Internet Explorer, for example, exposes objects that are appropriate to HTML authoring, such as the document, form, window, and browser. In your application, expose objects that are specific to your problem domain. After you determine the object model for your application, you can begin to implement the application. To host a script, you need to implement the necessary ActiveX scripting COM interfaces: IActiveScriptSite and IActiveScriptSiteWindow. By implementing these interfaces, you expose the properties and methods for each object in your object model and enable manipulation of these objects from VBScript or JavaScript. 332 Objective Toolkit’s SECAScriptHost class provides a default encapsulation and implementation of these two interfaces. Consequently, you only need to instantiate an SECAScriptHost to implement both COM interfaces. 19.5 ActiveScript Classes The ActiveScript classes are discussed in the following topics. 19.5.1 SECAAppObj The SECAAppObj class defines an application object that you can load from an ActiveScript script. An application object and a form object (SECAFormObj) work together to create new forms or load existing forms. An application object has the member functions that implement form creation and loading. The form object can return the application object that created it. You can also create another form from the returned application object. The SECAAppObj supports OLE automation and exposes the commands OpenForm, NewForm, and FormByName. You can call these commands from your scripts to invoke the OpenForm(), NewForm(), and FormByName() methods of the SECAAppObj class. SECAAppObj derives from CCmdTarget and implements a dispatch map. 19.5.2 SECAFormObj SECAFormObj is an ActiveX control that implements a scriptable form object to link your script to your application. SECAFormObj supports OLE automation and exposes commands for manipulating forms such as Show, Hide, Minimize, GetApp, and more. The GetApp command returns an application object (SECAAppObj) that you can use to create additional forms. In addition, the form defines events that it sends to its container, such as OnLoad and OnUnload. 19.5.3 SECAScriptHost The script host implemented by the SECAScriptHost class is a COM object that implements the IActiveScriptSite and IActiveScriptSiteWindow interfaces. Microsoft defines the IActiveScriptSite and IActiveScriptSiteWindow interfaces as part of their ActiveScript technology. A script host can create and destroy a script engine (either VBScript or JScript), create a new top level OLE automation object as part of the script, set a reference to the window hosting the script, and parse and execute a script. An application that requires scripting must instantiate a script host and use its pointer to create the scripting engine and execute a script. Chapter 19 ActiveScript Hosting Framework 333 19.5.4 SECAScriptOccManager The SECAScriptOccManager implements an OLE control container for use with ActiveScript. The class creates a script control site and a script control container. You must instantiate this class in your application’s InitInstance() method to enable support for the containment of ActiveScript controls and hosting COM objects. 19.5.5 ActiveScriptErrorHandler IActiveScriptErrorHandler is the basic interface for receiving and handling Active Scripting runtime error notifications. Derive the class that hosts scripts from IActiveScriptErrorHandler and override its HandleError() method. 334 19.6 Using the ActiveScript Framework The following topics describe how to use the ActiveScript framework. 19.6.1 ActiveScript and Type-libraries Objective Toolkit must link to the type library ScriptHost.tlb to compile correctly. By default, the ScriptHost type library is included as a resource in your application at resource ID 1. If you are building an application with its own type library, such as an automation server, put your type library as resource 1 and ScriptHost.tlb as resource 2. This can be done in the resource editor by importing the .tlb file as a custom resource of the type TYPELIB. You need to explicitly load and register the ScriptHost.tlb. This can be done in your InitInstance() method as the following code snippet illustrates. AfxOleRegisterTypeLib(m_hInstance, _AHost_tlid, _T("MyApp.exe\\2")); If you are using static linking, you need only to include ahostguid.h in your CWinApp-derived class cpp file: #include "Toolkit\ActvHost\AhostGUID.h" If your application is linking to the DLL version of Objective Toolkit, the GUID AHost_tlid is unresolved. Add the following includes to your CWinApp-derived class cpp file: #include <initguid.h> #include "Toolkit\ActvHost\AhostGUID.h" 19.6.2 To prevent the Objective Toolkit library from automatically including ScriptHost.tlb as resource To prevent the Objective Toolkit library from automatically including ScriptHost.tlb as resource 1, insert: #define SEC_NO_TLB before any inclusion of: #include "\Toolkit\SecRes.rc" 19.6.3 To incorporate scripting into your application 1. Setup your application as an OLE control container. The following lines should be added to your application’s InitInstance() method for this purpose. BOOL CScriptApp::InitInstance() { . . . Chapter 19 ActiveScript Hosting Framework 335 // Initialize OLE libraries, //a must for ActiveScript usage. if (!AfxOleInit()) { TRACE(_T("Failed to initialize OLE libraries\n")); return FALSE; } SECAScriptOccManager *pScriptOccMgr = new SECAScriptOccManager(); AfxEnableControlContainer( pScriptOccMgr ); 2. Determine the window within which you want to host the script and multiply inherit it from IActiveScriptErrorHandler. For example, class CDlgScript : public CDialog, public IActiveScriptErrorHandler { . . . 3. Override the HandleError() method defined by the IActiveScriptErrorHandler interface. For example, HRESULT CDlgScript::HandleError(IActiveScriptError *pse) { . . . 4. Define the method that actually creates a script engine and executes a script. This function is application-specific and can be called in response to an application specific event. Assuming the script is run when the user presses a Run button, the handler might look something like this: void CDlgScript::OnButtonRun() { SECAScriptHost* pScript = new SECAScriptHost(this); pScript->CreateScriptEngine( SECAScriptHost::VBScript); pScript->SetHostWindow(this); The three lines of code shown above create a scripting host that implements the requisite IActiveScriptSite and IActiveScriptSiteWindow COM interfaces. The parameter to the SECAScriptHost constructor is a pointer to an error handler that implements the IActiveScriptErrorHandler interface. The script host forwards incoming error notifications to the error handler pointer. The call to the CreateScriptEngine() method calls CoCreateInstance() to create the COM object responsible for delivering the VBScript or JavaScript service, depending upon the parameter passed in. The call to SetHostWindow() sets the window that is hosting the script. 5. Create a form object and add it to the script’s name space as a top-level item. This step also establishes the connection between the script and the form that it runs within. SECAFormObj* pForm=new SECAFormObj; LPDISPATCH pFormDisp = pForm->GetIDispatch(FALSE); CString strForm = _T("DemoForm"); BSTR bstrForm = strForm.AllocSysString(); pScript->AddTopLevelItem(bstrForm, pFormDisp); ::SysFreeString(bstrForm); 336 6. Load and parse the actual script. For example, CString strScript=m_strEditScript; UINT nSize=strScript.GetLength(); pScript->ParseBuffer(strScript,nSize); 7. Execute the script. For example, pScript->SetScriptState(SCRIPTSTATE_CONNECTED); pForm->FireOnLoad(); Once the execution is completed, the control returns to the statement following call to the FireOnLoad() method. 8. Perform cleanup operations—close the form, destroy the script engine, and release the VBScript host and form COM objects. pForm->FireOnUnload(); pScript->DestroyScriptEngine(); pFormDisp->Release(); pScript->Release(); 19.7 ActiveScript Sample The ScriptMDI sample includes a dialog that demonstrates the canonical form of an ActiveScript host. Refer to the CDlgScript class in the sample for an example of a minimal ActiveScript host. This sample does not ship with the product. If you need this sample, please submit a request to Stingray customer support. Chapter 19 ActiveScript Hosting Framework 337 338 Chapter20 ActiveHost Form Scripting and Editing Framework 20.1 Overview The Objective Toolkit forms engine works in concert with the ActiveScript Hosting Engine to provide a completely scriptable user interface. The ActiveHost Form Editing Engine is a scaled-down Visual Basic®-like editing and run-time environment that you can embed in your own applications. The forms engine enables the end user to edit and script MFC graphical user interfaces interactively using a form-editing user interface. Adding forms editing to an application is as easy as modifying the parameters for the document template instantiated in the application’s InitInstance() member. Because the ActiveHost form scripting and editing engine frequently uses the ActiveScript classes, we suggest you read Chapter 19, “ActiveScript Hosting Framework,” before you read this chapter. Chapter 20 ActiveHost Form Scripting and Editing Framework 339 20.2 ActiveHost Classes 20.2.1 SECScriptHostDoc The SECScriptHostView is a powerful CFormView-derived class that: Implements the drag-and-drop for OLE controls, Manages its accompanying toolbars, and Implements the user interface for sizing, positioning, and the setting properties of contained ActiveX controls. For example, SECScriptHostDoc implements the ability to toggle between edit and run mode, to create and delete script items, to handle ActiveScript error notifications, and to create, destroy, and serialize forms and their contents. You can pass an SECScriptHostDoc directly into your doc-template instantiation or you can derive your own document class from it and customize its behavior. 20.2.2 SECScriptHostView The SECScriptHostView is a powerful form view that is derived from CFormView. It can: Implement the drag-and-drop of OLE controls onto its surface, and Implement the user interface for sizing, positioning, and setting the properties of contained ActiveX controls and manage the toolbars that accompany it. The SECScriptHostView can be passed directly into your doc-template instantiation or you can derive your own view class from it and customize its behavior. 20.2.3 SECAFloatDocTemplate This class helps you create new forms that support dynamic form creation. Use this class in conjunction with SECScriptHostDoc, SECScriptHostView, and SECADlgFrame to support scriptable frame creation. When the script executes a NewForm() or OpenForm() function, the SECAAppObj responds by instructing the SECAFloatDocTemplate to create a new form. The forms created by the SECAFloatDocTemplate object are usually SECADlgFrame-derived classes. 20.2.4 SECADlgFrame This frame implements a simple SDI top-level frame that ActiveScript scripts can use during dynamic form creation. The SECAFloatDocTemplate object instantiates a frame of this type whenever the script creates a new form via the OpenForm(), NewForm(), and FormByName() commands. 340 20.3 Using the ActiveHost Form Editing Framework 20.3.1 To incorporate ActiveHost into your application 1. As with ActiveScript, set up your application as an OLE control container by adding the following lines to your application’s InitInstance() method. BOOL CScriptApp::InitInstance() { . . . // Initialize OLE libraries, a must for ActiveScript usage. if (!AfxOleInit()) { TRACE(_T("Failed to initialize OLE libraries\n")); return FALSE; } SECAScriptOccManager *pScriptOccMgr = new SECAScriptOccManager(); AfxEnableControlContainer( pScriptOccMgr ); 2. Modify your document template instantiation in your application’s InitInstance() method so the ActiveHost Document and View classes are created. You may want to derive your document classes or view classes or both from SECScriptHostDoc or SECScriptHostView. pDocTemplate = new CMultiDocTemplate( IDR_SCRPTMTYPE, RUNTIME_CLASS(SECScriptHostDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(SECScriptHostView)); 3. If you want to support direct scripting access to this application (for example, through the SECAAppObj class), make the following document template entry. Use all three of the runtime classes listed below or use derived classes. SECAFloatDocTemplate* pFloatDocTempl= new SECAFloatDocTemplate(IDR_TOPLEVEL, RUNTIME_CLASS(SECScriptHostDoc), RUNTIME_CLASS(SECADlgFrame), RUNTIME_CLASS(SECScriptHostView)); AddDocTemplate(pFloatDocTempl); 20.4 The ActiveHost Sample The scriptmdi and scriptsdi samples illustrate how to use the ActiveHost framework in MDI and SDI based applications. These samples are in the \samples\toolkit\MFC\ascript subdirectory. Chapter 20 ActiveHost Form Scripting and Editing Framework 341 342 Chapter 21 Advanced Docking Windows 21.1 Overview The Advanced Docking Window (ADW) architecture is built on top of the Objective Toolkit Layout Manager. It provides a powerful and flexible mechanism for docking any layout node such as any CWnd or Device Context rendered image (for example, a bitmap) to any other layout node that supports the proper docking interfaces. Although the architecture is intentionally independent of MFC’s docking controlbar architecture, ADW is compatible with any preexisting controlbar-based docking application and works in conjunction with controlbars as our samples demonstrate. Chapter 21 Advanced Docking Windows 343 21.2 Advanced Docking Windows Architecture Internally, ADW is based on the Layout Manager and leverages this inheritance to provide layout management within ADW nodes. You can use the Layout Manager transparently inside any existing container (CWnd, CView, CDialog, and more). You can also link it to any arbitrary layout element (CWnd control or a nonwindowed entity like a bitmap or DC image). For these reasons, the Layout Manager is an ideal infrastructure base for providing a generic docking mechanism. One of the benefits of the Advanced Docking Window architecture is that you can use it seamlessly with existing ControlBar-based docking windows. The ADW architecture shrinks the client area to accommodate its docking infrastructure the same way an OLE shrinks a frame window's client area to accommodate its own toolbars. This allows any existing dockable controlbars, including MFC’s toolbars and status bar and Stingray’s customizable toolbars/menubars and docking views, to cooperate with no adverse interaction with the ADW docking infrastructure. In addition, because you can wrap any CWnd in a dockable ADW node, you can migrate an existing CControlBar to the advanced architecture. Some of the main features of this architecture are as follows: 344 Docking inside an MDI childframe. Docking between different parent windows (for example, between MDI child and mainframe). Fixed-sized docking windows. Resizing at run time requires some customization. Realtime Office-97 style docking (for example, instant docking with no prediction rectangles). Indefinite nesting of docking windows (a docking window containing docking windows). Alternate docking layout logic that negotiates left/right first, top-bottom second and more. Simplified docking API and implementation procedure. Requires less than five lines of code. Lightweight implementation that does not depend on existing frame window classes. You can use ADW with the MFC frame window classes. 21.3 Advanced Docking Windows Features The sections that follow provide descriptions of specific features of the Advanced Docking Windows architecture. 21.3.1 Docking Inside an MDI Child Frame One of the strengths of the docking architecture is that the core docking logic is not tied to a specific parent window. This allows you to plug the docking mechanism into any container window transparently, including frames, dialogs, and generic CWnds without modifying the existing architecture. Objective Toolkit includes an external interface for hooking into frame window docking. You can apply our docking logic to any CFrameWnd derived object, including CMDIFrameWnd, CMDIChildWnd, SECFrameWnd, SECMDIFrameWnd, SECWorkbookWnd, and more. Note that the ADW architecture does not require the usage of the Stingray SEC docking frame classes (SECFrameWnd, SECMDIFrameWnd). This enables you to create a framework with a light footprint. In addition, note that you can apply the docking logic to a MDI child frame window. 21.3.2 Floating MultiDock Mode Another new feature of this architecture is the floating frame MultiDock capability. This feature allows you to dock more than one dockable node inside the same floating window and then redock that entire floating window as one entity. 21.3.3 Realtime Drag Mode ADW also supports realtime Office-Style drag-and-drop for toolbars. When you have this feature enabled, the contents of a docking operation are updated when the end-user presses the mouse button and drags. In other words, when the end-user presses the mouse button and then drags, he moves the toolbar not a prediction rectangle. For information on implementing this feature, see Section 21.5.5. 21.3.4 Alternate Border Layout Logic By default, the top and bottom borders receive precedence over the left and right borders. For example, the top and bottom borders always occupy the entire available client width, and the left and right borders shrink to accommodate the height between the top and bottom borders. This mode is configurable so that the left and right borders can receive priority over the top and bottom. For information on implementing this feature, see Section 21.5.7, “To use alternate border layout logic.” Chapter 21 Advanced Docking Windows 345 21.3.5 Advanced Docking Configurations This section describes some of the advanced configuration options available in the Advanced Docking Windows architecture and how to the implement or disable these features. You can use the SECDockInsertionConstraints insertion constraints object in conjunction with SECFrameDockingFeature::DockNode() to create advanced docking configurations. This mechanism allows reuse of similar constraints across multiple insertions easily. In addition, the relative insertion constraints are automatically updated to reflect the last insertion at each docking call. The following table lists some of the primary members of SECDockInsertionConstraints. For a comprehensive list, please refer to the Objective Toolkit Class Reference. Table 50 – SECDockInsertionConstraints class members Member Description SECLayoutNode* m_pNodeRelative Docking occurs relative to this node pointer. BOOL m_bInsertAfter If TRUE, newly docked node is positioned after m_pNodeRelative, else before. Void SetInsertPosition(UINT nPos,BOOL bAfter=TRUE,BOOL bPrimaryCnstr=TRUE); Alternative to m_pNodeRelative. If you use this mechanism, a caller can specify a positional index, instead of a concrete relative node pointer. If the position is out of range, it is truncated within the target grid’s cell bound. If bPrimaryCnstr is FALSE, this mechanism is only used as a backup when m_pNodeRelative cannot be located. BOOL m_bCreateNewLine If TRUE, a new line is created for the newly docked node, instead of positioning relative on an existing line. BOOL m_bDynamicNode If FALSE, docked node is not bounded by sizing splitters. Note that the parent grid row/column to which it belongs can still have sizing splitters. See SetBorderSizing. Int m_nForcedSize Allows specification of a specific size for the newly inserted node instead of “best fit” insertion. Int m_nForcedNewLineSize If a new line is created (m_bCreateNewLine==TRUE), then this value specifies the line size. Void SetDockSite(DWORD dwDockSite) Allows specification of the dock site via SEC_DOCK_* parameters. Void SetDockSite(SECFrameDockingFeatureBas e::DockSite site) Allows specification of the dock site via SECFrameDockingFeatureBase::top, bottom, left, or right. For information on using docking insertion constraints, Section 21.5.3, “To use docking insertion constraints.” 346 21.4 Advanced Docking Windows Splitter Styles The ADW architecture allows you to reconfigure the appearance and functionality of individual splitter windows in your application. The static member function, SECSplitterBase::SetDrawingStyleAll() can accomplish this at run time. The following splitter styles are available: Table 51 – Splitter Styles Splitter style Description SEC_SPLITTERBASE_DRAW_TRADITIONAL Traditional “3d” style. SEC_SPLITTERBASE_DRAW_FLAT “2d” style flag splitter. SEC_SPLITTERBASE_DRAW_BORDER “Border Style” splitter. This splitter has no visual effect, however, it responds to a splitter drag like any other splitter. The following figure illustrates the border style. The following figures exemplify some of the available splitter styles. Figure 143 – Example of the “2D” splitter style Chapter 21 Advanced Docking Windows 347 Figure 144 – Example of the “Border style” splitter 348 21.5 Using the Advanced Docking Windows Architecture The ADW feature is a component of Objective Toolkit. It is not included with the MFC Docking Window extensions in Objective Toolkit. To enable your library with ADW support, run the Objective Toolkit Build Configuration Wizard and select the Advanced Docking Window. If you want to use the customizable toolbars and menubars in Objective Toolkit, you must enable them explicitly through the Build Wizard. At a lower level, the advanced docking architecture can be used to support any container window seamlessly. The API support is only available for docking inside frame windows. 21.5.1 To incorporate advanced docking windows into your application You can use the advanced docking windows architecture to provide any frame window (CFrameWnd, CMDIFrameWnd, CMDIChildWnd, SECFrameWnd, SECMDIFrameWnd, SECWorkbookWnd, and more) with docking support. 1. In your frame header, instantiate a docking feature (SECFrameDockingFeature) and a docking factory (SECLayoutDockFactory) object. The docking feature is the hook to enable the docking architecture for that window and the docking factory is a convenience object designed to simplify the process of creating dockable node participants. For example, protected: // AdvDocking Additions SECLayoutDockFactory m_LFactory; SECFrameDockingFeature m_dockFeat; 2. In your frame window’s OnCreate() handler, call EnableDocking() on the docking feature. Note that this is a member of SECFrameDockingFeatureBase. Do not confuse it with MFC’s CFrameWnd::EnableDocking() member. The prototype for SECFrameDockingFeatureBase::EnableDocking() is as follows: virtual BOOL EnableDocking(CWnd* pParent,DWORD dwDockStyle=SEC_DOCK_ANY, BOOL bCreateNewLocalDockMgr=FALSE, SECLNDockingMgr* pMgr=NULL); The first parameter is a pointer to the frame window to participate in the docking architecture. Chapter 21 Advanced Docking Windows 349 You can apply a combination of zero or more of the following flags to the second parameter: Table 52 – DockStyle Flags Docking style Behavior SEC_DOCK_LEFT Allows docking to left border. SEC_DOCK_RIGHT Allows docking to right border. SEC_DOCK_BOTTOM Allows docking to bottom border. SEC_DOCK_TOP Allows docking to top border. SEC_DOCK_ANY Allows docking to any border. The third parameter specifies whether to create a local docking manager or not. If a local docking manager is created, then nodes docked to this docking feature will not be able to dock to any other docking features. This is typically used inside MDI children, to prevent docking with other frame windows. The fourth parameter is a pointer to the docking manager to associate with this feature. You can use the same manager for multiple dock features (1 feature per frame) to configure docking between specific windows. If this parameter is defined, bCreateNewLocalDockMgr is ignored. An example: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { . . . m_dockFeat.EnableDocking(this); } 3. Using the dock factory, create a dockable node for each entity (CWnd or DC image) you want to incorporate into the docking framework. For example, // Get the CWnd you want to wrap in a dockable node CWnd* pMyWnd=GetMyWndFromSomewhere(); // Create the dockable node. The first parameter is the CWnd, // the second is the parent window, the third is the default // title string (displayed when floating) and the fourth // signals that we want this node to automatically scale when // sized. Note that the factory performs all memory // management, // do not deallocate directly. SECLayoutNode* pDockNode=m_LFactory.CreateDockableNodeWnd(pMyWnd, this,_T(“Dockable Node 1”),TRUE); // Or, you could create a dockable node window from a // child control id: SECLayoutNode* pDockNode=m_LFactory.CreateDockableNodeWnd(IDC_CHILDID, this,_T(“Dockable Node 2”)); 350 A similar procedure can be used for creating dockable Device Context Nodes. See Section 21.5.2, “To create dockable device context nodes.” 4. Now, use DockNode() or FloatNode() or both members of SECFrameDockingFeature to place your dockable nodes created in the previous step initially. Several overloaded varieties of these functions are available for use so consult the online help for a thorough description of each parameter. For example: // Dock a node to the bottom, initially sized 100 pixels wide m_dockFeat.DockNode(pDockNode,SEC_DOCK_BOTTOM,100); // Dock a node to the bottom, using average row size m_dockFeat.DockNode(pDockNode2,SEC_DOCK_BOTTOM); // Dock a node to the right, creating a new column of width //of 50 pixels m_dockFeat.DockNode(pDockNode3,SEC_DOCK_RIGHT,50,TRUE); // Float a node at screen coordinates // left=100,top=150,right=300,bottom=400 m_dockFeat.FloatNode(pDockNode4,CRect(100,150,300,400)); 5. The result of the preceding code would be a layout similar to the following figure: Figure 145 – Sample Advanced Docking Windows application Chapter 21 Advanced Docking Windows 351 Note you can apply the DockNode() and FloatNode() methods iteratively to reset the position of a particular dockable node throughout the lifetime of that particular node. In addition, you can use SECFrameDockingFeatureBase::ShowNode() to show or hide a docked node. For examples of configurations, Section 21.3.5, “Advanced Docking Configurations.” 21.5.2 To create dockable device context nodes The Advanced Docking architecture enables you to create lightweight, windowless (no HWND) dockable nodes based on Device Context drawing. This node type is ideal for rendered images, pictures, and MVC ViewPorts. To create a dockable DC node, do the following: 1. Derive a class from SECReparentableNodeDC and then override the following protected virtual method: class CMyDCNode : public SECReparentableNodeDC { protected: virtual void OnDrawNode(HDC hDC,const CRect& rectDraw); }; 2. In your override, render your image to the DC passed in, given the bounding rect passed in. For example: void CMyDCNode::OnDrawNode(HDC hDC,const CRect& rectDraw) { CDC* pDC=CDC::FromHandle(hDC); ASSERT_VALID(pDC); pDC->FillSolidRect(rectDraw,m_clr); CRect rc=rectDraw; pDC->SetTextColor(COLORREF(RGB(0,255,0))); pDC->DrawText(m_strDraw,rc,DT_SINGLELINE|DT_CENTER|DT_VCENTER); } 3. Follow the procedure described in Section 21.5.1, with one exception. Instead of creating a dockable node, use the CreateDockableNodeDC() method of SECLayoutDockFactory. For example: // The DC node will be wrapped in a dockable node wrapper. // If we need to manipulate the DC node, it can be done // through a back pointer returned via the NODEBP macro // below. CMyDCNode* pDCNode; SECLayoutNode* pDockNode; // // // // // // // // 352 Create the dockable DC node, and its docking wrapper (pDockNode). The docking wrapper should be used for the DockNode operation, not the pDCNode back pointer. The NODEDC_CLASS macro is used to provide the run-time class name for factory allocation. Note that the factory performs all memory management, do not deallocate directly. pDockNode = m_LFactory.CreateDockableNodeDC(this, NODEDC_CLASS(CMyDCNode), _T(“Node 1”), NODEBP(pDockNode)); ASSERT(pDockNode && pDCNode); // pDockNode back-pointer provided for object manipulation, // if needed. If not needed, you could have called // CreateDockableNodeDC like this: pDockNode = m_LFactory.CreateDockableNodeDC(this, NODEDC_CLASS(CMyDCNode)); // Call a CMyDCNode function (i.e. App-Specific) pDockNode ->SetDrawColors(COLORREF(RGB(0,0,255))); m_dockFeat.DockNode(pDockNode); // no second parm, use // defaults 21.5.3 To use docking insertion constraints The following code demonstrates one way you can use the SECDockInsertionConstraints object to create an advanced docking configuration. CMainFrame::OnCreate(…) { // (EnableDocking call, then node creation) // Create our reusable insertion constraint object SECDockInsertionConstraints cnstr; // Dock the first node to the bottom dock site, using default // sizing cnstr.SetDockSite(SEC_DOCK_BOTTOM); m_dockFeat.DockNode(pDockNode,cnstr); // Dock a second node immediately after pDockNode (to the right, // since the bottom is horizontal), but set the second // node to occupy exactly 100 pixels in width. pDockNode will // subsequently be adjusted to fit the remaining width cnstr.m_nForcedSize=100; m_dockFeat.DockNode(pDockNode2,cnstr); // Dock a third node immediately below nodes 1 and 2, occupying a // new line with a height of 75 pixels cnstr.m_bCreateNewLine=TRUE; cnstr.m_nForcedNewLineSize=75; m_dockFeat.DockNode(pDockNode3,cnstr); // Now dock a node to the top border instead cnstr.Reset(); // reset constraints set above back to defaults cnstr.SetDockSite(SEC_DOCK_TOP); m_dockFeat.DockNode(pDockNode4,cnstr); Chapter 21 Advanced Docking Windows 353 // Let’s set node 5 to not be dynamic, i.e. not provide a sizing // splitter to change its width within the top docksite. // Dock next to node 4 (to the right) cnstr.m_bDynamicNode=FALSE; m_dockFeat.DockNode(pDockNode5,cnstr); // Dock node 6 to the left (before) node 4, i.e. the head of the // row. Node 6 is a dynamic node, so reset constraint from the last // step cnstr.Reset(); cnstr.m_pNodeRelative=pDockNode4; cnstr.m_bInsertAfter=FALSE; m_dockFeat.DockNode(pDockNode6,cnstr); ... The preceding constraints would create a layout similar to the following figure: Figure 146 – Objective Toolkit Advanced Docking Windows Sample 354 21.5.4 To adjust the border sizing Use the SECFrameDockingFeature::SetBorderSizing() method to control sizing properties specific to each border docksite (top, bottom, left, and right) individually or as a group. This method provides the following support: Table 53 – SECSetBorderSizingParms Members SECSetBorderSizingParms members Description m_bSplitter If TRUE, the docksite will provide a sizing border to increase or decrease the border size. If FALSE, size will be fixed. m_bSizeOnDock If TRUE, border will increase or decrease size to accommodate new line insertion or old line removal. If FALSE, border will remain fixed in size. m_bRealtimeDrag If TRUE, border splitter will track instantly. If FALSE, splitter prediction tracker will be used. For example: // Right border: no splitter, no size on dock, // splitter tracks instantly: SECSetBorderSizingParms SizingParms(FALSE,FALSE,TRUE); VERIFY(m_dockFeat.SetBorderSizing(SEC_DOCK_RIGHT,SizingParms)); // Or, the following can be used to apply settings to all borders: // VERIFY(m_dockFeat.SetBorderSizing(SizingParms)); In the following figure, the right border does not provide a sizing splitter. If nodes 5 or 6 or both were not dynamic (see SECDockInsertionConstraints::m_bDynamicNode in the Objective Toolkit Class Reference), there would not be a splitter between nodes 5 and 6. Chapter 21 Advanced Docking Windows 355 Figure 147 – Objective Toolkit Advanced Docking Windows Sample with no sizing splitter 21.5.5 To use ‘real-time’ drag mode You can use this feature to enable or disable. Use the following static function: static SECDragDropDockingFeature::SetRealtimeDrag(BOOL bEnable); 21.5.6 To use floating multidock mode By default, this support is enabled, you can disable via the following static function: static SECFloatDynGridLineTarget::EnableMultiDock(BOOL bEnable); 21.5.7 To use alternate border layout logic By default, the frame docking feature is configured so that the top and bottom borders receives precedence over the left and right borders, (for example, the top/bottom always occupies the entire available client width and the left/right borders shrink to accommodate the height between the top/bottom borders). This mode is configurable so that the left/right borders can receive priority over the top/bottom. 356 The following function is provided to modify the border layout logic: SECFrameDockingFeatureBase::SetBorderLayout( SECLNBorderClient::BorderAlgorithm BorderAlg, BOOL bRecalc=TRUE); By default, the top and bottom dock sites receive precedence over the left and right dock sites. This behavior is the same as MFC’s CDockbar layout algorithm's behavior. The following figure illustrates this layout: Figure 148 – Sample demonstrating top/bottom precedence The following code can be used to toggle the border mode so that left/right receives priority over top/bottom. m_dockFeat.SetBorderLayout(SEC_DOCKBORDERS_LRTB); This appears as follows. Notice how the left/right borders now occupy the entire height, whereas the bottom is shrunk to fit between. Note also the dockable toolbar and menubar are participants in MFC’s controlbar architecture so they are not subject to this layout manipulation. Chapter 21 Advanced Docking Windows 357 Figure 149 – Sample Demonstrating Left/Right Precedence 21.5.8 To integrate a dockable node inside an MDI child frame 1. Follow the steps as described in Section 21.5.1, “To incorporate advanced docking windows into your application.” However, allocate and initialize inside your CChildFrame class instead of CMainFrame. The lifetime of dockable nodes associated with a MDI child frame can be shorter than that of your mainframe. By default, enabling a MDI childframe for docking using the procedure outlined above allows any dockable node on the mainframe to be docked to that MDI child, and vice versa. In addition, the MDI child nodes can be docked to other MDI child nodes. As a result, when the MDI child is closed, all contained nodes, regardless of their original parent, are destroyed as well. Avoid accessing node pointers that were destroyed with the destruction of a MDI childframe. 2. If you want to limit the docking of a MDI child exclusively to that child, you must call EnableDocking() with the third parameter set to TRUE. For example: m_dockFeat.EnableDocking(this,SEC_DOCK_ANY,TRUE); 358 The third parameter signals to the docking feature that all nodes initialized for that frame can only dock to that frame and no other. In addition, dockable nodes created for other frames do not participate with that MDI child in the docking operation (for example, you cannot dock a node from the mainframe to that child frame, or vice versa). This is similar to how MFC’s controlbar architecture works. For more information on defining a docking participant subset, please refer to Objective Toolkit Class Reference for SECFrameDockingFeature::EnableDocking(). The following figure illustrates an MDI childframe with docking windows. The same core docking logic is leveraged in both the mainframe and childframe. Figure 150 – Sample demonstrating an MDI childframe with docking windows Chapter 21 Advanced Docking Windows 359 21.6 Advanced Docking Windows Examples Several examples are provided in the \samples\toolkit\MFC\advdocking directory to demonstrate the usage of the ADW architecture. These samples demonstrate ADW used in conjunction with Objective Toolkit docking extensions (AdvDockDeluxe) as well as stand-alone samples independent of the existing Objective Toolkit mechanism (AdvMDI, AdvSDI). 360 Chapter 22 Docking Views 22.1 Overview Objective Toolkit provides docking windows. Objective Toolkit’s docking views greatly extend this capability by allowing you to dock complete MFC CViews and frame windows. Docking views is an enhancement to MDI that allows you to convert any document window (for example, MDI child) into a docking window. Docking views builds on the capabilities of the Extended Control Bar Architecture, which allows you to dock not only control bars but also CViews, CSplitterWnds or any arbitrary CWnd-derivative. Chapter 22 Docking Views 361 22.2 Features of Docking Views The Objective Toolkit docking views architecture allows views to float as MDI children, dock to the main frame, and also float outside the main frame. When a view is docked, you can resize the dockable views using the splitter bars positioned along the window’s edge. When a view is floating as an MDI child or outside the main frame, you can resize the frames embedding the view in both directions. Figure 151 illustrates how docking views appear in each of these docking states. Figure 151 – Example Application with Docking Views 362 22.3 Issues when Docking a CView You cannot dock CViews using the SECControlBar as a parent. Instead, you need to build a docking views architecture over the extended docking windows architecture to accomplish this. The reasons are as follows: MFC assumes that a CView has a CFrameWnd parent. This assumption permits no other type of parent. The control bar architecture is unaware of the document/view architecture of which a CView is an integral part. You can attach a CView to a document, but the document template cannot be involved in the creation or initialization of the document, view, or parent window. Because the document was not created through standard processes, the document manager does not know it exists. This can cause problems during shutdown. A control bar cannot receive focus or become the active window. Control bars are intentionally designed to remain inactive and without focus at all times, whereas CViews are designed to be active and receive focus. This conflict of interest manifests itself in many strange ways — mostly focus anomalies. Chapter 22 Docking Views 363 22.4 Docking Views Options There are three options that you can specify to change the appearance and behavior of docking views. These options are listed below. They can used by overriding the OnEnableSysCommandEx() method of SECMultiDocTemplate. For an example of this, see Section 22.10.3. Table 54 – Docking View Options 364 Docking View Option Description SCXOPT_ENABLED Enables the options specified below. SCXOPT_NO_HANDLE_RDBLCLK Disallows right double-clicks to dock/undock the window. SCXOPT _NO_CAPTION_BUTTON Hides the caption button. 22.5 The Docking Views Classes Because the docking views architecture builds on the extended docking windows architecture, each of the docking windows classes also participates in the docking views architecture. Figure 152 – Objective Toolkit’s Docking Views Class Hierarchy CFrameWnd SECFrameWnd SECDockableFrame CMDIChildWnd SECMDIChildWnd CControlBar SECControlBar SECFrameBar CMultiDocTemplate SECMultiDocTemplate There are also changes and additions to existing classes such as SECMDIFrameWnd and SECMDIChildWnd. These changes ensure that SECDockableFrame objects are included in the command routing class chain so that menu picks are properly routed to a docked document if it is active. 22.5.1 SECDockableFrame The SECDockableFrame class derives from SECFrameWnd and is the focal point of the docking views architecture. This class is a hybrid of a control bar and a frame window. It possesses the attributes and functionality of both. Because it is a frame-derived object, an instance of SECDockableFrame can parent a CView, CSplitterWnd, SECTabWnd or any CWnd-derivative. An SECDockableFrame also has an optional title bar for showing activation and contains the logic required to change the menu bar when activated, like a frame window. Like an SECControlBar, a dockable frame can be docked and resized when it is docked. An SECDockableFrame object acts as the immediate parent window of the view when its docked or floating outside the main frame, which fulfills the MFC requirement that views always reside as a child of a frame object. 22.5.2 SECFrameBar The SECFrameBar class derives from SECControlBar. This class acts as a container for the SECDockableFrame when the view is either docked or floating outside the main frame. It adds the accessor method GetDockedFrame(). This class acts as the bridge between the docking views architecture and the docking windows architecture. Chapter 22 Docking Views 365 22.5.3 SECMDIChildWnd The SECMDIChildWnd class derives from CMDIChildWnd and acts as the parent window for the view when floating as an MDI child. 22.5.4 SECMultiDocTemplate The SECMultiDocTemplate class plays an essential role. Derived from CMultiDocTemplate, it not only creates the document, view, and frame window but it also connects them together. As you might expect, the SECMultiDocTemplate inherits most of its functionality from its base class. However, it adds knowledge of the SECDockableFrame class and knows how to open a new document as either a normal MDI child or a docking document. In addition, the SECMultiDocTemplate class also adds a ToggleDocking() method, which allows open documents to be toggled between docked and undocked states. SECMultiDocTemplate has an overloaded constructor that allows you to specify the run-time classes of the necessary participants in case you need to derive from the base architecture classes. SECMultiDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass, UINT nIDDockableResource = 0, CRuntimeClass* pDockableFrameClass = 0, CRuntimeClass* pControlBarClass = 0); The parameters of this constructor are described in the table below. The first four parameters are the same as that of the parent class, CMultiDocTemplate. Table 55 – Parameters for SECMultiDocTemplate’s constructor 366 Parameter Description nIDResource The ID of the resources used with the document type. pDocClass Points to the CRuntimeClass object of the document class. pFrameClass Points to the CRuntimeClass object of the frame window class. pViewClass Points to the CRuntimeClass object of the view class. nIDDockableResource The ID of the resources used to load the accelerator table and menus for the SECDockableFrame. pDdockableFrameClass Points to the CRuntimeClass object of the dockable frame class (defaults to SECDockableFrame). pCcontrolBarClass Points to the CRuntimeClass object of the control bar class (defaults to SECFrameBar). 22.6 Architectural Overview Before delving into a point-by-point explanation of how to resolve problems with docking views, lets consider the architecture. The focal point of the docking views architecture is the SECDockableFrame class. This class is a hybrid of control bar and frame window. It possesses the attributes and functionality of both. Like a frame window, an instance of SECDockableFrame can parent a CView, CSplitterWnd, SECTabWnd or any CWnd-derivative. An SECDockableFrame also has an optional title bar for showing activation and contains the logic required to change the menu bar when activated, like a frame window. Like an SECControlBar, SECDockableFrame is resizable when it is docked. The SECDockableFrame class is not the only component comprising the dockable views architecture. The SECMultiDocTemplate class plays a key role as well. SECMultiDocTemplate is derived from CMultiDocTemplate, whose role is to create the document, view, and frame window and then connect them. As you would expect, the SECMultiDocTemplate inherits most of its functionality from its base class. However, it adds knowledge of the SECDockableFrame class and knows how to open a new document as either a normal MDI child or as a docking document. In addition, the SECMutliDocTemplate class also adds a ToggleDocking member function that allows open documents to be toggled between docked and undocked states. There are also changes and additions to existing classes such as SECMDIFrameWnd and SECMDIChildWnd. These changes ensure that SECDockableFrame objects are included in the command routing class chain so menu picks are properly routed to a docked document if it is active. Chapter 22 Docking Views 367 22.7 Docking Views Containment Model When a view is floating as an MDI child, the view is contained in an SECMDIChildWnd; in the same way that views in typical MDI applications are contained by CMDIChildWnd frames. Figure 153 – Example Containment of a View as a MDI Child However, when the view is docked, this containment relationship changes significantly. The view is actually re-parented to a new frame window called an SECDockableFrame. The SECDockableFrame acts as a godparent. It fulfills the MFC requirement that stipulates that only frames may contain CView classes. The SECDockableFrame is in turn parented by an SECFrameBar that is SECControlBar derived. In turn SECControlBar resides in one of the dock bars belonging to the main frame. During a docking operation, only the CView survives. Figure 154 – Example Containment for a Docked View When a view is floated outside the main frame, the containment relationship is similar to that of a docked view state. 368 Figure 155 – Example Containment for a Floating View When a docked view becomes floated, a new SECFrameBar instance is created. In other words, this instance is not the same instance as when the frame was docked to the main frame. However, the SECDockableFrame instance does survive the transition from the docked state and remains the parent of the view. When the view is floated again as an MDI child, the SECFrameBar is undocked from the SECDockBar, both the SECDockableFrame and the SECFrameBar are destroyed, and the CView is re-parented to a newly instantiated SECMDIChildWnd. Again, note that during this transition from docked or floating to an MDI child state only the CView survives. Chapter 22 Docking Views 369 22.8 WM_SYSCOMMANDEX You can use the WM_SYSCOMMANDEX message in your CView class to allow the CView to modify its docking state programmatically. The CView sends this message to its current parent frame. The following code sends a message to the view’s parent frame. using namespace nsSysCommandEx; ScxInfo si(GetParentFrame()->m_hWnd); si.m_dw[0]=SCXID_DOCKRIGHT; GetParentFrame()->SendMessage(WM_SYSCOMMANDEX, SCX_NEWFRAME,(LPARAM)si); The following SCXID parameters are available and defined in <stingrayinstalldir>\Include\Toolkit\syscmdex.h. Table 56 – SCXID Parameters 370 SCXID parameter Description SCXID_MDICHILD Float the view as an MDI child. SCXID_MINMDICHILD Minimize the view as an MDI child. SCXID_MAXMDICHILD Maximize the view as an MDI child. SCXID_RESTOREDMDICHILD Restore the view as an MDI child. SCXID_DOCKED Dock the view in the next docking position on the main frame. SCXID_DOCKLEFT Dock the view on the left side of the main frame. SCXID_DOCKTOP Dock the view on the top of the main frame. SCXID_DOCKRIGHT Dock the view on the right side of the main frame. SCXID_DOCKBOTTOM Dock the view on the bottom of the main frame. SCXID_FLOATING Float the view outside the main frame. 22.9 Docking Views and MDI Alternatives The docking views architecture is ideal for the Workbook Document Interface (WDI). The mdi sample program in the <stingray-installdir>Samples\Toolkit\MFC\DockingViews\Bounce subdirectory demonstrates integrating docking views into a WDI application. The Floating Document Interface and the Multiple Top-Level Interface are not suited for the concept of docking views. These MDI alternatives act as multiple SDI windows with frames classes that do not support docking containment. 22.10Using the Docking Views Architecture The topics that follow demonstrate how to utilize docking views in your applications. 22.10.1To incorporate docking views into your application 1. Incorporate docking windows into your application. See Section 21.5, “Using the Advanced Docking Windows Architecture,” for more information. 2. In the InitInstance() method of your application object, modify your document template class to utilize SECMultiDocTemplate in place of CMultiDocTemplate. SECMultiDocTemplate* pTemplate=new SECMultiDocTemplate( IDR_RICHEDITTYPE, RUNTIME_CLASS(CREditDoc), pDefaultMdiChildFrameClass, RUNTIME_CLASS(CRichEditView)); AddDocTemplate(pTemplate); Docking views are not supported by SDI applications. 22.10.2To set docking view styles See Section 8.11.7, “To set the style of a docking window.” In this chapter, see Section 22.4, “Docking Views Options.” 22.10.3To toggle the presence of the docking button on a dockable view frame 1. Create an SECMultiDocTemplate-derived class. Add the static data member, m_bDockingViewCaptionButton, to this class. Because it is static, this flag affects every docking view. For example: // static docking view flags for easy access static BOOL m_bDockingViewCaptionButton; Chapter 22 Docking Views 371 2. In the new class, override the OnEnableSysCommandEx() method to set the SCXOPT_NO_CAPTION_BUTTON style based conditionally on the state of m_bDockingviewCaptionButton, and then call the base class implementation. This override ensures that each new frame has the appropriate docking style. // This virtual callback is called when a new // frame is being created. Override to customize // the docking view support. Note that changes // set here do not take effect until the next // frame is created (either a new document // is opened, or an existing document is // docked/undocked). Calling the EnableSysCommandEx //frame window function is necessary for immediate //update (must be used in conjunction with // this override to maintain config through all // docking states). void CMyMultiDocTemplate::OnEnableSysCommandEx (CFrameWnd* pFrame, DWORD dwScxFlags) { // use the nsSysCommandEx namespace for // easy typing using namespace nsSysCommandEx; // Flag that we are actively using the // syscommandEx options: dwScxFlags|=SCXOPT_ENABLED; // do we want a caption button? if(m_bDockingViewCaptionButton) dwScxFlags&=~SCXOPT_NO_CAPTION_BUTTON; else dwScxFlags|=SCXOPT_NO_CAPTION_BUTTON; // Pass this info along to the base class SECMultiDocTemplate::OnEnableSysCommandEx( pFrame, dwScxFlags); } 3. In the InitInstance() method of your application object, modify your document template class to utilize the SECMultiDocTemplate-derived class. CMyMultiDocTemplate* pTemplate = new CMyMultiDocTemplate( IDR_MYRESTYPE, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(SECWorksheetWnd), RUNTIME_CLASS(CMyView))); AddDocTemplate(pTemplate); 4. In the view class, add a new method, UpdateParentFrame(). This method updates the frame so they reflects the current state of the caption button. void CMyView::UpdateParentFrame() { DWORD dwScxFlags=SCXOPT_ENABLED; if(!CMyMultiDocTemplate::m_bDockingViewCaptionButton) dwScxFlags|=SCXOPT_NO_CAPTION_BUTTON; 372 // If parent is a dockable frame, send flags // as approp. CFrameWnd* pFrame=GetParentFrame(); ASSERT(pFrame); if( pFrame->IsKindOf( RUNTIME_CLASS(SECMDIChildWnd))) ((SECMDIChildWnd*)pFrame) ->EnableSysCommandEx(dwScxFlags); else if( pFrame->IsKindOf( RUNTIME_CLASS(SECDockableFrame))) ((SECDockableFrame *)pFrame) ->EnableSysCommandEx(dwScxFlags); // Signal a frame change to refresh // caption button (if necessary) pFrame->SetWindowPos(NULL,0,0,0,0, SWP_NOZORDER|SWP_NOMOVE| SWP_NOSIZE|SWP_FRAMECHANGED); } 5. Add a toolbar button or a menu item handler that toggles the state of the caption button for the current view. void CMyView::OnToggleDockingCaption() { CMyMultiDocTemplate::m_bDockingViewCaptionButton= !CMyMultiDocTemplate::m_bDockingViewCaptionButton; // The SECMultiDocTemplate::OnEnableSysCommandEx // override will insure each new frame created // will have the latest docking style. We // still must explicitly update this // current frame, though, for the changes to // have an immediate effect. UpdateParentFrame(); } See the mdi sample in the <stingrayinstalldir>Samples\Toolkit\MFC\DockingViews\Bounce subdirectory for a demonstration of this technique. 22.10.4To disable right mouse double-clicks on the docking view caption bar See Section 22.10.3, “To toggle the presence of the docking button on a dockable view frame,” and replace SCXOPT_NO_CAPTION_BUTTON with SCXOPT_NO_HANDLE_RDBLCLK. 22.10.5To create an initially docked view When you call OpenDocumentFile(), which is typically in the InitInstance() method of your application object, specify the bInitiallyDocked parameter (the third parameter) to be TRUE. It defaults to FALSE if not specified. For example: Chapter 22 Docking Views 373 // Doc the cloud view initially GetTemplate(CLOUD_DOCNAME) ->OpenDocumentFile(NULL, TRUE, TRUE); 22.10.6To start an application with no initial view Insert the following line immediately before the call to ProcessShellCommand() in the application object's InitInstance() method. cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing; 22.10.7To put a splitter in a docking view 1. Add a CSplitter object as a member variable in your view. You need to make the splitter a member of the view instead of the frame because the frame does not survive the docking process. For more information, see Section 22.7, “Docking Views Containment Model.” For example: CSplitterWnd m_wndSplitter; 2. In the OnCreate() method of the view, create the splitter window: // Create a 1 x 2 splitter BOOL bRC = m_wndSplitter.CreateStatic( this, 1, 2); 3. Then, add additional windows or views. // Embed view 1 in pane 0 m_wndSplitter.CreateView ( 0, 0, RUNTIME_CLASS(CMyView), size, pContext ); // Embed view 2 in pane 1 m_wndSplitter.CreateView ( 0, 1, RUNTIME_CLASS(CMyView2), size, pContext ); 4. Add an OnSize() method to resize the splitter as needed. // // VERY IMPORTANT!!! // You must size the contained window to the current size of // the view otherwise you will see nothing!!! void CMySplitterView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); m_wndSplitter.SetWindowPos(&wndTop,0,0,cx,cy, SWP_SHOWWINDOW); } The OnCreate() method of the view has the LPCREATESTRUCT lpCreateStruct parameter. One of the members of this structure contains the pointer to the CCreateContext: 374 CCreateContext* pContext = (CCreateContext*)lpCreateStruct->lpCreateParams; The source code for this section was taken from our SplitView sample application in <stingrayinstalldir>\Samples\Toolkit\MFC\DockingViews\DockTabs\SpltView.cpp. 22.10.8To dock a view Have your CView send a WM_SYSCOMMANDEX message to its current parent frame window. For example, the following code notifies the frame that the view is to be docked to the right. using namespace nsSysCommandEx; ScxInfo si(GetParentFrame()->m_hWnd); si.m_dw[0]=SCXID_DOCKRIGHT; GetParentFrame()->SendMessage(WM_SYSCOMMANDEX, SCX_NEWFRAME,(LPARAM)si); For a list of SCXID parameters, see Section 22.8, “WM_SYSCOMMANDEX.” 22.10.9To float a view as an MDI child From your CView, send a WM_SYSCOMMANDEX message to its current parent frame window. For example: using namespace nsSysCommandEx; ScxInfo si(GetParentFrame()->m_hWnd); si.m_dw[0]=SCXID_MDICHILD; GetParentFrame()->SendMessage(WM_SYSCOMMANDEX, SCX_NEWFRAME,(LPARAM)si); For a list of SCXID parameters, see Section 22.8, “WM_SYSCOMMANDEX.” 22.10.10To obtain a pointer to the view Within the context of your application’s main frame object (SECMDIFrameWnd-derived), call the SECMDIFrameWnd::GetActiveFrame() and CFrameWnd::GetActiveView() methods. For example: // Retrieve a pointer to the currently active view, // regardless of whether it is inside an MDI child, // docked frame, or floating frame. CFrameWnd* pActiveFrame=GetActiveFrame(); if(pActiveFrame) CView* pActiveView= pActiveFrame->GetActiveView(); CMDIFrameWnd::MDIGetActive() does not return the appropriate value for a docked view. Chapter 22 Docking Views 375 22.10.11To control the initial size and position of a docking view as it is docked 1. Derive a class from SECMultiDockTemplate. 2. Override SECMultiDocTemplate::ToggleDocking(CFrameWnd* pFrame). 3. In the override, issue a call to the SECMultiDockTemplate::Dock() method instead of invoking the base class. Because this method is overloaded, you need to call the overload that accepts the SECDockPos() and SECDockSize() parameters. For example: void CMyMultiDocTemplate::ToggleDocking(CFrameWnd* pFrame) { BOOL bDocked = IsDocked(pFrame); if (bDocked) // No change here Undock(pFrame); else { // IsToCustomDock is some function you // write to determine if special // handling needed... if(IsToCustomDock(pFrame)) { // Establish your custom docking config // dockbar row 3, col 2 SECDockPos pos(3,2); // 50% width, 100 height SECDockSize size(0.50f,100); // or whatever… UINT nDockbarID=AFX_IDW_DOCKBAR_RIGHT; // Call the overload Dock(pFrame,nDockbarID,&pos,&size); } else // old way Dock(pFrame); } } 376 Chapter 23 Layout Manager Framework 23.1 Overview One of the biggest problems Windows developers face is window layout management and device independent positioning. Before Objective Toolkit was introduced, developers had to write thousands of lines of custom code to handle the resizing of dialogs and forms. Objective Toolkit’s Layout Manager allows you to circumvent this challenge by providing a framework for implementing plug-in layout algorithms. The framework includes several sample layout algorithms such as grid bag layout, relative, scaled and others. It also affords you the flexibility to design custom layout managers based on your needs (for example, for low-resolution displays). The Layout Manager plugs seamlessly into your existing dialog, formview, controlbar, property page, or any other window to allow for nested layouts. You can integrate the Layout Manager into applications in a matter of minutes. Chapter 23 Layout Manager Framework 377 23.2 Issues with Resizable Windows Whenever you create a dialog, formview, or property page with child window controls you need to decide what to do when the user tries to resize the window. You could forbid the resize event, but this leads to an awkward user interface. You could ignore the event, but this leads to an under utilized window with a disproportionate amount of extra space. You could even trap the size event and code your own custom layout logic. Unfortunately, you would need to devote a significant part of your schedule to creating a large amount of implementation specific code in order to do this. The code is also subject to change whenever you want to modify the window’s layout. In addition, if you want to achieve resolution independent positioning, even more work is required. Objective Toolkit provides a powerful layout management framework that encapsulates all the details of laying out your child window controls so that you can concentrate on content rather than the mechanics of your user interface presentation. Using the layout management framework, you do not need to create and maintain hard-coded pixel positions. Instead, you can create a layout that consists of a series of layout constraints that are easy to remember and change. Using a relative constraint-based layout algorithm guarantees that your window or dialog looks the same no matter what type of screen it is displayed on— be it a 640x480 laptop or a 1600x1200 workstation. 378 23.3 Benefits of the Objective Toolkit Layout Manager The concept of a layout manager is not a new idea. Java, Motif, OWL, and Visual Basic OCX’s all offer layout management. What makes the Objective Toolkit Layout Manager different is that it is tightly integration with MFC, is based on a strong and highly extensible architecture, and is easy to integrate. 23.3.1 Objective Toolkit Layout Manager: MFC Integration MFC does not include default layout management. Although the Visual Studio IDE offers an excellent mechanism for creating and initializing dialog templates, it only provides a static view of your application. The Objective Toolkit Layout Manager is written in MFC for MFC. You can plug it into your existing MFC application seamlessly, without making any modifications to your existing window class hierarchy. In addition, the Objective Toolkit Layout Manager offers several calculation and redraw optimizations that minimize the amount of CPU processing and screen flicker that occur with every window resize. 23.3.2 Objective Toolkit Layout Manager: Strong Architecture The Objective Toolkit Layout Manager is extensible by design. Most of the other layout managers offer a canned layout algorithm that is tightly tied to a specific dialog or formview implementation. In contrast, the Objective Toolkit Layout Manager is designed to be scalable. It includes a tree-like division of labor that makes it easy to nest layouts or to add your own layout type. In addition, the Layout Manager is not tied directly to CWnd so you can position non-CWnd entities, like a rendered image, on a CView. The strong architecture detaches the layout logic from the actual visual implementation, which provides a strong component for reuse in an endless variety of contexts. 23.3.3 Objective Toolkit Layout Manager: Ease of Integration Many layout managers force you to abandon your existing window base class and use a canned base class that is layout-aware. This is problematic if you are already using a custom base class (for example, a special CDialog derivative) that contains the functionality you need. Because MFC is not designed for multiple inheritance, you need to decide which of the two base classes is more valuable to you and discard the functionality of the other class. Chapter 23 Layout Manager Framework 379 The Objective Toolkit Layout Manager uses aggregation rather than inheritance to apply its layout logic. Merging the Layout Manager into your application is as easy as instantiating a member layout factory, and producing a listener object that hooks laterally into your existing class without making any changes to your base class. The layout factory tracks memory management and allows you to merge a layout into your existing window with as little as three lines of source code and one declared member variable. 380 23.4 Layout Manager Architecture The Layout Manager has a strong architecture that is suited for reuse and extensibility. Based in part on the Composite design pattern, the Layout Manager is a collection of nodes arranged in a treelike hierarchy of responsibility. 23.4.1 Layout Nodes The basis of this tree is the base class SECLayoutNode, which defines the interface for membership in this tree. A layout node is defined as any object that derives its interface from the SECLayoutNode base class. Conceptually, layout nodes are either proactive or reactive in nature. Proactive nodes, also known as composites, hold the layout algorithms. Each proactive node encapsulates one layout algorithm. Examples are SECLNRelative, SECLNScale, and SECLNGridBag. Proactive nodes are designed to contain and administrate child nodes. Reactive nodes, also known as primitives, are home to the leaf objects controlled by the proactive nodes. When you override the appropriate functions in the SECLayoutNode base class, a reactive node can respond to events driven by its parent node and position, resize, and render itself as appropriate. SECLayoutNodeWnd is an example of a reactive node. Although SECLayoutNodeWnd is designed to link to a CWnd, you could also design your own reactive node to link to a non-CWnd, like a rendered graphic in a view. The distinction between proactive and reactive nodes is only conceptual in nature. Syntactically, both are derived from SECLayoutNode and possess the same type-interface. Only the intended usage of the object defines its designation. Some objects can be both proactive and reactive. For example, the SECLayoutNodeSplitter class is a reactive end-node with a simple proactive layout algorithm. In general, proactive nodes are not visible entities. For example, an algorithmic layout node is a black box rectangle that is responsible for administrating all its children within that rectangle. Nonetheless, one of its children could be a proactive node that administers its child nodes. This is the strength of a polymorphic layout node in a composite, tree-like hierarchy. For example, suppose you want to create a dialog with three push buttons. Suppose you want to lay out those push buttons in two rows, with one button spanning the entire top row, and the other two buttons sharing the bottom row. Figure 156 – Sample Button Layout Use nested layouts to solve this problem. At the top of your layout tree, create an SECLNGrid grid layout node. You now have one black-box grid layout node. Chapter 23 Layout Manager Framework 381 Figure 157 – Sample Grid Layout Node If you configured this grid layout node to have two rows and one column, it would have two layout node children. Figure 158 – Sample Grid Layout Node with Two Children The SECLNGrid parent does consider what kind of children it creates; however, it does know that it has two child objects derived from SECLayoutNode. To hook Child Node 1 to a button, use SECLayoutNodeWnd. SECLayoutNodeWnd attaches to a CWnd through aggregation, which guarantees type compatibility with the layout-node hierarchy. For Child Node 2, you need to administrate both button two and button three. The easiest way to do this is to nest another proactive node. In this case, you would embed another grid node to administrate the two buttons. Figure 159 – Sample Grid Layout with One Button Child Node and One Grid Child Node Finally, you would configure this nested grid node to be a 1x2 grid, with two SECLayoutNodeWnd children (one for button two, one for button three). Here’s the result. 382 Figure 160 – Final Three Button Layout The following figure shows this layout represented in a tree structure. Figure 161 – Tree Structure for the Three Button Layout The top-level grid node only possesses knowledge of its two children: an SECLayoutNodeWnd object and an SECLNGrid object. Because all children are derived from SECLayoutNode, it can polymorphically manipulate the two children with the same interface. After the top-level grid node positions the nested grid, the nested grid positions its two children — the two SECLayoutNodeWnd objects attached to buttons 2 and 3. Each parent treats its child nodes as black boxes. There is no parent-grandchild manipulation. This divide-and-conquer tactic provides a powerful and extensible mechanism. You can easily add your own custom algorithms or window types into this framework with little or no change to an existing layout. Chapter 23 Layout Manager Framework 383 23.4.2 Window Listeners The SECWndListener class acts as a bridge between the WIN32 windowing world and the abstracted layout node tree. The window listener attaches laterally through aggregation to an existing dialog, view, control bar, or any other CWnd-derived window. It listens for the appropriate windows messages to start the layout management recalculations. The listen mechanism uses a streamlined window subclass that has a negligible performance hit and is in no way disruptive to the normal message flow. This enables you to create a seamless hook into your application’s layout framework without making any changes to the base class. The window listener is not required for use of the layout framework. The window listener’s sole purpose is to simplify the layout insertion process. 23.4.3 Layout Factory The SECLayoutFactory class simplifies the process of creating and merging layout nodes into the Layout Manager framework. A component is a window control, graphical entity, layout algorithm, or other object. Because every component of a layout must have a corresponding layout node, you need to allocate a large number of layout node objects. The layout factory simplifies this process by: Dynamically creating the node type of interest. Automatically setting some default parent-child relationships and other miscellaneous settings. Keeping track of the allocated storage for self-contained memory management. Typically, a user of the layout framework only needs to instantiate one layout factory object as a member of his or her parent window class. Then, the layout factory methods are used to allocate all additional storage required. The layout factory can also allocate a window listener object, a feature that simplifies layout integration even further. Like the window listener, use of the layout factory is not required for utilization of the Layout Manager, although it is highly recommended. 23.4.4 Splitter Node The SECLayoutNodeSplitter class easily merges splitter functionality into your Layout Manager. This powerful splitter class is optimized so that you can plug it into the layout framework just as you would any other layout node. This approach is much easier than trying to use CSplitterWnd with a window that is not a CView. The SECLayoutNodeSplitter works with any layout node. You can even embed layouts inside a splitter pane. It also works inside a dialog, or a controlbar. Additionally, it supports full drag mode for instant dragging without a tracker, two-dimensional dragging for both three and four pane splitters, and virtual callbacks for further customization. 384 23.5 Layout Algorithms This section describes each of the default layout algorithms provided with the Objective Toolkit Layout Manager component. You can create your own custom layouts, which is addressed in a later section. 23.5.1 Alignment Layout The Alignment layout aligns a child node relative to the parent alignment node. The child node can be aligned with the left, right, top, bottom, horizontal center, vertical center, or both centers. You can also specify top, left, right, and bottom margins. You can use this layout algorithm as a nested layout. Figure 162 – Example of the Alignment Layout 23.5.2 Scale Layout The Scale layout maintains every child with a constant aspect ratio to the parent scale node. In other words, the child node’s top, left, right, and bottom coordinates are stored as percentages of the parent node’s size and are resolved to actual pixel values with each recalculation. This guarantees a constant aspect ratio regardless of the size of the parent node. Chapter 23 Layout Manager Framework 385 Figure 163 – Example of the Scale Layout 23.5.3 Grid Layout Inspired by the Java Grid Layout, this algorithm allows you to position child nodes in a twodimensional grid of specific or arbitrary size. You can initialize the grid to a specific two-dimensional matrix, or set it to grow arbitrarily in one direction (rows or columns). Figure 164 – Example Grid Layout 23.5.4 GridBag Layout Inspired by the Java GridBag Layout, GridBag supports: 386 Node spanning of multiple rows or columns or both. Variable width columns. Variable height rows. Variable row/column resize weights that control the rate of change. Grid cell insets, grid fill modes, and grid cell anchoring. Figure 165 – Example Gridbag Layout 23.5.5 Relative Layout The Relative Layout allows a logical organization of layout nodes. You can set constraints with English-like semantics. For example: “Set the left side of node 1 equal to the right side of node 2 plus 10 pixels.” “Set the bottom of node 1 to 25 percent of the height of node 2.” “Move node 1 such that its bottom is equal to the top of node 2 minus 10 pixels.” This algorithm also guarantees device independent positioning. Chapter 23 Layout Manager Framework 387 Figure 166 – Example Relative Layout 388 23.6 Using the Layout Manager The following topics describe how to use the Layout Manager via procedures and examples. 23.6.1 Adding Layout Management to Your Applications The process of merging the layout framework into your application is easy. The following procedure outlines the recommended steps. 1. Add an instance of the layout factory to the header of the window class to which you want to apply the Layout Manager. For example: Class CMyResizableDIalog : public CDialog { . . . protected: SECLayoutFactory m_LayoutFactory; . . . 2. During the window’s initialization stage (for example, in a CDialog’s OnInitDialog(), a CWnd’s OnCreate(), or a CView’s OnInitialUpdate()), initialize the layout. You can use the layout factory to produce both the layout algorithms and the window nodes required for the constraint setup. CMyDialog::OnInitDialog() { // Create top-level scale node. The layout factory will // automatically // deallocate the storage when it goes out of scope. SECLNScale* pScaleNode=(SECLNScale*)m_LayoutFactory.CreateNode( NODE_CLASS(SECLNScale)); // Create a “window node” and link it as a child of the scale // layout SECLayoutNodeWnd* pNode = m_LayoutFactory.CreateNodeWnd(IDC_BUTTON1, this,pScaleNode); 3. Now, add the relevant layout constraints. See the next section for more information. 4. Finally, create the window listener and bridge between the parent window and the layout framework. This guarantees the appropriate windows events trigger the layout recalculations. // Use the factory to create the listener, this way we don’t // have to consider memory management SECLayoutWndListener* pListener=m_LayoutFactory.CreateLayoutWndListener(); // Use the AutoInit function to bridge the gap between window // and layout pListener->AutoInit(pGrid,this); Both SECLNScale and SECLNGrid are derived from a common class—SECLayoutNode. Chapter 23 Layout Manager Framework 389 23.7 Layout Manager Examples The following examples demonstrate integration of the Layout Manager. For more information, see the Layout demo provided with Objective Toolkit. All examples assume you have added the layout factory to your class header, as described in Section 23.6.1, “Adding Layout Management to Your Applications.”The code in the examples is to be inserted in the appropriate initialization handler (OnCreate(), OnInitDialog(), OnInitialUpdate()). 23.7.1 Scale Layout in a Dialog // Create top-level layout node SECLNScale* pScaleNode=(SECLNScale *) m_LayoutFactory.CreateNode( NODE_CLASS(SECLNScale)); // Optional: configure the top level layout node’s minimum // and maximum size. This means the dialog will never get // smaller than 150x225 or larger than 900x600 pScaleNode->SetMinMaxSize(CSize(150,225),CSize(900,600),0); // This useful factory function can iterate through all child // windows of this dialog, creating an SECLayoutNodeWnd for each, // and linking as children of the pScaleNode object. m_LayoutFactory.AutoPopulateNodeWnd(pScaleNode,this); // // // // // // // create and attach the listener. If we didn’t use the listener, we would have to manually catch WM_SIZE and WM_GETMINMAXINFO and kick off the Layout Manager as needed. It is highly recommended that you use the listener, but if you can’t, there is an alternate solution. You should check out the SECLayoutNodeWndListner message handlers and duplicate the code as needed. SECLayoutWndListener* pListener = m_LayoutFactory.CreateLayoutWndListener(); pListener->AutoInit(pScaleNode,this); 23.7.2 Relative Layout in a Dialog // Create the top-level relative layout node SECLNRelative* pRelNode=(SECLNRelative *) m_LayoutFactory.CreateNode(NODE_CLASS(SECLNRelative)); // Say we have 2 buttons, create 1 window node for each. // Set relative layout node as the parent. SECLayoutNodeWnd* pNode = m_LayoutFactory.CreateNodeWnd( IDC_BUTTON1,this,pRelNode); SECLayoutNodeWnd* pNode2 = m_LayoutFactory.CreateNodeWnd( IDC_BUTTON2,this,pRelNode); // Set the constraints, see documentation on relative layout // for more information pRelNode->SetConstraint(pNode1,REL_MOVER,pRelNode,REL_RIGHT,-20); pRelNode->SetConstraint(pNode2,REL_MOVET,pNode1,REL_BOTTOM,10); 390 // Set the window listener SECLayoutWndListener* pListener = m_LayoutFactory.CreateLayoutWndListener(); pListener->AutoInit(pScaleNode,this); 23.7.3 Grid Layout, Alignment Layout and Splitter in a Formview // Link layout in OnInitialUpdate, not OnCreate, as // child dialog controls must exist for proper attachment // Manually populate the children - we are not using // autopopulate here since we want to better customize // the layout configuration. // Layout will be a simple 2x1 grid, with the bottom grid // cell occupied by a splitter "sublayout". Note that if // zero is passed as one of the 2 grid dimensions, grid // will autogrow specified direction. SECLNGrid* pGrid = (SECLNGrid *) m_LayoutFactory.CreateNode( NODE_CLASS(SECLNGrid)); pGrid->SetGridDimensions(0,1); // n rows by 1 column // Add a simple text message in top grid cell // Use an alignment node to center the text inside the grid. SECLNAlign* pAlign = (SECLNAlign *) m_LayoutFactory.CreateNode( NODE_CLASS(SECLNAlign)); SECLayoutNode* pNode = m_LayoutFactory.CreateNodeWnd( IDC_STATIC1,this); // the alignment node controls the static window node pAlign->AddLayoutNode(pNode); pAlign->SetAlignment(SECLAYOUT_ALIGN_CENTER_HORZ | SECLAYOUT_ALIGN_CENTER_VERT); // first grid cell, row 0, col 0 pGrid->AddLayoutNode(pAlign); // Add the splitter node to the bottom grid cell. // The splitter node will in turn have 3 window node // children (2 buttons and a MLE) SECLayoutNodeSplitter* pSplitNode = (SECLayoutNodeSplitter *) m_LayoutFactory.CreateNode( NODE_CLASS(SECLayoutNodeSplitter)); pSplitNode->Create(this); pSplitNode->SetSplitterFlags(SEC_SPLITTER_REALTIME_DRAG); pGrid->AddLayoutNode(pSplitNode); // 2nd grid cell, row 1, col 0 // Set the 2 push buttons from the dialog template as separate // splitter children pNode=m_LayoutFactory.CreateNodeWnd(IDC_BUTTON1,this); pSplitNode->AddPane(0,0,pNode); // splitter pane row 0, col 0 pNode=m_LayoutFactory.CreateNodeWnd(IDC_BUTTON2,this); pSplitNode->AddPane(1,0,pNode); // splitter pane row 1, col 0 pNode=m_LayoutFactory.CreateNodeWnd(IDC_EDIT1,this); Chapter 23 Layout Manager Framework 391 pSplitNode->AddPane(0,1,pNode); // splitter pane row 0, col 1 // link a layout listener for auto-sizing SECLayoutWndListener* pListener=m_LayoutFactory.CreateLayoutWndListener(); pListener->AutoInit(pGrid,this); Both SECLNScale and SECLNGrid are derived from a common class—SECLayoutNode. 23.7.4 To specify min/max sizes for layout nodes Call the SetMinMaxSize() method on the node. For example: // the dialog will never get smaller than 150x225 // or larger than 900x600 pScaleNode->SetMinMaxSize(CSize(150,225),CSize(900,600),0); 23.7.5 To implement a custom layout manager 1. Derive a class from SECLayoutNode. 2. Override the OnRecalcLayout() method. A node’s parent calls this method whenever a size/position recalculation is required. 3. In your override, reposition your custom node’s children as you see fit, issuing a call to RecalcLayout() on each node. Any of the provided layout algorithms can be used as a model for this customization. 23.8 Layout Manager Sample The Objective Toolkit Layout sample in the samples\toolkit\MFC\layout subdirectory demonstrates the use of the Layout Manager classes. 392 Chapter 24 Microsoft Agent Extensions 24.1 Overview Objective Toolkit includes MFC extensions to the basic Microsoft Agent ActiveX components, providing additional features and simplifying their incorporation into your MFC applications. These extensions provide Agent-enabled Dynamic Data Validation, Tooltips, Message Boxes, and Tip of the Day support in your MFC applications, as well as the basic Agent features. They also provide a framework with a simple API to initialize the AgentServer, load/unload a default agent character with a default notification sink, and create Acts associated with ActIDs. These Acts can be categorized as critical and non-critical; the former gaining priority over the latter when both occur simultaneously. The Act framework is described in more detail in the SECAgentCharAct class description. 24.2 Overview of Microsoft Agent Technology Microsoft Agent is a set of programmable software services that supports the presentation of interactive animated characters within the Microsoft Windows interface. Developers can use characters as interactive assistants to introduce, guide, entertain, or otherwise enhance their web pages or applications in addition to the conventional use of windows, menus and controls. Microsoft Agent is deprecated as of Windows 7, and may be unavailable in subsequent versions of Windows. You can download the SDK and get more information from http://msdn.microsoft.com/enus/library/ms695784(VS.85).aspx. The Objective Toolkit extensions work with Microsoft Agent version 2.0 or later. Chapter 24 Microsoft Agent Extensions 393 24.3 Agent Extension Classes 24.3.1 SECAgentCharacterExPtr The SECAgentCharacterExPtr class derives from the IAgentCharacterExPtr smart pointer, which is created by running #import on the AgentSvr.exe containing the Agent components’ type library. This is done within the Objective Toolkit headers, and the user does not have to worry about #importing the type-library manually. However, be sure to add a path to AgentSvr.exe in the Visual Studio Libraries variable to compile the Objective Toolkit libraries properly. You can create an SECAgentCharacterExPtr object by calling IAgentApp::CreateChar(), or initialize it just as you would an IAgentCharacterExPtr smart pointer.This class creates and registers a default notification sink for the underlying character. This class creates a command, "Change Default Character", which can be enabled programmatically. Using this command, users can change the underlying character at any time. When users invoke this command, it brings up a dialog with a combo-box allowing the user to chose a character out of the available characters in the system, previewing the chosen character. The class provides APIs that instruct the underlying character to prompt at a Window or Rectangle. It has a MessageBox API with which you can display a message box accompanied by the Agent reading out the text from it. It also provides an elaborate framework to enclose character actions within Act objects. Take a look at SECAgentCharAct for more information. 24.3.2 IAgentApp Deriving your CWinApp object from IAgentApp instantly hooks a lot of default Agent functionality into your application. Take a look at the SECAgentApp template for more information. SECAgentApp exposes simple APIs to initialize the AgentServer, creates and maintains a default agent character (an SECAgentCharacterExPtr object), provides agent support in Dynamic Data Validation (DDV) of forms and dialogs in the application, provides support for Tip of the Day and more. You need not necessarily derive your CWinApp class from IAgentApp. You could create an instance of IAgentApp anywhere in your application. If you do this, part of the DDV support implemented in the SECAgentApp template will have to be reproduced for proper agent-enabled DDV functionality. Also, you need not have an IAgentApp interface in your application to work with SECAgentCharacterExPtr. 24.3.3 SECAgentApp Derive your application class from SECAgentApp, passing in CWinApp as the template variable. This will provide agent-enabled Dynamic Data Validation in your forms and dialogs, besides providing the other features implemented in IAgentApp. 394 24.3.4 SECAgentCharAct SECAgentCharAct is an abstract base class from which all Act objects are derived. This class provides the basic implementation for the Act object. You can create custom Acts by deriving from this abstract class. An important drawback of the Microsoft Agent architecture is that the function calls that initiate an animation are asynchronous, hence there is no easy way of knowing which animation is currently being played. Furthermore, the programmers have to deal with low-level animations rather than high-level Acts (sequence of animations). The Objective Toolkit framework, allows an animation sequence on a Character (an Act) within an SECAgentCharAct object to be represented by an ActID. Acts are categorized as Critical or NonCritical. Using the ActID, the corresponding Act can be interrupted any time. Also, the framework provides high priority to the Critical Acts by interrupting any current/pending NonCritical Acts. The classification of Acts into Critical/NonCritical, together with the ability to interrupt the current Acts, provides a powerful way of handling the agent character for demonstrative and instructive purposes. For example, if the character is in the middle of providing a tooltip hint and the user triggers Dynamic Data Validation—causing a Message Prompt via the agent, then the agent should immediately abandon the tooltip task and move on to the DDV Prompt message task. This is possible only if the Acts are finely demarcated and prioritized, as in this framework. A few implementations of SECAgentCharAct that are provided are SECAgentPromptAtWndAct, SECAgentPromptAtRectAct, and SECAgentSpeakAct. 24.3.5 SECAgentNotifySink SECAgentNotifySink is the default sink for an SECAgentCharacterExPtr object. This class listens for RequestComplete(), RequestStart() and Command() notifications. You can derive from this class and listen for more notifications or modify the default behavior by overriding the corresponding virtuals. You can attach your custom sink object to an SECAgentCharacterExPtr object via its m_pNotifySink member. Chapter 24 Microsoft Agent Extensions 395 24.4 Using the Agent Extension Classes In Your Applications 1. Build the Objective Toolkit libraries with the Microsoft Agent Extensions option enabled in the Build Wizard. 2. Derive your application object from SECAgentApp, as follows: class CAgent1App : public SECAgentApp<CWinApp> 3. In your application’s InitInstance(), call LoadAgent() followed by AttachChar() to create a default character for your application. You can access the default character from anywhere in your application via the m_pptrChar member in IAgentApp. 4. For Agent-enabled Dynamic Data Validation, in your overridden DoDataExchange() in your formview or dialog, call IAgentApp* pAgentApp = dynamic_cast<IAgentApp*>(AfxGetApp()); BEGIN_AGENT_DDV(pAgentApp) in the beginning and END_AGENT_DDV() at the end. 5. For tooltip support, include the DECLARE_AGENT_TOOLTIPS() macro in your parent view or window class’s header (.h) file and BEGIN_AGENT_TOOLTIPS() macro in the corresponding implementation (.cpp) file. 6. For Tip of the Day support, call EnableTipOfTheDay() and override the GetTipOfTheDayAct() virtual defined in IAgentApp. Then, a Tip will be executed whenever the user selects the “Next Tip” command. 7. You can also call the MessageBox on the character object to display a message box and make the Agent read out the text in the message box. 8. Be sure to call UnLoadAgent() in your application’s ExitInstance(). Take a look at the sample provided for more information. 24.4.1 Agent Extensions Sample The agent sample under the samples\toolkit\MFC directory demonstrates the use of the Agent Extension classes. 396 Chapter 25 Namespace Extension Wizard 25.1 Overview The Stingray Namespace Extension Wizard feature helps you to quickly create a working namespace extension project. After the Wizard generates the project, you can easily extend the generated skeleton namespace extension to a fully functional namespace extension using the classes in Objective Toolkit. 25.2 Installing Stingray Namespace Extension Wizard The Stingray Namespace Extension is one of several Objective Toolkit ATL Wizards installed during the installation process if the Objective Toolkit product is selected in the installer's feature tree. These ATL wizards are integrated into the selected and available MSVS compilers present during installation. If the Namespace Extension Wizard does not work after installation, you may need to manually register the custom property page component for the namespace extension. To install manually, run regsvr32.exe with SECNSPpg.dll as its argument. A prebuilt version of SECNSPpg.dll is provided in the <stingray-installdir>\Bin directory. Now you are ready to use the wizard. Chapter 25 Namespace Extension Wizard 397 25.3 Creating a Skeleton Namespace Extension After installing the Stingray Namespace Extension Wizard, you can start Visual Studio IDE and run the Namespace Extension Wizard to create a skeleton namespace project. A namespace extension must be an in-proc server; therefore, you must select create DLL as server type in the first page of the ATL COM AppWizard of Visual Studio. After the ATL COM AppWizard is done, use the Insert|New ATL Object menu or context menu to add our namespace object into the project. When the wizard page shows up, choose Stingray Category, select the namespace extension object, and then click Next. The property sheet in the next step contains three pages; one of the pages is our custom namespace extension page. Give your namespace extension object a name in the Names page, then select proper attributes for the namespace extension attributes. The only attribute required is the Apartment model attribute; all other selections are optional. On the Namespace page, there are several options to choose from. The meaning of each of the options is explained in Section 25.4. 25.4 Selecting Namespace Options There are several options in the Namespace page of the Stingray Namespace Extension Wizard. 25.4.1 Show Up The Show up option controls where the namespace extension will show up in the Explorer. The possible settings are: Desktop – The namespace extension will show up under the Desktop node of the Explorer. My Computer – The namespace extension will show up under the My Computer node of Explorer My Network Places – The namespace extension will show up under the My Network Places node of Explorer. 25.4.2 Register For The Register For option controls how the namespace extension object is registered. This option helps to put proper registration keys in the generated registration file of your namespace extension COM object. The possible settings are: 398 Current User – the object will be registered on a user by user basis. Local Machine – the object will be registered for the whole local machine; every user on this machine will be able to access this object. 25.4.3 Support UI Object The Support UI Object section controls which optional interface your namespace extension supports. These interfaces are returned when Explorer calls GetUIObject() method of your IShellFolder interface. For each item you check in this section, the wizard generates a corresponding implementation class in the project, putting the proper code in the GetUIObject() method to export that interface. The remaining two check boxes decide whether you want to see some more detailed comments in the generated code and whether you want the wizard to generate some sample code. If the Some sample code check box is selected, you can compile the generated code right away and get a working namespace extension you can view from the Explorer. Note that the implementation classes— SECIDropSourceImpl, SECIDropTargetImpl and SECIDataObjectImpl—contain only the IUnknown implementation and the reference-counting logic. All the implementations for other methods are not meaningful. You must provide implementations for all methods other than those of IUnknown. In general, you should also override the methods IExtractIcon() and IQueryInfo() in the SECIExtractIconImpl and SECIQueryInfoImpl classes, but it is not required. The default implementation of SECIExtractIconImpl and SECIQueryInfoImpl will work fine, but it will not always be what you want. SECIContextMenuImpl wraps IContextMenu and makes the support of IContextMenu a lot easier. Generally, you only need to add your menu item into the menu map and override the CommandHandler() method to do the menu handling. Chapter 25 Namespace Extension Wizard 399 25.5 Tutorial This tutorial demonstrates how to create a useful namespace extension using the set of classes in Objective Toolkit and Stingray namespace extension wizard. Every namespace must implement at least the IEnumIDList, IShellFolder and IShellView interfaces. Most namespaces also implement IContextMenu and IExtractIcon. Objective Toolkit has implementations for all of these classes, while the Stingray namespace extension wizard lays out the basic framework for how all these interfaces work together. Combining these two makes namespace development much easier. To develop a namespace extension, we must deal with pointers to item ID list, such as PIDL, which involves dealing with the raw memory. This is the hard part of namespace extension development. To ease this, Stingray developers created the SECPidlMgr<T> template class, which encapsulates the complication. In this tutorial, we use Objective Toolkit classes and the namespace extension wizard to create a light version of the WinView namespace extension, which was developed by Dino Esposito in his book Visual C++ Window Shell Programming. For serious shell and namespace extension development, this book is a must. As you will see, the framework created by our namespace wizard makes the development much easier. 25.5.1 Create the Skeleton Namespace Extension Project The first step is to create a skeleton namespace extension project as follows: 1. Create a DLL server type project using ATL COM AppWizard and naming the project NSExtTutor. Every namespace extension must be an in-proc server. 2. After the ATL COM AppWizard finishes, right-click the root node named NSExtTutor in the solution explorer. 3. Select Add, and click Add Class from the menu. 4. Select the Stingray category in the ATL Object Wizard, then select the Namespace extension icon in the objects list box and choose the Next button. 5. Give the namespace extension a name, NSExtComp, in the Short Name: edit box of the Names property page. 6. Select the Options page, choose the Custom option in the Interface section and No in the Aggregation section, and leave every other check box unchecked. Since we are not going to implement the interface INSExtComp, we also avoid the dual interface and aggregation support, making our extension lightweight. 7. Select the Namespace page and check the following options: Desktop in the Show up section. Current User in the Register For section. IExtractIcon and IContextMenu in the Support UI Object section. Verbose comment and Some sample code. After you click OK, the wizard will generate a working namespace extension object. 400 Now you can compile and register the namespace extension we just created. After the compilation is done, you can open Explorer and your namespace extension NSExtComp will show up under the Desktop node. Figure 167 demonstrates what it looks like. Figure 167 – Example Namespace Extension 25.5.2 Work Through the Generated Code The Wizard generates five classes for our namespace extension: CNSExtComp, CNSExtCompEnum, CNSExtCompIcon, CNSExtCompMenu, and CNSExtCompView. CNSExtComp is the main class of our namespace extension. It implements the IShellFolder interface and handles the registration for our namespace extension. CNSExtCompEnum implements the IEnumIDList interface. CNSExtCompIcon implements the IExtractIcon interface. CNSExtCompMenu implements the IContextMenu interface—making the menu addition and handling extremely easy. CNSExtCompView implements the IShellView interface. As we mentioned before, only CNSExtComp, CNSExtCompEnum and CNSExtCompView are required for a namespace extension. As documented in the shell SDK, the Explorer starts to interact with our namespace extension COM object through the IShellFolder interface. Through this interface, Explorer obtains IEnumIDList and IShellView interfaces. The relationship of IEnumIDList to Chapter 25 Namespace Extension Wizard 401 IShellView in the namespace extension is very similar to that of CDocument to CView in standard doc/view application, with IEnumIDList behaving as a kind of document and IShellView behaving as a view. Do not try to implement required interfaces or optional interfaces inside a single class; it will not work properly. The CNSExtCompEnum class extends the SECIEnumIDListImpl template class and implements the CreateEnumIDList() method. The first step in extending the skeleton namespace extension is to add a meaningful implementation for this virtual function. The base class has a variable, m_pPidlMgr, that is a pointer to our PIDL manager class. The PIDL manager is derived from SECPidlMgr template class. All the PIDL should be created and freed using the PIDL manager; the PIDL manager in turn uses the IMallc interface from the shell to manage the memory. This class is responsible for creating the PIDL list for the namespace extension. The framework requires that our CNSExtCompEnum class have a default constructor. When Explorer asks IShellFolder for IEnumIDList interface, the framework creates a new instance of IEnumIDList implementation class, and immediately calls CreateEnumIDList() once, passing the PIDL and some flags to this functions, and then returns the interface IEnumIDList to Explorer if the CreateEnumIDList() has succeeded. The CNSExtCompView class extends the SECIShellViewImpl template class. This class must have a constructor with two arguments; the first argument is a pointer to the IShellFolder interface and the second argument is a pointer to PIDL. When Explorer calls the CreateViewObject() method of IShellFolder interface, the framework creates a new instance of our IShellView implementation class with the IShellFolder pointer and the PIDL, then returns the interface to Explorer if everything is all right. CNSExtCompView is responsible for providing a view window to the Explorer shell, so it is derived from CWindowImpl class as well. Generally, we should handle the WM_CREATE() and WM_SIZE() messages to create a child window, such as a list view to display our folder contents, and resize the child window. Then we can pass all other messages to the base class for further processing. The example CNSExtCompView class uses a CListView as child window; this window is held in the m_listView member. You can use any other kind of child window. The base class SECIShellViewImpl saves the argument IShellFolder into a smart pointer and makes a copy of the argument PIDL. The CNSExtComp class simplifies registration by extending the SECIShellFolderImpl class and using the ATL COM object implementation. Our SECIShellFolderImpl class takes four template arguments. The first one is our final implementation class, the second is our IShellView implementation class, the third is our IEnumIDList implementation class, and the last one is our PIDL manager class. Since we chose to support IExtractIcon and IContextMenu when we run the namespace extension wizard, the wizard added some code into the GetUIObject() method to inform the caller(i.e. Explorer) that we support these optional interfaces. Since we have chosen to support two optional interfaces, IContextMenu and IExtractIcon, the wizard also generated classes for each of these two interface implementations. CNSExtCompIcon basically just initializes its base class; CNSExtCompMenu contains the command map definition and some sample code demonstrating adding menu items to the context menu and adding a menu command. The command map cannot be removed, even you don’t have any command items inside. If we had chosen to support other optional interfaces, the wizard would have generated the corresponding implementation classes. 402 25.5.3 Change The Data Structure For PIDL Now let us begin to make our skeleton namespace extension functional. First, we have to decide what data to put into the PIDL. Since Explorer doesn’t know anything of our data, it just passes the data around as PIDL. We must therefore package our data completely into a PIDL. The data in the PIDL cannot be pointers that point to some other data outside the PIDL. For this tutorial, we will use the following structure. struct CMyData { HWND hWnd; }; Go to NSExtComp.h and change the following structure into our new data structure. struct YourDataStruct { DWORD data; }; After changing the data structure, you must also make two other changes. One of them is the #define immediately following the structure. typedef YourDataStruct SECPidlData; You should change the above line to the following: typedef CMyData SECPidlData; The second change is in the CreateEnumIDList() function of CNSExtCompEnum class. You should remove the code that uses the old data structure. 25.5.4 Implementing CreateEnumIDList() Since we are going to enumerate windows, we need a helper data structure for the enumeration. Let’s add the following global data structure at the top of NSExtComp.h. struct ENUMHWND { LPARAM lParam; HWND hwndParent; DWORD dwFlags; }; After that, we need to change the implementation of CreateEnumIDList(). The new implementation should be similar to the following. virtual BOOL CreateEnumIDList(LPCITEMIDLIST pidl, DWORD dwFlags) { HWND hWnd = NULL; if( pidl != NULL ) { //The GetLastDataPointer method of // SECPidlMgr<SCEPidlData> will //return a pointer to our data structure SECPidlData. Chapter 25 Namespace Extension Wizard 403 hWnd = m_pPidlMgr->GetLastDataPointer(pidl)->hWnd; } if( hWnd == NULL ) { // We start with the desktop window SECPidlData data; data.hWnd = GetDesktopWindow(); AddToEnumList(m_pPidlMgr->CreateItem ( (LPBYTE)&data, sizeof(data)) ); //Initialize the current iterator m_iterCurrent = m_idlList.begin(); //Return TRUE if succeeded, otherwise return FALSE. return TRUE; } ENUMHWND ew; ew.lParam = (LPARAM)this; ew.hwndParent = hWnd; ew.dwFlags = dwFlags; ::EnumChildWindows(hWnd, CreateEnumIDListHelper, (LPARAM)&ew); //Intialize the current iterator m_iterCurrent = m_idlList.begin(); //Return TRUE if succeeded, otherwise return FALSE. return TRUE; } One important point to notice is that when you use the CreateItem() function of SECPidlMgr class, you need to package the data into an SECPidlData structure and pass the structure to CreateItem() function together with the actual size of the data. If you look at the implementation of CreateItem(), you will see that it copies the block of data passed into it. To complete the implementation of CreateEnumIDList()), we need another static member function for the EnumChildWindow() method. The implementation of CreateEnumIDListHelper() follows. This method uses CreateItem() exactly as in the CreateEnumIDList() method. static BOOL CALLBACK CreateEnumIDListHelper(HWND hwndChild, LPARAM lParam) { ENUMHWND* pew = (ENUMHWND*)(lParam); HWND h = ::GetParent(hwndChild); if( h != NULL && (h != pew->hwndParent) ) return TRUE; CNSExtCompEnum* pEnumIDList = (CNSExtCompEnum*)(pew->lParam); // Explorer wants non-folder items if(pew->dwFlags & SHCONTF_NONFOLDERS) { SECPidlData data; data.hWnd = hwndChild; 404 pEnumIDList->AddToEnumList( pEnumIDList->m_pPidlMgr->CreateItem((LPBYTE)&data, sizeof(data)) ); return TRUE; } // Explorer wants folder items if(pew->dwFlags & SHCONTF_FOLDERS) { // If it has no children, drop it because it has // already been added. if(::GetWindow(hwndChild, GW_CHILD) == NULL) return TRUE; else { SECPidlData data; data.hWnd = hwndChild; pEnumIDList->AddToEnumList( pEnumIDList->m_pPidlMgr->CreateItem((LPBYTE)&data, sizeof(data)) ); } } return TRUE; } The last change we need to make is in the InitList() method of CNSExtCompView class. The old data structure is used in this method. 25.5.5 Modify CNSExtCompView::InitList() Let’s first make the simplest change to this method by changing one line of code to use our new data structure instead of the old one. The change is in bold text. void InitList() { // large portion of code omitted here spEnumIDList->Reset(); while((spEnumIDList->Next(1, &pidl, &dwFetched) == S_OK) && dwFetched) { // Add code here to add item to the listview !!! wsprintf(buf, _T("As binary: 0x%x"), m_pPidlMgr->GetLastDataPointer(pidl)->hWnd); m_listView.InsertItem(LVIF_TEXT, m_listView.GetItemCount(), buf, -1, -1, 0, NULL); } // remainder of code omitted here } After we recompile and register the namespace, it will look something like Figure 168 in the Explorer. Chapter 25 Namespace Extension Wizard 405 Figure 168 – Recompiled and Registered Namespace Notice that a context menu for the node appears in the left pane of Explorer, because we chose to support context menus when we ran the wizard. The wizard generated two sample context menu items for us. The default handler displays the menu item ID in a message box. If you want the menu command to do meaningful things, you can override the CommandHandler() method of SECIContextMenuImpl class in the CNSExtCompMenu class; see the sample code shown inside the comment of this class. As you can see, the SECIContextMenuImpl class hides all the complications of context menu implementation. All you have to do is to add the menu item into the existing command map. You give the menu item text, help string, and the menu item ID, and then override the CommandHandler() method to look for your menu ID to decide what to do. It is really simple and easy. Please note that the context menu on the right pane is the responsibility of CNSExtCompView. You have to handle the WM_CONTEXTMENU() message and draw the context menu. 406 Figure 169 – Namespace With Context Menu To make it more interesting, perform the following modification to the InitList() method. void InitList() { // Empty the list view m_listView.DeleteAllItems(); m_listView.InsertColumn(0, _T("State"), LVCFMT_LEFT | LVCF_TEXT, 100, -1); m_listView.InsertColumn(1, _T("Class Name"), LVCFMT_LEFT | LVCF_TEXT, 100, -1); m_listView.InsertColumn(2, _T("HWND"), LVCFMT_LEFT | LVCF_TEXT, 100, -1); m_listView.InsertColumn(3, _T("Title"), LVCFMT_LEFT | LVCF_TEXT, 100, -1); CComPtr<IEnumIDList> spEnumIDList; // Here we call this function to get an IEnumIDList interface. // This function call will call CreateEnumIDList to create the //EnumIDList form the content of the m_pPidl !!! HRESULT hr = m_spShellFolder->EnumObjects(m_hWnd, SHCONTF_NONFOLDERS | SHCONTF_FOLDERS, &spEnumIDList); if(SUCCEEDED(hr)) { LPITEMIDLIST pidl = NULL; // Stop redrawing to avoid flickering m_listView.SetRedraw(FALSE); Chapter 25 Namespace Extension Wizard 407 TCHAR buf[100] = {0}; // Add items DWORD dwFetched; //First reset the Enum spEnumIDList->Reset(); while((spEnumIDList->Next(1, &pidl, &dwFetched) == S_OK) && dwFetched) { HWND h = m_pPidlMgr->GetLastDataPointer(pidl)->hWnd; // Column 1: state // If has children if( ::GetWindow(h, GW_CHILD) != NULL ) _tcsncpy(buf, _T("[Parent]"), 100); else _tcsncpy(buf, _T("No Children"), 100); int i = m_listView.InsertItem(LVIF_TEXT, m_listView.GetItemCount(), buf, -1, -1, -1, NULL); // Fill the subitem 2: HWND TCHAR szBuf[MAX_PATH] = {0}; wsprintf(szBuf, _T("0x%04X"), h); m_listView.SetItemText(i, 2, szBuf); // Fill the subitem 3: Title ::GetWindowText(h, szBuf, MAX_PATH); m_listView.SetItemText(i, 3, szBuf); // Fill the subitem 1: Class ::GetClassName(h, szBuf, MAX_PATH); m_listView.SetItemText(i, 1, szBuf); } // Redraw the list view m_listView.SetRedraw(); m_listView.Invalidate(); m_listView.UpdateWindow(); // Dont't call spEnumIDList->Release() here, // spEnumIDList will automatically // released when it go out of this scope!!! } } Now if we recompile and register the namespace extension, it looks something similar to the following image in the Explorer. 408 Figure 170 – Namespace Extension After Recompilation 25.5.6 Give Each Node an Informative Name The node name in the left pane of Explorer should be more informative. To correct this, we need to override the GetPidlName() method of SECIShellFolderImpl() method in the CNSExtComp class. The default implementation of this function displays the first byte data of the PIDL corresponding to the node as a binary. Let’s override GetPidlName() in CNSExtComp and change the code to the following. virtual void GetPidlName(LPCITEMIDLIST pidl, LPTSTR lpszOut, DWORD nSize) { HWND hwnd = NULL; if( pidl != NULL ) hwnd = m_pPidlMgr->GetLastDataPointer(pidl)->hWnd; if( hwnd == NULL ) hwnd = GetDesktopWindow(); TCHAR szClass[100] = {0}, szTitle[100] = {0}; ::GetWindowText(hwnd, szTitle, 100); ::GetClassName(hwnd, szClass, 100); // Add a description to the desktop window (of class "#32769") if(!_tcscmp(szClass, _T("#32769"))) _tcscpy(szClass, _T("Desktop")); Chapter 25 Namespace Extension Wizard 409 // Return a string in the form "title [class]" if(_tcslen(szTitle)) { TCHAR buf[210] = {0}; _stprintf(buf, _T("%s [%s]"), szTitle, szClass); _tcsncpy(lpszOut, buf, nSize); } else { _stprintf(szTitle, _T("[%s]"), szClass); _tcsncpy(lpszOut, szTitle, nSize); } } Since we know the data in each item is a window handle, we should display the window’s class name, or, if it has a title, the combination of class name and title. If the handle is NULL, it means we are at the top node, which we know it is the desktop window. After we recompile, the namespace looks like Figure 171. Figure 171 – Renamed Namespace 25.5.7 Change the Default Context Menu Handling As we saw before, the context menu didn’t do anything other than display the corresponding menu item ID. In order to make the context menu useful, we have to override the CommandHandler() function. Let’s uncomment the CommandHandler() method in the 410 CNSExtCompMenu class generated by our wizard. Assume we want to display the information about the node the mouse right-clicked on. For this tutorial, we want to display the Windows class name and the window handle as binary data. In order for our context menu to display information about the clicked node, we need to save the PIDL for the node passed to us by Explorer when it requested the IContextMenu interface in the GetUIObject() method of IShellFolder. The PIDL is available from our class CNSExtCompMenu in the GetUIObject() method. GetUIObject() also saves the owner window handle into our class. This window handle can be used when we want to display a dialog box or message box inside our CNSExtCompMenu implementation code. The implementation of CommandHandler() is listed below. virtual void CommandHandler(HWND hwndOwner, UINT nID) { if( nID == ID_SAMPLE_COMMAND || nID == ID_SAMPLE_COMMAND1) { TCHAR buf[MAX_PATH] = {0}; GetPidlName(m_pPidl, buf, MAX_PATH); TCHAR buf1[100] = {0}; HWND hWnd = NULL; if( m_pPidl == NULL ) hWnd = GetDesktopWindow(); else hWnd = m_pPidlMgr->GetLastDataPointer(m_pPidl)->hWnd; stprintf(buf1, _T("\n\nWindow handle = 0x%x"), (DWORD)hWnd); int len = _tcslen(buf); _tcsncpy(buf + len, buf1, MAX_PATH - len); TCHAR szTitle[100] = {0}; _stprintf(szTitle, _T("Sample command %d selected"), nID==ID_SAMPLE_COMMAND ? 1 : 2); MessageBox(hwndOwner, buf, szTitle, MB_OK | MB_ICONINFORMATION); } } This function makes uses the GetPidlName() method, which we can copy from CNSExtComp. The m_pPidlmember is in our base class. Now when you right-click any node and select one of the two menu items, the message box will display the relevant information for this node. The context menu is shown in Figure 172. Chapter 25 Namespace Extension Wizard 411 Figure 172 – Namespace With Context Menu Handling 25.5.8 Give Node An Icon The last thing we are going to do is to give the node our own icon. The default implementation of SECIExtractIconImpl gives every node a small Windows logo icon. To provide a different icon for the node on the left pane, we have to override the Extract() function of IExtractIcon interface. First, add a small icon into our project and name the icon ID to IDI_ICONNODE, as shown in Figure 173. Figure 173 – Icon for ID IDI_ICONNODE Then we change the default implementation of Extract() method in CNSExtCompIcon class to the following: STDMETHOD (Extract)(LPCTSTR pszFile, UINT nIconIndex, HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize) { *phiconLarge = LoadIcon(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDI_ICONNODE)); *phiconSmall = LoadIcon(_Module.GetResourceInstance(), 412 The completed namespace is shown in Figure 174. Figure 174 – Namespace With Icon That’s all for this tutorial. You can add more features to the namespace extension, such as modifying the menu of Explorer, adding toolbars into Explorer, and displaying status text. We strongly recommend the excellent reference Visual C++ Windows Shell Programming by Dino Esposito, published by Wrox Press, as well as the MSDN. Chapter 25 Namespace Extension Wizard 413 414 Chapter 26 The Hyperlink Classes 26.1 Overview The Objective Toolkit hyperlink classes provide a convenient way to add point-and-click navigation to conventional non-browser enabled applications. These classes, SECHyperlink and SECRichHyperlink, derive from ATL's CWindowImpl base and can be added to almost any ATL/MFC application. SECHyperlink is a lightweight implementation and provides a single static line containing the hyperlink. SECRichHyperlink, on the other hand, superclasses the Win32 rich edit control, thus providing enhanced hyperlink display options, including a delimiter option for hot-text areas. The SECHyperlink and SECRichHyperlink classes may be used for providing navigation links to standard web resources (http://www.roguewave.com), e-mail resources (mailto:[email protected]), FTP resources, and registered document types (*.doc, *.xls). To invoke the link, SECHyperlink and SECRichHyperlink use the Win32::ShellExecute() function, and hence usage of these components is constrained by the limitations of the API call. Figure 175 – Example Hyperlink Dialog Chapter 26 The Hyperlink Classes 415 26.2 Using the Hyperlink Classes ATL applications can directly use the hyperlink classes. Non-ATL-enabled MFC applications, however, must include the ATL base headers. The following steps show you how to add the requisite ATL support to an MFC application. 1. Include the following lines in your stdafx.h header file. #include <atlbase.h> extern CComModule _Module; #include <atlcom.h> #include <atlwin.h> 2. Within your application file (for example, MyApp.cpp) define an instance of CComModule at global scope. CComModule _Module; 3. Within the application’s InitInstance() function, initialize the ATL COM module using the CComModule::Init() method. _Module.Init(NULL, AfxGetInstanceHandle()); 4. When using the SECRichHyperlink class, MFC applications should initialize the rich edit control at run time. This can be done by calling the AfxInitRichEdit() MFC API from within your InitInstance() function. Now that the requisite ATL support has been added, the following implementation steps are identical for both ATL and MFC applications. 5. Add an SECHyperlink or SECRichHyperlink data member to the parent class that is expected to house the hyperlink. // Add an instance of the SECHyperlink class SECHyperlink m_Hyperlink; 6. When using the hyperlink control in a dialog, it is possible to substitute an existing placeholder with the hyperlink using the SECHyperlink::AttachHyperlink() method. As an alternative, or in a non-dialog scenario, you can also use the SECHyperlink::Create() function to create the control. This can be done from within the WM_INITDIALOG handler or the WM_CREATE handler in the case of a conventional CWnd/CWindow. // IDC_STATIC_RICHLINK is the placeholder control m_richHyperlink.AttachHyperlink(m_hWnd, IDC_STATIC_RICHLINK); 7. Initialize the control with the hyperlink text as well as the user-friendly display text. The SECHyperlink::SetHyperlink() and SECHyperlink::SetDisplayText() methods can be used for this purpose. m_hyperlink.SetDisplayText("My Hyperlink text"); m_hyperlink.SetHyperlink("http://www.roguewave.com"); 8. A hot-text delimiter can be specified for the rich-text version of the control by enclosing the hot-text within <> tags. m_richHyperlink.SetDisplayText( "This is my <status report>"); 416 m_richHyperlink.SetHyperlink("D:\\Reports\\Status1.doc"); 9. If you choose, the hyperlink control can automatically resize itself to fit the text when you invoke the SECHyperlink::SizeToText() method with a TRUE parameter. 26.3 Customizing the Hyperlink Control The visual attributes of the control such as the display text color, font, cursor, and so on, can be set using methods implemented by the SECHyperlink and SECRichHyperlink classes. The following excerpt illustrates using some of these functions. // Sets the display text color for a link that // is yet to be accessed. m_richHyperlink.SetClrDispTextNormal(RGB(0,255,0)); // Sets the color for the hot-text portion of the display text. m_richHyperlink.SetClrDispTextHotTrack(RGB(255,0,0)); // Sets the color for a link that has been visited. if( m_richHyperlink.GetClrDispTextVisited() != RGB(0,125,125) ) m_richHyperlink.SetClrDispTextVisited(RGB(0,125,125)); // Sets the cursor displayed when the mouse is over the // hyperlink text. HCURSOR hCurArrow = LoadCursor(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDC_CUR_ARROW)); m_richHyperlink.SetUserCursor(hCurArrow); 26.4 Sample The hyperlink sample (...\Samples\Toolkit\ATL\Hyperlink) demonstrates the use of the SECHyperlink and SECRichHyperlink classes. Chapter 26 The Hyperlink Classes 417 418 Chapter 27 Web Browser Extensions 27.1 Overview Objective Toolkit‘s Web browser extensions provide utility functions and a COM Server handy for working with DHTML interfaces in C++. The utilities are provided as a COM Server (under Studio\COMservers\Toolkit\WBUtils) and via functions declared in Include\Toolkit\WBExt\WBUtilFuncs.h. 27.2 Feature List 27.2.1 Getting the IWebBrowser2 Interface in IE The Microsoft-recommended way to hook into Internet Explorer (IE) and get hold of the IWebBrowser2 control in an instance of IE is to write a Browser Helper Object (BHO). However, BHOs are cumbersome to deal with in that they need to reside in their own DLLs— exposing a specified interface— and they are always loaded with IE, whether needed or not. We provide two ways to hook into IE and get hold of the underlying IWebBrowser2 interface on an as-needed basis. 27.2.1.1IWebBrowser2 from a NEW IE instance The WBUtils COM server we provide (under Studio\COMservers\Toolkit\WBUtils) with source code provides you an interface through which you can ask it to create a new instance of IE and provides you with an IWebBrowser2 interface of the underlying Web browser control (asynchronously via an IDispatch). Take a look at the IWBUtilFuncs::CreateNewIEInstance() function in the COM Server. Chapter 27 Web Browser Extensions 419 27.2.1.2Grab IE under HWND You could instead get hold of the IWebBrowser2 interface in IE (only in IE) under a specified screen point (or the HWND under that point). The WalkAndExamineIEBrowsers() function uses ShellWindows to walk through the collection of IE windows (as described in MSDN Knowledge Base article Q176792) and determines the browser control associated with the specified HWND. Also, as mentioned in the above article, the limitations with this approach hold true. You can search for article Q176792 in the Microsoft Knowledge Base at: http://search.support.microsoft.com/kb/c.asp. 27.2.2 Init IWebBrowser2 with HTML in Memory There is no easy way to initialize a Web browser control via HTML in memory. The LoadHTML() method in the COM Server interface is one way. Another way is the LoadWBViaIPersist() utility function, which, as the name implies, uses the IPersistStreamInit interface to load the control with HTML. 27.2.3 Retrieve HTML in IWebBrowser2 We also provide three different ways to save the HTML displayed in the browser control to a file. As mentioned below, each of them retrieves a different source associated with the control. The three different utility functions are: SaveWBViaCache()— Will save the original HTML source stored in the local cache. SaveWBViaIPersistFile()— Will save the resultant HTML (scripts expanded) as rendered by the Web browser control. SaveWBViaDOM()— Will save the HTML with the latest changes applied to the document via the DOM. You could also use SaveWBViaIPersist() to retrieve the HTML as a BSTR. (This is the equivalent of SaveWBViaIPersistFile(), described above). The COM Server exposes two functions (SaveHTML() and SaveToFile()), which in turn use the above functions to retrieve the HTML into memory and file, respectively. 27.2.4 CHTMLView Extensions SECHTMLView, deriving from CHTMLView, provides: 420 A IDocHostUIHandler implementation to listen for the corresponding events and the Document events. DDX-like macros for bi-directional transfer between client data structures and Web browser control element properties. A utility class to simplify finding elements by id/tagName. 27.2.5 Miscellaneous Utility Functions Other simple utility functions and classes are declared in include\toolkit\WBExt\WBUtilFuncs.h, which you could use while working with the IWebBrowser2 interface. 27.3 Sample For more information, take a look at WBExtSample (...\Samples\Toolkit\MFC\), which utilizes the above COM Server and utility functions. Chapter 27 Web Browser Extensions 421 422 Chapter 28 The APP ATL Object 28.1 Overview The Objective Toolkit Asynchronous Pluggable Protocol (referred to as APP) ATL Object allows you to add a COM object that implements the IInternetProtocol and IInternetProtocolInfo interfaces required for implementing any custom APP. The ATL Object Wizard provides a very convenient way to insert the Objective Toolkit APP ATL Object into any ATL project. The inserted object provides default features like parsing and validating the supplied URL, spawning a worker thread to perform asynchronous data fetch, and the ability to abort the operation gracefully. The COM object contains an implementation object to which it delegates all of the function calls. Subclassing the implementation object will provide you virtual notifications and allow you to customize the default behavior. 28.1.1 Overview of Asynchronous Pluggable Protocol Pluggable protocol handlers can be used to handle a custom Uniform Resource Locator (URL) protocol scheme or to filter data for a designated MIME type. The ability to handle a custom URL protocol scheme using a pluggable protocol handler gives developers the ability to implement new or custom protocol schemes for Internet Explorer 4.0 (and later) and for applications using URL monikers. Existing protocol schemes, such as HTTP and FTP, are handled by the default pluggable protocol handler included with Internet Explorer. For more information take a look at the documentation available at MSDN (http://msdn.microsoft.com). Chapter 28 The APP ATL Object 423 28.2 Objective Toolkit APP ATL Object Classes The ATL Object Wizard inserts the following classes into your project when you insert an Objective Toolkit APP ATL Object. 28.2.1 SECPlugProt This class contains the core implementation of IInternetProtocol and IInternetProtocolInfo. It uses ATL COM to implement IUnknown and other required interfaces. It contains an instance of and delegates all of its function calls to SECPlugProtImp. 28.2.2 SECPlugProtImp This implementation class parses and validates the URL and spawns a new worker thread to perform the fetch operation asynchronously. It also contains code to abort the worker thread gracefully. The Worker Thread and the implementation object share a FileDownloadInfo object through which they share the asynchronous data download information. You should derive a class from SECPlugProtImp to customize the default behavior and hook it into the SECPlugProt implementation. 28.2.3 FileDownloadInfo This object, shared between the Worker Thread and the SECPlugProtImp object, allows access to information stored in its members regarding the data fetched so far, remaining data to be fetched and the actual fetched data, all in a thread-safe manner. 28.2.4 SECWorkerThreadFetchObject The actual data fetch operation in a worker thread is performed within an instance of this interface. The interface for this class is designed to facilitate the worker thread’s logic of fetching the data in blocks and transferring control intermittently to the main thread using Switch/Continue. The wizard-inserted Worker Thread code should be completed by hooking in a custom instance of SECWorkerThreadFetchObject with the worker thread. 28.2.5 WorkerThreadMain This class is the default worker thread control function. It initializes an instance of SECWorkerThreadFetchObject and instructs it to fetch data in blocks. It also communicates with the main thread to transfer control intermittently (using Switch/Continue), to inform it regarding the thread completion, and to check if it should abort the operation before completion. 424 28.3 Using the Objective Toolkit APP ATL Object in Your Applications From inside your existing ATL project, open the Insert|New ATL Object… dialog in Visual Studio. Select the Objective Toolkit APP Object from the Stingray category. If this object is not available as an option, it is probably not registered properly. See the readme notes under the Utils\Toolkit\Template\AsyPlgProt directory. Supply the necessary arguments and run the wizard to completion. This will insert the appropriate files and modify the IDL appropriately. The inserted files provide a default implementation of IInternetProtocol, IInternetProtocolRoot, and IInternetProtocolInfo. Before compiling the project, you have to provide an implementation of the SECWorkerThreadFetchObject class. And, if necessary, subclass SECPlugProtImp as well. Take a look at the app sample for pointers. 28.4 Sample The app sample in the Samples\Toolkit\MFC directory demonstrates the use of this ATL Object. Take a look at the read1st.txt notes in the sample folder for more information. Chapter 28 The APP ATL Object 425 426 Chapter29 ATL and Objective Toolkit Components 29.1 Overview Objective Toolkit components, like those from any other MFC extension library, can be used in an Active Template Library (ATL) application that has MFC support enabled. However, using Objective Toolkit components in ATL applications easily, effectively, and extensibly requires attention to several issues. These issues are not specific to Objective Toolkit; in fact, they apply to any other MFC extension library as well. To address them, we have provided a set of custom ATL Object Wizards and some sample code that will produce ATL ActiveX controls that wrap Objective Toolkit components right out of the box. In the following sections, we illustrate how to export Objective Toolkit component functionality (through the ATL ActiveX controls you develop) to the users of your controls. Chapter 29 ATL and Objective Toolkit Components 427 29.2 Wrapping Objective Toolkit Components in an ATL ActiveX Control ATL is usually the first choice for writing an ActiveX wrapper for any C++ code. It is very powerful when it comes to implementing COM functionality; it makes it very easy to implement dual interfaces. MFC, on the other hand, has somewhat convoluted COM support. While MFC starts out being very easy to use, more often than not, it ends up being rather difficult to maintain and extend. Also, dual interfaces are somewhat more difficult to implement with MFC than they are with ATL. Combining ATL with a rich MFC UI library like Objective Toolkit can produce an ActiveX control that is reusable, extensible, and customizable. We can easily link statically to MFC, thus eliminating the need for redistributing the MFC DLL. (MFC ActiveX controls cannot be linked statically to the MFC DLL.) One issue is that these controls will be somewhat larger than controls that are written in native Win32 without MFC. However, with the use of advanced optimization techniques and just plain clever linking, it is possible to have a lean control. Using optimization techniques it is possible to create a very basic grid ActiveX control, with ATL used for the COM implementation, for under 600k. Remember that this control will have no dependency on MFC run times. Packaged in a CAB (compressed Cabinet) file, it will be even smaller. Such a control will also be much more maintainable in the long run than a native Win32 control would be. And, of course, consider all the time that will be saved when you reuse a powerful control such as those included in Objective Toolkit instead of writing a Win32 based control from scratch. Having laid out the case for the usefulness of Objective Toolkit ATL ActiveX controls, let us look into implementation issues. The most common way to use MFC controls inside of ATL ActiveX controls is to create the MFC control window as a child of the ATL control window. This approach is useful if the MFC control is a small part of the control UI and functionality. However, in this section, we are more concerned with ActiveX controls that expose MFC based controls, with the MFC control forming the dominant part of the UI and functionality. If this is the case, this approach is not efficient as it results in the creation of two windows (one by the ATL control and one by the embedded control). Having a parent window that is essentially unused is a significant drawback, and creates several problems. Though these problems can be worked around, it is desirable to have one control window. We have come up with an easy approach that allows the usage of one window in cases such as these. This solution basically involves chaining together the MFC message maps (in turn the MFC window procedure) and the ATL message maps. We have implemented a base class that does much of this work. We have also included a set of ATL Object Wizards that can be used to generate these controls easily. You can find these wizards under the Stingray category. After you have inserted an Objective Toolkit control, you can easily add methods and properties using the ATL wizards. With this type of wizard support, writing an ATL based Objective Toolkit ActiveX control is very easy. The generated control exposes a few predefined properties and methods that you can remove as required. These are provided as sample code. 428 29.3 An Example: An ATL ActiveX Control Built From SECTreeCtrl As an illustration of the procedures involved, consider this walkthrough demonstration of building a control that wraps Objective Toolkit functionality. 1. After launching Visual Studio, start up a new project and select ATL Project from the Templates: frame. 2. Name your project acmecontrol, as shown in Figure 176. Figure 176 – New Project Window 3. Click OK and proceed to the next screen. Chapter 29 ATL and Objective Toolkit Components 429 Figure 177 – Step 1 of ATL COM AppWizard Remember to select Support MFC. This is required; if you don’t, when you attempt to insert your Objective Toolkit component as an ATL object, the Object Wizard will display an error message and will not allow you to proceed. 4. Click Finish. 5. Once your initial project files are generated, select Add Class from the Project menu. 6. Click the Stingray folder in the Categories: frame, and select Objective Toolkit Tree Control from the Templates: frame as shown in Figure 178. 430 Figure 178 – ATL Object Wizard 7. Click Open to produce the following dialog: Figure 179 – ATL Object Wizard Properties Window Chapter 29 ATL and Objective Toolkit Components 431 8. Type the short name mytreecontrol. The wizard automatically derives class names, file names, an interface name, and so forth. 9. Accept these defaults by clicking Finish. This example illustrates the Stingray Objective Toolkit tree control. For the Objective Toolkit Extended Combo Box control, we've gone a step further by adding support for connection points and firing events back to a client program. To enable this, for the Extended Combo Box only, select the “Attributes” tab in the dialog box above, select “Support Connection Points,” as illustrated below, and click OK. 10. The wizard should take only a couple of seconds to generate the required source code. Examine the Class View tab which you can view by selecting View|Class View. 432 Figure 180 – Class View Tab The wizard has generated all the required source code to instantiate and run an Objective Toolkit Tree control. Note that the one exported method, InsertItem(), is provided only as an example. As a developer, you will add method declarations (under the Imytreecontrol interface) and implementation (under Cmytreecontrol) to export and/or extend the Objective Toolkit Tree control’s functionality in your own custom control. 29.3.1 Pre-Build Set-Up We are almost ready to build our control, but first there are a few things we must do to make sure RTTI is enabled. 1. Open the Project Properties dialog by selecting Properties from the Project menu. Chapter 29 ATL and Objective Toolkit Components 433 2. Select Language from the C/C++ folder as shown in Figure 181. 3. Make sure that Enable Run-Time Type Info is set to Yes. Figure 181 – Project Settings 434 29.3.2 Building Your Control You can now build your control. 1. Start the build process. Figure 182 – Build Results 29.3.3 Testing Your Control After successfully building your control, you are ready to test it. You have two options: Option 1—Open an existing VB project Open an existing VB project by clicking File|Open|Project. Figure 183 – Open Project Dialog Chapter 29 ATL and Objective Toolkit Components 435 Option2—Open a new project Create a new VB project from scratch, add in the control you've just built, and proceed from there. This second option is illustrated here. 1. Create a new Visual Basic project by clicking File|New|Project. 2. Select Windows Application from the Templates: frame, as show in Figure 184. Figure 184 – New Project Dialog 436 3. This leads to the familiar VB default new project development environment… Figure 185 – Visual Basic Project Window 4. Now, add your freshly-built ActiveX control to your VB form by right clicking on the Toolbox and select Customize Toolbox. Find your ActiveX class, as shown below. Chapter 29 ATL and Objective Toolkit Components 437 Figure 186 – Customize Toolbox Figure 187 – Component Window 438 5. After you click OK, the icon for the control (currently the default ATL icon) appears on VB’s tool palette. Figure 188 – Tree Control Icon on VB Tool Palette 6. Click the tree control icon (circled above) and draw an instance of the tree control on the VB form. Figure 189 – Visual Basic Form 7. Add code to the Form_Load procedure: Chapter 29 ATL and Objective Toolkit Components 439 Figure 190 – Form_Load Procedure 8. Finally, run the VB project. Figure 191 – Running the application 440 29.4 Further Extensions We’ve only exported one method. We have not exported any of the Objective Toolkit Tree control’s bitmap functionality. This and other extensions are left to the developer. You can get a good idea of how to go about extending this by examining the code for the method implementation we’ve provided, InsertItem(): STDMETHODIMP Cmytreecontrol::InsertItem(VARIANTARG nIndexParent, BSTR text,VARIANTARG *pNode) { //!AFX_MANAGE_STATE is necessary whenever functions callable from //other modules are provided! AFX_MANAGE_STATE(AfxGetStaticModuleState()) TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask = TVIF_TEXT; _bstr_t bstrBuffer(text); tvi.pszText = bstrBuffer; TV_INSERTSTRUCT tvis; memmove(&(tvis.item), &tvi, sizeof(TV_ITEM)); //!If the value of the parent node passed in was zero, insert at //the root level. Otherwise, insert the new node as the child of //the specified parent. if (nIndexParent.lVal != 0) { tvis.hParent = (HTREEITEM)(nIndexParent.lVal); }else { tvis.hParent = TVI_ROOT; } tvis.hInsertAfter = TVI_LAST; HTREEITEM htiAX = m_wndClassImpl.InsertItem( &tvis ); //!Return an identifier (in this case, actually, a pointer) of //the just-created node as a variant in the [out,retval] parameter //to the caller. This implementation is provided as an example //only. Production code might implement some type of 'key' scheme //for the nodes and should also include error checking. This code //can also be modified and extended to enable use of the bitmap/ //imagelist functionality of which the SECTreeCtrl is capable. pNode->lVal = (DWORD)(htiAX); pNode->vt = VT_I4; return S_OK; } As explained in the comments in this routine, a production version would need a great deal more error checking. Nonetheless, it does illustrate two important points. Note the call to AFX_MANAGE_STATE(). As is explained below, this is necessary in any routine that is callable externally. Chapter 29 ATL and Objective Toolkit Components 441 Note the invocation of the Objective Toolkit Tree control method InsertItem()— called on the member variable m_wndClassImpl. This is the actual instance of the class SECTreeCtrl provided by wrapper classes in header file sectxmacs.h. See this file for implementation details. In general, this member variable may be used to access any SECTreeCtrl class provided functionality. This method can be modified (for example, to access the bitmap/imagelist functionality as described) and other methods may be defined on the Imytreecontrol interface to make functionality in the base Tree control accessible through the ActiveX control you are developing. The Objective Toolkit libraries require initialization before they can be used. During this process, resources are allocated and made available to the module that uses them. It is important that any such resources be available only to the module and not to the application. If such resources were to live in the application, several conflicts could arise. Consider a case where two ATL-based DLLs link in Objective Toolkit. Assume that the first performs control registration. The second is then loaded. Both work fine. Then let us assume that the first control gets terminated, while the rest of the application continues to run. Like any good control, the first control cleans up after itself, un-registering the class. When the second control tries to create a control of this class, it fails. Objective Toolkit is completely aware of these issues and can be used freely inside different ATL modules. Remember to call AFX_MANAGE_STATE(AfxGetStaticModuleState()) when you export functions that will be called from other modules. Non-module state-aware MFC controls will fail under these situations. In this chapter, we have provided guidelines and working code, as well as code generation helpers, for better ATL compatibility with Objective Toolkit. Please contact us if you encounter problems with this support or if there are other features that you would like to see implemented. 29.5 Sample Code For an example control generated using this wizard, please refer to the basicDate control under the ...\Samples\Toolkit\ATL folder. This sample uses SECDateTimeCtrl. 442 Chapter30 Introduction to Objective Toolkit for ATL 30.1 Overview Objective Toolkit for ATL is a mix of GUI and non-GUI classes and Object Wizards that have been built specifically for the Visual C++ ATL developer. The source code for the GUI and non-GUI class header files are located under <stingray-installdir>\Include\Toolkit\ATL. The template code for the Objective Toolkit ATL wizards are integrated into the selected and available MSVS compilers present during installation. The ATL wizards are available for all supported compilers, as described in the Stingray support matrix. Chapter 30 Introduction to Objective Toolkit for ATL 443 30.2 Features and Benefits OTL is fully compatible with the latest 32-bit release of Microsoft Visual Studio and AT. Previous versions of the compiler or ATL are not supported. 30.2.1 Features of Objective Toolkit for ATL Objective Toolkit for ATL is a feature-rich library that includes: 444 COM Collection classes and Object Wizard Threading classes Interface token class for automatic marshaling An easy to use functor class SAFEARRAY classes A Visual Editing tool for .RGS scripts (RGSEdit) Microsoft Message Queue class XML helpers Internet Explorer Band Object Wizard and classes Desktop Application toolbar class and Object Wizard Window Layout manager for Composite Controls COM task allocator memory debugging tools Marshaling classes Comprehensive Online Help 30.3 COM Collection Classes The COM collection classes are used as base classes for a collection object to create an Automation compatible COM collection object. These classes appear in the object models of Microsoft Office applications, such as Word and Excel. For example, Excel exposes a Worksheets collection object, which you can use to Add, Remove, or retrieve a contained Worksheet object. You can easily create your own collection of objects using the OTL Collection Wizard. OTL examines the IDL for the project to create a list of the possible object types the collection can contain. An object type can be a custom or dual interface name, or generic IDispatch pointer. The generated collection object also supports the _NewEnum() method that Visual Basic clients expect when using For/Each enumeration syntax. The wizard is a modified simple ATL Object Wizard that creates an ATL object that inherits from one of the OTL collection implementation classes. Select a base class that suits your internal collection implementation, which is based on your performance and indexing criteria: CComCollection—A custom linked list implementation that does not have Standard Template Library (STL) dependencies. This class supports object retrieval by string key or by index. CComCollectionOnSTLMap—Implemented on an STL map container, this class is a specialized case of the more general CComCollectionOnSTLAssoc, which takes any STL associative container, such as the map<> class, as a template argument. This class supports object retrieval by string key or by index. CComCollectionOnSTLAssoc—The only base class not specifically supported by the collection wizard, this class requires a complete STL associative container type specification as a template parameter. The collection type must support value pairs with a _bstr_t (or other BSTR compatible type with a < operator) as the first value and the object interface pointer type as the second value. CComCollectionOnSTLMap is a predefined derivation for the common use case of an STL map. CComCollectionOnSTL—For retrieval by numeric index only, this class is a thin wrapper for the ATL ICollectionOnSTLImpl class, using ATL's CComEnumOnSTL as the enumeration mechanism. This class is suitable for STL sequence containers, such as vector and list, which access items by numeric index, not string key. All CComCollectionOnSTL methods and data members are inherited from the ATL class ICollectionOnSTLImpl (atlcom.h), which currently supports the Item, Count, and _NewEnum COM collection members. This class requires an STL sequence container type specification as a template parameter, as the following code section from the Cars sample illustrates: #include <vector> #include <OtlComCollectionSTL.h> #include "carobj.h" class ATL_NO_VTABLE CCarsDual : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CCarsDual, &CLSID_CarsDual>, public CComCollectionOnSTL<ICarObj, std::vector<ICarObj*>, IDispatchImpl<ICarsDual, &IID_ICarsDual, &LIBID_CARCOLLECTIONLib> > { Chapter 30 Introduction to Objective Toolkit for ATL 445 The wizard creates a fully functional collection object. If you need to populate the collection before exposing the object to clients, override FinalConstruct(), and use the Add() method of your new collection object to build the collection. The Add() method is implemented by the OTL collection base class, which is the class from which your object derives, so it is not listed in your collection objects header file. The exception to this population rule is CComCollectionOnSTL, which does not support the Add() method because it’s derived from ATL’s ICollectionOnSTLImpl class. In this case, you can populate the collection by directly accessing the underlying STL container. The container is always available in the m_coll data member. The following code is a CComCollectionOnSTL-derived collection object populating itself with predefined objects: HRESULT FinalConstruct() { CComObject<CCarObj>* pCar; ICarObj* pICar = NULL; for(int i = 0; i < NUMOBJECTS; i++) { CComObject<CCarObj>::CreateInstance(&pCar); if(SUCCEEDED(pCar->QueryInterface(IID_ICarObj, (void**)&pICar))) { pICar->put_ID(i+1); AddItem(pICar); pICar->Release(); } } return S_OK; } void AddItem(ICarObj* pICar) { ATLASSERT(pICar); pICar->AddRef(); m_coll.push_back(pICar); } 446 30.4 Threading Classes Multiple threads can pose a problem for COM developers. Our threading classes target the COM worker thread use case, where apartments must be initialized correctly and interface pointers may need to be marshaled into the worker thread procedure. These are not pooling classes, but primarily encapsulations of common client usage. Suppose you have a COM object in your main thread. Now, let’s suppose that you want to launch a worker thread to perform some background task on that object. With our threading classes, you can declare the thread procedure with typed arguments, not just a void*, then create a COtlThreadFunction object that wraps it all up. The thread is constructed in a suspended state, and can be executed using the Start() function. Here is some code from the InterfaceToken sample that illustrates this: COtlThreadFunction mta(functokII, COINIT_MULTITHREADED); mta.Start(); You can create thread objects on the heap or on the stack. The destructor does not return until the thread is complete and its handle is signaled. You can also use your own thread synchronization methods by accessing the encapsulated thread handle directly. Between construction and Start, you can make priority adjustments by using the thread handle directly. The handle and thread ID are available through the COtlThreadFunction m_hthread and m_dwTID public data members. COtlThreadFunction relies on an OTL functor object, which is described in Section 30.6. The preceding sample code passes a previously constructed functor object (functokII) to the thread object constructor. The COM apartment is initialized and uninitialized transparently by the COtlThreadFunction object. You declare the apartment type in the thread object constructor with a standard COINIT_XXX constant. Apartment threading is the default. Interface pointers can be wrapped in a COtlInterfaceToken object (one per interface pointer) and passed directly to the thread procedure. Chapter 30 Introduction to Objective Toolkit for ATL 447 30.5 Interface Token Class COtlInterfaceToken template class encapsulates an interface pointer for stream based marshaling to a worker thread. Just declare the thread procedure to accept COtlInterfaceToken<InterfaceType> as a parameter, and then use the parameter like a regular interface pointer in the worker thread. Any number of interface pointers can be passed to a thread procedure in this manner without explicit calls to marshaling APIs. The following code from the InterfaceToken sample is of a global function receiving an interface token as a parameter: void fworker( LPWSTR strTitle, COtlInterfaceToken<IMarshalThis> pimt) { // use the token just like an interface pointer short s; HRESULT hr = pimt->Method1(34, &s); … } While the token can be used any number of times in the receiving function, it is only unmarshaled once. Because it can only be used in the context of a single function call, construct the interface token in the calling parameter list. The constructor takes an interface pointer as its only argument. You can use a smart pointer or standard interface pointer in the constructor parameter, as shown here: CComPtr<IMarshalThis> spimt; spimt.CreateInstance(…); fworker(L"Hello", COtlInterfaceToken<IMarshalThis>(spimt) ); This code does not really do any threading, but shows how the parameters must be declared to accept an interface token object. The next section shows you how to encapsulate the entire function call with parameters in a functor object. As an alternative to interface tokens, Objective Toolkit also includes a Global Interface Table class that you can use to marshal interface pointers: OtlGIT. For more information about OtIGIT, please see the OtlGIT online documentation. 448 30.6 Functors A functor is an object used to represent a function invocation. Functors capture the information necessary to describe and make a call to a specific function, including a pointer to the function and the argument values to pass to it. The key feature of functors is their ability to supply an interface for invocation that is independent of the actual function. This allows functors to be invoked by entities that have no knowledge of the encapsulated function or its arguments. OTL encapsulates a functor in the COtlFunctor class. Functors can be used by themselves, or in conjunction with COtlThreadFunction to encapsulate a function call as a thread procedure. 30.6.1 Constructing Functors Functors are implemented using a handle-body architecture. The construction of a functor handle does not result in the construction of a complete functor object. To build a viable functor object we must first construct a functor implementation object and bind that object to one or more functor handles. Functors can be implemented on global functions and non-static class member functions. A functor object is constructed using an otlMakeFunctor() global template function. This relies on the compiler to extract the signature of the function you specify, allowing it to select, construct, and initialize an appropriate functor implementation instance. This is illustrated in the following code fragment: void func( int n ) { } COtlFunctor functor = otlMakeFunctor( func, 1000 ); Functors can also be created on non-static public class member functions, as shown below: class CDog { public: void Bark( int nDecibels ); }; CDog dog; int nDb = 120; COtlFunctor BarkLoudly = otlMakeFunctor( dog,&CDog::Bark, nDb ); 30.6.2 Invoking Functors Invoking a functor is simple. Each functor class overloads operator() to provide a style of invocation identical to that of a function call: functor( ); BarkLoudly( ); Chapter 30 Introduction to Objective Toolkit for ATL 449 30.6.3 Interface Tokens and Functors An interface token requires no special handling to be used as a parameter in a functor. With that in mind, we can create a functor that accepts tokenized interface pointers as arguments. Consider the global fworker() function from the interface token section: void fworker( LPWSTR strTitle, COtlInterfaceToken<IMarshalThis> pimt) { // use the token just like an interface pointer short s; HRESULT hr = pimt->Method1(34, &s); … } This function takes two parameters: a string and an interface token. We can construct a functor for this in the following manner: CComPtr<IMarshalThis> spimt; spimt.CreateInstance(…); … COtlFunctor functok = otlMakeFunctor(fworker, L"token marshaling", COtlInterfaceToken<IMarshalThis>(spimt)); 30.6.4 Threads and Functors Once you construct a functor, you can use the functor to represent a thread procedure when combined with the COtlThreadFunction class. The result is an encapsulated thread procedure that can accept typed parameters and interface pointers as tokens. COtlThreadFunction takes a functor that you want to be the thread procedure as a constructor argument. To make the functor described above into a worker thread procedure, you would use the following code: COtlThreadFunction sta(functok); Starting the thread is easy, as the following code demonstrates: sta.Start(); When Start() executes, the thread is resumed and the global fworker() function is called with an interface pointer token that is ready to use. No unmarshaling is required. Call interface methods the same way you would call a normal interface pointer: with the -> operator provided by the interface token. You do not need to call the COM CoInitialize or CoUninitialize APIs in your thread function. COtlThreadFunction handles that for you. 450 30.7 SAFEARRAY Classes For COM developers targeting Visual Basic and Java clients, the only way to effectively transfer arrays between objects and clients is to use SAFEARRAYs. SAFEARRAYs are self-describing, selfbounded arrays. Unfortunately, using SAFEARRAYs requires you to work with a complex API of C functions. COtlSafeArray is useful for creating and accessing multiple-dimension SAFEARRAYs. The syntax for using COtlSafeArray is generally simpler than working with the SAFEARRAY API. The COtlSafeArray class supports constructing empty arrays and adding elements as well as copying arrays from existing arrays. Here is a code example for a method using an array of BSTRs: #include "otlsafearray.h" using namespace StingrayOTL; void UseSafeArray() { OtlSABSTR* posaBSTR; // OTL predefined type… long rgnBounds[1]; rgnBounds[0] = 10; m_posaBSTR = new OtlSABSTR; SomeFunctionThatGeneratesASafeArray(VT_BSTR, 1, rgnBounds, posaBSTR); for(long l = 0; l < posaBSTR->GetSingleDimSize(); l++) { BSTR bstr; posaBSTR->GetElement(l, &bstr); CString str(bstr); // test the [] operator BSTR bstrTemp = ((*posaBSTR)[l]); CString strTemp(bstrTemp); ASSERT(strTemp == str); // do something with the string... SysFreeString(bstr); SysFreeString(bstrTemp); } if(posaBSTR) { delete posaBSTR; } } 30.7.1 COtlSimpleSafeArray COtlSimpleSafeArray is used for creating and accessing single dimension SAFEARRAYs. The class allows a SAFEARRAY to be used like a normal C style array. This is especially useful for creating [out] SAFEARRAY or VARIANT array parameters. COtlSimpleSafeArray is templated on the type of element contained in the array. There are several constructors for creation based on an existing VARIANT or SAFEARRAY, or an empty array. You can also construct the array with an [out] Chapter 30 Introduction to Objective Toolkit for ATL 451 SAFEARRAY** or VARIANT* parameter, which automatically receives the array in the COtlSimpleSafeArray destructor. The following code is of a method in a server that creates and returns a SAFEARRAY of BSTRs: STDMETHODIMP CSimpleServer::GetNames(VARIANT *pNames) { ATLASSERT(pNames); // create a new SafeArray with 4 elements. // passing the [out] pNames parameter allows auto assignment on // destruction COtlSimpleSafeArray<BSTR> array(pNames, VT_BSTR, 4); array[0] = ::SysAllocString(L"Don Box"); array[1] = ::SysAllocString(L"Tim Ewald"); array[2] = ::SysAllocString(L"Chris Sells"); array[3] = ::SysAllocString(L"George Shepherd"); return S_OK; } 452 30.8 RGSEdit COM servers written using ATL employ an ATL-supplied component called the Registrar. The Registrar components take a registry script as input and apply changes to the registry. Unfortunately, editing the registry script is a tedious process that is error-prone. OT/ATL provides a visual editor for managing registry scripts. The OT/ATL Registry Script Editor (RGSEdit) reads a registry script (a .RGS file) and then displays the registry hierarchy using a tree control. Once the registry is displayed as a tree, you can edit the registry script by adding, editing, and deleting keys and values like you would in the Windows registry using the RegEdit utility. The following code is an example of a registry script from an ATL-based COM class. HKCR { BigOldATLSvr.AIObject.1 = s 'AIObject Class' { CLSID = s '{50B086BF-78B9-11D2-B79F-0060081EE21C}' } BigOldATLSvr.AIObject = s 'AIObject Class' { CLSID = s '{50B086BF-78B9-11D2-B79F-0060081EE21C}' CurVer = s 'BigOldATLSvr.AIObject.1' } NoRemove CLSID { ForceRemove {50B086BF-78B9-11D2-B79F-0060081EE21C} = s 'AIObject’ { ProgID = s 'BigOldATLSvr.AIObject.1' VersionIndependentProgID = s 'BigOldATLSvr.AIObject' ForceRemove 'Programmable' InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Apartment' } 'TypeLib' = s '{50B086B0-78B9-11D2-B79F-0060081EE21C}' } } } 30.8.1 Changing What Gets Registered Before the advent of our RGSEdit utility, ATL/COM developers used the 101-key TextWizard. They also needed to understand the registry script syntax to change or add to the default entries for a COM class. By using RGSEdit, you can avoid bungling the registry script syntax and adding spurious registry entries to a client machine's registry. Our RGSEdit utility lets COM developers view and edit a .RGS file in the same way a system administrator can view the registry on a particular workstation. Chapter 30 Introduction to Objective Toolkit for ATL 453 30.8.2 Editing a Registry Script RGSEdit is a straightforward application whose document type is an ATL registry script file. To start editing a script, open a .RGS file from the file menu. After you open the registry script, you can add, change, and delete keys and values and then save the edited script. RGSEdit is a splitter window with three panes. The far-left pane includes a tree control specifying the hierarchical arrangement of the registry script. The nodes on the tree expand to reveal sub keys. The middle pane shows the values related to the key selected in the left pane. The far right pane shows the current state of the registry script. If you saved the file, the selections in this pane would be applied. 30.8.3 Adding Keys To add a key to the registry script, select the parent key in the left pane and click the right mouse button. A local menu appears. Select Add New Key from the menu. A new key appears in the farleft pane. 30.8.4 Editing Keys If you want to change the default name provided by RGSEdit or you want to change the name of the key for some other reason, select the new key and then open the local menu. Click Edit to enable editing of the key. ATL's registrar component also allows you to apply various attributes to the key that direct the registrar towards various actions. You can force the removal and re-creation of a particular key to prevent removal of a particular key or to delete a key. These options are directed by keywords that appear before each key in the script. To apply these keywords to any key, select a key, right click the key, and select Properties from the menu. 30.8.5 Deleting Keys Select the local menu on the far-left pane and choose Delete to delete the selected key and all of its subkeys. 30.8.6 Adding Values To add a value to any of the keys, select the key on the far-left pane and then click the right mouse button to open the local menu. RGSEdit lets you add either a string value or a DWORD value. 454 30.8.7 Editing Values To edit a value, double-click the value name in the middle pane. If the value is a string value, you can edit the string as it appears in the dialog box. If the value is a DWORD value, you can edit the value as it appears in the dialog box. If the value represents Miscellaneous Status Bits, RGSEdit displays a dialog box that lets you turn each bit on or off. If the value represents the COM class's threading model, you can double click to open a dialog box that lets you select from one of the COM threading models. Editing values also lets you change the name of the registry value as well as the value itself. If you don't supply a name for the value, the value becomes the default value for the key. 30.8.8 Managing Categories When developing new COM classes, it's useful to categorize them. For example, the formal definition of a COM control is any COM class that implements IUnknown. However, classes that only implement IUnknown are not very useful in a real application—useful COM classes implement several interfaces. In addition, sets of functionality have been subdivided into smaller categories such as scriptable controls, Internet Explorer controls, and more. In the past, there was no way to determine if an object was a control or a scriptable control other than querying for a certain set of interfaces. Component Categories allow developers to divide components into various categories—scriptable controls and Internet Explorer controls, for example. You can advertise that some classes implement certain features by amending their registry entries: HKCR\CLSID\{... some guid ...}\Implemented Categories COM clients can also indicate that they require components to implement various interfaces in the same fashion by amending: HKCR\CLSID\{... some guid ...}\Required Categories You can insert this information into the registry script by hand or use a facility in the RGSEditor that allows you to add component category entries to your registry script. To amend a component's registry entry and apply implemented and required categories to them, select the class's CLSID key in the far left pane of REGEdit and right-click to open the local menu. Then select, "Manage categories" to open the "Manage Categories" dialog box. The list box on the far-left side of the dialog box displays all the categories in the registry and the categories implemented by the component. When you select a category from the list of available categories, the dialog box indicates which registered components already implement/require the selected category. The two check boxes on the bottom of the dialog box let you apply the category to the class. Chapter 30 Introduction to Objective Toolkit for ATL 455 30.8.9 Launching RGSEdit from the IDE You can operate RGSEdit as a stand-alone application or as a Visual Studio Addin. To use RGSEdit as an add-in, select Tools|Customize in Visual Studio and then select the Add-Ins and Macro Files tab. Check the box next to RGSEditorAddIn.DSAddIn.1 to make it an add-in. After you select RGSEditorAddIn, you can open RGSEdit by clicking the newly installed toolbar button. Now, Visual Studio will load the add-in every time you start Visual Studio. 456 30.9 Microsoft Message Queue Class MSMQ is a Microsoft messaging technology that enables computers in an enterprise to send and receive messages to global queue objects that are registered with a directory service. OTL provides COtlMSMQ as a convenient wrapper for a queue and its configuration information. Using COtlMSMQ you can: Create a new queue. Open a queue for send or receive. Receive or send a message. Receive or send a COM object’s persistent state. Use DTC or MSMQ internal transactions for send or receive. Delete a queue. 30.9.1 Requirements To utilize OTL's MSMQ support, install Microsoft Message Queue server and client. You must also have MSMQ client and PEC (Primary-Enterprise-Controller) or PSC (Primary-Site-Controller) installed on an NT server at your site. Please see the relevant Microsoft MSMQ administration documentation for details on the installation process. 30.9.2 Creating a queue Queues are created by setting the queue properties with CreateInfo() or SetInfo(), followed by a call to the Create() method. Queue properties are stored internally as an MSMQQueueInfo COM object, which COtlMSMQ maintains a pointer to internally. As mentioned above, you can either create these property settings before attempting to open or create a queue, or you can attach existing queue info using SetInfo(). Transactional queues can be created by passing TRUE as the first parameter to Create(). Queues are not transactional by default. A queue can be destroyed with Delete(). Once created, a queue’s transactional property cannot be changed, which means you cannot change a queue from transactional to non-transactional (and vice versa). The queue must be deleted and re-created. This is an MSMQ limitation. MSMQ Explorer can be used to determine if a queue is transactional by looking at its properties. Creating a queue does not open it for send or receive. 30.9.3 Opening a queue Queues can be opened for send, receive, or peek using OpenForSend(), OpenForReceive(), OpenForPeek(). The current queue info, as set by SetInfo() or CreateInfo(), is used to determine which queue to open. It is also possible to search for queues matching certain criteria using FindQueue(), which returns a collection of MSMQQueueInfo objects. You can open any of those objects by passing the queue info to SetInfo(), and calling one of the Open() methods above. A queue must exist for the open methods to succeed. The following code shows how to create and open a queue for receive access: Chapter 30 Introduction to Objective Toolkit for ATL 457 COtlMSMQ q; HRESULT hr = q.CreateInfo(L".\\OTLMsmq", // path L"OTL Test Queue", // label and type GUID L"{BAE18490-B9EE-11d2-B363-006008AFB3D7}"); if(SUCCEEDED(hr)) hr = q.Create(TRUE); // transactional queue if(SUCCEEDED(hr) || (hr == MQ_ERROR_QUEUE_EXISTS)) hr = q.OpenForReceive(); The preceding code creates a queue on the local hard drive called OTLMsmq. Only the computer name and the queue name are needed. You can use ".\\ " as a shortcut for the local computer name, or you can specify a remote name. This is not a file path. When the queue is created, it is added to the global directory maintained by the server. Private queues can also be created by specifying a queue name in this format: Computername\Private$\QueueName. See the MSMQ documentation for more information, in particular the limitations of private queues. 30.9.4 Receiving Messages A queue that is open for receive can be read using ReceiveMessage(). ReceiveMessage() operates synchronously. It times out after a specified period or blocks infinitely until a message arrives. You can specify a transaction if you want. By default, no transaction is used. ReceiveMessage() returns a pointer to a message object. ReceiveObject() is used to create a COM object and then load it with state from the queue. The object must be sent with SendObject(). The Msmq sample demonstrates sending objects across machines using COtlMSMQ. The following code waits for a user defined rectangle object to be sent to the queue. COtlMSMQ recreates the object and its state and then returns the requested interface pointer on the object: // receive a rect object CComPtr<IRectObj> pRect; hr = q.ReceiveObject(IID_IRectObj, (void**)&pRect); Notice that the format of ReceiveObject() is identical to QueryInterface(). 30.9.5 Sending Messages to a Queue You can write a queue that was opened with OpenForSend() using SendMessage(). If you want to send a message to many queues, create the message once using CreateMessage() and then use the SendMessage(IMSMQMessage*) method. You can specify a transaction for each individual Send operation. By default no transaction is used. To send a COM object to a queue, use the SendObject() method instead. The object must support IPersistStream. The following code creates a rectangle object based on user input from the keyboard for left, top, right, and bottom. The object is then sent to a queue using an internal transaction: // create a rect object CComPtr<IRectObj> pRect; hr = pRect.CoCreateInstance(CLSID_RectObj); 458 // get the rect params so we have some known state to pass long l =0, t = 0, r = 0, b = 0; GetRectInput(&l, &t, &r, &b); // set the rect object state if(SUCCEEDED(hr)) hr = pRect->SetRect(l,t,r,b); // start an internal transaction CComPtr<IMSMQTransaction> pTrans; if(SUCCEEDED(hr)) hr = q.BeginTransactionInternal(&pTrans); // send the object if(SUCCEEDED(hr)) hr = q.SendObject(static_cast<IUnknown*>(pRect), (BSTR)NULL, pTrans); if(SUCCEEDED(hr)) { // send was OK, but receiver will not get it until commit CComBSTR bstrPath; q.m_pqinfo->get_PathName(&bstrPath); printf("Object Sent to Queue %S\n", bstrPath ); printf("Press any key to Commit the transaction..."); getch(); // commit hr = pTrans->Commit(&vtMissing, &vtMissing, &vtMissing); Once sent, the object can be released. The object state and CLSID is held in the queue until a receiver reads the message. 30.9.6 Cleanup You must release any interface pointers that you receive as [out] parameters from COtlMSMQ methods. Close the queue at the end of messaging by calling Close(). The destructor releases the current queue and queue info objects held internally. Chapter 30 Introduction to Objective Toolkit for ATL 459 30.10XML Helpers OTL provides the COtlXMLDocument class to provide a simplified method for creating XML files and editing their content. The class is based on the Microsoft XML parser provided in Internet Explorer. COtlXMLDocument is compatible with recent IE releases. To begin using COtlXMLDocument, create an instance either on the stack or heap, and then read the following section. 30.10.1Creating an XML Document From Scratch Use the Create() function to generate an empty XML document. You can specify the root tag name and XML header string as parameters, as shown below: COtlXMLDocument xDoc; if(!xDoc.Create( _T("MetaModel"), _T("<?xml version=\"1.0\" standalone=\"yes\"?>"))) return E_FAIL; 30.10.2Opening an Existing XML Document Existing documents can be loaded directly from a file using the LoadFromFile() function, which specifies a full file path, as shown here: if(FAILED(xDoc.LoadFromFile(_T("c:\\test2.xml")))) return E_FAIL; 30.10.3Adding Tags The AddTag() and AddTextTag() methods create new document tags as children of the root element, or any other element. AddTag() gives you full control of the type of tag to create. AddTextTag() creates two tags: an outer element with an inner text element. This is essentially a name/value pair. For example, the following code results in the following XML added as a child of the root element: xDoc.AddTextTag(_T("Child"), _T("1234")); <Child>1234</Child> 30.10.4Saving the XML Document to a File You can save a document directly to a file using the SaveToFile() function, as shown here: xDoc.SaveToFile(_T("c:\\test2.xml")); If the file does not exist, it is created. If it exists, it is overwritten. 460 30.10.5Reading Tag Values You can retrieve the value of a tag by searching on the name of the element. Searching is supported by COtlXMLDocument mapping of tag names to values in an STL map. You specify the level in the hierarchy where the map begins using MapElements() and a flat map of all children is generated. LoadMap() can be used instead of MapElements() when the entire file is mapped. You can disable recursion with a parameter to MapElements(). You can generate maps multiple times during a session. Every time MapElements() or LoadMap() is called, the contained STL map is cleared and populated with new value pairs. Once the map is generated, you can use FindTextTag() to get its associated text as a string or variant. For example, the <Child>1234</Child> tag can be read as a variant of type long integer like this: CComVariant vaVal; if(xDoc.FindTextTag("CHILD", vaVal, VT_I4)) ATLTRACE("Found CHILD\n"); If you already have an IXMLElement pointer, its associated text value can be retrieved using GetTextTag() instead. 30.10.6Trace Output COtlXMLDocument has debug facilities for dumping the contents of an XML element and its children to the Visual Studio debug output window. Use the TRACE_ELEMENT() function with no parameters to show the contents of the root element only. Use TRACE_ELEMENT(IXMLElement* pElem) to show all children of pElem. TRACE_ELEMENT() is a function name—not a macro. Chapter 30 Introduction to Objective Toolkit for ATL 461 30.11Internet Explorer Band Object Wizard and Classes The Internet Explorer extensibility model allows you to create visible COM objects that inhabit the browser frame, or the enhanced desktop taskbar. OTL provides implementations of IDeskBand and other shell interfaces needed to accomplish this integration. The OTL band Object Wizard can generate a generic band object for any of the three possible types: Desk Band Docked in the Windows Taskbar on the desktop Explorer Band (Vertical Explorer Bar) Docked in the left side of the browser. Communications Band (Horizontal Explorer Bar) Docked in the bottom edge of the browser From a development perspective, the differences among these types are minor. In fact, the only difference among types is the registry category and default size. The wizard-generated COM object is also a window you can draw your UI onto by handling WM_PAINT messages. Your band object window can contain child windows or whatever user interface you choose. 30.11.1Changing Size Constraints Window size limits are controlled by the m_bandInfo member of OtlIDeskbandImpl. m_bandInfo is a DESKBANDINFO structure, which holds the minimum, maximum, integral change and ideal sizes for the band object window. These members can be modified directly in your derived class constructor. 30.11.2Context Menu Commands A band object can have context menu commands that appear when the window is right-clicked. OTL supports this through the OtlIContextMenuImpl base class. Menu commands can be conveniently handled in your message map as WM_COMMAND messages. By default, the band Object Wizard adds one custom context menu command handler to the object. The menu handlers are declared in an OTL command map. The command map supplies the menu item text, help string, and a command ID for the menu item. The code below shows a default command map generated by the band Object Wizard, containing a single command: OTL_BEGIN_COMMAND_MAP() OTL_COMMAND(_T("&Sample command"), _T("Help string"), ID_SAMPLE_COMMAND) OTL_END_COMMAND_MAP() Context menu commands are added from top to bottom when the menu is invoked. You can use the command map macros to create the context menu items that you want to add. A context menu command adheres to the following form: 462 OTL_BEGIN_COMMAND_MAP( ) OTL_COMMAND_ID(UINT ID_COMMAND, UINT ID_HELP ) OTL_COMMAND(LPCTSTR strCommand, LPCTSTR strHelp , UINT ID_COMMAND ) OTL_END_COMMAND_MAP( ) The command map entries can be either OTL_COMMAND or OTL_COMMAND_ID or a mix of both. The choice depends on whether the menu strings are defined in resources or string literals. 30.11.2.1Parameters ID_COMMAND A resource ID identifying the string resource that is the menu item text. This is also the command ID that is sent in the OtlIContextMenuImpl::FireCommand() function. Override this function to handle a menu item or use OtlIContextMenuMsgImpl, which overrides FireCommand() to generate WM_COMMAND messages to your window. ID_HELP A resource ID identifying the string resource that is the help text for the menu item. Can be zero if no help string is available. strCommand The string that appears on the context menu. strHelp The help string for the menu item. If no help string is needed, use _T(""). This map can be used in conjunction with either OtlIContextMenuImpl or OtlIContextMenuMsgImpl to build context menu extensions. If your object has a window, OtlIContextMenuMsgImpl transparently converts your ID_COMMAND IDs to WM_COMMAND messages that can then be handled in your message map like standard command messages. By default, band objects use OtlIContextMenuMsgImpl and WM_COMMAND handlers for context menu additions. Chapter 30 Introduction to Objective Toolkit for ATL 463 30.12Desktop Application Toolbar Class and Object Wizard To enable developers to create desktop toolbars similar to the Windows taskbar, OTL provides the COtlAppBarImpl class. Unlike IE Deskband objects, application toolbars are based on callbacks and windows messaging, not COM interfaces. OTL AppBars also support taskbar features like Autohide, Always on Top, and Incremental Sizing. The AppBar also has a mirror mode, which forces the Appbar settings to follow the Windows taskbar settings. An application toolbar, or appbar, runs as an executable, or a DLL loaded in the process you define. All you need is a window, and COtlAppBarImpl as a base class. COtlAppBarImpl is a CMessageMap derivative that listens for sizing and mouse messages sent to your window. It also implements a callback handler that is registered with the Windows application bar support mechanism. OTL provides an Object Wizard to generate a simple dialog-based application bar that you can customize. The wizard generates an ATL CAxDialogImpl-derived class that also inherits from COtlAppBarImpl. The message map for the dialog chains to COtlAppBarImpl via the CHAIN_OTL_APPBAR() macro at the beginning of the message map. A context menu is provided to access common AppBar features. 30.12.1Creating an AppBar You can create a wizard generated AppBar the same way you create a normal dialog box. The WM_INITDIALOG handler needs to call the InitAppbar() and Dock() functions of COtlAppBarImpl. You can also build Appbars using the ATL CWindow classes instead of dialog classes. In that case, handle WM_CREATE to do the initialization. 30.12.2Docking and Layout By default, you can drag the AppBar to any edge of the desktop. If you want to prevent docking to a particular edge, override PrepareToDock(uEdge) and return FALSE for that edge. The AppBar supports the real time dragging. Whenever the AppBar is sized or moved, OnLayoutBar() is called. The AppBar Object Wizard generates code that overrides OnLayoutBar() in your COtlAppBarImpl-derived class. Reposition any child window and make any UI adjustment for docking orientation (vertical/horizontal) in OnLayoutBar(). Floating AppBars are not supported. 30.12.3Appbar Tab Window Control OTL also has a tab control class that is specifically designed for creating rows and columns of owner-draw buttons with tooltips, like the windows taskbar. The ZappBar sample shows the COtlAppBarTabs class used with COtlAppBarImpl to create a desktop navigation bar. COtlAppBarTabs is designed to be used as a child control inside of an Application Desktop Toolbar (AppBar), similar to the Windows Taskbar. This class subclasses the Win32 common tab control. The control has the following enhancements: 464 Owner-draw buttons (tabs) with images. Elided text on buttons when sizing is below minimum. Automatic tooltips based on button text. Dynamic change of tooltip hit rectangles when resizing. 30.12.4Creation After construction, you can initialize this control with a call to Create(), or SubclassWindow(). If subclassing, use the following styles in the dialog editor: TCS_FORCELABELLEFT | TCS_HOTTRACK | TCS_BUTTONS | TCS_MULTILINE | TCS_FIXEDWIDTH | TCS_FOCUSONBUTTONDOWN | TCS_OWNERDRAWFIXED | TCS_TOOLTIPS If you want the flat button style, use ModifyStyle to add TCS_FLATBUTTONS after creation. This style is only supported in common controls version 4.71 and higher. 30.12.5Message Reflection The parent window must reflect messages back to COtlAppBarTabs by adding the ATL macro REFLECT_NOTIFICATIONS() to its message map or no tab drawing can occur. 30.12.6Image List If you need images to load an image list, call SetImageList() and then populate the tab control using the AddTab() method. 30.12.7Selection Notification Handle the TCN_SELCHANGING notification to perform some action when a button is clicked. In the handler, you can retrieve the button that was clicked with GetFocusItem(). Returning TRUE from this handler allows the button to return to the up position. Returning FALSE causes the button to remain pressed until another selection is made. Chapter 30 Introduction to Objective Toolkit for ATL 465 30.13Window Layout Manager for Composite Controls The Layout Manager classes provide child window size and position dynamics for resizable ATL composite controls, dialog boxes, and windows. You can also utilize the size constraint capabilities to limit the sizing of your control or dialog to reasonable values. Layout capabilities are expressed in the layout algorithm classes. OT/ATL provides the COtlScaleNode and COtlRelativeNode algorithm classes for this purpose. A corresponding layout node object represents each child window. The top-level window or dialog is usually represented by an algorithm node object that maintains a collection of managed child nodes, much like child window relationships. Layout management is enabled by inheriting from an algorithm class and a message listener or plug-in class. This can be done in one step with the template class COtlLayoutImpl. You need to create child nodes for the child windows that you want to manage. Typically, do this in OnInitDialog(). The COtlLayoutFactory class has convenience functions for creating child window nodes (COtlWindowNode) for all child windows or selected ranges. You can create your own layout node types derived from COtlLayoutNode. These can be algorithm nodes or simple child nodes. The following sections below explore these topics in more detail. 30.13.1Layout Manager Algorithms This section describes each of the default layout algorithms provided with the Objective Toolkit for ATL Layout Manager component. You can also create your own custom layouts that are derived from our algorithms or from the common base class COtlLayoutNode. 30.13.2Scale Layout The Scale Layout maintains all children with a constant aspect ratio to the parent scale node. In other words, the child node’s top, left, right, and bottom coordinates are stored as percentages of the parent node’s size and are resolved to actual pixel values with each recalculation. This guarantees a constant aspect ratio no matter what size the parent node is. This algorithm is implemented by COtlScaleNode. 30.13.3Relative Layout The Relative Layout allows a logical organization of layout nodes in coordinates relative to other layout nodes. Common actions here might include moving or stretching child controls in response to an overall dialog size change. For example, you can achieve layouts such as: 466 “Set the left side of node 1 equal to the right side of node 2 plus 10 pixels,” or “Set the bottom of node 1 to 25 percent of the height of node 2,” or “Move node 1 so that its bottom is equal to the top of node 2 minus 10 pixels,” and so on. Each of these rules is called a constraint. Constraints are objects that define units of relative layout behavior. This algorithm is implemented by COtlRelativeNode. 30.13.4Adding Layout Management to Your Applications Choose a layout algorithm and then include the necessary header files in your dialog or window implementation class. The Layout Manager implementation class (COtlLayoutImpl) is added to your list of base classes, templatized on the algorithm chosen. Here is the scale algorithm: #include <otlscalenode.h> #include <otllayoutimpl.h> #include <otllayoututil.h> // CScaleDialog class CScaleDialog : public CAxDialogImpl<CScaleDialog>, public COtlLayoutImpl<COtlScaleNode> { Now the Layout Manager needs to gather a list of child nodes to manage. Typically, a window layout node is created for every child window on the dialog, although this is not a requirement. We can use the OnInitDialog() message handler to create child nodes. Insert the following code after all the child windows have been created and initialized: // optional: set dialog box size limits SetMinMaxSize(150, 255, 900, 600, 0); // helper object for enumerating child windows and building nodes COtlLayoutFactory layoutFactory; // makes a node for every child window layoutFactory.AutoPopulateNodeWnd(this, m_hWnd); // set all child nodes to use optimized redraw // (static controls require it) ModifyNodeStyleEx(0,OTL_LNODE_EX_OPTIMIZE_REDRAW,TRUE); // kick off the layout process AutoInit(this, m_hWnd); In this case, the dialog itself serves as the parent scale node and all its child windows are managed as child COtlWindowNode objects. Some Layout Manager functions require a parent node and a window handle. This allows for flexibility in the case of nested layouts, and when the parent node is not the same as the window class. Notice that the preceding example uses a helper object, COtlLayoutFactory. This is a convenient utility class that you can use to build the layout node tree. The only thing you need to do is let the Layout Manager look at the incoming window messages. To do this, add the following line to the dialog's message map: CHAIN_MSG_MAP( COtlLayoutImpl< COtlScaleNode > ) The template parameter for COtlLayoutImpl must match the one you used for the base class. COtlLayoutImpl is a very thin convenience class that derives from your algorithm of choice and COtlLayoutPlugin, which listens for sizing messages. Now, give your dialog a resizable border using the resource editor so scaling is applied when the dialog is resized. Chapter 30 Introduction to Objective Toolkit for ATL 467 30.14COM Task Allocator Memory Debugging Tools Because of their distributed nature, COM clients and servers must manage memory differently than client and servers with client code and the object code that share the same memory pool. Whenever clients and objects need to transfer blocks of memory back and forth, they need to use the task allocator—a COM-provided memory allocator. Any time large pieces of memory need to be communicated between clients and objects, developers need to use the task allocator, which includes memory allocated for BSTRs. The only way to debug memory leaks from the task allocator is to implement an interface named IMallocSpy. Windows provides a hook for tracking memory allocated by the COM Task Allocator. Developers wishing to plug into the task allocator to watch the memory allocations as they occur implement an interface named IMallocSpy and hand this interface over to Windows via the CoRegisterMallocSpy() function. IMallocSpy provides pre- and post-processing functions for each function within the standard IMalloc interface. For example, IMalloc includes a function named Alloc(). IMallocSpy includes a function named PreAlloc() and a function named PostAlloc(). If an implementation of IMallocSpy has been plugged into the task allocator, the task allocator calls IMallocSpy::PreAlloc() before allocating memory and IMallocSpy::PostAlloc() after the memory has been allocated. This lets developers watch memory blocks as they’re being managed. OTL provides a class named OtlMallocSpyImpl that tracks memory activity occurring within the task allocator and indicates that activity through trace statements in the debug window. 30.14.1Using the Task Allocator Debugger Using OTL’s task allocator debugger is straightforward. // in the .CPP file: using namespace StingrayOTL; #include "otlmallocspy.h" OtlMallocSpyImpl otlMallocSpy; // creating one of these registers // a malloc spy... //… Code that exercises the task allocator //… otlMallocSpy is a global variable, so it unattatches from the //… task allocator. COtlMallocSpyImpl registers the IMallocSpy implementation with the system. Declaring an instance of COtlMallocSpyImpl turns on the debugging process. Task allocations appear in the output debug window as they occur. In addition, the task allocator spy informs you of any unreleased memory within its destructor. 468 30.15Marshaling Classes From time to time, COM developers need to move an interface pointer from one apartment to another (for example, if two threads need access to the same object). The process of getting an interface pointer from one apartment to another is called marshaling the interface pointer. OTL provides marshaling help in several forms. First, OT/ATL contains a class named OtlGIT to help you work with the Global Interface Table. The OtlGIT class manages the IGlobalInterfaceTable interface for you so you no longer need to call CoCreateInstance() to retrieve the interface. Here’s an example illustrating how to use the global interface helper. #include <OtlGIT.h> using namespace StingrayOTL; OtlGIT* potlgit; DWORD g_dwMarshalThis = 0; DWORD WINAPI ThreadProc(void *pv) { IMarshalThis* pMarshalThis = NULL; HRESULT hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED); IMarshalThis* pRaw = (IMarshalThis*)pv; if(pRaw) { hr = pRaw->Method2(3); if(FAILED(hr)) { printf("Raw pointer didn't work \n"); } else { printf("Raw pointer did work \n"); pRaw->Release(); } } // Unmarshal the interface pointerfrom the GIT hr = potlgit->GetInterface(g_dwMarshalThis, IID_IMarshalThis, (void**)&pMarshalThis); if(SUCCEEDED(hr)) { short s; Chapter 30 Introduction to Objective Toolkit for ATL 469 // Use the pointer hr = pMarshalThis->Method1(34, &s); if(SUCCEEDED(hr)) { printf("Marshaled pointer did work \n"); } else { printf("Marshaled pointer didn't work \n"); } // Release the interface pointers. pMarshalThis->Release(); } // Leave the apartment. CoUninitialize(); return 0; } int main(int argc, char* argv[]) { // Enter the multi-threaded apartment. We're all partying in // the same room now. CoInitializeEx(0, COINIT_MULTITHREADED); IMarshalThis* pMarshalThis = NULL; potlgit = new OtlGIT; if(!potlgit) return 0; // CoCreateInstance on the SomeATLObj HRESULT hr = CoCreateInstance(CLSID_MarshalThisObj, NULL, CLSCTX_INPROC_SERVER, IID_IMarshalThis, (void**)&pMarshalThis); if(SUCCEEDED(hr)) { // Register the interface pointer with the global // interface table potlgit->RegisterInterface(pMarshalThis, IID_IMarshalThis, &g_dwMarshalThis); // go ahead and call these functions from this apartment. // Then create a new thread in a new apartment DWORD dwTID; HANDLE hthread = CreateThread(0, 0, ThreadProc, pMarshalThis, 0, &dwTID); // Wait for the thread to exit and close the handle. // allow STA to dispatch calls AtlWaitWithMessageLoop( hthread ); CloseHandle(hthread); 470 pMarshalThis->Release(); potlgit->RevokeInterface(g_dwMarshalThis); } else { printf("Couldn't create CLSID_MarshalThisObj\n"); } if(potlgit) { delete potlgit; } printf( "\nPress any key to exit..." ); getchar(); CoUninitialize(); return 0; } 30.16Software Requirements Objective Toolkit for ATL is compatible with the latest 32-bit release of the Visual Studio compilers and their associated ATL versions supported by Stingray Studio. The following operating systems are supported (Intel processors only): Windows 2000 SP4 Windows 2003 Server Windows XP SP2 Windows Vista 30.17Distributing Objective Toolkit for ATL Applications Please read the license agreement that shipped with this package. You are bound by the licensing restrictions contained in that document. Do not use this product unless you accept all the terms of the license agreement. Chapter 30 Introduction to Objective Toolkit for ATL 471 472 INDEX A ActIDs 393 Active Template Library and Objective Toolkit 427 ActiveHost adding it to your application 341 introduction 339 samples 341 ActiveHost Form Editing Engine 339 ActiveScript Hosting Engine 339 sample 337 type library 335 ActiveScript, classes 333 ActiveScript, controlling inclusion of ScriptHost.tlb 335 ActiveScript,incorporating into your application 337 ActiveX wrapper, using ATL 428 Acts 393 adding scripting to your application 335 advanced docking windows architecture 344 configurations 346 examples 360 introduction 343 splitter styles 347 using 349 Advanced docking windows (ADW) 13 AFX_MANAGE_STATE 442 Agent 396 agent extensions 393–396 AgentServer 394 AgentSvr.exe 394 alignment layout 385 APP 423–425 app sample 425 architecture advanced docking windows 344 Asynchronous Pluggable Protocol. See APP ATL and Objective Toolkit 443– 471 and shortcut bars 199–205 basicDate example 442 ATLShortcut 205 auto-hide docking windows 137 B basicDate example 442 BHO 419 bitmapped dialog class hierarchy 245 customizing 246 display mode flags 246 introduction 245 using 246 browse edit controls 35, 36 browse button 35 class hierarchy 36 customizing 38 OnBrowse method 38 sample 38 using 37 Browser Helper Object 419 button controls alignment modes 42 class hierarchy 40 Button macros COMBO_BUTTON 101 TWOPART_BUTTON 101 Button map macros STD_BUTTON 99 TEXT_BUTTON 100 TEXT_BUTTON_EX 101 C calculator control class hierarchy 45 customizing 46 sample 47 SECCalculator 45 SECPopupCalendar 46 using 46 calendar control class hierarchy 48 customizing 50 introduction 48 key methods 49 sample 50 SECCalendar 48 CComModule 416 CDealListener 320 CDealProcessor 313 CHAIN_OTL_APPBAR() 464 Change Default Character command 394 cipher modes 289 class hierarchy customizable toolbar 91 Cmytreecontrol 433 CNSExtComp 401, 409 CNSExtCompEnum 401, 402 CNSExtCompIcon 401, 402, 412 CNSExtCompMenu 401, 402, 406, 411 CNSExtCompView 401, 402, 405, 406 color well control class hierarchy 51 introduction 51 sample 54 COM server 419 COMBO_BUTTON 101 compressed file classes class hierarchy 287 introduction 287 sample 288 SECCompressFile 288 using 288 compression algorithm 163 COtlAppBarImpl 464 COtlAppBarTabs 464 COtlLayoutFactory 466 COtlLayoutImpl 466 COtlLayoutNode 466 Index 473 COtlMSMQ 457, 458 COtlRelativeNode 466, 467 COtlScaleNode 466 COtlSimpleSafeArray 451 COtlWindowNode 466 COtlXMLDocument 460, 461 CRgDefListener 313 CRgProcessor 313 currency edit control class hierarchy 55 edit control messages 58 formats 56 sample 58 custom status bar class hierarchy 256 customizing 259 customizing panes 259 introduction 256 using 256 custom tokens 317 customizable toolbar button classes 96 button map macros 99, 100, 101 button state macros 102 button style macros 102 class hierarchy 91 classes 93, 94 creating new button types 103 customization dialogs 104 persistence 105 using 106 customizable toolbar button classes SECComboBtn 98 SECStdBtn 96 SECStdMenuBtn 97 SECTBTextBtn 97 SECTwoPartBtn 97 SECWndBtn 97 customizable toolbar classes SECNewToolBar 95 SECToolBarCmdPage 95 SECToolBarSheet 95 SECToolBarsPage 95 CWinApp 394 D data extraction 309 HTML 312 474 Index date formats predefined 61 date/time edit control class hierarchy 59 date formats 61 format strings 61 introduction 59 null date entry mode 62 sample 65 SECDateTimeCtrl,description 60 SECDTGadget 60 using 63 Deals sample listener 318 location 313 DHTML 419 directory edit control 36 distributing Objective Toolkit applications 17 distributing Objective Edit applications 5 docking views architecture overview 367 class hierarchy 365 CMultiDocTemplate 366 containment model 369 features 362 introduction 361 options 364 SECFrameBar 365 SECMDIChildWnd 366 using 371 WM_SYSCOMMANDEX 37 0 docking windows active 138 advanced architecture 344 auto-hide 137 auto-hide control bar 137 auto-hide pins 138 callback notifications 153 class hierarchy 140 control 144 creating auto-hide 139 embedding CViews 146 floating grippers 138 frame classes 141 horizontal text 138 icons 138 introduction 133 key members 157 message routing issues 143 sample 157 SECControlBar 142 SECControlBarManager 142 SECDialogBar 142 SECDockState 142 SECFrameWnd 141 SECMDIChildWnd 141 SECMDIFrameWnd 141 using 147 vertical frame docking 137 vertical text 138 documentation formats 4 dual interfaces 428 E ECB mode 289 encrypted file class 289 cipher modes topology 291 class hierarchy 289 encryption algorithm 290 introduction 289 limitations 292 password transformation 290 sample 293 SECBlackBox 290 SECCryptoFile 289 stream cipher 290 using 292 enhanced combobox class hierarchy 79 description 79 using 79 exception 294 exceptions, data extraction classes 322 extended progress control class hierarchy 77 customizing 78 customizing styles 78 description 77 extending 78 using 77 F FDI as compared to MTI 178 class hierarchy 178 converting an MDI application 179 converting an SDI application 178 creating a new application 179 introduction 177 sample 179 file system class class hierarchy 296 introduction 296 sample 298 SECFileSystem 296 using 296 FileDownloadInfo 424 floating document interface as compared to MTI 178 class hierarchy 178 converting an MDI application 179 converting an SDI application 178 creating a new application 179 introduction 177 sample 179 Format class definition 56 rules 56 full-screen view class 282 introduction 282 sample 285 styles 283 using 283 functor 449 and interface tokens 450 and threads 450 constructing 449 invoking 449 customizing 417 using 416–417 hyperlink sample 417 I IActiveScriptErrorHandler definition 334 IAgentApp 394, 396 CreateChar() 394 IAgentCharacterExPtr 394 ICollectionOnSTLImpl 445 icons, with auto-hide docking windows 138 Image classes PCX 161 TIFF 161 image classes bitmap 161 class hierarchy 160 GIF 161 internal format 162 introduction 159 JPEG 161 key methods 170 sample 170 TARGA 161 using 163 IMallocSpy 468 installing Namespace Extension Wizard 397 IPersistStreamInit 420 IWebBrowser2 419 G GIF images 163 gradient caption class hierarchy 247 introduction 247 SECFrameWnd 247 SECMDIFrameWnd 248 using 248 grid layout 386 gridbag layout 386 K keyboard shortcut classes SECCommandList 249 SECShortcutDlg 249 SECShortcutTable 249 keyboard shortcuts classes 249 excluded ids 252 introduction 249 notes 253 sample 253 saving 253 using 250 ktop 178 H hyperlink classes 415–417 L layout manager adding to your application 389 architecture 381 benefits 379 implementing a custom layout manager 392 introduction 377 layout algorithms 385, 386 layout issues 378 min/max sizes for layout nodes 392 relative layout 387 sample 392 layout manager architecture layout factory 384 splitter node 384 window listeners 384 layout manager examples grid layout 391 relative layout 390 scale layout 390 layout nodes 381 lbedit sample 70 license agreement 5 list box edit control 66 browse button 66 class hierarchy 66 command buttons 66 customizing 68, 69 SECListBoxDirEditor 68 SECListBoxEditor 67 SECListBoxFileEditor 67 using 68 LoadWBViaIPersist() 420 LogBinary() 295 LZW 163 M marquee control class hierarchy 71 customizing 72 customizing appearance 72 description 71 sample 73 using 71 masked edit control class hierarchy 74 creating masks 75 description 74 samples 76 SECMaskEdit 74 using 74 Index 475 MDI alternatives benefits 172 introduction 171 MTI 172 menu bars adding customizable 124 adding noncustomizable 127 bitmap menus without coollook toolbars 129 button map macros 121 class hierarchy 119 customizing pop-ups 120 introduction 117 removing close button while floating 128 sample 132 switching between menus 129 using bitmap menus in context menus 130 WM_EXTENDCONTEXTME NU 122 MessageBox 394, 396 MFC and shortcut bars 199–205 MFCShortcut 205 Microsoft Agent Objective Toolkit extensions 393–396 overview 393 Microsoft Office 2003 3D tab controls 87 control bars 86 menus 85 nested tabs 87 shortcut bars 87 status bars 89 window tabs 87 Msmq sample 458 MTI class hierarchy 174 converting an MDI interface 175 converting an SDI application 175 creating a new application 175 customizing 175 dispatching messages 176 introduction 172 loading the document 176 other uses 176 476 Index sample 177 multiple library configurations 13 multiple top level interface class hierarchy 174 converting an MDI interface 175 converting an SDI application 175 creating a new application 175 customizing 175 dispatching messages 176 introduction 172 loading the document 176 other uses 176 sample 177 multi-threaded logging class, use of 294 MultiThreadLogTraits 294 MultiTrace 294 exception handling 294 N Namespace Extension Wizard 397–413 installing 397 options 398–399 namespaces creating skeleton project 398 extending 397–413 tutorial 400–413 NM_TREEVIEW 226 notification messages 192 O Objective Edit license agreement 5 Objective Toolkit for ATL 443– 471 OFB mode 289 Office XP enabling 84 menu bars 83 toolbars 83 OtlGIT 448 OtlIContextMenuImpl 462 P pan and zoom views class hierarchy 323, 324 introduction 323 sample 330 SECPanWnd 324 using SECZoomView 326 zoom modes 325 pluggable protocol handlers 423 protocol handlers, pluggable 423 R random number class class hierarchy 299 introduction 299 sample 301 SECRandom 299 using 300 weighted results 299 REFLECT_NOTIFICATIONS() 4 65 Regex++ 309 registrar 453 registry class introduction 304 sample 307 using 304 RTTI 13 Layout Manager 13 Microsoft Agent 13 Outlook bar 13 S samples ActiveHost 341 app 425 FDI 179 hyperlink 417 lbedit 70 Msmq 458 on Rogue Wave Web site 4 ScriptMDI 337 tabbed windows 222 VIZ 198 ZappBar 464 SaveWBViaCache() 420 SaveWBViaDOM() 420 SaveWBViaIPersist() 420 SaveWBViaIPersistFile() 420 scale layout 385 scanner, set up 313 scanning a text stream 314 scripting adding to your application 335 ScriptMDI sample 337 SEC3DTabControl description 210 SEC3DTabWnd description 211 SECAAppObj,definition 333 SECAFloatDocTemplate definition 340 SECAFormObj, definition 333 SECAgentApp 394, 396 SECAgentCharAct 393, 394, 395 SECAgentCharacterExPtr 394, 395 SECAgentNotifySink 395 SECAgentPromptAtRectAct 395 SECAgentPromptAtWndAct 395 SECAgentSpeakAct 395 SECAScriptOccManager definition 334 SECATLShortcutBarHosted 200, 201, 203 SECATLShortcutBarWnd 200, 201 SECBitmapButton description 41 using 41 SECBrowseDirEdit description 36 SECBrowseEditBase definition 36 description 36 SECBrowseFileEdit definition 36 description 36 SECCalculator description 45 SECCalendar customizing 50 description 48 key methods 49 sample 50 SECColorWell customizing 53 description 52 key methods 53 sample 54 using 52 SECComboBoxEx class hierarchy 79 description 79 using 79 SECComboBtn description 98 SECCommandList description 249 SECCompressFile description 288 sample 288 using 288 SECControlBar 137, 139 description 142 SECControlBarManager description 142 SECCryptoFile class hierarchy 289 description 289 SECCurrency description 55 SECCurrencyEdit edit control messages 58 sample 58 SECCustomStatusBar class hierarchy 256 customizing 259 customizing panes 259 using 256 SECCustomToolBar 93 description 93 SECDateTimeCtrl 442 description 60 null date entry mode 62 SECDialogBar 142 description 142 SECDib description 161 SECDockableFrame 365 description 365 SECDockState description 142 SECDropEdit description 55 SECDTGadget description 60 subclasses 60 SECEncryptFile encryption algorithm 290 limitations 292 password transformation 290 sample 293 stream cipher 290 using 292 SECFDIChildWnd definition 178 SECFDIFrameWnd definition 178 SECFileSystem description 296 introduction 296 sample 298 using 296 SECFrameBar description 365 SECFrameWnd 137 description 141 SECFullScreenView description 282 sample 285 styles 283 using 283 SECGIF 163 SECGif description 161 SECHTMLView 420 SECHyperlink 415, 416, 417 SECIContextMenuImpl 399, 406 SECIDataObjectImpl 399 SECIDropSourceImpl 399 SECIDropTargetImpl 399 SECIEnumIDListImpl 402 SECIExtractIconImpl 399, 412 SECImage description 160 internal format 162 key methods 170 sample 170 SECIQueryInfoImpl 399 SECIShellViewImpl 402 SECJpeg description 161 SECLayoutFactory definition 384 SECLayoutNode definition 381 SECLayoutNodeSplitter definition 384 SECListBoxDirEditor description 68 SECListBoxEditor customizing 68 description 67 Index 477 extending 69 sample 70 window styles 69 SECListBoxFileEditor description 67 SECListCtrl description 224 SECListView description 225 SECMarquee class hierarchy 71 customizing 72 customizing appearance 72 description 71 sample 73 using 71 SECMaskEdit creating masks 75 description 74 samples 76 using 74 SECMDIChildWnd description 141, 366 SECMDIFrameWnd 137, 139 description 141 SECMDIMenuBar definition 123 SECMenuBar definition 119 SECMenuButton description 42 direction flags 43 using 42 SECMFCShortcutBarHosted 200, 201, 203 SECMFCShortcutBarWnd 200, 201 SECNewToolBar description 95 SECOwnerDrawButton customizing 41 definition 40 using 40 SECPanView 324 description 324 key methods 328 sample 330 using 326 SECPanWnd description 324 SECPcx 478 Index description 161 SECPidlData 404 SECPidlMgr 402, 404 SECPlugProt 424 SECPlugProtImp 424, 425 SECPopupCalculator description 46 SECPopupColorWell description 53 sample 54 SECProgressCtrl class hierarchy 77 customizing 78 customizing styles 78 description 77 extending 78 using 77 SECRandom class hierarchy 299 description 299 introduction 299 sample 301 using 300 weighted results 299 SECRegistry introduction 304 sample 307 using 304 SECRichHyperlink 415, 416, 417 SECScriptHostDoc definition 340 SECScriptHostView 340 definition 340 SECShortcutBar 192 SECShortcutBarComp 201 SECShortcutDlg description 249 SECShortcutTable description 249 SECSplashWnd description 254 key methods 254 samples 255 using 255 SECSplashWnd splash window key methods 254 SECStdBtn description 96 SECStdMenuBtn description 97 SECTabControl description 209 SECTabControlBase description 209 SECTabWnd description 210 SECTabWndBase description 210 SECTarga description 161 SECTiff 163 description 161 SECTipOfDay class hierarchy 264 resource ids 264 using 265 SECTNBitmap description 262 SECTNDC description 262 SECTNDocument description 262 SECTNFileDialog description 262 SECTNView description 262 SECTNWinApp description 263 SECToolBarCmdPage description 95 SECToolBarsBase 94 description 94 SECToolBarsDlg 94 description 94 SECToolBarSheet description 95 SECToolBarsPage description 95 SECToplevelFrame definition 174 other uses 176 SECTrayIcon introduction 268 sample 270 SECTreeCtrl description 224 SECTreeCtrl, building an ATL ActiveX Control 429 SECTreeView description 225 SECTwoPartBtn description 97 SECUserTool description 271 SECUserToolsDlg 272 description 272 SECWellButton customizing 44 description 43 using 44 SECWndBtn description 97 SECWndListener definition 384 SECWorkbookClientWnd definition 183 SECWorkbookWnd definition 182 SECWorkerThreadFetchObject 4 24, 425 SECWorksheetWnd definition 183 SECWorkspaceManager class hierarchy 273 using 280 SECWorkspaceManagerEx 275 dynamic controlbar support 274 multiple views support 275 using 275 vs. SECWorkspaceManager 274 SECZoomView 324 description 324 key methods 327 sample 330 zoom modes 325 SFL 10 shortcut bars class hierarchy 201 context menus 204 framework-specific 199–205 incorporating into an application 193 introduction 189 samples 205 styles 202 using non-windowed 203 using windowed 203 visual aspects 204 SingleThreadLogTraits 294 splash window class 254 classes 254 introduction 254 samples 255 using 255 STD_BUTTON 99 STD_MENU_BUTTON 100 Stingray Foundation Library (SFL) 10 styles look and feel 81 Microsoft Office 2003 85 Office XP 83 Visual Studio .NET 83 styles, of shortcut classes 202 sub expressions, sub states 317 T tabbed windows accessing the associated CWnd 219 adding a view 218 adding a window 218 adding to a dialog 216 changing font 220 class hierarchy 209 class SECTabControlBase 209 definition 209 introduction 207 keyboard accelerator support 217 notification messages 214, 220 position of tabs 216 removing a tab 219 samples 222 scroll bars 217 scroll buttons 216 styles 212 three dimensional 207 two dimensional 207 using 215 tabbed windows classes SEC3DTabControl 210 SEC3DTabWnd 211 SECTabControl 209 SECTabWnd 210 SECTabWndBase 210 text horizontal 138 vertical 138 TEXT_BUTTON 100 TEXT_BUTTON_EX 101 thumbnail classes class hierarchy 261 introduction 261 sample 263 SECTNBitmap 262 SECTNDC 262 SECTNDocument 262 SECTNFileDialog 262 SECTNView 262 SECTNWinApp 263 using 263 TIFF images 163 tip of the day dialog 396 caption 266 class hierarchy 264 introduction 264 modal 265 modeless 265 resource ids 264 using 265 token definitions 316 trace output 294 tray icon class handling mouse events 269 introduction 268 sample 270 using 268 tree control control notifications 234 creating dynamically 236 data structures 226 data structures, NM_TREEVIEW 226 data structures,TV_ITEM 226 image list 238 introduction 223 item colors 240 item fonts 241 item selection 240 multiple columns 237, 238 overlay image 240 sample 243 state image 238, 239 styles 232 using 236, 242 using in a dialog 236 tree control classes hierarchy 224 SECListCtrl 224 Index 479 SECTreeCtrl 224 tree control data structures TV_HITTESTINFO 228 tree control view classes hierarchy 225 SECListView 225 SECTreeView 225 TV_HITTESTINFO 228 TV_ITEM 226 TWOPART_BUTTON 101 U user tools menu classes 272 class hierarchy 271 introduction 271 sample 273 SECUserTool 271 using 272 utility classes introduction 287 V view classes class hierarchy 323, 324 introduction 323 key pan methods 328 key zoom methods 327 sample 330 SECPanWnd 324 using 326 zoom modes 325 Vigenere cipher 290 Visual Studio .NET enabling 84 menu bars 83 toolbars 83 VIZ sample 198 W WalkAndExamineIEBrowsers() 4 20 WBUtils 419 WDI changing the icon 184 changing the tab display order 183 class hierarchy 182 converting an MDI application 183 customizing 183 drawing a different tab 480 Index label 183 introduction 179 key methods and members 184 window styles 69 workbook document interface changing the icon 184 changing the tab display order 183 class hierarchy 182 converting an MDI application 183 customizing 183 drawing a different tab label 183 introduction 179 key methods and members 184 WorkerThreadMain 424 workspace manager adding application specific information 276, 278 class hierarchy 273 dynamic controlbar support 274 introduction 273 loading and saving state 276 multiple views support 275 samples 281 SECWorkspaceManager vs. SECWorkspaceManagerE x 274 using 275 using SECWorkspaceManager 280 Z ZappBar sample 464 Index 481 482 Index Index 483 484 Index Index 485 486 Index Index 487 488 Index Index 489 490 Index