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 &amp; 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