Objective Toolkit User’s Guide

Stingray® Studio

Version 11.2.0 OBJECTIVE TOOLKIT USER’S GUIDE

THIS MANUAL

© Copyright 1997-2014 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 typo- graphical 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 OTHER- WISE, 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 SPE- CIAL, 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: (303) 473-9118 (800) 487-3217 Fax: (303) 473-9137 Web: 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 7 2.3 Building Objective Toolkit 7 2.3.1 Using the Build Wizard 8 2.3.2 DLL naming issues 9 2.3.3 Compiler Flags 9 2.3.4 Build Target Naming Conventions 10 2.3.5 Build Configuration Options 11 2.3.6 To build the 32-bit or 64-bit libraries 11 2.3.7 Miscellaneous Build Issues 12 2.3.8 Building the Samples 13 2.3.9 Automatic linking 13 2.3.10 To incorporate Objective Toolkit into your application 14

Contents iii 2.4 Distributing Objective Toolkit Applications 16

2.5 Basic Tutorial—MaskEdit Control 17

2.6 Using Component Headers to Increase Application Build Performance 21 2.6.1 The Component Headers 21 2.6.2 To use the component headers in your project 23

3Chapter 3 The Objective Toolkit AppWizard

3.1 Overview 25

3.2 Creating a Skeleton Application 27

4Chapter 4 Simple Controls

4.1 Overview 33 4.2 Browse Edit Controls 33 4.2.1 Browse Edit Classes 34 4.2.2 Using the Browse Edit Classes 35 4.2.3 Customizing the Browse Edit Control 36 4.3 Browse Edit Sample 36

4.4 Button Controls 37

4.5 Button Class Hierarchy 38 4.5.1 SECOwnerDrawButton 38 4.5.2 Using SECOwnerDrawButton 38 4.5.3 Customizing SECOwnerDrawButton 39 4.5.4 SECBitmapButton 39 4.5.5 Using SECBitmapButton 39 4.5.6 SECBitmapButton alignment 40 4.5.7 SECMenuButton 40 4.5.8 Using SECMenuButton 40 4.5.9 SECMenuButton Menu Placement 41 4.5.10 SECWellButton 41 4.5.11 Using SECWellButton 42 4.5.12 Customizing SECWellButton 42

4.6 Calculator Control 43 4.6.1 SECCalculator 43 4.6.2 SECPopupCalculator 44 4.6.3 Using SECCalculator 44 4.6.4 Customizing SECCalculator 44 4.6.5 Calculator Sample 45

iv 4.7 Control 46 4.7.1 To incorporate the SECCalendar class into your code 46 4.7.2 SECCalendar Key Methods 47 4.7.3 Customizing SECCalendar 48 4.7.4 SECCalendar Sample 48

4.8 Color Well Control 49 4.8.1 SECColorWell 50 4.8.2 SECPopupColorWell 51 4.8.3 ColorWell Sample 52

4.9 Currency Edit Control 53 4.9.1 SECDropEdit 53 4.9.2 SECCurrencyEdit 53 4.9.3 Using SECCurrencyEdit 53 4.9.4 SECCurrencyEdit::Format 54 4.9.5 SECCurrencyEdit Messages 55 4.9.6 SECCurrencyEdit Sample 56

4.10 Date/Time Edit Control 57 4.10.1 SECDateTimeCtrl 58 4.10.2 SECDTGadget 58 4.10.3 Date Formats 59 4.10.4 Null Data Entry Mode 60 4.10.5 Using SECDateTimeCtrl 61 4.10.6 Date/Time Edit Control Sample 63

4.11 List Box Edit Control 64 4.11.1 SECListBoxEditor 65 4.11.2 SECListBoxFileEditor 65 4.11.3 SECListBoxDirEditor 65 4.11.4 Using the List Box Edit Classes 66 4.11.5 Customizing the List Box Edit Classes 66 4.11.6 Extending the Editable List Box Classes 67 4.11.7 Editable List Box Sample 68

4.12 Marquee Control 69 4.12.1 Using SECMarquee 69 4.12.2 Customizing SECMarquee 70 4.12.3 Marquee Sample 71

4.13 Masked Edit Control 72 4.13.1 SECMaskEdit 72 4.13.2 Using SECMaskEdit 72 4.13.3 Creating a Mask to Use with SECMaskEdit 73 4.13.4 Mask Edit Sample 74

v 4.14 Extended Progress Control 75 4.14.1 Using SECProgressCtrl 75 4.14.2 Customizing SECProgressCtrl 76 4.14.3 Extending SECProgressCtrl 76

4.15 Enhanced ComboBox with AutoComplete 77 4.15.1 The Enhanced ComboBox Class 77 4.15.2 Using SECComboBoxEx 77

5Chapter 5 Look and Feel Styles

5.1 Overview 79

5.2 Microsoft Vista Classic Style 80

5.3 Visual Studio .NET/Office XP Style 81 5.3.1 Enabling .NET/Office XP Styles 82

5.4 Microsoft Office 2003 Style 83 5.4.1 Enabling Office 2003 Styles 88

6Chapter 6 Customizable Toolbars

6.1 Overview 89

6.2 The Customizable Toolbar Classes 91 6.2.1 SECCustomToolBar 91 6.2.2 SECToolBarManager 92 6.2.3 SECToolBarsBase 92 6.2.4 SECToolBarsDlg 92 6.2.5 SECNewToolBar 93 6.2.6 SECToolBarsPage 93 6.2.7 SECToolBarCmdPage 93 6.2.8 SECToolBarSheet 93

6.3 The Toolbar Button Classes 94 6.3.1 SECStdBtn 94 6.3.2 SECStdMenuBtn 95 6.3.3 SECTwoPartBtn 95 6.3.4 SECTBTextBtn 95 6.3.5 SECWndBtn 95 6.3.6 SECComboBtn 96 6.4 Comparing SECToolbar to SECCustomToolBar 96

6.5 Toolbar Button Map 97 6.5.1 STD_BUTTON 97

vi 6.5.2 STD_MENU_BUTTON 98 6.5.3 TEXT_BUTTON 98 6.5.4 TEXT_BUTTON_EX 99 6.5.5 TWOPART_BUTTON 99 6.5.6 COMBO_BUTTON 99 6.6 Toolbar Button Styles 100 6.6.1 Button style macros 100 6.6.2 Button State Macros 100 6.7 Creating New Button Types 101

6.8 Customization Dialogs 102 6.8.1 Creating SECCustomToolBars—Arguments and Cautions 103 6.8.2 The Effect of Modifying Toolbars on Persistence 103

6.9 Using the Customizable Toolbar Classes 104 6.9.1 To incorporate customizable toolbars into your application 104 6.9.2 To implement toolbars with the flat cool look with a toolbar manager 105 6.9.3 To implement toolbars with the flat cool look without a toolbar manager 106 6.9.4 To implement button groups 106 6.9.5 To use multiple toolbar bitmap resources with the toolbar manager 107 6.9.6 To find a button on a customizable toolbar 107 6.9.7 To use the button map 107 6.9.8 To implement a text button 108 6.9.9 To implement a text button with styles 108 6.9.10 To implement a combo button 109 6.9.11 To implement a twopart button 109 6.9.12 To implement a bitmap button using styles 109 6.9.13 To make a customizable toolbar dockable 110 6.9.14 To reposition customizable toolbars at run time 110 6.9.15 To obtain a pointer to a specific customizable toolbar 110 6.9.16 To iterate the customizable toolbars 110 6.9.17 To implement the toolbar customization dialog 111 6.9.18 To invoke the toolbar customization dialog with a toolbar button 112 6.9.19 To hide and show customizable toolbars 112 6.9.20 To set the docking order of customizable toolbars 112 6.9.21 To changing the font for text buttons 112 6.9.22 To save and restore customizable toolbars 113 6.9.23 To draw owner-draw controls embedded in a vertically docked toolbar 113

7Chapter 7 Menu Bars

7.1 Overview 115

7.2 Menu Bar Classes 117 7.2.1 SECMenuBar 117 7.2.2 SECMDIMenuBar 117

vii 7.3 Customizing the Display of Menu Pop-ups 118

7.4 Menu Button Map Macros 119

7.5 WM_EXTENDCONTEXTMENU 120

7.6 Using the Menu Bar Classes 121 7.6.1 To Incorporate Objective Toolkit Menubars Into Your Code—Simple Case 121 7.6.2 To Incorporate Objective Toolkit Menubars Into Your Code--Advanced Case 122 7.6.3 To Implement SECMenuBar Or SECMDIMenuBar Without a Toolbar Manager 125 7.6.4 To remove the close button from a floating menu 126 7.6.5 To switch between menus 126 7.6.6 To use bitmap menus without the cool-look toolbars 127 7.6.7 To use bitmap menus in context menus 128

7.7 MenuBar Sample 130

8Chapter 8 Docking Windows

8.1 Overview 131

8.2 Features of Docking Windows 132

8.3 Flat-Style Drawing 134

8.4 Auto-Hide Docking Windows 135 8.4.1 Features 135 8.4.2 Creating Auto-Hide Docking Windows 137

8.5 The Docking Window Classes 138

8.6 Docking Window Frame Classes 139 8.6.1 SECFrameWnd 139 8.6.2 SECMDIFrameWnd 139 8.6.3 SECMDIChildWnd 139

8.7 Docking Window Control Bar Classes 140 8.7.1 SECControlBar 140 8.7.2 SECDialogBar 140 8.7.3 SECControlBarManager 140 8.7.4 SECDockState 140

viii 8.8 Message Routing Issues 141

8.9 Extended ControlBar Styles 142

8.10 Embedding CViews in Docking Windows 144

8.11 Using the Docking Window Architecture 145 8.11.1 To create an application with Objective Toolkit docking windows 145 8.11.2 To incorporate Objective Toolkit docking windows into an existing MDI application 145 8.11.3 To incorporate Objective Toolkit docking windows into an existing SDI application 146 8.11.4 To use Objective Toolkit docking windows inside an OLE IP 146 8.11.5 To create a docking window based on a dialog resource 148 8.11.6 To create a docking window not based on a dialog resource 149 8.11.7 To set the style of a docking window 149 8.11.8 To make a docking window dockable 149 8.11.9 To create a non-dockable control bar 150 8.11.10 To dock a docking window that is floating 150 8.11.11 To float a docking window that is docked 151 8.11.12 To make an SECDialogBar size diagonally when floated 151 8.11.13 To receive notifications when the docked state of a docking window changes 151 8.11.14 To hide a docking window 152 8.11.15 To control the docking location of a docking window 152 8.11.16 To determine where a docking window is docked 152 8.11.17 To determine the row and column of a docked window 153 8.11.18 To modify a control bar’s context menu 153 8.11.19 To add a toolbar to a control bar 154 8.11.20 To access controls in the docking window inside a message handler 154 8.11.21 To route messages to the client area of SECControlBar 154

8.12 Customizing Objective Toolkit Docking Windows 155 8.12.1 Key Extended Control Bar Members 155 8.13 Docking Windows Sample 155

9Chapter 9 Image Classes

9.1 Overview 157 9.2 The Image Classes 158 9.2.1 SECImage 158 9.2.2 SECDib 159 9.2.3 SECGif 159 9.2.4 SECJpeg 159 9.2.5 SECPcx 159 9.2.6 SECTarga 159 9.2.7 SECTiff 159

ix 9.3 SECImage Format 160

9.4 Using the Image Classes 161 9.4.1 To read an image from a file 161 9.4.2 To view GIF/TIFF images 161 9.4.3 To display an image 161 9.4.4 To convert an image 162 9.4.5 To copy an image 163 9.4.6 To manipulate an image 163 9.4.7 To write an image to a file 164 9.4.8 To convert to a CBitmap object 164 9.4.9 To convert from a CBitmap object 164 9.4.10 To create from a CDC object 165 9.4.11 To load an image from a resource 165 9.4.12 To stream image data 166

9.5 Key Image Methods 168 9.6 Image Sample 168

10Chapter 10 MDI Alternatives

10.1 Overview 169

10.2 Benefits of MDI Alternatives 170 10.2.1 Multiple Top-level Interface (MTI) 170 10.2.2 MTI Class – SECToplevelFrame 172 10.2.3 Floating Document Interface (FDI) 175 10.2.4 Workbook Document Interface (WDI) 177

11Chapter 11 Shortcut Bar

11.1 Overview 185

11.2 The Shortcut Bar Classes 187 11.2.1 SECShortcutBar 187 11.2.2 SECBar 188 11.2.3 SECListBar 188 11.2.4 SECShortcutListCtrl 188

11.3 Shortcut Bar Styles 189

11.4 Shortcut Bar Notification Messages 190

11.5 Shortcut Bar Callbacks 190

11.6 Using SECShortcutBar 191 11.6.1 To incorporate an SECShortcutBar into your application 191

x 11.6.2 To add selectable icons to a shortcut bar 191 11.6.3 To embed a window in a shortcut bar 192 11.6.4 To change the way the bars are drawn 192 11.6.5 To allow shortcut bars to behave like buttons 193 11.6.6 To enable context menus in a shortcut bar 193 11.6.7 To have the shortcut bars display the focus rectangle 194 11.6.8 To enable/disable animated scrolling 194 11.6.9 To receive notifications when an icon in the SECShortcutListCtrl is clicked 194 11.6.10 To determine which icon is clicked in an SECListBar window 195 11.6.11 To change the orientation of the shortcut bar at run time 196 11.6.12 To embed an SECShortcutBar into a splitter pane 196 11.7 Shortcut Bar Samples 196

12Chapter 12 Framework-Tailored Shortcut Bars

12.1 Overview 197

12.2 The Shortcut Bar Classes 199 12.2.1 ATL 199 12.2.2 MFC 199

12.3 Shortcut Bar Styles 200

12.4 Using the Shortcut Bar 201 12.4.1 Using the Windowed Shortcut Bar 201 12.4.2 Using the Non-Windowed Shortcut Bar 201 12.4.3 Setting visual aspects of the shortcut bar 202 12.4.4 Adding a context menu to the shortcut bar 202 12.5 Shortcut Bar Sample 203

13Chapter 13 Tabbed Windows

13.1 Overview 205

13.2 The Tabbed Window Classes 207 13.2.1 SECTabControlBase 207 13.2.2 SECTabControl 207 13.2.3 SEC3DTabControl 208 13.2.4 SECTabWndBase 208 13.2.5 SECTabWnd 208 13.2.6 SEC3DTabWnd 209

xi 13.3 Tabbed Window Styles 210

13.4 Tab Control Notification Messages 212

13.5 Using SECTabWnd and SEC3DTabWnd 213 13.5.1 To add SECTabWnd or SEC3DTabWnd to a frame window 213 13.5.2 To add a tabbed window to a dialog 214 13.5.3 Removing the 2D Tab Scroll Buttons 214 13.5.4 To put 3D tabs on the side or top of the tabbed window 214 13.5.5 To enable scroll bars in the 2D tabbed window 215 13.5.6 To add keyboard accelerator support 215 13.5.7 To add a window to the tabbed window 216 13.5.8 To create and add a view to the tabbed window 216 13.5.9 To remove a tab 217 13.5.10 To access the CWnd associated with a tab 217 13.5.11 To change the font of a tab 218 13.5.12 To receive user event notifications from the tabbed window 218 13.5.13 To get a pointer to the SECTabWnd from a contained view 218 13.5.14 To insert a splitter window into a tabbed window 219 13.5.15 Problem with Tabbed Windows in Docking Views 219

13.6 Tabbed Window Sample 220

14Chapter 14 Tree Control & Tree View

14.1 Overview 221

14.2 The Tree Control Classes 222 14.2.1 SECTreeCtrl 222 14.2.2 SECListCtrl 222

14.3 The Tree View Classes 223 14.3.1 SECTreeView 223 14.3.2 SECListView 223

14.4 Tree Control Data Structures 224 14.4.1 TV_ITEM 224 14.4.2 NM_TREEVIEW 224 14.4.3 TV_HITTESTINFO 225

14.5 Tree Item States 226

14.6 Tree Control/Tree View Styles 228

14.7 Tree Control Notifications 232

14.8 Using the Tree Control Classes 234 14.8.1 To create a tree control in a dialog 234 14.8.2 To create a tree control dynamically 234 xii 14.8.3 To add a tree item 235 14.8.4 To create multiple columns 235 14.8.5 To set the text on subitems of multi-column trees 236 14.8.6 To create a standard image list for tree items 236 14.8.7 To create a state image list for tree items 236 14.8.8 To create a tree item with a state image 236 14.8.9 To change a state image on a tree item 237 14.8.10 To add an overlay image to a tree item 238 14.8.11 To find out which items are selected 238 14.8.12 To specify different colors for tree items 238 14.8.13 To specify different fonts for tree items 239 14.8.14 To update the tree control 239 14.8.15 To incorporate SECTreeCtrl into an application already using CtreeCtrl 240

14.9 Tree Control Samples 241

15Chapter 15 User Interface Extensions

15.1 Overview 243 15.2 Bitmapped Dialog 243 15.2.1 Using SECBitmapDialog 244 15.2.2 Customizing SECBitmapDialog 244 15.3 Gradient Caption Extension 245 15.3.1 The Gradient Caption Classes 245 15.3.2 SECFrameWnd 245 15.3.3 SECMDIFrameWnd 246 15.3.4 Using the Gradient Caption Feature 246

15.4 Keyboard Shortcuts 247 15.4.1 The Keyboard Shortcut Classes 247 15.4.2 SECShortcutTable 247 15.4.3 SECCommandList 247 15.4.4 SECShortcutDlg 247 15.4.5 Using the Keyboard Shortcut Classes 248 15.4.6 Keyboard Shortcut Sample 251

15.5 Splash Window 252 15.5.1 The SECSplashWnd Class 252 15.5.2 Using SECSplashWnd 253 15.5.3 SECSplashWnd Samples 253

15.6 Custom Status Bar 254 15.6.1 Using SECCustomStatusBar 254 15.6.2 Customizing SECCustomStatusBar 257

xiii 15.7 Thumbnail Classes 259 15.7.1 The Thumbnail Classes 259 15.7.2 Using the Thumbnail Classes 261 15.7.3 Thumbnail Sample 261

15.8 Tip of the Day Dialog 262 15.8.1 The SECTipOfDay Class 262 15.8.2 SECTipOfDay Resource IDs 262 15.8.3 Using SECTipOfDay 263 15.8.4 SECTipOfDay Sample 265

15.9 Tray Icon Class 266 15.9.1 To incorporate the Tray Icon Class into your application 266 15.9.2 To add notification handlers for mouse events 267 15.9.3 To animate a tray icon 268 15.9.4 Tray Icon Sample 268

15.10User-Tools Menu 269 15.10.1 The User-Tools Menu Classes 269 15.10.2 Using the User-Tools Menu Classes 270 15.10.3 User-Tool Menu Sample 271 15.11Workspace Manager 271 15.11.1 The Workspace Manager Classes 271 15.11.2 Using SECWorkspaceManagerEx 273 15.11.3 Using SECWorkspaceManager 278 15.11.4 Workspace Manager Samples 279

15.12Full-Screen View 280 15.12.1 The Full-Screen View Class 280 15.12.2 SECFullScreenView Styles 281 15.12.3 Using the SECFullScreenView Class 281 15.12.4 SECFullScreenView Sample 283

16Chapter 16 Utility Classes

16.1 Overview 285 16.2 Compressed File I/O 285 16.2.1 SECCompressFile 285 16.2.2 Using SECCompressFile 286 16.2.3 SECCompressFile Sample 286

16.3 Encrypted File I/O 287 16.3.1 Electronic Codebook (ECB) 287 16.3.2 Output Feedback Mode (OFB) 287 16.3.3 The SECCryptoFile Classes 287

xiv 16.3.4 The Encryption Algorithm 288 16.3.5 Limitations 290 16.3.6 Using SECCryptoFile 290 16.3.7 SECCryptoFile Sample 291

16.4 Safe Multi-Threaded Trace Output 292 16.4.1 Use of the Multi-Threaded Logging Class 292

16.5 Access 294 16.5.1 SECFileSystem 294 16.5.2 Using SECFileSystem 294 16.5.3 SECFileSystem Sample 296

16.6 Random Number Generation 297 16.6.1 The SECRandom Class 297 16.6.2 Using SECRandom 298 16.6.3 SECRandom Sample 299

16.7 Formula Engine 300 16.7.1 Features of the Formula Scanner 300 16.7.2 Use of the Formula Engine Class 300

16.8 Win32 Registry Access 302 16.8.1 The SECRegistry Class 302 16.8.2 Using SECRegistry 302 16.8.3 SECRegistry Sample 305

17Chapter 17 Data Extraction Classes

17.1 Overview 307 17.1.1 Building the Libraries 307 17.1.2 Using Regular Expression Libraries 309

17.2 Data Extraction Framework 310

17.3 Example: Setting Up a Scanner 311 17.3.1 Introduction to the Deals Sample 311 17.3.2 Declaring the Processor 311 17.3.3 Implementation Details 313

xv 17.4 Note on Exceptions 320

18Chapter 18 View Classes

18.1 Overview 321

18.2 The Zoom and Pan View Classes 321

18.3 SECZoomView 322 18.3.1 SECPanView 322 18.3.2 SECPanWnd 322

18.4 Zoom Modes 323

18.5 Using the View Classes 324 18.5.1 To incorporate zooming support into an application 324 18.5.2 To incorporate panning support into an application 324 18.5.3 To incorporate a panning overview window to an application 325 18.5.4 Key Zooming Methods 325 18.5.5 Key Panning Methods 326

18.6 Zooming/Panning Sample 328

19Chapter 19 ActiveScript Hosting Framework

19.1 Overview 329

19.2 Overview of JavaScript 330 19.3 VBScript 330 19.4 Hosting an Active Script 330 19.5 ActiveScript Classes 331 19.5.1 SECAAppObj 331 19.5.2 SECAFormObj 331 19.5.3 SECAScriptHost 331 19.5.4 SECAScriptOccManager 332 19.5.5 ActiveScriptErrorHandler 332

19.6 Using the ActiveScript Framework 333 19.6.1 ActiveScript and Type-libraries 333 19.6.2 To prevent the Objective Toolkit library from automatically including ScriptHost.tlb as resource 333 19.6.3 To incorporate scripting into your application 333

xvi 19.7 ActiveScript Sample 335

20Chapter 20ActiveHost Form Scripting and Editing Framework

20.1 Overview 337

20.2 ActiveHost Classes 338 20.2.1 SECScriptHostDoc 338 20.2.2 SECScriptHostView 338 20.2.3 SECAFloatDocTemplate 338 20.2.4 SECADlgFrame 338

20.3 Using the ActiveHost Form Editing Framework 339 20.3.1 To incorporate ActiveHost into your application 339 20.4 The ActiveHost Sample 339

21Chapter 21 Advanced Docking Windows

21.1 Overview 341

21.2 Advanced Docking Windows Architecture 342

21.3 Advanced Docking Windows Features 343 21.3.1 Docking Inside an MDI Child Frame 343 21.3.2 Floating MultiDock Mode 343 21.3.3 Realtime Drag Mode 343 21.3.4 Alternate Border Layout Logic 343 21.3.5 Advanced Docking Configurations 344

21.4 Advanced Docking Windows Splitter Styles 345

21.5 Using the Advanced Docking Windows Architecture 347 21.5.1 To incorporate advanced docking windows into your application 347 21.5.2 To create dockable device context nodes 350 21.5.3 To use docking insertion constraints 351 21.5.4 To adjust the border sizing 353 21.5.5 To use ‘real-time’ drag mode 354 21.5.6 To use floating multidock mode 354 21.5.7 To use alternate border layout logic 354 21.5.8 To integrate a dockable node inside an MDI child frame 356

xvii 21.6 Advanced Docking Windows Examples 358

22Chapter 22 Docking Views

22.1 Overview 359

22.2 Features of Docking Views 360

22.3 Issues when Docking a CView 361

22.4 Docking Views Options 362

22.5 The Docking Views Classes 363 22.5.1 SECDockableFrame 363 22.5.2 SECFrameBar 363 22.5.3 SECMDIChildWnd 364 22.5.4 SECMultiDocTemplate 364

22.6 Architectural Overview 365

22.7 Docking Views Containment Model 366

22.8 WM_SYSCOMMANDEX 368

22.9 Docking Views and MDI Alternatives 369 22.10Using the Docking Views Architecture 369 22.10.1 To incorporate docking views into your application 369 22.10.2 To set docking view styles 369 22.10.3 To toggle the presence of the docking button on a dockable view frame 369 22.10.4 To disable right mouse double-clicks on the docking view caption bar 371 22.10.5 To create an initially docked view 371 22.10.6 To start an application with no initial view 372 22.10.7 To put a splitter in a docking view 372 22.10.8 To dock a view 373 22.10.9 To float a view as an MDI child 373 22.10.10 To obtain a pointer to the view 373 22.10.11 To control the initial size and position of a docking view as it is docked 374

23Chapter 23 Layout Manager Framework

23.1 Overview 375

23.2 Issues with Resizable Windows 376

23.3 Benefits of the Objective Toolkit Layout Manager 377 23.3.1 Objective Toolkit Layout Manager: MFC Integration 377 23.3.2 Objective Toolkit Layout Manager: Strong Architecture 377 23.3.3 Objective Toolkit Layout Manager: Ease of Integration 377 xviii 23.4 Layout Manager Architecture 379 23.4.1 Layout Nodes 379 23.4.2 Window Listeners 382 23.4.3 Layout Factory 382 23.4.4 Splitter Node 382

23.5 Layout Algorithms 383 23.5.1 Alignment Layout 383 23.5.2 Scale Layout 383 23.5.3 Grid Layout 384 23.5.4 GridBag Layout 384 23.5.5 Relative Layout 385

23.6 Using the Layout Manager 387 23.6.1 Adding Layout Management to Your Applications 387

23.7 Layout Manager Examples 388 23.7.1 Scale Layout in a Dialog 388 23.7.2 Relative Layout in a Dialog 388 23.7.3 Grid Layout, Alignment Layout and Splitter in a Formview 389 23.7.4 To specify min/max sizes for layout nodes 390 23.7.5 To implement a custom layout manager 390 23.8 Layout Manager Sample 390

24Chapter 24 Microsoft Agent Extensions

24.1 Overview 391 24.2 Overview of Microsoft Agent Technology 391

24.3 Agent Extension Classes 392 24.3.1 SECAgentCharacterExPtr 392 24.3.2 IAgentApp 392 24.3.3 SECAgentApp 392 24.3.4 SECAgentCharAct 393 24.3.5 SECAgentNotifySink 393

24.4 Using the Agent Extension Classes In Your Applications 394 24.4.1 Agent Extensions Sample 394

xix 25Chapter 25 Namespace Extension Wizard

25.1 Overview 395 25.2 Installing Stingray Namespace Extension Wizard 395

25.3 Creating a Skeleton Namespace Extension 396 25.4 Selecting Namespace Options 396 25.4.1 Show Up 396 25.4.2 Register For 396 25.4.3 Support UI Object 397

25.5 Tutorial 398 25.5.1 Create the Skeleton Namespace Extension Project 398 25.5.2 Work Through the Generated Code 399 25.5.3 Change The Data Structure For PIDL 401 25.5.4 Implementing CreateEnumIDList() 401 25.5.5 Modify CNSExtCompView::InitList() 403 25.5.6 Give Each Node an Informative Name 407 25.5.7 Change the Default Context Menu Handling 408 25.5.8 Give Node An Icon 410

26Chapter 26 TheHyperlinkClasses

26.1 Overview 413

26.2 Using the Hyperlink Classes 414 26.3 Customizing the Hyperlink Control 415 26.4 Sample 415

27Chapter 27 Web Browser Extensions

27.1 Overview 417 27.2 Feature List 417 27.2.1 Getting the IWebBrowser2 Interface in IE 417 27.2.2 Init IWebBrowser2 with HTML in Memory 418 27.2.3 Retrieve HTML in IWebBrowser2 418 27.2.4 CHTMLView Extensions 418 27.2.5 Miscellaneous Utility Functions 419

xx 27.3 Sample 419

28Chapter 28 The APP ATL Object

28.1 Overview 421 28.1.1 Overview of Asynchronous Pluggable Protocol 421

28.2 Objective Toolkit APP ATL Object Classes 422 28.2.1 SECPlugProt 422 28.2.2 SECPlugProtImp 422 28.2.3 FileDownloadInfo 422 28.2.4 SECWorkerThreadFetchObject 422 28.2.5 WorkerThreadMain 422

28.3 Using the Objective Toolkit APP ATL Object in Your Applications 423 28.4 Sample 423

29Chapter 29 ATL and Objective Toolkit Components

29.1 Overview 425

29.2 Wrapping Objective Toolkit Components in an ATL ActiveX Control 426

29.3 An Example: An ATL ActiveX Control Built From SECTreeCtrl 427 29.3.1 Pre-Build Set-Up 431 29.3.2 Building Your Control 433 29.3.3 Testing Your Control 433

29.4 Further Extensions 439 29.5 Sample Code 440

30Chapter 30 Introduction to Objective Toolkit for ATL

30.1 Overview 441

30.2 Features and Benefits 442 30.2.1 Features of Objective Toolkit for ATL 442

30.3 COM Collection Classes 443

30.4 Threading Classes 445

30.5 Interface Token Class 446

30.6 Functors 447 30.6.1 Constructing Functors 447

xxi 30.6.2 Invoking Functors 447 30.6.3 Interface Tokens and Functors 448 30.6.4 Threads and Functors 448

30.7 SAFEARRAY Classes 449 30.7.1 COtlSimpleSafeArray 449

30.8 RGSEdit 451 30.8.1 Changing What Gets Registered 451 30.8.2 Editing a Registry Script 452 30.8.3 Adding Keys 452 30.8.4 Editing Keys 452 30.8.5 Deleting Keys 452 30.8.6 Adding Values 452 30.8.7 Editing Values 453 30.8.8 Managing Categories 453 30.8.9 Launching RGSEdit from the IDE 454

30.9 Microsoft Message Queue Class 455 30.9.1 Requirements 455 30.9.2 Creating a queue 455 30.9.3 Opening a queue 455 30.9.4 Receiving Messages 456 30.9.5 Sending Messages to a Queue 456 30.9.6 Cleanup 457

30.10XML Helpers 458 30.10.1 Creating an XML Document From Scratch 458 30.10.2 Opening an Existing XML Document 458 30.10.3 Adding Tags 458 30.10.4 Saving the XML Document to a File 458 30.10.5 Reading Tag Values 459 30.10.6 Trace Output 459

30.11Internet Explorer Band Object Wizard and Classes 460 30.11.1 Changing Size Constraints 460 30.11.2 Context Menu Commands 460

30.12Desktop Application Toolbar Class and Object Wizard 462 30.12.1 Creating an AppBar 462 30.12.2 Docking and Layout 462 30.12.3 Appbar Tab Window Control 462 30.12.4 Creation 463 30.12.5 Message Reflection 463 30.12.6 Image List 463 30.12.7 Selection Notification 463

xxii 30.13Window Layout Manager for Composite Controls 464 30.13.1 Layout Manager Algorithms 464 30.13.2 Scale Layout 464 30.13.3 Relative Layout 464 30.13.4 Adding Layout Management to Your Applications 465

30.14COM Task Allocator Memory Debugging Tools 466 30.14.1 Using the Task Allocator Debugger 466

30.15Marshaling Classes 467 30.16Software Requirements 469 30.17Distributing Objective Toolkit for ATL Applications 469 Index 471

xxiii xxiv 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 Mic- rosoft 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 func- tionality. 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 com- pilers (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 \Samples\Toolkit\, 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 Plat- forms” 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 follow- ing 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 Sting- ray 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 Sup- port 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 devel- opment, 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 licens- ing 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 dis- tribute 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

Before continuing, ensure that Objective Toolkit has been properly installed on your system. If you have any questions about installation requirements, please refer to the Installation Guide.

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 Plat- forms.) 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 rec- ommend, 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 sys- tem they are built on.

Chapter 2 Objective Toolkit Quick Start 7 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 Tool- kit 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 auto- matically 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 dif- ferent 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.

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.

8 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 tar- get 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 debug- ging, Unicode characters, and linking to MFC as a DLL.

The flags in Table 1 are specific to Objective Toolkit.

Chapter 2 Objective Toolkit Quick Start 9 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 pre- vents this problem from occurring.

2.3.4 Build Target Naming Conventions

The library naming conventions are illustrated in Figure 1. Figure 1 – Build Configurations

10 2.3.5 Build Configuration Options

The following table shows the default library names for the various configurations, where stands for the current product version number. Refer to the naming convention described above.

Library Objective MFC Unicode Build Type name Toolkit library configuration supported configuration

OT Static Static No Release

OTd Static Static No Debug

OTa Static DLL No Release

OTad Static DLL No Debug

OTas DLL DLL No Release

OTasd DLL DLL No Debug

OTu Static Static Yes Release

OTud Static Static Yes Debug

OTau Static DLL Yes Release

OTaud Static DLL Yes Debug

OTasu DLL DLL Yes Release

OTasu DLL DLL Yes Debug d

Objective Toolkit makefiles place .lib, .dll and .pdb files into the directory OT\lib\\\ (where is either vc71, vc8, or vc9, and 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 Sting- ray Studio include, source, library and executable directory paths. Please refer to Section 2.6.3, “Stingray Studio Paths in Property Sheets,” 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.

Chapter 2 Objective Toolkit Quick Start 11 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 prob- lems. 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.2, “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/).

12 2.3.8 Building the Samples

Each sample project has a variety of build options to show you how to use the various build config- urations. 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 Objective Toolkit as a static library, MFC as a Debug static library, ANSI, Debug

WIN32 or x64 – Lib MFC Lib Objective Toolkit as a static library, MFC as a Release static library, ANSI, Release

WIN32 or x64 – Lib MFC DLL Objective Toolkit as a static library, MFC as a Debug DLL, ANSI, Debug

WIN32 or x64 – Lib MFC DLL Objective Toolkit as a static library, MFC as Release DLL, ANSI, Release

WIN32 or x64 – DLL MFC DLL Objective Toolkit as a DLL, MFC as a DLL, Debug ANSI, Debug

WIN32 or x64 – DLL MFC DLL Objective Toolkit as a DLL, MFC as a DLL, Release ANSI, Release

WIN32 or x64 – Lib MFC Lib Uni Objective Toolkit as a static library, MFC as a Debug static library, Unicode, Debug

WIN32 or x64 – Lib MFC Lib Uni Objective Toolkit as a static library, MFC as a Release static library, Unicode, Release

WIN32 or x64 – Lib MFC DLL Objective Toolkit as a static library, MFC as a Uni Debug DLL, Unicode, Debug

WIN32 or x64 – Lib MFC DLL Objective Toolkit as a static library, MFC as a Uni Release DLL, Unicode, Release

WIN32 or x64 – DLL MFC DLL Objective Toolkit as a DLL, MFC as a DLL, Uni- Uni Debug code, Debug

WIN32 or x64 – DLL MFC DLL Objective Toolkit as a DLL, MFC as a DLL, Uni- Uni Release code, 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 configu- rations, you can see how the compiler flags link to the appropriate Objective Toolkit library variant automatically.

Chapter 2 Objective Toolkit Quick Start 13 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 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 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 read- only 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 Set- tings|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 Tool- kit samples that demonstrate the use and capabilities of the class.

14 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.

Chapter 2 Objective Toolkit Quick Start 15 2.4 Distributing Objective Toolkit Applications

Please read the license agreement that was shipped with this package. You are bound by the licens- ing 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 dis- tribute 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.

16 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 abil- ity 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).

Chapter 2 Objective Toolkit Quick Start 17 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 avail- able to your project. Find the stdafx.h file for the project and add the following line:

#include

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 // MFC core and standard components #include // MFC extensions #include // MFC OLE automation classes #ifndef _AFX_NO_AFXCMN_SUPPORT #include // MFC support for Windows Common Controls #endif // _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"

18 #include // 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.

Chapter 2 Objective Toolkit Quick Start 19 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.

20 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

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

Chapter 2 Objective Toolkit Quick Start 21 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 toolkit\ot_fdi.h (FDI)

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 toolkit\ot_mti.h (MTI)

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

22 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 toolkit\ot_wdi.h (WDI)

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

Add one or more of the component headers to stdafx.h, depending on which components your application requires. For example:

#include // Docking Windows #include // 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

Chapter 2 Objective Toolkit Quick Start 23 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.

24 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 owner- draw).

 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 Infor- mation (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 25 Figure 2 – Sample Application Generated Using AppWizard

26 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 proj- ect 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 Applica- tion AppWizard. The Objective Toolkit-specific portions of the AppWizard appear after you complete the Finished panel.

Chapter 3 The Objective Toolkit AppWizard 27 Figure 4 – Step 6 of the AppWizard

2. Instead of clicking Finish, click Next to view the remaining panels and choose options spe- cific to Objective Toolkit. Figure 5 – Choosing options specific to Objective Toolkit

28 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 stati- cally, remove the #define _SECDLL statement.

Select Enable Toolbar Customization to add an SECCustomToolBar with the two-bar grip- per as your main toolbar.

By selecting Enable Toolbar Customization, you also enable the toolbar manager and cus- tomization 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 win- dow with a selection of child windows to place inside the docking window.

Chapter 3 The Objective Toolkit AppWizard 29 30 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 posi- tioning 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 31 7. Click OK. The AppWizard closes. You can examine the new project in Visual Studio.

8. If you are using Visual Studio 2010 or later, please refer to Section 2.6.3, “Stingray Studio Paths in Property Sheets,” 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.

32 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 immedi- ately 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 33 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 function- ality 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 but- ton, 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.

34 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 creat- ing 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 35 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.

36 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 37 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.

38 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 draw- ing 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 39 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 con- trol 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 align- ment 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.

40 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 place- ment 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 41 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 con- trol 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.

 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.

42 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 43 4.6.2 SECPopupCalculator

The SECPopupCalculator class creates a pop-up window for the calculator that is destroyed when- ever 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.).

 LoadDecSeparator(). Override to change the decimal separator displayed.

44 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 45 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 drop- down 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:

46 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 val- ues 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 47 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 prod- uct. For information on how to obtain this sample, see Section 3.6.1, “Location of Sample Code,” in the Stingray Studio Getting Started Guide.

48 4.8 Color Well Control

The Objective Toolkit Color Well classes provide a sophisticated Color Selection control. This con- trol 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 49 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.

50 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 unse- lected 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 com- mon 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 51 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.

52 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 acti- vates a calculator to help in the data entry.

4.9.2 SECCurrencyEdit

SECCurrencyEdit provides an extensible class for entering and displaying custom-formatted cur- rency 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 cus- tomize 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 53 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:

 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-$111$

2$-12$ 1

54 Negative Format Positive Format

3 $1- 3 1 $

4 (1$)

5-1$

61-$

71$-

8 -1 $

9-$ 1

10 $ 1-

The ParseValue() and FormatValue() methods convert between a numeric and a string represen- tation. 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 55 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 con- trol 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 Tool- kitcurrency 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.

56 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 CObject

SECDateTimeCtrl SECDTGadget

Chapter 4 Simple Controls 57 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 cen- tury 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:

 SECDTStaticGadget. Implements a non-editable text gadget.

 SECDTNumericGadget. Implements an editable numeric gadget.

58  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 for- mat 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 July 21, 1998 format string (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 59 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

MMonth

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 infor- mation 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.

60 Figure 28 – Using Date Time Controls in Null Data Entry Mode

When not in null date entry mode, GetDateTime() always returns a COleDateTime object contain- ing 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 61 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)

62 { //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 63 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 dia- log 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

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.

64 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 chap- ter 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 65 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 direc- tory 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.

66 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 67 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 implemen- tations 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, “Loca- tion of Sample Code,” in the Stingray Studio Getting Started Guide.

68 4.12 Marquee Control

The SECMarquee implements a marquee control that scrolls text across its window. You can config- ure 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 69 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

Method Behavior

Draw3Dborder() Draws a 3D border around the marquee control.

DoPaint() Paints the marquee control.

70 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 71 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.

72 To retrieve the data from the masked edit control:

Call GetData() to retrieve the data without the mask or GetRawData() to retrieve the entire con- tents 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 73 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 direc- tory 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.

74 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 customiza- tions 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 75 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 config- ure 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

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.

76 4.15 Enhanced ComboBox with AutoComplete

The SECComboBoxEx class extends MFC’s CComboBoxEx class by providing a type ahead or auto- complete feature. The Address combobox in MS 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 77 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("\\Include");

m_combo.InsertItem(&comboItem);

where 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 charac- ters 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 key with the following calls.

m_combo.SetDelimiterKey(VK_TAB); m_combo.SetDelimiterString(_T("\\"));

78 Chapter 5 Look and Feel Styles

5.1 Overview

Objective Toolkit, along with the SFLEx libraries, provide developers with components that simu- late Visual Studio application styles or themes, such as APPSTYLE_OFFICE_*, where * denotes XP, BLUE, BLACK, SILVER, AQUA or 2003. Other application styles include APPSTYLE_WIN_2000, APPSTYLE_WIN_XP, APPSTYLE_VS_2005, APPSTYLE_VS_2008, and APPSTYLE_WIN_7.

You cannot dynamically switch between look-and-feel styles without some noticeable drawing side effects with tool- bar 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 79 5.2 Microsoft Vista Classic Style

The Vista Classic style feature allows your applications to take on a look and fill similar to Win- dows 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. MFC- derived 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 \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:

 RW_VISUALSTYLE_WINDOWSCLASSIC

 RW_VISUALSTYLE_DOTNET

 RW_VISUALSTYLE_OFFICE2003

 RW_VISUALSTYLE_VISTACLASSIC.

80 5.3 Visual Studio .NET/Office XP Style

The following are examples of the styles of Visual Studio.NET/Office XP components that Objec- tive 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 81 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.”

82 5.4 Microsoft Office 2003 Style

The Office 2003 style feature allows your applications to take on a similar look and feel to the Mic- rosoft 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 83 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

84 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 85 Figure 64 – Tabs on left with double-nested tabs on bottom

Figure 65 – Tabs on top with double-nested tabs on bottom

86 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 87 5.4.1 Enabling Office 2003 Styles

To enable Microsoft Office 2003 styles, add RWSetOfc2003Style (TRUE); in your main applica- tion 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 auto- matically 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.

88 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. Some- times 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 89 Figure 70 – Example Toolbar Customization Property Sheet

90 6.2 The Customizable Toolbar Classes

The hierarchies of the customizable toolbar classes are as follows: Figure 71 – Customizable Toolbar Class Hierarchies

CControlBar CObject

SECControlBar SECControlBarManager

SECCustomToolBar SECToolBarManager

SECToolBarsBase CPropertyPage

CDialog SECToolBarCmdPage

SECToolBarsDlg SECWndBtn

CPropertySheet CComboBox

SECToolBarSheet SECComboBtn

SECToolBarsPage 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 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 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 91 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 can- not 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 tool- bar 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.

92 6.2.5 SECNewToolBar

SECNewToolBar constructs the New Toolbar dialog. This dialog is invoked from within the Cus- tomize 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 per- form 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 93 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 cus- tomizable 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 tool- bars 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 as- needed 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 infor- mation, 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.

94 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 dis- plays 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 95 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.

96 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 but- tons, 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 win- dow 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 intro- ducing 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 97 6.5.2 STD_MENU_BUTTON

The STD_MENU_BUTTON macro extends the standard customizable toolbar by creating a bitmap tool- bar 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 Description Parameter

ButtonID The command ID for the button. This command is not actually exe- cuted 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)

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).

98 6.5.4 TEXT_BUTTON_EX

The TEXT_BUTTON_EX macro is similar to TEXT_BUTTON (it implements a customizable toolbar but- ton 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 99 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.

6.6 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 logi- cally 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 | Marks the start of a group of check buttons. TBBS_CHECKBOX

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.

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

100 TBBS_HIDDEN TBSTATE_HIDDEN Button is hidden.

TBBS_WRAPPED TBSTATE_WRAP Button is wrapped at this point.

6.7 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 101 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

102 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 103 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 com- mand 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 essen- tially 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)));

104 // 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, includ- ing 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 105 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 spe- cific 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 asso- ciated 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.

106 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 mem- ber 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;nIndexm_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 107 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 implementa- tion 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);

108 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 implementa- tion 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 109 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 mem- ber 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 } }

110 6.9.17 To implement the toolbar customization dialog

1. Incorporate toolbars with a toolbar manager. See Section 6.9.1, “To incorporate customiz- able 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 111 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 construc- tor. For example:

112 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 113 114 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 115 Figure 78 – Sample Application with Objective Toolkit customizable menu bars

116 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 appropri- ate 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 applica- tion 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 but- tons 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 infor- mation on using this class.

Chapter 7 Menu Bars 117 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 contain- ing 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 han- dler 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).

118 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_N- ODISABLE 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 119 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.

120 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(,

,,...,);

For example:

pMgr->SetMenuInfo(4,IDR_MAINFRAME,IDR_EDITVIEW, IDR_LISTVIEW, IDR_FILEVIEW);

Chapter 7 Menu Bars 121 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 docu- ment 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 0x80000001 #define ID_MENUBAR_EDIT 0x80000002

122 #define ID_MENUBAR_VIEW 0x80000003 #define ID_MENUBAR_TOOLS 0x80000004 #define ID_MENUBAR_WINDOW 0x80000005 #define ID_MENUBAR_HELP 0x80000006

// View Bit Flags #define BITFLAG_MAINFRAME 0x00000001 #define BITFLAG_EDITVIEW 0x00000002 #define BITFLAG_ALL 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 123 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.

124 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 125 // 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() func- tion 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);

126 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 exam- ple, 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 exam- ple, 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 127 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 pro- cess 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 main- frame. 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.

128 3. In your CView::OnInitialUpdate(), initialize the bitmap menu plugin, defining the tool- bar 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 129 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 cur- rently active view.

130 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 appli- cation 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 Micro- soft Excel.

Menu bars and Docking Views are not supported in this configuration (inside an IP Server).

Chapter 8 Docking Windows 131 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.

132 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, alter- nate 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 133 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 cre- ation 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.

134 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 dock- bar 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, float- ing 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 135 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 previ- ously 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. Hid- den 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 win- dows 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.

136 8.4.2 Creating Auto-Hide Docking Windows

Your frame class should be derived from SECFrameWnd if you want SDI capability or from SECM- DIFrameWnd is you want MDI capability. Auto-hide docking windows are SECControlBar- derived 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 \Samples\Toolkit\MFC\Docking\Viz\.

Chapter 8 Docking Windows 137 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 fea- tures, 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 CFrameWnd CObject

SECControlBar SECFrameWnd CCmdTarget

SECDialogBar CMDIChildWnd SECControlBarManager

SECToolBar SECMDIChildWnd CDockState

SECStatusBar CMDIFrameWnd SECDockState

SECCustomToolbar SECMDIFrameWnd

138 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 support- ing 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 serializa- tion 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 com- patible with the improved serialization support.

Chapter 8 Docking Windows 139 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 implementa- tion 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 meth- ods 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 vari- ables 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 CDialog- Bars 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.

140 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 141 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

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 cli- ent 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 tool- bar). 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.

142 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 cus- tomize 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 143 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).

144 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 pass- ing 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 145 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 pass- ing 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 applica- tion 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.

146 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 sam- ple 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 147 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.

148 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 149 // 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.

150 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 imple- mentation calls OnBarDock().

OnBarDock() Called before a bar is docked. The default imple- mentation does nothing.

OnBarEndDock() Called after a bar is docked. The default imple- mentation does nothing.

OnBarBeginFloat() Called before a bar is floated. The default imple- mentation calls OnBarFloat().

OnBarFloat() Called before a bar is floated. The default imple- mentation does nothing.

OnBarEndFloat() Called after a bar is floated. The default implemen- tation does nothing.

OnBarBeginMDIFloat() Called before a bar is docked as an MDI child. The default implementation calls OnBarMDIFloat().

Chapter 8 Docking Windows 151 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] = { { AFX_IDW_DOCKBAR_TOP, CBRS_TOP }, { AFX_IDW_DOCKBAR_BOTTOM, CBRS_BOTTOM }, { AFX_IDW_DOCKBAR_LEFT, CBRS_LEFT }, { AFX_IDW_DOCKBAR_RIGHT, CBRS_RIGHT }, };

const DWORD SECMDIFrameWnd::dwSECDockBarMap[4][2] = { { AFX_IDW_DOCKBAR_TOP, CBRS_TOP }, { AFX_IDW_DOCKBAR_BOTTOM, CBRS_BOTTOM }, { AFX_IDW_DOCKBAR_LEFT, CBRS_LEFT }, { AFX_IDW_DOCKBAR_RIGHT, 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(...) {

152 // 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 Dock- ing). 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 153 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 mes- sage to it. If not, let default handling occur.

We have solved this problem in our Docking Views.

154 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 per- sist. 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 illus- trates how to use docking windows in an MDI application.

Chapter 8 Docking Windows 155 156 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 restric- tions 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:

 using namespace stingray::foundation; 

Exporting an entire namespace into your project is not a good way to maintain compartmental- ization; 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:

 #include “Toolkit\ot_secdib.h” 

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 157 Several other “Objective Toolkit” classes are also part of the Stingray Foundation Library— including color well con- trols 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 for- mat. 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 gen- eral 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.

158 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 Inter- net 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 applica- tions 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-per- pixel 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 159 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 com- pressed 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 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 imple- mented to become familiar with the internal format.

160 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 deriva- tive 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 dig- ital 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 bit- map (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 161 attempted to display a 24-bits-per-pixel image on an 8-bits-per-pixel display in the WIN32 environ- ment, 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 con- verted 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”);

162 // 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 num- ber 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-clock- wise by calling the Rotate90() method.

Chapter 9 Image Classes 163 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 cre- ated 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-

164 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 165 // 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");

166 // 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 167 9.5 Key Image Methods

Here is an overview of some of the key SECImage methods.

Table 28 – Methods for SECImage

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 for- mat for all image types. ConvertImage() copies the internal image and deletes the original without the over- head 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.

9.6 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 pre- view and print them using the MFC document/view architecture.

168 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 Work- book Document Interface (WDI) augments the capabilities of MDI without changing the interface drastically.

Chapter 10 MDI Alternatives 169 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 docu- ment. 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.

170 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 171 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 .

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

172 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 (docu- ment/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 173 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 win- dows. 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 doc- ument/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.

174 10.2.2.9MTI Sample

The Objective Toolkit mt_scrib sample (\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 docu- ments 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 chil- dren; 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 .

Chapter 10 MDI Alternatives 175 10.2.3.1Differences between FDI and MTI

Because FDI allows you to place document windows on the desktop, its interface is somewhat sim- ilar 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 docu- ment 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 applica- tions 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 com- prehensive. It may require you to re-derive several of your application’s classes.

2. Follow the steps described in Section 10.2.2.3.

176 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 (\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 search- ing for it.

Chapter 10 MDI Alternatives 177 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 functional- ity 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);

178 The Viz sample demonstrates this feature, and can be found at \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 draw- ing, 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 CShortcutList- DockBar::OnCreate function implementation in LSTDBAR.cpp, VIZ sample: m_scListBar.SetFlatStyleMode(TRUE);

The Viz sample demonstrates this feature, and can be found at \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 win- dow. 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 179 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 \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.

180 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 181 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 pre- sented 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 assign- ing a value to this member.

SECWorkbookWnd::AddSheet() A virtual method that is called whenever a new MDI child window is created. This mem- ber 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.

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.

182 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 asso- ciated 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 A virtual method that renders the icon and AndLabel() tab label on top of the blank tab drawn by SECWorkbookWnd::OnDrawTab(). Over- ride this member if you require the tab’s inscription to be rendered differently.

Chapter 10 MDI Alternatives 183 184 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 185 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

186 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 CWnd- derived objects. Because it is CWnd-derived, SECShortcutBar can also be held by other CWnd- derived 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 187 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 embed- ded 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.

188 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 con- text 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 spec- ifying (WS_VISIBLE | WS_CHILD | SEC_OBS_VERT).

Chapter 11 Shortcut Bar 189 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

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.

190 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 bit- map 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 191 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)

192 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 193 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 mes- sage. 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.

194 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 win- dow, 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 195 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 \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.

196 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 Con- troller (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 MvcVisu- alComponent 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 197 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-win- dowed 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:

 SECATLShortcutBarHosted

 SECATLShortcutBarWnd

 SECMFCShortcutBarHosted

 SECMFCShortcutBarWnd

198 12.2 The Shortcut Bar Classes

Figure 102 – Shortcut bar class hierarchy

SECShortcutBarComp

CEventRouterMap

CWindowImpl

IVisualWindow

SECATLShortcutBarWnd

SECShortcutBarComp

CEventRouterMap

SECATLShortcutBarHosted

12.2.1 ATL

The SECATLShortcutBarWnd class is the windowed version of the shortcut bar that has been tai- lored 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 par- ent 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 199 12.3 Shortcut Bar Styles

Table 33 lists the bar styles that can be applied to shortcut bars.

Table 33 – Shortcut bar styles

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 dis- ables this effect.

SEC_TABBAR_ CNTXTMENU When this style is set, right-clicking the shortcut bar will result in a WM_TABBAR_CNTXTMENU mes- sage 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.

200 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 under- stands. 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 201 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.

202 // 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 203 204 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 win- dow 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 205 Figure 104 – Example of the Three-Dimensional Tabbed Windows

206 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 ’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 refer- ences an associated CWnd.

Chapter 13 Tabbed Windows 207 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 sup- ports 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.

208 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 209 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 (SECT- Description abWnd) Style

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 and 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

3D Tabbed window Description (SEC3DtabWnd) Style

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.

210 Table 35 – Tabbed Window Styles for SEC3DTabWnd (Continued)

3D Tabbed window Description (SEC3DtabWnd) Style

TWS_NOACTIVE_TAB_ By default, the active tab is drawn enlarged. The ENLARGED TWS_NOACTIVE_TAB_ENLARGED style flag dis- ables 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 211 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 Definition Description Message

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 unse- lected, 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.

212 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 Then: is:

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 213 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.”

214 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 215 // 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); }

216 The implementations of the GetDocToUse() and GetDocTemplateToUse() methods are applica- tion-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 217 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 mes- sage. See Section 13.4. Each message-map macro entry takes the following form:

ON_MESSAGE( , memberFxn )

memberFxn is the name of the parent member function you wrote to handle the notification and 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);

218 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 win- dow 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 cli- ent 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 219 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.

220 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 resiz- able 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 fea- tures 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 221 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 col- umn, 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.

222 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 mem- bers. 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 col- umns in the tree control. This class is not supported outside the scope of the SECTreeView class.

Chapter 14 Tree Control & Tree View 223 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 con- tains 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;

224 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 225 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

226 Figure 111 – Example Objective Toolkit Tree View and Tree Item States

Chapter 14 Tree Control & Tree View 227 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 col- umn 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

LVXS_LINESBETWEENCOLUMNS Paints vertical lines between columns.

LVXS_LINESBETWEENITEMS Paints horizontal lines between items.

228 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 col- umn 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 229 Figure 112 – Examples Tree Control Styles

The following figure illustrates some of the extended styles.

230 Figure 113 – Examples Tree Control Extended Styles

Chapter 14 Tree Control & Tree View 231 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

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.

232 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 233 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 ); }

234 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 235 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 your- self 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 differ- ent 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,

236 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 mem- bers, 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 237 2 : // checked 1); // 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 Tool- kit 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,

238 // 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 struc- ture 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 239 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.

240 14.9 Tree Control Samples

See the samples TreeDemo and DynaTree in the 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 selec- tion, 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 241 // 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 );

242 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 243 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 dis- play 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 over- load accepts the filename of a bitmap file. The third overload takes a pointer to an SECImage- derived 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.

244 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 align- ment of caption text.

In order to match the Windows XP look and feel in a themed application, gradient caption extension will be auto- matically 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 CMDIFrameWnd

SECFrameWnd 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 245 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 proce- dure 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); }

246 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

SECShortcutTable

CDialog

SECShortcutDlg

CArray

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 pres- ents 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 247 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 short- cut 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 accelera- tor 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.

248 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 typi- cally 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 249 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 corre- sponding 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 automati- cally 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 can- not 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:

250  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 .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 associ- ated 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 Tool- help 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-spe- cific 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 stan- dard 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 251 15.5 Splash Window

A splash window is a transient window that appears temporarily when the user starts the applica- tion. 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 spec- ified 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 dis- played. Default: Parent enabled.

The SECSplashWnd constructor has an alternate constructor that takes two additional parame- ters— bWaitForTimer and bAlwaysOnTop.

252 SECSplashWnd(UINT nNewBitmapID, UINT nNewDuration = 2500, BOOL bWaitForTimer = FALSE, BOOL 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 253 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 abil- ity 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 cur- sor 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 program- matically 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 fea- tures 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.

254 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, // added here! ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL; };

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 255 ... 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, &m_wndPanelEdit, SECCustomStatusBar::FitPaneToWnd); ...

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 dis- play 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, cre- ate 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.

256 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 sta- tus 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 align- ment flags like TA_LEFT are defined in WINGDI.H.)

SBP_FOREGROUND Use the COLORREF set in the crTextFore- ground 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 257 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 cus- tomize the behavior of the class.

Table 40 – Virtual Member Functions for SECCustomStatusBar

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.

258 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 thumb- nail 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 can- not 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 259 Figure 124 – Thumbnail Class Hierarchy CWinApp CFileDialog

SECTNWinApp SECTNFileDialog

CDocument CBitmap

SECTNDocument SECTNBitmap

CView CDC

SECTNView 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.

260 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 can- not infer how you want your view’s thumbnail to appear, so you need to write special thumbnail- generating 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 261 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 edi- tor. 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

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.

262 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 cre- ated 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 con- structor 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 263 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);

264 // 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 265 15.9 Tray Icon Class

The SECTrayIcon class helps you create applications that are invoked via the Windows tray inter- face. 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 cus- tom 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.

266 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 right- click.

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 267 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.

268 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 programmat- ically. 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 man- age the list of tools. Figure 129 – User-Tool Menu Class Hierarchy

CObject CDialog

SECUserTool 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 269 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 user- tool upon execution.

6. Call the SetDirectory() method to set the absolute path of the directory in which the user- tool 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.

270 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 addi- tion, 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 CWnd

SECWorkspaceManager SECWndPlugin

SECWorkspaceManagerEx

The next few sections describe the differences between these two classes.

Chapter 15 User Interface Extensions 271 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 set- tings 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-

272 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 docu- ment 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 appropri- ate 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 work- space 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 273 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));

274 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 275 // 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 log- ically 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;

// 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

276 // 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 277

// 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 encour- aged 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 applica- tion 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; }

278 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 (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 279 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 compo- nent. 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 sup- ported. 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 dis- play 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 full- screen 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

280 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 cre- ates 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 com- mand 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 281 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 sam- ple 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 applica- tion 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

282 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 283 284 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 nor- mal 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 285 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.

286 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 appli- cation 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 287 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, th and l3, respectively, the mask value M[i] for the i character of the message would be:

M[i] = B1[i%l1]^B2[i%l2]^B3[i%l3];

288 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 incor- rectly, 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 dis- abled in OFB mode, and they fire an ASSERT when used.

Chapter 16 Utility Classes 289 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 triple- ranked 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 dif- ferent 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:

290 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 exam- ple 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 Get- ting Started Guide.

Chapter 16 Utility Classes 291 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:

292 MultiTrace 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 incor- porate the thread ID and the time (to the millisecond):

<25c 15:24:10.0024> At this point, the value of x is 4

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==

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 293 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);

294 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 295 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) demon- strates 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.

296 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 prob- ability 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

01

17

20

32

The sum of the weights is 10. So there is a 10 percent chance that a random number of 0 will be gen- erated, 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 297 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);

298 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 299 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 evalua- tion 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 #include

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 }

300 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 301 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 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 Win- dows 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 evalu- ation. 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.

302 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 303 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”));

304 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 infor- mation 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 305 306 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. 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 \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 documenta- tion. 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 307 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 compo- nent 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 \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.6.3, “Stingray Studio Paths in Property Sheets,” 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 \Regex. As described in regex.htm, it is also possible to link to a static version of Regex++ and avoid installation of additional libraries.

308 17.1.2 Using Regular Expression Libraries

PERL and other such languages are commonly used to solve the frequently encountered program- ming 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:

City of Raleigh, NC, temp at 7 P.M. 52 degrees

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.

City of ([a-z]*), ([a-z]{1,2}), temp at ([0-9]{1,2}) (P.M|A.M). ([0- 9]{1,3}) degrees

Chapter 17 Data Extraction Classes 309 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 pro- cess 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 follow- ing 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.

310 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 auto- matically 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 311 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.)

. . .

Div Date
N/A
Small: [1d | 5d | 1y | none]
Big: [1d | 5d | 3m | 1y | 2y | 5y]
Day's Range
5 3/4 - 6 3/16 Bid
5 3/4 Ask
5 7/8 Open
6 1/8 Avg Vol
49,818 Ex-Div
N/A 52-week Range
3 - 12 7/8Earn/Shr
-0.06 P/E
N/AMkt Cap
63.3M Div/Shr
N/A Yield
N/A

. . .

ThuAug

312 10RWAV ROGUE WAVE SOFTWARE INC /OR/ - Quarterly Report (SEC form 10-Q) - Other Mon Aug 7 RWAV Rogue Wave Software Embraces Japanese C++ and XML Components Market - PR Newswire Mon Jul 17 RWAV ROGUE WAVE SOFTWARE INC., posts 3Q EPS $0.02 vs. $0.04 - Standard & Poor's

. . .

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 313 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 dif- ferent 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 parame- ters—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.

314 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 (com- bined) 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 My name is ([a-z]*); my My name is ([a-z]*); my e- address is e-mail address is happy- e-mail address is (.*) happy- [email protected] [email protected]. dmy 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)"), estateDeals) rg_end_state()

rg_start_state(stateDeals, estateDeals) rg_match(match_any)

Chapter 17 Data Extraction Classes 315

rg_sub_state(etokenRef, _T("(href=\"(.*)\")"), CRgSubState::stateContinue)

rg_sub_state(etokenDealName, ,_T("(class=\"newsquick\">([^<>]*))"), CRgSubState::stateContinue)

// ignore the date

rg_sub_state(CRgSubState::tokenNone, ,_T("(class=\"newsquick\">([0-9/]*)

rg_sub_state(etokenExpired, _T("( Expired)"), CRgSubState::stateContinue)

rg_sub_state(etokenNotExpired, _T("()"), CRgSubState::stateContinue)

rg_sub_state(CRgSubState::tokenNone, _T("(View Deals From

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 pro- cessing 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;

316 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 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 317  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.

318 Figure 141 – Latest deals on the Web sample

Chapter 17 Data Extraction Classes 319 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.

320 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 CWnd

SECZoomView SECPanWnd

SECPanView

Chapter 18 View Classes 321 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 speci- fying either a zoom rectangle or a zoom percentage.

18.3.1 SECPanView

SECPanView builds on SECZoomView and adds panning support. Panning enhances scrolled win- dows 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.

322 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 con- trol zooming.

SEC_ZOOMFIT Enables zoom to fit mode. The view is automatically zoomed to fit inside the client area so that no scrab- blers 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 com- pletes the move and releases the mouse button.

SEC_PANINSTANT The view is updated instantly for every move- ment of the mouse.

Chapter 18 View Classes 323 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 sug- gest 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 hierar- chy, 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. SECZoom- View 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.

324 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 over- view 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() over- load 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 325 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

Panning mode Description methods

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.

326 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 327 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.

328 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 329 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 con- trol-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 Appli- cations (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 inte- grated 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/en- us/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 appli- cation. 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.

330 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 manipulat- ing 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 automa- tion 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 331 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 run- time error notifications. Derive the class that hosts scripts from IActiveScriptErrorHandler and override its HandleError() method.

332 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 #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 333 // 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. Assum- ing 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);

334 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 335 336 Chapter20 ActiveHost Form Scripting and Editing Framework

20.1 Overview

The Objective Toolkit forms engine works in concert with the ActiveScript Hosting Engine to pro- vide 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 interac- tively 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 337 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-tem- plate 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 con- junction 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 when- ever the script creates a new form via the OpenForm(), NewForm(), and FormByName() commands.

338 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 fol- lowing 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 run- time 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 339 340 Chapter 21 Advanced Docking Windows

21.1 Overview

The Advanced Docking Window (ADW) architecture is built on top of the Objective Toolkit Lay- out 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 341 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 non- windowed 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:

 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.

342 21.3 Advanced Docking Windows Features

The sections that follow provide descriptions of specific features of the Advanced Docking Win- dows 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 trans- parently, 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 foot- print. 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 re- dock 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 343 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 mecha- nism 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 Alternative to m_pNodeRelative. If you use this bAfter=TRUE,BOOL bPrimaryCnstr=TRUE); 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 Allows specification of the dock site via SetDockSite(SECFrameDockingFeatureBas SECFrameDockingFeatureBase::top, bottom, e::DockSite site) left, or right.

For information on using docking insertion constraints, Section 21.5.3, “To use docking insertion constraints.”

344 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 split- ter 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 fig- ure 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 345 Figure 144 – Example of the “Border style” splitter

346 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 Objec- tive 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 dock- ing 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 fea- ture. 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 347 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, bCreateNewLocalDock- Mgr 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”));

348 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 variet- ies 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 349 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, pic- tures, 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;

// 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.

350 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 351 // 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

352 21.5.4 To adjust the border sizing

Use the SECFrameDockingFeature::SetBorderSizing() method to control sizing properties spe- cific to each border docksite (top, bottom, left, and right) individually or as a group. This method provides the following support:

Table 53 – SECSetBorderSizingParms Members

SECSetBorderSizing- Description Parms members

m_bSplitter If TRUE, the docksite will pro- vide 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 353 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 pre- cedence 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.

354 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 illus- trates 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 355 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);

356 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 partic- ipate 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 357 21.6 Advanced Docking Windows Examples

Several examples are provided in the \samples\toolkit\MFC\advdocking directory to demon- strate 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 inde- pendent of the existing Objective Toolkit mechanism (AdvMDI, AdvSDI).

358 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 359 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 dock- able 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

360 22.3 Issues when Docking a CView

You cannot dock CViews using the SECControlBar as a parent. Instead, you need to build a dock- ing 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 361 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

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.

362 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 CControlBar

SECFrameWnd SECControlBar

SECDockableFrame SECFrameBar

CMDIChildWnd CMultiDocTemplate

SECMDIChildWnd 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 attri- butes 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 363 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. How- ever, 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

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).

364 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 architec- ture. 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 docu- ments 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 365 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.

366 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 tran- sition from docked or floating to an MDI child state only the CView survives.

Chapter 22 Docking Views 367 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 fol- lowing 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 \Include\Toolkit\syscmdex.h.

Table 56 – SCXID Parameters

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.

368 22.9 Docking Views and MDI Alternatives

The docking views architecture is ideal for the Workbook Document Interface (WDI). The mdi sam- ple program in the 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 con- cept 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, “Dock- ing 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 369 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 over- ride 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;

370

// 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 Samples\Toolkit\MFC\DockingViews\Bounce subdirectory for a demon- stration 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 371 // 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:

372 CCreateContext* pContext = (CCreateContext*)lpCreateStruct->lpCreateParams;

The source code for this section was taken from our SplitView sample application in \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 373 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); } }

374 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 thou- sands 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 imple- menting 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 lay- outs. You can integrate the Layout Manager into applications in a matter of minutes.

Chapter 23 Layout Manager Framework 375 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 uti- lized 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 addi- tion, 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.

376 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 excel- lent 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 exist- ing 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 optimiza- tions 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 enti- ties, 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 377 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 lay- out 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.

378 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 tree- like 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 encap- sulates 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. None- theless, 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 379 Figure 157 – Sample Grid Layout Node

If you configured this grid layout node to have two rows and one column, it would have two lay- out 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.

380 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 381 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 exist- ing 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 win- dow 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. Addi- tionally, 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.

382 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 guaran- tees a constant aspect ratio regardless of the size of the parent node.

Chapter 23 Layout Manager Framework 383 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 two- dimensional grid of specific or arbitrary size. You can initialize the grid to a specific two-dimen- sional 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:

 Node spanning of multiple rows or columns or both.

 Variable width columns.

384  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 385 Figure 166 – Example Relative Layout

386 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 proce- dure 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 387 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 lay- out 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 han- dler (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);

388 // 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 389 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 demon- strates the use of the Layout Manager classes.

390 Chapter 24 Microsoft Agent Extensions

24.1 Overview

Objective Toolkit includes MFC extensions to the basic Microsoft Agent ActiveX components, pro- viding 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 inter- active animated characters within the 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/en- us/library/ms695784(VS.85).aspx.

The Objective Toolkit extensions work with Microsoft Agent version 2.0 or later.

Chapter 24 Microsoft Agent Extensions 391 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 ini- tialize 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 programmati- cally. 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 charac- ter 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 function- ality. 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 pro- viding the other features implemented in IAgentApp.

392 24.3.4 SECAgentCharAct

SECAgentCharAct is an abstract base class from which all Act objects are derived. This class pro- vides 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 Non- Critical. 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 imme- diately 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 393 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

3. In your application’s InitInstance(), call LoadAgent() followed by AttachChar() to cre- ate 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(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 when- ever 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.

394 Chapter 25 Namespace Extension Wizard

25.1 Overview

The Stingray Namespace Extension Wizard feature helps you to quickly create a working name- space 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 Objec- tive 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 .exe with SECNSPpg.dll as its argument. A prebuilt version of SECNSPpg.dll is provided in the \Bin directory.

Now you are ready to use the wizard.

Chapter 25 Namespace Extension Wizard 395 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 attri- bute 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:

 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.

396 25.4.3 Support UI Object

The Support UI Object section controls which optional interface your namespace extension sup- ports. 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 correspond- ing 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 sam- ple 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 implemen- tations 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 397 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 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 develop- ment, 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 exten- sion 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.

398 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 inter- face, Explorer obtains IEnumIDList and IShellView interfaces. The relationship of IEnumIDList to

Chapter 25 Namespace Extension Wizard 399 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 behav- ing 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 every- thing 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 implemen- tation 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 name- space 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 wiz- ard 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 corre- sponding implementation classes.

400 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 implementa- tion should be similar to the following. virtual BOOL CreateEnumIDList(LPCITEMIDLIST pidl, DWORD dwFlags) { HWND hWnd = NULL; if( pidl != NULL ) { //The GetLastDataPointer method of // SECPidlMgr will //return a pointer to our data structure SECPidlData.

Chapter 25 Namespace Extension Wizard 401 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 imple- mentation 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;

402 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 403 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.

404 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 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 405 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 fol- lowing image in the Explorer.

406 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 corre- sponding 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 407 // 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

408 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 409 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(),

410 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 modify- ing the menu of Explorer, adding toolbars into Explorer, and displaying status text. We strongly recommend the excellent reference Visual C++ Programming by Dino Esposito, pub- lished by Wrox Press, as well as the MSDN.

Chapter 25 Namespace Extension Wizard 411 412 Chapter 26 The Hyperlink Classes

26.1 Overview

The Objective Toolkit hyperlink classes provide a convenient way to add point-and-click naviga- tion 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 413 26.2 Using the Hyperlink Classes

ATL applications can directly use the hyperlink classes. Non-ATL-enabled MFC applications, how- ever, 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 extern CComModule _Module; #include #include

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 place- holder 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 ");

414 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 415 416 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 (asyn- chronously via an IDispatch).

Take a look at the IWBUtilFuncs::CreateNewIEInstance() function in the COM Server.

Chapter 27 Web Browser Extensions 417 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 limita- tions 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:

 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.

418  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 419 420 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 inter- faces 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 validat- ing 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) pro- tocol 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 421 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 per- form 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/Con- tinue), to inform it regarding the thread completion, and to check if it should abort the operation before completion.

422 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 Stu- dio. 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 423 424 Chapter 29 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 Objec- tive 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 Wiz- ards 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 425 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 inter- faces. 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 sec- tion, 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.

426 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 Tem- plates: 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 427 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 Con- trol from the Templates: frame as shown in Figure 178.

428 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 429 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.

430 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 imple- mentation (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 431 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

432 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 433 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

434 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 Tool- box and select Customize Toolbox.

Find your ActiveX class, as shown below.

Chapter 29 ATL and Objective Toolkit Components 435 Figure 186 – Customize Toolbox

Figure 187 – Component Window

436 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 437 Figure 190 – Form_Load Procedure

8. Finally, run the VB project. Figure 191 – Running the application

438 29.4 Further Extensions

We’ve only exported one method. We have not exported any of the Objective Toolkit Tree control’s bitmap func- tionality. 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 439  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 functional- ity 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 per- forms 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 con- trol, 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.

440 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 \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 441 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:

 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

442 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 enumera- tion 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 collec- tion 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 #include #include "carobj.h"

class ATL_NO_VTABLE CCarsDual : public CComObjectRootEx, public CComCoClass, public CComCollectionOnSTL, IDispatchImpl > {

Chapter 30 Introduction to Objective Toolkit for ATL 443 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 imple- mented 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* pCar; ICarObj* pICar = NULL; for(int i = 0; i < NUMOBJECTS; i++) { CComObject::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); }

444 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 primar- ily 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 pre- ceding 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 445 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 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 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 spimt; spimt.CreateInstance(…); fworker(L"Hello", COtlInterfaceToken(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.

446 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 invoca- tion identical to that of a function call:

functor( ); BarkLoudly( );

Chapter 30 Introduction to Objective Toolkit for ATL 447 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 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 spimt; spimt.CreateInstance(…); … COtlFunctor functok = otlMakeFunctor(fworker, L"token marshaling", COtlInterfaceToken(spimt));

30.6.4 Threads and Functors

Once you construct a functor, you can use the functor to represent a thread procedure when com- bined 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.

448 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, self- bounded 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 449 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 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; }

450 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. Unfortu- nately, 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 spuri- ous 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 451 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 far- left 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 reg- istrar 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.

452 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 defini- tion 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 imple- ment 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 catego- ries" 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 453 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.

454 30.9 Microsoft Message Queue Class

MSMQ is a 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 doc- umentation 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 deter- mine 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 deter- mine 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 455 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 spec- ifying 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() oper- ates 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 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 cre- ates 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 pRect; hr = pRect.CoCreateInstance(CLSID_RectObj);

456 // 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 pTrans; if(SUCCEEDED(hr)) hr = q.BeginTransactionInternal(&pTrans);

// send the object if(SUCCEEDED(hr)) hr = q.SendObject(static_cast(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 457 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(""))) 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 ele- ment, 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"));

1234

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.

458 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 ses- sion. 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 1234 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 chil- dren 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 459 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 Docked in the left side of the browser. Bar)

Communications Band (Hori- Docked in the bottom edge of the browser zontal Explorer Bar)

From a development perspective, the differences among these types are minor. In fact, the only dif- ference 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 win- dow 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 conve- niently 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:

460 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 com- mand 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 461 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 , 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 win- dow, 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 cus- tomize. 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 Tool- bar (AppBar), similar to the Windows Taskbar. This class subclasses the Win32 common tab control. The control has the following enhancements:

462  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 sup- ported 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 463 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 algo- rithm 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 fol- lowing 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 guaran- tees 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:

 “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.

464 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 #include #include

// CScaleDialog class CScaleDialog : public CAxDialogImpl, public COtlLayoutImpl {

Now the Layout Manager needs to gather a list of child nodes to manage. Typically, a window lay- out 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 465 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 unre- leased memory within its destructor.

466 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 inter- face 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

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 467 // 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);

468 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 469 470 INDEX

A windows 342 SECPopupCalendar 44 ActIDs 391 Asynchronous Pluggable Proto- using 44 Active Template Library and Ob- col. See APP calendar control jective Toolkit 425 ATL class hierarchy 46 ActiveHost and Objective Toolkit 441– customizing 48 adding it to your 469 introduction 46 application 339 and shortcut bars 197–203 key methods 47 introduction 337 basicDate example 440 sample 48 samples 339 ATLShortcut 203 SECCalendar 46 ActiveHost Form Editing auto-hide docking windows 135 CComModule 414 Engine 337 CDealListener 318 ActiveScript B CDealProcessor 311 Hosting Engine 337 basicDate example 440 CHAIN_OTL_APPBAR() 462 sample 335 BHO 417 Change Default Character type library 333 bitmapped dialog command 392 ActiveScript, classes 331 class hierarchy 243 cipher modes 287 ActiveScript, controlling inclusion customizing 244 class hierarchy of ScriptHost.tlb 333 display mode flags 244 customizable toolbar 89 ActiveScript,incorporating into introduction 243 Cmytreecontrol 431 using 244 your application 335 CNSExtComp 399, 407 browse edit controls 33, 34 ActiveX wrapper, using ATL 426 CNSExtCompEnum 399, 400 Acts 391 browse button 33 class hierarchy 34 CNSExtCompIcon 399, 400, 410 adding customizing 36 CNSExtCompMenu 399, 400, 404, scripting to your OnBrowse method 36 409 application 333 sample 36 CNSExtCompView 399, 400, 403, advanced docking windows using 35 404 architecture 342 Browser Helper Object 417 color well control configurations 344 class hierarchy 49 examples 358 button controls introduction 49 introduction 341 alignment modes 40 sample 52 splitter styles 345 class hierarchy 38 using 347 Button macros COM server 417 Advanced docking windows COMBO_BUTTON 99 COMBO_BUTTON 99 (ADW) 12 TWOPART_BUTTON 99 compressed file classes AFX_MANAGE_STATE 440 Button map macros class hierarchy 285 STD_BUTTON 97 introduction 285 Agent 394 TEXT_BUTTON 98 sample 286 agent extensions 391–394 TEXT_BUTTON_EX 99 SECCompressFile 286 AgentServer 392 using 286 AgentSvr.exe 392 C compression algorithm 161 alignment layout 383 calculator control COtlAppBarImpl 462 APP 421–423 class hierarchy 43 COtlAppBarTabs 462 customizing 44 app sample 423 COtlLayoutFactory 464 sample 45 architecture SECCalculator 43 COtlLayoutImpl 464 advanced docking COtlLayoutNode 464

Index 471 COtlMSMQ 455, 456 predefined 59 message routing issues 141 COtlRelativeNode 464, 465 date/time edit control sample 155 COtlScaleNode 464 class hierarchy 57 SECControlBar 140 SECControlBarManager 140 COtlSimpleSafeArray 449 date formats 59 format strings 59 SECDialogBar 140 COtlWindowNode 464 introduction 57 SECDockState 140 COtlXMLDocument 458, 459 null date entry mode 60 SECFrameWnd 139 CRgDefListener 311 sample 63 SECMDIChildWnd 139 CRgProcessor 311 SECDateTimeCtrl,description SECMDIFrameWnd 139 currency edit control 58 using 145 class hierarchy 53 SECDTGadget 58 vertical frame docking 135 edit control messages 56 using 61 vertical text 136 formats 54 Deals sample documentation sample 56 listener 316 formats 4 custom status bar location 311 dual interfaces 426 class hierarchy 254 DHTML 417 customizing 257 directory edit control 34 E customizing panes 257 distributing ECB mode 287 introduction 254 Objective Toolkit encrypted file class 287 using 254 applications 16 cipher modes topology 289 custom tokens 315 distributing Objective Edit class hierarchy 287 customizable toolbar applications 5 encryption algorithm 288 introduction 287 button classes 94 docking views limitations 290 button map macros 97, 98, 99 architecture overview 365 password transformation 288 button state macros 100 class hierarchy 363 sample 291 button style macros 100 CMultiDocTemplate 364 SECBlackBox 288 class hierarchy 89 containment model 367 SECCryptoFile 287 classes 91, 92 features 360 stream cipher 288 creating new button introduction 359 using 290 types 101 options 362 customization dialogs 102 SECFrameBar 363 enhanced combobox persistence 103 SECMDIChildWnd 364 class hierarchy 77 using 104 using 369 description 77 customizable toolbar button class- WM_SYSCOMMANDEX 36 using 77 es 8 exception 292 SECComboBtn 96 docking windows exceptions, data extraction SECStdBtn 94 active 136 classes 320 SECStdMenuBtn 95 advanced architecture 342 extended progress control SECTBTextBtn 95 auto-hide 135 class hierarchy 75 SECTwoPartBtn 95 auto-hide control bar 135 customizing 76 SECWndBtn 95 auto-hide pins 136 customizing styles 76 customizable toolbar classes callback notifications 151 description 75 SECNewToolBar 93 class hierarchy 138 extending 76 SECToolBarCmdPage 93 control 142 using 75 SECToolBarSheet 93 creating auto-hide 137 SECToolBarsPage 93 embedding CViews 144 F CWinApp 392 floating grippers 136 FDI frame classes 139 as compared to MTI 176 D horizontal text 136 class hierarchy 176 data extraction 307 icons 136 converting an MDI HTML 310 introduction 131 application 177 date formats key members 155 converting an SDI

472 Index application 176 using 414–415 application 387 creating a new hyperlink sample 415 architecture 379 application 177 benefits 377 introduction 175 I implementing a custom lay- sample 177 IActiveScriptErrorHandler out manager 390 file system class definition 332 introduction 375 layout algorithms 383, 384 class hierarchy 294 IAgentApp 392, 394 layout issues 376 introduction 294 CreateChar() 392 sample 296 min/max sizes for layout IAgentCharacterExPtr 392 SECFileSystem 294 nodes 390 using 294 ICollectionOnSTLImpl 443 relative layout 385 FileDownloadInfo 422 icons, with auto-hide docking sample 390 windows 136 floating document interface layout manager architecture as compared to MTI 176 Image classes layout factory 382 class hierarchy 176 PCX 159 splitter node 382 converting an MDI TIFF 159 window listeners 382 application 177 image classes layout manager examples converting an SDI bitmap 159 grid layout 389 application 176 class hierarchy 158 relative layout 388 creating a new GIF 159 scale layout 388 application 177 internal format 160 layout nodes 379 introduction 175 introduction 157 lbedit sample 68 JPEG 159 sample 177 license agreement 5 Format class key methods 168 sample 168 list box edit control 64 definition 54 browse button 64 rules 54 TARGA 159 using 161 class hierarchy 64 full-screen view command buttons 64 IMallocSpy 466 class 280 customizing 66, 67 introduction 280 installing SECListBoxDirEditor 66 sample 283 Namespace Extension SECListBoxEditor 65 styles 281 Wizard 395 SECListBoxFileEditor 65 using 281 IPersistStreamInit 418 using 66 functor 447 IWebBrowser2 417 LoadWBViaIPersist() 418 and interface tokens 448 LogBinary() 293 and threads 448 K LZW 161 constructing 447 keyboard shortcut classes invoking 447 SECCommandList 247 M SECShortcutDlg 247 marquee control G SECShortcutTable 247 class hierarchy 69 GIF images 161 keyboard shortcuts customizing 70 gradient caption classes 247 customizing appearance 70 class hierarchy 245 excluded ids 250 description 69 introduction 245 introduction 247 sample 71 SECFrameWnd 245 notes 251 using 69 SECMDIFrameWnd 246 sample 251 masked edit control using 246 saving 251 class hierarchy 72 using 248 grid layout 384 creating masks 73 gridbag layout 384 ktop 176 description 72 samples 74 H L SECMaskEdit 72 hyperlink classes 413–415 layout manager using 72 customizing 415 adding to your MDI alternatives

Index 473 benefits 170 multiple library sample 328 introduction 169 configurations 12 SECPanWnd 322 MTI 170 multiple top level interface using SECZoomView 324 menu bars class hierarchy 172 zoom modes 323 adding customizable 122 converting an MDI pluggable protocol handlers 421 adding non- interface 173 protocol handlers, pluggable 421 customizable 125 converting an SDI bitmap menus without cool- application 173 R look toolbars 127 creating a new random number class button map macros 119 application 173 class hierarchy 297 class hierarchy 117 customizing 173 introduction 297 customizing pop-ups 118 dispatching messages 174 sample 299 introduction 115 introduction 170 SECRandom 297 removing close button while loading the document 174 using 298 floating 126 other uses 174 weighted results 297 sample 130 sample 175 REFLECT_NOTIFICATIONS() 4 switching between multi-threaded logging class, use 63 menus 127 of 292 Regex++ 307 using bitmap menus in con- MultiThreadLogTraits 292 text menus 128 registrar 451 MultiTrace 292 WM_EXTENDCONTEXTME registry class exception handling 292 NU 120 introduction 302 sample 305 MessageBox 392, 394 N using 302 MFC and shortcut bars 197–203 Namespace Extension RTTI 12 MFCShortcut 203 Wizard 395–411 Layout Manager 12 Microsoft Agent installing 395 Microsoft Agent 12 Objective Toolkit options 396–397 Outlook bar 12 extensions 391–394 namespaces overview 391 creating skeleton project 396 S Microsoft Office 2003 extending 395–411 samples 3D tab controls 85 tutorial 398–411 ActiveHost 339 control bars 84 NM_TREEVIEW 224 app 423 menus 83 notification messages 190 FDI 177 nested tabs 85 hyperlink 415 shortcut bars 85 O lbedit 68 status bars 87 Objective Edit Msmq 456 window tabs 85 license agreement 5 on Rogue Wave Web site 4 Msmq sample 456 Objective Toolkit for ATL 441– ScriptMDI 335 MTI 469 tabbed windows 220 class hierarchy 172 OFB mode 287 VIZ 196 converting an MDI ZappBar 462 Office XP interface 173 SaveWBViaCache() 418 converting an SDI enabling 82 SaveWBViaDOM() 418 application 173 menu bars 81 creating a new toolbars 81 SaveWBViaIPersist() 418 application 173 OtlGIT 446 SaveWBViaIPersistFile() 418 customizing 173 OtlIContextMenuImpl 460 scale layout 383 dispatching messages 174 scanner, set up 311 introduction 170 P scanning a text stream 312 loading the document 174 pan and zoom views scripting other uses 174 class hierarchy 321, 322 adding to your sample 175 introduction 321 application 333

474 Index ScriptMDI sample 335 using 77 SECFDIChildWnd SEC3DTabControl SECComboBtn definition 176 description 208 description 96 SECFDIFrameWnd SEC3DTabWnd SECCommandList definition 176 description 209 description 247 SECFileSystem SECAAppObj,definition 331 SECCompressFile description 294 SECAFloatDocTemplate description 286 introduction 294 definition 338 sample 286 sample 296 using 294 SECAFormObj, definition 331 using 286 SECFrameBar SECAgentApp 392, 394 SECControlBar 135, 137 description 140 description 363 SECAgentCharAct 391, 392, 393 SECControlBarManager SECFrameWnd 135 SECAgentCharacterExPtr 392, description 140 description 139 393 SECCryptoFile SECFullScreenView SECAgentNotifySink 393 class hierarchy 287 description 280 SECAgentPromptAtRectAct 393 description 287 sample 283 SECAgentPromptAtWndAct 393 SECCurrency styles 281 SECAgentSpeakAct 393 description 53 using 281 SECAScriptOccManager SECCurrencyEdit SECGIF 161 definition 332 edit control messages 56 SECGif SECATLShortcutBarHosted 198, sample 56 description 159 199, 201 SECCustomStatusBar SECHTMLView 418 SECATLShortcutBarWnd 198, class hierarchy 254 SECHyperlink 413, 414, 415 199 customizing 257 SECIContextMenuImpl 397, 404 SECBitmapButton customizing panes 257 SECIDataObjectImpl 397 using 254 description 39 SECIDropSourceImpl 397 using 39 SECCustomToolBar 91 SECIDropTargetImpl 397 SECBrowseDirEdit description 91 SECIEnumIDListImpl 400 description 34 SECDateTimeCtrl 440 SECIExtractIconImpl 397, 410 SECBrowseEditBase description 58 definition 34 null date entry mode 60 SECImage description 34 SECDialogBar 140 description 158 internal format 160 SECBrowseFileEdit description 140 key methods 168 definition 34 SECDib sample 168 description 34 description 159 SECIQueryInfoImpl 397 SECCalculator SECDockableFrame 363 description 43 description 363 SECIShellViewImpl 400 SECCalendar SECDockState SECJpeg customizing 48 description 140 description 159 description 46 SECDropEdit SECLayoutFactory key methods 47 description 53 definition 382 sample 48 SECDTGadget SECLayoutNode SECColorWell description 58 definition 379 customizing 51 subclasses 58 SECLayoutNodeSplitter description 50 SECEncryptFile definition 382 key methods 51 encryption algorithm 288 SECListBoxDirEditor sample 52 limitations 290 description 66 using 50 password transformation 288 SECListBoxEditor SECComboBoxEx sample 291 customizing 66 class hierarchy 77 stream cipher 288 description 65 description 77 using 290 extending 67

Index 475 sample 68 SECPidlData 402 description 207 window styles 67 SECPidlMgr 400, 402 SECTabControlBase SECListBoxFileEditor SECPlugProt 422 description 207 description 65 SECPlugProtImp 422, 423 SECTabWnd SECListCtrl SECPopupCalculator description 208 description 222 description 44 SECTabWndBase SECListView SECPopupColorWell description 208 description 223 description 51 SECTarga SECMarquee sample 52 description 159 class hierarchy 69 SECProgressCtrl SECTiff 161 customizing 70 class hierarchy 75 description 159 customizing appearance 70 customizing 76 SECTipOfDay description 69 customizing styles 76 class hierarchy 262 sample 71 description 75 resource ids 262 using 69 extending 76 using 263 SECMaskEdit using 75 SECTNBitmap creating masks 73 SECRandom description 260 description 72 class hierarchy 297 SECTNDC samples 74 description 297 description 260 using 72 introduction 297 SECTNDocument SECMDIChildWnd sample 299 description 260 description 139, 364 using 298 SECTNFileDialog SECMDIFrameWnd 135, 137 weighted results 297 description 260 description 139 SECRegistry SECTNView SECMDIMenuBar introduction 302 description 260 definition 121 sample 305 SECTNWinApp SECMenuBar using 302 description 261 definition 117 SECRichHyperlink 413, 414, 415 SECToolBarCmdPage SECMenuButton SECScriptHostDoc description 93 description 40 definition 338 SECToolBarsBase 92 direction flags 41 SECScriptHostView 338 description 92 using 40 definition 338 SECToolBarsDlg 92 SECMFCShortcutBarHosted 198, SECShortcutBar 190 description 92 199, 201 SECShortcutBarComp 199 SECMFCShortcutBarWnd 198, SECToolBarSheet SECShortcutDlg description 93 199 description 247 SECNewToolBar SECToolBarsPage SECShortcutTable description 93 description 93 description 247 SECToplevelFrame SECOwnerDrawButton SECSplashWnd customizing 39 definition 172 description 252 other uses 174 definition 38 key methods 252 SECTrayIcon using 38 samples 253 introduction 266 SECPanView 322 using 253 sample 268 description 322 SECSplashWnd splash window SECTreeCtrl key methods 326 key methods 252 sample 328 description 222 SECStdBtn using 324 SECTreeCtrl, building an ATL Ac- description 94 SECPanWnd tiveX Control 427 SECStdMenuBtn description 322 SECTreeView description 95 SECPcx description 223 SECTabControl description 159 SECTwoPartBtn

476 Index description 95 splash window TEXT_BUTTON 98 SECUserTool class 252 TEXT_BUTTON_EX 99 description 269 classes 252 thumbnail classes SECUserToolsDlg 270 introduction 252 class hierarchy 259 description 270 samples 253 introduction 259 using 253 SECWellButton sample 261 customizing 42 STD_BUTTON 97 SECTNBitmap 260 description 41 STD_MENU_BUTTON 98 SECTNDC 260 using 42 Stingray Foundation Library SECTNDocument 260 SECWndBtn (SFL) 8 SECTNFileDialog 260 description 95 styles SECTNView 260 SECTNWinApp 261 SECWndListener look and feel 79 using 261 definition 382 Microsoft Office 2003 83 TIFF images 161 SECWorkbookClientWnd Office XP 81 definition 181 Visual Studio .NET 81 tip of the day dialog 394 caption 264 SECWorkbookWnd styles, of shortcut classes 200 class hierarchy 262 definition 180 sub expressions, sub states 315 introduction 262 SECWorkerThreadFetchObject 4 T modal 263 22, 423 modeless 263 tabbed windows SECWorksheetWnd resource ids 262 accessing the associated definition 181 using 263 CWnd 217 SECWorkspaceManager adding a view 216 token definitions 314 class hierarchy 271 adding a window 216 trace output 292 using 278 adding to a dialog 214 tray icon class SECWorkspaceManagerEx 273 changing font 218 handling mouse events 267 dynamic controlbar class hierarchy 207 introduction 266 support 272 class SECTabControlBase 207 sample 268 multiple views support 273 definition 207 using 266 using 273 introduction 205 tree control vs. keyboard accelerator control notifications 232 SECWorkspaceManager support 215 creating dynamically 234 272 notification messages 212, data structures 224 SECZoomView 322 218 data structures, description 322 position of tabs 214 NM_TREEVIEW 224 key methods 325 removing a tab 217 data structures,TV_ITEM 224 sample 328 samples 220 image list 236 zoom modes 323 scroll bars 215 introduction 221 SFL 8 scroll buttons 214 item colors 238 shortcut bars styles 210 item fonts 239 class hierarchy 199 three dimensional 205 item selection 238 context menus 202 two dimensional 205 multiple columns 235, 236 framework-specific 197–203 using 213 overlay image 238 incorporating into an tabbed windows classes sample 241 application 191 SEC3DTabControl 208 state image 236, 237 introduction 187 SEC3DTabWnd 209 styles 230 samples 203 SECTabControl 207 using 234, 240 styles 200 SECTabWnd 208 using in a dialog 234 using non-windowed 201 SECTabWndBase 208 tree control classes using windowed 201 text hierarchy 222 visual aspects 202 horizontal 136 SECListCtrl 222 SingleThreadLogTraits 292 vertical 136 SECTreeCtrl 222

Index 477 tree control data structures key methods and TV_HITTESTINFO 226 members 182 tree control view classes window styles 67 hierarchy 223 workbook document interface SECListView 223 changing the icon 182 SECTreeView 223 changing the tab display TV_HITTESTINFO 226 order 181 TV_ITEM 224 class hierarchy 180 TWOPART_BUTTON 99 converting an MDI application 181 U customizing 181 drawing a different tab user tools menu classes 270 label 181 class hierarchy 269 introduction 177 introduction 269 key methods and sample 271 members 182 SECUserTool 269 using 270 WorkerThreadMain 422 utility classes workspace manager introduction 285 adding application specific information 274, 276 V class hierarchy 271 dynamic controlbar view classes support 272 class hierarchy 321, 322 introduction 271 introduction 321 loading and saving state 274 key pan methods 326 multiple views support 273 key zoom methods 325 samples 279 sample 328 SECWorkspaceManager vs. SECPanWnd 322 SECWorkspaceManagerE using 324 x 272 zoom modes 323 using 273 Vigenere cipher 288 using Visual Studio .NET SECWorkspaceManager enabling 82 278 menu bars 81 toolbars 81 Z VIZ sample 196 ZappBar sample 462

W WalkAndExamineIEBrowsers() 4 18 WBUtils 417 WDI changing the icon 182 changing the tab display order 181 class hierarchy 180 converting an MDI application 181 customizing 181 drawing a different tab label 181 introduction 177

478 Index