Cross-Platform GUI Programming with wxWidgets

wxWidgets is a programmer’s toolkit for writing desktop or mobile applications with graphical user interfaces (GUIs). It’s a framework, in the sense that it does a lot of the housekeeping work and provides default application behavior.
1041
Просмотров
Книги > Техника/Технологии
Дата публикации: 2011-12-24
Страниц: 744

Smart_FMf.qxd 6/10/05 1:52 PM Page i Cross-Platform GUI Programming with wxWidgets


Smart_FMf.qxd 6/10/05 1:52 PM Page ii BRUCE P ERENS ’ O PEN SOURCE SERIES http://www.phptr.com/perens Java™ Application Development on Linux® Carl Albing and Michael Schwarz C++ GUI Programming with Qt 3 Jasmin Blanchette and Mark Summerfield Managing Linux Systems with Webmin: System Administration and Module Development Jamie Cameron The Linux Book David Elboth Understanding the Linux Virtual Memory Manager Mel Gorman PHP 5 Power Programming Andi Gutmans, Stig Bakken, and Derick Rethans Linux® Quick Fix Notebook Peter Harrison Linux Desk Reference, Second Edition Scott Hawkins Implementing CIFS: The Common Internet File System Christopher Hertel Open Source Security Tools: A Practical Guide to Security Applications Tony Howlett Apache Jakarta CommonsReusable Java™ Components Will Iverson Embedded Software Development with eCos Anthony Massa Rapid Application Development with Mozilla Nigel McFarlane Subversion Version Control: Using the Subversion Version Control System in Development Projects William Nagel Linux Assembly Language Programming Bob Neveln Intrusion Detection with SNORT: Advanced IDS Techniques Using SNORT, Apache, MySQL, PHP, and ACID Rafeeq Ur Rehman Cross-Platform GUI Programming with wxWidgets Julian Smart and Kevin Hock with Stefan Csomor Samba-3 by Example: Practical Exercises to Successful Deployment John H. Terpstra The Official Samba-3 HOWTO and Reference Guide John H. Terpstra and Jelmer R. Vernooij, Editors Real World Linux Security, Second Edition Bob Toxen

Smart_FMf.qxd 6/10/05 1:52 PM Page iii Cross-Platform GUI Programming with wxWidgets Julian Smart and Kevin Hock with Stefan Csomor Upper Saddle River, NJ • Boston • Indianapolis • San Francisco New York • Toronto • Montreal • London • Munich • Paris Madrid • Capetown • Sydney • Tokyo • Singapore • Mexico City


Smart_FMf.qxd 6/10/05 1:52 PM Page iv Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and the publisher was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals. The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. The publisher offers excellent discounts on this book when ordered in quantity for bulk purchases or special sales, which may include electronic versions and/or custom covers and content particular to your business, training goals, marketing focus, and branding interests. For more information, please contact: U. S. Corporate and Government Sales (800) 382-3419 corpsales@pearsontechgroup.com For sales outside the U. S., please contact: International Sales international@pearsoned.com This Book Is Safari Enabled The Safari Enabled icon on the cover of your favorite technology book means the book is available through Safari Bookshelf. When you buy this book, you get free access to the online edition for 45 days. Safari Bookshelf is an electronic reference library that lets you easily search thousands of technical books, find code samples, download chapters, and access technical information when ever and wherever you need it. To gain 45-day Safari Enabled access to this book: •Go to http://www.awprofessional.com/safarienabled •Complete the brief registration form •Enter the coupon code 33G2-XGSI-ZFYZ-D7IV-B9HE If you have difficulty registering on Safari Bookshelf or accessing the online edition, please e-mail customer- service@safaribooksonline.com. Visit us on the Web: www.phptr.com Library of Congress Catalog Number: 2005924108 Copyright © 2006 Pearson Education, Inc. Printed in the United States of America. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/). ISBN 0-13-147381-6 Text printed in the United States on recycled paper at R.R. Donnelley & Sons in Crawfordsville, Indiana. First printing, July 2005

Smart_FMf.qxd 6/10/05 1:52 PM Page v

Smart_FMf.qxd 6/10/05 1:52 PM Page vi

Smart_FMf.qxd 6/10/05 1:52 PM Page vii Contents Foreword by Mitch Kapor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xxiii Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xxv Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xxxiii About the Authors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xxxv 1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 What Is wxWidgets? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Why Use wxWidgets? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 A Brief History of wxWidgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5 The wxWidgets Community . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6 wxWidgets and Object-Oriented Programming . . . . . . . . . . . . . . . . . . . . . . . . .7 License Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7 The wxWidgets Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8 wxMSW . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8 wxGTK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8 wxX11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9 wxMotif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10 wxMac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10 wxCocoa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10 wxWinCE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10 wxPalmOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12 wxOS2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12 wxMGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12 Internal Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13 2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15 A Small wxWidgets Sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15 The Application Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .16 vii

Smart_FMf.qxd 6/10/05 1:52 PM Page viii viii Contents The Frame Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17 The Event Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .18 The Frame Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19 The Whole Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .20 Compiling and Running the Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .22 Program Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .23 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .23 3 Event Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25 Event-Driven Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25 Event Tables and Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .26 Skipping Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29 Pluggable Event Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29 Dynamic Event Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .30 Window Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31 Defining Custom Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .34 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .37 4 Window Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .39 Anatomy of a Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .39 The Concept of a Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .40 Client and Non-Client Areas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .40 Scrollbars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41 Caret and Cursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41 Top-Level Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41 Coordinate System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41 Painting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41 Color and Font . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42 Window Variant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42 Sizing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42 Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42 Idle Time Processing and UI Updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42 Window Creation and Deletion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .43 Window Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .44 A Quick Guide to the Window Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .44 Base Window Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45 Top-Level Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45 Container Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45 Non-Static Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45 Static Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46 Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46 Control Bars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46 Base Window Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46 wxWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .47 wxWindow Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .47 wxWindow Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .49

Smart_FMf.qxd 6/10/05 1:52 PM Page ix Contents ix wxWindow Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .50 wxControl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .54 wxControlWithItems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .54 wxControlWithItems Member Functions . . . . . . . . . . . . . . . . . . . . . . . . .54 Top-Level Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .55 wxFrame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .55 wxFrame Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .58 wxFrame Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .59 wxFrame Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .60 Non-Rectangular Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .61 Mini-Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .63 wxMDIParentFrame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .63 wxMDIParentFrame Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .64 wxMDIParentFrame Member Functions . . . . . . . . . . . . . . . . . . . . . . . . .65 wxMDIChildFrame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .65 wxMDIChildFrame Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .65 wxMDIChildFrame Member Functions . . . . . . . . . . . . . . . . . . . . . . . . .66 wxDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .66 wxDialog Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .69 wxDialog Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .70 wxDialog Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .71 wxPopupWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .71 Container Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .72 wxPanel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .72 wxPanel Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .72 wxPanel Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .72 wxNotebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .72 Notebook Theme Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .74 wxNotebook Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .74 wxNotebook Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .75 wxNotebook Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .75 Alternatives to wxNotebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .76 wxScrolledWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .76 wxScrolledWindow Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .78 wxScrolledWindow Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .78 wxScrolledWindow Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . .79 Scrolling Without Using wxScrolledWindow . . . . . . . . . . . . . . . . . . . . .80 wxSplitterWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .81 wxSplitterWindow Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .83 wxSplitterWindow Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .83 wxSplitterWindow Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . .84 Sizing Issues with wxSplitterWindow . . . . . . . . . . . . . . . . . . . . . . . . . . .84 Alternatives to wxSplitterWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .85 Non-Static Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .86 wxButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .86

Smart_FMf.qxd 6/10/05 1:52 PM Page x x Contents wxButton Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .86 wxButton Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .87 wxButton Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .87 wxButton Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .87 wxBitmapButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .89 wxBitmapButton Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .90 wxBitmapButton Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .90 wxBitmapButton Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . .90 wxChoice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .90 wxChoice Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91 wxChoice Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91 wxChoice Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91 wxComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .91 wxComboBox Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .92 wxComboBox Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .93 wxComboBox Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .93 wxCheckBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .93 wxCheckBox Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .94 wxCheckBox Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .95 wxCheckBox Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .95 wxListBox and wxCheckListBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .95 wxListBox and wxCheckListBox Styles . . . . . . . . . . . . . . . . . . . . . . . . . .97 wxListBox and wxCheckListBox Events . . . . . . . . . . . . . . . . . . . . . . . . .97 wxListBox Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .98 wxCheckListBox Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . .98 wxRadioBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .98 wxRadioBox Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .99 wxRadioBox Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .100 wxRadioBox Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .100 wxRadioButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .100 wxRadioButton Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .101 wxRadioButton Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .101 wxRadioButton Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . .101 wxScrollBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .101 wxScrollBar Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .102 wxScrollBar Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .102 wxScrollBar Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .103 wxSpinButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .103 wxSpinButton Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104 wxSpinButton Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104 wxSpinButton Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104 wxSpinCtrl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104 wxSpinCtrl Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .105 wxSpinCtrl Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .105 wxSpinCtrl Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .106 wxSlider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .106

Smart_FMf.qxd 6/10/05 1:52 PM Page xi Contents xi wxSlider Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .106 wxSlider Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .107 wxSlider Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .107 wxTextCtrl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .107 wxTextCtrl Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .109 wxTextCtrl Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .110 wxTextCtrl Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .110 wxToggleButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .111 wxToggleButton Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .112 wxToggleButton Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .112 wxToggleButton Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . .112 Static Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .112 wxGauge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .112 wxGauge Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .113 wxGauge Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .113 wxGauge Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .113 wxStaticText . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .113 wxStaticText Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .114 wxStaticText Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .114 wxStaticBitmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .114 wxStaticBitmap Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .115 wxStaticBitmap Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . .115 wxStaticLine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .115 wxStaticLine Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .115 wxStaticLine Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .116 wxStaticBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .116 wxStaticBox Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .116 wxStaticBox Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .116 Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .116 wxMenu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .117 wxMenu Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .118 wxMenu Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .120 Control Bars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .122 wxMenuBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .122 wxMenuBar Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .123 wxMenuBar Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .123 wxMenuBar Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .123 wxToolBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .124 Tool Bitmap Colors Under Windows . . . . . . . . . . . . . . . . . . . . . . . . . . .125 wxToolBar Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .126 wxToolBar Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .126 wxToolBar Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .127 wxStatusBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .128 wxStatusBar Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .129 wxStatusBar Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .129 wxStatusBar Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .129 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .130

Smart_FMf.qxd 6/10/05 1:52 PM Page xii xii Contents 5 Drawing and Printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31 Understanding Device Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .131 Available Device Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .132 Drawing on Windows with wxClientDC . . . . . . . . . . . . . . . . . . . . . . . . . . .133 Erasing Window Backgrounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .134 Drawing on Windows with wxPaintDC . . . . . . . . . . . . . . . . . . . . . . . . . . .135 Drawing on Bitmaps with wxMemoryDC . . . . . . . . . . . . . . . . . . . . . . . . .138 Creating Metafiles with wxMetafileDC . . . . . . . . . . . . . . . . . . . . . . . . . . .138 Accessing the Screen with wxScreenDC . . . . . . . . . . . . . . . . . . . . . . . . . .139 Printing with wxPrinterDC and wxPostScriptDC . . . . . . . . . . . . . . . . . . .139 Drawing Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .140 wxColour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .141 wxPen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .142 wxBrush . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .143 wxFont . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .145 wxPalette . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .147 Device Context Drawing Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .148 Drawing Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .150 Drawing Lines and Shapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .152 Drawing Splines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .156 Drawing Bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .157 Filling Arbitrary Areas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .159 Logical Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .159 Using the Printing Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .161 More on wxPrintout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .163 Scaling for Printing and Previewing . . . . . . . . . . . . . . . . . . . . . . . . . . . . .165 Printing under Unix with GTK+ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .167 3D Graphics with wxGLCanvas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .168 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .170 6 Handling Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .171 Mouse Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .171 Handling Button and Motion Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . .173 Handling Mouse Wheel Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .174 Handling Keyboard Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .175 An Example Character Event Handler . . . . . . . . . . . . . . . . . . . . . . . . . . .178 Key Code Translation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .179 Modifier Key Variations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .179 Accelerators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .180 Handling Joystick Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .181 wxJoystick Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .183 wxJoystickEvent Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . .184 wxJoystick Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .184 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .185

Smart_FMf.qxd 6/10/05 1:52 PM Page xiii Contents xiii 7 Window Layout Using Sizers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .187 Layout Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .187 Sizers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .188 Common Features of Sizers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .190 Programming with Sizers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .192 Programming with wxBoxSizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .193 Programming with wxStaticBoxSizer . . . . . . . . . . . . . . . . . . . . . . . . . . . .196 Programming with wxGridSizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .197 Programming with wxFlexGridSizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . .198 Programming with wxGridBagSizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . .200 Further Layout Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .201 Dialog Units . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .202 Platform-Adaptive Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .202 Dynamic Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .204 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .204 8 Using Standard Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .205 Informative Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .205 wxMessageDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .205 wxMessageDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .207 wxMessageBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .207 wxProgressDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .208 wxProgressDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .209 wxBusyInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .210 wxBusyInfo Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .210 wxShowTip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .210 wxShowTip Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .211 File and Directory Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .212 wxFileDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .212 wxFileDialog Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .214 wxFileDialog Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .215 wxFileDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .215 wxDirDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .216 wxDirDialog Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .218 wxDirDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .218 Choice and Selection Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .218 wxColourDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .218 wxColourData Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .221 wxColourDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .221 wxFontDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .221 wxFontData Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .223 Font Selector Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .224 wxSingleChoiceDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .224 wxSingleChoiceDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .225

Smart_FMf.qxd 6/10/05 1:52 PM Page xiv xiv Contents wxMultiChoiceDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .225 wxMultiChoiceDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .226 Entry Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .227 wxNumberEntryDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .227 wxNumberEntryDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .227 wxTextEntryDialog and wxPasswordEntryDialog . . . . . . . . . . . . . . . . . . .227 wxTextEntryDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .228 wxFindReplaceDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .228 Handling Events from the Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . .229 wxFindDialogEvent Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .230 Passing Data to the Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .230 wxFindReplaceData Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .230 Find and Replace Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .231 Printing Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .232 wxPageSetupDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .232 wxPageSetupData Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .235 wxPageSetupDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .235 wxPrintDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .235 wxPrintDialogData Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .238 wxPrintDialog Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .238 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .239 9 Writing Custom Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .241 Steps in Creating a Custom Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .241 An Example: PersonalRecordDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .242 Deriving a New Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .243 Designing Data Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .243 Coding the Controls and Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .244 Data Transfer and Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .247 Handling Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .249 Handling UI Updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .250 Adding Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .251 Tooltips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .251 Context-Sensitive Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .251 Online Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .252 The Complete Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .253 Invoking the Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .253 Adapting Dialogs for Small Devices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .254 Further Considerations in Dialog Design . . . . . . . . . . . . . . . . . . . . . . . . . . .255 Keyboard Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .255 Data and UI Separation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .256 Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .256 Aesthetics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .257 Alternatives to Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .257

Smart_FMf.qxd 6/10/05 1:52 PM Page xv Contents xv Using wxWidgets Resource Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .257 Loading Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .258 Using Binary and Embedded Resource Files . . . . . . . . . . . . . . . . . . . . . .259 Translating Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .261 The XRC Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .261 Writing Resource Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .262 Foreign Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .263 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .264 10 Programming with Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .265 Image Classes in wxWidgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .265 Programming with wxBitmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .267 Creating a wxBitmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .268 Setting a wxMask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .269 The XPM Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .270 Drawing with Bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .271 Packaging Bitmap Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .272 Programming with wxIcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .272 Creating a wxIcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .273 Using wxIcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .274 Associating an Icon with an Application . . . . . . . . . . . . . . . . . . . . . . . . . .274 Programming with wxCursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .275 Creating a wxCursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .276 Using wxCursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .278 Using wxSetCursorEvent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .278 Programming with wxImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .279 Loading and Saving Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .280 Transparency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .282 Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .284 Color Reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .284 Manipulating wxImage Data Directly . . . . . . . . . . . . . . . . . . . . . . . . . . . .285 Image Lists and Icon Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .285 Customizing Art in wxWidgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .288 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .290 11 Clipboard and Drag and Drop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .291 Data Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .291 Data Source Duties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .292 Data Target Duties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .292 Using the Clipboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .293 Implementing Drag and Drop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .294 Implementing a Drag Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .294 1. Preparation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .295 2. Drag Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .295

Smart_FMf.qxd 6/10/05 1:52 PM Page xvi xvi Contents 3. Dragging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .295 4. Processing the Result . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .296 Implementing a Drop Target . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .297 1. Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .297 2. Dragging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .298 3. Drop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .298 Using Standard Drop Targets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .298 Creating a Custom Drop Target . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .299 More on wxDataObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .300 Deriving from wxDataObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .301 The wxWidgets Drag and Drop Sample . . . . . . . . . . . . . . . . . . . . . . . .302 Drag and Drop Helpers in wxWidgets . . . . . . . . . . . . . . . . . . . . . . . . . . . .311 wxTreeCtrl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .311 wxListCtrl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .312 wxDragImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .313 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .316 12 Advanced Window Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .317 wxTreeCtrl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .317 wxTreeCtrl Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .319 wxTreeCtrl Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .320 wxTreeCtrl Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .321 wxListCtrl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .322 wxListCtrl Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .323 wxListCtrl Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .324 wxListItem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .325 wxListCtrl Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .327 Using wxListCtrl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .329 Virtual List Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .330 wxWizard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .331 wxWizard Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .331 wxWizard Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .332 wxWizard Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .332 wxHtmlWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .338 wxHtmlWindow Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .341 wxHtmlWindow Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .342 Embedding Windows in HTML Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . .343 HTML Printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .344 wxGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .346 The wxGrid System of Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .348 wxGrid Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .349 wxGrid Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .350 Functions for Creation, Deletion, and Data Retrieval . . . . . . . . . . . . .350 Presentation Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .351 Functions for Setting and Getting wxGrid Metrics . . . . . . . . . . . . . . .352

Smart_FMf.qxd 6/10/05 1:52 PM Page xvii Contents xvii Selection and Cursor Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .352 Miscellaneous wxGrid Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .353 wxTaskBarIcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .354 wxTaskBarIcon Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .358 wxTaskBarIcon Member Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .358 Writing Your Own Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .359 The Custom Control Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .360 Adding DoGetBestSize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .362 Defining a New Event Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .363 Displaying Information on the Control . . . . . . . . . . . . . . . . . . . . . . . . . . .363 Handling Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .364 Defining Default Event Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .364 Implementing Validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .365 Implementing Resource Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .367 Determining Control Appearance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .367 A More Complex Example: wxThumbnailCtrl . . . . . . . . . . . . . . . . . . . . . .369 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .371 13 Data Structure Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .373 Why Not STL? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .373 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .374 Using wxString . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .374 wxString, Characters, and String Literals . . . . . . . . . . . . . . . . . . . . . . . . .375 Basic wxString to C Pointer Conversions . . . . . . . . . . . . . . . . . . . . . . . . .375 Standard C String Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .376 Converting to and from Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .377 wxStringTokenizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .378 wxRegEx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .379 wxArray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .379 Array Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .379 wxArrayString . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .380 Array Construction, Destruction, and Memory Management . . . . . . . . .381 Array Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .381 wxList and wxNode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .383 wxHashMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .385 Storing and Processing Dates and Times . . . . . . . . . . . . . . . . . . . . . . . . . . . .387 wxDateTime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .388 wxDateTime Constructors and Modifiers . . . . . . . . . . . . . . . . . . . . . . . . .388 wxDateTime Accessors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .388 Getting the Current Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .389 Parsing and Formatting Dates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .389 Date Comparisons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .389 Date Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .389 Helper Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .391 wxObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .391

Smart_FMf.qxd 6/10/05 1:52 PM Page xviii xviii Contents wxLongLong . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .392 wxPoint and wxRealPoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .392 wxRect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .392 wxRegion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .393 wxSize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .394 wxVariant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .394 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .395 14 Files and Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .397 File Classes and Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .397 wxFile and wxFFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .397 wxTextFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .399 wxTempFile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .400 wxDir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .400 wxFileName . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .401 File Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .402 Stream Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .403 File Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .403 Memory and String Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .405 Reading and Writing Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .405 Socket Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .406 Filter Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .407 Zip Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .407 Virtual File Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .408 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .411 15 Memory Management, Debugging, and Error Checking . . . . . . . . . .413 Memory Management Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .413 Creating and Deleting Window Objects . . . . . . . . . . . . . . . . . . . . . . . . . . .413 Creating and Copying Drawing Objects . . . . . . . . . . . . . . . . . . . . . . . . . .415 Initializing Your Application Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .415 Cleaning Up Your Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .416 Detecting Memory Leaks and Other Errors . . . . . . . . . . . . . . . . . . . . . . . . .416 Facilities for Defensive Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .418 Error Reporting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .419 wxMessageOutput Versus wxLog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .423 Providing Run-Time Type Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . .424 Using wxModule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .426 Loading Dynamic Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .427 Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .428 Debugging Tips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .429 Debugging X11 Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .429 Simplify the Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .430 Debugging a Release Build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .430 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .431

Smart_FMf.qxd 6/10/05 1:52 PM Page xix Contents xix 16 Writing International Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . .433 Introduction to Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .433 Providing Translations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .434 poEdit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .434 Step-by-Step Guide to Using Message Catalogs . . . . . . . . . . . . . . . . . . . .435 Using wxLocale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .437 Character Encodings and Unicode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .438 Converting Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .439 wxEncodingConverter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .440 wxCSConv (wxMBConv) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .440 Converting Outside of a Temporary Buffer . . . . . . . . . . . . . . . . . . . . . . . .441 Help Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .442 Numbers and Dates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .443 Other Media . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .443 A Simple Sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .444 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .446 17 Writing Multithreaded Applications . . . . . . . . . . . . . . . . . . . . . . . . . . .447 When to Use Threads, and When Not To . . . . . . . . . . . . . . . . . . . . . . . . . . . .447 Using wxThread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .448 Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .449 Specifying Stack Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .450 Specifying Priority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .450 Starting the Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .450 How to Pause a Thread or Wait for an External Condition . . . . . . . . . . .450 Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .451 Synchronization Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .451 wxMutex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .451 Deadlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .452 wxCriticalSection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .453 wxCondition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .453 wxCondition Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .453 wxSemaphore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .456 The wxWidgets Thread Sample . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .456 Alternatives to Multithreading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .457 Using wxTimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .457 Idle Time Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .459 Yielding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .460 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .460 18 Programming with wxSocket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .463 Socket Classes and Functionality Overview . . . . . . . . . . . . . . . . . . . . . . . . .464 Introduction to Sockets and Basic Socket Processing . . . . . . . . . . . . . . . . . .464 The Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .465 The Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .466

Smart_FMf.qxd 6/10/05 1:52 PM Page xx xx Contents Connecting to a Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467 Socket Addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .468 Socket Clients . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .468 Socket Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .469 Socket Event Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .469 wxSocketEvent Major Member Functions . . . . . . . . . . . . . . . . . . . . . . .469 Using Socket Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .470 Socket Status and Error Notifications . . . . . . . . . . . . . . . . . . . . . . . . . . . .470 Sending and Receiving Socket Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .471 Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .471 Writing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .472 Creating a Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .472 wxSocketServer Major Member Functions . . . . . . . . . . . . . . . . . . . . . .472 Handling a New Connection Event . . . . . . . . . . . . . . . . . . . . . . . . . . . .472 Socket Event Recap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .473 Socket Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .473 Blocking and Non-Blocking Sockets in wxWidgets . . . . . . . . . . . . . . . . . .474 The Impossible Socket Combination . . . . . . . . . . . . . . . . . . . . . . . . . . .475 How Flags Affect Socket Behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .475 Using wxSocket as a Standard Socket . . . . . . . . . . . . . . . . . . . . . . . . . . . .476 Using Socket Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .476 File Sending Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .477 File Receiving Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .478 Alternatives to wxSocket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .479 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .480 19 Working with Documents and Views . . . . . . . . . . . . . . . . . . . . . . . . . . .481 Document/View Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .481 Step 1: Choose an Interface Style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .483 Step 2: Create and Use Frame Classes . . . . . . . . . . . . . . . . . . . . . . . . . . .483 Step 3: Define Your Document and View Classes . . . . . . . . . . . . . . . . . . .486 Step 4: Define Your Window Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . .495 Step 5: Use wxDocManager and wxDocTemplate . . . . . . . . . . . . . . . . . . .497 Other Document/View Capabilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .500 Standard Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .500 Printing and Previewing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .500 File History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .500 Explicit Document Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .501 Strategies for Implementing Undo/Redo . . . . . . . . . . . . . . . . . . . . . . . . . . . .501 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .503 20 Perfecting Your Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .505 Single Instance or Multiple Instances? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .506 Modifying Event Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .510 Reducing Flicker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .512

Smart_FMf.qxd 6/10/05 1:52 PM Page xxi Contents xxi Implementing Online Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .513 Using a Help Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .515 Extended wxWidgets HTML Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .517 Authoring Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .518 Other Ways to Provide Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .519 Context-Sensitive Help and Tooltips . . . . . . . . . . . . . . . . . . . . . . . . . . . . .520 Menu Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .521 Parsing the Command Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .521 Storing Application Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .523 Reducing the Number of Data Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .523 Finding the Application Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .524 Invoking Other Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .525 Running an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .525 Launching Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .525 Redirecting Process Input and Output . . . . . . . . . . . . . . . . . . . . . . . . . . .527 Managing Application Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .529 Storing Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .530 Editing Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .531 Application Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .532 Installation on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .532 Installation on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .534 Shared Library Issues on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .535 Installation on Mac OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .535 Following UI Design Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .538 Standard Buttons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .538 Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .538 Icons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .539 Fonts and Colors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .539 Application Termination Behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .539 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .540 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .540 A Installing wxWidgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .543 B Building Your Own wxWidgets Applications . . . . . . . . . . . . . . . . . . . . .559 C Creating Applications with DialogBlocks . . . . . . . . . . . . . . . . . . . . . . . .575 D Other Features in wxWidgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .585 E Third-Party Tools for wxWidgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .593 F wxWidgets Application Showcase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .601 G Using the CD-ROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .611

Smart_FMf.qxd 6/10/05 1:52 PM Page xxii xxii Contents H How wxWidgets Processes Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .613 I Event Classes and Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .617 J Code Listings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .627 K Porting from MFC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .643 Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .657 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .663

Smart_FMf.qxd 6/10/05 1:52 PM Page xxiii Foreword It's a pleasure to introduce you to Cross-Platform GUI Programming with wxWidgets, the first book on wxWidgets since it was originally released more than a decade ago. wxWidgets is a first-class, open source response to the need for portability in an increasingly heterogeneous computing world. Being tied to specific hard- ware or a single operating system is often undesirable and sometimes impermis- sible, hence the well-understood need for cross-platform GUI frameworks. Given the long life of open source products and the often-transient nature of proprietary solutions, developers are wise to base their applications on an infrastructure that is going to survive long-term, as wxWidgets has and will continue to do. wxWidgets combines countless years’ worth of hard-earned wisdom con- tributed by developers worldwide, abstracting functionality and finding solu- tions for platform-specific issues. You, the developer, are protected both from shifts in computing trends and from the intricacies and frustrations of each platform’s native API. Becoming a wxWidgets user is an invitation into a community that spans individuals, startups, government organizations, large companies, and open source projects. When you contribute, you are forging a connection between yourself and a community that is broadly representative of the reach of infor- mation technology in the 21st century. wxWidgets-based applications may be found not just in the software industry but also in medicine, archaeology, physics, astronomy, processor manufacturing, education, geological exploration, the transport industry, space exploration, and many other fields as well. "Chandler," the Personal Information Manager now under development at the Open Source Applications Foundation, uses wxWidgets to run under Windows, Mac OS X, and Linux. Some of our developers have become active contributors to the wxWidgets project, following the virtuous circle of open source development. We look forward to having you join us in the ever-growing community of developers using wxWidgets, and I personally wish you all the best with your wxWidgets projects. Mitch Kapor, Chair OSAF June 2005 xxiii

Smart_FMf.qxd 6/10/05 1:52 PM Page xxiv

Smart_FMf.qxd 6/10/05 1:52 PM Page xxv Preface WHO THIS BOOK IS FOR This book is a guide to using wxWidgets, an open-source construction kit for writing sophisticated C++ applications targeting a variety of platforms, including Windows, Linux, Mac OS X, and Pocket PC. With help from this book, a competent programmer can create multi-platform applications with confidence. Developers already familiar with wxWidgets should also find it useful for brushing up their knowledge. This book is accessible to developers with a variety of experience and backgrounds. You may come from a Windows or Unix perspective; you may previously have experience in MFC, OWL, Win32, Mac OS, Motif, or console- mode Unix programming. Or perhaps you have come from a different career entirely and are looking for a way to get up to speed on multiple platforms. The book can’t specifically cover the details of the C++ language, but it’s com- mon for people to successfully learn C++ and wxWidgets at the same time, and the straightforward nature of the wxWidgets API makes this process easier. The reader does not need to know more advanced C++ techniques like tem- plates, streams, and exceptions. However, wxWidgets does not prevent you from using these techniques. Managers will find the book useful in discovering what wxWidgets can do for them, particularly in Chapter 1, “Introduction.” The combination of the book and the resources on the accompanying CD-ROM will give your staff all they need for getting started on cross-platform programming projects. You’ll see how wxWidgets puts tools of tremendous power into your hands, with ben- efits that include:

Cost savings from writing code once that will compile on Windows, Unix, Mac OS X, and other platforms.

Customer satisfaction from delivering stable, fast, attractive applica- tions with a native look and feel. xxv

Smart_FMf.qxd 6/10/05 1:52 PM Page xxvi xxvi Preface

Increased productivity from the wide variety of classes that wxWidgets provides, both for creating great GUIs and for general appli- cation development.

Increased market share due to support for platforms you may not have previously considered, and the ability to internationalize your applica- tions.

Support from a large, active wxWidgets community that answers ques- tions helpfully and provides prompt bug fixing. The sample of third-party add-ons listed in Appendix E, “Third-Party Tools for wxWidgets,” is evi- dence of a thriving ecosystem.

Access to the source for enhancement and trouble-shooting. This is a guide to writing wxWidgets application with C++, but you can use a variety of other languages such as Python, Perl, a BASIC variant, Lua, Eiffel, JavaScript, Java, Ruby, Haskell, and C#. Some of these bindings are more advanced than others. For more information, please see Appendix E and the wxWidgets web site at http://www.wxwidgets.org. We focus on three popular desktop platforms: Microsoft Windows, Linux using GTK+, and Mac OS X. However, most of the book also applies to other platforms supported by wxWidgets. In particular, wxWidgets can be used with most Unix variants. THE CD-ROM The CD-ROM contains example code from the book, the wxWidgets 2.6 distri- bution for Windows, Linux, Mac OS X, and other platforms, and several tools to help you use wxWidgets, including the translation tool poEdit. For Windows users, we supply three free compilers you can use with wxWidgets: MinGW, Digital Mars C++, and OpenWatcom C++. In addition, we provide you with DialogBlocks Personal Edition, a sophis- ticated rapid application development (RAD) tool for you to create complex windows with very little manual coding. You can use it to compile and run samples that accompany the book as well as to create your own applications for personal use, and it also provides convenient access to the wxWidgets ref- erence manual. Updates to the book and CD-ROM can be obtained from this site: http://www.wxwidgets.org/book HOW TO USE THIS BOOK It’s advisable to read at least Chapters 1 through 10 in order, but you can skip to other chapters if you need to complete a particular task. If you haven’t installed wxWidgets before, you may want to look at Appendix A, “Installing

Smart_FMf.qxd 6/10/05 1:52 PM Page xxvii Preface xxvii wxWidgets,” early on. MFC programmers will find it useful to read Appendix K, “Porting from MFC,” as a point of reference. Because this book is not a complete API reference, you’ll find it useful to keep the wxWidgets reference manual open. The reference manual is available in a number of formats, including Windows HTML Help and PDF, and it should be in your wxWidgets distribution; if not, it can be downloaded from the wxWidgets web site. You can also refer to the many samples in the wxWidgets distribution to supplement the examples given in this book. Note that the book is intended to be used in conjunction with wxWidgets 2.6 or later. The majority of the book will apply to earlier versions, but be aware that some functionality will be missing, and in a small number of cases, the behavior may be different. In particular, sizer behavior changed somewhat between 2.4 and 2.5. For details, please see the topic “Changes Since 2.4.x” in the wxWidgets reference manual. CONVENTIONS For code examples, we mostly follow the wxWidgets style guidelines, for example:

Words within class names and functions have an initial capital, for exam- ple MyFunkyClass.

The m_ prefix denotes a member variable, s_ denotes a static variable, g_ denotes a global variable; local variables generally start with a lowercase letter, for example textCtrl. You can find more about the wxWidgets style guidelines at http://www. wxwidgets.org/standard.htm. Sometimes we’ll also use comments that can be parsed by the documen- tation tool Doxygen, such as: /*! A class description */ /// A function description Classes, functions, identifiers, variables, and standard wxWidgets objects are marked with a teletype font in the text. User interface commands, such as menu and button labels, are marked in italics.

Smart_FMf.qxd 6/10/05 1:52 PM Page xxviii xxviii Preface CHAPTER SUMMARY Chapter 1: Introduction What is wxWidgets, and why use it? A brief history; the wxWidgets commu- nity; the license; wxWidgets ports and architecture explained. Chapter 2: Getting Started A small wxWidgets sample: the application class; the main window; the event table; an outline of program flow. Chapter 3: Event Handling Event tables and handlers; how a button click is processed; skipping events; pluggable and dynamic event handlers; defining custom events; window iden- tifiers. Chapter 4: Window Basics The main features of a window explained; a quick guide to the commonest window classes; base window classes such as wxWindow; top-level windows; con- tainer windows; non-static controls; static controls; menus; control bars. Chapter 5: Drawing and Printing Device context principles; the main device context classes described; buffered drawing; drawing tools; device context drawing functions; using the printing framework; 3D graphics with wxGLCanvas. Chapter 6: Handling Input Handling mouse and mouse wheel events; handling keyboard events; key- codes; modifier key variations; accelerators; handling joystick events. Chapter 7: Window Layout Using Sizers Layout basics; sizers introduced; common features of sizers; programming with sizers. Further layout issues: dialog units; platform-adaptive layouts; dynamic layouts.

Smart_FMf.qxd 6/10/05 1:52 PM Page xxix Preface xxix Chapter 8: Using Standard Dialogs Informative dialogs such as wxMessageBox and wxProgressDialog; file and direc- tory dialogs such as wxFileDialog; choice and selection dialogs such as wxColourDialog and wxFontDialog; entry dialogs such as wxTextEntryDialog and wxFindReplaceDialog; printing dialogs: wxPageSetupDialog and wxPrintDialog. Chapter 9: Writing Custom Dialogs Steps in creating a custom dialog; an example: PersonalRecordDialog; deriving a new class; designing data storage; coding the controls and layout; data trans- fer and validation; handling events; handling UI updates; adding help; adapt- ing dialogs for small devices; further considerations in dialog design; using wxWidgets resource files; loading resources; using binary and embedded resource files; translating resources; the XRC format; writing resource han- dlers; foreign controls. Chapter 10: Programming with Images Image classes in wxWidgets; programming with wxBitmap; programming with wxIcon; programming with wxCursor; programming with wxImage; image lists and icon bundles; customizing wxWidgets graphics with wxArtProvider. Chapter 11: Clipboard and Drag and Drop Data objects; data source duties; data target duties; using the clipboard; implementing drag and drop; implementing a drag source; implementing a drop target; using standard drop targets; creating a custom drop target; more on wxDataObject; drag and drop helpers in wxWidgets. Chapter 12: Advanced Window Classes wxTreeCtrl; wxListCtrl; wxWizard; wxHtmlWindow; wxGrid; wxTaskBarIcon; writing your own controls; the control declaration; defining a new event class; display- ing information; handling input; defining default event handlers; implement- ing validators; implementing resource handlers; determining control appearance. Chapter 13: Data Structure Classes Why not STL? wxString; wxStringTokenizer; wxRegEx; wxArray; wxList; wxHashMap; wxDateTime; wxObject; wxLongLong; wxPoint and wxRealPoint; wxRect; wxRegion; wxSize; wxVariant.

Smart_FMf.qxd 6/10/05 1:52 PM Page xxx xxx Preface Chapter 14: Files and Streams wxFile and wxFFile; wxTextFile; wxTempFile; wxDir; wxFileName; file functions; file streams; memory and string streams; data streams; socket streams; filter streams; zip streams; virtual file systems. Chapter 15: Memory Management, Debugging, and Error Checking Creating and deleting window objects; creating and copying drawing objects; initializing your application object; cleaning up your application; detecting memory leaks and other errors; facilities for defensive programming; error reporting; providing run-time type information; using wxModule; loading dynamic libraries; exception handling; debugging tips. Chapter 16: Writing International Applications Introduction to internationalization; providing translations; using message catalogs; using wxLocale; character encodings and Unicode; converting data; help files; numbers and dates; other media; an example. Chapter 17: Writing Multithreaded Applications When to use threads, and when not to; using wxThread; thread creation; start- ing the thread; how to pause a thread or wait for an external condition; termi- nation; synchronization objects; wxMutex; deadlocks; wxCriticalSection; wxCondition; wxSemaphore; the wxWidgets thread sample; alternatives to multi- threading: wxTimer, idle time processing, and yielding. Chapter 18: Programming with wxSocket Socket classes and functionality overview; introduction to sockets and basic socket processing; connecting to a server; socket events; socket status and error noti- fications; sending and receiving socket data; creating a server; socket event recap; socket flags; blocking and non-blocking sockets in wxWidgets; how flags affect socket behavior; using wxSocket as a standard socket; using socket streams; alternatives to wxSocket. Chapter 19: Working with Documents and Views Document/view basics; choosing an interface style; creating and using frame classes; defining your document and view classes; defining your window classes; using wxDocManager and wxDocTemplate; other document/view capabili- ties; standard identifiers; printing and previewing; file history; explicit docu- ment creation; strategies for implementing undo/redo.

Smart_FMf.qxd 6/10/05 1:52 PM Page xxxi Preface xxxi Chapter 20: Perfecting Your Application Single instance versus multiple instances; modifying event handling; reducing flicker; using a help controller; extended wxWidgets HTML help; authoring help; other ways to provide help; parsing the command line; storing applica- tion resources; invoking other applications; launching documents; redirecting process input and output; managing application settings; application installa- tion on Windows, Linux and Mac OS X; following UI design guidelines. Appendix A: Installing wxWidgets Downloading and unpacking wxWidgets; configuration/build options; Windows—Microsoft Visual Studio and VC++ command-line; Windows— Borland C++; Windows—MinGW with and without MSYS; Unix/Linux and Mac OS X—GCC; customizing setup.h; rebuilding after updating wxWidgets files; using contrib libraries. Appendix B: Building Your Own wxWidgets Applications Windows—Microsoft Visual Studio; Linux—KDevelop; Mac OS X—Xcode; makefiles; cross-platform builds using Bakefile; wxWidgets symbols and head- ers; using wx-config. Appendix C: Creating Applications with DialogBlocks What is DialogBlocks? Installing and upgrading DialogBlocks; the DialogBlocks interface; the sample project; compiling the sample; creating a new project; creating a dialog; creating a frame; creating an application object; debugging your application. Appendix D: Other Features in wxWidgets Further window classes; ODBC classes; MIME types manager; network func- tionality; multimedia classes; embedded web browsers; accessibility; OLE automation; renderer classes; event loops. Appendix E: Third-Party Tools for wxWidgets Language bindings such as wxPython and wxPerl; tools such as wxDesigner, DialogBlocks and poEdit; add-on libraries such as wxMozilla, wxCURL, wxPropertyGrid.

Smart_FMf.qxd 6/10/05 1:52 PM Page xxxii xxxii Preface Appendix F: wxWidgets Application Showcase Descriptions of notable wxWidgets applications, such as AOL Communicator and Audacity. Appendix G: Using the CD-ROM Browsing the CD-ROM; the CD-ROM contents. Appendix H: How wxWidgets Processes Events An illustrated description of how event processing works. Appendix I: Event Classes and Macros A summary of the important event classes and macros. Appendix J: Code Listings Code listings for the PersonalRecordDialog and the wxWizard examples. Appendix K: Porting from MFC General observations; application initialization; message maps; converting dialogs and other resources; documents and views; printing; string handling and translation; database access; configurable control bars; equivalent func- tionality by macros and classes.

Smart_FMf.qxd 6/10/05 1:52 PM Page xxxiii Acknowledgments wxWidgets owes its success to the hard work of many talented people. We would like to thank them all, with special consideration for that essential support network: our long-suffering families and partners. wxWidgets sup- porters and contributors include the following (apologies for any uninten- tional omissions): Yiorgos Adamopoulos, Jamshid Afshar, Alejandro Aguilar-Sierra, Patrick Albert, Bruneau Babet, Mitchell Baker, Mattia Barbon, Nerijus Baliunas, Karsten Ballueder, Jonathan Bayer, Michael Bedward, Kai Bendorf, Yura Bidus, Jorgen Bodde, Borland, Keith Gary Boyce, Chris Breeze, Sylvain Bougnoux, Wade Brainerd, Pete Britton, Ian Brown, C. Buckley, Doug Card, Marco Cavallini, Dmitri Chubraev, Robin Corbet, Cecil Coupe, Stefan Csomor, Andrew Davison, Gilles Depeyrot, Duane Doran, Neil Dudman, Robin Dunn, Hermann Dunkel, Jos van Eijndhoven, Chris Elliott, David Elliott, David Falkinder, Rob Farnum, Joel Farley, Tom Felici, Thomas Fettig, Matthew Flatt, Pasquale Foggia, Josep Fortiana, Todd Fries, Dominic Gallagher, Roger Gammans, Guillermo Rodriguez Garcia, Brian Gavin, Wolfram Gloger, Aleksandras Gluchovas, Markus Greither, Norbert Grotz, Stephane Gully, Stefan Gunter, Bill Hale, Patrick Halke, Stefan Hammes, Guillaume Helle, Harco de Hilster, Kevin Hock, Cord Hockemeyer, Klaas Holwerda, Markus Holzem, Ove Kaaven, Mitch Kapor, Matt Kimball, Hajo Kirchoff, Olaf Klein, Jacob Jansen, Leif Jensen, Mark Johnson, Bart Jourquin, John Labenski, Guilhem Lavaux, Ron Lee, Hans Van Leemputten, Peter Lenhard, Jan Lessner, Nicholas Liebmann, Torsten Liermann, Per Lindqvist, Jesse Lovelace, Tatu Männistö, Lindsay Mathieson, Scott Maxwell, Bob Mitchell, Thomas Myers, Oliver Niedung, Stefan Neis, Ryan Norton, Robert O'Connor, Jeffrey Ollie, Kevin Ollivier, William Osborne, Hernan Otero, Ian Perrigo, Timothy Peters, Giordano Pezzoli, Harri Pasanen, Thomaso Paoletti, Garrett Potts, Robert Rae, Marcel Rasche, Mart Raudsepp, Andy Robinson, Robert Roebling, Alec Ross, Gunnar Roth, Thomas Runge, Tom Ryan, Dino Scaringella, Jobst Schmalenbach, Dimitri Schoolwerth, Arthur Seaton, Paul Shirley, Wlodzimierz Skiba, John Skiff, Vaclav Slavik, Brian Smith, Neil xxxiii

Smart_FMf.qxd 6/10/05 1:52 PM Page xxxiv xxxiv Acknowledgments Smith, Stein Somers, Petr Smilauer, Kari Systä, George Tasker, Austin Tate, Arthur Tetzlaff-Deas, Paul Thiessen, Jonathan Tonberg, Jyrki Tuomi, Janos Vegh, Andrea Venturoli, David Webster, Michael Wetherell, Otto Wyss, Vadim Zeitlin, Xiaokun Zhu, Zbigniew Zagórski, Edward Zimmermann. Thanks also to Dotsrc.org and SourceForge for hosting project services. Thanks are due in particular to Vadim Zeitlin, Vaclav Slavik, Robert Roebling, Stefan Csomor, and Robin Dunn for permission to adapt some of their contributions to the wxWidgets reference manual. Special thanks go to Stefan Csomor who contributed Chapter 16 and Chapter 17, and to Kevin Ollivier who wrote the Bakefile tutorial in Appendix B. We would also like to thank Mitch Kapor for writing the foreword. We are very grateful to Mark Taub for his patience and advice through- out. A big thank you goes to Marita Allwood, Harriet Smart, Antonia Smart, Clayton Hock, and Ethel Hock for all their love, support, and encouragement. A debt is also owed to all those who have reviewed and suggested improve- ments to the book, including: Stefan Csomor, Dimitri Schoolwerth, Robin Dunn, Carl Godkin, Bob Paddock, Chris Elliott, Michalis Kabrianis, Marc- Andre Lureau, Jonas Karlsson, Arnout Engelen, Erik van der Wal, Greg Smith, and Alexander Stigsen. Finally, we hope that you enjoy reading this book and, most importantly, have fun using wxWidgets to build great-looking, multi-platform applications! Julian Smart and Kevin Hock June 2005

Smart_FMf.qxd 6/10/05 1:52 PM Page xxxv About the Authors Julian Smart has degrees from the University of St. Andrews and the University of Dundee. After working on model-based reasoning at the Scottish Crop Research Institute, he moved to the Artificial Intelligence Applications Institute at the University of Edinburgh, where he founded the wxWidgets project in 1992. Since starting Anthemion Software in 1996, Julian has been helping other companies deploy wxWidgets, and he sells tools for program- mers, including DialogBlocks and HelpBlocks. He has worked as a consultant for various companies including Borland and was a member of Red Hat's eCos team, writing GUI tools to support the embedded operating system. In 2004, Julian and his wife Harriet launched a consumer product for fiction writers called Writer’s Café, written with wxWidgets. Julian and Harriet live in Edinburgh with their daughter Toni. Kevin Hock has degrees from Miami University (Oxford, Ohio) in Computer Science and Accounting and has taught courses at Miami in both Java and client-server systems. In 2002, he started work on an instant messaging sys- tem and founded BitWise Communications, LLC, in 2003, offering both profes- sional and personal instant messaging. During the course of developing BitWise using wxWidgets, Kevin became a wxWidgets developer and has pro- vided enhancements to all platforms. Kevin lives in Oxford, Ohio. Stefan Csomor is director and owner of Advanced Concepts AG, a company that specializes in cross-platform development and consulting. In addition to being a qualified medical doctor, he has more than 15 years of experience in object-oriented programming and has been writing software for 25 years. Stefan is the main author of the Mac OS port of wxWidgets. xxxi

Smart_FMf.qxd 6/10/05 1:52 PM Page xxxvi

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 1 C H A P T E R 1 Introduction In this chapter, we answer a few basic questions about what wxWidgets is and what sets it apart from other solutions. We outline the project’s history, how the wxWidgets community works, how wxWidgets is licensed, and an overview of the architecture and available parts. WHAT IS WXWIDGETS? wxWidgets is a programmer’s toolkit for writing desktop or mobile applica- tions with graphical user interfaces (GUIs). It’s a framework, in the sense that it does a lot of the housekeeping work and provides default application behav- ior. The wxWidgets library contains a large number of classes and methods for the programmer to use and customize. Applications typically show windows containing standard controls, possibly drawing specialized images and graph- ics and responding to input from the mouse, keyboard, or other sources. They may also communicate with other processes or drive other programs. In other words, wxWidgets makes it relatively easy for the programmer to write an application that does all the usual things modern applications do. While wxWidgets is often labeled a GUI development toolkit, it is in fact much more than that and has features that are useful for many aspects of application development. This has to be the case because all of a wxWidgets application needs to be portable to different platforms, not just the GUI part. wxWidgets provides classes for files and streams, multiple threads, applica- tion settings, interprocess communication, online help, database access, and much more. 1

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 2 2 Introduction Chapter 1 WHY USE WXWIDGETS? One area where wxWidgets differs from many other frameworks, such as MFC or OWL, is its multi-platform nature. wxWidgets has an Application Programming Interface (API) that is the same, or very nearly the same, on all supported platforms. This means that you can write an application on Windows, for example, and with very few changes (if any) recompile it on Linux or Mac OS X. This has a huge cost benefit compared with completely rewriting an application for each platform, and it also means that you do not need to learn a different API for each platform. Furthermore, it helps to future-proof your applications. As the computing landscape changes, wxWidgets changes with it, allowing your application to be ported to the latest and greatest systems supporting the newest features. Another distinguishing feature is that wxWidgets provides a native look and feel. Some frameworks use the same widget code running on all platforms, perhaps with a theme makeover to simulate each platform’s native appear- ance. By contrast, wxWidgets uses the native widgets wherever possible (and its own widget set in other cases) so that not only does the application look native on the major platforms, but it actually is native. This is incredibly important for user acceptance because even small, almost imperceptible dif- ferences in the way an application behaves, compared with the platform stan- dard, can create an alienating experience for the user. To illustrate, Figure 1-1 shows a wxWidgets application called StoryLines, a tool to help fiction writers plot their stories, running on Windows XP. Figure 1-1 StoryLines on Windows

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 3 Why Use wxWidgets? 3 It’s recognizably a Windows application, with GUI elements such as tabs, scrollbars, and drop-down lists conforming to the current Windows theme. Similarly, Figure 1-2 shows StoryLines as a Mac OS X application, with the expected Aqua look and feel. There is no menu bar attached to the StoryLines window because it follows the Mac OS convention of showing the current win- dow’s menu bar at the top of the screen. Figure 1-2 StoryLines on Mac OS X Finally, Figure 1-3 shows StoryLines as a GTK+ application running on Red Hat Linux. Why not just use Java? While Java is great for web-based applications, it’s not always the best choice for the desktop. In general, C++-based applica- tions using wxWidgets are faster, have a more native look and feel, and are easier to install because they don’t rely on the presence of the Java virtual machine. C++ also allows greater access to low-level functionality and is eas- ier to integrate with existing C and C++ code. For all these reasons, very few of the popular desktop applications that you use today are built with Java. wxWidgets allows you to deliver the high-performance, native applications that your users expect.

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 4 4 Introduction Chapter 1 Figure 1-3 StoryLines on Linux wxWidgets is an open source project. Naturally, this means that it costs nothing to use wxWidgets (unless you feel like generously donating to the project!), but it also has important philosophical and strategic significance. Open source software has a habit of outlasting its proprietary equivalents. As a developer using wxWidgets, you know that the code you rely on will never disappear. You can always fix any problems yourself by changing the source code. It can also be a lot more fun to take part in an open source community than trying to get hold of corporate support staff. Participants in open source projects tend to be there because they love what they’re doing and can’t wait to share their knowledge, whereas corporate support staff members are not always so idealistically motivated. When you use wxWidgets, you tap into an astonishing talent pool, with contributors from a wide range of backgrounds. Many aspects of application development that you might otherwise have to laboriously code yourself have been encapsulated by these developers in easy- to-use classes that you can plug into your code. An active user community will assist you on the mailing lists, and you’ll enjoy discussions not only about wxWidgets but often other matters close to the hearts of both experienced and inexperienced developers as well. Perhaps one day you’ll join in the success of wxWidgets and become a contributor yourself! wxWidgets has wide industry support, or to use a popular buzzword, mindshare. The list of users includes AOL, AMD, CALTECH, Lockheed Martin, NASA, the Open Source Applications Foundation, Xerox, and many

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 5 A Brief History of wxWidgets 5 others. wxWidgets encompasses the whole spectrum of users, from single developer software outfits to large corporations, from computer science depart- ments to medical research groups, and from ecological research to the telecom- munications industry. It’s also used by a myriad of open source projects, such as the Audacity audio editor and the pgAdmin III database design and man- agement system. People use wxWidgets for many different reasons, whether simply as an elegant MFC replacement on a single platform, or to allow them to move easily from (say) Microsoft Windows to Unix and Mac OS X. wxWidgets is addressing the challenges of mobile platforms, too, with ports for embedded Linux, Microsoft Pocket PC, and (soon) Palm OS. A BRIEF HISTORY OF WXWIDGETS The wxWidgets project started life in 1992 when Julian Smart was working at the University of Edinburgh on a diagramming tool called Hardy. He didn’t want to choose between deploying it either on Sun workstations or PCs, so he decided to use a cross-platform framework. Because the range of existing cross-platform frameworks was limited, and the department didn’t have a budget for it anyway, there was little choice but to write his own. The univer- sity gave him permission to upload wxWidgets 1.0 to the department’s FTP site in September 1992, and other developers began to use the code. Initially, wxWidgets targeted XView and MFC 1.0; Borland C++ users complained about the requirement for MFC, so it was rewritten to use pure Win32. Because XView was giving way to Motif, a Motif port quickly followed. Over time, a small but enthusiastic community of wxWidgets users was established and a mailing list created. Contributions and fixes were sent in, including an Xt port by Markus Holzem. wxWidgets gradually picked up more and more users from all over the world: individuals, academics, government departments, and—most gratifying of all—corporate users who found that wxWidgets offered a better product and better support than the commercial products they had looked at or used. In 1997, a new wxWidgets 2 API was designed with help from Markus Holzem. Wolfram Gloger suggested that wxWidgets should be ported to GTK+, the up-and-coming widget set being adopted for the GNOME desktop environ- ment. Robert Roebling became the lead developer for wxGTK, which is now the main Unix/Linux port of wxWidgets. In 1998, the Windows and GTK+ ports were merged and put under CVS control. Vadim Zeitlin joined the proj- ect to contribute huge amounts of design and code, and Stefan Csomor started a Mac OS port, also in 1998. 1999 saw the addition of Vaclav Slavik’s impressive wxHTML classes and the HTML-based help viewer. In 2000, SciTech, Inc. sponsored initial development of wxUniversal, wxWidgets’s own set of widgets for use on platforms

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 6 6 Introduction Chapter 1 that have no widget set of their own. wxUniversal was first used in SciTech’s port to MGL, their low-level graphics layer. In 2002, Julian Smart and Robert Roebling added the wxX11 port using the wxUniversal widgets. Requiring only Unix and X11, wxX11 is suitable for any Unix environment and can be used in fairly low-spec systems. In July 2003, wxWidgets started running on Windows CE, and Robert Roebling demonstrated wxGTK applications running on the GPE embedded Linux platform. In 2004, wxWidgets was renamed from the original moniker “wxWindows,” after objections from Microsoft based on its Windows trademark. Also during 2004, Stefan Csomor and a host of other contributors completely revamped wxMac for OS X, significantly improving the appearance and function- ality of OS X applications. A port using Cocoa was also steadily improved, led by David Elliot, and William Osborne won our challenge to deliver an embryonic Palm OS 6 port that supports the wxWidgets “minimal” sample. Version 2.6 was released in April 2005, incorporating major improvements to all ports. Future plans for wxWidgets include

A package management tool, to make it easier to integrate third-party components

Improved support for embedded applications

Alternative event handling mechanisms

Enhanced controls, such as a combined tree and list control

wxHTML 2, with full web capabilities on all platforms

Further compatibility with standards such as STL

A full Palm OS port THE WXWIDGETS COMMUNITY The wxWidgets community is a vibrant one, with two mailing lists: wx-users (for users) and wx-dev (for contributors). The web site has news, articles, and links to releases, and there is also the wxWidgets “Wiki,” a set of web pages where everyone can add information. A forum is also available for developers and users alike. Here’s a list of the web addresses for these resources:

http://www.wxwidgets.org: the wxWidgets home page

http://lists.wxwidgets.org: the mailing list archives

http://wiki.wxwidgets.org: the wxWidgets Wiki

http://www.wxforum.org: the wxWidgets forum As with most open source projects, wxWidgets is developed using a CVS repository, a source management system that keeps track of code history. In order to prevent a chaotic free-for-all, a small number of developers have write

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 7 License Considerations 7 access to CVS, and others can contribute by posting bug reports and patches (currently handled by SourceForge’s trackers). Development occurs on two main branches: the “stable” branch, where only binary-compatible bug fixes are allowed, and the “development” branch (CVS head). So-called stable releases are even-numbered (for example, 2.4.x) and development releases are odd-numbered (for example, 2.5.x). Users can wait for new releases or down- load the source from the appropriate branch by anonymous CVS. Decisions about API changes and other technical issues are made by con- sensus, usually by discussion on the wx-dev list. As well as the main wxWidgets community, many projects have spun off and enjoy their own communities— for example wxPython and wxPerl (see Appendix E, “Third-Party Tools for wxWidgets”). WXWIDGETS AND OBJECT-ORIENTED PROGRAMMING Like all modern GUI frameworks, wxWidgets benefits from heavy use of object- oriented programming concepts. Each window is represented as a C++ object; these objects have well-defined behavior, and can receive and react to events. What the user sees is the visual manifestation of this interacting system of objects. Your job as a developer is to orchestrate these objects’ collective behavior, a task made easier by the default behaviors that wxWidgets implements for you. Of course, it’s no coincidence that object-oriented programming and GUIs mesh well—they grew up together. The object-oriented language Smalltalk designed by Alan Kay and others in the 1970s was an important milestone in GUI history, making innovations in user interface technology as well as lan- guage design, and although wxWidgets uses a different language and API, the principles employed are broadly the same. LICENSE CONSIDERATIONS The wxWidgets license (officially, the “wxWindows License” for legal and his- torical reasons) is L-GPL with an exception clause. You can read the license files in detail on the web site or in the docs directory of the distribution, but in summary, you can use wxWidgets for commercial or free software with no roy- alty charge. You can link statically or dynamically to the wxWidgets library. If you make changes to the wxWidgets source code, you are obliged to make these freely available. You do not have to make your own source code or object files available. Please also consult the licenses for the optional subordinate libraries that are distributed with wxWidgets, such as the PNG and JPEG libraries. The source code that accompanies this book is provided under the wxWindows License.

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 8 8 Introduction Chapter 1 THE WXWIDGETS ARCHITECTURE Table 1-1 shows the four conceptual layers: the wxWidgets public API, each major port, the platform API used by that port, and finally the underlying operating system. Table 1-1 wxWidgets Ports wxWidgets API wxWidgets Port wxMSW wxGTK wxX11 wxMotif wxMac wxCocoa wxOS2 wxPalmOS wxMGL Platform API Win32 GTK+ Xlib Motif/ Carbon Cocoa PM Palm OS MGL Lesstif Protein APIs Operating System Windows/ Unix/Linux Mac OS 9/ Mac OS X OS/2 Palm OS Unix/ Windows Mac OS X DOS CE The following are the main wxWidgets ports that exist at the time of writing. wxMSW This port compiles and runs on all 32-bit and 64-bit variants of the Microsoft Windows operating system, including Windows 95, Windows 98, Windows ME, Windows NT, Windows 2000, Windows XP and Windows 2003. It can also be compiled to use Winelib under Linux, and has a configuration that works on Windows CE (see “wxWinCE”). wxMSW can be configured to use the wxUniversal widgets instead of the regular Win32 ones. wxGTK wxWidgets for GTK+ can use versions 1.x or 2.x of the GTK+ widget set, on any Unix variant that supports X11 and GTK+ (for example, Linux, Solaris, HP-UX, IRIX, FreeBSD, OpenBSD, AIX, and others). It can also run on embed- ded platforms with sufficient resources—for example, under the GPE Palmtop Environment (see Figure 1-4). wxGTK is the recommended port for Unix- based systems.

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 9 The wxWidgets Architecture 9 Figure 1-4 The wxWidgets “Life!” demo under GPE on an iPAQ PDA wxX11 wxWidgets for X11 uses the wxUniversal widget set and runs directly on Xlib with no native widget set. This makes the port suitable for embedded systems, but it can also be used for desktop applications where it is undesirable to link with GTK+. This is supported on any Unix system running X11. wxX11 is not as mature as the wxGTK port. Figure 1-5 shows the Life! demo compiled under wxX11 and running on Familiar Linux / TinyX on an iPAQ PDA. Figure 1-5 The wxWidgets “Life!” demo running on embedded wxX11

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 10 10 Introduction Chapter 1 wxMotif This port can use Motif, OpenMotif, or Lesstif on most Unix systems. Sun Microsystems is putting its weight behind GNOME and GTK+, so Motif is no longer an attractive option for most developers and users. wxMac wxMac targets Mac OS 9 (from 9.1 upwards) and Mac OS X (from 10.2.8 upwards). For Mac OS 9 builds, you need the Metrowerks CodeWarrior tools, and for Mac OS X, you can use either Metrowerks CodeWarrior or Apple tools. When using Apple’s tools, you should use Xcode 1.5 or higher, or—if you are just using command line tools—GCC 3.3 or higher. wxCocoa A port in progress, this targets the Cocoa API of Mac OS X. Although the func- tionality of Carbon and Cocoa is similar, this port has the potential for sup- porting GNUStep running on platforms other than a Mac. wxWinCE The Windows CE port encompasses various SDKs based on the Windows CE platform, including Pocket PC and Smartphone. The bulk of this port consists of the wxMSW Win32 port, with some omissions and additions for the smaller platform. Figure 1-6 shows the wxWidgets Life! demo running on the Pocket PC 2003 emulator. Figure 1-7 shows four screens from the wxWidgets dialog demo running on Smartphone 2003 with a 176 × 220 pixel display. User inter- face adaptations done by wxWidgets for this restricted platform include con- structing a nested menu in place of the usual menu bar because Smartphone only supports two menu buttons. Some additional application hints are required, such as calling SetLeftMenu and SetRightMenu instead of adding con- ventional OK and Cancel buttons to a dialog.

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 11 The wxWidgets Architecture 11 Figure 1-6 The wxWidgets “Life!” demo on Pocket PC 2003 Figure 1-7 The wxWidgets “dialogs” demo on Smartphone 2003

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 12 12 Introduction Chapter 1 wxPalmOS This is a port to Palm OS 6 (Cobalt). At the time of writing, the port is in its infancy but can be used to compile and run a simple sample in the Palm OS 6 simulator (see Figure 1-8). Figure 1-8 A wxWidgets sample under Palm OS 6 wxOS2 wxOS2 is a Presentation Manager port for OS/2 or eComStation. wxMGL This port targets the MGL low-level graphics layer from SciTech Software, Inc., and uses the wxUniversal widget set. Internal Organization Internally, the wxWidgets code base is broadly separated into six layers: 1. Common code is used in all ports. It includes data structure classes, run-time type information, and the base classes, such as wxWindowBase, which are used to factor out code common to all implementations of a class. 2. Generic code implements advanced widgets independently of any plat- form, allowing emulation of controls and other functionality not present on a given platform. wxWizard and wxCalendarCtrl are examples of generic controls. 3. wxUniversal is a set of basic widgets for those platforms that do not have their own native widget set, such as bare X11 and MGL.

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 13 Summary 13 4. Platform-specific code implements classes using native functionality. An example of platform-specific code is the wxMSW implementation of wxTextCtrl wrapping the Win32 edit control. 5. Contributed code exists in a separate hierarchy named contrib and includes non-essential but useful classes such as wxStyledTextCtrl. 6. Third-party code comprises libraries that were developed independ- ently of wxWidgets but are used to implement important features. Examples of third-party code include the JPEG, Zlib, PNG, and Expat libraries. Each port takes what it needs from these layers to implement the wxWidgets API. How does wxWidgets know which classes to use when you’re compiling your application? When you include a wxWidgets header file, such as wx/textctrl.h, you’re actually including a platform-specific file such as wx/msw/textctrl.h, due to directives in wx/textctrl.h that conditionally include the appropriate declarations. You then link your application against a library that has been compiled with suitable settings for the platform in question. You can have several configurations available at once, in particular Debug and Release versions, and you can normally link either statically or dynamically to the wxWidgets code. If you want, you can disable components in a wxWidgets build, or make choices such as Unicode versus ANSI, by editing the file setup.h or using configure options depending on compiler. For more details, please see Appendix A, “Installing wxWidgets.” Note that although wxWidgets is a wrapper around each native API, you are not prevented from writing platform-specific code in that native API if you need to, although this is seldom necessary. SUMMARY In this chapter, we’ve established what wxWidgets is, described a little of its history, summarized the available ports, and taken a brief look at how the library is organized internally. In the next chapter, “Getting Started,” we will look at some sample code and get a feeling for what it’s like to write a wxWidgets application.

Smart_Ch01f.qxd 6/10/05 11:13 AM Page 14

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 15 C H A P T E R 2 Getting Started In this chapter, we’ll get a feel for the structure of a simple wxWidgets pro- gram, using a tiny sample. We’ll look at where and how a wxWidgets applica- tion starts and ends, how to show the main window, and how to react to commands from the user. Following the wxWidgets philosophy of keeping things nice and simple, that’s all we’re going to cover in this chapter. You may also want to refer to Appendix A, “Installing wxWidgets.” A SMALL WXWIDGETS SAMPLE Figure 2-1 shows what our sample looks like under Windows. Figure 2-1 Minimal sample under Windows The minimal wxWidgets application shows a main window (a wxFrame) with a menu bar and status bar. The menus allow you to show an “about box” or quit the program. Not exactly a killer app, but it’s enough to show some of the basic principles of wxWidgets—and to reassure you that you can start sim- ple and work your way up to a complete application as your confidence and expertise grow. 15

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 16 16 Getting Started Chapter 2 THE APPLICATION CLASS Every wxWidgets application defines an application class deriving from wxApp. There is only one instance of it, and this instance represents the running application. At the very least, your class should define an OnInit function that will be called when wxWidgets is ready to start running your code (equivalent to main or WinMain when writing a C or Win32 application). Here is the smallest application class declaration you can sensibly write: // Declare the application class class MyApp : public wxApp { public: // Called on application startup virtual bool OnInit(); }; The implementation of OnInit usually creates at least one window, interprets any command-line arguments, sets up data for the application, and performs any other initialization tasks required for the application. If the function returns true, wxWidgets starts the event loop that processes user input and runs event handlers as necessary. If the function returns false, wxWidgets will clean up its internal structures, and the application will terminate. A simple implementation of OnInit might look like this: bool MyApp::OnInit() { // Create the main application window MyFrame *frame = new MyFrame(wxT(“Minimal wxWidgets App”)); // Show it frame->Show(true); // Start the event loop return true; } This creates an instance of our new class MyFrame (we’ll define this class shortly), shows it, and returns true to start the event loop. Unlike child win- dows, top-level windows such as frames and dialogs need to be shown explic- itly after creation. The frame title is passed to the constructor wrapped in the wxT() macro. You’ll see this used a lot in wxWidgets samples and in the library code itself— it converts string and character literals to the appropriate type to allow the application to be compiled in Unicode mode. This macro is also known by the alias _T(). There is no run-time performance penalty for using it. (You’ll also see the underscore macro _() used to enclose strings, which tells wxWidgets to translate the string. See Chapter 16, “Writing International Applications,” for more details.)

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 17 The Frame Class 17 Where is the code that creates the instance of MyApp? wxWidgets does this internally, but you still need to tell wxWidgets what kind of object to create. So you need to add a macro in your implementation file: // Give wxWidgets the means to create a MyApp object IMPLEMENT_APP(MyApp) Without specifying the class, wxWidgets would not know how to create a new application object. This macro also inserts code that checks that the applica- tion and library were compiled using the same build configuration, allowing wxWidgets to report accidental mismatches that might later cause a hard-to- debug run-time failure. When wxWidgets creates a MyApp object, it assigns the result to the global variable wxTheApp. You can use this in your application, but it would be more convenient if you didn’t have to cast the wxApp pointer to MyApp. By inserting this macro after your application class declaration: // Implements MyApp& wxGetApp() DECLARE_APP(MyApp) you can then call the function wxGetApp, which returns a reference to the MyApp object. Tip Even if you don’t use DECLARE_APP, you can still use the variable wxTheApp to call wxApp functions. This will avoid the need to include your specific application header. It can be useful within code (such as a library) that doesn’t know about specific application classes, and to save compilation time. THE FRAME CLASS Let’s look at the frame class MyFrame. A frame is a top-level window that con- tains other windows, and usually has a title bar and menu bar. Here’s our sim- ple frame class declaration that we will put after the declaration of MyApp: // Declare our main frame class class MyFrame : public wxFrame { public: // Constructor MyFrame(const wxString& title); // Event handlers

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 18 18 Getting Started Chapter 2 void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); private: // This class handles events DECLARE_EVENT_TABLE() }; Our frame class has a constructor, two event handlers to link menu commands to C++ code, and a macro to tell wxWidgets that this class handles events. THE EVENT HANDLERS As you may have noticed, the event handler functions in MyFrame are not vir- tual and should not be virtual. How, then, are they called? The answer lies in the event table, as follows. // Event table for MyFrame BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(wxID_ABOUT, MyFrame::OnAbout) EVT_MENU(wxID_EXIT, MyFrame::OnQuit) END_EVENT_TABLE() An event table, placed in a class’s implementation file, tells wxWidgets how events coming from the user or from other sources are routed to member func- tions. With the event table shown previously, mouse clicks on menu items with the identifiers wxID_EXIT and wxID_ABOUT are routed to the functions MyFrame::OnQuit and MyFrame::OnAbout, respectively. EVT_MENU is just one of many event table macros you can use to tell wxWidgets what kind of event should be routed to what function. The identifiers used here are predefined by wxWidgets, but you will often define your own identifiers, using enums, con- sts, or preprocessor defines. This kind of event table is a static way of routing events, and cannot be changed at runtime. In the next chapter, we’ll describe how to set up dynamic event handlers. While we’re dealing with event tables, let’s see the two functions we’re using as event handlers. void MyFrame::OnAbout(wxCommandEvent& event) { wxString msg; msg.Printf(wxT(“Hello and welcome to %s”), wxVERSION_STRING); wxMessageBox(msg, wxT(“About Minimal”), wxOK | wxICON_INFORMATION, this); }

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 19 The Frame Constructor 19 void MyFrame::OnQuit(wxCommandEvent& event) { // Destroy the frame Close(); } MyFrame::OnAbout shows a message box when the user clicks on the About menu item. wxMessageBox takes a message, a caption, a combination of styles, and a parent window. MyFrame::OnQuit is called when the user clicks on the Quit menu item, thanks to the event table. It calls Close to destroy the frame, triggering the shutdown of the application, because there are no other frames. In fact, Close doesn’t directly destroy the frame—it generates a wxEVT_CLOSE_WINDOW event, and the default handler for this event destroys the frame using wxWindow::Destroy. There’s another way the frame can be closed and the application shut down—the user can click on the close button on the frame, or select Close from the system (or window manager) menu. How does OnQuit get called in this case? Well, it doesn’t—instead, wxWidgets sends a wxEVT_CLOSE_WINDOW event to the frame via Close (as used in OnQuit). wxWidgets handles this event by default and destroys the window. Your application can override this behavior and provide its own event handler—for example, if you want to ask the user for confirmation before closing. For more details, please see Chapter 4, “Window Basics.” This sample doesn’t need it, but most applications should provide an OnExit function in its application class to clean up data structures before quitting. Note that this function is only called if OnInit returns true. THE FRAME CONSTRUCTOR Finally, we have the frame constructor, which implements the frame icon, a menu bar, and a status bar. #include “mondrian.xpm” MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) { // Set the frame icon SetIcon(wxIcon(mondrian_xpm)); // Create a menu bar wxMenu *fileMenu = new wxMenu; // The “About” item should be in the help menu wxMenu *helpMenu = new wxMenu; helpMenu->Append(wxID_ABOUT, wxT(“&About...\tF1”), wxT(“Show about dialog”)); fileMenu->Append(wxID_EXIT, wxT(“E&xit\tAlt-X”), wxT(“Quit this program”));

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 20 20 Getting Started Chapter 2 // Now append the freshly created menu to the menu bar... wxMenuBar *menuBar = new wxMenuBar(); menuBar->Append(fileMenu, wxT(“&File”)); menuBar->Append(helpMenu, wxT(“&Help”)); // ... and attach this menu bar to the frame SetMenuBar(menuBar); // Create a status bar just for fun CreateStatusBar(2); SetStatusText(wxT(“Welcome to wxWidgets!”)); } This constructor calls the base constructor with the parent window (none, hence NULL), window identifier, and title. The identifier argument is wxID_ANY, which tells wxWidgets to generate an identifier itself. The base constructor creates the actual window associated with the C++ instance—another way to achieve this is to call the default constructor of the base class, and then explic- itly call wxFrame::Create from within the MyFrame constructor. Small bitmaps and icons can be implemented using the XPM format on all platforms. XPM files have valid C++ syntax and so can be included as shown previously; the SetIcon line creates an icon on the stack using the C++ variable mondrian_xpm defined in mondrian.xpm, and associates it with the frame. The menu bar is created next. Menu items are added using the identifier (such as the standard identifier wxID_ABOUT), the label to be displayed, and a help string to be shown on the status bar. Within each label, a mnemonic letter is marked by a preceding ampersand, and an accelerator is preceded by the tab character (\t). A mnemonic is the letter a user presses to highlight a par- ticular item when the menu is displayed. An accelerator is a key combination (such as Alt+X) that can be used to perform that action without showing the menu at all. The last thing that the constructor does is to create a status bar with two fields at the bottom of the frame and set the first field to the string “Welcome to wxWidgets!” THE WHOLE PROGRAM It’s worth putting together the bits so you can see what the whole program looks like. Normally, you’d have a separate header file and implementation file, but for such a simple program, we can put it all in the same file. Listing 2-1 The Complete Example // Name: minimal.cpp // Purpose: Minimal wxWidgets sample // Author: Julian Smart #include “wx/wx.h”

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 21 The Whole Program 21 // Declare the application class class MyApp : public wxApp { public: // Called on application startup virtual bool OnInit(); }; // Declare our main frame class class MyFrame : public wxFrame { public: // Constructor MyFrame(const wxString& title); // Event handlers void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); private: // This class handles events DECLARE_EVENT_TABLE() }; // Implements MyApp& GetApp() DECLARE_APP(MyApp) // Give wxWidgets the means to create a MyApp object IMPLEMENT_APP(MyApp) // Initialize the application bool MyApp::OnInit() { // Create the main application window MyFrame *frame = new MyFrame(wxT(“Minimal wxWidgets App”)); // Show it frame->Show(true); // Start the event loop return true; } // Event table for MyFrame BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(wxID_ABOUT, MyFrame::OnAbout) EVT_MENU(wxID_EXIT, MyFrame::OnQuit) END_EVENT_TABLE() void MyFrame::OnAbout(wxCommandEvent& event) { wxString msg; msg.Printf(wxT(“Hello and welcome to %s”), wxVERSION_STRING); (continues)

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 22 22 Getting Started Chapter 2 Listing 2-1 (continued) wxMessageBox(msg, wxT(“About Minimal”), wxOK | wxICON_INFORMATION, this); } void MyFrame::OnQuit(wxCommandEvent& event) { // Destroy the frame Close(); } #include “mondrian.xpm” MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) { // Set the frame icon SetIcon(wxIcon(mondrian_xpm)); // Create a menu bar wxMenu *fileMenu = new wxMenu; // The “About” item should be in the help menu wxMenu *helpMenu = new wxMenu; helpMenu->Append(wxID_ABOUT, wxT(“&About...\tF1”), wxT(“Show about dialog”)); fileMenu->Append(wxID_EXIT, wxT(“E&xit\tAlt-X”), wxT(“Quit this program”)); // Now append the freshly created menu to the menu bar... wxMenuBar *menuBar = new wxMenuBar(); menuBar->Append(fileMenu, wxT(“&File”)); menuBar->Append(helpMenu, wxT(“&Help”)); // ... and attach this menu bar to the frame SetMenuBar(menuBar); // Create a status bar just for fun CreateStatusBar(2); SetStatusText(wxT(“Welcome to wxWidgets!”)); } COMPILING AND RUNNING THE PROGRAM The sample can be found on the accompanying CD-ROM in examples/chap02, which you should copy to a folder on your hard drive for compiling. Because it’s not possible to provide makefiles that work “out of the box” with every reader’s software environment, we provide a DialogBlocks project file with configurations for most platforms and compilers. See Appendix C, “Creating Applications with DialogBlocks,” for help with configuring DialogBlocks for your compiler. We also cover compiling wxWidgets applications in detail in Appendix B, “Building Your Own wxWidgets Applications.”

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 23 Summary 23 Install wxWidgets and DialogBlocks from the accompanying CD-ROM. On Windows, you should install one or more of the compilers provided on the CD-ROM if you do not already own a suitable compiler. After setting your wxWidgets and compiler paths in the DialogBlocks Paths settings page, open the file examples/chap02/minimal.pjd. Select a suitable configuration for your compiler and platform such as MinGW Debug or VC++ Debug (Windows), GCC Debug GTK+ (Linux), or GCC Debug Mac (Mac OS X), and press the green Build and Run Project button. You may be prompted to build wxWidgets if you have not already built it for the selected configuration. You can also find a similar sample in samples/minimal in your wxWidgets distribution. If you do not wish to use DialogBlocks, you can simply compile this sample instead. See Appendix A, “Installing wxWidgets,” for instructions on how to build wxWidgets samples. PROGRAM FLOW This is how the application starts running: 1. Depending on platform, the main, WinMain, or equivalent function runs (supplied by wxWidgets, not the application). wxWidgets initializes its internal data structures and creates an instance of MyApp. 2. wxWidgets calls MyApp::OnInit, which creates an instance of MyFrame. 3. The MyFrame constructor creates the window via the wxFrame constructor and adds an icon, menu bar, and status bar. 4. MyApp::OnInit shows the frame and returns true. 5. wxWidgets starts the event loop, waiting for events and dispatching them to the appropriate handlers. As noted here, the application terminates when the frame is closed, when the user either selects the Quit menu item or closes the frame via standard but- tons or menus (these will differ from one platform to the next). SUMMARY This chapter gave you an idea of how a really simple wxWidgets application works. We’ve touched on the wxFrame class, event handling, application initial- ization, and creating a menu bar and status bar. However complicated your own code gets, the basic principles of starting the application will remain the same, as we’ve shown in this small example. In the next chapter, we’ll take a closer look at events and how your application handles them.

Smart_Ch02f.qxd 6/10/05 11:14 AM Page 24

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 25 C H A P T E R 3 Event Handling This chapter explains the principles behind event-driven programming in wxWidgets, including how events are generated, how an application handles them using event tables, and where window identifiers fit in. We’ll also discuss plug-in and dynamic event handlers, and we’ll describe how you can create your own event class, types, and macros. EVENT-DRIVEN PROGRAMMING When programmers encountered an Apple Macintosh for the first time, they were astonished at how different it was from the conventional computer expe- rience of the period. Moving the pointer from one window to another, playing with scrollbars, menus, text controls and so on, it was hard to imagine how the underlying code sorted out this fabulous complexity. It seemed that many dif- ferent things were going on in parallel—in reality, a clever illusion. For many people, the Macintosh was their first introduction to the world of event-driven programming. All GUI applications are event-driven. That is to say, the application sits in a loop waiting for events initiated by the user or from some other source (such as a window needing to be refreshed, or a socket connection), and then dispatches the event to an appropriate function that handles it. Although it may seem that windows are being updated simultaneously, most GUI applica- tions are not multithreaded, so each task is being done in turn—as becomes painfully obvious when something slows your computer to a crawl and you can see each window being repainted, one after the other. Different frameworks have different ways of exposing event handling to the developer—the primary method in wxWidgets is the use of event tables, as explained in the next section. 25

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 26 26 Event Handling Chapter 3 EVENT TABLES AND HANDLERS The wxWidgets event processing system is a more flexible mechanism than virtual functions, allowing us to avoid declaring all possible event handlers in a base class, which would be totally impractical as well as inefficient. Every class that derives from wxEvtHandler, including frames, buttons, menus, and even documents, can contain an event table to tell wxWidgets how events are routed to handler functions. All window classes (derived from wxWindow), and the application class, are derived from wxEvtHandler. To create a static event table (one that’s created at compile time), you need to 1. Declare a new class that is derived directly or indirectly from wxEvtHandler. 2. Add a member function for each event that must be handled. 3. Declare the event table in the class with DECLARE_EVENT_TABLE. 4. Implement the event table in the source file with BEGIN_EVENT_TABLE... END_EVENT_TABLE. 5. Add event table entries to the table (such as EVT_BUTTON), mapping each event to the appropriate member function. All event handler functions have the same form—their return type is void, they are not virtual, and they take a single event object argument. (If you’re familiar with MFC, this will come as a relief, because there is no standard sig- nature for message handlers in MFC.) The type of this argument changes according to the type of event being handled; for example, simple control com- mands and menu commands share the wxCommandEvent class. A size event (caused by a window being resized either by the program or by the user) is rep- resented by the wxSizeEvent class. Each event type has different accessors that you can use to learn about the cause of the event or the resulting UI change, such as a change in the value of a text control. In simple cases (such as a but- ton press), you can often ignore the event object. Expanding on the example from the previous chapter, let’s add a handler for frame sizing, plus an OK button. A simple class declaration for an event- handling class looks like this: // Declare our main frame class class MyFrame : public wxFrame { public: // Constructor MyFrame(const wxString& title); // Event handlers void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); void OnSize(wxSizeEvent& event); void OnButtonOK(wxCommandEvent& event); private: // This class handles events DECLARE_EVENT_TABLE() };

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 27 Event Tables and Handlers 27 The code to add menu items will be similar to the code in the previous chapter, while code to add an OK button might look like this, in the frame constructor: wxButton* button = new wxButton(this, wxID_OK, wxT(“OK”), wxPoint(200, 200)); Here’s the event table, allowing the frame to handle menu, button, and size events. // Event table for MyFrame BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU (wxID_ABOUT, MyFrame::OnAbout) EVT_MENU (wxID_EXIT, MyFrame::OnQuit) EVT_SIZE ( MyFrame::OnSize) EVT_BUTTON (wxID_OK, MyFrame::OnButtonOK) END_EVENT_TABLE() When the user clicks on the About or Quit menu items, the event is sent to the frame, and MyFrame’s event table tells wxWidgets that a menu event with iden- tifier wxID_ABOUT should be sent to MyFrame::OnAbout, and a menu event with identifier wxID_EXIT should be sent to MyFrame::OnQuit. In other words, these functions will be called with a single argument (in this case, wxCommandEvent) when the event loop processes the events. The EVT_SIZE macro does not take an identifier argument because a size event can only be handled by the object that generated it. The EVT_BUTTON entry will cause OnButtonOK to be called when a button in the frame’s window hierarchy with identifier wxID_OK is pressed. This example shows that the event can be processed by a window other than the source of the event. Let’s assume the button is a child of MyFrame. When the button is pressed, wxWidgets will check the wxButton class for a suitable handler. When one is not found, it checks the parent of the button—in this case, a MyFrame instance. The event matches an entry in the event table, so MyFrame::OnButtonOK is called. This search of the window component hierarchy, as well as the inheritance hierarchy, means that you can choose where you handle events. For example, if you are designing a dialog class that must respond to commands such as wxID_OK, but you need to leave the creation of the actual controls to other programmers using your code, you can still define default behavior for the controls as long as they have the expected identifiers. The generation of a button click event and its subsequent matching against an appropriate event table entry is illustrated in Figure 3-1. Two class hierarchies are shown, for wxButton and MyFrame. Each class has its own event table that potentially contains the entry that will match the event. When the user clicks on the OK button, a new wxCommandEvent object is created that con- tains the identifier (wxID_OK) and the type of event (wxEVT_COMMAND_BUTTON_ CLICKED). Now the event tables are searched using wxEvtHandler::ProcessEvent;

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 28 28 Event Handling Chapter 3 all of the wxButton’s event table events are tried, then wxControl’s, then wxWindow’s. If no appropriate entry is found that matches against the event type and identifier, wxWidgets tries the parent of the button and starts searching its event table. It has a matching entry: EVT_BUTTON(wxID_OK,MyFrame::OnButtonOK) so MyFrame::OnButtonOK is called. Note that only command events (whose event classes are based directly or indirectly on wxCommandEvent) are recursively applied to the window parent’s event handler. As this quite often causes confusion for users, here is a list of system events that will not get sent to the parent’s event handler: wxActivate Event, wxCloseEvent, wxEraseEvent, wxFocusEvent, wxKeyEvent, wxIdleEvent, wxInitDialogEvent, wxJoystickEvent, wxMenuEvent, wxMouseEvent, wxMoveEvent, wxPaintEvent, wxQueryLayoutInfoEvent, wxSizeEvent, wxScrollWinEvent, and wxSysColourChangedEvent. These events do not propagate because they are meant for a particular window, and sending a child’s paint event to its parent, for example, does not make sense. 1. Clicking OK Instance of MyFrame wxCommandEvent generates an wxEVT_COMMAND_BUTTON_CLICKED event. id=wxID_OK OK 2.Event tables Event table are searched. wxButton wxControl Event table child of Event table wxWindow 3. An entry matched and the function is called. Event table MyFrame wxCommandEvent wxEVT_..._CLICKED Event table id=wxID_OK wxFrame MyFrame::OnButtonOK Event table wxTopLevelWindow wxWindow Event table Figure 3-1 Processing a button click event

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 29 Pluggable Event Handlers 29 SKIPPING EVENTS The wxWidgets event processing system implements something very close to virtual methods in normal C++, which means that it is possible to alter the behavior of a class by overriding its event handling functions. In many cases, this works even for changing the behavior of native controls. For example, it is possible to filter out selected keystroke events sent by the system to a native text control by overriding wxTextCtrl and defining a handler for key events using EVT_KEY_DOWN. This would indeed prevent any key events from being sent to the native control, which might not be what is desired. In this case, the event handler function has to call wxEvent::Skip to indicate that the search for the event handler should continue. To summarize, instead of explicitly calling the base class version, as you would have done with C++ virtual functions, you should instead call Skip on the event object. For example, to make the derived text control only accept “a” to “z” and “A” to “Z,” we would use this code: void MyTextCtrl::OnChar(wxKeyEvent& event) { if ( wxIsalpha( event.KeyCode() ) ) { // Keycode is within range, so do normal processing. event.Skip(); } else { // Illegal keystroke. We don’t call event.Skip() so the // event is not processed anywhere else. wxBell(); } } PLUGGABLE EVENT HANDLERS You don’t have to derive a new class from a window class in order to process events. Instead, you can derive a new class from wxEvtHandler, define the appropriate event table, and then call wxWindow::PushEventHandler to add this object to the window’s stack of event handlers. Your new event handler will catch events first; if they are not processed, the next event handler in the stack will be searched, and so on. Use wxWindow::PopEventHandler to pop the topmost event handler off the stack, passing true if you want it to be deleted. With this method, you can avoid a lot of class derivation and potentially use the same event handler object to handle events from instances of different classes.

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 30 30 Event Handling Chapter 3 Normally, the value returned from wxWindow::GetEventHandler is the win- dow itself, but if you have used PushEventHandler, this will not be the case. If you ever have to call a window’s event handler manually, use the GetEvent Handler function to retrieve the window’s topmost event handler and use that to call the member function in order to ensure correct processing of the event handler stack. One use of PushEventHandler is to temporarily or permanently change the behavior of the GUI. For example, you might want to invoke a dialog editor in your application. You can grab all the mouse input for an existing dialog box and its controls, processing events for dragging sizing handles and moving controls, before restoring the dialog’s normal mouse behavior. This could be a useful technique for online tutorials, where you take a user through a series of steps and don’t want them to diverge from the lesson. Here, you can examine the events coming from buttons and windows and, if acceptable, pass them through to the original event handler using wxEvent::Skip. Events not handled by your event handler will pass through to the window’s event table. DYNAMIC EVENT HANDLERS We have discussed event handling mostly in terms of static event tables because this is the most common way to handle events. However, you can also specify the mapping between events and their handlers at runtime. You might use this method because you want to use different event handlers at different times in your program, or because you are using a different language (such as Python) that can’t use static event tables, or simply because you prefer it. Event handlers allow greater granularity—you can turn individual event han- dlers on and off at runtime, whereas PushEventHandler and PopEventHandler deal with a whole event table. Plus, they allow sharing of event handler func- tions between different objects. There are two functions associated with dynamic event tables: wxEvtHandler::Connect and wxEvtHandler::Disconnect. Often, you won’t need to call wxEvtHandler::Disconnect because the disconnection will happen when the window object is destroyed. Let’s use our simple frame class with two event handlers as an example. // Declare our main frame class class MyFrame : public wxFrame { public: // Constructor MyFrame(const wxString& title); // Event handlers void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); private: };

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 31 Window Identifiers 31 Notice that this time, we do not use the DECLARE_EVENT_TABLE macro. To specify event handling dynamically, we add a couple of lines to our application class’s OnInit function: frame->Connect( wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnQuit) ); frame->Connect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnAbout) ); We pass to Connect the window identifier, the event identifier, and finally a pointer to the event handler function. Note that the event identifier (wxEVT_COMMAND_MENU_SELECTED) is different from the event table macro (EVT_MENU); the event table macro makes use of the event identifier internally. The wxCommandEventHandler macro around the function name is necessary to appease the compiler—casts are done by static event table macros automati- cally. In general, if you have a handler that takes an event called wxXYZEvent, then in your Connect call, you will wrap the function name in the macro wxXYZEventHandler. If we want to remove the mapping between event and handler function, we can use the wxEvtHandler::Disconnect function, as follows: frame->Disconnect( wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnQuit) ); frame->Disconnect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventFunction(MyFrame::OnAbout) ); WINDOW IDENTIFIERS Window identifiers are integers, and are used to uniquely determine window identity in the event system. In fact, identifiers do not need to be unique across your entire application, just unique within a particular context, such as a frame and its children. You may use the wxID_OK identifier, for example, on any number of dialogs as long as you don’t have several within the same dialog. If you pass wxID_ANY to a window constructor, an identifier will be gener- ated for you automatically by wxWidgets. This is useful when you don’t care about the exact identifier, either because you’re not going to process the events from the control being created at all, or because you process the events from all controls in one place. In this case, you should specify wxID_ANY in the event table or wxEvtHandler::Connect call as well. The generated identifiers are

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 32 32 Event Handling Chapter 3 always negative, so they will never conflict with the user-specified identifiers, which must always be positive. wxWidgets supplies the standard identifiers listed in Table 3-1. Use the standard identifiers wherever possible: some systems can use the information to provide standard graphics (such as the OK and Cancel buttons on GTK+) or default behavior (such as responding to the Escape key by emulating a wxID_CANCEL event). On Mac OS X, wxID_ABOUT, wxID_PREFERENCES and wxID_EXIT menu items are interpreted specially and transferred to the application menu. Some wxWidgets components, such as wxTextCtrl, know how to handle menu items or buttons with identifiers such as wxID_COPY, wxID_PASTE, wxID_UNDO. You can use wxID_HIGHEST to determine the number above which it is safe to define your own identifiers, or you can use identifiers below wxID_LOWEST. Table 3-1 Standard Window Identifiers Identifier Name Description wxID_ANY This may be passed to a window constructor as an identifier, and wxWidgets will generate an appro- priate identifier wxID_LOWEST The lowest standard identifier value (4999) wxID_HIGHEST The highest standard identifier value (5999) wxID_OPEN File open wxID_CLOSE Window close wxID_NEW New window, file or document wxID_SAVE File save wxID_SAVEAS File save as (prompts for a save dialog) wxID_REVERT Revert (revert to file on disk) wxID_EXIT Exit application wxID_UNDO Undo the last operation wxID_REDO Redo the last undo wxID_HELP General help (for example, for dialog Help buttons) wxID_PRINT Print wxID_PRINT_SETUP Print setup dialog wxID_PREVIEW Print preview wxID_ABOUT Show a dialog describing the application wxID_HELP_CONTENTS Show the help contents wxID_HELP_COMMANDS Show the application commands wxID_HELP_PROCEDURES Show the application procedures

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 33 Window Identifiers 33 Identifier Name Description wxID_HELP_CONTEXT Unused wxID_CUT Cut wxID_COPY Copy wxID_PASTE Paste wxID_CLEAR Clear wxID_FIND Find wxID_DUPLICATE Duplicate wxID_SELECTALL Select all wxID_DELETE Delete (cut without copying) wxID_REPLACE Replace wxID_REPLACE_ALL Replace all wxID_PROPERTIES Show properties for the selection wxID_VIEW_DETAILS View details in a list control wxID_VIEW_LARGEICONS View as large icons in a list control wxID_VIEW_SMALLICONS View as small icons in a list control wxID_VIEW_LIST View as a list in a list control wxID_VIEW_SORTDATE Sort by date wxID_VIEW_SORTNAME Sort by name wxID_VIEW_SORTSIZE Sort by size wxID_VIEW_SORTTYPE Sort by type wxID_FILE1 to wxID_FILE9 View recent file wxID_OK Confirms dialog selections wxID_CANCEL Vetoes dialog selections wxID_APPLY Applies selections to data wxID_YES Identifier for a Yes button wxID_NO Identifier for a No button wxID_STATIC Identifier for static text or bitmap control wxID_FORWARD Navigate forward wxID_BACKWARD Navigate backward wxID_DEFAULT Restore default settings wxID_MORE View more settings wxID_SETUP View a setup dialog wxID_RESET Reset settings (continues)

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 34 34 Event Handling Chapter 3 Table 3-1 Standard Window Identifiers (Continued) Identifier Name Description wxID_CONTEXT_HELP Show context-sensitive help wxID_YESTOALL Reply yes to all prompts wxID_NOTOALL Reply no to all prompts wxID_ABORT Abort the current operation wxID_RETRY Retry the operation wxID_IGNORE Ignore an error condition wxID_UP Navigate up wxID_DOWN Navigate down wxID_HOME Navigate home wxID_REFRESH Refresh wxID_STOP Stop the current operation wxID_INDEX Show an index wxID_BOLD Highlight in bold wxID_ITALIC Highlight in italic wxID_JUSTIFY_CENTER Center wxID_JUSTIFY_FILL Format wxID_JUSTIFY_RIGHT Right align wxID_JUSTIFY_LEFT Left align wxID_UNDERLINE Underline wxID_INDENT Indent wxID_UNINDENT Unindent wxID_ZOOM_100 Zoom to 100% wxID_ZOOM_FIT Fit to page wxID_ZOOM_IN Zoom in wxID_ZOOM_OUT Zoom out wxID_UNDELETE Undelete wxID_REVERT_TO_SAVED Revert to saved state DEFINING CUSTOM EVENTS If you want to define your own event class and macros, you need to follow these steps: 1. Derive your class from a suitable class, declaring dynamic type informa- tion and including a Clone function. You may or may not want to add data members and accessors. Derive from wxCommandEvent if you want the event

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 35 Defining Custom Events 35 to propagate up the window hierarchy, and from wxNotifyEvent if you also want handlers to be able to call Veto. 2. Define a typedef for the event handler function. 3. Define a table of event types for the individual events your event class supports. The event table is defined in your header with BEGIN_DECLARE_EVENT_TYPES()... END_DECLARE_EVENT_TYPES() and each type is declared with DECLARE_EVENT_TABLE(name, integer). Then in your imple- mentation file, write DEFINE_EVENT_TYPE(name). 4. Define an event table macro for each event type. Let's make this clearer with an example. Say we want to implement a new control class, wxFontSelectorCtrl, which displays a font preview; the user can click on the preview to pop up a font selector dialog to change the font. The application may want to intercept the font selection event, so we'll send a cus- tom command event from within our low-level mouse handling code. We will need to define a new event class wxFontSelectorCtrlEvent. An application will be able to route font change events to an event handler with the macro EVT_FONT_SELECTION_CHANGED(id, func), which uses the single event type wxEVT_COMMAND_FONT_SELECTION_CHANGED. Here's what we need in our new control header file, as well as the control declaration itself (not shown): /*! * Font selector event class */ class wxFontSelectorCtrlEvent : public wxNotifyEvent { public: wxFontSelectorCtrlEvent(wxEventType commandType = wxEVT_NULL, int id = 0): wxNotifyEvent(commandType, id) {} wxFontSelectorCtrlEvent(const wxFontSelectorCtrlEvent& event): wxNotifyEvent(event) {} virtual wxEvent *Clone() const { return new wxFontSelectorCtrlEvent(*this); } DECLARE_DYNAMIC_CLASS(wxFontSelectorCtrlEvent); }; typedef void (wxEvtHandler::*wxFontSelectorCtrlEventFunction) (wxFontSelectorCtrlEvent&); /*! * Font selector control events and macros for handling them */

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 36 36 Event Handling Chapter 3 BEGIN_DECLARE_EVENT_TYPES() DECLARE_EVENT_TYPE(wxEVT_COMMAND_FONT_SELECTION_CHANGED, 801) END_DECLARE_EVENT_TYPES() #define EVT_FONT_SELECTION_CHANGED(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_COMMAND_FONT_SELECTION_CHANGED, id, -1, (wxObjectEventFunction) (wxEventFunction) (wxFontSelectorCtrlEventFunction) & fn, (wxObject *) NULL ), In our implementation file, we write DEFINE_EVENT_TYPE(wxEVT_COMMAND_FONT_SELECTION_CHANGED) IMPLEMENT_DYNAMIC_CLASS(wxFontSelectorCtrlEvent, wxNotifyEvent) To send the custom event, the font selector control can call ProcessEvent when a selection is detected from within the mouse handling code: wxFontSelectorCtrlEvent event( wxEVT_COMMAND_FONT_SELECTION_CHANGED, GetId()); event.SetEventObject(this); GetEventHandler()->ProcessEvent(event); Now an application can write a font selection event handler, for example: BEGIN_EVENT_TABLE(MyDialog, wxDialog) EVT_FONT_SELECTION_CHANGED(ID_FONTSEL, MyDialog::OnChangeFont) END_EVENT_TABLE() void MyDialog::OnChangeFont(wxFontSelectorCtrlEvent& event) { // Take appropriate action when the font selection changed ... } The event identifier value (801) is not used in recent versions of wxWidgets and is only included for compatibility with wxWidgets 2.4. Let's take another look at the event macro definition: #define EVT_FONT_SELECTION_CHANGED(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_COMMAND_FONT_SELECTION_CHANGED, id, -1, (wxObjectEventFunction) (wxEventFunction) (wxFontSelectorCtrlEventFunction) & fn, (wxObject *) NULL ), The macro places information into an array that forms the event table, which is why the syntax looks rather strange. The five entries in the event table record are as follows: 1. The event type. One event class can handle several types, but in our example, we only define one event type, and therefore there is only one event table macro. This type must match the type of the event being processed for the event handler to be called.

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 37 Summary 37 2. The identifier value passed to the macro. The event handler function will only be called if the value in the table matches the value in the event being processed. 3. A second identifier value, used when specifying a range of values. -1 indi- cates that there is no second value. 4. The event handler function. The sequence of casts is needed for some compilers, and this is where the member function typedef is used. 5. User data, normally NULL. The full custom event example can be found in examples/chap03, and it includes a font selection control implementation and handy validator class that you can use in your own applications. You can also look at include/wx/event.h in your wxWidgets distribution for more inspiration. SUMMARY In this chapter, we’ve discussed how events are propagated through inheri- tance and window hierarchies, introduced pluggable and dynamic event han- dlers, talked about window identifiers, and described how you can write your own custom event classes and macros. For more on the mechanics of event handling, please refer to Appendix H, “How wxWidgets Processes Events.” Appendix I, “Event Classes and Macros,” lists commonly used event classes and macros. You can also look at a number of the wxWidgets samples for exam- ples of event usage, notably samples/event . Next, we'll discuss a range of important GUI components that you can start putting to use in your applications.

Smart_Ch03f.qxd 6/10/05 11:16 AM Page 38

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 39 C H A P T E R 4 Window Basics In this chapter, we’ll first look at the main elements of a window before describing the window classes that are most commonly used in applications. These elements will almost certainly be familiar from other programming you’ve done or from applications you’ve used. Despite the differences between systems, the available controls are surprisingly similar in function- ality, with wxWidgets handling nearly all of the differences. Remaining dif- ferences are usually handled using optional platform-specific window styles. ANATOMY OF A WINDOW Naturally, you know what a window is, but for a full understanding of how to use the wxWidgets API, it’s good to have a grasp of the window model that wxWidgets uses. This differs in small respects from the window model used on each individual platform. Figure 4-1 shows the basic elements of a window. 39

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 40 40 Window Basics Chapter 4 Window border and decorations Client area (0, 0) Optional scrollbars Child window Figure 4-1 The elements of a window The Concept of a Window A window is any rectangular area with a common set of properties: it can be resized, it can paint itself, it can be shown and hidden, and so on. It may con- tain other windows (such as a frame with menu bar, toolbar, and status bar), or no child windows (such as a static text control). Normally, a window that you see on the screen in a wxWidgets application has a corresponding object of the wxWindow class or derived class, but this is not always the case: for example, the drop-down list in a native wxComboBox is not usually modeled with a sepa- rate wxWindow. Client and Non-Client Areas When we refer to the size of a window, we normally include the outer dimen- sions, including decorations such as the border and title bar. When we refer to the size of the client area of a window, we mean the area inside the window that can be drawn upon or into which child windows may be placed. A frame’s client area excludes any space taken by the menu bar, status bar, and toolbar.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 41 Anatomy of a Window 41 Scrollbars Most windows are capable of showing scrollbars, managed by the window rather than added explicitly by the application. The client area is then reduced by the space used by the scrollbars. For optimization, only windows that have the wxHSCROLL and wxVSCROLL style are guaranteed to own their own scrollbars. More information on scrolling can be found later in this chapter when we discuss wxScrolledWindow. Caret and Cursor A window can have a wxCaret (for displaying the current text position) and a wxCursor (for displaying the current mouse position). When the mouse enters a window, wxWidgets automatically displays the cursor that has been set for the window. When a window receives the focus, the caret (if any) will be shown at its current position, or at the mouse position if the focus was a result of a mouse button click. Top-Level Windows Windows are broadly divided into top-level windows (wxFrame, wxDialog, wxPopup) and other windows. Only top-level windows may be created with a NULL parent, and only top-level windows have delayed destruction (they are not deleted until idle time, when all other events have been processed). Except for pop-up windows, top-level windows can have decorations such as title bars and close buttons and can normally be dragged around the screen and resized, if the application allows it. Coordinate System The coordinate system always has (0, 0) at the top-left corner, and window dimensions are in pixels. The origin and scale can be changed for a device con- text that is used to paint on the window. For more on device contexts, see Chapter 5, “Drawing and Printing.” Painting When a window needs to be painted, it receives two events, wxEVT_ERASE_ BACKGROUND to paint the background, and wxEVT_PAINT to paint the foreground. Ready-to-use classes such as wxButton paint themselves, but to create your own special window class, you will need to handle these events. You can opti- mize painting by getting the update region (the part that needs refreshing) from the window.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 42 42 Window Basics Chapter 4 Color and Font Every window has background and foreground color settings that can be used to determine the background color and (less commonly) foreground color. The default background erase handler uses the window’s background color, or if none has been set, the appropriate color or texture for the current color scheme or theme. A window also has a font setting, which may or may not be used depending on the kind of window. Window Variant On Mac OS X, a window has the concept of window variant, whereby it can be shown in a selection of sizes: wxWINDOW_VARIANT_NORMAL (the default), wxWINDOW_ VARIANT_SMALL, wxWINDOW_VARIANT_MINI, or wxWINDOW_VARIANT_LARGE. Changing to a smaller variant is useful when you have a lot of information to convey and limited space, but it should be used in moderation. Some applications use the small variant throughout. Sizing When a window is resized, either by the application or by the user, it receives a wxEVT_SIZE event. If the window has children, they will need to be positioned and sized appropriately, and the recommended way is to use sizers, as dis- cussed in Chapter 7, “Window Layout Using Sizers.” Most stock windows have a notion of a default size and position if you pass them wxDefaultSize or wxDefaultPosition (or -1 as an individual size or position value). To this end, each control implements DoGetBestSize, which returns a reasonable default size based on the control content, current font, and other factors. Input Any window can receive input from the mouse at any time, unless another window has temporarily captured the mouse or the window is disabled. Only the window with the current focus can receive keyboard input. An application can set the window focus itself; wxWidgets also sets the focus to a window when the user clicks on it. The wxEVT_SET_FOCUS event is sent to a window that is receiving the focus; wxEVT_KILL_FOCUS is sent when the focus is moving to a different window. See Chapter 6, “Handling Input,” for more details on han- dling input. Idle Time Processing and UI Updates All windows are (unless otherwise specified) recipients of idle events (wxEVT_IDLE), which are sent when all other events have been processed, speci- fied with the EVT_IDLE(func) macro. For more information, see “Idle Time Processing” in Chapter 17, “Writing Multithreaded Applications.”

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 43 Anatomy of a Window 43 A special kind of idle time processing is user interface updating, in which all windows can specify a function that updates the window’s state. This func- tion is called periodically in idle time. In the descriptions of events that follow, EVT_UPDATE_UI(id, func) is usually omitted for brevity. User interface updating is covered in Chapter 9, “Creating Custom Dialogs.” Window Creation and Deletion In general, windows are created on the heap with new, but see Chapter 15, “Memory Management, Debugging, and Error Checking,” for details and exceptions. Most window classes allow creation either in one or two steps. wxButton has typical constructor signatures: wxButton(); wxButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, const wxString& name = wxT(“button”)); The following example of one-step construction takes advantage of all the con- structor’s default values: wxButton* button = new wxButton(parent, wxID_OK); Unless the window is a frame or dialog, you must pass a non-NULL parent win- dow to the constructor. This will automatically add the child window to the parent, and when the parent is destroyed, the children will also be destroyed. As we’ve seen previously, you pass a standard or user-defined identifier to the window for the purposes of uniquely identifying it. You can also pass wxID_ANY, and wxWidgets will generate a suitable identifier (a negative value, to differ- entiate it from user-defined or standard identifiers). You can pass a position and size to the window, a validator, if appropriate (see Chapter 9), a style (see the following), and a string name. You can pass an arbitrary string to the name parameter, or ignore it; it is rarely used now but was introduced to allow win- dow customization under Xt and Motif, which require controls to be identified by name. With two-step construction, you use the default constructor and then call Create, which has the same signature as the full constructor. For example, to create a button in two steps: wxButton* button = new wxButton; button->Create(parent, wxID_OK);

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 44 44 Window Basics Chapter 4 Only when you call Create is the underlying window created, and a wxEVT_ CREATE event sent to the window in case it needs to do further processing at this point. Why would you want to use two-step construction? One reason is that you may want to delay full creation of the window until it’s really required. Another is to set some properties of the window before Create is called, espe- cially if those properties are used within the Create call. For example, you may want to set the wxWS_EX_VALIDATE_RECURSIVELY extra style (which must be set with SetExtraStyle). In the case of a dialog, this style is used from within its Create function when initial validation takes place, so if you need it, it’s impor- tant to set it before the dialog is created. When you create a wxWindow, or any non-top-level derived window class, it is always in a visible state—so if the parent is visible at the time of creation, the window will also be visible. You can then use Show(false) to hide it if required. This is different from wxDialog and wxFrame, which are initially cre- ated in a hidden state to enable the application to lay out the controls without initial flicker, before showing with Show or (for modal dialogs) ShowModal. Windows are deleted by calling Destroy (for top-level windows) or delete (for child windows), and the wxEVT_DESTROY event is sent just before the actual window is destroyed. In fact, child windows are deleted automatically, so it is rare to delete them explicitly. Window Styles A window has a style and an extra style. Window styles are a concise way to specify alternative behavior and appearances for windows when they are cre- ated. The symbols are defined in such as way that they can be combined in a “bit-list” using the C++ bitwise-or operator. For example: wxCAPTION | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxTHICK_FRAME The wxWindow class has a basic set of styles, such as border styles, and each derived class may add further styles. The “extra” style accommodates values that cannot fit into the style value. A QUICK GUIDE TO THE WINDOW CLASSES The rest of this chapter provides you with enough detailed information about the most commonly used window classes for you to apply them to your own applications. However, if you are reading this book for the first time, you may want to skip ahead to Chapter 5 and browse the window descriptions later.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 45 A Quick Guide to the Window Classes 45 Here’s a summary of the classes we cover, to help you navigate this chap- ter. For other window classes, see Chapter 12, “Advanced Window Classes,” and Appendix E, “Third-Party Tools for wxWidgets.” Base Window Classes These base classes implement functionality for derived concrete classes.

wxWindow. The base class for all windows.

wxControl. The base class for controls, such as wxButton.

wxControlWithItems. The base class for multi-item controls. Top-Level Windows Top-level windows usually exist independently on the desktop.

wxFrame. A resizable window containing other windows.

wxMDIParentFrame. A frame that manages other frames.

wxMDIChildFrame. A frame managed by a parent frame.

wxDialog. A resizable window for presenting choices.

wxPopupWindow. A transient window with minimal decoration. Container Windows Container windows manage child windows.

wxPanel. Awindow for laying out controls.

wxNotebook. Awindow for switching pages using tabs.

wxScrolledWindow. A window that scrolls children and graphics.

wxSplitterWindow. A window that manages two child windows. Non-Static Controls These controls can be edited by the user.

wxButton. A push-button control with a text label.

wxBitmapButton. A push-button control with a bitmap label.

wxChoice. A drop-down list of choices.

wxComboBox. An editable field with a list of choices.

wxCheckBox. A control representing a check box, on or off.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 46 46 Window Basics Chapter 4

wxListBox. A list of selectable string items.

wxRadioBox. A grid of radio buttons.

wxRadioButton. A control resembling a radio button, on or off.

wxScrollBar. A scrollbar control.

wxSpinButton. Arrows for incrementing/decrementing values.

wxSpinCtrl. A text field and spin button for editing integers.

wxSlider. A control for changing a value within a given range.

wxTextCtrl. A single- or multiple-line text entry field.

wxToggleButton. A button that can be toggled on and off. Static Controls These controls present information and cannot be edited by the user.

wxGauge. Acontrol showing a quantity.

wxStaticText. A control that shows a text label.

wxStaticBitmap. A control that shows a bitmap label.

wxStaticLine. A control displaying a line.

wxStaticBox. A control displaying a box around other controls. Menus Menus are transient windows containing lists of commands.

wxMenu. A menu that can be used as a popup or in a menu bar. Control Bars Control bars present concise access to commands and information, usually within a wxFrame.

wxMenuBar. A menu bar that presents commands in a wxFrame.

wxToolBar. A toolbar that provides quick access to commands.

wxStatusBar. A status bar that shows information in multiple fields. BASE WINDOW CLASSES It’s worth mentioning base classes that you may or may not be able use directly but that implement a lot of functionality for derived classes. Use the API reference for these (and other) base classes as well as the reference for the derived classes to get a full understanding of what’s available.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 47 Base Window Classes 47 wxWindow wxWindow is both an important base class and a concrete window class that you can instantiate. However, it’s more likely that you will derive classes from it (or use pre-existing derived classes) than use it on its own. As we’ve seen, a wxWindow may be created either in one step, using a non- default constructor, or two steps, using the default constructor followed by Create. For one-step construction, you use this constructor: wxWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxT(“panel”)); For example: wxWindow* win = new wxWindow(parent, wxID_ANY, wxPoint(100, 100), wxSize(200, 200)); wxWindow Styles Each window class may add to the basic styles defined for wxWindow, listed in Table 4-1. Not all native controls support all border styles, and if no border is specified, a default style appropriate to that class will be used. For example, on Windows, most wxControl-derived classes use wxSUNKEN_BORDER by default, which will be interpreted as the border style for the current theme. An appli- cation may suppress the default border by using a style such as wxNO_BORDER. Table 4-1 Basic Window Styles wxSIMPLE_BORDER Displays a thin border around the window. wxDOUBLE_BORDER Displays a double border. wxSUNKEN_BORDER Displays a sunken border, or control border consis- tent with the current theme. wxRAISED_BORDER Displays a raised border. wxSTATIC_BORDER Displays a border suitable for a static control. Windows only. wxNO_BORDER Displays no border. This overrides any attempt wxWidgets makes to add a suitable border. wxTRANSPARENT_WINDOW Specifies a transparent window (one that doesn’t receive paint events). Windows only. (continues)

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 48 48 Window Basics Chapter 4 Table 4-1 Basic Window Styles (Continued) wxTAB_TRAVERSAL Use this to enable tab traversal for non-dialog windows. wxWANTS_CHARS Use this to indicate that the window wants to get all char/key events—even for keys like Tab or Enter, which are used for dialog navigation and which wouldn’t be generated without this style. wxFULL_REPAINT_ON_RESIZE By default on Windows, wxWidgets won’t repaint the entire client area during a resize. This style ensures that the whole client area will be invali- dated during a resize. wxVSCROLL Enables a vertical scrollbar. wxHSCROLL Enables a horizontal scrollbar. wxALWAYS_SHOW_SB If a window has scrollbars, disables them instead of hiding them when they are not needed (when the size of the window is big enough to not require the scrollbars to navigate it). This style is currently only implemented for Windows and wxUniversal. wxCLIP_CHILDREN On Windows only, used to eliminate flicker caused by a window erasing the background of its children. Table 4-2 lists extra styles that cannot be accommodated in the regular style and that are set using wxWindow::SetExtraStyle. Table 4-2 Basic Extra Window Styles wxWS_EX_VALIDATE_RECURSIVELY By default, Validate, TransferDataToWindow, and TransferDataFromWindow only work on direct children of the window. Set this style to make them recursively descend into all subwindows. wxWS_EX_BLOCK_EVENTS wxCommandEvents and the objects of derived classes are forwarded to the parent window and so on recursively by default. Using this style for the given window enables you to block this prop- agation at this window to prevent the events from being propagated further upwards. Dialogs have this style on by default, but note that if SetExtraStyle is called by the application, it may be reset. wxWS_EX_TRANSIENT Don’t use this window as an implicit parent for other windows. This must be used with tran- sient windows; otherwise, there is the risk of creating a dialog or frame with this window as a parent, which would lead to a crash if the par- ent were destroyed before the child.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 49 Base Window Classes 49 wxWS_EX_PROCESS_IDLE This window should always process idle events, even if the mode set by wxIdleEvent::SetMode is wxIDLE_PROCESS_SPECIFIED. wxWS_EX_PROCESS_UI_UPDATES This window should always process UI update events, even if the mode set by wxUpdateUIEvent:: SetMode is wxUPDATE_UI_PROCESS_SPECIFIED. See Chapter 9 for more information on UI update events. wxWindow Events wxWindow and all its derived classes generate the events listed in Table 4-3. Events generated by mouse, keyboard, or joystick input are covered in Chapter 6. Table 4-3 wxWindow Events EVT_WINDOW_CREATE(func) Processes a wxEVT_CREATE propagating event, generated when the underlying window has just been created. Handlers take a wxWindowCreateEvent object. EVT_WINDOW_DESTROY(func) Processes a wxEVT_DELETE propagating event, generated when the window is about to be destroyed. Handlers take a wxWindowDestroyEvent object. EVT_PAINT(func) Processes a wxEVT_PAINT event, generated when the window needs updating. Handlers take a wxPaintEvent object. EVT_ERASE_BACKGROUND(func) Processes a wxEVT_ERASE_BACKGROUND event, generated when the window background needs updating. Handlers take a wxEraseEvent object. EVT_MOVE(func) Processes a wxEVT_MOVE event, generated when the window moves. Handlers take a wxMoveEvent object. EVT_SIZE(func) Processes a wxEVT_SIZE event, generated when the window is resized. Handlers take a wxSizeEvent object. EVT_SET_FOCUS(func) Processes wxEVT_SET_FOCUS and wxEVT_KILL_ EVT_KILL_FOCUS(func) FOCUS events, generated when the keyboard focus is gained or lost for this window. Handlers take a wxFocusEvent object. EVT_SYS_COLOUR_CHANGED(func) Processes a wxEVT_SYS_COLOUR_CHANGED event, generated when the user changed a color in the control panel (Windows only). Handlers take a wxSysColourChangedEvent object. EVT_IDLE(func) Processes a wxEVT_IDLE event, generated in idle time. Handlers take a wxIdleEvent object. EVT_UPDATE_UI(func) Processes a wxEVT_UPDATE_UI event, generated in idle time to give the window a chance to update itself.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 50 50 Window Basics Chapter 4 wxWindow Member Functions Because wxWindow is the base class for all other window classes, it has the largest number of member functions. We can’t describe them all here in detail, so instead we present a summary of some of the most important functions. Browsing them should give you a good idea of the general capabilities of win- dows, and you can refer to the reference manual for details of parameters and usage. CaptureMouse captures all mouse input, and ReleaseMouse releases the cap- ture. This is useful in a drawing program, for example, so that moving to the edge of the canvas scrolls it rather than causing another window to be acti- vated. Use the static function GetCapture to retrieve the window with the cur- rent capture (if it’s within the current application), and HasCapture to determine if this window is capturing input. Centre (Center), CentreOnParent (CenterOnParent), and CentreOnScreen (CenterOnScreen) center the window relative to the screen or the parent window. ClearBackground clears the window by filling it with the current back- ground color. ClientToScreen and ScreenToClient convert between coordinates relative to the top-left corner of this window, and coordinates relative to the top-left corner of the screen. Close generates a wxCloseEvent whose handler usually tries to close the window. Although the default close event handler will destroy the window, calling Close may not actually close the window if a close event handler has been provided that doesn’t destroy the window. ConvertDialogToPixels and ConvertPixelsToDialog convert between dialog and pixel units, which is useful when basing size or position on font size in order to give more portable results. Destroy destroys the window safely. Use this function instead of the delete operator because different window classes can be destroyed differently. Frames and dialogs are not destroyed immediately when this function is called but are added to a list of windows to be deleted on idle time, when all pending events have been processed. This prevents problems with events being sent to non-existent windows. Enable enables or disables the window and its children for input. Some controls display themselves in a different color when disabled. Disable can be used instead of passing false to Enable. FindFocus is a static function that can be used to find the window that currently has the keyboard focus. FindWindow can be used with an identifier or a name to find a window in this window’s hierarchy. It can return a descendant or the parent window itself. If you know the type of the window, you can use wxDynamicCast to safely cast to the correct type, returning either a pointer to that type or NULL:

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 51 Base Window Classes 51 MyWindow* window = wxDynamicCast(FindWindow(ID_MYWINDOW), MyWindow); Fit resizes the window to fit its children. This should be used with sizer-based layout. FitInside is similar, but it uses the virtual size (useful when the win- dow has scrollbars and contains further windows). Freeze and Thaw are hints to wxWidgets that display updates between these two function calls should be optimized. For example, you could use this when adding a lot of lines to a text control separately. Implemented for wxTextCtrl on GTK+, and all windows on Windows and Mac OS X. GetAcceleratorTable and SetAcceleratorTable get and set the accelerator table for this window. GetBackgroundColour and SetBackgroundColour are accessors for the win- dow background color, used by the default wxEVT_ERASE_BACKGROUND event. After setting the color, you will need to call Refresh or ClearBackground to show the window with the new color. SetOwnBackgroundColour is the same as SetBackgroundColour but the color is not inherited by the window’s children. GetBackgroundStyle and SetBackgroundStyle are accessors for the window background style. By default, the background style is wxBG_STYLE_SYSTEM, which tells wxWidgets to draw the window background with whatever style is appro- priate, whether a texture drawn according to the current theme (for example, wxDialog), or a solid color (for example, wxListBox). If you set the style to wxBG_STYLE_COLOUR, wxWidgets will use a solid color for this window. If you set it to wxBG_STYLE_CUSTOM, wxWidgets will suppress the default background drawing, and the application can paint it from its erase or paint event handler. If you want to draw your own textured background, then setting the style to wxBG_STYLE_CUSTOM is recommended for flicker-free refreshes. GetBestSize returns the minimal size for the window in pixels (as imple- mented for each window by DoGetBestSize). This is a hint to the sizer system not to resize the window so small that it cannot be viewed or used properly. For example, for a static control, it will be the minimum size such that the con- trol label is not truncated. For windows containing subwindows (typically wxPanel), the size returned by this function will be the same as the size the window would have had after calling Fit. GetCaret and SetCaret are accessors for the wxCaret object associated with the window. GetClientSize and SetClientSize are accessors for the size of the client area in pixels. The client area is the region within any borders and window decorations, inside which you can draw or place child windows. GetCursor and SetCursor are accessors for the cursor associated with the window. GetDefaultItem returns a pointer to the child button that is the default for this window, or NULL. The default button is the one activated by pressing the Enter key. Use wxButton::SetDefault to set the default button.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 52 52 Window Basics Chapter 4 GetDropTarget and SetDropTarget are accessors for the wxDropTarget object which handles dropped data objects for this window. Drag and drop is covered in Chapter 11, “Clipboard and Drag and Drop.” GetEventHandler and SetEventHandler are accessors for the first event han- dler for the window. By default, the event handler is the window itself, but you can interpose a different event handler. You can also use PushEventHandler and PopEventHandler to set up a handler chain, with different handlers dealing with different events. wxWidgets will search along the chain for handlers that match incoming events (see Chapter 3, “Event Handling”). GetExtraStyle and SetExtraStyle are accessors for the “extra” style bits. Extra styles normally start with wxWS_EX_. GetFont and SetFont are accessors for the font associated with this win- dow. SetOwnFont is the same as SetFont, except that the font is not inherited by the window’s children. GetForegroundColour and SetForegroundColour are accessors for the win- dow foreground color, whose meaning differs according to the type of window. SetOwnForegroundColour is the same as SetOwnForegroundColour but the color is not inherited by the window’s children. GetHelpText and SetHelpText are accessors for the context-sensitive help string associated with the window. The text is actually stored by the current wxHelpProvider implementation, and not in the window. GetId and SetId are accessors for the window identifier. GetLabel returns the label associated with the window. The interpreta- tion of this value depends on the particular window class. GetName and SetName are accessors for the window name, which does not have to be unique. The window name has no special significance to wxWidgets, except under Motif where it is the resource name for the window. GetParent returns a pointer to the parent window. GetPosition returns the position of the top-left corner of the window in pixels, relative to its parent. GetRect returns a wxRect object (see Chapter 13, “Data Structure Classes”) representing the size and position of the window in pixels. GetSize and SetSize retrieve and set the outer window dimensions in pixels. GetSizer and SetSizer are accessors for the top-level sizer used for arranging child windows on this window. GetTextExtent gets the dimensions of the string in pixels, as it would be drawn on the window with the currently selected font. GetToolTip and SetToolTip are accessors for the window tooltip object. GetUpdateRegion returns the portion of the window that currently needs refreshing (since the last paint event was handled). GetValidator and SetValidator are accessors for the optional wxValidator object associated with the window, to handle transfer and validation of data. See Chapter 9 for more about validators.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 53 Base Window Classes 53 GetVirtualSize returns the virtual size of the window in pixels, as deter- mined by setting the scrollbar dimensions. GetWindowStyle and SetWindowStyle are accessors for the window style. InitDialog sends a wxEVT_INIT_DIALOG event to initiate transfer of data to the window. IsEnabled indicates whether the window is enabled or disabled. IsExposed indicates whether a point or a part of the rectangle is in the update region. IsShown indicates whether the window is shown. IsTopLevel indicates whether the window is top-level (a wxFrame or a wxDialog). Layout invokes the sizer-based layout system if there is a sizer associated with the window. See Chapter 7 for more about sizers. Lower sends a window to the bottom of the window hierarchy, while Raise raises the window above all other windows. This works for top-level windows and child windows. MakeModal disables all the other top-level windows in the application so that the user can only interact with this window. Move moves the window to a new position. MoveAfterInTabOrder moves the tab order of this window to a position just after the window passed as argument, and MoveBeforeInTabOrder is the same but moves the tab order in front of the window argument. PushEventHandler pushes an event handler onto the event stack for this window, and PopEventHandler removes and returns the top-most event handler on the event handler stack. RemoveEventHandler finds a handler in the event handler stack and removes it. PopupMenu shows a menu at the specified position. Refresh and RefreshRect causes a paint event (and optionally an erase event) to be sent to the window. SetFocus gives the window the current keyboard focus. SetScrollbar sets the properties for a built-in scrollbar. SetSizeHints allows specification of the minimum and maximum window sizes, and window size increments, in pixels. This is applicable to top-level windows only. Show shows or hides the window; Hide is equivalent to passing false to Show. TransferDataFromWindow and TransferDataToWindow transfer data from and to the window. By default, these call validator functions, but they can be over- ridden by the application. Update immediately repaints the invalidated area of the window. UpdateWindowUI sends wxUpdateUIEvents to the window to give the window (and application) a chance to update window elements, such as toolbars as menu items. Validate validates the current values of the child controls using their validators.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 54 54 Window Basics Chapter 4 wxControl wxControl derives from wxWindow and is the abstract base class for controls: windows that display items of data and usually respond to mouse clicks or keyboard input by sending command events. wxControlWithItems wxControlWithItems is an abstract base class for some wxWidgets controls that contain several items, such as wxListBox, wxCheckListBox, wxChoice, and wxComboBox. The use of this intermediate class ensures that a consistent API is used across several controls that have similar functionality. The items in a wxControlWithItems have string labels and, optionally, client data associated with them. Client data comes in two different flavors: either simple untyped (void*) pointers, which are stored by the control but not used in any way by it, or typed pointers (wxClientData*). These typed pointers are owned by the control, meaning that the typed client data will be deleted when an item is deleted or when the entire control is cleared (for example, when it is destroyed). All items in the same control must have client data of the same type: either all typed or all untyped (if it has any at all). The client data type is determined by the first call to Append, SetClientData, or SetClientObject. To work with typed client data, you should derive a class from wxClientData containing the data you want to store, and pass it to Append or SetClientObject. wxControlWithItems Member Functions Append adds a single item or an array of items to the control. When adding a single item, you can also associate typed or untyped data with the item by passing a second argument. For example: wxArrayString strArr; strArr.Add(wxT(“First string”)); strArr.Add(wxT(“Second string”)); controlA->Append(strArr); controlA->Append(wxT(“Third string”)); controlB->Append(wxT(“First string”), (void *) myPtr); controlC->Append(wxT(“First string”), new MyTypedData(1)); Clear clears all items from the controls (deleting all typed client data). Delete removes an item (and its typed client data) using a zero-based position. FindString returns the zero-based index to the item matching a string, or wxNOT_FOUND if no item was found. GetClientData and GetClientObject return a pointer to the client data associated with the specified item (if any). SetClientData and SetClientObject can be used to set the data for a specified item.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 55 Top-Level Windows 55 GetCount returns the number of items in the control. GetSelection returns the index of the selected item, or wxNOT_FOUND if none was selected. SetSelection sets the index of the selected item. GetString returns the item label at the given position; SetString sets an item’s label. GetStringSelection returns the label of the selected item or an empty string if no item is selected; SetStringSelection sets the selection. To avoid an assertion, first check that the string is available in the control using FindString. Insert inserts an item (with or without client data) at a specified position in the control. IsEmpty returns true if the control has no items, and false otherwise. TOP-LEVEL WINDOWS Top-level windows are placed directly on the desktop and are not contained within other windows. They can be moved around the screen, and resized if the application permits it. There are three basic kinds of top-level window: wxFrame and wxDialog, both derived from an abstract base class called wxTopLevelWindow, and wxPopupWindow, which has less functionality and is derived directly from wxWindow. A dialog can be either modal or modeless, whereas a frame is almost always modeless. Modal means that flow through the application effectively halts until the user dismisses the dialog. This is very handy for getting a response before continuing, but it’s always good to see whether an alternative user interface can be used (for example, a font control on the toolbar rather than in a properties window) to keep the interaction more fluid. Top-level windows normally have a title bar and can have decorations for closing, minimizing, or restoring the window. A frame often has a menu bar, toolbar, and status bar, but a dialog generally does not. On Mac OS X, a frame’s menu bar is not shown at the top of the frame, but at the top of the screen. Don’t confuse this usage of “top-level window” with the window returned by wxApp::GetTopWindow, by which wxWidgets or your application can get hold of the “main window,” most often the first frame or dialog you create. If needed, you can access all current top-level windows using the global variable wxTopLevelWindows, which is a wxWindowList. wxFrame wxFrame is a popular choice for the main application window. Figure 4-2 shows the elements that compose a frame. A frame may optionally have a title bar (with decorations such as a button for closing the window), a wxMenuBar, a wxToolBar, and a wxStatusBar. The area that is left for child windows is called

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 56 56 Window Basics Chapter 4 the client area, and it is up to the application to arrange the children’s size and position if there is more than one child. You should use sizers for this pur- pose, described in Chapter 7, or perhaps a splitter window (covered later in this chapter) if you need a couple of child windows. Title bar Window border and decorations Demo Application File Edit Help Menu bar Toolbar Client area Status bar Ready Figure 4-2 The elements of a frame Because some platforms do not allow direct painting on a frame, and to support keyboard navigation between child controls, you should create an intermediate wxPanel (see later). A frame can have more than one toolbar, but it will only do sizing and positioning for one toolbar. You must implement explicit layout for multiple toolbars. It is highly likely that you will derive a new class from wxFrame rather than using it directly, so that you can handle events such as wxEVT_CLOSE (see the following) and command events. Often you will create child windows and a menu bar within your frame constructor. You can assign an icon to the frame to be shown by the system, such as on the taskbar or in the file manager. On Windows, it’s best to use a compound icon at 16×16 and 32×32 pixels and perhaps several color depths. On Linux, the same icons as for Windows usually suffices. On Mac OS X, you’ll need a variety of icons in different colors and depths. For more details on icons and icon bundles, see Chapter 10, “Programming with Images.”

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 57 Top-Level Windows 57 wxFrame has the following constructor in addition to the default constructor: wxFrame(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_FRAME_STYLE, const wxString& name = wxT(“frame”)); For example: wxFrame* frame = new wxFrame(NULL, ID_MYFRAME, wxT(“Hello wxWidgets”), wxDefaultPosition, wxSize(500, 300)); frame->Show(true); Note that the frame won’t be shown until Show(true) is called, to give the application the chance to do child window layout invisibly. You don’t have to pass a parent window to the new frame, but if you do, the new window will be displayed on top of the parent window if the wxFRAME_FLOAT_ON_PARENT style is specified. To destroy a frame, do not use delete, but instead use Destroy or Close to tell wxWidgets to clean up the frame in idle time when all its events have been processed. When you call Close, you send a wxEVT_CLOSE event to the window. The default handler for this event calls Destroy. When a frame is deleted, its children are automatically deleted, as long as they are not themselves top- level windows. When the last top-level window has been destroyed, the application exits (although you can change this behavior by calling wxApp::SetExitOnFrame Delete). Your main window’s wxEVT_CLOSE handler may need to destroy other top-level windows that might be open—for example, a Find dialog—otherwise the application will not close when the main window is closed. A frame does not have the equivalent of wxDialog::ShowModal to enter an event loop and disable other top-level windows. However, you can get similar behavior by creating a wxWindowDisabler object when you require other top- level windows to be disabled. Or, you can use a wxModalEventLoop object, pass- ing the frame pointer, calling Run to start a local event loop and calling Exit (usually from an event handler) to quit the loop. Figure 4-3 shows the frame of a consumer application running on Windows. The frame has a title bar at the top, a menu bar, a colorful toolbar, a splitter window in the frame’s client area, and a status bar at the bottom, giving help on toolbar buttons and menu items as the user drags the mouse over them.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 58 58 Window Basics Chapter 4 Figure 4-3 A typical wxFrame wxFrame Styles wxFrame can have the window styles listed in Table 4-4, in addition to those described for wxWindow. Table 4-4 wxFrame Styles wxDEFAULT_FRAME_STYLE Defined as wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX. wxICONIZE Displays the frame iconized (minimized). Windows only. wxCAPTION Puts a caption on the frame. wxMINIMIZE Identical to wxICONIZE. Windows only. wxMINIMIZE_BOX Displays a minimize box on the frame. wxMAXIMIZE Displays the frame maximized. Windows only. wxMAXIMIZE_BOX Displays a maximize box on the frame. wxCLOSE_BOX Displays a close box on the frame. wxSTAY_ON_TOP The frame stays on top of all other windows. Windows only. wxSYSTEM_MENU Displays a system menu. wxRESIZE_BORDER Displays a resizable border around the window. wxFRAME_TOOL_WINDOW Causes a frame with a small title bar to be created; the frame does not appear in the taskbar under Windows.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 59 Top-Level Windows 59 wxFRAME_NO_TASKBAR Creates an otherwise normal frame, but it does not appear in the taskbar under Windows or Linux (note that on Windows it will minimize to the desk- top window, which may seem strange to users and thus it might be better to use this style without the wxMINIMIZE_BOX style). Has no effect under other platforms. wxFRAME_FLOAT_ON_PARENT The frame will always be on top of its parent. A frame created with this style must have a non-NULL parent. wxFRAME_SHAPED Windows with this style are allowed to have their shape changed with the SetShape method. Table 4-5 shows the extra styles that couldn’t be accommodated in the regular style and that are set using wxWindow::SetExtraStyle. Table 4-5 wxFrame Extra Styles wxFRAME_EX_CONTEXTHELP Under Windows, puts a query button on the caption. When pressed, Windows will go into a context-sensitive help mode and wxWidgets will send a wxEVT_HELP event if the user clicked on an application window. You cannot use this style together with wxMAXIMIZE_BOX or wxMINIMIZE_BOX. wxFRAME_EX_METAL Under Mac OS X, this style will give the frame a metallic appearance. This should be used sparingly and is intended for consumer applications that emu- late a physical device such as an audio player. wxFrame Events wxFrame and its derived classes generate the events listed in Table 4-6, in addi- tion to those mentioned for wxWindow. Table 4-6 wxFrame Events EVT_ACTIVATE(func) Processes a wxEVT_ACTIVATE event, generated when the frame is about to be activated or deactivated. Handlers take a wxActivateEvent object. EVT_CLOSE(func) Processes a wxEVT_CLOSE event, generated when the program or windowing system is trying to close the frame. Handlers take a wxCloseEvent object and can veto the close by calling Veto on the object. (continues)

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 60 60 Window Basics Chapter 4 Table 4-6 wxFrame Events (Continued) EVT_ICONIZE(func) Processes a wxEVT_ICONIZE event, generated when the frame is being iconized (minimized) or restored. Handlers take a wxIconizeEvent object. Call IsIconized to check if this is an iconize or restore event. EVT_MAXIMIZE(func) Processes a wxEVT_MAXIMIZE event, generated when the frame is being maximized or restored. Handlers take a wxMaximizeEvent object. Call IsMaximized to check if this is a maximize or restore event. wxFrame Member Functions These are the major wxFrame functions. Because wxFrame derives from wxTopLevelWindow and wxWindow, please also refer also to the member functions for these classes. CreateStatusBar creates one or more status fields at the bottom of the frame. Use SetStatusText to set the text for a particular field, and SetStatusWidths to customize the widths of the fields (see also wxStatusBar later in this chapter). For example: frame->CreateStatusBar(2, wxST_SIZEGRIP); int widths[3] = { 100, 100, -1 }; frame->SetStatusWidths(3, widths); frame->SetStatusText(wxT(“Ready”), 0); CreateToolBar creates a toolbar under the menu bar and associates it with the frame. Alternatively, you can create a toolbar by using the wxToolBar con- structor, but to allow it to be managed by the frame, you need to call wxFrame::SetToolBar. GetMenuBar and SetMenuBar are accessors for the frame’s wxMenuBar. There is only one menu bar per frame. You can replace an existing one with a new menu bar, and the old menu bar will be deleted. GetTitle and SetTitle are accessors for the title that appears on the frame’s title bar. Iconize iconizes or restores the frame. You can test the frame’s iconic state by calling IsIconized. Maximize resizes the frame to fit the desktop, or restores it to the previous state if it is currently maximized. Call IsMaximized to test whether the frame is maximized. SetIcon sets the icon displayed when the frame is minimized. It’s also used for other purposes by the window manager, such as displaying the pro- gram’s icon in Windows Explorer or on the taskbar. You can also use SetIcons to set an icon bundle (multiple icons with a variety of resolutions and depths).

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 61 Top-Level Windows 61 SetShape sets a region specifying the shape of the frame on some plat- forms (currently Windows, Mac OS X, and GTK+, and X11 if the appropriate X11 extension is available). ShowFullScreen hides as many decorations as possible and shows the client window at the maximum size of the display. It can also restore the frame to its former size and state. Use IsFullScreen to determine whether the frame is currently being shown full-screen. Non-Rectangular Frames If you want to write a more unusual-looking consumer application, such as a clock or media player, you can set a non-rectangular region for the frame, and only that region will be displayed. In Figure 4-4, the frame has no decorations (such as caption, border, or menu bar), and its paint handler is displaying a penguin bitmap. There is a region associated with the frame that acts as a mask that lets only the penguin show through. Figure 4-4 A shaped wxFrame The principles are demonstrated by the code in samples/shaped, although it uses a different image from the one shown here. When the frame is created, a bitmap is loaded, and a region is created out of it. On GTK+, setting the win- dow shape must be delayed until the window creation event is sent, so you will need a __WXGTK__ test in your code. The following code demonstrates the required event table, frame constructor, and window creation event handler: BEGIN_EVENT_TABLE(ShapedFrame, wxFrame) EVT_MOTION(ShapedFrame::OnMouseMove) EVT_PAINT(ShapedFrame::OnPaint)

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 62 62 Window Basics Chapter 4 #ifdef __WXGTK__ EVT_WINDOW_CREATE(ShapedFrame::OnWindowCreate) #endif END_EVENT_TABLE() ShapedFrame::ShapedFrame() : wxFrame((wxFrame *)NULL, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, 300), | wxFRAME_SHAPED | wxSIMPLE_BORDER | wxFRAME_NO_TASKBAR | wxSTAY_ON_TOP ) { m_hasShape = false; m_bmp = wxBitmap(wxT(“penguin.png”), wxBITMAP_TYPE_PNG); SetSize(wxSize(m_bmp.GetWidth(), m_bmp.GetHeight())); #ifndef __WXGTK__ // On wxGTK we can’t do this yet because the window hasn’t // been created yet so we wait until the EVT_WINDOW_CREATE // event happens. On wxMSW and wxMac the window has been created // at this point so we go ahead and set the shape now. SetWindowShape(); #endif } // Used on GTK+ only void ShapedFrame::OnWindowCreate(wxWindowCreateEvent& WXUNUSED(evt)) { SetWindowShape(); } To set the shape, we create a region from the bitmap and the color to be used as the transparent color, and call SetShape. void ShapedFrame::SetWindowShape() { wxRegion region(m_bmp, *wxWHITE); m_hasShape = SetShape(region); } In order to allow the window to be moved around the screen, there is a mouse handler that explicitly moves the window. void ShapedFrame::OnMouseMove(wxMouseEvent& evt) { wxPoint pt = evt.GetPosition(); if (evt.Dragging() && evt.LeftIsDown()) { wxPoint pos = ClientToScreen(pt); Move(wxPoint(pos.x - m_delta.x, pos.y - m_delta.y)); } }

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 63 Top-Level Windows 63 The paint handler is very simple, but of course in a real application, you will have other windows or graphics displayed inside the frame. void ShapedFrame::OnPaint(wxPaintEvent& evt) { wxPaintDC dc(this); dc.DrawBitmap(m_bmp, 0, 0, true); } For more details, see samples/shaped in the wxWidgets distribution. Mini-Frames On Windows and GTK+, you can use wxMiniFrame for frames that must have a small caption—for example, if implementing tool palettes. Figure 4-5 shows a wxMiniFrame on Windows. This class is implemented as a normal frame on Mac OS X. There are no special styles or member functions for this class. Figure 4-5 A wxMiniFrame wxMDIParentFrame This frame class, derived from wxFrame, is part of wxWidgets’ Multiple Document Interface (MDI) support, whereby a parent frame manages zero or more wxMDIChildFrame windows. How it does so depends on platform; the main visual differences are illustrated in Figure 4-6. On Windows, the child win- dows are clipped to the boundary of the main window. These windows can be tiled, overlapped, or maximized within the main frame so that only one shows at a time, and a Window menu (automatically added by wxWidgets) is available for controlling the child windows. The MDI style has the advan- tage of keeping a desktop relatively uncluttered, grouping together all the windows in the application. Also, because the main frame’s menu bar is replaced by the active child frame’s menu bar, the clutter of multiple menu bars is also reduced.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 64 64 Window Basics Chapter 4 Under GTK+, wxWidgets emulates the MDI style using tabbed windows; only one window is shown at a time, but the user can switch between windows quickly using the tabs. On Mac OS, wxMDIParentFrame and wxMDIChildFrame win- dows look like normal frames, reflecting the fact that documents always open in a new window on Mac OS. Figure 4-6 wxMDIParentFrame on Windows and GTK+ On platforms where MDI children are contained within the parent, a wxMDIParentFrame arranges its children on a wxMDIClientWindow, which can coex- ist with other windows in the frame. In Figure 4-6, the parent frame’s size event handler sets the sizes and positions of a text control and the client win- dow. For details, see samples/mdi in your wxWidgets distribution. Each child frame can have its own menu bar, in addition to the parent frame’s menu bar. When a child frame is activated, its menu bar is shown. When there are no child frames, the parent frame’s menu bar is shown. You need to construct your child frame menu bar carefully to include the same commands as the parent’s menu bar, adding others that are specific to the child. The parent frame and child frames can have their own toolbars and sta- tus bars, but they are not swapped like menu bars. wxMDIParentFrame has the same constructor as wxFrame. wxMDIParentFrame Styles wxMDIParentFrame can have the window styles listed in Table 4-7, in addition to those described for wxFrame. Table 4-7 wxMDIParentFrame Styles wxFRAME_NO_WINDOW_MENU Under Windows, removes the Window menu that is normally added automatically.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 65 Top-Level Windows 65 wxMDIParentFrame Member Functions These are the major wxMDIParentFrame functions, in addition to those defined for wxFrame. ActivateNext and ActivatePrevious activate the next or previous child frame. Cascade and Tile provide two methods to arrange the child frames: over- lapping and tiling them, respectively. ArrangeIcons lays out any minimized frames within the client window. These three functions only apply on Windows. GetActiveChild provides the application with a pointer to the currently active child (if any). GetClientWindow returns a pointer to the client window (a container for the child frames). You can provide a different client window from the default by overriding OnCreateClient and returning an instance of your own wxMDIClientWindow-derived class, but you must then use two-step parent frame construction. wxMDIChildFrame wxMDIChildFrame should always be created as a child of a wxMDIParentFrame win- dow. As explained in the previous section, its appearance depends on the plat- form and will either be free-floating or constrained to the boundary of its parent. Its constructor is the same as a regular frame; despite the fact that its true parent is a wxMDIClientWindow, you should pass the frame parent to the constructor. For example: #include “wx/mdi.h” wxMDIParentFrame* parentFrame = new wxMDIParentFrame( NULL, ID_MYFRAME, wxT(“Hello wxWidgets”)); wxMDIChildFrame* childFrame = new wxMDIChildFrame( parentFrame, ID_MYCHILD, wxT(“Child 1”)); childFrame->Show(true); parentFrame->Show(true); wxMDIChildFrame Styles wxMDIChildFrame takes the same styles as wxFrame, although depending on plat- form, not all of them will take effect.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 66 66 Window Basics Chapter 4 wxMDIChildFrame Member Functions These are the major wxMDIChildFrame functions. See also the base class, wxFrame. Activate activates this frame, bringing it to the front and displaying its menu bar. Maximize resizes the frame to fit the parent (Windows only). Restore sets the frame to the size it was before it was maximized (Windows only). wxDialog A dialog is a top-level window used for presenting information, options, or selections. It can optionally have a title bar with decorations such as the close window button and minimize button, and as with wxFrame, you can assign an icon to the dialog to be shown in the taskbar or equivalent. A dialog can con- tain any combination of non-top level windows and control—for example, a wxNotebook with OK and Cancel buttons underneath. As its name suggests, you use this class to initiate a dialog with a user, presenting specific information and choices, compared with the frame’s usual role as the main window of an application. There are two kinds of dialog—modal and modeless. A modal dialog blocks program flow and user input on other windows until it is dismissed (EndModal is called), whereas a modeless dialog behaves more like a frame in that program flow continues, and input in other windows is still possible. To show a modal dialog, you should use the ShowModal method, whereas to show a dialog modelessly, you simply use Show, as with frames. Note that the modal dialog is one of the very few examples of wxWindow- derived objects that may be created on the stack and not on the heap. In other words, you can use the heap method as follows: void AskUser() { MyAskDialog *dlg = new MyAskDialog(...); if ( dlg->ShowModal() == wxID_OK ) ... //else: dialog was cancelled or some another button pressed dlg->Destroy(); } You can also achieve the same result by using the stack: void AskUser() { MyAskDialog dlg(...); if ( dlg.ShowModal() == wxID_OK ) ... // no need to call Destroy() here }

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 67 Top-Level Windows 67 Normally you will derive a new class from wxDialog rather than using it directly so that you can handle events such as wxEVT_CLOSE (see the following) and command events. Often you will create your dialog’s controls within the constructor. Just as with wxFrame, wxWidgets will resize a single child window to fill the dialog, but for more than one child, the application is responsible for sizing and positioning the controls using sizers (see Chapter 7). When you call Show, wxWidgets calls InitDialog to send a wxInitDialog Event to the dialog, transferring data to the dialog via validators or other means. wxDialog has the following constructor in addition to the default constructor: wxDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE, const wxString& name = wxT(“dialog”)); For example: wxDialog* dialog = new wxDialog(NULL, ID_MYDIALOG, wxT(“Hello wxWidgets”), wxDefaultPosition, wxSize(500, 300)); dialog->Show(true); The dialog won’t be shown until Show(true) or ShowModal is called to give the application the chance to do child window layout invisibly. By default, a dialog created with a NULL parent window will be given the application’s top-level window as parent. Use the wxDIALOG_NO_PARENT style to prevent this from happening and create an orphan dialog. This is not recom- mended for modal dialogs. As with wxFrame, do not use delete to destroy a dialog, but instead use Destroy or Close to delay deletion until all the object’s events have been processed. When you call Close, the default wxEVT_CLOSE handler for this func- tion usually calls Destroy. Note that before a modal dialog is destroyed, an event handler should have called EndModal, passing the identifier of the command that closed it (for example, wxID_OK or wxID_CANCEL). This will exit the dialog’s event loop so that the dialog object can be destroyed by the code that called ShowModal. The identifier passed to EndModal will be returned by ShowModal. To clarify, the OnCancel function in the following example will be called while ShowModal is in its event loop:

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 68 68 Window Basics Chapter 4 // Event handler for wxID_CANCEL void MyDialog::OnCancel(wxCommandEvent& event) { EndModal(wxID_CANCEL); } // Show a dialog void ShowDialog() { // Show the dialog MyDialog dialog; // OnCancel or other function is called from within ShowModal if (dialog.ShowModal() == wxID_CANCEL) { ... } } Figure 4-7 shows a typical simple dialog, which we’ll create in Chapter 9. Dialogs can, of course, get more complicated, such as the settings dialog shown in Figure 4-8. This one has a splitter window, a tree control to navigate between multiple panels, and a grid control acting as a property editor. Figure 4-7 A typical simple dialog

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 69 Top-Level Windows 69 Figure 4-8 A more complex dialog wxDialog Styles wxDialog can have the window styles listed in Table 4-8 in addition to those described for wxWindow. Table 4-8 wxDialog Styles wxDEFAULT_DIALOG_STYLE Defined as wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX. wxCAPTION Puts a caption on the dialog. wxMINIMIZE_BOX Displays a minimize box on the dialog. wxMAXIMIZE_BOX Displays a maximize box on the dialog. wxCLOSE_BOX Displays a close box on the dialog. wxSTAY_ON_TOP The dialog stays on top of all other windows. Windows only. wxSYSTEM_MENU Displays a system menu. wxRESIZE_BORDER Displays a resizable border around the window. wxDIALOG_NO_PARENT A dialog created with a NULL parent window will be given the application’s top-level window as parent. Use this style to create an “orphan” dialog. This is not recommended for modal dialogs.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 70 70 Window Basics Chapter 4 Table 4-9 describes extra styles that couldn’t be accommodated in the regular style, and so are set using wxWindow::SetExtraStyle. Although wxWS_EX_BLOCK_ EVENTS is valid for all windows, we repeat it here because it is set by default. Table 4-9 wxDialog Extra Styles wxDIALOG_EX_CONTEXTHELP Under Windows, puts a query button on the caption. When pressed, Windows will go into a context- sensitive help mode and wxWidgets will send a wxEVT_HELP event if the user clicked on an application window. You cannot use this style together with wxMAXIMIZE_BOX or wxMINIMIZE_BOX. wxWS_EX_BLOCK_EVENTS Blocks command event propagation above this win- dow (the default). Be aware that calling SetExtra Style may reset the style. wxDIALOG_EX_METAL Under Mac OS X, this style will give the dialog a metallic appearance. This should be used sparingly and is intended for consumer applications that emu- late a physical device such as an audio player. wxDialog Events wxDialog generates the events listed in Table 4-10, in addition to those men- tioned for wxWindow. Table 4-10 wxDialog Events EVT_ACTIVATE(func) Processes a wxEVT_ACTIVATE event, generated when the dialog is about to be activated or deactivated. Handlers take a wxActivateEvent object. EVT_CLOSE(func) Processes a wxEVT_CLOSE event, generated when the pro- gram or windowing system is trying to close the dialog. Handlers take a wxCloseEvent object and can veto the close by calling Veto on the object. EVT_ICONIZE(func) Processes a wxEVT_ICONIZE event, generated when the dialog is being iconized (minimized) or restored. Handlers take a wxIconizeEvent object. Call IsIconized to check if this is an iconize or restore event. EVT_MAXIMIZE(func) Processes a wxEVT_MAXIMIZE event, generated when the dialog is being maximized or restored. Handlers take a wxMaximizeEvent object. Call IsMaximized to check if this is a maximize or restore event. EVT_INIT_DIALOG(func) Processes a wxEVT_INIT_DIALOG event, generated to en- able the dialog to initialize itself. Handlers take a wxInit DialogEvent object. This event is also generated for wxPanel. The default handler calls TransferDataToWindow.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 71 Top-Level Windows 71 wxDialog Member Functions These are the major wxDialog functions. Please refer also to the member func- tions for wxWindow and wxTopLevelWindow, from which wxDialog is derived. GetTitle and SetTitle are accessors for the title that appears on the dia- log’s title bar. Iconize iconizes or restores the dialog. You can test the dialog’s iconic state by calling IsIconized. Maximize maximizes the dialog (makes it as large as the desktop) or restores it to the previous state. Call IsMaximized to test whether the dialog is maximized. Windows only. SetIcon sets the icon displayed when the dialog is minimized. It’s also used for other purposes by the window manager, such as displaying the pro- gram’s icon in Windows Explorer or on the taskbar. You can also use SetIcons to set an icon bundle (multiple icons with a variety of resolutions and depths). ShowModal is used when showing a modal dialog. It returns the value of the identifier passed to EndModal—normally this is the identifier of the control that the user clicked on to dismiss the dialog. By default (implemented in the dialog’s wxEVT_CLOSE handler), closing the dialog sends a simulated wxID_CANCEL to the dialog. The default handler for wxID_CANCEL calls EndModal with wxID_ CANCEL. Therefore, if you provide a button with the identifier wxID_CANCEL, the logic for canceling the dialog is handled for you, unless you need extra functionality. SetLeftMenu and SetRightMenu are only available on Microsoft Smartphone, and they set the commands allocated to the left and right menu buttons. They take a command identifier, such as wxID_OK, a label, and an optional pointer to a wxMenu to show. wxPopupWindow Pop-up windows are not implemented on all platforms (in particular, they are missing from Mac OS X), and so we will only briefly mention them. wxPopupWindow is a top-level window that normally has minimal decora- tion and is used to implement windows that are shown briefly, such as a menu or a tooltip. Create it by passing a parent window and optional style to the constructor (defaulting to wxNO_BORDER). Move it into position with Position, which takes a screen position and size and makes sure that the popup is visi- ble on the display. wxPopupTransientWindow is a special kind of wxPopupWindow that dismisses itself (hides) when it loses the focus or the mouse is clicked outside of the win- dow. It can also be hidden with Dismiss.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 72 72 Window Basics Chapter 4 CONTAINER WINDOWS Container windows are designed to hold further visual elements, either child windows or graphics drawn on the window. wxPanel wxPanel is essentially a wxWindow with some dialog-like properties. This is usu- ally the class to use when you want to arrange controls on a window that’s not a wxDialog, such as a wxFrame. It’s often used for pages of a wxNotebook. wxPanel normally takes the system dialog color. As with wxDialog, you can use InitDialog to send a wxInitDialogEvent to the panel, transferring data to the panel via validators or other means. wxPanel also handles navigation keys such as the Tab key to provide automatic traversal between controls, if the wxTAB_TRAVERSAL style is provided. wxPanel has the following constructor in addition to the default constructor: wxPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL|wxNO_BORDER, const wxString& name = wxT(“panel”)); For example: wxPanel* panel = new wxPanel(frame, wxID_ANY, wxDefaultPosition, (500, 300)); wxPanel Styles There are no specific styles for wxPanel, but see the styles for wxWindow. wxPanel Member Functions There are no distinct wxPanel functions; please refer to the wxWindow member functions, inherited by wxPanel. wxNotebook This class represents a control with several pages, switched by clicking on tabs along an edge of the control. A page is normally a wxPanel or a class derived from it, although you may use other window classes.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 73 Container Windows 73 Notebook tabs may have images as well as, or instead of, text labels. The images are supplied via a wxImageList (see Chapter 10), and specified by posi- tion in the list. To use a notebook, create a wxNotebook object and call AddPage or InsertPage, passing a window to be used as the page. Do not explicitly destroy the window for a page that is currently managed by wxNotebook; use DeletePage instead, or let the notebook destroy the pages when it is itself destroyed. Here’s an example of creating a notebook with three panels, and a text label and icon for each tab: #include “wx/notebook.h” #include “copy.xpm” #include “cut.xpm” #include “paste.xpm” // Create the notebook wxNotebook* notebook = new wxNotebook(parent, wxID_ANY, wxDefaultPosition, wxSize(300, 200)); // Create the image list wxImageList* imageList = new wxImageList(16, 16, true, 3); imageList->Add(wxIcon(copy_xpm)); imageList->Add(wxIcon(paste_xpm)); imageList->Add(wxIcon(cut_xpm)); // Create and add the pages wxPanel1* window1 = new wxPanel(notebook, wxID_ANY); wxPanel2* window2 = new wxPanel(notebook, wxID_ANY); wxPanel3* window3 = new wxPanel(notebook, wxID_ANY); notebook->AddPage(window1, wxT(“Tab one”), true, 0); notebook->AddPage(window2, wxT(“Tab two”), false, 1); notebook->AddPage(window3, wxT(“Tab three”), false 2); Figure 4-9 shows the result on Windows. Figure 4-9 A wxNotebook

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 74 74 Window Basics Chapter 4 On most platforms, there are scroll buttons to view tabs that cannot all be displayed on the window at once. However, on Mac OS, the tabs do not scroll, so the number you can display is limited by the window and tab label size. If you use sizers to lay out controls on individual pages, and pass wxDefaultSize to the notebook constructor, wxNotebook will adjust its size to fit the sizes of its pages. Notebook Theme Management On Windows XP, the default theme paints a gradient on the notebook’s pages. Although this is the expected native behavior, it can slow down performance, and you may prefer a solid background for aesthetic reasons, especially when the notebook is not being used in a dialog. If you want to suppress themed drawing, there are three ways of doing it. You can use the wxNB_NOPAGETHEME style to disable themed drawing for a particular notebook, you can call wxSystemOptions::SetOption to disable it for the whole application, or you can disable it for individual pages by using SetBackgroundColour. To disable themed pages globally, do this: wxSystemOptions::SetOption(wxT(“msw.notebook.themed-background”), 0); Set the value to 1 to enable it again. To give a single page a solid background that matches the current theme, use wxColour col = notebook->GetThemeBackgroundColour(); if (col.Ok()) { page->SetBackgroundColour(col); } On platforms other than Windows, or if the application is not using Windows themes, GetThemeBackgroundColour will return an uninitialized color object, and this code will therefore work on all platforms. Please note that this syntax and behavior is subject to change, so refer to the wxNotebook documentation in your wxWidgets distribution for the latest information. wxNotebook Styles wxNotebook can have the window styles listed in Table 4-11, in addition to those described for wxWindow.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 75 Container Windows 75 Table 4-11 wxNotebook Styles wxNB_TOP Place tabs on the top side. wxNB_LEFT Place tabs on the left side. Not supported under Windows XP for all themes. wxNB_RIGHT Place tabs on the right side. Not supported under Windows XP for all themes. wxNB_BOTTOM Place tabs under instead of above the notebook pages. Not supported under Windows XP for all themes. wxNB_FIXEDWIDTH All tabs will have same width. Windows only. wxNB_MULTILINE There can be several rows of tabs. Windows only. wxNB_NOPAGETHEME On Windows, suppresses the textured theme painting for the notebook’s pages, drawing a solid color to match the current theme instead. This can improve performance in addition to giving an aesthetic choice. wxNotebook Events wxNotebookgenerates wxNotebookEvent propagating events (events that can be handled by the notebook or its ancestors) specified in Table 4-12. Table 4-12 wxNotebook Events EVT_NOTEBOOK_PAGE_CHANGED(id, func) The page selection has changed. EVT_NOTEBOOK_PAGE_CHANGING(id, func) The page selection is about to change. You can veto the selection change with Veto. wxNotebook Member Functions These are the major wxNotebook functions. AddPage adds a page, and InsertPage inserts a page at the given position. You can use a text label for the tab, or an image (specified by index into an image list), or both. For example: // Adds an unselected page with a label and an image // (index 2 in the associated image list). notebook->AddPage(page, wxT(“My tab”), false, 2); DeletePage removes and destroys the specified page, while RemovePage just removes the page without deleting the page. Call DeleteAllPages to delete all the pages. When the wxNotebook is deleted, it will delete all its pages.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 76 76 Window Basics Chapter 4 AdvanceSelection cycles through the pages, and SetSelection sets the specified page by zero-based index. Use GetSelection to get the index of the selected page, or wxNOT_FOUND if none was selected. SetImageList sets a wxImageList to be used by the notebook but does not take ownership of it. Call AssignImageList if you want the notebook to delete the image list when it is destroyed. GetImageList returns the associated image list. An image list stores images to be shown on each page tab, if required. wxImageList is described in Chapter 10. Use GetPage to return the page window for a given index, and use GetPageCount to return the number of pages in the notebook. SetPageText and GetPageText are accessors for the label for a given page (by index). SetPageImage and GetPageImage are accessors for the index of a page’s image index in the notebook’s image list. Alternatives to wxNotebook wxNotebook is derived from a base class wxBookCtrlBase, which abstracts the concept of a control that manages pages. There are two API-compatible varia- tions of the wxNotebook concept, wxListbook and wxChoicebook, and you can implement your own, such as wxTreebook. wxListbook uses a wxListCtrl to change pages; the list control displays icons with text labels underneath them, and can be on any of the four sides, defaulting to the left side. This is an attractive alternative to wxNotebook, and it has the advantage of being able to cope with an arbitrary number of pages even on Mac OS X because the list control can scroll. wxChoicebook uses a choice control (a drop-down list) and is particularly handy for small devices with restricted screen space, such as smartphones. It does not display images, and by default, it will display the choice control at the top. The include files for these classes are wx/listbook.h and wx/choicebk.h. Event handlers for these two classes take a wxListbookEvent or wxChoicebookEvent argument, respectively, and you can use the event macros EVT_XXX_PAGE_CHANGED(id, func) and EVT_XXX_PAGE_CHANGING(id, func) where XXX is LISTBOOK or CHOICEBOOK. You can use the same window styles as wxNotebook, or you can use the equivalents, such as wxCHB_TOP or wxLB_TOP instead of wxNB_TOP. wxScrolledWindow All windows can have scrollbars, but extra code is required to make scrolling work. This gives the flexibility to define appropriate scrolling behaviors for dif- ferent kinds of windows. wxScrolledWindow implements commonly required scrolling behavior by assuming that scrolling happens in consistent units, not different-sized jumps, and that page size (the amount scrolled when “paging”

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 77 Container Windows 77 up, down, left, or right) is represented by the visible portion of the window. It is suited to drawing applications but is not so suitable for a sophisticated editor in which the amount scrolled may vary according to the size of text on a given line. For this, you would derive from wxWindow and implement scrolling your- self. wxGrid is an example of a class that implements its own scrolling, largely because columns and rows can vary in size. To use a scrolled window, you need to define the number of pixels per log- ical scroll unit (how much the window is scrolled for a line up or down scroll event) and provide the virtual size in logical units. wxScrolledWindow will then take care of showing the scrollbars with appropriately sized “thumbs” (the parts you can drag) and will show or hide scrollbars as appropriate, according to the actual size of the window. The following fragment shows how to create a scrolled window: #include “wx/scrolwin.h” wxScrolledWindow* scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxPoint(0, 0), wxSize(400, 400), wxVSCROLL|wxHSCROLL); // Set up virtual window dimensions. It will be 1000x1000 // and will scroll 10 pixels at a time int pixelsPerUnixX = 10; int pixelsPerUnixY = 10; int noUnitsX = 1000; int noUnitsY = 1000; scrolledWindow->SetScrollbars(pixelsPerUnitX, pixelsPerUnitY, noUnitsX, noUnitsY); A second way to specify the virtual size is to use SetVirtualSize, which takes the virtual size in pixels, plus a call to SetScrollRate to set the horizontal and vertical scrolling increments. A third way is to set a sizer for the window, and the scrolled window will calculate the required scrollbar dimensions from the space taken up by the child windows. You will still need to call SetScrollRate to specify the scrolling increments. You can provide a paint event handler as normal, but in order to draw the graphics at the appropriate position for the current positions of the scroll- bars, call DoPrepareDC before drawing. This sets the device context’s device origin. For example: void MyScrolledWindow::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); DoPrepareDC(dc); dc.SetPen(*wxBLACK_PEN); dc.DrawLine(0, 0, 100, 100); }

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 78 78 Window Basics Chapter 4 Alternatively, you can override the OnDraw virtual function; wxScrolledWindow creates a paint device context and calls DoPrepareDC for you before calling your OnDraw function, so the code simplifies to the following: void MyScrolledWindow::OnDraw(wxDC& dc) { dc.SetPen(*wxBLACK_PEN); dc.DrawLine(0, 0, 100, 100); } Note that you will need to call DoPrepareDC if you draw on the window from outside the paint event, such as within a mouse event handler. You can provide your own DoPrepareDC function. The default function sim- ply shifts the device origin according to the current scroll positions so that subsequent drawing will appear at the right place: void wxScrolledWindow::DoPrepareDC(wxDC& dc) { int ppuX, ppuY, startX, startY; GetScrollPixelsPerUnit(& ppuX, & ppuY); GetViewStart(& startX, & startY); dc.SetDeviceOrigin( - startX * ppuX, - startY * ppuY ); } For more on painting on a wxScrolledWindow, including using buffered drawing, please see the section on wxPaintDC in Chapter 5, “Drawing and Printing.” wxScrolledWindow Styles There are no special styles for wxScrolledWindow, but usually you will supply wxVSCROLL|wxHSCROLL (the default style for wxScrolledWindow). On some plat- forms, if these styles are not present, no scrollbars will be provided for effi- ciency reasons. wxScrolledWindow Events wxScrolledWindow generates wxScrollWinEvent events (see Table 4-13). These events do not propagate up the window parent-child hierarchy, so if you want to intercept these events, you must derive a new class or plug an event han- dler object into the window object. Normally you will not need to override the existing handlers for these events.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 79 Container Windows 79 Table 4-13 wxScrolledWindow Events EVT_SCROLLWIN(func) Handles all scroll events. EVT_SCROLLWIN_TOP(func) Handles wxEVT_SCROLLWIN_TOP scroll-to-top events. EVT_SCROLLWIN_BOTTOM(func) Handles wxEVT_SCROLLWIN_TOP scroll-to- bottom events. EVT_SCROLLWIN_LINEUP(func) Handles wxEVT_SCROLLWIN_LINEUP line up events. EVT_SCROLLWIN_LINEDOWN(func) Handles wxEVT_SCROLLWIN_LINEDOWN line down events. EVT_SCROLLWIN_PAGEUP(func) Handles wxEVT_SCROLLWIN_PAGEUP page up events. EVT_SCROLLWIN_PAGEDOWN(func) Handles wxEVT_SCROLLWIN_PAGEDOWN page down events. wxScrolledWindow Member Functions These are the major wxScrolledWindow functions. CalcScrolledPosition and CalcUnscrolledPosition both take four argu- ments: two integers for the position input in pixels, and two pointers to inte- gers for the transformed position output, also in pixels. CalcScrolledPosition calculates the device position from the logical position. For example, if the window is scrolled 10 pixels down from the top, the logical first visible position is 0, but the device position is -10. CalcUnscrolledPosition does the inverse, cal- culating the logical position from the device position. EnableScrolling enables or disables physical scrolling in horizontal and vertical directions independently. Physical scrolling is the physical transfer of bits up or down the screen when a scroll event occurs. If the application scrolls by a variable amount (for example, if there are different font sizes), then phys- ical scrolling will not work, and you should switch it off. If physical scrolling is disabled, you will have to reposition child windows yourself. Physical scrolling may not be available on all platforms, but it is enabled by default where it is available. GetScrollPixelsPerUnit returns the horizontal and vertical scroll unit sizes in two pointers to integers. A value of zero indicates that there is no scrolling in that direction. GetViewStart returns the position of the first visible position on the win- dow, in logical units. Pass two pointers to integers to receive the values. You will need to multiply by the values returned by GetScrollPixelsPerUnit to get pixel values.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 80 80 Window Basics Chapter 4 GetVirtualSize returns the size in device units (pixels) of the scrollable window area. Pass two pointers to integers to receive the virtual width and height. DoPrepareDC prepares the device context by setting the device origin according to the current scrollbar positions. Scroll scrolls a window so that the view is at the given point in scroll units (not pixels), passed as two integers. If either parameter is -1, that posi- tion will be unchanged. SetScrollbars sets the pixels per unit in each direction, the number of units for the virtual window in each direction, the horizontal and vertical posi- tion to scroll to (optional), and a boolean to indicate whether the window should be refreshed (false by default). SetScrollRate sets the horizontal and increment scroll rate (the same as the pixels per unit parameters in SetScrollbars). SetTargetWindow can be used to scroll a window other than the wxScrolledWindow. Scrolling Without Using wxScrolledWindow If you want to implement your own scrolling behavior, you can derive a class from wxWindow and use wxWindow::SetScrollbar to set scrollbar properties. SetScrollbar takes the arguments listed in Table 4-14. Table 4-14 SetScrollbar Arguments int orientation The scrollbar to set: wxVERTICAL or wxHORIZONTAL. int position The position of the scrollbar “thumb” in scroll units. int visible The size of the visible portion of the scrollbar, in scroll units. Normally, a scrollbar is capable of indicating the visible por- tion visually by showing a different length of thumb. int range The maximum value of the scrollbar, where zero is the start position. You choose the units that suit you, so if you wanted to display text that has 100 lines, you would set this to 100. Note that this doesn’t have to correspond to the number of pixels scrolled—it is up to you how you actually show the contents of the window. bool refresh true if the scrollbar should be repainted immediately. Let’s say you want to display 50 lines of text, using the same font. The window is sized so that you can only see 16 lines at a time. You would use SetScrollbar(wxVERTICAL, 0, 16, 50)

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 81 Container Windows 81 Note that with the window at this size, the thumb position can never go above 50 minus 16, or 34. You can determine how many lines are currently visible by dividing the current view size by the character height in pixels. When defining your own scrollbar behavior, you will always need to recalculate the scrollbar settings when the window size changes. You could therefore introduce a new function AdjustScrollbars into which you place your scrollbar calculations and SetScrollbar call. AdjustScrollbars can be called ini- tially, and also from your wxSizeEvent handler function. It’s instructive to look at the implementations of wxScrolledWindow and wxGrid if you’re thinking of implementing your own scrolling behavior. You may want to look at wxVScrolledWindow in the wxWidgets reference manual; this can be used to build a scrolled window class that can scroll by lines of unequal height in the vertical direction. wxSplitterWindow This class manages up to two subwindows (use nested splitter windows if you need more splits). The current view can be split into two by the application, for example, from a menu command. It can be unsplit either by the application or via the splitter window user interface by double-clicking on the sash or drag- ging the sash until one of the panes has zero size (override the latter behavior with SetMinimumPaneSize). On most platforms, when the sash is dragged, a reverse-video line will be drawn to show where the sash will end up. You can pass wxSP_LIVE_UPDATE to let the sash move in “real time” instead, resizing the child windows. This is the default (and only) mode on Mac OS X. The following fragment shows how to create a splitter window, creating two subwindows and hiding one of them. #include “wx/splitter.h” wxSplitterWindow* splitter = new wxSplitterWindow(this, wxID_ANY, wxPoint(0, 0), wxSize(400, 400), wxSP_3D); leftWindow = new MyWindow(splitter); leftWindow->SetScrollbars(20, 20, 50, 50); rightWindow = new MyWindow(splitter); rightWindow->SetScrollbars(20, 20, 50, 50); rightWindow->Show(false); splitter->Initialize(leftWindow); // Unncomment this to prevent unsplitting // splitter->SetMinimumPaneSize(20);

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 82 82 Window Basics Chapter 4 This fragment shows how the splitter window can be manipulated after creation: void MyFrame::OnSplitVertical(wxCommandEvent& event) { if ( splitter->IsSplit() ) splitter->Unsplit(); leftWindow->Show(true); rightWindow->Show(true); splitter->SplitVertically( leftWindow, rightWindow ); } void MyFrame::OnSplitHorizontal(wxCommandEvent& event) { if ( splitter->IsSplit() ) splitter->Unsplit(); leftWindow->Show(true); rightWindow->Show(true); splitter->SplitHorizontally( leftWindow, rightWindow ); } void MyFrame::OnUnsplit(wxCommandEvent& event) { if ( splitter->IsSplit() ) splitter->Unsplit(); } Figure 4-10 shows how the wxWidgets splitter sample looks on Windows with- out the wxSP_NO_XP_THEME style. If you use this style, the splitter will take on a more traditional look with a sunken border and 3D sash. Figure 4-10 A wxSplitterWindow

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 83 Container Windows 83 wxSplitterWindow Styles wxSplitterWindow can have the window styles shown in Table 4-15 in addition to those described for wxWindow. Table 4-15 wxSplitterWindow Styles wxSP_3D Draws a 3D-effect border and sash. wxSP_3DSASH Draws a 3D-effect sash. wxSP_3DBORDER Synonym for wxSP_BORDER. wxSP_BORDER Draws a standard border. wxSP_NOBORDER No border (the default). wxSP_NO_XP_THEME Add a sunken border and 3D sash on Windows, if you don’t like the minimal (but more native) look. wxSP_PERMIT_UNSPLIT Always enable the window to unsplit, even with the minimum pane size other than zero. wxSP_LIVE_UPDATE Resize the child windows immediately as the splitter is being moved. wxSplitterWindow Events wxSplitterWindow generates wxSplitterEvent propagating events, as listed in Table 4-16. Table 4-16 wxSplitterWindow Events EVT_SPLITTER_SASH_POS_ Processes a wxEVT_COMMAND_SPLITTER_SASH_ CHANGING(id, func) POS_CHANGING event, generated when the sash position is in the process of being changed. Call Veto to stop the sash position changing, or call the event's SetSashPosition function to change the sash position. EVT_SPLITTER_SASH(id, func) Processes a wxEVT_COMMAND_SPLITTER_ SASH_POS_CHANGED event, generated when the sash position is changed. May be used to modify the sash position before it is set, or to prevent the change from taking place, by calling the event's SetSashPosition function. EVT_SPLITTER_UNSPLIT(id, func) Processes a wxEVT_COMMAND_SPLITTER_ UNSPLIT event, generated when the splitter is unsplit. EVT_SPLITTER_DCLICK(id, func) Processes a wxEVT_COMMAND_SPLITTER_ DOUBLECLICKED event, generated when the sash is double-clicked.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 84 84 Window Basics Chapter 4 wxSplitterWindow Member Functions These are the major wxSplitterWindow functions. GetMinimumPaneSize and SetMinimumPaneSize are accessors for the mini- mum pane size. The default minimum pane size is zero, which means that either pane can be reduced to zero by dragging the sash, thus removing one of the panes. To prevent this behavior (and veto out-of-range sash dragging), set a minimum size, for example 20 pixels. However, if the wxSP_PERMIT_UNSPLIT style is used when a splitter window is created, the window may be unsplit even if the minimum size is non-zero. GetSashPosition and SetSashPosition are accessors for the sash position. Passing true to SetSashPosition resizes the pane and redraws the sash and border. GetSplitMode and SetSplitMode are accessors for the split orientation, which can be wxSPLIT_VERTICAL or wxSPLIT_HORIZONTAL. GetWindow1 and GetWindow2 get the pointers to the two panes. Initialize can be called with a pointer to a window if you only want to have one pane initially. IsSplit tests whether the window is split. ReplaceWindow replaces one of the windows managed by the wxSplitter Window with another one. Generally, it’s better to use this function instead of calling Unsplit and then resplitting the window. SetSashGravity takes a floating-point argument, which determines the position of the sash as the window is resized. A value of 0.0 (the default) means that only the bottom or right child window will be resized, and a value of 1.0 means that only the top or left child window will be resized. Values inbe- tween indicate that the change in size should be distributed between both child windows (a value of 0.5 distributes the size evenly). Use GetSashGravity to return the current setting. SplitHorizontally and SplitVertically initialize the splitter window with two panes and optionally an initial sash size. Unsplit removes the specified pane. UpdateSize causes the splitter to update its sash position immediately (normally, this is done in idle time). Sizing Issues with wxSplitterWindow There are subtleties to be aware of when using a splitter window as part of a sizer hierarchy. If you don’t need the sash to be moveable, you can create both child windows with absolute sizes. This will fix the minimum size of both child windows, and the sash will therefore not be free to move. If you need the sash to be moveable, as is normally the case, pass default values to the child win- dows and specify an initial minimum size in the splitter window constructor. Then add the splitter window to its sizer, passing the wxFIXED_MINSIZE flag to Add, which tells wxWidgets to treat the specified size as the minimum size. Another issue is that a splitter does not set its sash position (and there- fore the sizes of its child windows) until idle time, when it can be sure that sizing

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 85 Container Windows 85 has been finalized and the sash position won’t be set prematurely. This can result in a sash that visibly repositions itself just after the window has been shown. To fix this, call wxSplitterWindow::UpdateSize as soon as you have done your layout, for example after a wxSizer::Fit call. The splitter will update its sash and child window sizes immediately. By default, when the user or application resizes the splitter, only the bot- tom (or right) window is adjusted to take into account the new size. If you need different behavior, use SetSashGravity as documented in the previous section. Alternatives to wxSplitterWindow If you have a lot of “split” windows in your application, consider using wxSashWindow. This is a window that allows any of its edges to have a sash (as specified by the application) that can be dragged to resize the window. The actual content window is normally created by the application as a child of wxSashWindow. When a sash is dragged, it notifies the application with a wxSashEvent so the handler can change the window size accordingly before laying out the win- dows. Layout is achieved via a class called wxLayoutAlgorithm, which provides LayoutWindow, LayoutFrame, and LayoutMDIFrame methods for arranging the sash windows on different kinds of parent windows. You can also use the class wxSashLayoutWindow, which responds to events of type wxQueryLayoutInfoEvent to provide orientation and size information to wxLayoutAlgorithm. Please see the reference manual for further details. wxSashWindow doesn’t permit moving or undocking windows, and it’s likely that these classes will be superceded by a general docking and layout framework in the near future. Figure 4-11 shows a view of the wxSashWindow sample provided in samples/sashtest. Figure 4-11 The wxSashWindow demo

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 86 86 Window Basics Chapter 4 NON-STATIC CONTROLS Non-static controls, such as wxButton and wxListBox, respond to mouse and key- board input. We’ll describe the basic ones here; more advanced controls are described in Chapter 12. You can also download others (see Appendix E) or create your own. wxButton A wxButton is a control that looks like a physical push button with a text label, and it is one of the most common elements of a user interface. It may be placed on a dialog box or panel, or almost any other window. A command event is gen- erated when the user clicks on the button. Here’s a simple example of creating a button: #include “wx/button.h” wxButton* button = new wxButton(panel, wxID_OK, wxT(“OK”), wxPoint(10, 10), wxDefaultSize); Figure 4-12 shows how a button with the default size looks on Windows XP. Figure 4-12 A wxButton wxWidgets obtains the default button size by calling the static function wxButton::GetDefaultSize,calculated appropriately for each platform, but you can let wxWidgets size the button to just fit the label by passing the style wxBU_EXACTFIT. wxButton Styles Table 4-17 lists the specific window styles for wxButton. Table 4-17 wxButton Styles wxBU_LEFT Left-justifies the label. Windows and GTK+ only. wxBU_TOP Aligns the label to the top of the button. Windows and GTK+ only. wxBU_RIGHT Right-justifies the bitmap label. Windows and GTK+ only. wxBU_BOTTOM Aligns the label to the bottom of the button. Windows and GTK+ only. wxBU_EXACTFIT Creates the button as small as possible instead of making it the standard size. wxNO_BORDER Creates a flat button. Windows and GTK+ only.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 87 Non-Static Controls 87 wxButton Events wxButton generates a wxCommandEvent propagating event, as shown in Table 4-18. Table 4-18 wxButton Events EVT_BUTTON(id, func) Processes a wxEVT_COMMAND_BUTTON_CLICKED event, generated when the user left-clicks on a wxButton. wxButton Member Functions These are the major wxButton functions. SetLabel and GetLabel are accessors for the button label. You can use an ampersand to indicate that the following letter is a mnemonic on Windows and GTK+. SetDefault sets this button to be the default button on the parent win- dow, so pressing the Enter key activates this button. wxButton Labels You can use an ampersand in the button label to indicate that the next letter is an underlined mnemonic (or “access key”), so that the user can press that key instead of clicking on the button. The mnemonic only works on Windows and GTK+; on other platforms, the ampersand will simply be stripped from the label and ignored. On some systems, notably GTK+, standard buttons such as OK and New are displayed with special graphics in line with the native look and feel for that platform. wxWidgets maps some of its standard window identifiers to these stock buttons, but it also permits the application to substitute a custom label should the need arise. The recommended usage is as follows. When using a stock button identi- fier, and you want wxWidgets to supply the label, just supply the identifier and not the label (or an empty string for the label). For example: wxButton* button = new wxButton(this, wxID_OK); wxWidgets will substitute the correct standard label on all platforms. For example, on Windows and Mac OS X, the string “&OK” will be used. On GTK+, the stock OK button will be used. However, if you supply a label that is different from the stock label, wxWidgets will use that label. For example: wxButton* button = new wxButton(this, wxID_OK, wxT(“&Apply”));

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 88 88 Window Basics Chapter 4 This will result in the “Apply” label being displayed on all platforms, overrid- ing the standard identifier. You can get the stock button label for a given identifier with wxGetStockLabel (include wx/stockitem.h), passing the identifier, true (if you want menu codes to be included), and an optional accelerator string to append. Table 4-19 shows the stock button identifiers and their corresponding labels. Table 4-19 Stock Button Identifiers Stock Button Identifier Stock Button Label wxID_ADD "Add" wxID_APPLY "&Apply" wxID_BOLD "&Bold" wxID_CANCEL "&Cancel" wxID_CLEAR "&Clear" wxID_CLOSE "&Close" wxID_COPY "&Copy" wxID_CUT "Cu&t" wxID_DELETE "&Delete" wxID_FIND "&Find" wxID_REPLACE "Rep&lace" wxID_BACKWARD "&Back" wxID_DOWN "&Down" wxID_FORWARD "&Forward" wxID_UP "&Up" wxID_HELP "&Help" wxID_HOME "&Home" wxID_INDENT "Indent" wxID_INDEX "&Index" wxID_ITALIC "&Italic" wxID_JUSTIFY_CENTER "Centered" wxID_JUSTIFY_FILL "Justified" wxID_JUSTIFY_LEFT "Align Left" wxID_JUSTIFY_RIGHT "Align Right" wxID_NEW "&New" wxID_NO "&No" wxID_OK "&OK" wxID_OPEN "&Open" wxID_PASTE "&Paste"

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 89 Non-Static Controls 89 wxID_PREFERENCES "&Preferences" wxID_PRINT "&Print" wxID_PREVIEW "Print previe&w" wxID_PROPERTIES "&Properties" wxID_EXIT "&Quit" wxID_REDO "&Redo" wxID_REFRESH "Refresh" wxID_REMOVE "Remove" wxID_REVERT_TO_SAVED "Revert to Saved" wxID_SAVE "&Save" wxID_SAVEAS "Save &As..." wxID_STOP "&Stop" wxID_UNDELETE "Undelete" wxID_UNDERLINE "&Underline" wxID_UNDO "&Undo" wxID_UNINDENT "&Unindent" wxID_YES "&Yes" wxID_ZOOM_100 "&Actual Size" wxID_ZOOM_FIT "Zoom to &Fit" wxID_ZOOM_IN "Zoom &In" wxID_ZOOM_OUT "Zoom &Out" wxBitmapButton A bitmap button is like a normal text button, but it shows a bitmap instead of text. A command event is generated when the user clicks on the button. Here’s a simple example of creating a bitmap button: #include “wx/bmpbuttn.h” wxBitmap bitmap(wxT(“print.xpm”), wxBITMAP_TYPE_XPM); wxBitmapButton* button = new wxBitmapButton(panel, wxID_OK, bitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW); Figure 4-13 shows the result under Windows. Figure 4-13 A wxBitmapButton

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 90 90 Window Basics Chapter 4 A bitmap button can be supplied with a single bitmap (optionally with transparency information), and wxWidgets will draw all button states using this bitmap. If the application needs more control, additional bitmaps for the selected state, unpressed focused state, and grayed-out state may be supplied. XPM is a good choice of bitmap format for buttons because it supports transparency and can be included into C++ code, but you can load them from other formats too, such as JPEG, PNG, GIF, and BMP. wxBitmapButton Styles Table 4-20 lists the specific window styles for wxBitmapButton. Table 4-20 wxBitmapButton Styles wxBU_AUTODRAW If this is specified, the button will be drawn automatically using the label bitmap only, providing a 3D-look border. If this style is not specified, the button will be drawn without borders and using all provided bitmaps. Windows and Mac OS only. wxBU_LEFT Left-justifies the bitmap label. Ignored on Mac OS. wxBU_TOP Aligns the bitmap label to the top of the button. Ignored on Mac OS. wxBU_RIGHT Right-justifies the bitmap label. Ignored on Mac OS. wxBU_BOTTOM Aligns the bitmap label to the bottom of the button. Ignored on Mac OS. wxBitmapButton Events wxBitmapButton generates wxCommandEvent propagating events, identical to wxButton. wxBitmapButton Member Functions These are the major wxBitmapButton functions. SetBitmapLabel and GetBitmapLabel are accessors for the main button label bitmap. You can also use SetBitmapFocus, SetBitmapSelected, and SetBitmapDisabled and their corresponding getters for more precise control of the button in different states. SetDefault sets this button to be the default button on the parent win- dow, so pressing the Enter key will activate the button. wxChoice The choice control consists of a read-only text area that reflects the selection of a drop-down list box. The list box is hidden until the user presses a button on the control to reveal the list of strings.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 91 Non-Static Controls 91 To create a choice control, pass the usual parent window, identifier, posi- tion, size, and style parameters, plus an array of strings to populate the list. For example: #include “wx/choice.h” wxArrayString strings; strings.Add(wxT(“One”)); strings.Add(wxT(“Two”)); strings.Add(wxT(“Three”)); wxChoice* choice = new wxChoice(panel, ID_COMBOBOX, wxDefaultPosition, wxDefaultSize, strings); On most platforms, the look is similar to wxComboBox (see Figure 4-14), except that the user cannot edit the text. On GTK+, wxChoice is a button with a drop- down menu. You may like to use a read-only wxComboBox to get the benefit of the scrolling drop-down list. wxChoice Styles There are no special styles for wxChoice. wxChoice Events wxChoice generates wxCommandEvent propagating events, as shown in Table 4-21. Table 4-21 wxChoice Events EVT_CHOICE(id, func) Processes a wxEVT_COMMAND_CHOICE_SELECTED event, generated by a wxChoice control when the user selects an item in the list. wxChoice Member Functions All wxChoice functions are described by wxControlWithItems: Clear, Delete, FindString, GetClientData, GetClientObject, SetClientData, SetClientObject, GetCount, GetSelection, SetSelection, GetString, SetString, GetStringSelection, SetStringSelection, Insert, and IsEmpty. wxComboBox The combo box is a combination of a list box and a single-line text field, and it allows you to set and get the text of the text field independently of the list box. The text field can be read-only, in which case it behaves very much like wxChoice. Normally, the list box is hidden until the user presses a button on the control to reveal the list of strings. This makes for a very compact way of allowing the user to enter text and also to choose from a list of existing options.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 92 92 Window Basics Chapter 4 To create a combo box, pass the usual parent window, identifier, position, size, and style parameters, plus the initial text and an array of strings to pop- ulate the list. For example: #include “wx/combobox.h” wxArrayString strings; strings.Add(wxT(“Apple”)); strings.Add(wxT(“Orange”)); strings.Add(wxT(“Pear”)); strings.Add(wxT(“Grapefruit”)); wxComboBox* combo = new wxComboBox(panel, ID_COMBOBOX, wxT(“Apple”), wxDefaultPosition, wxDefaultSize, strings, wxCB_DROPDOWN); The result on Windows is shown in Figure 4-14 with the drop-down list activated. Figure 4-14 A wxComboBox wxComboBox Styles Table 4-22 lists the specific window styles for wxComboBox. Table 4-22 wxComboBox Styles wxCB_SIMPLE Creates a combo box with a permanently displayed list. Windows only. wxCB_DROPDOWN Creates a combo box with a drop-down list. wxCB_READONLY Same as wxCB_DROPDOWN but only the strings specified as the combo box choices can be selected, and it is impossible to select a string that is not in the choices list, even from application code. wxCB_SORT Creates a combo box whose items are always sorted alphabetically.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 93 Non-Static Controls 93 wxComboBox Events wxComboBox generates wxCommandEvent propagating events, described in Table 4-23. Table 4-23 wxComboBox Events EVT_TEXT(id, func) Processes a wxEVT_COMMAND_TEXT_UPDATED event, generated by the wxComboBox control when its text is edited. EVT_COMBOBOX(id, func) Processes a wxEVT_COMMAND_COMBOBOX_SELECTED event, generated by a wxComboBox control when the user selects an item in the list. wxComboBox Member Functions These are the major wxComboBox functions. Please refer also to the wxControlWithItems member functions from earlier in this chapter. Copy copies the selected text onto the clipboard from the text field. Cut does the same, and it also deletes the selected text. Paste copies text from the clipboard into the text field. GetInsertionPoint returns the insertion point for the combo box’s text field (a long integer representing the position), and SetInsertionPoint sets it. Use SetInsertionPointEnd to set the insertion point at the end of the text field. GetLastPosition returns the last position in the text field. GetValue returns the value of the text field, and SetValue sets it. For a combo box with the wxCB_READONLY style, the string must be in the combo box choices list; otherwise, the call is ignored in release mode, and it displays an alert in debug mode. SetSelection with two arguments selects the text in the combo box text field between two given positions. Replace replaces the text between two given positions with specified text. Remove removes the text between two given posi- tions. See also the following functions from wxControlWithItems: Clear, Delete, FindString, GetClientData, GetClientObject, SetClientData, SetClientObject, GetCount, GetSelection, SetSelection, GetString, SetString, GetStringSelection, SetStringSelection, Insert, and IsEmpty. wxCheckBox A check box is a control that normally has two states: on or off. It is repre- sented by a box containing a cross or tick if checked, with a label to the left or right of the check box. Optionally, it can have a third state, called the mixed or undetermined state, which can be used to indicate that the item does not apply (for example, a component in an installer that is always installed and therefore cannot be selected or deselected).

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 94 94 Window Basics Chapter 4 Here’s a simple example of creating a check box: #include “wx/checkbox.h” wxCheckBox* checkbox = new wxCheckBox(panel, ID_CHECKBOX, wxT(“&Check me”), wxDefaultPosition, wxDefaultSize); checkBox->SetValue(true); Figure 4-15 shows how this looks on Windows. Figure 4-15 A wxCheckBox A check box with the wxCHK_3STATE style looks like Figure 4-16 on Windows. Figure 4-16 A three-state wxCheckBox wxCheckBox Styles Table 4-24 lists the specific window styles for wxCheckBox. Table 4-24 wxCheckBox Styles wxCHK_2STATE Create a two-state check box. This is the default. wxCHK_3STATE Create a three-state check box. wxCHK_ALLOW_3RD_STATE_FOR_USER By default, a user can’t set a three-state check box to the third state. It can only be done from code. Using this style enables the user to set the check box to the third state by clicking. wxALIGN_RIGHT Makes the check box appear to the right of the label.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 95 Non-Static Controls 95 wxCheckBox Events wxCheckBox generates wxCommandEvent propagating events, described in Table 4-25. Table 4-25 wxCheckBox Events EVT_CHECKBOX(id, func) Processes a wxEVT_COMMAND_CHECKBOX_ CLICKED event, generated when the user checks or unchecks a wxCheckBox control. wxCheckBox Member Functions These are the major wxCheckBox functions. SetLabel and GetLabel are accessors for the check box label. You can use an ampersand to indicate that the following letter is the mnemonic (or “access key”) on Windows and GTK+. GetValue and SetValue get and set the boolean state. Use Get3StateValue or Set3StateValue to get and set one of wxCHK_UNCHECKED, wxCHK_CHECKED, or wxCHK_UNDETERMINED. Is3State can be used to determine whether the check box is a three-state check box. IsChecked returns true if the check box is checked. wxListBox and wxCheckListBox A wxListBox is used to select one or more of a list of strings, numbered from zero. The strings are displayed in a scrolling box, with the selected strings marked in reverse video. A list box can be single-selection: if an item is selected, the previous selection is removed. In a multiple-selection list box, clicking an item toggles the item on or off independently of other selections. Here’s an example of creating a single-selection list box: #include “wx/listbox.h” wxArrayString strings; strings.Add(wxT(“First string”)); strings.Add(wxT(“Second string”)); strings.Add(wxT(“Third string”)); strings.Add(wxT(“Fourth string”)); strings.Add(wxT(“Fifth string”)); strings.Add(wxT(“Sixth string”)); wxListBox* listBox = new wxListBox(panel, ID_LISTBOX, wxDefaultPosition, wxSize(180, 80), strings, wxLB_SINGLE);

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 96 96 Window Basics Chapter 4 Figure 4-17 shows what this looks like under Windows. Figure 4-17 A wxListBox wxCheckListBox is derived from wxListBox and inherits its functionality, but in addition, it can display a check box next to each item label. Include wx/checklst.h to use this class. Figure 4-18 shows a wxCheckListBox on Windows. Figure 4-18 A wxCheckListBox If you have a lot of items to display, consider using wxVListBox. This is a virtual list box that displays data directly from a source that you specify by deriving a new class and implementing the functions OnDrawItem and OnMeasureItem. Its event macros are the same as for wxListBox. wxHtmlListBox is derived from wxVListBox and offers an easy way to dis- play complex items. wxHtmlListBox obtains HTML fragments from the OnGetItem function, which your derived class must override. Figure 4-19 shows the wxWidgets wxHtmlListBox sample (in samples/htlbox), with custom separa- tors drawn by an overridden OnDrawSeparator function.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 97 Non-Static Controls 97 Figure 4-19 The wxHtmlListBox sample wxListBox and wxCheckListBox Styles Table 4-26 lists the specific window styles for wxListBox and wxCheckListBox. Table 4-26 wxListBox and wxCheckListBox Styles wxLB_SINGLE Single-selection list. wxLB_MULTIPLE Multiple-selection list: the user can toggle multiple items on and off. wxLB_EXTENDED Extended-selection list: the user can select multiple items using the Shift key and the mouse or special key combinations. wxLB_HSCROLL Create a horizontal scrollbar if contents are too wide. Windows only. wxLB_ALWAYS_SB Always show a vertical scrollbar. wxLB_NEEDED_SB Only create a vertical scrollbar if needed. wxLB_SORT The list box contents are sorted in alphabetical order. wxListBox and wxCheckListBox Events wxListBox and wxCheckListBox generate wxCommandEvent propagating events, described in Table 4-27.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 98 98 Window Basics Chapter 4 Table 4-27 wxListBox Events EVT_LISTBOX(id, func) Processes a wxEVT_COMMAND_LISTBOX_SELECTED event, generated by a wxListBox control when the user selects an item in the list. EVT_LISTBOX_DCLICK(id, func) Processes a wxEVT_COMMAND_LISTBOX_ DOUBLECLICKED event, generated by a wxListBox control when the user double-clicks on an item in the list. EVT_CHECKLISTBOX (id, func) Processes a wxEVT_COMMAND_CHECKLISTBOX_ TOGGLED event, generated by a wxCheckListBox control when the user checks or unchecks an item. wxListBox Member Functions These are the major wxListBox functions. Deselect deselects an item in the list box. GetSelections fills a wxArrayInt array with the positions of the currently selected items and returns it. InsertItems inserts the given number of strings before the specified posi- tion. Pass either the number of items, a C++ array of wxStrings, and the inser- tion position, or a wxArrayString object and the insertion position. Selected returns true if the given item is selected. Set clears the list box and adds the given strings to it. Pass either the number of items, a C++ array of wxStrings, and an optional array of void* client data, or a wxArrayString object and an optional array of void* client data. SetFirstItem sets the specified item to be the first visible item. SetSelection and SetStringSelection take an integer or string item and an optional boolean for the selection state, defaulting to true. See also the wxControlWithItems functions: Clear, Delete, FindString, GetClientData, GetClientObject, SetClientData, SetClientObject, GetCount, GetSelection, GetString, SetString, GetStringSelection, Insert, and IsEmpty. wxCheckListBox Member Functions In addition to wxListBox’s functions, wxCheckListBox has the following functions. Check takes an item index and boolean and checks or unchecks the item. IsChecked returns true if the given item is checked, and false otherwise. wxRadioBox A radio box is used to select an item from a number of mutually exclusive but- tons. It is displayed as a vertical column or horizontal row of labeled buttons, within a static box, which may have a label.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 99 Non-Static Controls 99 The way that the buttons are laid out depends on two constructor param- eters: the major dimension, and the orientation style, which can be wxRA_ SPECIFY_COLS (the default) or wxRA_SPECIFY_ROWS. The major dimension is the number of rows or columns. For example, eight buttons laid out with a major dimension of two and the wxRA_SPECIFY_COLS style will have two columns and four rows. Changing to wxRA_SPECIFY_ROWS will give the radio box two rows and four columns. Here’s an example of creating a radio box with three columns: #include “wx/radiobox.h” wxArrayString strings; strings.Add(wxT(“&One”)); strings.Add(wxT(“&Two”)); strings.Add(wxT(“T&hree”)); strings.Add(wxT(“&Four “)); strings.Add(wxT(“F&ive “)); strings.Add(wxT(“&Six “)); wxRadioBox* radioBox = new wxRadioBox(panel, ID_RADIOBOX, wxT(“Radiobox”), wxDefaultPosition, wxDefaultSize, strings, 3, wxRA_SPECIFY_COLS); The constructor specifies that the buttons should be laid out in three columns. On Windows, this produces the result shown in Figure 4-20. Figure 4-20 A wxRadioBox wxRadioBox Styles wxRadioBox can have the window styles listed in Table 4-28 in addition to those described for wxWindow. Specifying a different major dimension changes the but- ton ordering. Table 4-28 wxRadioBox Styles wxRA_SPECIFY_ROWS The major dimension parameter refers to the maximum number of rows. wxRA_SPECIFIY_COLS The major dimension parameter refers to the maximum number of columns.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 100 100 Window Basics Chapter 4 wxRadioBox Events wxRadioBox generates wxCommandEvent propagating events, as shown in Table 4-29. Table 4-29 wxRadioBox Events EVT_RADIOBOX(id, func) Processes a wxEVT_COMMAND_RADIOBOX_ SELECTED event, generated by a wxRadioBox control when the user clicks on a radio button. wxRadioBox Member Functions These are the major wxRadioBox functions. Enable with an index and a boolean enables or disables a specified button. FindString returns the index of a button matching the given string, or wxNOT_FOUND if no match was found. GetCount returns the number of buttons in the radio box. GetString and SetString are accessors for the label of the specified but- ton. GetLabel and SetLabel set the radio box label. GetSelection returns the zero-based index of the selected radio button. GetStringSelection returns the label of the selected button. SetSelection and SetStringSelection set the selection without generating a command event. Show shows or hides an individual button or the whole radio box. wxRadioButton A radio button usually denotes one of several mutually exclusive options. It has a text label next to a button, which is normally round in appearance. It has two states: on or off. You can create a group of mutually exclusive radio buttons by specifying wxRB_GROUP for the first in the group. The group ends when another radio button group is created, or when there are no more con- trols. You can also create other types of control within a group. You might use a group of radio buttons instead of a radio box when the layout is slightly more complex: for example, you may have an extra descrip- tion or other control next to each radio button. Or you may use radio buttons simply to avoid the static box that wxRadioBox provides. Figure 4-21 A pair of radio buttons

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 101 Non-Static Controls 101 Here’s a simple example of a group of two radio buttons. #include “wx/radiobut.h” wxRadioButton* radioButton1 = new wxRadioButton (panel, ID_RADIOBUTTON1, wxT(“&Male”), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); radioButton1->SetValue(true); wxRadioButton* radioButton2 = new wxRadioButton (panel, ID_RADIOBUTTON2, wxT(“&Female”)); // Sizer code to group the buttons horizontally wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(radioButton1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); sizer->Add(radioButton2, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); parentSizer->Add(sizer, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); On Windows, this will create the controls shown in Figure 4-22. wxRadioButton Styles Table 4-30 lists the specific window styles for wxRadioButton. Table 4-30 wxRadioButton Styles wxRB_GROUP Marks the beginning of a new group of radio buttons. wxRB_USE_CHECKBOX Displays a check box button instead of a radio button (Palm OS only). wxRadioButton Events wxRadioButton generates wxCommandEvent propagating events, which are described in Table 4-31. Table 4-31 wxRadioButton Events EVT_RADIOBUTTON(id, func) Processes a wxEVT_COMMAND_ RADIOBUTTON_ SELECTED event, generated by a wxRadioButton control when a user clicks on it. wxRadioButton Member Functions GetValue and SetValue get and set the boolean state. wxScrollBar A wxScrollBar is a control that represents a horizontal or vertical scrollbar. It is distinct from the two scrollbars that some windows provide automatically,

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 102 102 Window Basics Chapter 4 but the two types of scrollbar share the way events are received. A scrollbar has the following main attributes: range, thumb size, page size, and position. The range is the total number of units associated with the view repre- sented by the scrollbar. For a table with 15 columns, the range would be 15. The thumb size is the number of units that are currently visible. For the table example, the window might be sized so that only 5 columns are currently visible, in which case the application would set the thumb size to 5. When the thumb size becomes the same as or greater than the range, the scrollbar will automatically be hidden on most platforms. The page size is the number of units that the scrollbar should scroll when paging through the data. The scrollbar position is the current thumb position. To create a scrollbar control, pass the usual parent window, identifier, position, size, and style parameters. For example: #include “wx/scrolbar.h” wxScrollBar* scrollBar = new wxScrollBar(panel, ID_SCROLLBAR, wxDefaultPosition, wxSize(200, 20), wxSB_HORIZONTAL); Under Windows, this will look like the control in Figure 4-22. Figure 4-22 A wxScrollBar After creation, call SetScrollbar to set its properties. For more informa- tion on using this function, see the description of wxScrolledWindow earlier in this chapter. wxScrollBar Styles Table 4-32 lists the specific window styles for wxScrollBar. Table 4-32 wxScrollBar Styles wxSB_HORIZONTAL Specifies a horizontal scrollbar. wxSB_VERTICAL Specifies a vertical scrollbar. wxScrollBar Events wxScrollBar generates wxScrollEvent propagating events. You can use EVT_ COMMAND_SCROLL... macros with window identifiers when intercepting scroll events from controls, or EVT_SCROLL... macros without window identifiers for intercepting scroll events from the receiving window—except for this, the

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 103 Non-Static Controls 103 macros behave exactly the same. Use EVT_SCROLL(func) to respond to all scroll events. For a comprehensive list of scroll event macros, please see Table I-1 in Appendix I, “Event Classes and Macros,” and also see the reference manual. wxScrollBar Member Functions These are the major wxScrollBar functions. GetRange returns the length of the scrollbar. GetPageSize returns the number of scroll units that will be scrolled when the user pages up or down. Often it is the same as the thumb size. GetThumbPosition and SetThumbPosition are accessors for the current posi- tion of the scrollbar thumb. GetThumbLength returns the thumb or “view” size. SetScrollbar sets the scrollbar properties. It takes the position in scroll units, thumb size, range, page size, and optional boolean to specify whether the control will be refreshed. wxSpinButton wxSpinButton has two small up and down (or left and right) arrow buttons. It is often used next to a text control for incrementing and decrementing a value. Portable programs should try to use wxSpinCtrl instead as wxSpinButton is not implemented for all platforms. The range supported by this control (and wxSpinCtrl) depends on the platform but is at least -32768 to 32767. To create a wxSpinButton control, pass the usual parent window, identifier, position, size, and style parameters. For example: #include “wx/spinbutt.h” wxSpinButton* spinButton = new wxSpinButton(panel, ID_SPINBUTTON, wxDefaultPosition, wxDefaultSize, wxSP_VERTICAL); On Windows, the result is the control shown in Figure 4-23. Figure 4-23 A wxSpinButton

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 104 104 Window Basics Chapter 4 wxSpinButton Styles Table 4-33 lists the specific window styles for wxSpinButton. Table 4-33 wxSpinButton Styles wxSP_HORIZONTAL Specifies a horizontal spin button. This style is not sup- ported in wxGTK. wxSP_VERTICAL Specifies a vertical spin button. wxSP_ARROW_KEYS The user can use arrow keys to change the value. wxSP_WRAP The value wraps at the minimum and maximum. wxSpinButton Events wxSpinButton generates wxSpinEvent propagating events, as shown in Table 4-34. Table 4-34 wxSpinButton Events EVT_SPIN(id, func) Handles a wxEVT_SCROLL_THUMBTRACK event, generated whenever the up or down arrows are clicked. EVT_SPIN_UP(id, func) Handles a wxEVT_SCROLL_LINEUP event, generated when the up arrow is clicked. EVT_SPIN_DOWN(id, func) Handles a wxEVT_SCROLL_LINEDOWN event, generated when the down arrow is clicked. wxSpinButton Member Functions These are the major wxSpinButton functions. GetMax returns the maximum permissible value. GetMin returns the minimum permissible value. GetValue returns the current spin button value, and SetValue sets the cur- rent spin value. SetRange sets the minimum and maximum values. wxSpinCtrl wxSpinCtrl combines a wxTextCtrl and a wxSpinButton into one control. When you click on the up and down arrow buttons, the value displayed in the text control will be incremented or decremented, and you can also type integers directly into the text control.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 105 Non-Static Controls 105 To create a wxSpinCtrl control, pass the usual parent window, identifier, position, size, and style parameters. The following code creates a spin control with a range of zero to 100 and an initial value of 5. #include “wx/spinctrl.h” wxSpinCtrl* spinCtrl = new wxSpinCtrl(panel, ID_SPINCTRL, wxT(“5”), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, 5); On Windows, this will look like the control in Figure 4-24. Figure 4-24 A wxSpinCtrl wxSpinCtrl Styles Table 4-35 lists the specific window styles for wxSpinCtrl. Table 4-35 wxSpinCtrl Styles wxSP_ARROW_KEYS The user can use arrow keys to change the value. wxSP_WRAP The value wraps at the minimum and maximum. wxSpinCtrl Events wxSpinCtrl generates wxSpinEvent propagating events as shown in Table 4-36. You can also use EVT_TEXT to intercept text updates with a wxCommandEvent handler. Table 4-36 wxSpinCtrl Events EVT_SPIN(id, func) Handles a wxEVT_SCROLL_THUMBTRACK event, gener- ated whenever the up or down arrow is clicked. EVT_SPIN_UP(id, func) Handles a wxEVT_SCROLL_LINEUP event, generated when the up arrow is clicked. EVT_SPIN_DOWN(id, func) Handles a wxEVT_SCROLL_LINEDOWN event, gener- ated when the down arrow is clicked. EVT_SPINCTRL(id, func) Handles all events generated for the wxSpinCtrl.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 106 106 Window Basics Chapter 4 wxSpinCtrl Member Functions These are the major wxSpinCtrl functions. GetMax returns the maximum permissible value. GetMin returns the minimum permissible value. GetValue returns the current integer spin button value, and SetValue sets the current spin value. SetRange sets the minimum and maximum values. wxSlider A slider is a control with a handle that can be moved back and forth to change the value. To create a wxSlider control, pass the usual parent window, identifier, position, size, and style parameters. The following code creates a slider control with a range of zero to 40 and an initial position of 16. #include “wx/slider.h” wxSlider* slider = new wxSlider(panel, ID_SLIDER, 16, 0, 40, wxDefaultPosition, wxSize(200, -1), wxSL_HORIZONTAL|wxSL_AUTOTICKS|wxSL_LABELS); On Windows, this creates the control shown in Figure 4-25. Figure 4-25 A wxSlider wxSlider Styles Table 4-37 lists the specific window styles for wxSlider. Table 4-37 wxSlider Styles wxSL_HORIZONTAL Displays the slider horizontally. wxSL_VERTICAL Displays the slider vertically. wxSL_AUTOTICKS Displays tick marks. wxSL_LABELS Displays minimum, maximum, and value labels. wxSL_LEFT Displays ticks on the left if it’s a vertical slider. wxSL_RIGHT Displays ticks on the right if it’s a vertical slider. wxSL_TOP Displays ticks on the top if it’s a horizontal slider. The default is to display them along the bottom. wxSL_SELRANGE Enables the user to select a range on the slider. Windows only.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 107 Non-Static Controls 107 wxSlider Events wxSlider generates wxCommandEvent propagating events, as shown in Table 4-38, but if you need finer control, you can use EVT_COMMAND_SCROLL_... with wxScrollEvent handlers; see Table I-1 in Appendix I. Table 4-38 wxSlider Events EVT_SLIDER(id, func) Processes a wxEVT_COMMAND_SLIDER_UPDATED event, generated by a wxSlider control when the user moves the slider. wxSlider Member Functions These are the major wxSlider functions. ClearSel clears the selection for a slider with wxSL_SELRANGE on Windows. ClearTicks clears the ticks on Windows. GetLineSize and SetLineSize are accessors for the number of units incre- mented or decremented when the arrow buttons are clicked. GetPageSize and SetPageSize are accessors for the number of units paged when clicking either side of the thumb. GetMax returns the maximum permissible value. GetMin returns the minimum permissible value. GetSelEnd and GetSelStart return the selection end and start points; use SetSelection to set the selection. These functions are only implemented on Windows. GetThumbLength and SetThumbLength are accessors for the slider thumb size. GetTickFreq and SetTickFreq are accessors for the tick frequency on Windows. Use SetTick to set a tick position on Windows. GetValue returns the current slider value, and SetValue sets the slider value. SetRange sets the minimum and maximum values. wxTextCtrl The text control enables text to be displayed and edited, either as a single-line or a multi-line control. Some simple styling and formatting is supported on some platforms (Windows, GTK+, and Mac OS X via setting and getting text attributes using the wxTextAttr class.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 108 108 Window Basics Chapter 4 To create a text control, pass the usual parent window, identifier, posi- tion, size, and style parameters, plus the initial text. For example, to create a multi-line text control: #include “wx/textctrl.h” wxTextCtrl* textCtrl = new wxTextCtrl(panel, ID_TEXTCTRL, wxEmptyString, wxDefaultPosition, wxSize(240, 100), wxTE_MULTILINE); On Windows, this will create the control shown in Figure 4-26. Figure 4-26 A multiline wxTextCtrl Multi-line text controls always store text as a sequence of lines separated by \n characters, using Unix newlines even on non-Unix platforms. As a result, you can ignore the differences between platforms, but at a price: indices such as those returned by GetInsertionPoint or GetSelection cannot be used as indices into the string returned by GetValue as they’re going to be slightly off for platforms using \r\n as the separator as Windows does. Instead, if you need to obtain a substring between the two indices obtained from the control with the help of the functions mentioned previously, you should use GetRange. The indices themselves can only be passed to other methods, such as SetInsertionPoint or SetSelection. Never use the indices returned by multi-line text controls as indices into the string it contains, but only as arguments to be passed back to other wxTextCtrl methods. Multi-line text controls support setting styles: you can set colors and fonts for individual characters. Note that under Windows, the wxTE_RICH style is required for style support. To use the styles, you can either call SetDefaultStyle before inserting the text or call SetStyle later to change the style of the text already in the control. The first method is much more efficient. In either case, if the style doesn’t specify some of the attributes, the val- ues of the default style will be used. If there is no default style, the attributes of the text control itself are used.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 109 Non-Static Controls 109 In the following code, the second call to SetDefaultStyle doesn’t change the text foreground color (which stays red), while the last one doesn’t change the background color (which stays gray): text->SetDefaultStyle(wxTextAttr(*wxRED)); text->AppendText(wxT(“Red text\n”)); text->SetDefaultStyle(wxTextAttr(wxNullColour, *wxLIGHT_GREY)); text->AppendText(wxT(“Red on gray text\n”)); text->SetDefaultStyle(wxTextAttr(*wxBLUE); text->AppendText(wxT(“Blue on gray text\n”)); wxTextCtrl Styles Table 4-39 lists the specific window styles for wxTextCtrl. Table 4-39 wxTextCtrl Styles wxTE_PROCESS_ENTER The control will generate the event wxEVT_ COMMAND_TEXT_ENTER. Otherwise, pressing the Enter key is either processed internally by the con- trol or used for navigation between dialog controls. wxTE_PROCESS_TAB The control will receive wxEVT_CHAR events when Tab is pressed—normally, the Tab key is used for passing to the next control in a dialog instead. wxTE_MULTILINE The text control supports multiple lines. wxTE_PASSWORD Text will be echoed as asterisks. wxTE_READONLY The text will not be user-editable. wxTE_RICH Uses a rich text control under Windows. This enables the control to store more than 64KB of text in the control; the vertical scrollbar is only shown when needed. This style is ignored under other platforms. wxTE_RICH2 Uses a rich text control version 2.0 or 3.0 under Windows; the vertical scrollbar is always shown. This style is ignored on other platforms. wxTE_AUTO_URL Highlight URLs and generate wxTextUrlEvents when mouse events occur over them. On Windows this requires wxTE_RICH. Windows and GTK+ only. wxTE_NOHIDESEL By default, the Windows text control doesn’t show the selection when it doesn’t have focus—use this style to force it to always show the selection. Ignored under other platforms. wxHSCROLL A horizontal scrollbar will be created and used so that text won’t be wrapped. No effect under GTK+. wxTE_LEFT The text in the control will be left justified (the default). wxTE_CENTRE The text in the control will be centered. wxTE_RIGHT The text in the control will be right justified. (continues)

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 110 110 Window Basics Chapter 4 Table 4-39 wxTextCtrl Styles (Continued) wxTE_DONTWRAP Same as the wxHSCROLL style. wxTE_LINEWRAP Wrap lines that are too long to be shown entirely at any position. Currently only supported under wxUniversal. wxTE_WORDWRAP Wrap lines that are too long to be shown entirely at word boundaries only. Currently only supported under wxUniversal. wxTE_NO_VSCROLL Removes the vertical scrollbar. No effect on GTK+. wxTextCtrl Events wxTextCtrl generates wxCommandEvent propagating events, as described in Table 4-40. Table 4-40 wxTextCtrl Events EVT_TEXT(id, func) Processes a wxEVT_COMMAND_TEXT_UPDATED event, generated when the text is changed. EVT_TEXT_ENTER(id, func) Processes a wxEVT_COMMAND_TEXT_ENTER event, generated when the user presses the Enter key. Note that you must use wxTE_PROCESS_ ENTER style when creating the control if you want it to generate such events. EVT_TEXT_MAXLEN(id, func) Processes a wxEVT_COMMAND_TEXT_MAXLEN event, generated when the user tries to enter more characters into it than the limit previ- ously set with SetMaxLength. Windows and GTK+ only. wxTextCtrl Member Functions These are the major wxTextCtrl functions. AppendText appends the given text to the end of the text control, and WriteText writes the text at the current insertion point. SetValue clears and then sets the value, after which IsModified returns false. You can pass strings with newlines for a multi-line text control. Be aware that these functions send text update events. GetValue returns the entire contents of the control, possibly with new- lines for a multi-line control. GetLineText gets just one line from a multi-line control. GetRange gets the text between two positions. Copy copies the selected text onto the clipboard from the text field. Cut does the same, and it also deletes the selected text. Paste copies text from the clipboard into the text field. You can use CanCopy, CanCut, and CanPaste in UI update event handlers.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 111 Non-Static Controls 111 Clear clears the text in the control. Note that this will generate a text update event. DiscardEdits resets the internal “modified” flag as if the current edits had been saved. EmulateKeyPress inserts the character that would have been inserted if the given key event had occurred in the text control. GetDefaultStyle and SetDefaultStyle are accessors for the font currently used for new text. GetStyle returns the style at the given position in the text, and SetStyle sets the style for the given range. GetInsertionPoint and SetInsertionPoint get and set the current inser- tion point for new text. GetLastPosition returns the last position in the control, and SetInsertionPointEnd sets the insertion point at the end of the text. GetLineLength returns the length of the specified line in characters. GetNumberOfLines returns the number of lines of text in the control. GetStringSelection returns the text currently selected in the control, or an empty string if there is no selection. GetSelection returns the current selec- tion span in two pointers to long integers. SetSelection selects the text range indicated by two long integers representing positions in the text. IsEditable returns true if the contents may be edited. Call SetEditable to make the control read-only or writeable. IsModified returns true if the user has modified the text. IsMultiline returns true if the control is multi-line. LoadFile loads text from a file into the control, and SaveFile saves the contents as a file. PositionToXY converts a pixel position to character position and line num- ber, whereas XYToPosition goes the other way. Remove removes the text in the given span. Replace replaces the text in the given span. ShowPosition makes the line containing the given position visible. Undo undoes the last edit, and Redo redoes the last edit. This may do noth- ing on some platforms. You can use CanUndo and CanRedo to test whether these operations can be applied to the control’s text (but not whether the platform supports undo/redo). wxToggleButton wxToggleButton is a button that stays pressed when clicked. In other words, it is similar to wxCheckBox in functionality but looks like a wxButton. Here’s a simple example of creating a toggle button: #include “wx/tglbtn.h” wxToggleButton* toggleButton = new wxToggleButton(panel, ID_TOGGLE, wxT(“&Toggle label”), wxDefaultPosition, wxDefaultSize); toggleButton->SetValue(true);

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 112 112 Window Basics Chapter 4 Figure 4-27 shows how a toggle button looks on Windows in the toggled state. Figure 4-27 A wxToggleButton wxToggleButton Styles There are no specific wxToggleButton styles. wxToggleButton Events wxToggleButton generates wxCommandEvent propagating events, described in Table 4-41. Table 4-41 wxToggleButton Events EVT_TOGGLEBUTTON(id, func) Processes a wxEVT_COMMAND_TOGGLEBUTTON_CLICKED event, generated when the user clicks the button. wxToggleButton Member Functions These are the major wxToggleButton functions. SetLabel and GetLabel are accessors for the button label. You can use an ampersand to indicate that the following letter is the mnemonic (or “access key”), used on Windows and GTK+. GetValue and SetValue get and set the boolean state. STATIC CONTROLS Static controls do not take any input and are used to display information or to enhance the application’s aesthetics. wxGauge This is a horizontal or vertical bar that shows a quantity (often time) from zero to the specified range. No command events are generated for the gauge. Here’s a simple example of creating a gauge: #include “wx/gauge.h” wxGauge* gauge = new wxGauge(panel, ID_GAUGE, 200, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL); gauge->SetValue(50);

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 113 Static Controls 113 Under Windows, this is displayed as shown in Figure 4-28. Figure 4-28 A wxGauge wxGauge Styles Table 4-42 lists the specific window styles for wxGauge. Table 4-42 wxGauge Styles wxGA_HORIZONTAL Creates a horizontal gauge. wxGA_VERTICAL Creates a vertical gauge. wxGA_SMOOTH Creates a smooth progress bar with no spaces between steps. This is only supported on Windows. wxGauge Events Because it only displays information, wxGauge does not generate events. wxGauge Member Functions These are the major wxGauge functions. GetRange and SetRange are accessors for the gauge range (the maximum integer value). GetValue and SetValue get and set the integer value of the gauge. IsVertical returns true if the gauge is vertical, and false if horizontal. wxStaticText A static text control displays one or more lines of read-only text. To create a wxStaticText control, pass a parent window, identifier, label, position, size, and style. For example: #include “wx/stattext.h” wxStaticText* staticText = new wxStaticText(panel, wxID_STATIC, wxT(“This is my &static label”), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 114 114 Window Basics Chapter 4 Under Windows, this creates the control shown in Figure 4-29. Figure 4-29 A wxStaticText An ampersand in the label (as shown here) indicates to some platforms— currently Windows and GTK+—that the following letter should be under- scored and used as a shortcut key for navigating to the next non-static control. wxStaticText Styles Table 4-43 lists the specific window styles for wxStaticText. Table 4-43 wxStaticText Styles wxALIGN_LEFT Aligns the text to the left. wxALIGN_RIGHT Aligns the text to the right. wxALIGN_CENTRE Centers the text horizontally. wxALIGN_CENTER wxST_NO_AUTORESIZE By default, the control will adjust its size to exactly fit the size of the text when SetLabel is called. If this style is given, the control will not change its size. This style is especially useful with controls that also have wxALIGN_ RIGHT or wxALIGN_CENTER because otherwise they won’t make sense any longer after a call to SetLabel. wxStaticText Member Functions GetLabel and SetLabel are accessors for the text label. wxStaticBitmap A static bitmap control displays an image. To create a wxStaticBitmap control, pass a parent window, identifier, bitmap, position, size and style. For example: #include “wx/statbmp.h” #include “print.xpm” wxBitmap bitmap(print_xpm); wxStaticBitmap* staticBitmap = new wxStaticBitmap(panel, wxID_STATIC, bitmap);

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 115 Static Controls 115 This produces a simple image on the panel or dialog as shown in Figure 4-30. Figure 4-30 A wxStaticBitmap wxStaticBitmap Styles There are no special styles for wxStaticBitmap. wxStaticBitmap Member Functions GetBitmap and SetBitmap are accessors for the bitmap label. wxStaticLine This control displays a horizontal or vertical line, to be used as a separator in dialogs. Here’s an example of creating a wxStaticLine control: #include “wx/statline.h” wxStaticLine* staticLine = new wxStaticLine(panel, wxID_STATIC, wxDefaultPosition, wxSize(150, -1), wxLI_HORIZONTAL); Figure 4-31 shows what a horizontal static line looks like under Windows. Figure 4-31 A wxStaticLine wxStaticLine Styles Table 4-44 lists the specific window styles for wxStaticLine. Table 4-44 wxStaticLine Styles wxLI_HORIZONTAL Creates a horizontal line. wxLI_VERTICAL Creates a vertical line.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 116 116 Window Basics Chapter 4 wxStaticLine Member Functions IsVertical returns true if the line is vertical, false otherwise. wxStaticBox This control is a rectangle drawn around other controls to denote a logical grouping of items, with an optional text label. At present, the control should not be used as a parent for further controls; the controls that it surrounds are actually siblings of the box and should be created after it but with the same parent as the box. Future versions of wxWidgets may allow contained con- trols to be either siblings or children. Here’s an example of creating a wxStaticBox control: #include “wx/statbox.h” wxStaticBox* staticBox = new wxStaticBox(panel, wxID_STATIC, wxT(“&Static box”), wxDefaultPosition, wxSize(100, 100)); This will look like the control in Figure 4-32 under Windows. Figure 4-32 A wxStaticBox wxStaticBox Styles There are no special styles for wxStaticBox. wxStaticBox Member Functions Use GetLabel and SetLabel to get and set the static box text. MENUS In this section, we’ll describe programming with wxMenu, a simple way to pres- ent commands without taking up a lot of display space. In the next section, we’ll look at how menus are used in menu bars.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 117 Menus 117 wxMenu A menu is a list of commands that pops up either from a menu bar or on an arbitrary window, often as a “context menu” invoked by clicking the right mouse button (or equivalent). A menu item can be a normal command, or it can have a check or radio button next to the label. A menu item in a disabled state won’t respond to commands. A special kind of menu item can display a visual indication of a further pull-right menu, and this can be nested to an arbitrary level. Another kind of menu item is the separator, which simply dis- plays a line or space to indicate separation between two groups of items. Figure 4-33 shows a typical menu with normal, check, and radio items and a submenu. Figure 4-33 A typical menu The example shows the use of both mnemonics and shortcuts. A mnemonic is a highlighted key in a label (such as the “N” in “New”) that can be pressed when the menu is shown to execute that command. Specify a mnemonic by preceding the letter with an ampersand (“&”). A shortcut (or accelerator) is a key combination that can be used when the menu is not shown, and it is indicated in a menu item by a key combination following a tab character. For example, the New menu item in the example was created with this code: menu->Append(wxID_NEW, wxT(“&New...\tCtrl+N”)); For more on creating accelerators via menu items or by programming with wxAcceleratorTable, please see Chapter 6. Check and radio items automatically update their state; that is, when the user toggles a check item, it will be shown in the reverse state when the menu is next popped up. Similarly, consecutive radio items form a group and when one item is checked, the other items in the group are unchecked. You can also set these states yourself, for example from a user interface update event handler (see Chapter 9).

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 118 118 Window Basics Chapter 4 You can create a menu and show it at a particular point in a window using wxWindow::PopupMenu, for example: void wxWindow::OnRightClick(wxMouseEvent& event) { if (!m_menu) { m_menu = new wxMenu; m_menu->Append(wxID_OPEN, wxT(“&Open”)); m_menu->AppendSeparator(); m_menu->Append(wxID_EXIT, wxT(“E&xit”)); } PopupMenu(m_menu, event.GetPosition()); } Events are sent to the menu itself before travelling up the hierarchy of win- dows starting from the window the popup menu was shown on. PopupMenu will cause program flow to “block” at this point, resuming when the user has dis- missed the menu. If you want, you can delete and re-create the menu every time it needs to be shown, or you can reuse the same menu. Where possible, use standard wxWidgets identifiers in menus, such as wxID_OPEN, wxID_ABOUT, wxID_PRINT, and so on. You can find a full list of these in Chapter 3. In particular, wxID_ABOUT, wxID_PREFERENCES and wxID_EXIT are inter- preted specially on Mac OS X. When used in a menu bar, these menu items are not shown in the menus to which they are appended, but are shown instead in the standard application menu. Thus wxWidgets adapts your menus automat- ically to Mac OS X conventions, but beware of side effects such as a Help menu with no menu items, or two separators together. See samples/menu in the wxWidgets distribution for a test of most menu functionality, and also see samples/ownerdrw for a demonstration of the use of custom fonts and bitmaps in menu items. wxMenu Events There are four different kinds of event associated with wxMenu: wxCommandEvent, wxUpdateUIEvent, wxMenuEvent, and wxContextMenuEvent. Table 4-45 lists the command events, whose handlers take a wxCommandEvent argument. Use these for processing menu commands, either from a pop-up menu or a menu bar on a frame. These are interchangeable with the equivalent toolbar event macros so that events generated from both menus and toolbar buttons can be processed by the same handler.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 119 Menus 119 Table 4-45 wxMenu Command Events EVT_MENU(id, func) Processes a wxEVT_COMMAND_MENU_SELECTED event, generated by a menu item. EVT_MENU_RANGE(id1, id2, func) Processes a wxEVT_COMMAND_MENU_RANGE event, generated by a range of menu items. Table 4-46 lists the event macros for update events—events generated by the framework in idle time to give the application a chance to update ele- ments of the UI—for example, enabling or disabling menu items. Although wxUpdateUIEvent applies to all windows, menu event handlers can use them slightly differently than other event handlers: they can call Check and SetText as well as Enable. Check checks or unchecks the menu item, while SetText sets the menu item label, which is useful if the label changes dynamically accord- ing to some condition. For example: BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_UPDATE_UI(ID_TOGGLE_TOOLBAR, MyFrame::OnUpdateToggleToolbar) END_EVENT_TABLE() void MyFrame::OnUpdateToggleToolbar(wxUpdateUIEvent& event) { event.Enable(true); event.Check(m_showToolBar); event.SetText(m_showToolBar ? wxT(“Show &Toolbar (shown)”) : wxT(“Show &Toolbar (hidden)”)); } For more on UI update events, please see Chapter 9. Table 4-46 wxMenu Update Events EVT_UPDATE_UI(id, func) Processes a wxEVT_UPDATE_UI event. The handler can call Enable, Check, and SetText among other functions. EVT_UPDATE_UI_RANGE(id1, id2, func) Processes a wxEVT_UPDATE_UI event for a range of identifiers. Table 4-47 lists the other menu-related events. EVT_CONTEXT_MENU handlers take a wxContextMenuEvent, which is derived from wxCommandEvent and therefore propagates up the parent-child window hierarchy. Use this in preference to intercepting a right mouse button click when you want to show a context menu, and call the event object’s GetPosition function to find out where to show the menu. The remaining macros process wxMenuEvent objects, and these are only sent from a menu bar to its frame. They tell the application when a menu has been opened and closed, and when the user has highlighted a menu item. The

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 120 120 Window Basics Chapter 4 default EVT_MENU_HIGHLIGHT handler shows a menu item’s help string in the sta- tus bar, but you can provide your own handler to do something different. Table 4-47 Other wxMenu Events EVT_CONTEXT_MENU(func) Processes the event generated when the user has requested a popup menu to appear by pressing a special key (under Windows) or by right-clicking the mouse. The handler takes a wxContextMenuEvent. EVT_COMMAND_CONTEXT_MENU(id, func) The same as EVT_CONTEXT_MENU, but it takes a window identifier. EVT_MENU_OPEN(func) Handles a wxEVT_MENU_OPEN event, sent when a menu is about to be opened. On Windows, this is only sent once for each navigation of the menu bar. EVT_MENU_CLOSE(func) Handles a wxEVT_MENU_CLOSE event, sent when a menu has just been closed. EVT_MENU_HIGHLIGHT(id, func) Handles a wxEVT_MENU_HIGHLIGHT event, sent when the menu item with the speci- fied id has been highlighted. This is used to show help prompts in a frame’s status bar. EVT_MENU_HIGHLIGHT_ALL(func) Handles a wxEVT_MENU_HIGHLIGHT event for any menu identifier. wxMenu Member Functions These are the major wxMenu functions. Append adds a menu item: specify an identifier, a label, a help string, and the kind of item (wxITEM_NORMAL, wxITEM_SEPARATOR, wxITEM_CHECK or wxITEM_ RADIO). You can also use AppendCheckItem and AppendRadioItem to avoid specify- ing wxITEM_CHECK or wxITEM_RADIO. For example: // Append a normal item menu->Append(wxID_NEW, wxT(“&New...\tCtrl+N”)); // Append a check item menu->AppendCheckItem(ID_SHOW_STATUS, wxT(“&Show Status”)); // Append a radio item menu->AppendRadioItem(ID_PAGE_MODE, wxT(“&Page Mode”)); Another overload of Append enables you to append a submenu, for example: // Append a submenu menu->Append(ID_SUBMENU, wxT(“&More options...”), subMenu);

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 121 Menus 121 Yet another overload of Append enables you to use a wxMenuItem object directly to append an item, and this is the only way to show bitmaps on menus or to set special fonts. For example: // Initialization of bitmaps and font not shown wxBitmap bmpEnabled, bmpDisabled; wxFont fontLarge; // Create a menu item wxMenuItem* pItem = new wxMenuItem(menu, wxID_OPEN, wxT(“&Open…”)); // Set bitmaps and font pItem->SetBitmaps(bmpEnabled, bmpDisabled); pItem->SetFont(fontLarge); // Finally append it to the menu menu->Append(pItem); Use Insert to insert a menu at a particular position. There are also the func- tions Prepend, PrependCheckItem, PrependRadioItem, and PrependSeparator for inserting items at the start of the menu. AppendSeparator adds a separator, and InsertSeparator inserts a separa- tor in a given position. For example: // Append a separator menu->AppendSeparator(); Break inserts a break in a menu, causing the next appended item to appear in a new column. Use Check to toggle a check or radio item on or off, passing the menu item identifier and a boolean value. Use IsChecked to get the checked status. Delete deletes a menu item specified by identifier or by wxMenuItem pointer. If the item is a menu, the submenu will not be deleted. Use Destroy if you want to remove and delete a submenu. Remove removes the menu item from a menu without deleting the returned wxMenuItem object. Use Enable to enable or disable a menu item, but rather than doing this explicitly, you may want to use UI update events (see Chapter 9). IsEnabled returns the enabled status. Use FindItem to find an item by label or identifier. Use FindItemByPosition to find an item by position in the menu. GetHelpString and SetHelpString are accessors for the help string associ- ated with a menu item. When the menu is part of a menu bar, wxFrame shows this string in the status bar (if available), as the user highlights each menu item. GetLabel and SetLabel get or set the menu item label, given its identifier. GetMenuCount returns the number of items in the menu.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 122 122 Window Basics Chapter 4 GetMenuItems returns a reference to the list of menu items, a wxMenuItemList object. GetTitle and SetTitle are accessors for the optional title of a menu and are only used for pop-up menus. UpdateUI sends UI update events to the event handler argument or to the owning window if NULL is passed. This is called just before the menu is popped up, but the application may call it at other times if required. CONTROL BARS A control bar provides a convenient way to contain and arrange multiple con- trols. There are currently three kinds of control bars: wxMenuBar, wxToolBar, and wxStatusBar. wxMenuBar can only belong to a wxFrame. wxToolBar and wxStatusBar are most commonly used with wxFrame, but they can also be children of other windows. wxMenuBar A menu bar contains a series of menus accessible from the top of a frame under the title bar. You can replace a frame’s current menu bar by calling SetMenuBar. To create a menu bar, use the default constructor and append wxMenu objects. For example: wxMenuBar* menuBar = new wxMenuBar; wxMenu* fileMenu = new wxMenu; fileMenu->Append(wxID_OPEN, wxT(“&Open...”), wxT(“Opens a file”)); fileMenu->AppendSeparator(); fileMenu->Append(wxID_EXIT, wxT(“E&xit”), wxT(“Quits the program”)); menuBar->Append(fileMenu); frame->SetMenuBar(menuBar, wxT(“&File”)); This code creates a one-menu menu bar, as shown in Figure 4-34. Figure 4-34 A wxMenuBar

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 123 Control Bars 123 You can append submenus to a wxMenu, and you can create check and radio menu items (refer to the “Menus” section earlier in this chapter). As in the previous example, an ampersand in a label indicates that the following character should be used as a mnemonic (pressing that key when the menu is shown executes the associated command). If you provide a help string, it will be shown in the frame’s status bar (if any) by virtue of the default EVT_MENU_HIGHLIGHT handler. wxMenuBar Styles wxMenuBar takes the wxMB_DOCKABLE style, used under GTK+ to allow the menu bar to be detached from the frame. wxMenuBar Events Menu bars use the events already covered in the description of wxMenu. wxMenuBar Member Functions These are the major wxMenuBar functions. Append adds a menu to the end of the menu bar, which will then own the menu and will destroy it when the menu bar is destroyed (usually by the own- ing frame). Pass the menu and a label. Insert inserts a menu at the given posi- tion. Enable enables or disables the given menu item, given its identifier. Use IsEnabled to check its enabled status. Check checks or unchecks a check or radio menu item. Use IsChecked to test its checked status. EnableTop enables or disables a whole menu, by zero-based position. FindMenu returns the index of a menu whose label matches the given string, with or without mnemonic characters. It returns wxNOT_FOUND if there was no match. FindMenuItem returns the index of a menu item given a menu name and a menu item. FindItem returns the wxMenuItem object given a menu item identifier, and if it is a submenu, its wxMenu pointer will be returned in the second argument. GetHelpString and SetHelpString are accessors for the help string for a given menu item. GetLabel and SetLabel are accessors for a menu item’s label. GetLabelTop and SetLabelTop are accessors for a menu’s label in the menu bar, given the zero-based menu position. GetMenu returns a pointer to the wxMenu at the given zero-based position. GetMenuCount returns the number of menus in the menu bar. Refresh redraws the menu bar.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 124 124 Window Basics Chapter 4 Remove removes a menu and returns the wxMenu object, which the applica- tion is then responsible for deleting. Replace replaces a menu at the given position with another one. The old menu is returned, and the application is responsible for deleting it. wxToolBar A toolbar contains a number of buttons and controls. It can be horizontal or vertical, and the buttons can be push, check, or radio buttons. The buttons can show labels as well as bitmaps. If you use wxFrame::CreateToolBar to create the toolbar, or wxFrame::SetToolBar to associate it with a frame, the frame will manage the toolbar, and it will not be counted as part of the client area. If you use it in any other way, then your code will have to manage the toolbar size and position using sizers or some other method. Here’s an example of creating a toolbar and associating it with a frame: #include “wx/toolbar.h” #include “open.xpm” #include “save.xpm” wxToolBar* toolBar = new wxToolBar(frame, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL|wxNO_BORDER); wxBitmap bmpOpen(open_xpm); wxBitmap bmpSave(save_xpm); toolBar->AddTool(wxID_OPEN, bmpOpen, wxT(“Open”)); toolBar->AddTool(wxID_SAVE, bmpSave, wxT(“Save”)); toolBar->AddSeparator(); wxComboBox* comboBox = new wxComboBox(toolBar, ID_COMBOBOX); toolBar->AddControl(comboBox); toolBar->Realize(); frame->SetToolBar(toolBar); Under Windows, this will create a toolbar, as shown in Figure 4-35. Figure 4-35 A wxToolBar

Smart_Ch04f.qxd 6/10/05 1:48 PM Page 125 Control Bars 125 Note the call to Realize, which must be performed after all buttons and controls have been added to the toolbar; otherwise, nothing will appear in the toolbar. Check out the wxWidgets sample in samples/toolbar for a demonstration of changing orientation, showing labels on buttons, changing the button size, and other aspects of wxToolBar. Tool Bitmap Colors Under Windows Under Windows, wxWidgets will attempt to map colors in the tool bitmaps that are close to “standard” colors to equivalents used by the current theme. In particular, light gray is used to indicate transparency within the bitmap. Table 4-48 lists these colors. In fact, colors in the bitmap only have to be close to the indicated color (each RGB element can be within 10 units of the stan- dard value) for the substitution to take place. Table 4-48 Standard Bitmap Colors Color Value Color Name Used For wxColour(0, 0, 0) Black Dark shadows wxColour(128, 128, 128) Dark gray Shadows for 3D edges facing away from the light source wxColour(192, 192, 192) Light gray 3D face (button background), indicates transparent area wxColour(255, 255, 255) White Highlights for 3D edges facing the light source This is fine for 16-color tool bitmaps, but if you use more colors, the mapping can be undesirable because it leads to a grainy effect. In this case, add the fol- lowing line to your code before the creation of the toolbar to switch off the mapping: wxSystemOptions::SetOption(wxT(“msw.remap”), 0); You will need to include wx/sysopt.h in your source file.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 126 126 Window Basics Chapter 4 wxToolBar Styles Table 4-49 lists the specific window styles for wxToolBar. Table 4-49 wxToolBar Styles wxTB_HORIZONTAL Creates a horizontal toolbar. wxTB_VERTICAL Creates a vertical toolbar. wxTB_FLAT Gives the toolbar a flat look. Windows and GTK+ only. wxTB_DOCKABLE Makes the toolbar floatable and dockable. GTK+ only. wxTB_TEXT Shows the text in the toolbar buttons; by default, only icons are shown. wxTB_NOICONS Specifies no icons in the toolbar buttons; by default, they are shown. wxTB_NODIVIDER Specifies no divider above the toolbar. Windows only. wxTB_HORZ_LAYOUT Shows the text and the icons alongside, not vertically stacked. Windows and GTK+ only. This style must be used with wxTB_TEXT. wxTB_HORZ_TEXT Combination of wxTB_HORZ_LAYOUT and wxTB_TEXT. wxToolBar Events Toolbar event macros are listed in Table 4-50. The toolbar class emits menu commands in the same way that a frame’s menu bar does, so you can use one EVT_MENU or EVT_TOOL macro for both a menu item and a toolbar button. The event handler functions take a wxCommandEvent argument. For most of the event macros, the identifier of the tool is passed, but for EVT_TOOL_ENTER, the toolbar window identifier is passed, and the tool identifier is retrieved from the wxCommandEvent. This is because the identifier may be -1 when the mouse moves off a tool, and -1 is not allowed as an identifier in the event system. Table 4-50 wxToolBar Events EVT_TOOL(id, func) Processes a wxEVT_COMMAND_TOOL_CLICKED event (a synonym for wxEVT_COMMAND_MENU_ SELECTED), generated when the user clicks on a toolbar tool. Pass the identifier of the tool. EVT_TOOL_RANGE(id1, id2, func) Processes a wxEVT_COMMAND_TOOL_CLICKED event for a range of identifiers. Pass the iden- tifier of the tools. EVT_TOOL_RCLICKED(id, func) Processes a wxEVT_COMMAND_TOOL_RCLICKED event, generated when the user right-clicks on a control. Pass the identifier of the tool. EVT_TOOL_RCLICKED_RANGE Processes a wxEVT_COMMAND_TOOL_RCLICKED (id1, id2, func) event for a range of identifiers. Pass the iden- tifiers of the tools.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 127 Control Bars 127 EVT_TOOL_ENTER(id, func) Processes a wxEVT_COMMAND_TOOL_ENTER event, generated when the mouse pointer moves into or out of a tool. Pass the identifier of the toolbar itself. The value of wxCommandEvent::GetSelection is the tool identifier, or -1 if the pointer has moved off a tool. wxToolBar Member Functions These are the major wxToolBar functions. AddTool adds a tool: specify the identifier, an optional label, a bitmap, a help string, and the kind of tool (wxITEM_NORMAL, wxITEM_CHECK, or wxITEM_RADIO). Use InsertTool to insert the tool at a particular position. You can also use AddCheckTool and AddRadioTool to avoid specifying wxITEM_CHECK or wxITEM_RADIO. AddSeparator adds a separator, which is a line or a space depend- ing on implementation. Use InsertSeparator to insert a separator in a given position. For example, the following line adds a checkable tool with a caption (“Save”), a bitmap, and a help string (“Toggle button 1”): toolBar->AddTool(wxID_SAVE, wxT(“Save”), bitmap, wxT(“Toggle button 1”), wxITEM_CHECK); AddControl adds a control, such as a combo box. InsertControl inserts a control at a given position. DeleteTool deletes a tool specified by identifier. DeleteToolByPos deletes a tool by position. RemoveTool removes a tool from the toolbar but doesn’t delete the wxToolBarTool object, which is returned from the function. Use EnableTool to enable or disable a tool, but rather than doing this explicitly, you may want to use UI update events (see Chapter 9). GetToolEnabled returns the enabled status. Use FindById and FindControl to find a tool or control by identifier. If you need to add bitmaps with a size other than the default 16×15, call SetToolBitmapSize. GetToolBitmapSize returns the current bitmap size. GetToolSize returns the size including decorations around the tool. GetMargins and SetMargins are accessors for the left/right and top/bottom margins. GetToolClientData and SetToolClientData can be used to return or associ- ate an arbitrary wxObject for a tool, given the tool identifier. GetToolLongHelp and SetToolLongHelp are accessors for the long help string associated with a tool. This is a line of help that could be shown in a sta- tus bar, for example. GetToolShortHelp and SetToolShortHelp are accessors for the short help string (tooltip) associated with a tool.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 128 128 Window Basics Chapter 4 GetToolPacking and SetToolPacking are accessors for the tool spacing in the vertical direction if the toolbar is horizontal, and for spacing in the hori- zontal direction if the toolbar is vertical. GetToolPosition returns the position in the toolbar given a tool identifier. GetToolSeparation and SetToolSeparation are accessors for the tool sepa- rator size. GetToolState and SetToolState are accessors for a check or radio tool on/off state. Realize must always be called after tools have been added. ToggleTool toggles the given radio or check tool on or off. wxStatusBar A status bar is a narrow window that can be placed along the bottom of a frame to give small amounts of status information. It can contain one or more fields, which can have fixed or variable width. If you use wxFrame::CreateStatusBar to create the status bar, or wxFrame::SetStatusBar to associate it with a frame, the frame will manage the status bar, and it will not be counted as part of the client area. If you use it in any other way, then your code will have to manage the status bar size and position using sizers or some other method. Here’s an example of creating a status bar with three fields, two that are 60 pixels wide and a third that expands to fill the rest of the status bar. #include “wx/statusbr.h” wxStatusBar* statusBar = new wxStatusBar(frame, wxID_ANY, wxST_SIZEGRIP); frame->SetStatusBar(statusBar); int widths[] = { 60, 60, -1 }; statusBar->SetFieldWidths(WXSIZEOF(widths), widths); statusBar->SetStatusText(wxT(“Ready”), 0); The resulting status bar is shown in Figure 4-36. Figure 4-36 A wxStatusBar

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 129 Control Bars 129 If you want, you can create small controls inside status bar fields. You will have to manage their size and position yourself—for example, from within the size event handler of a class derived from wxStatusBar. wxStatusBar Styles Table 4-51 shows the specific window style for wxStatusBar. You can also deter- mine the appearance of the individual field borders using SetStatusStyles. Table 4-51 wxStatusBar Style wxST_SIZEGRIP Displays a gripper at the right side of the status bar. wxStatusBar Events There are no special events for wxStatusBar. wxStatusBar Member Functions These are the major wxStatusBar functions. GetFieldRect returns the size and position of a field’s internal bounding rectangle. GetFieldsCount returns the number of fields in the status bar. Use SetFieldsCount to set the number of fields. GetStatusText gets the status text for a field, and SetStatusText sets the text for a field. PushStatusText saves the current field text in a stack and sets the field text to the string passed as an argument. PopStatusText sets the field text to the top of the stack and pops the stack of saved strings. SetMinHeight sets the minimal possible height for the status bar. SetStatusWidths takes the number of fields and an array of integers that represent each field’s width, or a negative number to specify a proportion of the status bar to fill. For example, to create one field of fixed width 100 in the right part of the status bar and two more fields that get 66% and 33% of the remaining space respectively, you should specify an array containing -2, -1, and 100. The negative numbers are used to distinguish absolute pixel values from proportions. SetStatusStyles takes the number of fields and an array of integer styles that determine the appearance of the fields. Use wxSB_NORMAL for a sunken field with a 3D border, wxSB_FLAT for a flat field with no border, and wxSB_RAISED for a field with a raised 3D border.

Smart_Ch04f.qxd 6/10/05 11:17 AM Page 130 130 Window Basics Chapter 4 SUMMARY This chapter has given you enough information about the capabilities of essential window and control classes to know how to start building useful applications. For more details on these and other window classes, please refer to the reference manual. For further window classes, and how to create your own controls, see Chapter 12. You’ll also find it useful to look at the samples in your wxWidgets distribution, such as samples/widgets, samples/toolbar, samples/text, and samples/listbox. Next, we’ll look at how how your application can draw on a variety of sur- faces, including windows, bitmaps, and the printed page.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 131 C H A P T E R 5 Drawing and Printing This chapter introduces the idea of the device context, generalizing the con- cept of a drawing surface such as a window or a printed page. We will discuss the available device context classes and the set of “drawing tools” that wxWidgets provides for handling fonts, color, line drawing, and filling. Next we describe a device context’s drawing functions and how to use the wxWidgets printing framework. We end the chapter by briefly discussing wxGLCanvas, which provides a way for you to draw 3D graphics on your win- dows using OpenGL. UNDERSTANDING DEVICE CONTEXTS All drawing in wxWidgets is done on a device context, using an instance of a class derived from wxDC. There is no such thing as drawing directly to a win- dow; instead, you create a device context for the window and then draw on the device context. There are also device context classes that work with bitmaps and printers, or you can design your own. A happy consequence of this abstraction is that you can define drawing code that will work on a num- ber of different device contexts: just parameterize it with wxDC, and if neces- sary, take into account the device’s resolution by scaling appropriately. Let’s describe the major properties of a device context. A device context has a coordinate system with its origin at the top-left of the surface. This position can be changed with SetDeviceOrigin so that graph- ics subsequently drawn on the device context are shifted—this is used when painting with wxScrolledWindow. You can also use SetAxisOrientation if you pre- fer, say, the y-axis to go from bottom to top. 131

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 132 132 Drawing and Printing Chapter 5 There is a distinction between logical units and device units. Device units are the units native to the particular device—for a screen, a device unit is a pixel. For a printer, the device unit is defined by the resolution of the printer, which can be queried using GetSize (for a page size in device units) or GetSizeMM (for a page size in millimeters). The mapping mode of the device context defines the unit of measurement used to convert logical units to device units. Note that some device contexts, in particular wxPostScriptDC, do not support mapping modes other than wxMM_TEXT. Table 5-1 lists the available mapping modes. Table 5-1 Mapping Modes wxMM_TWIPS Each logical unit is 1/20 of a point, or 1/1440 of an inch. wxMM_POINTS Each logical unit is a point, or 1/72 of an inch. wxMM_METRIC Each logical unit is 1 millimeter. wxMM_LOMETRIC Each logical unit is 1/10 of a millimeter. wxMM_TEXT Each logical unit is 1 pixel. This is the default mode. You can impose a further scale on your logical units by calling SetUser Scale, which multiplies with the scale implied by the mapping mode. For example, in wxMM_TEXT mode, a user scale value of (1.0, 1.0) makes logical and device units identical. By default, the mapping mode is wxMM_TEXT, and the scale is (1.0, 1.0). A device context has a clipping region, which can be set with SetClipping Region and cleared with DestroyClippingRegion. Graphics will not be shown outside the clipping region. One use of this is to draw a string so that it appears only inside a particular rectangle, even though the string might extend beyond the rectangle boundary. You can set the clipping region to be the same size and location as the rectangle, draw the text, and then destroy the clipping region, and the text will be truncated to fit inside the rectangle. Just as with real artistry, in order to draw, you must first select some tools. Any operation that involves drawing an outline uses the currently selected pen, and filled areas use the current brush. The current font, together with the foreground and background text color, determines how text will appear. We will discuss these tools in detail later, but first we’ll look at the types of device context that are available to us. Available Device Contexts These are the device context classes you can use:

wxClientDC. For drawing on the client area of a window.

wxBufferedDC. A replacement for wxClientDC for double-buffered painting.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 133 Understanding Device Contexts 133

wxWindowDC. For drawing on the client and non-client (decorated) area of a window. This is rarely used and not fully implemented on all platforms.

wxPaintDC. For drawing on the client area of a window during a paint event handler.

wxBufferedPaintDC. A replacement for wxPaintDC for double-buffered painting.

wxScreenDC. For drawing on or copying from the screen.

wxMemoryDC. For drawing into or copying from a bitmap.

wxMetafileDC. For creating a metafile (Windows and Mac OS X).

wxPrinterDC. For drawing to a printer.

wxPostScriptDC. For drawing to a PostScript file or printer. The following sections describe how to create and work with these device contexts. Working with printer device contexts is discussed in more detail later in the chapter in “Using the Printing Framework.” Drawing on Windows with wxClientDC Use wxClientDC objects to draw on the client area of windows outside of paint events. For example, to implement a doodling application, you might create a wxClientDC object within your mouse event handler. It can also be used within background erase events. Here’s a code fragment that demonstrates how to paint on a window using the mouse: BEGIN_EVENT_TABLE(MyWindow, wxWindow) EVT_MOTION(MyWindow::OnMotion) END_EVENT_TABLE() void MyWindow::OnMotion(wxMouseEvent& event) { if (event.Dragging()) { wxClientDC dc(this); wxPen pen(*wxRED, 1); // red pen of width 1 dc.SetPen(pen); dc.DrawPoint(event.GetPosition()); dc.SetPen(wxNullPen); } }

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 134 134 Drawing and Printing Chapter 5 For more realistic doodling code, see Chapter 19, “Working with Documents and Views.” The “Doodle” example uses line segments instead of points and implements undo/redo. It also stores the line segments, so that when the win- dow is repainted, the graphic is redrawn; using the previous code, the graphic will only linger on the window until the next paint event is received. You may also want to use CaptureMouse and ReleaseMouse to direct all mouse events to your window while the mouse button is down. An alternative to using wxClientDC directly is to use wxBufferedDC, which stores your drawing in a memory device context and transfers it to the window in one step when the device context is about to be deleted. This can result in smoother updates—for example, if you don’t want the user to see a complex graphic being updated bit by bit. Use the class exactly as you would use wxClientDC. For efficiency, you can pass a stored bitmap to the constructor to avoid the object re-creating a bitmap each time. Erasing Window Backgrounds A window receives two kinds of paint event: wxPaintEvent for drawing the main graphic, and wxEraseEvent for painting the background. If you just handle wxPaintEvent, the default wxEraseEvent handler will clear the background to the color previously specified by wxWindow::SetBackgroundColour, or a suitable default. This may seem rather convoluted, but this separation of background and foreground painting enables maximum control on platforms that follow this model, such as Windows. For example, suppose you want to draw a textured background on a window. If you tile your texture bitmap in OnPaint, you will see a brief flicker as the background is cleared prior to painting the texture. To avoid this, handle wxEraseEvent and do nothing in the handler. Alternatively, you can do the background tiling in the erase handler, and paint the fore- ground in the paint handler (however, this defeats buffered drawing as described in the next section). On some platforms, intercepting wxEraseEvent still isn’t enough to sup- press default background clearing. The safest thing to do if you want to have a background other than a plain color is to call wxWindow::SetBackgroundStyle passing wxBG_STYLE_CUSTOM. This tells wxWidgets to leave all background paint- ing to the application. If you do decide to implement an erase handler, call wxEraseEvent::GetDC and use the returned device context if it exists. If it’s NULL, you can use a wxClientDC instead. This allows for wxWidgets implementations that don’t pass a device context to the erase handler, which can be an unnecessary expense if it’s not used. This is demonstrated in the following code for drawing a bitmap as a background texture: BEGIN_EVENT_TABLE(MyWindow, wxWindow) EVT_ERASE_BACKGROUND(MyWindow::OnErase)

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 135 Understanding Device Contexts 135 END_EVENT_TABLE() void MyWindow::OnErase(wxEraseEvent& event) { wxClientDC* clientDC = NULL; if (!event.GetDC()) clientDC = new wxClientDC(this); wxDC* dc = clientDC ? clientDC : event.GetDC() ; wxSize sz = GetClientSize(); wxEffects effects; effects.TileBitmap(wxRect(0, 0, sz.x, sz.y), *dc, m_bitmap); if (clientDC) delete clientDC; } As with paint events, the device context will be clipped to the area that needs to be repaired, if using the object returned from wxEraseEvent::GetDC. Drawing on Windows with wxPaintDC If you define a paint event handler, you must always create a wxPaintDC object, even if you don’t use it. Creating this object will tell wxWidgets that the invalid regions in the window have been repainted so that the windowing sys- tem won’t keep sending paint events ad infinitum. In a paint event, you can call wxWindow::GetUpdateRegion to get the region that is invalid, or wxWindow::IsExposed to determine if the given point or rectangle is in the update region. If possible, just repaint this region. The device context will automatically be clipped to this region anyway during the paint event, but you can speed up redraws by only drawing what is necessary. Paint events are generated when user interaction causes regions to need repainting, but they can also be generated as a result of wxWindow::Refresh or wxWindow::RefreshRect calls. If you know exactly which area of the window needs to be repainted, you can invalidate that region and cause as little flicker as possible. One problem with refreshing the window this way is that you can’t guarantee exactly when the window will be updated. If you really need to have the paint handler called immediately—for example, if you’re doing time- consuming calculations—you can call wxWindow::Update after calling Refresh or RefreshRect. The following code draws a red rectangle with a black outline in the cen- ter of a window, if the rectangle was in the update region: BEGIN_EVENT_TABLE(MyWindow, wxWindow) EVT_PAINT(MyWindow::OnPaint) END_EVENT_TABLE() void MyWindow::OnPaint(wxPaintEvent& event) {

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 136 136 Drawing and Printing Chapter 5 wxPaintDC dc(this); dc.SetPen(*wxBLACK_PEN); dc.SetBrush(*wxRED_BRUSH); // Get window dimensions wxSize sz = GetClientSize(); // Our rectangle dimensions wxCoord w = 100, h = 50; // Center the rectangle on the window, but never // draw at a negative position. int x = wxMax(0, (sz.x—w)/2); int y = wxMax(0, (sz.y—h)/2); wxRect rectToDraw(x, y, w, h); // For efficiency, do not draw if not exposed if (IsExposed(rectToDraw)) DrawRectangle(rectToDraw); } Note that by default, when a window is resized, only the newly exposed areas are included in the update region. Use the wxFULL_REPAINT_ON_RESIZE window style to have the entire window included in the update region when the win- dow is resized. In our example, we need this style because resizing the window changes the position of the graphic, and we need to make sure that no odd bits of the rectangle are left behind. wxBufferedPaintDC is a buffered version of wxPaintDC. Simply replace wxPaintDC with wxBufferedPaintDC in your paint event handler, and the graph- ics will be drawn to a bitmap before being drawn all at once on the window, reducing flicker. As we mentioned in the previous topic, another thing you can do to make drawing smoother (particularly when resizing) is to paint the background in your paint handler, and not in an erase background handler. All the painting will then be done in your buffered paint handler, so you don’t see the back- ground being erased before the paint handler is called. Add an empty erase background handler, and call SetBackgroundStyle with wxBG_STYLE_CUSTOM to hint to some systems not to clear the background automatically. In a scrolling window, where the device origin is moved to shift the graphic for the current scroll position, you will need to calculate the position of the window client area for the current origin. The following code snippet illustrates how to achieve smooth painting and scrolling for a class derived from wxScrolledWindow: #include “wx/dcbuffer.h” BEGIN_EVENT_TABLE(MyCustomCtrl, wxScrolledWindow)

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 137 Understanding Device Contexts 137 EVT_PAINT(MyCustomCtrl::OnPaint) EVT_ERASE_BACKGROUND(MyCustomCtrl::OnEraseBackground) END_EVENT_TABLE() /// Painting void MyCustomCtrl::OnPaint(wxPaintEvent& event) { wxBufferedPaintDC dc(this); // Shifts the device origin so we don’t have to worry // about the current scroll position ourselves PrepareDC(dc); // Paint the background PaintBackground(dc); // Paint the graphic ... } /// Paint the background void MyCustomCtrl::PaintBackground(wxDC& dc) { wxColour backgroundColour = GetBackgroundColour(); if (!backgroundColour.Ok()) backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE); dc.SetBrush(wxBrush(backgroundColour)); dc.SetPen(wxPen(backgroundColour, 1)); wxRect windowRect(wxPoint(0, 0), GetClientSize()); // We need to shift the client rectangle to take into account // scrolling, converting device to logical coordinates CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y); dc.DrawRectangle(windowRect); } // Empty implementation, to prevent flicker void MyCustomCtrl::OnEraseBackground(wxEraseEvent& event) { } To increase efficiency when using wxBufferedPaintDC, you can maintain a bitmap large enough to cope with all window sizes (for example, the screen size) and pass it to the wxBufferedPaintDC constructor as its second argument. Then the device context doesn’t have to keep creating and destroying its bitmap for every paint event. The area that wxBufferedPaintDC copies from its buffer is normally the size of the window client area (the part that the user can see). The actual paint context that is internally created by the class is not transformed by the win- dow’s PrepareDC to reflect the current scroll position. However, you can specify that both the paint context and your buffered paint context use the same

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 138 138 Drawing and Printing Chapter 5 transformations by passing wxBUFFER_VIRTUAL_AREA to the wxBufferedPaintDC constructor, rather than the default wxBUFFER_CLIENT_AREA. Your window’s PrepareDC function will be called on the actual paint context so the transforma- tions on both device contexts match. In this case, you will need to supply a bitmap that is the same size as the virtual area in your scrolled window. This is inefficient and should normally be avoided. Note that at the time of writing, using buffering with wxBUFFER_CLIENT_AREA does not work with scaling (SetUserScale). For a full example of using wxBufferedPaintDC, you might like to look at the wxThumbnailCtrl control in examples/chap12/thumbnail on the CD-ROM. Drawing on Bitmaps with wxMemoryDC A memory device context has a bitmap associated with it, so that drawing into the device context draws on the bitmap. First create a wxMemoryDC object with the default constructor, and then use SelectObject to associate a bitmap with the device context. When you have finished with the device context, you should call SelectObject with wxNullBitmap to remove the association. The following example creates a bitmap and draws a red rectangle out- line on it: wxBitmap CreateRedOutlineBitmap() { wxMemoryDC memDC; wxBitmap bitmap(200, 200); memDC.SelectObject(bitmap); memDC.SetBackground(*wxWHITE_BRUSH); memDC.Clear(); memDC.SetPen(*wxRED_PEN); memDC.SetBrush(*wxTRANSPARENT_BRUSH); memDC.DrawRectangle(wxRect(10, 10, 100, 100)); memDC.SelectObject(wxNullBitmap); return bitmap; } You can also copy areas from a memory device context to another device con- text with the Blit function, described later in the chapter. Creating Metafiles with wxMetafileDC wxMetafileDC is available on Windows and Mac OS X, where it models a draw- ing surface for a Windows metafile or a Mac PICT, respectively. It allows you to draw into a wxMetafile object, which consists of a list of drawing instruc- tions that can be interpreted by an application or rendered into a device context with wxMetafile::Play.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 139 Understanding Device Contexts 139 Accessing the Screen with wxScreenDC Use wxScreenDC for drawing on areas of the whole screen. This is useful when giving feedback for dragging operations, such as the sash on a splitter window. For efficiency, you can limit the screen area to be drawn on to a specific region (often the dimensions of the window in question). As well as drawing with this class, you can copy areas to other device contexts and use it for capturing screenshots. Because it is not possible to control where other applications are drawing, use of wxScreenDC to draw on the screen usually works best when restricted to windows in the current application. Here’s example code that snaps the current screen and returns it in a bitmap: wxBitmap GetScreenShot() { wxSize screenSize = wxGetDisplaySize(); wxBitmap bitmap(screenSize.x, screenSize.y); wxScreenDC dc; wxMemoryDC memDC; memDC.SelectObject(bitmap); memDC.Blit(0, 0, screenSize.x, screenSize.y, & dc, 0, 0); memDC.SelectObject(wxNullBitmap); return bitmap; } Printing with wxPrinterDC and wxPostScriptDC wxPrinterDC represents the printing surface. On Windows and Mac, it maps to the respective printing system for the application. On other Unix-based sys- tems where there is no standard printing model, a wxPostScriptDC is used instead, unless GNOME printing support is available (see the later section, “Printing Under Unix with GTK+”). There are several ways to construct a wxPrinterDC object. You can pass it a wxPrintData after setting paper type, landscape or portrait, number of copies, and so on. An easier way is to show a wxPrintDialog and then call wxPrintDialog::GetPrintDC to retrieve an appropriate wxPrinterDC for the set- tings chosen by the user. At a higher level, you can derive a class from wxPrintout to specify behavior for printing and print previewing, passing it to an instance of wxPrinter (see the later section on printing). If your printout is mainly text, consider using the wxHtmlEasyPrinting class to bypass the need to deal with wxPrinterDC or wxPrintout altogether: just write an HTML file (using wxWidgets’ subset of HTML syntax) and create a wxHtmlEasyPrinting object to print or preview it. This could save you days or even weeks of programming to format your text, tables, and images. See Chapter 12, “Advanced Window Classes,” for more on this. wxPostScriptDC is a device context specifically for creating PostScript files for sending to a printer. Although mainly for Unix-based systems, it can be

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 140 140 Drawing and Printing Chapter 5 used on other systems too, where PostScript files need to be generated and you can’t guarantee the presence of a PostScript printer driver. You can create a wxPostScriptDC either by passing a wxPrintData object, or by passing a file name, a boolean to specify whether a print dialog should be shown, and a parent window for the dialog. For example: #include “wx/dcps.h” wxPostScriptDC dc(wxT(“output.ps”), true, wxGetApp().GetTopWindow()); if (dc.Ok()) { // Tell it where to find the AFM files dc.GetPrintData().SetFontMetricPath(wxGetApp().GetFontPath()); // Set the resolution in points per inch (the default is 720) dc.SetResolution(1440); // Draw on the device context ... } One of the quirks of wxPostScriptDC is that it can’t directly return text size information from GetTextExtent. You will need to provide AFM (Adobe Font Metric) files with your application and use wxPrintData::SetFontMetricPath to specify where wxWidgets will find them, as in this example. You can get a selection of GhostScript AFM files from ftp://biolpc22.york.ac.uk/pub/ support/gs_afm.tar.gz. DRAWING TOOLS Drawing code in wxWidgets operates like a very fast artist, rapidly selecting colors and drawing tools, drawing a little part of the scene, then selecting dif- ferent tools, drawing another part of the scene, and so on. Here we describe the wxColour, wxPen, wxBrush, wxFont and wxPalette classes. You will also find it useful to refer to the descriptions of other classes relevant to drawing—wxRect, wxRegion, wxPoint, and wxSize, which are described in Chapter 13, “Data Structure Classes.” Note that these classes use “reference-counting,” efficiently copying only internal pointers rather than chunks of memory. In most circumstances, you can create color, pen, brush, and font objects on the stack as they are needed without worrying about speed implications. If your application does have per- formance problems, you can take steps to improve efficiency, such as storing some objects as data members.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 141 Drawing Tools 141 wxColour You use wxColour to define various aspects of color when drawing. (Because wxWidgets started life in Edinburgh, the API uses British spelling. However, to cater for the spelling sensibilities of the New World, wxWidgets defines wxColor as an alias for wxColour.) You can specify the text foreground and background color for a device context using a device context’s SetTextForeground and SetTextBackground func- tions, and you also use wxColour to create pens and brushes. A wxColour object can be constructed in a number of different ways. You can pass red, green, and blue values (each 0 to 255), or a standard color string such as WHITE or CYAN, or you can create it from another wxColour object. Alternatively, you can use the stock color objects, which are pointers: wxBLACK, wxWHITE, wxRED, wxBLUE, wxGREEN, wxCYAN, and wxLIGHT_GREY. The stock object wxNullColour is an uninitialized color for which the Ok function always returns false. Using the wxSystemSettings class, you can retrieve some standard, system- wide colors, such as the standard 3D surface color, the default window back- ground color, menu text color, and so on. Please refer to the documentation for wxSystemSettings::GetColour for the identifiers you can pass. Here are some different ways to create a wxColour object: wxColour color(0, 255, 0); // green wxColour color(wxT(“RED”)); // red // The color used for 3D faces and panels wxColour color(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)); You can also use the wxTheColourDatabase pointer to add a new color, find a wxColour object for a given name, or find the name corresponding to the given color: wxTheColourDatabase->Add(wxT(“PINKISH”), wxColour(234, 184, 184)); wxString name = wxTheColourDatabase->FindName( wxColour(234, 184, 184)); wxString color = wxTheColourDatabase->Find(name); These are the available standard colors: aquamarine, black, blue, blue violet, brown, cadet blue, coral, cornflower blue, cyan, dark gray, dark green, dark olive green, dark orchid, dark slate blue, dark slate gray dark turquoise, dim gray, firebrick, forest green, gold, goldenrod, gray, green, green yellow, indian red, khaki, light blue, light gray, light steel blue, lime green, magenta, maroon, medium aquamarine, medium blue, medium forest green, medium goldenrod, medium orchid, medium sea green, medium slate blue, medium spring green, medium turquoise, medium violet red, midnight blue, navy, orange, orange red, orchid, pale green, pink, plum, purple, red, salmon, sea green, sienna, sky blue, slate blue, spring green, steel blue, tan, thistle, turquoise, vio- let, violet red, wheat, white, yellow, and yellow green.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 142 142 Drawing and Printing Chapter 5 wxPen You define the current pen for the device context by passing a wxPen object to SetPen. The current pen defines the outline color, width, and style for subse- quent drawing operations. wxPen has a low overhead, so you can create instances on the stack within your drawing code rather than storing them. As well as a color and a width, a pen has a style, as described in Table 5-2. Hatch and stipple styles are not supported by the GTK+ port. Table 5-2 wxPen Styles Style Example Description wxSOLID Lines are drawn solid. wxTRANSPARENT Used when no pen drawing is desired. wxDOT The line is drawn dotted. wxLONG_DASH Draws with a long dashed style. wxSHORT_DASH Draws with a short dashed style. On Windows, this is the same as wxLONG_SASH. wxDOT_DASH Draws with a dot and a dash. wxSTIPPLE Uses a stipple bitmap, which is passed as the first constructor argument. wxUSER_DASH Uses user-specified dashes. See the reference manual for further information. wxBDIAGONAL_HATCH Draws with a backward-diagonal hatch. wxCROSSDIAG_HATCH Draws with a cross-diagonal hatch. wxFDIAGONAL_HATCH Draws with a forward-diagonal hatch. wxCROSS_HATCH Draws with a cross hatch. wxHORIZONTAL_HATCH Draws with a horizontal hatch. wxVERTICAL_HATCH Draws with a vertical hatch.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 143 Drawing Tools 143 Call SetCap if you need to specify how the ends of thick lines should look: wxCAP_ROUND (the default) specifies rounded ends, wxCAP_PROJECTING specifies a square projection on either end, and wxCAP_BUTT specifies that the ends should be square and should not project. You can call SetJoin to set the appearance where lines join. The default is wxJOIN_ROUND, where the corners are rounded. Other values are wxJOIN_BEVEL and wxJOIN_MITER. There are some stock pens that you can use: wxRED_PEN, wxCYAN_PEN, wxGREEN_PEN, wxBLACK_PEN, wxWHITE_PEN, wxTRANSPARENT_PEN, wxBLACK_DASHED_PEN, wxGREY_PEN, wxMEDIUM_GREY_PEN, and wxLIGHT_GREY_PEN. These are pointers, so you’ll need to dereference them when passing them to SetPen. There is also the object wxNullPen (an object, not a pointer), an uninitialized pen object that can be used to reset the pen in a device context. Here are some examples of creating pens: // A solid red pen wxPen pen(wxColour(255, 0, 0), 1, wxSOLID); wxPen pen(wxT(“RED”), 1, wxSOLID); wxPen pen = (*wxRED_PEN); wxPen pen(*wxRED_PEN); The last two examples use reference counting, so pen’s internal data points to wxRED_PEN’s data. Reference counting is used for all drawing objects, and it makes the assignment operator and copy constructor cheap operations, but it does mean that sometimes changes in one object affect the properties of another. One way to reduce the amount of construction and destruction of pen objects without storing pen objects in your own classes is to use the global pointer wxThePenList to create and store the pens you need, for example: wxPen* pen = wxThePenList->FindOrCreatePen(*wxRED, 1, wxSOLID); The pen object will be stored in wxThePenList and cleaned up on application exit. Obviously, you should take care not to use this indiscriminately to avoid filling up memory with pen objects, and you also need to be aware of the refer- ence counting issue mentioned previously. You can remove a pen from the list without deleting it by using RemovePen. wxBrush The current brush, specified with SetBrush, defines the fill color and style for drawing operations. You also specify the device context background color using a wxBrush, rather than with just a color. As with wxPen, wxBrush has a low over- head and can be created on the stack.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 144 144 Drawing and Printing Chapter 5 Pass a color and a style to the brush constructor. The style can be one of the values listed in Table 5-3. Table 5-3 wxBrush Styles Style Example Description wxSOLID Solid color is used. wxTRANSPARENT Used when no filling is desired. wxBDIAGONAL_HATCH Draws with a backward-diagonal hatch. wxCROSSDIAG_HATCH Draws with a cross-diagonal hatch. wxFDIAGONAL_HATCH Draws with a forward-diagonal hatch. wxCROSS_HATCH Draws with a cross hatch. wxHORIZONTAL_HATCH Draws with a horizontal hatch. wxVERTICAL_HATCH Draws with a vertical hatch. wxSTIPPLE Uses a stipple bitmap, which is passed as the first constructor argument. You can use the following stock brushes: wxBLUE_BRUSH, wxGREEN_BRUSH, wxWHITE BRUSH, wxBLACK_BRUSH, wxGREY_BRUSH, wxMEDIUM_GREY_BRUSH, wxLIGHT_GREY_BRUSH, wxTRANSPARENT_BRUSH, wxCYAN_BRUSH, and wxRED_BRUSH. These are pointers. You can also use the wxNullBrush object (an uninitialized brush object). Here are some examples of creating brushes: // A solid red brush wxBrush brush(wxColour(255, 0, 0), wxSOLID); wxBrush brush(wxT(“RED”), wxSOLID); wxBrush brush = (*wxRED_BRUSH); // a cheap operation wxBrush brush(*wxRED_BRUSH); As with wxPen, wxBrush also has an associated list, wxTheBrushList, which you can use to cache brush objects: wxBrush* brush = wxTheBrushList->FindOrCreateBrush(*wxBLUE, wxSOLID); Use this with care to avoid proliferation of brush objects and side effects from reference counting. You can remove a brush from the list without deleting it by using RemoveBrush.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 145 Drawing Tools 145 wxFont You use font objects for specifying how text will appear when drawn on a device context. A font has the following properties: The point size specifies the maximum height of the text in points (1/72 of an inch). wxWidgets will choose the closest match it can if the platform is not using scalable fonts. The font family specifies one of a small number of family names, as described in Table 5-4. Specifying a family instead of an actual face name makes applications be more portable because you can’t usually rely on a par- ticular typeface being available on all platforms. The style can be wxNORMAL, wxSLANT, or wxITALIC. wxSLANT may not be imple- mented for all platforms and fonts. The weight is one of wxNORMAL, wxLIGHT, or wxBOLD. A font’s underline can be on (true) or off (false). The face name is optional and specifies a particular typeface. If empty, a default typeface will be chosen from the family specification. The optional encoding specifies the mapping between the character codes used in the program and the letters that are drawn onto the device context. Please see Chapter 16, “Writing International Applications,” for more on this topic. Table 5-4 Font Family Identifiers Identifier Example Description wxFONTFAMILY_SWISS A sans-serif font—often Helvetica or Arial depending on platform. wxFONTFAMILY_ROMAN A formal, serif font. wxFONTFAMILY_SCRIPT A handwriting font. wxFONTFAMILY_MODERN A fixed pitch font, often Courier. wxFONTFAMILY_DECORATIVE A decorative font. wxFONTFAMILY_DEFAULT wxWidgets chooses a default family. You can create a font with the default constructor or by specifying the proper- ties listed in Table 5-4. There are some stock font objects that you can use: wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT, and wxSWISS_FONT. These have the size of the stan- dard system font (wxSYS_DEFAULT_GUI_FONT), apart from wxSMALL_FONT, which is two points smaller. You can also use wxSystemSettings::GetFont to retrieve standard fonts.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 146 146 Drawing and Printing Chapter 5 To use a font object, pass it to wxDC::SetFont before performing text oper- ations, in particular DrawText and GetTextExtent. Here are some examples of font creation. wxFont font(12, wxFONTFAMILY_ROMAN, wxITALIC, wxBOLD, false); wxFont font(10, wxFONTFAMILY_SWISS, wxNORMAL, wxBOLD, true, wxT(“Arial”), wxFONTENCODING_ISO8859_1)); wxFont font(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); wxFont has an associated list, wxTheFontList, which you can use to find a previ- ously created font or add a new one: wxFont* font = wxTheFontList->FindOrCreateFont(12, wxSWISS, wxNORMAL, wxNORMAL); As with the pen and brush lists, use this with moderation because the fonts will be deleted only when the application exits. You can remove a font from the list without deleting it by using RemoveFont. We’ll see some examples of working with text and fonts later in the chapter. Also, you may like to play with the font demo in samples/font (see Figure 5-1). It lets you set a font to see how some text will appear, and you can change the font size and other properties. Figure 5-1 wxWidgets font demo

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 147 Drawing Tools 147 wxPalette A palette is a table, often with a size of 256, that maps index values to the red, green, and blue values of the display colors. It’s normally used when the dis- play has a very limited number of colors that need to be shared between appli- cations. By setting a palette for a client device context, an application’s color needs can be balanced with the needs of other applications. It is also used to map the colors of a low-depth bitmap to the available colors, and so wxBitmap has an optional associated wxPalette object. Because most computers now have full-color displays, palettes are rarely needed. RGB colors specified in an application are mapped to the nearest dis- play color with no need for a palette. A wxPalette can be created by passing a size and three arrays (unsigned char*) for each of the red, green, and blue components. You can query the num- ber of colors with GetColoursCount. To find the red, green, and blue values for a given index, use GetRGB, and you can find an index value for given red, green, and blue values with GetPixel. Set the palette into a client, window, or memory device context with wxDC::SetPalette. For example, you can set the palette obtained from a low- depth wxBitmap you are about to draw, so the system knows how to map the index values to device colors. When using drawing functions that use wxColour with a device context that has a palette set, the RGB color will be mapped automatically to the palette index by the system, so choose a palette that closely matches the colors you will be using. Another use for wxPalette is to query a wxImage or wxBitmap for the colors in a low-color image that was loaded from a file, such as a GIF. If there is an associated wxPalette object, it will give you a quick way to identify the unique colors in the original file, even though the image will have been converted to an RGB representation. Similarly, you can create and associate a palette with a wxImage that is to be saved in a reduced-color format. For example, the fol- lowing fragment loads a PNG file and saves it as an 8-bit Windows bitmap file: // Load the PNG wxImage image(wxT(“image.png”), wxBITMAP_TYPE_PNG); // Make a palette unsigned char* red = new unsigned char[256]; unsigned char* green = new unsigned char[256]; unsigned char* blue = new unsigned char[256]; for (size_t i = 0; i < 256; i ++) { red[i] = green[i] = blue[i] = i; } wxPalette palette(256, red, green, blue); // Set the palette and the BMP depth image.SetPalette(palette);

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 148 148 Drawing and Printing Chapter 5 image.SetOption(wxIMAGE_OPTION_BMP_FORMAT, wxBMP_8BPP_PALETTE); // Save the file image.SaveFile(wxT(“image.bmp”), wxBITMAP_TYPE_BMP); More realistic code would “quantize” the image to reduce the number of colors; see “Color Reduction” in Chapter 10, “Programming with Images,” for use of the wxQuantize class to do this. wxWidgets defines a null palette object, wxNullPalette. DEVICE CONTEXT DRAWING FUNCTIONS In this section, we’ll take a closer look at how we draw on device contexts. The major functions are summarized in Table 5-5. We cover most of them in the fol- lowing sections, and you can also find more details in the reference manual. Table 5-5 Device Context Functions Blit Copies from one device context to another. You can specify how much of the original to draw, where draw- ing should start, the logical function to use, and whether to use a mask if the source is a memory device context. Clear Fills the device context with the current background brush. SetClippingRegion Sets and destroys the clipping region, which restricts DestroyClippingRegion drawing to a specified area. The clipping region can GetClippingBox be specified as a rectangle or a wxRegion. Use GetClippingBox to get the rectangle surrounding the current clipping region. DrawArc Draws an arc or elliptic arc using the current pen and DrawEllipticArc brush. DrawBitmap Draws a wxBitmap or wxIcon at the specified location. DrawIcon The bitmap may have a mask to specify transparency. DrawCircle Draws a circle using the current pen and brush. DrawEllipse Draws an ellipse using the current pen and brush. DrawLine Draws a line or number of lines using the current DrawLines pen. The last point of the line is not drawn. DrawPoint Draws a point using the current pen. DrawPolygon DrawPolygon draws a filled polygon using an array of DrawPolyPolygon points or list of pointers to points, adding an optional offset coordinate. wxWidgets automatically closes the first and last points. DrawPolyPolygon draws one or more polygons at once, which can be a more efficient operation on some platforms.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 149 Device Context Drawing Functions 149 DrawRectangle Draws a rectangle or rounded rectangle using the DrawRoundedRectangle current pen and brush. DrawText Draws a text string, or rotated text string, at the DrawRotatedText specified point using the current text font and the current text foreground and background colors. DrawSpline Draws a spline between all given control points, using the current pen. FloodFill Flood fills the device context starting from the given point, using the current brush color. Ok Returns true if the device context is OK to use. SetBackground Sets and gets the background brush used in Clear GetBackground and in functions that use a complex logical function. The default is wxTRANSPARENT_BRUSH. SetBackgroundMode Sets and gets the background mode for drawing GetBackgroundMode text: wxSOLID or wxTRANSPARENT. Normally, you will want to set the mode to wxTRANSPARENT (the default) so the existing background will be kept when draw- ing text. SetBrush Sets and gets the brush to be used to fill shapes in GetBrush subsequent drawing operations. The initial value of the brush is undefined. SetPen Sets and gets the pen to be used to draw the outline GetPen of shapes in subsequent drawing operations. The ini- tial value of the pen is undefined. SetFont Sets and gets the font to be used in DrawText, GetFont DrawRotatedText, and GetTextExtent calls. The ini- tial value of the font is undefined. SetPalette Sets and gets wxPalette object mapping index values GetPalette to RGB colors. SetTextForeground Sets and gets the color to be used for text foreground GetTextForeground and background. The defaults are black and white, SetTextBackground respectively. GetTextBackground SetLogicalFunction The logical function determines how a source pixel GetLogicalFunction from a pen or brush color, or source device context if using Blit, combines with a destination pixel in the current device context. The default is wxCOPY, which simply draws with the current color. GetPixel Returns the color at the given point. This is not implemented for wxPostScriptDC and wxMetafileDC. GetTextExtent Returns metrics for a given text string. GetPartialTextExtents GetSize Returns the dimensions of the device in device units GetSizeMM or millimeters. (continues)

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 150 150 Drawing and Printing Chapter 5 Table 5-5 Device Context Functions (Continued) StartDoc Starts and ends a document. This is only applicable EndDoc to printer device contexts. When StartDoc is called, a message will be shown as the document is printed, and EndDoc will hide the message box. StartPage Starts and ends a page. This is only applicable to EndPage printer device contexts. DeviceToLogicalX Converts device coordinates to logical coordinates, DeviceToLogicalXRel either absolute (for positions) or relative (for widths DeviceToLogicalY and heights). DeviceToLogicalYRel LogicalToDeviceX Converts logical coordinates to device coordinates, LogicalToDeviceXRel either absolute (for positions) or relative (for widths LogicalToDeviceY and heights). LogicalToDeviceYRel SetMapMode As described earlier, this determines (along with GetMapMode SetUserScale) how logical units are converted to device units. SetAxisOrientation Sets the x- and y-axis orientation: the direction from lowest to highest values on the axis. The default ori- entation is to have the x-axis from left to right (true) and the y-axis from top to bottom (false). SetDeviceOrigin Sets and gets the device origin. You can use this to GetDeviceOrigin place a graphic in a particular place on a page, for example. SetUserScale Sets and gets the scale to be applied when converting GetUserScale from logical units to device units. Drawing Text The way text is drawn on a device context with DrawText is determined by the current font, the background mode (transparent or solid drawing), and the text foreground and background colors. If the background mode is wxSOLID, the area behind the text will be drawn in the current background color, and if wxTRANSPARENT, the text will be drawn without disturbing the background. Pass a string and either two integers or a wxPoint to DrawText. The text will be drawn with the given location at the very top-left of the string. Here’s a simple example of drawing a text string: // Draw a text string on a window at the given point void DrawTextString(wxDC& dc, const wxString& text, const wxPoint& pt) { wxFont font(12, wxFONTFAMILY_SWISS, wxNORMAL, wxBOLD); dc.SetFont(font); dc.SetBackgroundMode(wxTRANSPARENT);

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 151 Device Context Drawing Functions 151 dc.SetTextForeground(*wxBLACK); dc.SetTextBackground(*wxWHITE); dc.DrawText(text, pt); } You can also use the device context function DrawRotatedText to draw text at an angle by specifying the angle in degrees as the last parameter. The following code draws text at 45-degree increments, and the result is illustrated in Figure 5-2. wxFont font(20, wxFONTFAMILY_SWISS, wxNORMAL, wxNORMAL); dc.SetFont(font); dc.SetTextForeground(wxBLACK); dc.SetBackgroundMode(wxTRANSPARENT); for (int angle = 0; angle < 360; angle += 45) dc.DrawRotatedText(wxT(“Rotated text...”), 300, 300, angle); Figure 5-2 Drawing rotated text On Windows, only TrueType fonts can be drawn rotated. Be aware that the stock object wxNORMAL_FONT is not TrueType. Often, you’ll need to find out how much space text will take on a device context, which you can do by passing wxCoord (integer) pointers to the function GetTextExtent. Its prototype is void GetTextExtent(const wxString& string, wxCoord* width, wxCoord* height, wxCoord* descent = NULL, wxCoord* externalLeading = NULL, wxFont* font = NULL);

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 152 152 Drawing and Printing Chapter 5 The default arguments mean that you can call it just to find the overall width and height the string occupies, or you can pass extra arguments to get further text dimensions. If you imagine the bottoms of the characters sitting on a baseline, the descent is how far below the baseline the characters extend. The letter “g,” for example, extends below the baseline. External leading is the space between the descent of one line and the top of the line below. Finally, you can provide a font to be used in place of the current device context font. Here’s code that uses GetTextExtent to center a string on a window: void CenterText(const wxString& text, wxDC& dc, wxWindow* win) { // Set font, background mode for drawing text, // and text color dc.SetFont(*wxNORMAL_FONT); dc.SetBackgroundMode(wxTRANSPARENT); dc.SetTextForeground(*wxRED); // Get window and text dimensions wxSize sz = win->GetClientSize(); wxCoord w, h; dc.GetTextExtent(text, & w, & h); // Center the text on the window, but never // draw at a negative position. int x = wxMax(0, (sz.x - w)/2); int y = wxMax(0, (sz.y - h)/2); dc.DrawText(msg, x, y); } You can also use GetPartialTextExtents to retrieve the width of each character, passing a wxString and a wxArrayInt reference to receive the character width values. If you need accurate information about individual character widths, this can be quicker on some platforms than calling GetTextExtent for each character. Drawing Lines and Shapes The simpler drawing primitives include points, lines, rectangles, circles, and ellipses. The current pen determines the line or outline color, and the brush determines the fill color. For example: void DrawSimpleShapes(wxDC& dc) { // Set line color to black, fill color to green dc.SetPen(wxPen(*wxBLACK, 2, wxSOLID)); dc.SetBrush(wxBrush(*wxGREEN, wxSOLID)); // Draw a point dc.DrawPoint(5, 5);

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 153 Device Context Drawing Functions 153 // Draw a line dc.DrawLine(10, 10, 100, 100); // Draw a rectangle at (50, 50) with size (150, 100) // and hatched brush dc.SetBrush(wxBrush(*wxBLACK, wxCROSS_HATCH)); dc.DrawRectangle(50, 50, 150, 100); // Set a red brush dc.SetBrush(*wxRED_BRUSH); // Draw a rounded rectangle at (150, 20) with size (100, 50) // and corner radius 10 dc.DrawRoundedRectangle(150, 20, 100, 50, 10); // Draw another rounded rectangle with no border dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(*wxBLUE)); dc.DrawRoundedRectangle(250, 80, 100, 50, 10); // Set a black pen and black brush dc.SetPen(wxPen(*wxBLACK, 2, wxSOLID)); dc.SetBrush(*wxBLACK); // Draw a circle at (100, 150) with radius 60 dc.DrawCircle(100, 150, 60); // Set a white brush dc.SetBrush(*wxWHITE); // Draw an ellipse that fills the given rectangle dc.DrawEllipse(wxRect(120, 120, 150, 50)); } This produces the graphic in Figure 5-3. Figure 5-3 Drawing simple shapes Note that by convention, the last point of a line is not drawn.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 154 154 Drawing and Printing Chapter 5 To draw a circular arc, use DrawArc, taking a starting point, end point, and center point. The arc is drawn counterclockwise from the starting point to the end. For example: // Draw a cup-shaped arc int x = 10, y = 200, radius = 20; dc.DrawArc(x–radius, y, x + radius, y, x, y); This produces the arc shown in Figure 5-4. Figure 5-4 A circular arc For an elliptic arc, DrawEllipticArc takes the position and size of a rec- tangle that contains the arc, plus the start and end of the arc in degrees specified from the three o’clock position from the center of the rectangle. If the start and end points are the same, a complete ellipse will be drawn. The fol- lowing code draws the arc shown in Figure 5-5. // Draws an elliptical arc within a rectangle at (10, 100), // size 200x40. Arc extends from 270 to 420 degrees. dc.DrawEllipticArc(10, 100, 200, 40, 270, 420); Figure 5-5 An elliptical arc If you need to draw a lot of lines quickly, DrawLines can be more efficient than using DrawLine multiple times. The following example draws lines between ten points, at an offset of (100, 100). wxPoint points[10]; for (size_t i = 0; i < 10; i++) { pt.x = i*10; pt.y = i*20; }

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 155 Device Context Drawing Functions 155 int offsetX = 100; int offsetY = 100; dc.DrawLines(10, points, offsetX, offsetY); DrawLines does not fill the area surrounded by the lines. You can draw a filled shape with an arbitrary number of sides using DrawPolygon, and several of them with DrawPolyPolygon. DrawPolygon takes a point count, an array of points, optional offsets to add to the points, and an optional fill style: wxODDEVEN_RULE, the default, or wxWINDING_RULE. DrawPolygonPolygon additionally takes an array of integers that specifies the number of points to be used for each polygon. The following code demonstrates how to draw polygons and poly- polygons, with the result shown in Figure 5-6. void DrawPolygons(wxDC& dc) { wxBrush brushHatch(*wxRED, wxFDIAGONAL_HATCH); dc.SetBrush(brushHatch); wxPoint star[5]; star[0] = wxPoint(100, 60); star[1] = wxPoint(60, 150); star[2] = wxPoint(160, 100); star[3] = wxPoint(40, 100); star[4] = wxPoint(140, 150); dc.DrawPolygon(WXSIZEOF(star), star, 0, 30); dc.DrawPolygon(WXSIZEOF(star), star, 160, 30, wxWINDING_RULE); wxPoint star2[10]; star2[0] = wxPoint(0, 100); star2[1] = wxPoint(-59, -81); star2[2] = wxPoint(95, 31); star2[3] = wxPoint(-95, 31); star2[4] = wxPoint(59, -81); star2[5] = wxPoint(0, 80); star2[6] = wxPoint(-47, -64); star2[7] = wxPoint(76, 24); star2[8] = wxPoint(-76, 24); star2[9] = wxPoint(47, -64); int count[2] = {5, 5}; dc.DrawPolyPolygon(WXSIZEOF(count), count, star2, 450, 150); }

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 156 156 Drawing and Printing Chapter 5 Figure 5-6 Drawing polygons Drawing Splines DrawSpline lets you draw a curve known as a “spline” between multiple points. There is a version for three points, and a version for an arbitrary number of points, both illustrated in this example code: // Draw 3-point sline dc.DrawSpline(10, 100, 200, 200, 50, 230); // Draw 5-point spline wxPoint star[5]; star[0] = wxPoint(100, 60); star[1] = wxPoint(60, 150); star[2] = wxPoint(160, 100); star[3] = wxPoint(40, 100); star[4] = wxPoint(140, 150); dc.DrawSpline(WXSIZEOF(star), star); This produces the two splines illustrated in Figure 5-7. Figure 5-7 Drawing splines

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 157 Device Context Drawing Functions 157 Drawing Bitmaps There are two main ways of drawing bitmaps on a device context: DrawBitmap and Blit. DrawBitmap is a simplified form of Blit, and it takes a bitmap, a posi- tion, and a boolean flag specifying transparent drawing. The transparency can be either a simple mask or an alpha channel (which offers translucency), depending on how the bitmap was loaded or created. The following code loads an image with an alpha channel and draws it over lines of text. wxString msg = wxT(“Some text will appear mixed in the image’s shadow...”); int y = 75; for (size_t i = 0; i < 10; i++) { y += dc.GetCharHeight() + 5; dc.DrawText(msg, 200, y); } wxBitmap bmp(wxT(“toucan.png”), wxBITMAP_TYPE_PNG); dc.DrawBitmap(bmp, 250, 100, true); This produces the drawing in Figure 5-8, where the shadows in the bitmap appear to partially obscure the text underneath. Figure 5-8 Drawing with transparency The Blit function is more flexible and enables you to copy a specific por- tion of a source device context onto a destination device context. This is its pro- totype: bool Blit(wxCoord destX, wxCoord destY, wxCoord width, wxCoord height, wxDC* dcSource, wxCoord srcX, wxCoord srcY, int logicalFunc = wxCOPY, bool useMask = false, wxCoord srcMaskX = -1, wxCoord srcMaskY = -1);

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 158 158 Drawing and Printing Chapter 5 This code copies an area from a source device context dcSource to the destina- tion (the object that the function is operating on). An area of specified width and height is drawn starting at the position (destX, destY) on the destination surface, taken from the position (srcX, srcY) on the source. The logical function logicalFunc is usually wxCOPY, which means the bits are transferred from source to destination with no transformation. Not all platforms support a logi- cal function other than wxCOPY. For more information, please see “Logical Functions” later in this chapter. The last three parameters are used only when the source device context is a wxMemoryDC with a transparent bitmap selected into it. useMask specifies whether transparent drawing is used, and srcMaskX and srcMaskY enable the bitmap’s mask to start from a different position than the main bitmap start position. The following example loads a small pattern into a bitmap and uses Blit to fill a larger destination bitmap, with transparency if available. wxMemoryDC dcDest; wxMemoryDC dcSource; int destWidth = 200, destHeight = 200; // Create the destination bitmap wxBitmap bitmapDest(destWidth, destHeight); // Load the pattern bitmap wxBitmap bitmapSource(wxT(“pattern.png”), wxBITMAP_TYPE_PNG); int sourceWidth = bitmapSource.GetWidth(); int sourceHeight = bitmapSource.GetHeight(); // Clear the destination background to white dcDest.SelectObject(bitmapDest); dcDest.SetBackground(*wxWHITE_BRUSH); dcDest.Clear(); dcSource.SelectObject(bitmapSource); // Tile the smaller bitmap onto the larger bitmap for (int i = 0; i < destWidth; i += sourceWidth) for (int j = 0; j < destHeight; j += sourceHeight) { dcDest.Blit(i, j, sourceWidth, sourceHeight, & dcSource, 0, 0, wxCOPY, true); } // Tidy up dcDest.SelectBitmap(wxNullBitmap); dcSource.SelectBitmap(wxNullBitmap); You can also draw icons directly, with DrawIcon. This operation always takes transparency into account. For example:

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 159 Device Context Drawing Functions 159 #include “file.xpm” wxIcon icon(file_xpm); dc.DrawIcon(icon, 20, 30); Filling Arbitrary Areas FloodFill can be used to fill an arbitrary area of a device context up to a color boundary. Pass a starting point, a color for finding the flood area boundary, and a style to indicate how the color parameter should be used. The device context will be filled with the current brush color. The following example draws a green rectangle with a red border and fills it with black, followed by blue. // Draw a green rectangle outlines in red dc.SetPen(*wxRED_PEN); dc.SetBrush(*wxGREEN_BRUSH); dc.DrawRectangle(10, 10, 100, 100); dc.SetBrush(*wxBLACK_BRUSH); // Now fill the green area with black (while green is found) dc.FloodFill(50, 50, *wxGREEN, wxFLOOD_SURFACE); dc.SetBrush(*wxBLUE_BRUSH); // Then fill with blue (until red is encountered) dc.FloodFill(50, 50, *wxRED, wxFLOOD_BORDER); The function may fail if it cannot find the color specified, or the point is out- side the clipping region. FloodFill won’t work with printer device contexts, or with wxMetafileDC. Logical Functions The current logical function determines how a source pixel (from a pen or brush color, or source device context if using Blit) combines with a destination pixel in the current device context. The default is wxCOPY, which simply draws with the current color. The others combine the current color and the back- ground using a logical operation. wxINVERT is commonly used for drawing rub- ber bands or moving outlines because with this operation drawing a shape the second time erases the shape. The following example draws a dotted line using wxINVERT and then erases it before restoring the normal logical function. wxPen pen(*wxBLACK, 1, wxDOT); dc.SetPen(pen); // Invert pixels dc.SetLogicalFunction(wxINVERT);

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 160 160 Drawing and Printing Chapter 5 dc.DrawLine(10, 10, 100, 100); // Invert again, rubbing it out dc.DrawLine(10, 10, 100, 100); // Restore to normal drawing dc.SetLogicalFunction(wxCOPY); Another use for logical functions is to combine images to create new images. For example, here’s one method for creating transparent jigsaw puzzle pieces out of an image. First, draw a black outline of each shape on a white bitmap, using a grid of standard (but randomized) puzzle edges. Then, for each piece, flood-fill the outline to create a black puzzle shape on a white background. Blit the corresponding area of the puzzle image onto this template bitmap with the wxAND_REVERSE function to mask out the unwanted parts of the puzzle, leaving the “stamped out” puzzle piece on a black background. This can be made into a transparent wxBitmap by converting to a wxImage, setting black as the image mask color, and converting back to a transparent wxBitmap, which can be drawn appropriately. (Note that this technique depends on there being no black in the puzzle image, or else holes will appear in the puzzle pieces.) Table 5-6 shows the logical function values and their meanings. Table 5-6 Logical Functions Logical Function Meaning (src = source, dst = destination) wxAND src AND dst wxAND_INVERT (NOT src) AND dst wxAND_REVERSE src AND (NOT dst) wxCLEAR 0 wxCOPY src wxEQUIV (NOT src) XOR dst wxINVERT NOT dst wxNAND (NOT src) OR (NOT dst) wxNOR (NOT src) AND (NOT dst) wxNO_OP dst wxOR src OR dst wxOR_INVERT (NOT src) OR dst wxOR_REVERSE src OR (NOT dst) wxSET 1 wxSRC_INVERT NOT src wxXOR src XOR dst

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 161 Using the Printing Framework 161 USING THE PRINTING FRAMEWORK As we’ve seen, wxPrinterDC can be created and used directly. However, a more flexible method is to use the wxWidgets printing framework to “drive” print- ing. The main task for the developer is to derive a new class from wxPrintout, overriding functions that specify how to print a page (OnPrintPage), how many pages there are (GetPageInfo), document setup (OnPreparePrinting), and so on. The wxWidgets printing framework will show the print dialog, create the printer device context, and call appropriate wxPrintout functions when appro- priate. The same printout class can be used for both printing and preview. To start printing, a wxPrintout object is passed to a wxPrinter object, and Print is called to kick off the printing process, showing a print dialog before printing the pages specified by the layout object and the user. For example: // A global object storing print settings wxPrintDialogData g_printDialogData; // Handler for Print menu item void MyFrame::OnPrint(wxCommandEvent& event) { wxPrinter printer(& g_printDialogData); MyPrintout printout(wxT(“My printout”)); if (!printer.Print(this, &printout, true)) { if (wxPrinter::GetLastError() == wxPRINTER_ERROR) wxMessageBox(wxT(“There was a problem printing.\nPerhaps your current printer is not set correctly?”), wxT(“Printing”), wxOK); else wxMessageBox(wxT(“You cancelled printing”), wxT(“Printing”), wxOK); } else { (*g_printDialogData) = printer.GetPrintDialogData(); } } Because the Print function returns only after all pages have been rendered and sent to the printer, the printout object can be created on the stack. The wxPrintDialogData class stores data related to the print dialog, such as the pages the user selected for printing and the number of copies to be printed. It’s a good idea to keep a global wxPrintDialogData object in your appli- cation to store the last settings selected by the user. You can pass a pointer to this data to wxPrinter to be used in the print dialog, and then if printing is suc- cessful, copy the settings back from wxPrinter to your global object, as in the previous example. (In a real application, g_printDialogData would probably be

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 162 162 Drawing and Printing Chapter 5 a data member of your application class.) See Chapter 8, “Using Standard Dialogs,” for more about print and page dialogs and how to use them. To preview the document, create a wxPrintPreview object, passing two printout objects to it: one for the preview and one to use for printing if the user requests it. You can also pass a wxPrintDialogData object so that the preview picks up settings that the user chose earlier. Then pass the preview object to wxPreviewFrame, call the frame’s Initialize function, and show the frame. For example: // Handler for Preview menu item void MyFrame::OnPreview(wxCommandEvent& event) { wxPrintPreview *preview = new wxPrintPreview( new MyPrintout, new MyPrintout, & g_printDialogData); if (!preview->Ok()) { delete preview; wxMessageBox(wxT(“There was a problem previewing.\nPerhaps your current printer is not set correctly?”), wxT(“Previewing”), wxOK); return; } wxPreviewFrame *frame = new wxPreviewFrame(preview, this, wxT(“Demo Print Preview”)); frame->Centre(wxBOTH); frame->Initialize(); frame->Show(true); } When the preview frame is initialized, it disables all other top-level windows in order to avoid actions that might cause the document to be edited after the print or preview process has started. Closing the frame automatically destroys the two printout objects. Figure 5-9 shows the print preview window, with a control bar along the top providing page navigation, printing, and zoom control.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 163 Using the Printing Framework 163 Figure 5-9 Print preview window More on wxPrintout When creating a printout object, the application can pass an optional title that will appear in the print manager under some operating systems. You will need to provide at least GetPageInfo, HasPage, and OnPrintPage, but you can override any of the other methods below as well. GetPageInfo should be overridden to return minPage, maxPage, pageFrom, and pageTo. The first two integers represent the range supported by this print- out object for the current document, and the second two integers represent a user selection (not currently used by wxWidgets). The default values for minPage and maxPage are 1 and 32,000, respectively. However, the printout will stop printing if HasPage returns false. Typically, your OnPreparePrinting func- tion will calculate the values returned by GetPageInfo and will look something like this:

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 164 164 Drawing and Printing Chapter 5 void MyPrintout::GetPageInfo(int *minPage, int *maxPage, int *pageFrom, int *pageTo) { *minPage = 1; *maxPage = m_numPages; *pageFrom = 1; *pageTo = m_numPages; } HasPage must return false if the argument is outside the current page range. Often its implementation will look like this, where m_numPages has been calcu- lated in OnPreparePrinting: bool MyPrintout::HasPage(int pageNum) { return (pageNum >= 1 && pageNum <= m_numPages); } OnPreparePrinting is called before the print or preview process commences, and overriding it enables the application to do various setup tasks, including calculating the number of pages in the document. OnPreparePrinting can call wxPrintout functions such as GetDC, GetPageSizeMM, IsPreview, and so on to get the information it needs. OnBeginDocument is called with the start and end page numbers when each document copy is about to be printed, and if overridden, it must call the base wxPrintout::OnBeginDocument function. Similarly, wxPrintout::OnEndDocument must be called if overridden. OnBeginPrinting is called once for the printing cycle, regardless of the number of copies, and OnEndPrinting is called at the end. OnPrintPage is passed a page number, and the application should override it to return true if the page was successfully printed (returning false cancels the print job). This function will use wxPrintout::GetDC to get the device con- text to draw on. The following are the utility functions you can use in your overridden functions, and they do not need to be overridden. IsPreview can be called to determine whether this is a real print task or a preview. GetDC returns a suitable device context for the current task. When print- ing, a wxPrinterDC will be returned, and when previewing, a wxMemoryDC will be returned because a preview is rendered into a bitmap via a memory device context. GetPageSizeMM returns the size of the printer page in millimeters, whereas GetPageSizePixels returns the size in pixels (the maximum resolution of the printer). For a preview, this will not be the same as the size returned by wxDC::GetSize, which will return the preview bitmap size.

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 165 Using the Printing Framework 165 GetPPIPrinter returns the number of pixels per logical inch for the cur- rent device context, and GetPPIScreen returns the number of pixels per logical inch of the screen. Scaling for Printing and Previewing When drawing on a window, you probably don’t concern yourself about scaling your graphics because displays tend to have similar resolutions. However, there are several factors to take into account when drawing to a printer:

You need to scale and position graphics to fit the width of the page, and break the graphics into pages if necessary.

Fonts are based on screen resolution, so when drawing text, you need to set a scale so that the printer device context matches the screen resolu- tion. Dividing the printer resolution (GetPPIPrinter) by the screen resolu- tion (GetPPIScreen) can give a suitable scaling factor for drawing text.

When rendering the preview, wxWidgets uses a wxMemoryDC to draw into a bitmap. The size of the bitmap (returned by wxDC::GetSize) depends on the zoom scale, and an extra scale factor must be calculated to deal with this. Divide the size returned by GetSize by the actual page size returned by GetPageSizePixels to get this scale factor. This value should be multi- plied by any other scale you calculated. You can use wxDC::SetUserScale to let the device context perform the scal- ing for subsequent graphics operations and wxDC::SetDeviceOrigin to set the origin (for example, to center a graphic on a page). You can keep calling these scaling and device origin functions for different parts of your graphics, on the same page if necessary. The wxWidgets sample in samples/printing shows how to do scaling. The following example shows a function adapted from the printing sample, which scales and positions a 200×200 pixel graphic on a printer or preview device context. void MyPrintout::DrawPageOne(wxDC *dc) { // You might use THIS code if you were scaling // graphics of known size to fit on the page. // We know the graphic is 200x200. If we didn’t know this, // we’d need to calculate it. float maxX = 200; float maxY = 200; // Let’s have at least 50 device units margin float marginX = 50; float marginY = 50;

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 166 166 Drawing and Printing Chapter 5 // Add the margin to the graphic size maxX += (2*marginX); maxY += (2*marginY); // Get the size of the DC in pixels int w, h; dc->GetSize(&w, &h); // Calculate a suitable scaling factor float scaleX=(float)(w/maxX); float scaleY=(float)(h/maxY); // Use x or y scaling factor, whichever fits on the DC float actualScale = wxMin(scaleX,scaleY); // Calculate the position on the DC for centring the graphic float posX = (float)((w - (200*actualScale))/2.0); float posY = (float)((h - (200*actualScale))/2.0); // Set the scale and origin dc->SetUserScale(actualScale, actualScale); dc->SetDeviceOrigin( (long)posX, (long)posY ); // Now do the actual drawing dc.SetBackground(*wxWHITE_BRUSH); dc.Clear(); dc.SetFont(wxGetApp().m_testFont); dc.SetBackgroundMode(wxTRANSPARENT); dc.SetBrush(*wxCYAN_BRUSH); dc.SetPen(*wxRED_PEN); dc.DrawRectangle(0, 30, 200, 100); dc.DrawText( wxT(“Rectangle 200 by 100”), 40, 40); dc.SetPen( wxPen(*wxBLACK,0,wxDOT_DASH) ); dc.DrawEllipse(50, 140, 100, 50); dc.SetPen(*wxRED_PEN); dc.DrawText( wxT(“Test message: this is in 10 point text”), 10, 180); } In this code, we simply use wxDC::GetSize to get the preview or printer resolu- tion so we can fit the graphic on the page. In this example, we’re not interested in the points-per-inch printer resolution, as we might be if we were drawing text or lines of a specific length in millimeters, because the graphic doesn’t have to be a precise size: it’s just scaled to fit the available space. Next, we’ll show code that prints text at a size to match how it appears on the screen and that also draws lines that have a precise length, rather than simply being scaled to fit. void MyPrintout::DrawPageTwo(wxDC *dc) { // You might use THIS code to set the printer DC to roughly

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 167 Using the Printing Framework 167 // reflect the screen text size. This page also draws lines of // actual length 5cm on the page. // Get the logical pixels per inch of screen and printer int ppiScreenX, ppiScreenY; GetPPIScreen(&ppiScreenX, &ppiScreenY); int ppiPrinterX, ppiPrinterY; GetPPIPrinter(&ppiPrinterX, &ppiPrinterY); // This scales the DC so that the printout roughly represents the // the screen scaling. float scale = (float)((float)ppiPrinterX/(float)ppiScreenX); // Now we have to check in case our real page size is reduced // (e.g. because we’re drawing to a print preview memory DC) int pageWidth, pageHeight; int w, h; dc->GetSize(&w, &h); GetPageSizePixels(&pageWidth, &pageHeight); // If printer pageWidth == current DC width, then this doesn’t // change. But w might be the preview bitmap width, // so scale down. float overallScale = scale * (float)(w/(float)pageWidth); dc->SetUserScale(overallScale, overallScale); // Calculate conversion factor for converting millimetres into // logical units. // There are approx. 25.4 mm to the inch. There are ppi // device units to the inch. Therefore 1 mm corresponds to // ppi/25.4 device units. We also divide by the // screen-to-printer scaling factor, because we need to // unscale to pass logical units to DrawLine. // Draw 50 mm by 50 mm L shape float logUnitsFactor = (float)(ppiPrinterX/(scale*25.4)); float logUnits = (float)(50*logUnitsFactor); dc->SetPen(* wxBLACK_PEN); dc->DrawLine(50, 250, (long)(50.0 + logUnits), 250); dc->DrawLine(50, 250, 50, (long)(250.0 + logUnits)); dc->SetBackgroundMode(wxTRANSPARENT); dc->SetBrush(*wxTRANSPARENT_BRUSH); dc->SetFont(wxGetApp().m_testFont); dc->DrawText(wxT(“Some test text”), 200, 300 ); } Printing Under Unix with GTK+ Unlike Mac OS X and Windows, Unix does not provide a standard way to dis- play text and graphics onscreen and print it using the same API. Instead, screen display is done via the X11 library (via GTK+ and wxWidgets), whereas printing has to be done by sending a file of PostScript commands to the printer. Fonts are particularly tricky to handle; until recently, only a small number of applications have offered WYSIWYG (What You See Is What You

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 168 168 Drawing and Printing Chapter 5 Get) under Unix. In the past, wxWidgets offered its own printing implementa- tion using PostScript that never fully matched the screen display. From version 2.8, the GNOME Free Software Desktop Project provides printing support through the libgnomeprint and libgnomeprintui libraries by which most printing problems are solved. Beginning with version 2.5.4, the GTK+ port of wxWidgets can make use of these libraries if wxWidgets is configured accordingly and if the libraries are present. You need to config- ure wxWidgets with the --with-gnomeprint switch, which will cause your application to search for the GNOME print libraries at runtime. If they are found, printing will be done through these; otherwise, the application will fall back to the old PostScript printing code. Note that the application will not require the GNOME print libraries to be installed in order to run (there is no dependency on these libraries). 3D GRAPHICS WITH WXGLCANVAS It’s worth mentioning that wxWidgets comes with the capability of drawing 3D graphics, thanks to OpenGL and wxGLCanvas. You can use it with the OpenGL clone Mesa if your platform doesn’t support OpenGL. To enable wxGLCanvas support under Windows, edit include/wx/msw/ setup.h, set wxUSE_GLCANVAS to 1, and compile with USE_OPENGL=1 on the com- mand line. You may also need to add opengl32.lib to the list of libraries your program is linked with. On Unix and Mac OS X, pass --with-opengl to the configure script to compile using OpenGL or Mesa. If you’re already an OpenGL programmer, using wxGLCanvas is very sim- ple. You create a wxGLCanvas object within a frame or other container window, call wxGLCanvas::SetCurrent to direct regular OpenGL commands to the win- dow, issue normal OpenGL commands, and then call wxGLCanvas::SwapBuffers to show the OpenGL buffer on the window. The following paint handler shows the principles of rendering 3D graph- ics and draws a cube. The full sample can be compiled and run from samples/opengl/cube in your wxWidgets distribution. void TestGLCanvas::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); SetCurrent(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-0.5f, 0.5f, -0.5f, 0.5f, 1.0f, 3.0f); glMatrixMode(GL_MODELVIEW); /* clear color and depth buffers */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* draw six faces of a cube */ glBegin(GL_QUADS);

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 169 3D Graphics with wxGLCanvas 169 glNormal3f( 0.0f, 0.0f, 1.0f); glVertex3f( 0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(-0.5f,-0.5f, 0.5f); glVertex3f( 0.5f,-0.5f, 0.5f); glNormal3f( 0.0f, 0.0f,-1.0f); glVertex3f(-0.5f,-0.5f,-0.5f); glVertex3f(-0.5f, 0.5f,-0.5f); glVertex3f( 0.5f, 0.5f,-0.5f); glVertex3f( 0.5f,-0.5f,-0.5f); glNormal3f( 0.0f, 1.0f, 0.0f); glVertex3f( 0.5f, 0.5f, 0.5f); glVertex3f( 0.5f, 0.5f,-0.5f); glVertex3f(-0.5f, 0.5f,-0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glNormal3f( 0.0f,-1.0f, 0.0f); glVertex3f(-0.5f,-0.5f,-0.5f); glVertex3f( 0.5f,-0.5f,-0.5f); glVertex3f( 0.5f,-0.5f, 0.5f); glVertex3f(-0.5f,-0.5f, 0.5f); glNormal3f( 1.0f, 0.0f, 0.0f); glVertex3f( 0.5f, 0.5f, 0.5f); glVertex3f( 0.5f,-0.5f, 0.5f); glVertex3f( 0.5f,-0.5f,-0.5f); glVertex3f( 0.5f, 0.5f,-0.5f); glNormal3f(-1.0f, 0.0f, 0.0f); glVertex3f(-0.5f,-0.5f,-0.5f); glVertex3f(-0.5f,-0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f,-0.5f); glEnd(); glFlush(); SwapBuffers(); } Figure 5-10 shows another OpenGL sample, a cute (if angular) penguin that can be rotated using the mouse. You can find this sample in samples/opengl/penguin. Figure 5-10 OpenGL “penguin” sample

Smart_Ch05f.qxd 6/10/05 11:17 AM Page 170 170 Drawing and Printing Chapter 5 SUMMARY In this chapter, you have learned how to draw on device contexts and use the wxWidgets printing framework, and you received a quick introduction to wxGLCanvas. You can look at the following source code in your wxWidgets distri- bution for examples of drawing and printing code:

samples/drawing

samples/font

samples/erase

samples/image

samples/scroll

samples/printing

src/html/htmprint.cpp

demos/bombs

demos/fractal

demos/life For advanced 2D drawing applications, you might want to consider the wxArt2D library, which offers loading and saving of graphical objects using SVG files (Scalable Vector Graphics), flicker-free updating, gradients, vector paths, and more. See Appendix E, “ Third-Party Tools for wxWidgets,” for where to get wxArt2D. Next, we’ll look at how your application can respond to mouse, keyboard, and joystick input.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 171 C H A P T E R 6 Handling Input All GUI applications must respond to input in some way. This chapter shows how you can respond to user input from the mouse, keyboard, and joystick. MOUSE INPUT Broadly speaking, there are two categories of mouse input. Basic mouse events are sent using wxMouseEvent and are passed uninterpreted to your handler function. Commands associated with controls, on the other hand, are often the result of interpreting a mouse (or other) event as a particular command. For example, when you add EVT_BUTTON to an event table, you are inter- cepting a wxCommandEvent that was generated by the wxButton. Internally, the button is intercepting EVT_LEFT_DOWN and generating the command event as a result. (Of course, on most platforms, wxButton is implemented natively and doesn’t use low-level wxWidgets event handling, but it’s true of custom classes.) Because we’ve already seen examples of handling command events, we will concentrate on basic mouse events. You can intercept button up, button down, and double-click events for left, middle, and right mouse buttons. You can intercept motion events, whether the mouse is moving with or without buttons pressed. You can inter- cept events telling you that the mouse is entering or leaving the window. Finally, you can intercept scroll wheel events if the hardware provides a scroll wheel. When you receive a mouse event, you can also check the state of the mouse buttons, and the pressed state of the modifier keys (Shift, Alt, Control, and Meta). You can also retrieve the current mouse position relative to the top-left corner of the window’s client area. 171

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 172 172 Handling Input Chapter 6 Table 6-1 lists the event table macros you can use. wxMouseEvent does not propagate to parents of the originating window, so to handle these events, you must derive from a window class or derive from wxEvtHandler and plug the object into the window with SetEventHandler or PushEventHandler. Alternatively, you can use dynamic event handling with Connect. Table 6-1 Mouse Event Table Macros EVT_LEFT_DOWN(func Handles a wxEVT_LEFT_DOWN event, generated when the left mouse button changes to the “down” state. EVT_LEFT_UP(func) Handles a wxEVT_LEFT_UP event, generated when the left mouse button changes to the “up” state. EVT_LEFT_DCLICK(func) Handles a wxEVT_LEFT_DCLICK event, generated when the left mouse button is double-clicked. EVT_MIDDLE_DOWN(func) Handles a wxEVT_MIDDLE_DOWN event, gener- ated when the middle mouse button changes to the “down” state. EVT_MIDDLE_UP(func) Handles a wxEVT_MIDDLE_UP event, generated when the middle mouse button changes to the “up” state. EVT_MIDDLE_DCLICK(func) Handles a wxEVT_MIDDLE_DCLICK event, gener- ated when the middle mouse button is double-clicked. EVT_RIGHT_DOWN(func) Handles a wxEVT_RIGHT_DOWN event, generated when the right mouse button changes to the “down” state. EVT_RIGHT_UP(func) Handles a wxEVT_RIGHT_UP event, generated when the right mouse button changes to the “up” state. EVT_RIGHT_DCLICK(func) Handles a wxEVT_RIGHT_DCLICK event, gener- ated when the right mouse button is double-clicked. EVT_MOTION(func) Handles a wxEVT_MOTION event, generated when the mouse moves. EVT_ENTER_WINDOW(func) Handles a wxEVT_ENTER_WINDOW event, gener- ated when the mouse enters the window. EVT_LEAVE_WINDOW(func) Handles a wxEVT_LEAVE_WINDOW event, gener- ated when the mouse leaves the window. EVT_MOUSEWHEEL(func) Handles a wxEVT_MOUSEWHEEL event, generated when the mouse wheel moves. EVT_MOUSE_EVENTS(func) Handles all mouse events.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 173 Mouse Input 173 Handling Button and Motion Events These are the main mouse event functions that you can use within your event handler when handling mouse button and motion events. To test whether a modifier key is down at the time of generating the event, use AltDown, MetaDown, ControlDown, or ShiftDown. Use CmdDown if you want to test for either the Meta key (on Mac OS X) or the Control key (other platforms). See “Modifier Key Variations” later in the chapter for more on this. To determine which mouse button is currently pressed, use LeftIsDown, MiddleIsDown, and RightIsDown. You can also test whether a button is pressed by passing wxMOUSE_BTN_LEFT, wxMOUSE_BTN_MIDDLE, wxMOUSE_BTN_RIGHT or wxMOUSE_ BTN_ANY to Button. Note that these test whether a button is down at the time of the mouse event, rather than whether the button changed state. On Mac OS X, the Command key translates to Meta, and the Option key is Alt. Because the Mac is often configured with only one mouse button, the user holds down the Control key while clicking to generate a right-click event. This means that there is no such thing as Control-Right Click on Mac unless you have an external mouse with two or three buttons. You can test for the type of mouse event with Dragging (the mouse is mov- ing with a button pressed down), Moving (no button is currently pressed), Entering, Leaving, ButtonDown, ButtonUp, ButtonDClick, LeftClick, LeftDClick, LeftUp, RightClick, RightDClick, RightUp, ButtonUp, and IsButton. Retrieve the mouse position in device units (relative to the client win- dow’s top-left corner) with GetPosition or GetX and GetY. You can get the posi- tion in logical units by passing a device context to GetLogicalPosition. Here’s an example of mouse handling for a simple doodling application. BEGIN_EVENT_TABLE(DoodleCanvas, wxWindow) EVT_MOUSE_EVENTS(DoodleCanvas::OnMouseEvent) END_EVENT_TABLE() void DoodleCanvas::OnMouseEvent(wxMouseEvent& event) { static DoodleSegment *s_currentSegment = NULL; wxPoint pt(event.GetPosition()); if (s_currentSegment && event.LeftUp()) { // End the segment on mouse left up if (s_currentSegment->GetLines().GetCount() == 0) { // Empty segment: delete it delete s_currentSegment; s_currentSegment = (DoodleSegment *) NULL; } else { // We’ve got a valid segment, so store it DrawingDocument *doc = GetDocument(); doc->GetCommandProcessor()->Submit(

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 174 174 Handling Input Chapter 6 new DrawingCommand(wxT(“Add Segment”), DOODLE_ADD, doc, s_currentSegment)); doc->Modify(true); s_currentSegment = NULL; } } else if (m_lastX > -1 && m_lastY > -1 && event.Dragging()) { // We’re dragging: append a line to the current segment if (!s_currentSegment) s_currentSegment = new DoodleSegment; DoodleLine *newLine = new DoodleLine(m_lastX, m_lastY, pt.x, pt.y); s_currentSegment->GetLines().Append(newLine); wxClientDC dc(this); DoPrepareDC(dc); dc.SetPen(*wxBLACK_PEN); dc.DrawLine( m_lastX, m_lastY, pt.x, pt.y); } m_lastX = pt.x; m_lastY = pt.y; } In this application, line segments are stored in a document. While the user drags using the left button, the function adds lines to the current segment and also draws the lines. When the user releases the left mouse button, the current segment is submitted to the document using the command processor (part of the document-view framework), which allows undo/redo to be implemented. In the application’s OnPaint handler (not shown), all the document’s line seg- ments are drawn. For a complete doodling program with undo/redo, see Chapter 19, “Working with Documents and Views.” A more realistic application would capture the mouse on left button down and release it on left button up so that when dragging outside the win- dow, the window would still receive events. Handling Mouse Wheel Events When you get a mouse wheel event, you retrieve the positive or negative rota- tion amount with GetWheelRotation. Divide this value by the value returned by GetWheelDelta to get the number of lines that this rotation represents. Most devices generate one event per delta, but future devices may send events more frequently, so you need to be able to accumulate the amount of rotation and only take action when rotation equivalent to a full line has been received. Alternatively, you may be able to scroll by a fraction of a line. You should take into account the value returned by GetLinesPerAction, as configured by the user via the system control panel, and multiply by this amount to scroll the desired number of actual lines.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 175 Handling Keyboard Events 175 In fact, the mouse may be configured to scroll by a page at a time. In this case, you need to call IsPageScroll, and if this returns true, scroll by a page. To illustrate, here’s how wxScrolledWindow implements default scroll wheel processing. The variable m_wheelRotation accumulates the rotation, and action is only taken if the number of lines is non-zero. void wxScrollHelper::HandleOnMouseWheel(wxMouseEvent& event) { m_wheelRotation += event.GetWheelRotation(); int lines = m_wheelRotation / event.GetWheelDelta(); m_wheelRotation -= lines * event.GetWheelDelta(); if (lines != 0) { wxScrollWinEvent newEvent; newEvent.SetPosition(0); newEvent.SetOrientation(wxVERTICAL); newEvent.m_eventObject = m_win; if (event.IsPageScroll()) { if (lines > 0) newEvent.m_eventType = wxEVT_SCROLLWIN_PAGEUP; else newEvent.m_eventType = wxEVT_SCROLLWIN_PAGEDOWN; m_win->GetEventHandler()->ProcessEvent(newEvent); } else { lines *= event.GetLinesPerAction(); if (lines > 0) newEvent.m_eventType = wxEVT_SCROLLWIN_LINEUP; else newEvent.m_eventType = wxEVT_SCROLLWIN_LINEDOWN; int times = abs(lines); for (; times > 0; times—) m_win->GetEventHandler()->ProcessEvent(newEvent); } } } HANDLING KEYBOARD EVENTS Keyboard events are represented by the class wxKeyEvent. There are three dif- ferent kinds of keyboard events in wxWidgets: key down, key up, and charac- ter. Key down and up events are untranslated events, whereas character events are translated, which we’ll explain shortly. If the key is held down, you will typically get many down events but only one up event, so don’t assume that one up event corresponds to each down event.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 176 176 Handling Input Chapter 6 To receive key events, your window needs to have the keyboard focus, which you can achieve by calling wxWindow::SetFocus—for example, when a mouse button is pressed. Table 6-2 lists the three keyboard event table macros. Table 6-2 Keyboard Event Table Macros EVT_KEY_DOWN(func) Handles a wxEVT_KEY_DOWN event (untranslated key press). EVT_KEY_UP(func) Handles a wxEVT_KEY_UP event (untranslated key release). EVT_CHAR(func) Handles a wxEVT_CHAR event (translated key press). These are the main wxKeyEvent functions that you can use within your key event handler when handling keyboard events. To get the keycode, call GetKeyCode (in Unicode builds, you can also call GetUnicodeKeyCode). All valid key codes are listed in Table 6-3. Table 6-3 Key Code Identifiers WXK_BACK WXK_RIGHT WXK_TAB WXK_DOWN WXK_RETURN WXK_SELECT WXK_ESCAPE WXK_PRINT WXK_SPACE WXK_EXECUTE WXK_DELETE WXK_SNAPSHOT WXK_INSERT WXK_START WXK_HELP WXK_LBUTTON WXK_RBUTTON WXK_NUMPAD0 WXK_CANCEL WXK_NUMPAD1 WXK_MBUTTON WXK_NUMPAD2 WXK_CLEAR WXK_NUMPAD3 WXK_SHIFT WXK_NUMPAD4 WXK_CONTROL WXK_NUMPAD5 WXK_MENU WXK_NUMPAD6 WXK_PAUSE WXK_NUMPAD7 WXK_CAPITAL WXK_NUMPAD8 WXK_PRIOR WXK_NUMPAD9 WXK_NEXT WXK_END WXK_MULTIPLY WXK_HOME WXK_ADD WXK_LEFT WXK_SEPARATOR WXK_UP WXK_SUBTRACT

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 177 Handling Keyboard Events 177 WXK_DECIMAL WXK_PAGEDOWN WXK_DIVIDE WXK_NUMPAD_SPACE WXK_F1 WXK_NUMPAD_TAB WXK_F2 WXK_NUMPAD_ENTER WXK_F3 WXK_F4 WXK_NUMPAD_F1 WXK_F5 WXK_NUMPAD_F2 WXK_F6 WXK_NUMPAD_F3 WXK_F7 WXK_NUMPAD_F4 WXK_F8 WXK_NUMPAD_HOME WXK_F9 WXK_NUMPAD_LEFT WXK_F10 WXK_NUMPAD_UP WXK_F11 WXK_NUMPAD_RIGHT WXK_F12 WXK_NUMPAD_DOWN WXK_F13 WXK_NUMPAD_PRIOR WXK_F14 WXK_NUMPAD_PAGEUP WXK_F15 WXK_NUMPAD_NEXT WXK_F16 WXK_NUMPAD_PAGEDOWN WXK_F17 WXK_NUMPAD_END WXK_F18 WXK_NUMPAD_BEGIN WXK_F19 WXK_NUMPAD_INSERT WXK_F20 WXK_NUMPAD_DELETE WXK_F21 WXK_NUMPAD_EQUAL WXK_F22 WXK_NUMPAD_MULTIPLY WXK_F23 WXK_NUMPAD_ADD WXK_F24 WXK_NUMPAD_SEPARATOR WXK_NUMPAD_SUBTRACT WXK_NUMLOCK WXK_NUMPAD_DECIMAL WXK_SCROLL WXK_NUMPAD_DIVIDE WXK_PAGEUP To test whether a modifier key is pressed down at the time of generating the event, use AltDown, MetaDown, ControlDown, or ShiftDown. HasModifiers returns true if either Control or Alt was down at the time of the key event (but not the Shift or Meta key states). Instead of using ControlDown or MetaDown, you may want to use the higher- level CmdDown function that calls MetaDown on Mac OS X and ControlDown on other platforms. See also “Modifier Key Variations” in the following section for fur- ther explanation. GetPosition returns the position of the mouse pointer in client coordi- nates at the time the event was received.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 178 178 Handling Input Chapter 6 Tip If a key down event is caught and the event handler does not call event.Skip(), then the corresponding character event will not happen. If you don’t call event.Skip() for events that you don’t process in key event function, shortcuts may cease to work on some platforms. An Example Character Event Handler Here’s the key handler from the wxThumbnailCtrl sample that you can find in examples/chap12/thumbnail on the CD-ROM: BEGIN_EVENT_TABLE( wxThumbnailCtrl, wxScrolledWindow ) EVT_CHAR(wxThumbnailCtrl::OnChar) END_EVENT_TABLE() void wxThumbnailCtrl::OnChar(wxKeyEvent& event) { int flags = 0; if (event.ControlDown()) flags |= wxTHUMBNAIL_CTRL_DOWN; if (event.ShiftDown()) flags |= wxTHUMBNAIL_SHIFT_DOWN; if (event.AltDown()) flags |= wxTHUMBNAIL_ALT_DOWN; if (event.GetKeyCode() == WXK_LEFT || event.GetKeyCode() == WXK_RIGHT || event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_DOWN || event.GetKeyCode() == WXK_HOME || event.GetKeyCode() == WXK_PAGEUP || event.GetKeyCode() == WXK_PAGEDOWN || event.GetKeyCode() == WXK_PRIOR || event.GetKeyCode() == WXK_NEXT || event.GetKeyCode() == WXK_END) { Navigate(event.GetKeyCode(), flags); } else if (event.GetKeyCode() == WXK_RETURN) { wxThumbnailEvent cmdEvent( wxEVT_COMMAND_THUMBNAIL_RETURN, GetId()); cmdEvent.SetEventObject(this); cmdEvent.SetFlags(flags); GetEventHandler()->ProcessEvent(cmdEvent); } else event.Skip(); } For clarity, the navigation key handling is delegated to a separate function, Navigate. Pressing the Return or Enter key generates a higher-level command

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 179 Handling Keyboard Events 179 event that an application using the control can catch; for all other key presses, Skip is called to enable other parts of the application to process unused key events. Key Code Translation Key events provide untranslated key codes, whereas the character event pro- vides a translated key code. The untranslated code for alphanumeric keys is always an uppercase value. For the other keys, it is one of the WXK_XXX values from the keycodes table. The translated key is, in general, the character the user expects to appear as the result of the key combination when typing text into a text entry field. Here are a few examples to clarify this. When the A key is pressed, the key down event key code is equal to ASCII “A” (65), but the character event key code is ASCII A (97). On the other hand, if you press both the Shift and A keys simultaneously, the key code in the key down event will still be A, while the character event key code will now be A as well. In this simple case, it is clear that the ASCII code could be found in the key down event handler by checking both the untranslated key code and the value returned by ShiftDown. But in general, if you want the ASCII key code, you should use the character event (with EVT_CHAR) because for non- alphanumeric keys, the translation is dependent on keyboard layout and can only be done properly by the system itself. Another kind of translation is done when the Control key is pressed: for example, for Ctrl+A, the key down event still passes the same key code A as usual, but the character event will have a key code of 1, which is the ASCII value of this key combination. You may discover how the other keys on your system behave inter- actively by running the keyboard sample ( samples/keyboard ) and press- ing keys. Modifier Key Variations On Windows, there are Control and Alt modifier keys, and the special Windows key acts as the Meta key. On Unix, the key that acts as Meta is con- figurable (run xmodmap to see how your system is configured). The Numlock key is sometimes configured as a Meta key, and this is the reason HasModifiers does not return true if the Meta key is down—this allows key presses to be processed normally when Numlock is on. On Mac OS X, the Command key (with the apple symbol) translates to Meta, while the Option key translates to Alt. These differences are shown in Table 6-4, with the wxWidgets modifier name shown in the first column, and the key used for this modifier on each of the three major platforms. The Mac’s Option and Command keys are illus- trated for clarification.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 180 180 Handling Input Chapter 6 Table 6-4 Modifier Keys Under Windows, Unix, and Mac OS X Modifier Key on Windows Key on Unix Key on Mac Shift Shift Shift Shift Control Control Control Control Alt Alt Alt Option Meta Windows (Configurable) Command Because Mac OS X uses the Command key as a modifier where Control is used on other platforms, you may use wxKeyEvent’s CmdDown function instead of ControlDown or MetaDown to catch the appropriate command modi- fier on each platform. Note that as well as testing for a modifier key from within an event han- dler function, you can pass a key code to wxGetKeyState to test whether that key is down. Accelerators An accelerator implements a keyboard shortcut for a menu command, enabling the user to execute that command quickly. These shortcuts take precedence over other keyboard processing, such as EVT_CHAR handlers. Standard shortcuts include Ctrl+O to open a file and Ctrl+V to paste data into the application. The easiest way to implement accelerators is to specify them in menu items. For example: menu->Append(wxID_COPY, wxT(“Copy\tCtrl+C”)); wxWidgets interprets the text after the “tab” character as an accelerator and adds it to the menu’s accelerator table. In this example, when the user presses Ctrl+C the wxID_COPY command is sent, just as though the menu item was selected. You can use Ctrl, Alt, or Shift in various combinations, followed by a + or - and a character or function key. The following are all valid accelerator speci- fications: Ctrl+B, G, Shift-Alt-K, F9, Ctrl+F3, Esc, and Del. You can use the fol- lowing special key names: Del, Back, Ins, Insert, Enter, Return, PgUp, PgDn, Left, Right, Up, Down, Home, End, Space, Tab, Esc, and Escape. Case is not significant when interpreting the names (any combination of uppercase and lowercase will work). Note that on Mac OS X, a shortcut specification involving Ctrl will actu- ally use the Command key. Another way to provide accelerators is to populate an wxAccelerator Table with wxAcceleratorEntry objects and associate it with a window using

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 181 Handling Joystick Events 181 wxWindow::SetAcceleratorTable. Each wxAcceleratorEntry is initialized with a bit- list of modifiers (one or more of wxACCEL_ALT, wxACCEL_CTRL, wxACCEL_SHIFT, and wxACCEL_NORMAL), a key code (see Table 6-3), and an identifier. For example: wxAcceleratorEntry entries[4]; entries[0].Set(wxACCEL_CTRL, (int) ‘N’, wxID_NEW); entries[1].Set(wxACCEL_CTRL, (int) ‘X’, wxID_EXIT); entries[2].Set(wxACCEL_SHIFT, (int) ‘A’, wxID_ABOUT); entries[3].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_CUT); wxAcceleratorTable accel(4, entries); frame->SetAcceleratorTable(accel); You can use several accelerator tables in a window hierarchy, and you can combine menu string accelerator specifications with an explicit wxAcceleratorTable. This is useful if you have alternative accelerators for a sin- gle command, which you cannot entirely specify in the menu item label. HANDLING JOYSTICK EVENTS The wxJoystick class gives your application control over one or two joysticks on Windows or Linux. Typically, you’ll create a wxJoystick object passing wxJOYSTICK1 or wxJOYSTICK2 and keep the object on the heap while it’s needed. When you need input, call SetCapture passing a window pointer for receiving the joystick events, and then call ReleaseCapture when you no longer need the events. You might set the capture for the lifetime of the application instance (that is, calling SetCapture on initialization and ReleaseCapture on exit). Before describing the events and functions in more detail, let’s take a look at samples/joystick from the wxWidgets distribution. The user can con- trol the joystick to draw a sequence of lines on a canvas by clicking on one of the joystick’s buttons. Pressing the button also plays a sound. The following is a snippet of the initialization code. First, the application checks whether a joystick is installed by creating a temporary joystick object, terminating if a joystick isn’t found. The buttonpress.wav sound file is loaded into the wxSound object stored in the application object, and the minimum and maximum joystick positions are stored to permit scaling input to the size of the drawing window. #include “wx/wx.h” #include “wx/sound.h” #include “wx/joystick.h” bool MyApp::OnInit() { wxJoystick stick(wxJOYSTICK1); if (!stick.IsOk()) {

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 182 182 Handling Input Chapter 6 wxMessageBox(wxT(“No joystick detected!”)); return false; } m_fire.Create(wxT(“buttonpress.wav”)); m_minX = stick.GetXMin(); m_minY = stick.GetYMin(); m_maxX = stick.GetXMax(); m_maxY = stick.GetYMax(); // Create the main frame window ... return true; } MyCanvas is a window that stores the joystick object and also receives the joy- stick events. Here’s the implementation of MyCanvas. BEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow) EVT_JOYSTICK_EVENTS(MyCanvas::OnJoystickEvent) END_EVENT_TABLE() MyCanvas::MyCanvas(wxWindow *parent, const wxPoint& pos, const wxSize& size): wxScrolledWindow(parent, wxID_ANY, pos, size, wxSUNKEN_BORDER) { m_stick = new wxJoystick(wxJOYSTICK1); m_stick->SetCapture(this, 10); } MyCanvas::~MyCanvas() { m_stick->ReleaseCapture(); delete m_stick; } void MyCanvas::OnJoystickEvent(wxJoystickEvent& event) { static long xpos = -1; static long ypos = -1; wxClientDC dc(this); wxPoint pt(event.GetPosition()); // if negative positions are possible then shift everything up int xmin = wxGetApp().m_minX; int xmax = wxGetApp().m_maxX; int ymin = wxGetApp().m_minY; int ymax = wxGetApp().m_maxY; if (xmin < 0) { xmax += abs(xmin); pt.x += abs(xmin); }

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 183 Handling Joystick Events 183 if (ymin < 0) { ymax += abs(ymin); pt.y += abs(ymin); } // Scale to canvas size int cw, ch; GetSize(&cw, &ch); pt.x = (long) (((double)pt.x/(double)xmax) * cw); pt.y = (long) (((double)pt.y/(double)ymax) * ch); if (xpos > -1 && ypos > -1 && event.IsMove() && event.ButtonIsDown()) { dc.SetPen(*wxBLACK_PEN); dc.DrawLine(xpos, ypos, pt.x, pt.y); } xpos = pt.x; ypos = pt.y; wxString buf; if (event.ButtonDown()) buf.Printf(wxT(“Joystick (%d, %d) Fire!”), pt.x, pt.y); else buf.Printf(wxT(“Joystick (%d, %d)”), pt.x, pt.y); frame->SetStatusText(buf); if (event.ButtonDown() && wxGetApp().m_fire.IsOk()) { wxGetApp().m_fire.Play(); } } wxJoystick Events wxJoystick generates events of type wxJoystickEvent, and the relevant event table macros are listed in Table 6-5. Each event table macro takes a single argument: the event handler function. Table 6-5 Joystick Event Table Macros EVT_JOY_BUTTON(func) Handles a wxEVT_JOY_BUTTON_DOWN event, generated when a button is pressed. EVT_JOY_BUTTON(func) Handles a wxEVT_JOY_BUTTON_UP event, gener- ated when a button is released. EVT_JOY_MOVE(func) Handles a wxEVT_JOY_MOVE event, generated when the joystick is moved in the X-Y plane. EVT_JOY_ZMOVE(func) Handles a wxEVT_JOY_ZMOVE event, generated when the joystick is moved in the z-axis. EVT_JOYSTICK_EVENTS(func) Handles all joystick events.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 184 184 Handling Input Chapter 6 wxJoystickEvent Member Functions These are the wxJoystickEvent functions you can call to retrieve more informa- tion about the event. As usual, you can call GetEventType to get the type, which is useful if you are using EVT_JOYSTICK_EVENTS to catch all joystick events. Call ButtonDown to check if the event was a button press event; you can optionally pass a button identifier wxJOY_BUTTONn (where n is 1, 2, 3, or 4) to test which button was pressed, or wxJOY_BUTTON_ANY if you don’t care which but- ton was pressed. ButtonUp is similar but tests for a button up event. The func- tion IsButton is equivalent to ButtonDown() || ButtonUp(). To test whether a button is down at the time of an event (not whether the event itself was a button press), call ButtonIsDown with the same arguments as for ButtonDown. Alternatively, use GetButtonState with the same arguments to return a bit-list of wxJOY_BUTTONn identifiers. Call IsMove to test whether the event was a move event in the X-Y plane and IsZMove for a move in the z-axis. GetPosition returns a wxPoint for the current joystick position in the X-Y plane, while GetZPosition returns an integer representing the Z position, if supported. Finally, you can determine which joystick controller generated the event (wxJOYSTICK1 or wxJOYSTICK2) by calling GetJoystick. wxJoystick Member Functions We won’t list the joystick functions exhaustively—you can refer to the wxJoystick class reference for that—but the following are the more inter- esting ones. As we’ve seen in the example, SetCapture needs to be called to direct joystick input to a specified window, with a matching ReleaseCapture to release it and enable other applications to take control of the joystick. In case another application has captured the joystick, or the joystick is not functioning, call IsOK before trying to capture input. You can also determine the capabilities of the joystick with GetNumberButtons, GetNumberJoysticks, GetNumberAxes, HasRudder, and other functions. You can get the state of the joystick from outside an event handler with functions such as GetPosition and GetButtonState. Your application will almost always need to call GetXMin, GetXMax, and similar functions in order to determine the range supported by the joystick.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 185 Summary 185 SUMMARY In this chapter, you have learned about mouse, keyboard, and joystick input, and you can now add sophisticated interaction to your applications. For more insight, see wxWidgets samples such as samples/keyboard, samples/joytest, and samples/dragimag, and also the wxThumbnailCtrl class in examples/chap12 on the CD-ROM. The next chapter describes how you can achieve window layouts that are resizable, portable, translation-friendly, and above all attractive by using our flexible friend, the sizer.

Smart_Ch06f.qxd 6/10/05 11:18 AM Page 186

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 187 C H A P T E R 7 Window Layout Using Sizers As graphic designers will testify, people are very sensitive to the way that visual objects are arranged. A GUI framework must allow the creation of a visually appealing layout, but unlike with print layout, an application’s win- dows must often dynamically adapt to changes in size, font preferences, and even language. For platform-independent programming, the layout must also take into account the different sizes of individual controls from one platform to the next. All this means that a näive approach using absolute positions and sizes for controls simply won’t work. This chapter describes wxWidgets’ sys- tem of sizers, which gives you all the flexibility you need for even the most complex layouts. If it seems a bit daunting at first, remember that there are tools that will help you create sizer-based layouts—such as DialogBlocks, included on the accompanying CD-ROM—and you will rarely need to create entire layouts by hand. LAYOUT BASICS Before taking the plunge into the world of sizers, let’s review where you might need to program layout behavior and what options you have. A simple case is where you have a frame with a single window inside the client area. This is so simple that wxWidgets does the layout itself, fitting the child window to the size of the frame client area. For each frame, wxWidgets also manages the menu bar, one toolbar, and one status bar if they have been associated with the frame. If you need two toolbars, then you have to manage at least one of them yourself. If you have more than one child window in the client area, then wxWidgets expects you to manage them explicitly. You can do this with an OnSize event handler and calculate the position and size for each window and then set them. Or, you can use sizers. Similarly, if you create a custom control that consists of several child windows, you need to arrange for the child windows to resize appropriately when the overall control is resized. 187

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 188 188 Window Layout Using Sizers Chapter 7 Most applications have custom dialogs, sometimes dozens of them. The dialogs may be resizable, in which case the layout should look sensible even when the dialog is much larger than the initial size. The language may be changed, making some elements much larger or smaller than in the default language. If you had to program a hundred resize-friendly dialogs by hand, even with sizers, it would be almost impossibly daunting, so it’s fortunate that editors are available to make this task simple—even a pleasure. If (and when!) you choose to use sizers, you need to decide how you will create and deploy them. You or your dialog editor can create code in C++ or another language, or you can use XRC files, which are a declarative XML spec- ification of the sizer layout. XRC files can be loaded dynamically or embedded in the executable by compiling them into C++ files with the utility wxrc. Most dialog editors can generate both code and XRC files. Your choice of code or XRC may be a matter of taste; perhaps you prefer to separate the layout from the class, or maybe you prefer the additional flexibility of tweaking the C++ code and the immediacy of having it in the same file as the class. The next section describes the principles behind sizers, and the following sections describe how to program with individual sizer classes. SIZERS The layout algorithm used by sizers in wxWidgets is closely related to layout systems in other GUI toolkits, such as Java’s AWT, the GTK+ toolkit, or the Qt toolkit. It is based upon the idea of individual windows reporting their mini- mal required size and their ability to be stretched if the size of the parent win- dow has changed. This will most often mean that the programmer does not set the initial size of a dialog; instead, the dialog will be assigned a sizer, which will be queried about the recommended size. This sizer in turn will query its children (which can be windows, empty space, or other sizers) and further descendants. Note that wxSizer does not derive from wxWindow and thus does not interfere with tab ordering and requires very few resources compared to a real window. Sizers form a containment hierarchy parallel to the actual win- dow hierarchy: the sizer hierarchy for a complex dialog may be many deep, but the controls themselves will probably all be siblings with the dialog as their parent. This sizer hierarchy is depicted graphically in wxWidgets dialog edi- tors. Figure 7-1 shows Anthemion Software’s DialogBlocks editing the Personal Record dialog that we will use as an example in Chapter 9, “Creating Custom Dialogs.” A red border in the editor pane surrounds the currently selected element, and its immediate parent is shown with a blue

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 189 Sizers 189 border. The tree you see on the left represents the sizer view of the hierar- chy, but all controls are still parented directly on the dialog as far as the window hierarchy is concerned. Figure 7-1 Viewing a sizer hierarchy in a dialog editor To get a mental picture of how nested sizers work, consider Figure 7-2, a schematic view of the dialog being edited in Figure 7-1. The shaded areas rep- resent actual windows, while the white boxes represent sizers. There are two vertical sizers inside the dialog (to give extra space around the edge of the dia- log), and two horizontal sizers within the inner vertical sizer. A spacer is employed inside a horizontal sizer to keep one control away from another group of controls. As you can see, creating a sizer-based layout is really like sorting through a collection of different-sized cardboard boxes, placing smaller boxes in bigger boxes, and adding objects and packing material inside some of them. Of course, the analogy is imperfect because cardboard doesn’t stretch! There are currently five basic sizer classes available in wxWidgets. Each either represents a specific way to lay out windows or fulfills a special task such as wrapping a static box around another element. These sizers will be discussed in the following sections.

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 190 190 Window Layout Using Sizers Chapter 7 PersonalRecordDialog Vertical wxBoxSizer Vertical wxBoxSizer Horizontal wxBoxSizer Spacer Horizontal wxBoxSizer Figure 7-2 A schematic view of the sizers for PersonalRecordDialog Common Features of Sizers All sizers are containers—that is, they are used to lay out one or more ele- ments, which they contain. No matter how the individual sizers lay out their children, all children have certain features in common. A minimal size: The minimal size of a control is calculated from the control’s notion of its “best size” (supplied by implementing DoGetBestSize for each control). This is the control’s natural size. For example, the best size of a check box comprises the space taken up by the check box graphic and the label. However, if you want the initial window size (as passed to the window’s constructor) to be used as the minimal size instead, you can use the wxFIXED_MINSIZE style when adding the control to a sizer. Note that only some windows can calculate their size (such as a check box), whereas others (such as a list box) don’t have any natural width or height and thus require an

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 191 Sizers 191 explicit size. Some windows can calculate their height, but not their width— for example, a single-line text control. Figure 7-3 shows three controls on their own in a dialog, showing how they expand the dialog to fit their own minimal size. Figure 7-3 Windows reporting their minimal size A border: The border is just empty space that is used to separate ele- ments. This border can be all around, or it can be at any combination of sides, such as only above and below the control. The thickness of this border must be set explicitly, typically 5 pixels. Figure 7-4 shows dialogs with only one control (a button) and a border of 0, 5, and 10 pixels around the button, respectively. Figure 7-4 Different border sizes An alignment: An element can be moved to the center of the available space, or to either side of the space. Figure 7-5 shows a horizontal box sizer containing a list box and three buttons. One button is centered, one is aligned at the top, and one is aligned at the bottom. Alignment can be specified in hor- izontal or vertical orientations, but for most sizers, only one of these will have an effect. For example, in Figure 7-5, we have specified alignment in the verti- cal orientation, but horizontal alignment isn’t possible because of the way space is distributed among children of the sizer. (To achieve the effect of hori- zontal alignment, we would need to insert a stretching spacer, which we will look at shortly.)

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 192 192 Window Layout Using Sizers Chapter 7 Figure 7-5 Sizer alignment A stretch factor: If a sizer contains more than one child and is offered more space than its children and their borders need, the surplus space needs to be distributed among the children. For this purpose, a stretch factor may be assigned to each child, where the default value of zero indicates that the child will not get more space than its requested minimal size. A value of more than zero is interpreted in relation to the sum of all stretch factors in the children of the respective sizer, so if two children get a stretch factor of 1, they will each get half the extra space, independent of their minimal size. Figure 7-6 shows a dialog with three buttons, at the initial size and after resizing. The first button has a stretch factor of 1 and thus gets stretched, whereas the other two but- tons have a stretch factor of zero and keep their initial width. Note that the term “proportion” is sometimes used instead of stretch fac- tor in the wxWidgets reference manual. Figure 7-6 Stretch factor PROGRAMMING WITH SIZERS To create a sizer-based layout, create a top-level sizer (any kind of sizer may be used) and associate it with the parent window with wxWindow::SetSizer. Now you can hang your hierarchy of windows and further sizers from the top- level sizer. If you want the top-level window to fit itself around the contents, you call wxSizer::Fit passing the top-level window. If the window should never be resized smaller than the initial size, call wxSizer::SetSizeHints passing the top-level window. This will call wxWindow::SetSizeHints with the appropriate values. Instead of the three functions described in the previous paragraph, you can simply call the function wxWindow::SetSizerAndFit, which sets the sizer, calls Fit, and also calls SetSizeHints.

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 193 Programming with Sizers 193 If you have a panel inside a frame, you may be wondering which window gets the top-level sizer. Assuming you only have one panel in the frame, the frame already knows how to size the panel to fill the frame’s client area when the frame size is changed. Therefore, you should set the sizer for the panel to manage the panel’s children. If you had more than one panel in the frame, you might set a top-level sizer for the frame, which would manage the frame’s chil- dren. However, you would still need a top-level sizer for each child panel that had its own children to lay out. The following sections describe each kind of sizer and how to use it. Programming with wxBoxSizer wxBoxSizer can lay out its children either vertically or horizontally, depending on the style passed to its constructor. When using a vertical sizer, each child can be centered, aligned to the right, or aligned to the left. Correspondingly, when using a horizontal sizer, each child can be centered, aligned at the bot- tom, or aligned at the top. The stretch factor described previously is used for the main orientation, so when using a horizontal box sizer, the stretch factor determines how much the child can be stretched horizontally. Figure 7-7 shows the same dialog as in Figure 7-6, except that the sizer is a vertical box sizer. Figure 7-7 A verical wxBoxSizer You add child elements to a box sizer with Add: // Add a window void Add(wxWindow* window, int stretch = 0, int flags = 0, int border = 0); // Add a sizer void Add(wxSizer* window, int stretch = 0, int flags = 0, int border = 0);

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 194 194 Window Layout Using Sizers Chapter 7 The first parameter is a window or sizer. The second is the proportion or stretch factor. The third parameter is a bit-list specifying alignment behavior and borders. The alignment flags specify what happens when a vertical sizer changes its width, or when a horizontal sizer changes its height. The allowed values for specifying alignment and borders are shown in Table 7-1. The default alignment is wxALIGN_LEFT | wxALIGN_TOP. Table 7-1 Sizer Flags 0 Indicates that the window will preserve its original size. wxGROW Forces the window to grow with the sizer. wxEXPAND is a synonym for wxGROW. wxSHAPED Tells the window to change its size proportionally, preserving the original aspect ratio. wxALIGN_LEFT Aligns to the left edge of the sizer. wxALIGN_RIGHT Aligns to the right edge of the sizer. wxALIGN_TOP Aligns to the top edge of the sizer. wxALIGN_BOTTOM Aligns to the bottom edge of the sizer. wxALIGN_CENTER_HORIZONTAL Centers horizontally. wxALIGN_CENTER_VERTICAL Centers vertically. wxALIGN_CENTER Centers both horizontally and vertically. Defined as wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_ VERTICAL. wxLEFT Specifies a border on the left edge of the element. wxRIGHT Specifies a border on the right edge of the element. wxTOP Specifies a border on the top edge of the element. wxBOTTOM Specifies a border on the bottom edge of the element. wxALL Specifies a border on all edges of the element. Defined as wxLEFT | wxRIGHT | wxTOP | wxBOTTOM. The fourth parameter specifies the size of the border (on the edges that have been specified in the flags parameter). You can also add a spacer. There are three ways to do this: // Add a spacer (old method) void Add(int width, int height, int stretch = 0, int flags = 0, int border = 0); // Add a fixed-size spacer void AddSpacer(int size); // Add a stretching spacer void AddStretchSpacer(int stretch = 1);

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 195 Programming with Sizers 195 The second method is the equivalent of calling Add(size, size, 0), and the third method is equivalent to calling Add(0, 0, stretch). As an example, we will construct a dialog that will contain a text field at the top and two buttons at the bottom. This can be seen at the top level as a column with the text at the top and buttons at the bottom, and at the second level as a row with an OK button to the left and a Cancel button to the right. In many cases, the main window will be resizable by the user, and this change of size will have to be propagated to its children. Here, we want the text area to grow with the dialog, whereas the buttons should have a fixed size. In addi- tion, the buttons will be centered as the width of the dialog changes. Figure 7-8 shows how it will look. Figure 7-8 A simple dialog using sizers Here’s the code that produces this dialog: // A dialog with a stretching text control MyDialog::MyDialog(wxWindow *parent, wxWindowID id, const wxString &title ) : wxDialog(parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxBoxSizer *topSizer = new wxBoxSizer( wxVERTICAL ); // Create text ctrl with minimal size 100x60 topSizer->Add( new wxTextCtrl( this, wxID_ANY, “My text.”, wxDefaultPosition, wxSize(100,60), wxTE_MULTILINE), 1, // make vertically stretchable wxEXPAND| // make horizontally stretchable wxALL, // and make border all around 10 ); // set border width to 10 wxBoxSizer *buttonSizer = new wxBoxSizer( wxHORIZONTAL ); buttonSizer->Add( new wxButton( this, wxID_OK, “OK” ), 0, // make horizontally unstretchable

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 196 196 Window Layout Using Sizers Chapter 7 wxALL, // make border all around: implicit top alignment 10 ); // set border width to 10 buttonSizer->Add( new wxButton( this, wxID_CANCEL, “Cancel” ), 0, // make horizontally unstretchable wxALL, // make border all around (implicit top alignment) 10 ); // set border width to 10 topSizer->Add( buttonSizer, 0, // make vertically unstretchable wxALIGN_CENTER ); // no border and centre horizontally SetSizer( topSizer ); // use the sizer for layout topSizer->Fit( this ); // fit the dialog to the contents topSizer->SetSizeHints( this ); // set hints to honor min size } Programming with wxStaticBoxSizer wxStaticBoxSizer is a sizer derived from wxBoxSizer that manages a static box around the sizer. Note that this static box has to be created separately. Create wxStaticBoxSizer by passing a pointer to the static box and the orientation (wxHORIZONTAL or wxVERTICAL). The Add function is the same as for wxBoxSizer. Figure 7-9 shows an example of a dialog with a static box containing a check box control. Figure 7-9 A wxStaticBoxSizer Here’s the corresponding code: MyDialog::MyDialog(wxWindow *parent, wxWindowID id, const wxString &title ) : wxDialog(parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { // Create top-level sizer wxBoxSizer* topLevel = new wxBoxSizer(wxVERTICAL);

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 197 Programming with Sizers 197 // Create static box and static box sizer wxStaticBox* staticBox = new wxStaticBox(this, wxID_ANY, wxT(“General settings”)); wxStaticBoxSizer* staticSizer = new wxStaticBoxSizer(staticBox, wxVERTICAL); topLevel->Add(staticSizer, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); // Create a check box inside the static box sizer wxCheckBox* checkBox = new wxCheckBox( this, ID_CHECKBOX, wxT(“&Show splash screen”), wxDefaultPosition, wxDefaultSize); staticSizer->Add(checkBox, 0, wxALIGN_LEFT |wxALL, 5); SetSizer(topLevel); topLevel->Fit(this); topLevel->SetSizeHints(this); } Programming with wxGridSizer wxGridSizer is a sizer that lays out its children in a two-dimensional table with all table fields having the same size; that is, the width of each field is the width of the widest child, and the height of each field is the height of the tallest child. Create a wxGridSizer by passing the number of rows, number of columns, extra vertical gap between children, and extra horizontal gap between children. Add is the same as for wxBoxSizer. Figure 7-10 shows a grid sizer with three columns and two rows. The extra size of the second button has caused the space occupied by all the but- tons to increase because all the cells of a wxGridSizer are the same size. Figure 7-10 A wxGridSizer Here’s the code: MyDialog::MyDialog(wxWindow *parent, wxWindowID id, const wxString &title ) : wxDialog(parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { // Create top-level grid sizer wxGridSizer* gridSizer = new wxGridSizer(2, 3, 0, 0);

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 198 198 Window Layout Using Sizers Chapter 7 SetSizer(gridSizer); wxButton* button1 = new wxButton(this, ID_BUTTON1, wxT(“One”)); gridSizer->Add(button1, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button2 = new wxButton(this, ID_BUTTON2, wxT(“Two (the second button)”)); gridSizer->Add(button2, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button3 = new wxButton(this, ID_BUTTON3, wxT(“Three”)); gridSizer->Add(button3, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button4 = new wxButton(this, ID_BUTTON4, wxT(“Four”)); gridSizer->Add(button4, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button5 = new wxButton(this, ID_BUTTON5, wxT(“Five”)); gridSizer->Add(button5, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button6 = new wxButton(this, ID_BUTTON6, wxT(“Six”)); gridSizer->Add(button6, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); gridSizer->Fit(this); gridSizer->SetSizeHints(this); } Programming with wxFlexGridSizer wxFlexGridSizer is a sizer that lays out its children in a two-dimensional table with all table fields in one row having the same height and all fields in one column having the same width. However, unlike wxGridSizer, all rows or all columns are not necessarily the same height or width: this will depend on the size of elements in a row or column. Additionally, columns and rows can be declared to be stretchable, which means that as the sizer is expanded, these columns or rows will be allocated extra space. Create a wxFlexGridSizer by passing the number of rows, number of columns, extra vertical gap between children, and extra horizontal gap between children. Add is the same as for wxBoxSizer. Figure 7-11 shows a flex grid sizer at its initial size, where the first col- umn has been made stretchable. It’s essentially the same as the wxGridSizer example, but as you can see, the layout is more compact because the size of the middle column is not reflected in the other columns.

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 199 Programming with Sizers 199 Figure 7-11 A wxFlexGridSizer at its initial size Initially, we don’t see the effect of making the first column stretchable, but if we stretch it horizontally, as in Figure 7-12, we can see this column (con- taining buttons One and Four) taking up the extra space, with the buttons centered in the column. Figure 7-12 A resized wxFlexGridSizer Here’s the code that creates the dialogs we’ve shown: MyDialog::MyDialog(wxWindow *parent, wxWindowID id, const wxString &title ) : wxDialog(parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { // Create top-level flex grid sizer wxFlexGridSizer* flexGridSizer = new wxFlexGridSizer(2, 3, 0, 0); this->SetSizer(flexGridSizer); // Make the 1st row growable flexGridSizer->AddGrowableCol(0); wxButton* button1 = new wxButton(this, ID_BUTTON1, wxT(“One”)); flexGridSizer->Add(button1, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button2 = new wxButton(this, ID_BUTTON2, wxT(“Two (the second button)”)); flexGridSizer->Add(button2, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button3 = new wxButton(this, ID_BUTTON3, wxT(“Three”)); flexGridSizer->Add(button3, 0, wxALIGN_CENTER_HORIZONTAL|

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 200 200 Window Layout Using Sizers Chapter 7 wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button4 = new wxButton(this, ID_BUTTON4, wxT(“Four”)); flexGridSizer->Add(button4, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button5 = new wxButton(this, ID_BUTTON5, wxT(“Five”)); flexGridSizer->Add(button5, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* button6 = new wxButton(this, ID_BUTTON6, wxT(“Six”)); flexGridSizer->Add(button6, 0, wxALIGN_CENTER_HORIZONTAL| wxALIGN_CENTER_VERTICAL|wxALL, 5); flexGridSizer->Fit(this); flexGridSizer->SetSizeHints(this); } Programming with wxGridBagSizer This sizer attempts to reconcile the worlds of absolute positioning and sizer- based layout. It can lay out elements in a virtual grid, like a flex grid sizer, but in this case item row and column positions are specified using wxGBPosition, and items can optionally span more than one row and/or column using wxGBSpan. When creating a wxGridBagSizer, optionally pass sizers for vertical and horizontal gaps between rows and columns (defaulting to zero). Use the Add function to add windows or sizers, passing the position and optional span, plus optional flags and border size as for wxBoxSizer. Figure 7-13 shows a simple grid bag sizer example with four buttons, one of them spanning two columns (button Two). We also specify that the second row and third column are growable so that when we resize the dialog, we get the effect shown in Figure 7-14. Figure 7-13 A wxGridBagSizer at its original size

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 201 Further Layout Issues 201 Figure 7-14 A wxGridBagSizer after resizing Here’s the code that produces this layout: MyDialog::MyDialog(wxWindow *parent, wxWindowID id, const wxString &title ) : wxDialog(parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxGridBagSizer* gridBagSizer = new wxGridBagSizer(); SetTopSizer(gridBagSizer); wxButton* b1 = new wxButton(this, wxID_ANY, wxT(“One (0,0)”)); gridBagSizer->Add(b1, wxGBPosition(0, 0)); wxButton* b2 = new wxButton(this, wxID_ANY, wxT(“Two (2,2)”)); gridBagSizer->Add(b2, wxGBPosition(2, 2), wxGBSpan(1, 2), wxGROW); wxButton* b3 = new wxButton(this, wxID_ANY, wxT(“Three (3,2)”)); gridBagSizer->Add(b3, wxGBPosition(3, 2)); wxButton* b4 = new wxButton(this, wxID_ANY, wxT(“Four (3,3)”)); gridBagSizer->Add(b4, wxGBPosition(3, 3)); gridBagSizer->AddGrowableRow(3); gridBagSizer->AddGrowableCol(2); gridBagSizer->Fit(this); gridBagSizer->SetSizeHints(this); } FURTHER LAYOUT ISSUES In this section, we’ll discuss some further topics to bear in mind when you’re working with sizers.

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 202 202 Window Layout Using Sizers Chapter 7 Dialog Units Although sizers protect you from changes in basic control size on different platforms and in different languages, you may still have some hard-coded sizes in your dialogs (for example, for list boxes). If you would like these sizes to adjust to the current system font (or font supplied by the application), you can use dialog units instead of pixels. Dialog units are based on average char- acter width and height for a window’s font, and so the actual pixel dimension for a given dialog unit value will vary according to the current font. wxWindow has functions ConvertDialogToPixels and ConvertPixelsToDialog, and a convenience macro wxDLG_UNIT(window, ptOrSz) that can be used with both wxPoint and wxSize objects. So instead of passing a pixel size to your con- trol, use the wxDLG_UNIT macro, for example: wxListBox* listBox = new wxListBox(parent, wxID_ANY, wxDefaultPosition, wxDLG_UNIT(parent, wxSize(60, 20))); Dialog units can be specified in an XRC file by appending “d” to dimension values. Platform-Adaptive Layouts Although dialogs on different platforms are largely similar, sometimes the style guides are incompatible. For example, on Windows and Linux, it’s accept- able to have right-justified or centered OK, Cancel, and Help buttons, in that order. On Mac OS X, the Help should be on the left, and Cancel and OK but- tons are right aligned, in that order. To help with this issue, wxStdDialogButtonSizer is provided. It’s derived from wxBoxSizer, so it can be used in a similar way, but its orientation will depend on platform. This sizer’s constructor has no arguments. There are two ways of adding buttons: pass the button pointer to AddButton, or (if you’re not using standard identifiers) call SetAffirmativeButton, SetNegativeButton, and SetCancelButton. If using AddButton, you should use identifiers from this list: wxID_OK, wxID_YES, wxID_CANCEL, wxID_NO, wxID_SAVE, wxID_APPLY, wxID_HELP, and wxID_CONTEXT_HELP. Then, after the buttons have been added, call Realize so that the sizer can add the buttons in the appropriate order with the appropriate spacing (which it can only do when it knows about all the buttons in the sizer). The following code creates a standard button sizer with OK, Cancel, and Help buttons: wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); dialog->SetSizer(topSizer); wxButton* ok = new wxButton(dialog, wxID_OK); wxButton* cancel = new wxButton(dialog, wxID_CANCEL);

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 203 Further Layout Issues 203 wxButton* help = new wxButton(dialog, wxID_HELP); wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer; topSizer->Add(buttonSizer, 0, wxEXPAND|wxALL, 10); buttonSizer->AddButton(ok); buttonSizer->AddButton(cancel); buttonSizer->AddButton(help); buttonSizer->Realize(); As a convenience, wxDialog::CreateButtonSizer can be used, indirectly creating a wxStdDialogButtonSizer with buttons based on a flag list. If you look at the dialog implementations in src/generic, you will see that CreateButtonSizer is used for many of them. The flags in Table 7-2 can be passed to this function. Table 7-2 Flags for CreateButtonSizer wxYES_NO Add Yes and No buttons to the panel. wxYES Add Yes button to the panel, with identifier wxID_YES. wxNO Add No button to the panel, with identifier wxID_NO. wxNO_DEFAULT Make the No button the default, otherwise Yes or OK will be the default. wxOK Add an OK button to the panel, with identifier wxID_OK. wxCANCEL Add a Cancel button to the panel, with identifier wxID_CANCEL. wxAPPLY Add an Apply button to the panel, with identifier wxID_APPLY. wxHELP Add a Help button to the panel, with identifier wxID_HELP. Using CreateButtonSizer simplifies the example code shown previously to the following: wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); dialog->SetSizer(topSizer); topSizer->Add(CreateButtonSizer(wxOK|wxCANCEL|wxHELP), 0, wxEXPAND|wxALL, 10); There is another way to specify variations in UI on different platforms. XRC allows a platform parameter to be associated with each object; this parame- ter’s value can be a combination of unix, win, mac, and os2 separated by the pipe character (“|”). XRC will only create the element if the platform value matches the actual platform the program is running on. DialogBlocks sup- ports this property and can generate conditional C++ code if XRC is not being used. Alternatively, you can load a different XRC file for each platform, but this is harder to maintain than having the differences contained within a single dialog design.

Smart_Ch07f.qxd 6/10/05 11:19 AM Page 204 204 Window Layout Using Sizers Chapter 7 Dynamic Layouts Sometimes you’d like the layout to change dynamically; for example, clicking on a Details button might expand the dialog and show further controls. You can hide controls contained in sizers the same way you would hide any control, using the wxWindow::Show method. However, wxSizer also offers a separate method, which can tell the sizer not to consider a window in its size calcula- tions. To hide a window using the sizer, pass false to wxSizer::Show. You must then call wxSizer::Layout to force an update. SUMMARY Sizers takes some getting used to, so don’t worry if you found this chapter a bit heavy going. The best way of getting to grips with them is to play with a dialog editor such as DialogBlocks (included on the CD-ROM), experimenting with different layouts and examining the generated code. You can also look at samples/layout in your wxWidgets distribution. After you’ve tamed them, you’ll find sizers a very powerful tool, and their ability to adapt to different platforms and languages will prove to be a huge productivity benefit. Next, we’ll look at the standard dialogs provided by wxWidgets.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 205 C H A P T E R 8 Using Standard Dialogs This chapter describes the set of standard dialogs that wxWidgets provides for displaying information or getting data from users with just a few lines of code. Becoming familiar with the available standard dialogs is going to save you a lot of coding time, and it will help give your applications a professional feel. Where possible, wxWidgets uses the native dialogs implemented by each win- dowing system, but some, such as wxTextEntryDialog, are implemented in wxWidgets itself, and these are referred to as “generic” dialogs. In this chap- ter, we will show pictures of dialogs on more than one platform where there are significant visual differences. We will divide the dialogs into the categories Informative Dialogs, File and Directory Dialogs, Choice and Selection Dialogs, and Entry Dialogs. INFORMATIVE DIALOGS In this section, we’ll look at dialogs that present information: wxMessageDialog, wxProgressDialog, wxBusyInfo, and wxShowTip. wxMessageDialog This dialog shows a message plus buttons that can be chosen from OK, Cancel, Yes, and No. An optional icon can be shown, such as an exclamation mark or question mark. The message text can contain newlines (“\n”). The return value of wxMessageDialog::ShowModal indicates which button the user pressed. 205

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 206 206 Using Standard Dialogs Chapter 8 Figure 8-1 shows how the dialog looks under Windows, Figure 8-2 shows it under GTK+, and Figure 8-3 is the same dialog on Mac OS X. Figure 8-1 wxMessageDialog under Windows Figure 8-2 wxMessageDialog under GTK+ Figure 8-3 wxMessageDialog under Mac OS X To create this dialog, pass a parent window, message, and optional cap- tion, style, and position. Then call ShowModal to display the window, and test the returned value. The style is a bit-list of the values shown in Table 8-1. Table 8-1 wxMessageDialog Styles wxOK Shows an OK button. wxCANCEL Shows a Cancel button. wxYES_NO Shows Yes and No buttons. wxYES_DEFAULT Sets Yes as the default. Use with wxYES_NO. This is the default behavior for wxYES_NO. wxNO_DEFAULT Sets No as the default. Use with wxYES_NO. wxICON_EXCLAMATION Shows an exclamation mark.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 207 Informative Dialogs 207 wxICON_ERROR Shows an error icon. wxICON_HAND Shows an error icon. The same as wxICON_ERROR. wxICON_QUESTION Shows a question mark. wxICON_INFORMATION Shows an information icon. wxSTAY_ON_TOP On Windows, the message box will stay on top of all other windows, even those of other applications. wxMessageDialog Example Here’s an example of using wxMessageDialog: #include “wx/msgdlg.h” wxMessageDialog dialog( NULL, wxT(“Message box caption”), wxT(“Message box text”), wxNO_DEFAULT|wxYES_NO|wxCANCEL|wxICON_INFORMATION); switch ( dialog.ShowModal() ) { case wxID_YES: wxLogStatus(wxT(“You pressed \”Yes\””)); break; case wxID_NO: wxLogStatus(wxT(“You pressed \”No\””)); break; case wxID_CANCEL: wxLogStatus(wxT(“You pressed \”Cancel\””)); break; default: wxLogError(wxT(“Unexpected wxMessageDialog return code!”)); } wxMessageBox You can also use the convenience function wxMessageBox, which takes a mes- sage string, caption string, style, and parent window. For example: if (wxYES == wxMessageBox(wxT(“Message box text”), wxT(“Message box caption”), wxNO_DEFAULT|wxYES_NO|wxCANCEL|wxICON_INFORMATION, parent)) { return true; } Be aware that wxMessageBox returns values that are different from those returned by wxMessageDialog::ShowModal. wxMessageBox returns wxOK, wxCANCEL, wxYES, and wxNO, whereas wxMessageDialog::ShowModal returns wxID_OK, wxID_CANCEL, wxID_YES, and wxID_NO.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 208 208 Using Standard Dialogs Chapter 8 wxProgressDialog wxProgressDialog shows a short message and a progress bar representing how long the user has to wait. It can display a Cancel button to abort the task in progress, and it can also display elapsed time, estimated total time, and remaining time. This dialog is implemented by wxWidgets on all platforms. Figure 8-4 shows wxProgressDialog under Windows. Figure 8-4 wxProgressDialog under Windows You can create the dialog object on the stack or dynamically. Pass the fol- lowing parameters: a caption string, a message string to be displayed above the progress bar, the maximum value for the progress bar, a parent window, and a style. The style is a bit-list of the values listed in Table 8-2. Table 8-2 wxProgressDialog Styles wxPD_APP_MODAL Makes the progress dialog modal. If this style is not given, it is only “locally” modal—that is, the input to the parent window is disabled, but not to the other ones. wxPD_AUTO_HIDE Causes the progress dialog to disappear from the screen as soon as the maximum value of the progress meter has been reached. wxPD_CAN_ABORT Tells the dialog that it should have a Cancel button that the user may press. If this happens, the next call to Update will return false. wxPD_ELAPSED_TIME Tells the dialog that it should show the elapsed time since creating the dialog. wxPD_ESTIMATED_TIME Tells the dialog that it should show the estimated time. wxPD_REMAINING_TIME Tells the dialog that it should show the remaining time. After dialog creation, the program flow continues, but the parent window is disabled for input. If wxPD_APP_MODAL is specified, then all other windows in the application are disabled as well. The application should call Update with a

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 209 Informative Dialogs 209 value (between zero and the maximum specified in the constructor) and, optionally, a new message to display in the dialog. If specified, the elapsed, estimated, and remaining times are all calculated automatically by the dialog. If wxPD_AUTO_HIDE is specified, the progress dialog will be hidden (but not destroyed) as soon as the maximum value has been passed to Update. The application should destroy the dialog. You can call Resume if you need to resume an aborted progress dialog. wxProgressDialog Example Here’s an example of using the progress dialog: #include “wx/progdlg.h” void MyFrame::ShowProgress() { static const int max = 10; wxProgressDialog dialog(wxT(“Progress dialog example”), wxT(“An informative message”), max, // range this, // parent wxPD_CAN_ABORT | wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME); bool cont = true; for ( int i = 0; i <= max; i++ ) { wxSleep(1); if ( i == max ) cont = dialog.Update(i, wxT(“That’s all, folks!”)); else if ( i == max / 2 ) cont = dialog.Update(i, wxT(“Only a half left (very ➥long message)!”)); else cont = dialog.Update(i); if ( !cont ) { if ( wxMessageBox(wxT(“Do you really want to cancel?”), wxT(“Progress dialog question”), wxYES_NO | wxICON_QUESTION) == wxYES ) break; dialog.Resume(); } } if ( !cont ) wxLogStatus(wxT(“Progress dialog aborted!”)); else wxLogStatus(wxT(“Countdown from %d finished”), max); }

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 210 210 Using Standard Dialogs Chapter 8 wxBusyInfo wxBusyInfo isn’t actually a dialog—it’s derived from wxObject—but it behaves in a similar way. It shows a window displaying a message for as long as the object exists, and it is useful for asking the user to wait while the application is work- ing on something. On Windows, it looks like the window in Figure 8-5. Create a wxBusyInfo object on the stack or dynamically, passing a message and a window parent. Counting, please wait... Figure 8-5 wxBusyInfo dialog under Windows wxBusyInfo Example Here’s an example of using wxBusyInfo, first using wxWindowDisabler to disable all windows currently open in the application. #include “wx/busyinfo.h” wxWindowDisabler disableAll; wxBusyInfo info(wxT(“Counting, please wait...”), parent); for (int i = 0; i < 1000; i++) { DoCalculation(); } wxShowTip Many applications show a tip on startup to give you extra insights into using the application. Tips can be a good way to learn an application in small, easily digested doses, especially for users who find it tedious to read documentation.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 211 Informative Dialogs 211 The startup tip dialog under Windows is shown in Figure 8-6. Figure 8-6 Tip dialog under Windows Unlike most of the standard dialogs, startup tips are shown by calling a function: wxShowTip. Pass a parent window, a pointer to a wxTipProvider object, and optionally a boolean value specifying whether to show the Show Tips at Startup check box. The return value is the value of this check box. You must derive a new class from wxTipProvider and override the GetTip function to return a wxString containing the tip. Fortunately, wxWidgets pro- vides an implementation already: wxCreateFileTipProvider, which takes the name of a file of tips (one per line) and an index into the tips. The application is responsible for deleting the wxTipProvider object when it is no longer needed. wxShowTip Example Here’s a function that shows a startup tip using the standard tip provider: #include “wx/tipdlg.h” void MyFrame::ShowTip() { static size_t s_index = (size_t)-1; if ( s_index == (size_t)-1 ) { // randomize... srand(time(NULL)); // ...and pick a new tip s_index = rand() % 5; }

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 212 212 Using Standard Dialogs Chapter 8 // pass a tips file and tip index wxTipProvider *tipProvider = wxCreateFileTipProvider(wxT(“tips.txt”), s_index); m_showAtStartup = wxShowTip(this, tipProvider, true); delete tipProvider; } FILE AND DIRECTORY DIALOGS There are two dialogs you can use to get file and directory information from the user: wxFileDialog and wxDirDialog. wxFileDialog wxFileDialog can handle the selection of one file or several files, and it has variants for opening and saving files. Figure 8-7 shows the file dialog under Windows. Figure 8-7 wxFileDialog under Windows

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 213 File and Directory Dialogs 213 Figure 8-8 and Figure 8-9 show the file dialog under Linux using GTK+ versions 1 and 2, respectively. Figure 8-8 Generic wxFileDialog under GTK+ Figure 8-9 Native wxFileDialog under GTK+2.4 and above

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 214 214 Using Standard Dialogs Chapter 8 The file dialog appearance under Mac OS X is shown in Figure 8-10. Figure 8-10 wxFileDialog under Mac OS X To create a wxFileDialog, pass a parent window, a message to display in the dialog caption, a default directory, a default file name, a wildcard, the dia- log style, and a position (ignored on some platforms). Call ShowModal and test for a wxID_OK return value, which is returned if the user confirms a selection. Directory and file name are distinct elements of a full path name. If the directory is empty, the current directory will be used. If the file name is empty, no default file name will be supplied. The wildcard determines what files are displayed in the file selector. The wildcard may be a specification for multiple types of file with a description for each, such as “BMP files (*.bmp)|*.bmp|GIF files (*.gif)|*.gif” Typing a file name containing wildcards (“*”, “?”) in the file name text item and clicking on OK will result in only those files matching the pattern being displayed. wxFileDialog Styles The file dialog has the styles shown in Table 8-3.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 215 File and Directory Dialogs 215 Table 8-3 wxFileDialog Styles wxSAVE Specifies a “save” dialog. wxOPEN Specifies an “open” dialog (the default). wxOVERWRITE_PROMPT For a “save” dialog, the user will be prompted if the chosen file already exists. wxFILE_MUST_EXIT The user is forced to select an existing file. wxMULTIPLE The user can select multiple files. wxFileDialog Functions The wxFileDialog functions are as follows. GetDirectory returns the default directory or the directory component of the selected file for a single-selection file dialog. Use SetDirectory to specify the default directory. GetFilename returns the default file name (without the directory) or the selected file name for a single-selection file dialog. Use SetFilename to set the default file name. GetFilenames returns a wxArrayString of the file names of all selections in a multiple-selection dialog. Generally, these file names do not include the directory, but under Windows, if any shortcuts are selected, the file names do include directories. This is because the application cannot determine the full path of each referenced file by appending the file name to the selected direc- tory. Use GetPaths if you want to get an array of the selections including their directories. GetFilterIndex returns a zero-based index of the default or selected filter. Filters are usually displayed in a drop-down list under the list of files. Use SetFilterIndex to set the default index to be displayed when the dialog is shown. GetMessage returns the dialog caption. Use SetMessage to set the caption. GetPath returns the full path (directory and file name) of the file selected by the user or the default path. Use SetPath to set the default path. For a multiple- selection dialog, use GetPaths to get a wxArrayString of all selections including their directories. GetWildcard returns the wildcard specification, and SetWildcard sets it. wxFileDialog Example Here’s an example of using wxFileDialog to open a single BMP or GIF file: #include “wx/filedlg.h” wxString caption = wxT(“Choose a file”); wxString wildcard = wxT(“BMP files (*.bmp)|*.bmp|GIF files (*.gif)|*.gif”);

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 216 216 Using Standard Dialogs Chapter 8 wxString defaultDir = wxT(“c:\\temp”)); wxString defaultFilename = wxEmptyString; wxFileDialog dialog(parent, caption, defaultDir, defaultFilename, wildcard, wxOPEN); if (dialog.ShowModal() == wxID_OK) { wxString path = dialog.GetPath(); int filterIndex = dialog.GetFilterIndex(); } wxDirDialog wxDirDialog allows the user to choose a local or network directory (folder). Optionally, it can allow the user to create a new directory if the wxDD_NEW_ DIR_BUTTON style is passed to the constructor. Figure 8-11 shows wxDirDialog under Windows, where Windows supplies the dialog. The generic version of wxDirDialog is used for GTK+ on Linux, as Figure 8-12 shows. Figure 8-11 wxDirDialog under Windows

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 217 File and Directory Dialogs 217 Figure 8-12 wxDirDialog under GTK+ wxDirDialog on Mac OS X (Figure 8-13) looks very much like the file dialog. Figure 8-13 wxDirDialog under Mac OS X To create the directory dialog, pass a parent window, a message to show on the dialog, a default directory, a window style, a position, and a size (these last two may be ignored, depending on implementation). Call ShowModal and

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 218 218 Using Standard Dialogs Chapter 8 test for a wxID_OK return value, which indicates that the user confirmed a direc- tory selection. wxDirDialog Functions The functions for this dialog are described in the following. SetPath and GetPath are accessors for the default or user-selected directory. SetMessage sets the message that appears on the dialog, and GetMessage returns the current value. wxDirDialog Example Using wxDirDialog is easy, as this example shows: #include “wx/dirdlg.h” wxString defaultPath = wxT(“/”); wxDirDialog dialog(parent, wxT(“Testing directory picker”), defaultPath, wxDD_NEW_DIR_BUTTON); if (dialog.ShowModal() == wxID_OK) { wxString path = dialog.GetPath(); wxMessageBox(path); } CHOICE AND SELECTION DIALOGS In this section, we look at dialogs for getting choices and selections from the user: wxColourDialog , wxFontDialog , wxSingleChoiceDialog , and wxMultiChoiceDialog. wxColourDialog This dialog allows the user to pick from a standard set or a full range of colors. Under Windows, the native color selector dialog is used. This dialog con- tains three main regions: at the top left, a palette of 48 commonly used colors is shown. Below this, there is a palette of 16 custom colors, which can be set by the application. Additionally, the user may add to the custom color palette by expanding the dialog box and choosing a precise color from the color selector panel on the right. Figure 8-14 shows the color selector under Windows in full selection mode.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 219 Choice and Selection Dialogs 219 Figure 8-14 wxColourDialog under Windows The generic color dialog, shown in Figure 8-15 under GTK+ 1 and X11, shows palettes of 48 standard and 16 custom colors, with the area on the right containing three sliders for the user to select a color from red, green, and blue components. This color may be added to the custom color palette, and it will replace either the currently selected custom color or the first one in the palette if none is selected. The RGB color sliders are not optional in the generic color selector. The generic color selector is also available under Windows and other platforms; use the name wxGenericColourDialog. Figure 8-15 Generic wxColourDialog under X11

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 220 220 Using Standard Dialogs Chapter 8 Figure 8-16 shows the native color dialog under GTK+. Figure 8-16 wxColourDialog under GTK+ Figure 8-17 shows Mac OS X’s color dialog, which offers yet another way of getting a color from the user. Figure 8-17 wxColourDialog under Mac OS X To use this dialog, create a wxColourDialog object (dynamically allocated or on the stack) and pass it a parent window and a pointer to a wxColourData object. The information in wxColourData will be copied to the dialog to set some defaults. Call ShowModal to enter the modal loop, and when control is returned to your code, you can retrieve the user-modified data by calling GetColourData.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 221 Choice and Selection Dialogs 221 wxColourData Functions wxColourData has the following functions. SetChooseFull specifies that the color dialog should show the full selection of colors; otherwise only a subset will be shown. This currently works only under Windows. GetChooseFull retrieves the value of this boolean. SetColour sets the default color to show in the color selector, and GetColour retrieves the color that the user has chosen. SetCustomColour takes a zero-based index (maximum 15) and a wxColour object and sets one of the 16 custom colors. Use GetCustomColour to retrieve the custom colors, which may have changed if the user has added to the custom colors from within the color selector. wxColourDialog Example Here is an example of using wxColourDialog. The code sets various parameters of a wxColourData object, including a gray scale for the custom colors. If the user did not cancel the dialog, the application retrieves the selected color and uses it to set the background of a window. #include “wx/colordlg.h” wxColourData data; data.SetChooseFull(true); for (int i = 0; i < 16; i++) { wxColour color(i*16, i*16, i*16); data.SetCustomColour(i, color); } wxColourDialog dialog(this, &data); if (dialog.ShowModal() == wxID_OK) { wxColourData retData = dialog.GetColourData(); wxColour col = retData.GetColour(); myWindow->SetBackgroundColour(col); myWindow->Refresh(); } wxFontDialog wxFontDialog allows the user to provide font and, on some platforms, font color selections. Under Windows, the native font selector standard dialog is used. This presents a dialog box with controls for font name, point size, style, weight, underlining, strikeout, and text foreground color. A sample of the font is shown on a white area of the dialog box. Note that in the translation from full Windows fonts to wxWidgets font conventions, strikeout is ignored, and a font family (such as Swiss or Modern) is deduced from the actual font name (such

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 222 222 Using Standard Dialogs Chapter 8 as Arial or Courier). Under GTK+, the GTK+ standard font selector is used, which does not allow color selection. Figure 8-18 shows how the font dialog looks under Windows. Figure 8-18 wxFontDialog under Windows Figure 8-19 shows the native font dialog under GTK+. Figure 8-19 wxFontDialog under GTK+ Under platforms other than Windows and GTK+, the font selector is sim- pler: see Figure 8-20 for a view of the generic dialog on Mac OS X. Controls for font family, point size, style, weight, underlining, and text foreground color are provided, and a sample is shown upon a white background. The generic font selector is available on all platforms; use the name wxGenericFontDialog.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 223 Choice and Selection Dialogs 223 Figure 8-20 Generic wxFontDialog under Mac OS X To use wxFontDialog, create an object dynamically or on the stack and pass a parent window and a wxFontData object. Call ShowModal and test for a wxID_OK return value. Then retrieve the wxFontData from the dialog and call GetChosenFont and GetChosenColour as required. wxFontData Functions wxFontData has the following functions. EnableEffects enables controls for manipulating color and underline properties under Windows or on the generic dialog (no effect on GTK+). GetEnableEffects returns the current boolean value of this setting. Note that even if effects are disabled, the font color will be preserved. SetAllowSymbols allows the selection of symbol fonts (Windows only), and GetAllowSymbols returns the current boolean value of this setting. SetColour sets the default font color, and GetColour retrieves the font color selected by the user. SetInitialFont sets the default font that will be selected when the dialog is first opened. GetChosenFont retrieves the wxFont selected by the user. SetShowHelp can be called to indicate that the help button should be displayed (under Windows only). Use GetShowHelp to return the value of this setting. Call SetRange with the minimum and maximum point size that the user can select; the default (0, 0) indicates that any point size can be selected. This has an effect on Windows only.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 224 224 Using Standard Dialogs Chapter 8 Font Selector Example In this fragment, the application uses the returned font and color for drawing text on a window. #include “wx/fontdlg.h” wxFontData data; data.SetInitialFont(m_font); data.SetColour(m_textColor); wxFontDialog dialog(this, &data); if (dialog.ShowModal() == wxID_OK) { wxFontData retData = dialog.GetFontData(); m_font = retData.GetChosenFont(); m_textColor = retData.GetColour(); // Update the window to reflect the new font and color myWindow->Refresh(); } wxSingleChoiceDialog wxSingleChoiceDialog presents the user with a list of strings and allows the user to select one. It looks like the dialog in Figure 8-21. Figure 8-21 wxSingleChoiceDialog under Windows Pass to the dialog constructor the parent window, a message to show on the dialog, the dialog caption, and a wxArrayString for the strings to appear in the list. You can also pass an array size and a C array of strings (wxChar**) instead of passing a wxArrayString. You can use SetSelection to set the default selection before showing the dialog; after the dialog has been dismissed, query the user’s choice with GetSelection (to return the index) or GetStringSelection (to return the string).

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 225 Choice and Selection Dialogs 225 You also can pass an array of char* client data to the dialog’s constructor; when the dialog is dismissed, GetSelectionClientData will return the char* client data corresponding to the user selection. wxSingleChoiceDialog Example Here’s some code to show how wxSingleChoiceDialog is used. #include “wx/choicdlg.h” const wxArrayString choices; choices.Add(wxT(“One”)); choices.Add(wxT(“Two”)); choices.Add(wxT(“Three”)); choices.Add(wxT(“Four”)); choices.Add(wxT(“Five”)); wxSingleChoiceDialog dialog(this, wxT(“This is a small sample\nA single- ➥choice convenience dialog”), wxT(“Please select a value”), choices); dialog.SetSelection(2); if (dialog.ShowModal() == wxID_OK) wxMessageBox(dialog.GetStringSelection(), wxT(“Got string”)); wxMultiChoiceDialog wxMultiChoiceDialog is similar to wxSingleChoiceDialog, presenting the user with a list of strings, but it allows the user to select zero or more. This dialog is illustrated in Figure 8-22. Figure 8-22 wxMultiChoiceDialog under Windows

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 226 226 Using Standard Dialogs Chapter 8 Pass to the dialog constructor the parent window, a message to show on the dialog, the dialog caption, and a wxArrayString array of strings. As with wxSingleChoiceDialog, you may pass an array size and wxChar** array instead of the wxArrayString argument. Unlike wxSingleChoiceDialog, no client data may be passed to the constructor. To set the default selections, call SetSelections passing a wxArrayInt where each element specifies an index in the passed array of strings. Query the user’s choice with GetSelections to return a wxArrayInt of indices specify- ing the user’s selections. wxMultiChoiceDialog Example Here’s how you use a wxMultiChoiceDialog. #include “wx/choicdlg.h” const wxArrayString choices; choices.Add(wxT(“One”)); choices.Add(wxT(“Two”)); choices.Add(wxT(“Three”)); choices.Add(wxT(“Four”)); choices.Add(wxT(“Five”)); wxMultiChoiceDialog dialog(this, wxT(“A multi-choice convenience ➥dialog”), wxT(“Please select several values”), choices); if (dialog.ShowModal() == wxID_OK) { wxArrayInt selections = dialog.GetSelections(); wxString msg; msg.Printf(wxT(“You selected %u items:\n”), selections.GetCount()); for ( size_t n = 0; n < selections.GetCount(); n++ ) { msg += wxString::Format(wxT(“\t%d: %d (%s)\n”), n, selections[n], choices[selections[n]].c_str()); } wxMessageBox(msg, wxT(“Got selections”)); }

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 227 Entry Dialogs 227 ENTRY DIALOGS These dialogs ask you to type in information. They include wxNumberEntryDialog, wxTextEntryDialog, wxPasswordEntryDialog, and wxFindReplaceDialog. wxNumberEntryDialog wxNumberEntryDialog prompts the user for an integer within a given range. The dialog shows a spin control so that the number can be entered directly or by clicking on the up and down arrows. This dialog is implemented by wxWidgets, so it has the same functionality on all platforms. Create a wxNumberEntryDialog passing a parent window, message text, prompt text (that will precede the spin control), caption, default value, mini- mum value, maximum value, and position. Then call ShowDialog and, if wxID_OK is returned, retrieve the number using GetValue. Figure 8-23 shows what the dialog looks like under Windows. Figure 8-23 wxNumberEntryDialog under Windows wxNumberEntryDialog Example Figure 8-23 was created using the following code: #include “wx/numdlg.h” wxNumberEntryDialog dialog(parent, wxT(“This is some text, actually a lot of text\nEven two rows of ➥text”), wxT(“Enter a number:”), wxT(“Numeric input test”), 50, 0, 100); if (dialog.ShowModal() == wxID_OK) { long value = dialog.GetValue(); } wxTextEntryDialog and wxPasswordEntryDialog wxTextEntryDialog and wxPasswordEntryDialog present the user with a single- line text control and a message. They function identically except that the

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 228 228 Using Standard Dialogs Chapter 8 letters typed into a wxPasswordEntryDialog are masked so that they cannot be read. Figure 8-24 shows a wxTextEntryDialog under Windows. Figure 8-24 wxTextEntryDialog under Windows Pass a parent window, message, caption, default value, and style to the constructor. The style can be a bit-list of wxOK, wxCANCEL, and wxCENTRE (or wxCENTER), and you can also pass wxTextCtrl styles such as wxTE_CENTRE (or wxTE_CENTER). You can set the default string separately with SetValue, and GetValue returns the text entered by the user. wxTextEntryDialog Example Figure 8-24 was created using this code: #include “wx/textdlg.h” wxTextEntryDialog dialog(this, wxT(“This is a small sample\n”) wxT(“A long, long string to test out the ➥text entrybox”), wxT(“Please enter a string”), wxT(“Default value”), wxOK | wxCANCEL); if (dialog.ShowModal() == wxID_OK) wxMessageBox(dialog.GetValue(), wxT(“Got string”)); wxFindReplaceDialog wxFindReplaceDialog is a modeless dialog that allows the user to search for some text and replace it with something else, if desired. The actual searching must be done in a derived class or a parent window, responding to events gen- erated by the dialog’s buttons. Unlike most standard dialogs, this one must have a parent window. This dialog cannot be used modally; it is always, by design and implementation, modeless.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 229 Entry Dialogs 229 The Windows Find and Replace dialog is shown in Figure 8-25. Figure 8-25 wxFindReplaceDialog under Windows On other platforms, such as GTK+ and Mac OS X, wxWidgets uses the generic version of the dialog, as shown in Figure 8-26. Figure 8-26 wxFindReplaceDialog under GTK+ Handling Events from the Dialog wxFindReplaceDialog sends command events when the user clicks on controls in the dialog. Event handlers take a wxFindDialogEvent argument, and the event table macros take the dialog identifier and handler function, as listed in Table 8-4. Table 8-4 wxFindReplaceDialog Events EVT_FIND(id, func) Handles Find button clicks. EVT_FIND_NEXT(id, func) Handles Next button clicks. EVT_FIND_REPLACE(id, func) Handles Replace button clicks. EVT_FIND_REPLACE_ALL(id, func) Handles Replace All button clicks. EVT_FIND_CLOSE(id, func) Handles a close event, generated when the user closes the dialog via Cancel or other means.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 230 230 Using Standard Dialogs Chapter 8 wxFindDialogEvent Functions wxFindDialogEvent has the following functions. GetFlags returns flags for the current selections on the dialog. The value is a bit-list of wxFR_DOWN, wxFR_WHOLEWORD, and wxFR_MATCHCASE. GetFindString returns the string the user entered as the text to find. GetReplaceString returns the string the user entered as the text to use as the replacement. GetDialog returns a pointer to the wxFindReplaceDialog that generated the event. Passing Data to the Dialog To create a wxFindReplaceDialog, pass a window parent, a pointer to a wxFindReplaceData object, a dialog caption, and a style, which is a bit-list of val- ues shown in Table 8-5. Table 8-5 wxFindReplaceData Style wxFR_REPLACEDIALOG Specifies a find and replace dialog; otherwise, it will be a find dialog. wxFR_NOUPDOWN Specifies that the search direction should not be adjustable. wxFR_NOMATCHCASE Specifies that case-sensitive searching is not allowable. wxFR_NOWHOLEWORD Specifies that whole-word searching is not allowable. wxFindReplaceData holds the data for wxFindReplaceDialog. It is used to initial- ize the dialog with the default values and will keep the last values from the dialog when it is closed. It is also updated each time a wxFindDialogEvent is gen- erated, so instead of using the wxFindDialogEvent methods, you can also direct- ly query this object. Use the dialog’s GetData function to return a pointer to the data you passed to the dialog constructor. wxFindReplaceData Functions These are the functions for setting and accessing data in wxFindReplaceData. Note that the setters may only be called before showing the dialog, and calling them has no effect later. GetFindString and SetFindString are accessors for the search string, pro- vided by the application or entered by the user. GetFlags and SetFlags are accessors for the flags specifying the state of the find dialog (refer to Table 8-5). GetReplaceString and SetReplaceString are accessors for the replace string, provided by the application or entered by the user.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 231 Entry Dialogs 231 Find and Replace Example The following shows an example fragment of wxFindReplaceDialog usage, employing hypothetical DoFind and DoReplace functions to do the actual search and replace for the application. These functions would maintain application- dependent variables in the dialog class, storing the last position that was searched, so that each time the functions are called, the next match can be found. The functions will also change the document view and highlight the match. #include “wx/fdrepdlg.h” BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(ID_REPLACE, MyFrame::ShowReplaceDialog) EVT_FIND(wxID_ANY, MyFrame::OnFind) EVT_FIND_NEXT(wxID_ANY, MyFrame::OnFind) EVT_FIND_REPLACE(wxID_ANY, MyFrame::OnReplace) EVT_FIND_REPLACE_ALL(wxID_ANY, MyFrame::OnReplaceAll) EVT_FIND_CLOSE(wxID_ANY, MyFrame::OnFindClose) END_EVENT_TABLE() void MyFrame::ShowReplaceDialog( wxCommandEvent& event ) { if ( m_dlgReplace ) { delete m_dlgReplace; m_dlgReplace = NULL; } else { m_dlgReplace = new wxFindReplaceDialog ( this, &m_findData, wxT(“Find and replace dialog”), wxFR_REPLACEDIALOG ); m_dlgReplace->Show(true); } } void MyFrame::OnFind(wxFindDialogEvent& event) { if (!DoFind(event.GetFindString(), event.GetFlags())) { wxMessageBox(wxT(“No more matches.”)); } } void MyFrame::OnReplace(wxFindDialogEvent& event) { if (!DoReplace(event.GetFindString(), event.GetReplaceString(), event.GetFlags(), REPLACE_THIS))

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 232 232 Using Standard Dialogs Chapter 8 { wxMessageBox(wxT(“No more matches.”)); } } void MyFrame::OnReplaceAll(wxFindDialogEvent& event) { if (DoReplace(event.GetFindString(), event.GetReplaceString(), event.GetFlags(), REPLACE_ALL)) { wxMessageBox(wxT(“Replacements made.”)); } else { wxMessageBox(wxT(“No replacements made.”)); } } void MyFrame::OnFindClose(wxFindDialogEvent& event) { m_dlgReplace->Destroy(); m_dlgReplace = NULL; } PRINTING DIALOGS You use wxPageSetupDialog and wxPrintDialog in applications that print docu- ments. If you use the printing framework (including wxPrintout, wxPrinter, and other classes), you won’t need to invoke these dialogs explicitly in your code. For more on printing, refer to Chapter 5, “Drawing and Printing.” wxPageSetupDialog wxPageSetupDialog contains controls for paper size such as A4 and letter, orien- tation (landscape or portrait), and controls for setting left, top, right, and bot- tom margin sizes in millimeters. The user can also set printer-specific options by invoking a further dialog from this one.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 233 Printing Dialogs 233 Figure 8-27 shows the wxPageSetupDialog dialog under Windows. Figure 8-27 wxPageSetupDialog under Windows Figure 8-28 shows wxPageSetupDialog using the generic implementation under GTK+. If the GNOME printing libraries are installed, wxWidgets will instead use a native GNOME page setup dialog, as shown in Figure 8-29. Figure 8-28 wxPageSetupDialog under GTK+ without GNOME printing

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 234 234 Using Standard Dialogs Chapter 8 Figure 8-29 wxPageSetupDialog under GTK+ with GNOME printing The Mac OS X version of wxPageSetupDialog is shown in Figure 8-30. Figure 8-30 wxPageSetupDialog under Mac OS X To use this dialog, pass to the constructor a parent window and a point- er to a wxPageSetupDialogData object, which contains settings to pass to and retrieve from the dialog. You can create the dialog on the stack or dynamical- ly. The page setup data will be copied to the dialog’s own data; use GetPageSetupData to return a reference to the dialog’s data.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 235 Printing Dialogs 235 wxPageSetupData Functions wxPageSetupDialogData has the following functions. Ok returns true if the print data associated with the object is valid. This can return false on Windows if the current printer is not set, for example. On all other platforms, it returns true. SetMarginTopLeft takes a wxPoint object and sets the left and top margins in millimeters. Call GetMarginTopLeft to retrieve this value. SetMarginBottomRight takes a wxPoint object and sets the bottom and right margins in millimeters. Call GetMarginBottomRight to retrieve this value. SetPaperId sets the paper identifier to select the current paper size, instead of using SetPaperSize. See the documentation for this function for the symbols that are available. GetPaperId retrieves the paper identifier. SetPaperSize takes a wxSize object and sets the paper size in millimeters. Use GetPaperSize to retrieve the current paper size. EnableMargins enables or disables the margin controls (Windows only). Call GetEnableMargins to test the value of this setting. EnableOrientation enables or disables the orientation control (Windows only). Call GetEnableOrientation to test the value of this setting. EnablePaper enables or disables the paper size control (Windows only). Call GetEnablePaper to test the value of this setting. EnablePrinter enables or disables the Printer button, which invokes a print setup dialog. Call GetEnablePrinter to test the value of this setting. wxPageSetupDialog Example Here’s an example of using wxPageSetupDialog: #include “wx/printdlg.h” void MyFrame::OnPageSetup(wxCommandEvent& event) { wxPageSetupDialog pageSetupDialog(this, & m_pageSetupData); if (pageSetupDialog.ShowModal() == wxID_OK) m_pageSetupData = pageSetupDialog.GetPageSetupData(); } wxPrintDialog This class represents the print and print setup standard dialogs. You may obtain a wxPrinterDC device context from a successfully dismissed print dialog.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 236 236 Using Standard Dialogs Chapter 8 Figure 8-31 shows wxPrintDialog under Windows. Figure 8-31 wxPrintDialog under Windows Figure 8-32 shows wxPrintDialog under GTK+ without the GNOME printing libraries, and Figure 8-33 shows the dialog shown when the GNOME printing libraries are installed. Figure 8-32 wxPrintDialog under GTK+ without GNOME printing

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 237 Printing Dialogs 237 Figure 8-33 wxPrintDialog under GTK+ with GNOME printing Figure 8-34 shows wxPrintDialog under Mac OS X. As you can see from the buttons along the bottom, Mac OS X gives you the added advantage of sav- ing your document as a PDF file, and you can use the Mac OS X previewer as an alternative to the application’s preview window. Figure 8-34 wxPrintDialog under Mac OS X To use wxPrintDialog, create it on the stack or dynamically and pass the parent window and a pointer to a wxPrintDialogData object, whose contents will be copied to internal data in the dialog object. Call wxPrintDialogData:: SetSetupDialog with true before passing the data to the dialog if you want to show the print setup dialog instead of the print dialog. Following Microsoft’s conventions, the print setup dialog has been replaced by the wxPageSetupDialog, but for compatibility, some applications may still need to use the setup dialog.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 238 238 Using Standard Dialogs Chapter 8 When the dialog returns successfully, you can retrieve the wxPrintDialogData using the GetPrintDialogData function. Call GetPrintDC on the dialog to get a printer device context based on the settings the user has chosen. If the function returns a non-null pointer, the application is then responsible for deleting the device context. Ok returns true if the print data associated with the dialog is valid. This can return false on Windows if the current printer is not set, for example. On all other platforms, it returns true. wxPrintDialogData Functions These are the functions you can use with wxPrintDialogData. EnableHelp enables or disables the Help button. Use GetEnableHelp to return the value of this setting. EnablePageNumbers enables or disables the page number controls, and GetEnablePageNumbers returns the value of this setting. EnablePrintToFile enables or disables the Print to File check box. Use GetEnablePrintToFile to return the value of this setting. EnableSelection enables or disables the Selection radio button that lets the user specify that the current selection should be printed. Use GetEnableSelection to return the value of this setting. SetCollate sets the Collate check box to be true or false. Use GetCollate to return the value of this setting. SetFromPage and SetToPage set the page range to print. Use GetFromPage and GetToPage to return this range. SetMinPage and SetMaxPage set the minimum and maximum page numbers that can be printed. Use GetMinPage and GetMaxPage to return these values. SetNoCopies sets the default number of copies that will be printed. Use GetNoCopies to return the value of this setting. SetPrintToFile sets the Print to File check box to true or false. Use GetPrintToFile to return the value of this setting. SetSelection sets the Selection radio button. Use GetSelection to return the value of this setting. SetSetupDialog determines whether the print setup dialog is shown (true) or the normal print dialog is shown (false). Use GetSetupDialog to return the value of this setting. SetPrintData sets the internal wxPrintData object. GetPrintData returns a reference to the internal wxPrintData object. wxPrintDialog Example The following example shows wxPrintDialog being used to return a suitable printer device context: #include “wx/printdlg.h” void MyFrame::OnPrint(wxCommandEvent& event) {

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 239 Summary 239 wxPrintDialogData dialogData; dialogData.SetFromPage(0); dialogData.SetToPage(10); wxPrintDialog printDialog(this, & m_dialogData); if (printDialog.ShowModal() == wxID_OK) { // After calling GetPrintDC(), the application // owns the DC wxDC* dc = printDialog.GetPrintDC(); // Draw on the device context ... // Destroy it delete dc; } } However, usually you can avoid invoking the print dialog directly. Instead, use the higher-level printing framework (refer to Chapter 5). The print dialog will be shown as a side effect of calling wxPrinter::Print. SUMMARY In this chapter, you have learned about the standard dialogs that you can use to present information and retrieve user choices with very little code. For fur- ther examples of using standard dialogs, see samples/dialogs in your wxWidgets distribution. Next, we’ll show you how to write your own dialogs.

Smart_Ch08f.qxd 6/10/05 11:19 AM Page 240

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 241 C H A P T E R 9 Writing Custom Dialogs Sooner or later, you will have to create your own dialogs, whether simple ones with only a few buttons and some text or highly complex dialogs with notebook controls, multiple panels, custom controls, context-sensitive help, and so on. In this chapter, we cover the principles of creating custom dialogs and transfer- ring data between C++ variables and the controls. We also describe the wxWidgets resource system, which enables you to load dialogs and other user interface elements from XML files. STEPS IN CREATING A CUSTOM DIALOG When you start writing your own specialized dialogs, the fun really starts. Here are the steps you’ll typically need to take: 1. Derive a new class from wxDialog. 2. Decide where the data is stored and how the application accesses user choices. 3. Write code to create and lay out the controls. 4. Add code that transfers data between C++ variables and the controls. 5. Add functions and their event table entries to handle events from con- trols. 6. Add user interface (UI) update handlers to set controls to the correct state. 7. Add help, in particular tooltips, context-sensitive help (not implemented on Mac OS X), and a way of showing an explanation of the dialog in your application’s user manual. 8. Invoke the dialog from a suitable place in your application code. Let’s illustrate these steps with a concrete example. 241

Smart_Ch09f.qxd 6/10/05 3:28 PM Page 242 242 Writing Custom Dialogs Chapter 9 AN EXAMPLE: PERSONALRECORDDIALOG As we saw in the previous chapter, dialogs come in two flavors: modal and modeless. We’ll illustrate custom dialog creation with a modal dialog because it’s the more common kind and has fewer complications. The application will invoke the dialog with ShowModal and then query the dialog for user selections. Until ShowModal returns, all user interactions with the application will be con- tained within the little world of your custom dialog (and any further modal dialogs that your dialog may invoke). Many of the steps involved in creating a custom dialog can be accom- plished very easily by using a dialog editor, such as wxDesigner or DialogBlocks. The amount of coding left to do depends on the complexity of your dialog. Here, we will assume handcrafting of all the code in order to demonstrate the principles, but it’s highly recommended that you use a tool to help you because it will save you many hours of repetitive work. We’ll illustrate the steps involved in creating a custom dialog with a sim- ple example where the user is required to enter his or her name, age, sex, and whether the user wants to vote. This dialog is called PersonalRecordDialog, as shown in Figure 9-1. Figure 9-1 Personal record dialog under Windows The Reset button restores all controls to their default values. The OK but- ton dismisses the dialog and returns wxID_OK from ShowModal. The Cancel but- ton returns wxID_CANCEL and does not update the dialog’s variables from the values shown in the controls. The Help button invokes a few lines of text describing the dialog (although in a real application, this button should invoke a nicely formatted help file). A good user interface should not allow the user to enter data that has no meaning in the current context. In this example, the user should not be able to use the Vote control if Age is less than the voting age (18 in the U.S. or U.K.). So, we will ensure that when the age entered is less than 18, the Vote check box is disabled.

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 243 An Example: PersonalRecordDialog 243 Deriving a New Class Here’s the declaration for our PersonalRecordDialog. We provide run-time type information by using DECLARE_CLASS, and we add an event table with DECLARE_EVENT_TABLE. /*! * PersonalRecordDialog class declaration */ class PersonalRecordDialog: public wxDialog { DECLARE_CLASS( PersonalRecordDialog ) DECLARE_EVENT_TABLE() public: // Constructors PersonalRecordDialog( ); PersonalRecordDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& caption = wxT(“Personal Record”), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxCAPTION|wxRESIZE_BORDER|wxSYSTEM_MENU ); // Initialize our variables void Init(); // Creation bool Create( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& caption = wxT(“Personal Record”), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxCAPTION|wxRESIZE_BORDER|wxSYSTEM_MENU ); // Creates the controls and sizers void CreateControls(); }; Note that we follow wxWidgets convention by allowing both one-step and two- step construction—we provide a default constructor and Create function as well as a more verbose constructor. Designing Data Storage We have four pieces of data to store: name (string), age (integer), sex (boolean), and voting preference (boolean). To make it easier to use a wxChoice control with the data, we’re going to use an integer to store the boolean value for sex, but the class interface can present it as boolean: true for female and false for male. Let’s add these data members and accessors to the PersonalRecordDialog class:

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 244 244 Writing Custom Dialogs Chapter 9 // Data members wxString m_name; int m_age; int m_sex; bool m_vote; // Name accessors void SetName(const wxString& name) { m_name = name; } wxString GetName() const { return m_name; } // Age accessors void SetAge(int age) { m_age = age; } int GetAge() const { return m_age; } // Sex accessors (male = false, female = true) void SetSex(bool sex) { sex ? m_sex = 1 : m_sex = 0; } bool GetSex() const { return m_sex == 1; } // Does the person vote? void SetVote(bool vote) { m_vote = vote; } bool GetVote() const { return m_vote; } Coding the Controls and Layout Now let’s add a CreateControls function to be called from Create. CreateControls adds wxStaticText controls, wxButton controls, a wxSpinCtrl, a wxTextCtrl, a wxChoice, and a wxCheckBox. Refer to Figure 9-1 earlier in the chapter to see the resulting dialog. We’re using sizer-based layout for this dialog, which is why it looks a bit more involved than you might expect for a small number of controls. (We described sizers in Chapter 7, “Window Layout Using Sizers”—briefly, they enable you to create dialogs that look good on any platform and that easily adapt to translation and resizing.) You can use a different method if you want, such as loading the dialog from a wxWidgets resource file (XRC file). The basic principle of sizer-based layout is to put controls into nested boxes (sizers), which can distribute space among the controls or stretch just enough to contain its controls. The sizers aren’t windows—they form a sepa- rate hierarchy, and the controls remain children of their parent, regardless of the complexity of the hierarchy of sizers. You might like to refresh your mem- ory by looking at the schematic view of a sizer layout that we showed in Figure 7-2 in Chapter 7. In CreateControls, we’re using a vertical box sizer (boxSizer) nested in another vertical box sizer (topSizer) to give a decent amount of space around the dialog’s controls. A horizontal box sizer is used for the wxSpinCtrl, wxChoice, and wxCheckBox, and a second horizontal box sizer (okCancelSizer) is used for the Reset, OK, Cancel, and Help buttons. /*! * Control creation for PersonalRecordDialog */

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 245 An Example: PersonalRecordDialog 245 void PersonalRecordDialog::CreateControls() { // A top-level sizer wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); this->SetSizer(topSizer); // A second box sizer to give more space around the controls wxBoxSizer* boxSizer = new wxBoxSizer(wxVERTICAL); topSizer->Add(boxSizer, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); // A friendly message wxStaticText* descr = new wxStaticText( this, wxID_STATIC, wxT(“Please enter your name, age and sex, and specify whether you wish to\nvote in a general election.”), wxDefaultPosition, wxDefaultSize, 0 ); boxSizer->Add(descr, 0, wxALIGN_LEFT|wxALL, 5); // Spacer boxSizer->Add(5, 5, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); // Label for the name text control wxStaticText* nameLabel = new wxStaticText ( this, wxID_STATIC, wxT(“&Name:”), wxDefaultPosition, wxDefaultSize, 0 ); boxSizer->Add(nameLabel, 0, wxALIGN_LEFT|wxALL, 5); // A text control for the user’s name wxTextCtrl* nameCtrl = new wxTextCtrl ( this, ID_NAME, wxT(“Emma”), wxDefaultPosition, wxDefaultSize, 0 ); boxSizer->Add(nameCtrl, 0, wxGROW|wxALL, 5); // A horizontal box sizer to contain age, sex and vote wxBoxSizer* ageSexVoteBox = new wxBoxSizer(wxHORIZONTAL); boxSizer->Add(ageSexVoteBox, 0, wxGROW|wxALL, 5); // Label for the age control wxStaticText* ageLabel = new wxStaticText ( this, wxID_STATIC, wxT(“&Age:”), wxDefaultPosition, wxDefaultSize, 0 ); ageSexVoteBox->Add(ageLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); // A spin control for the user’s age wxSpinCtrl* ageSpin = new wxSpinCtrl ( this, ID_AGE, wxEmptyString, wxDefaultPosition, wxSize(60, -1), wxSP_ARROW_KEYS, 0, 120, 25 ); ageSexVoteBox->Add(ageSpin, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); // Label for the sex control wxStaticText* sexLabel = new wxStaticText ( this, wxID_STATIC, wxT(“&Sex:”), wxDefaultPosition, wxDefaultSize, 0 ); ageSexVoteBox->Add(sexLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); // Create the sex choice control

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 246 246 Writing Custom Dialogs Chapter 9 wxString sexStrings[] = { wxT(“Male”), wxT(“Female”) }; wxChoice* sexChoice = new wxChoice ( this, ID_SEX, wxDefaultPosition, wxSize(80, -1), WXSIZEOF(sexStrings), sexStrings, 0 ); sexChoice->SetStringSelection(wxT(“Female”)); ageSexVoteBox->Add(sexChoice, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); // Add a spacer that stretches to push the Vote control // to the right ageSexVoteBox->Add(5, 5, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5); wxCheckBox* voteCheckBox = new wxCheckBox( this, ID_VOTE, wxT(“&Vote”), wxDefaultPosition, wxDefaultSize, 0 ); voteCheckBox ->SetValue(true); ageSexVoteBox->Add(voteCheckBox, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); // A dividing line before the OK and Cancel buttons wxStaticLine* line = new wxStaticLine ( this, wxID_STATIC, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); boxSizer->Add(line, 0, wxGROW|wxALL, 5); // A horizontal box sizer to contain Reset, OK, Cancel and Help wxBoxSizer* okCancelBox = new wxBoxSizer(wxHORIZONTAL); boxSizer->Add(okCancelBox, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); // The Reset button wxButton* reset = new wxButton( this, ID_RESET, wxT(“&Reset”), wxDefaultPosition, wxDefaultSize, 0 ); okCancelBox->Add(reset, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); // The OK button wxButton* ok = new wxButton ( this, wxID_OK, wxT(“&OK”), wxDefaultPosition, wxDefaultSize, 0 ); okCancelBox->Add(ok, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); // The Cancel button wxButton* cancel = new wxButton ( this, wxID_CANCEL, wxT(“&Cancel”), wxDefaultPosition, wxDefaultSize, 0 ); okCancelBox->Add(cancel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); // The Help button wxButton* help = new wxButton( this, wxID_HELP, wxT(“&Help”), wxDefaultPosition, wxDefaultSize, 0 ); okCancelBox->Add(help, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); }

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 247 An Example: PersonalRecordDialog 247 Data Transfer and Validation Now we have the bare controls of the dialog, but the controls and the dialog’s data are not connected. How do we make that connection? When a dialog is first shown, wxWidgets calls InitDialog, which in turn sends a wxEVT_INIT_DIALOG event. The default handler for this event calls TransferDataToWindow on the dialog. To transfer data from the controls back to the variables, you can call TransferDataFromWindow when the user confirms his or her input. Again, wxWidgets does this for you by defining a default handler for wxID_OK command events, which calls TransferDataFromWindow before calling EndModal to dismiss the dialog. So, you can override TransferDataToWindow and TransferDataFromWindow to transfer your data. For our dialog, the code might look like this: /*! * Transfer data to the window */ bool PersonalRecordDialog::TransferDataToWindow() { wxTextCtrl* nameCtrl = (wxTextCtrl*) FindWindow(ID_NAME); wxSpinCtrl* ageCtrl = (wxSpinCtrl*) FindWindow(ID_SAGE); wxChoice* sexCtrl = (wxChoice*) FindWindow(ID_SEX); wxCheckBox* voteCtrl = (wxCheckBox*) FindWindow(ID_VOTE); nameCtrl->SetValue(m_name); ageCtrl->SetValue(m_age); sexCtrl->SetSelection(m_sex); voteCtrl->SetValue(m_vote); return true; } /*! * Transfer data from the window */ bool PersonalRecordDialog::TransferDataFromWindow() { wxTextCtrl* nameCtrl = (wxTextCtrl*) FindWindow(ID_NAME); wxSpinCtrl* ageCtrl = (wxSpinCtrl*) FindWindow(ID_SAGE); wxChoice* sexCtrl = (wxChoice*) FindWindow(ID_SEX); wxCheckBox* voteCtrl = (wxCheckBox*) FindWindow(ID_VOTE); m_name = nameCtrl->GetValue(); m_age = ageCtrl->GetValue(); m_sex = sexCtrl->GetSelection(); m_vote = voteCtrl->GetValue(); return true; } However, there’s an easier way of transferring data. wxWidgets supports validators, which are objects that link data variables and their corresponding

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 248 248 Writing Custom Dialogs Chapter 9 controls. Although not always applicable, the use of validators where possible will save you a lot of time and can make it unnecessary to write TransferData ToWindow and TransferDataFromWindow functions. In our PersonalRecordDialog example, we can use the following code instead of the previous two functions: FindWindow(ID_NAME)->SetValidator( wxTextValidator(wxFILTER_ALPHA, & m_name)); FindWindow(ID_AGE)->SetValidator( wxGenericValidator(& m_age)); FindWindow(ID_SEX)->SetValidator( wxGenericValidator(& m_sex); FindWindow(ID_VOTE)->SetValidator( wxGenericValidator(& m_vote); These few lines of code at the end of CreateControls replace the two overrid- den functions. As a bonus, the user will be prevented from accidentally entering numbers in the Name field. Validators can perform two jobs—as well as data transfer, they can validate the data and show error messages if the data doesn’t conform to a particular specification. In this example, no actual validation of the input is done, other than for the name. wxGenericValidator is a relatively simple class, only doing data transfer. However, it works with the basic control classes. The other validator provided as standard, wxTextValidator, has more sophisticated behavior and can even intercept keystrokes to veto invalid characters. In the example, we just use the standard style wxFILTER_ALPHA, but we could also specify which characters should or should not be regarded as valid by using the validator’s SetIncludes and SetExcludes functions. We need to dig a bit deeper into how wxWidgets handles validators in order to understand what’s going on here. As we’ve seen, the default OnOK han- dler calls TransferDataToWindow, but before it does so, it calls Validate, vetoing the calls to TransferDataToWindow and EndModal if validation fails. This is the default implementation of OnOK: void wxDialog::OnOK(wxCommandEvent& event) { if ( Validate() && TransferDataFromWindow() ) { if ( IsModal() ) EndModal(wxID_OK); // If modal else { SetReturnCode(wxID_OK); this->Show(false); // If modeless } } } The default implementation of Validate iterates through all the children of the dialog (and their descendants, if you specified the extra window style wxWS_EX_VALIDATE_RECURSIVELY), calling Validate for each control’s wxValidator

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 249 An Example: PersonalRecordDialog 249 object. If any of these calls fails, then validation for the dialog fails, and the dialog is not dismissed. The validator is expected to show a suitable error mes- sage from within its Validate function if it fails the validation. Similarly, TransferDataToWindow and TransferDataFromWindow will be called automatically for the validators of a dialog’s controls. A validator must do data transfer, but validation is optional. A validator is an event handler, and the event processing mechanism will route events to the validator, if present, before passing the events on to the control. This enables validators to intercept user input—for example, to veto characters that are not permitted in a control. Such vetoing should normally be accompanied by a beep to inform the user that the key was pressed but not accepted. Because the two provided validator classes may not be sufficient for your needs, especially if you write your own custom controls, you can derive new validator classes from wxValidator. This class should have a copy constructor and a Clone function that returns a copy of the validator object, as well as implementations for data transfer and validation. A validator will typically store a pointer to a C++ variable, and the constructor may take flags to speci- fy modes of use. You can look at the files include/wx/valtext.h and src/ common/valtext.cpp in wxWidgets to see how a validator can be implemented; see also “Writing Your Own Controls” in Chapter 12, “Advanced Window Classes.” Handling Events In this example, wxWidgets’ default processing for OK and Cancel are suffi- cient without any extra coding on our part, as long as we use the standard wxID_OK and wxID_CANCEL identifiers for the controls. However, for non-trivial dialogs, you probably will have to intercept and handle events from controls. In our example, we have a Reset button, which can be clicked at any time to reset the dialog back to its default values. We add an OnResetClick event han- dler and a suitable entry in our event table. Implementing OnResetClick turns out to be very easy; first we reset the data variables by calling the Init func- tion we added to centralize data member initialization. Then we call TransferDataToWindow to display that data. BEGIN_EVENT_TABLE( PersonalRecordDialog, wxDialog ) ... EVT_BUTTON( ID_RESET, PersonalRecordDialog::OnResetClick) ... END_EVENT_TABLE() void PersonalRecordDialog::OnResetClick( wxCommandEvent& event ) { Init(); TransferDataToWindow(); }

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 250 250 Writing Custom Dialogs Chapter 9 Handling UI Updates One of the challenges faced by the application developer is making sure that the user can’t click on controls and menus that are not currently applicable. A sure sign of sloppy programming is the appearance of messages that say, “This option is not currently available.” If an option isn’t available, then it should not look available, and clicking on the control or menu should do nothing. As time-consuming as it can be, the programmer should update the elements of the interface to reflect the context at every instant. In our example, we must disable the Vote check box when the user’s age is less than 18 because in that case, the decision is not available to the user. Your first thought might be to add an event handler for the Age spin control and enable or disable the Vote check box according to the spin control’s value. Although this may be fine for simple user interfaces, imagine what happens when many factors are influencing the availability of controls. Even worse, there are some cases where the approach doesn’t work at all because you can- not be notified when the change occurs. An example of this situation is when you need to enable a Paste button or menu item when data becomes available on the clipboard. This event is outside your power to intercept because the data may become available from another program. To solve these problems, wxWidgets provides an event class called wxUpdateUIEvent that it sends to all windows in idle time—that is, when the event loop has finished processing all other input. You can add EVT_UPDATE_UI event table entries to your dialog, one for each control whose state you need to maintain. Each UI update event handler evaluates the current state of the world and calls functions in the event object (not the control) to enable, dis- able, check, or uncheck the control. This technique puts the logic for updating each control in one place, calling the event handler even when no real event has been handled in the application. You can breathe a sigh of relief because you don’t have to remember to update the user interface after any change that might happen to be relevant! Here’s our UI update handler for the Vote control. Note that we can’t use the m_age variable because transfer from the controls to the variables doesn’t happen until the user clicks OK. BEGIN_EVENT_TABLE( PersonalRecordDialog, wxDialog ) ... EVT_UPDATE_UI( ID_VOTE, PersonalRecordDialog::OnVoteUpdate ) ... END_EVENT_TABLE() void PersonalRecordDialog::OnVoteUpdate( wxUpdateUIEvent& event ) { wxSpinCtrl* ageCtrl = (wxSpinCtrl*) FindWindow(ID_AGE); if (ageCtrl->GetValue() < 18) {

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 251 An Example: PersonalRecordDialog 251 event.Enable(false); event.Check(false); } else event.Enable(true); } Don’t worry unduly about efficiency considerations; plenty of spare cycles are available for processing these handlers. However, if you have a very complex application and run into performance problems, see the wxUpdateUIEvent docu- mentation for the functions SetMode and SetUpdateInterval that can be used to decrease the time wxWidgets spends processing these events. Adding Help There are at least three kinds of help you can provide for your dialog:

Tooltips

Context-sensitive help

Online help You can probably think of further techniques not explicitly supported by wxWidgets. We already have some descriptive text on the dialog described here. For a more complex dialog, you could create a wxHtmlWindow instead of a wxStaticText and load an HTML file containing further details. Alternatively, a small help button could be placed next to each control to show a description when clicked. The three main types of help supported by wxWidgets are described in the following sections. Tooltips Tooltips are little windows that pop up when the pointer is hovering over a control, containing a short description of the control’s purpose. You call SetToolTip to set the tooltip text for a control. Because this can get annoying for experienced users, you should provide an application setting to switch this off (that is, SetToolTip will not be called when dialogs are created and dis- played). Context-Sensitive Help Context-sensitive help provides a short pop-up description similar to a tooltip. The user must first click on a special button and then on a control to get the help or press F1 to get help for the focused control (on Windows). On Windows, you can specify the extra window style wxDIALOG_EX_CONTEXTHELP to create the little question mark button on the dialog title. On other platforms, you can

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 252 252 Writing Custom Dialogs Chapter 9 create a wxContextHelpButton on the dialog (usually next to the OK and Cancel buttons). In your application initialization, you should call #include “wx/cshelp.h” wxHelpProvider::Set(new wxSimpleHelpProvider); This tells wxWidgets how to provide the strings for context-sensitive help. You call SetHelpText to set the help text for a control. Here’s a function to add context-sensitive help and tooltips to our dialog: // Sets the help text for the dialog controls void PersonalRecordDialog::SetDialogHelp() { wxString nameHelp = wxT(“Enter your full name.”); wxString ageHelp = wxT(“Specify your age.”); wxString sexHelp = wxT(“Specify your gender, male or female.”); wxString voteHelp = wxT(“Check this if you wish to vote.”); FindWindow(ID_NAME)->SetHelpText(nameHelp); FindWindow(ID_NAME)->SetToolTip(nameHelp); FindWindow(ID_AGE)->SetHelpText(ageHelp); FindWindow(ID_AGE)->SetToolTip(ageHelp); FindWindow(ID_SEX)->SetHelpText(sexHelp); FindWindow(ID_SEX)->SetToolTip(sexHelp); FindWindow(ID_VOTE)->SetHelpText(voteHelp); FindWindow(ID_VOTE)->SetToolTip(voteHelp); } If you want to invoke context-sensitive help yourself, as opposed to letting the dia- log or wxContextHelpButton handle it, you can simply put this in an event handler: wxContextHelp contextHelp(window); This will put wxWidgets in a loop that detects a left-click on a control, after which it will send a wxEVT_HELP event to the control to initiate popping up a help window. You don’t have to limit yourself to the way wxWidgets implements the stor- age and display of help text, though. You can create your own class derived from wxHelpProvider, overriding GetHelp, SetHelp, AddHelp, RemoveHelp, and ShowHelp. Online Help Most applications come with a help file that provides detailed instructions for use. wxWidgets provides the means to control several kinds of help windows through different derivations of the wxHelpControllerBase class. See Chapter 20, “Perfecting Your Application,” for more information about pro- viding online help.

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 253 An Example: PersonalRecordDialog 253 For the purposes of this example, we’ll just use a wxMessageBox to display some help when the user clicks on the Help button. BEGIN_EVENT_TABLE( PersonalRecordDialog, wxDialog ) ... EVT_BUTTON( wxID_HELP, PersonalRecordDialog::OnHelpClick ) ... END_EVENT_TABLE() void PersonalRecordDialog::OnHelpClick( wxCommandEvent& event ) { // Normally we would wish to display proper online help. /* wxGetApp().GetHelpController().DisplaySection(wxT(“Personal record dialog”)); */ // For this example, we’re just using a message box. wxString helpText = wxT(“Please enter your full name, age and gender.\n”) wxT(“Also indicate your willingness to vote in general elections.\n\n”) wxT(“No non-alphabetical characters are allowed in the name field.\n”) wxT(“Try to be honest about your age.”); wxMessageBox(helpText, wxT(“Personal Record Dialog Help”), wxOK|wxICON_INFORMATION, this); } The Complete Class The complete implementation of the dialog is listed in Appendix J, “Code Listings,” and can also be found in examples/chap09 on the CD-ROM. Invoking the Dialog Now that we have the dialog completely coded, we can invoke it: PersonalRecordDialog dialog(NULL, ID_PERSONAL_RECORD, wxT(“Personal Record”)); dialog.SetName(wxEmptyString); dialog.SetAge(30); dialog.SetSex(0); dialog.SetVote(true); if (dialog.ShowModal() == wxID_OK) { wxString name = dialog.GetName(); int age = dialog.GetAge(); bool sex = dialog.GetSex(); bool vote = dialog.GetVote(); }

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 254 254 Writing Custom Dialogs Chapter 9 ADAPTING DIALOGS FOR SMALL DEVICES wxWidgets can be used on mobile and other embedded devices, using GTK+, X11, and Windows CE ports (and others in the future). The most obvious lim- itation associated with many of these devices is the size of the display, which for a smartphone may be as little as 176×220 pixels. Many dialogs will need an alternative dialog layout for small displays; some controls may be omitted altogether, especially as the functionality of the application may be reduced compared with a desktop application. You can detect the size of the device with wxSystemSettings::GetScreenType, for example: #include “wx/settings.h” bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA); GetScreenType returns one of the values listed in Table 9-1. Because the types increase in value as the screen size increases, you can use integer comparison operators to deal with classes of devices with screens below a certain size, as in the example we’ve just seen. Table 9-1 Screen Types wxSYS_SCREEN_NONE Undefined screen type wxSYS_SCREEN_TINY Tiny screen, less than 320×240 wxSYS_SCREEN_PDA PDA screen, 320×240 or more but less than 640×480 wxSYS_SCREEN_SMALL Small screen, 640×480 or more but less than 800×600 wxSYS_SCREEN_DESKTOP Desktop screen, 800×600 or more If you need more detail about the display size, there are three ways to get it: 1. Use wxSystemSettings::GetMetric, passing wxSYS_SCREEN_X or wxSYS_SCREEN_Y. 2. Call wxGetDisplaySize, which returns a wxSize object. 3. Create a wxDisplay object and call GetGeometry, which returns a wxRect containing the bounding rectangle of the display. When you know you may have a stunted display to run on, what can you do with this information? Here are some strategies you can use: 1. Replace the whole layout by loading a different XRC file or executing dif- ferent control creation code. If the controls don’t change type, you may not need to change the event handling code at all. 2. Reduce the number of controls and space.

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 255 Further Considerations in Dialog Design 255 3. Change the type of some controls to take less space (for example, from wxListBox to wxComboBox). This will need some modification of the associ- ated event handler. 4. Change the orientation of one or several sizers. Some small devices have a lot more space in one direction than in another. Occasionally you will need to use API enhancements for particular platforms. Microsoft Smartphone has two special buttons that you can assign labels, such as “OK” and “Cancel”. On this platform, instead of creating two wxButton objects, you should call wxDialog::SetLeftMenu and wxDialog::SetRightMenu with an identifier, label, and optional submenu to show. Because these functions only exist on the Smartphone port, you need to conditionally compile your code. For example: #ifdef __SMARTPHONE__ SetLeftMenu(wxID_OK, wxT(“OK”)); SetRightMenu(wxID_OK, wxT(“Cancel”)); #else wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL); GetTopSizer()->Add(buttonSizer, 0, wxALL|wxGROW, 0); buttonSizer->Add(new wxButton(this, wxID_OK), 0, wxALL, 5); buttonSizer->Add(new wxButton(this, wxID_CANCEL), 0, wxALL, 5); #endif FURTHER CONSIDERATIONS IN DIALOG DESIGN Here are a few tips to help you create professional-looking dialogs. Keyboard Navigation Provide mnemonics in static text labels and other labeled controls by inserting ampersands in front of characters. On some platforms (notably Windows and GTK+), this will help the user navigate between controls. Always provide a means for the user to cancel the dialog, preferably with the option of using the Escape key. If a dialog has a button with the identifier wxID_CANCEL, its handler will automatically be called when the user presses the Escape key. So, if you have a Close button, consider giving it the wxID_CANCEL identifier. Provide a default button (often OK)—for example, by calling wxButton:: SetDefault. The command for this button will be invoked when the user press- es the Enter key.

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 256 256 Writing Custom Dialogs Chapter 9 Data and UI Separation To simplify the example, the data variables that PersonalRecordDialog uses are stored in the class itself. However, a better design would be to provide a data class separate from the dialog class, with a copy constructor and assignment operator, so that you can pass a copy of the data to the dialog and retrieve the modified data from the dialog only if the user confirms any changes. This is the approach adopted for some of the standard dialogs. As an exercise, you can rewrite the PersonalRecordDialog using a PersonalRecordData class. The dialog constructor will take a PersonalRecordData reference, and there will be a GetData function so that the calling application can retrieve the data. In general, always consider how you can separate out the UI functional- ity from non-UI functionality. The result will usually be code that is more com- pact and easier to understand and debug. Don’t be afraid to introduce new classes to make the design more elegant, and make use of copy constructors and assignment operators so that objects can easily be copied and assigned without the application having to repeat lots of low-level code. Unless you provide an Apply button that commits your changes to the underlying data, canceling the dialog should leave the application data in the same state as it was before the dialog was opened. The use of a separate data class makes this easier to achieve because the dialog isn’t editing “live” data but rather a copy. Layout If your dialog looks claustrophobic or somehow odd, it may be due to a lack of space. Try adding a bigger border around the edge of the dialog by using an additional sizer (as in our PersonalRecordDialog example) and adding space between groups of controls. Use wxStaticBoxSizer and wxStaticLine to logically group or separate controls. Use wxGridSizer and wxFlexGridSizer to align con- trols and their labels so that they don’t appear as a random jumble. In sizer- based layouts, use expanding spacers to align a group of controls. For example, often OK, Cancel, and Help buttons are in a right-aligned group, which can be achieved by placing a spacer and the buttons in a horizontal wxBoxSizer and setting the spacer to expand horizontally (give it a positive stretch factor). If possible and appropriate, make your dialog resizable. Traditionally, Windows dialog boxes haven’t often been resizable, but there is no reason why this should be the case, and fiddling with tiny controls on a large display can be a frustrating experience for the user. wxWidgets makes it easy to create resizable dialogs with sizers, and you should be using sizers anyway to allow for font and control size differences and changes in language. Choose careful- ly which elements should grow; for example, there may be a multi-line text control that is a good candidate for growing and giving the user more elbow room. Again, you can put expanding spacers to good use to preserve alignment

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 257 Using wxWidgets Resource Files 257 in a resized dialog. Note that we’re not resizing controls in the sense of zoom- ing in and out, making text bigger or smaller—we’re simply giving more or less space for items in the control. See Chapter 7 for more about sizers. If you find that your dialog is becoming too large, split it up into a num- ber of panels and use a wxNotebook, wxListbook, or wxChoicebook to enable selec- tion of one page at a time. Using lots of independent dialogs is annoying to the user and clutters up your menus, whereas browsing through pages is perfect- ly acceptable. Scrolling panels should be avoided unless there’s a very good reason to use them. The ability to scroll controls is not supported on all plat- forms, and use of scrolling can be a sign that the user interface has not been adequately planned. If you have many properties to edit, consider using a property editor based on wxGrid or a third-party class (see wxPropertyGrid, which is mentioned in Appendix E, “Third-Party Tools for wxWidgets”). Aesthetics Be consistent with label capitalization. Don’t be tempted to use custom colors or fonts in your dialog; this can be distracting and can look out of place in the context of the current theme and other dialogs in the application. For best results across platforms, leave control fonts and colors to wxWidgets. Instead, consider providing some impact through judicious use of wxStaticBitmap con- trols. Alternatives to Dialogs Finally, consider whether you should be creating an independent dialog box at all—a modeless solution, such as a tab in the main application window, might be better. Most of the principles of dialog design and implementation apply to modeless dialogs and panels, but there are added challenges of layout (the win- dow has less control of its size) and synchronization (the window may no longer have exclusive use of the data it is showing). USING WXWIDGETS RESOURCE FILES You can load specifications of dialogs, frames, menu bars, toolbars, and so on from XML files with extension xrc instead of creating these elements explicit- ly in C++ code. This enables better separation of code and user interface, such as enabling an application’s dialog design to be changed at runtime. XRC files can be exported by a range of UI design tools, including wxDesigner, DialogBlocks, XRCed, and wxGlade.

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 258 258 Writing Custom Dialogs Chapter 9 Loading Resources To use XRC files in your application, you need to include wx/xrc/xmlres.h in your application code. If you will be converting your XRC files to binary XRS files, as we will describe shortly, install the zip file system handler by placing an AddHandler call in your OnInit function: #include “wx/filesys.h” #include “wx/fs_zip.h” wxFileSystem::AddHandler(new wxZipFSHandler); Initialize the XRC system by adding this to your OnInit: wxXmlResource::Get()->InitAllHandlers(); Load the XRC file with code like this: wxXmlResource::Get()->Load(wxT(“resources.xrc”)); This makes wxWidgets aware of the resources in the file; to create a real UI element, we need another call. For example, the following fragment creates a dialog whose resource name is dialog1: MyDialog dlg; wxXmlResource::Get()->LoadDialog(& dlg, parent, wxT(“dialog1”)); dlg.ShowModal(); The following code shows how to load menu bars, menus, toolbars, bitmaps, icons, and panels. MyFrame::MyFrame(const wxString& title): wxFrame(NULL, -1, title) { SetMenuBar(wxXmlResource::Get()->LoadMenuBar(wxT(“mainmenu”))); SetToolBar(wxXmlResource::Get()->LoadToolBar(this, wxT(“toolbar”))); wxMenu* menu = wxXmlResource::Get()->LoadMenu(wxT(“popupmenu”)); wxIcon icon = wxXmlResource::Get()->LoadIcon(wxT(“appicon”)); SetIcon(icon); wxBitmap bitmap = wxXmlResource::Get()->LoadBitmap(wxT(“bmp1”)); // Finish creating panelA after making an instance of it

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 259 Using wxWidgets Resource Files 259 MyPanel* panelA = new MyPanel; panelA = wxXmlResource::Get()->LoadPanel(panelA, this, wxT(“panelA”)); // A second method: get XRC to both create and load panelB wxPanel* panelB = wxXmlResource::Get()->LoadPanel(this, wxT(“panelB”)); } wxWidgets maintains a single wxXmlResource object that you can use, but alter- natively, you can create a wxXmlResource object, load resources, and then destroy it. You can also use wxXmlResource::Set to set the current global resource object, destroying the old one. To define event tables for windows loaded from a resource file, you can’t use integer identifiers because resources have string names. Instead, use the XRCID macro, which takes a resource name and returns an integer identifier associated with the name. XRCID is an alias for the function wxXmlResource:: GetXRCID. Here’s an example of XRCID usage: BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(XRCID(“menu_quit”), MyFrame::OnQuit) EVT_MENU(XRCID(“menu_about”), MyFrame::OnAbout) END_EVENT_TABLE() Using Binary and Embedded Resource Files It can be convenient to combine a number of resource files into one binary file (extension xrs). To compile XRC files into a zip file that the resource system can load, use the utility wxrc located in the utils/wxrc directory in your wxWidgets distribution: wxrc resource1.xrc resource2.xrc -o resource.xrs Use wxXmlResource::Load to load a binary resource file in just the same way as with a plain XML file. Tip Instead of creating a separate zip file for your XRC files, you can include them in a single zip file that includes other files your applications needs, such as HTML files, images, and so on. wxXmlResource::Load accepts vir- tual file system specifications, as described in Chapter 14, “Files and Streams,” so you can write wxXmlResource::Get()->Load(wxT(“resources.bin#zip:dialogs.xrc”));

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 260 260 Writing Custom Dialogs Chapter 9 You can also compile your XRC files into C++ code that may be embedded in your application, thereby eliminating a separate resource file. Here’s the wxrc command to do this: wxrc resource1.xrc resource2.xrc –c -o resource.cpp Compile this C++ file as normal and link it with your application. The file includes a function InitXmlResource, which you have to call, for example: extern void InitXmlResource(); // defined in generated file wxXmlResource::Get()->InitAllHandlers(); InitXmlResource(); Table 9-2 lists the command-line options and arguments that wxrc accepts. Table 9-2 wxrc Commands Short Command Long Command Description -h —help Shows a help message. -v —verbose Shows verbose logging information. -c —cpp-code Writes C++ source rather than an XRS file. -p —python-code Writes Python source rather than an XRS file. -e —extra-cpp-code If used together with -c, generates a C++ header file containing class definitions for the windows defined by the XRC file. -u —uncompressed Do not compress XML files (C++ only). -g —gettext Outputs underscore-wrapped strings that poEdit or gettext can scan. Outputs to stdout, or a file if -o is used. -n —function <name> Specifies a C++ initialization function name (use with -c). -o <filename> —output <filename> Specifies the output file, such as resource.xrs or resource.cpp. -l <filename> —list-of-handlers Outputs a list of resource handlers <filename> that are needed for the specified resources.

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 261 Using wxWidgets Resource Files 261 Translating Resources If the wxXmlResource object has been created with the wxXRC_USE_LOCALE flag (the default behavior), all displayable strings will be subject to translation, as detailed in Chapter 16, “Writing International Applications.” However, poEdit cannot scan XRC files for strings to translate as it can for C++ code, so you can create a file of such strings using wxrc with the -g option. For example: wxrc -g resources.xrc -o resource_strings.cpp Then you can run poEdit to scan the strings in this and other files. The XRC Format There isn’t space to describe the XRC format in detail, but here is an example showing a simple dialog with sizers: <?xml version=”1.0”?> <resource version=”2.3.0.1”> <object class=”wxDialog” name=”simpledlg”> <title>A simple dialog</title> <object class=”wxBoxSizer”> <orient>wxVERTICAL</orient> <object class=”sizeritem”> <object class=”wxTextCtrl”> <size>200,200d</size> <style>wxTE_MULTILINE|wxSUNKEN_BORDER</style> <value>Hello, this is an ordinary multiline\n textctrl....</value> </object> <option>1</option> <flag>wxEXPAND|wxALL</flag> <border>10</border> </object> <object class=”sizeritem”> <object class=”wxBoxSizer”> <object class=”sizeritem”> <object class=”wxButton” name=”wxID_OK”> <label>Ok</label> <default>1</default> </object> </object> <object class=”sizeritem”> <object class=”wxButton” name=”wxID_CANCEL”> <label>Cancel</label> </object> <border>10</border> <flag>wxLEFT</flag> </object> </object> <flag>wxLEFT|wxRIGHT|wxBOTTOM|wxALIGN_RIGHT</flag>

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 262 262 Writing Custom Dialogs Chapter 9 <border>10</border> </object> </object> </object> </resource> A detailed specification of the XRC format can be found in the technical note docs/tech/tn0014.txt in your wxWidgets distribution. If you use an editor to create your user interfaces, you won’t need to know about XRC’s format. You may be wondering how a text XRC file can be used to specify binary bitmaps and icons. These resources may be specified as URLs, and wxWidgets’ virtual file system will extract them from sources such as a zip file. For example: <object class=”wxBitmapButton” name=”wxID_OK”> <bitmap>resources.bin#zip:okimage.png</bitmap> </object> See Chapter 10, “Programming with Images,” and Chapter 14, “Files and Streams,” for more information on using virtual file systems to load resources such as images. Writing Resource Handlers The XRC system uses a resource handler to recognize the XML specification of each type of resource. If you write your own custom control, you may want to write a resource handler so that applications can use the custom control with XRC. As an illustration, the declaration for wxButton’s handler looks like this: #include “wx/xrc/xmlres.h” class wxButtonXmlHandler : public wxXmlResourceHandler { DECLARE_DYNAMIC_CLASS(wxButtonXmlHandler) public: wxButtonXmlHandler(); virtual wxObject *DoCreateResource(); virtual bool CanHandle(wxXmlNode *node); }; The handler implementation is quite simple. In the handler’s constructor, the XRC_ADD_STYLE macro is used to make the handler aware of specific button styles, and AddWindowStyles is called to add common window styles. In DoCreateResource, the button object is created in two steps, using XRC_MAKE_INSTANCE and then Create, extracting parameters such as the label,

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 263 Using wxWidgets Resource Files 263 position, and size. Finally, CanHandle tests whether this handler can handle the node in question. It’s permissible for a single handler class to handle more than one kind of resource. IMPLEMENT_DYNAMIC_CLASS(wxButtonXmlHandler, wxXmlResourceHandler) wxButtonXmlHandler::wxButtonXmlHandler() : wxXmlResourceHandler() { XRC_ADD_STYLE(wxBU_LEFT); XRC_ADD_STYLE(wxBU_RIGHT); XRC_ADD_STYLE(wxBU_TOP); XRC_ADD_STYLE(wxBU_BOTTOM); XRC_ADD_STYLE(wxBU_EXACTFIT); AddWindowStyles(); } wxObject *wxButtonXmlHandler::DoCreateResource() { XRC_MAKE_INSTANCE(button, wxButton) button->Create(m_parentAsWindow, GetID(), GetText(wxT(“label”)), GetPosition(), GetSize(), GetStyle(), wxDefaultValidator, GetName()); if (GetBool(wxT(“default”), 0)) button->SetDefault(); SetupWindow(button); return button; } bool wxButtonXmlHandler::CanHandle(wxXmlNode *node) { return IsOfClass(node, wxT(“wxButton”)); } To use a handler, an application needs to include the header and register the handler, as follows: #include “wx/xrc/xh_bttn.h” wxXmlResource::AddHandler(new wxBitmapXmlHandler); Foreign Controls An XRC file can specify a foreign, or “unknown” control, by specifying class=”unknown” in the object definition. This can stand in for a control that is actually created in the C++ code, after the parent is loaded from XRC. When

Smart_Ch09f.qxd 6/10/05 11:20 AM Page 264 264 Writing Custom Dialogs Chapter 9 XRC loads the unknown object, a placeholder window is created. Then the application calls AttachUnknownControl to superimpose the real window onto the placeholder window, with the correct position and size. For example: wxDialog dlg; // Load the dialog wxXmlResource::Get()->LoadDialog(&dlg, this, wxT(“mydialog”)); // Make an instance of our new custom class. MyCtrl* myCtrl = new MyCtrl(&dlg, wxID_ANY); // Attach it to the dialog wxXmlResource::Get()->AttachUnknownControl(wxT(“custctrl”), myCtrl); // Show the dialog dlg.ShowModal(); The custom control definition can look like this: <object class=”unknown” name=”custctrl”> <size>100,100</size> </object> Using this technique, you can lay out interfaces in tools that don’t know about your custom controls, and you also avoid the need to write a resource handler. SUMMARY In this chapter, you have learned the fundamentals of custom dialog design and implementation, including a quick look at sizers, the use of validators, and the advantages of using UI update events. For examples of creating custom dialogs, see samples/dialogs in your wxWidgets distribution. Also see samples/validate for use of the generic and text validator classes. Next, we’ll look at how to handle images.

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 265 C H A P T E R 10 Programming with Images This chapter shows what you can do with bitmapped images. Images are great for introducing “design values” into your application, and they can be used with controls such as toolbars, tree controls, notebooks, buttons, HTML win- dows, or in custom drawing code. Sometimes they can be used invisibly in an application, for example to achieve flicker-free drawing. In this chapter, we cover the different image classes and how to override standard icons and bitmaps used with wxWidgets. IMAGE CLASSES IN WXWIDGETS wxWidgets supports four kinds of bitmap images: wxBitmap, wxIcon, wxCursor, and wxImage. wxBitmap represents a platform-dependent bitmap, with an optional wxMask to support drawing with transparency. On Windows, wxBitmap is imple- mented using device-independent bitmaps (DIBs). On GTK+ and X11, each wxBitmap contains the pixmap object of GDK and X11, respectively. On Mac, a PICT is used. A wxBitmap can be converted to and from a wxImage. wxIcon represents the platform’s concept of an icon, a small image with transparency that can be used for giving frames and dialogs a recognizable visual cue, among other things. On GTK+, X11, and Mac, an icon is simply a bitmap that always has a wxMask. On Windows, an icon is represented by an HICON object. wxCursor represents the mouse pointer image; this is a GdkCursor on GTK+, a Cursor on X11, an HCURSOR in Windows, and a Cursor on Mac. It has the notion of a hotspot (the pixel in the cursor image that is considered to be the exact mouse pointer location) and a mask. wxImage is the only class of the four with a platform-independent imple- mentation, supporting 24-bit images with an optional alpha channel. A wxImage can be created from data or by using wxBitmap::ConvertToImage. A wxImage can be loaded from a file in a variety of formats, and it is extensible to new formats via 265

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 266 266 Programming with Images Chapter 10 image format handlers. Functions are available to set and get image bits, so it can be used for basic image manipulation. Unlike a wxBitmap, a wxImage cannot be drawn directly to a wxDC. Instead, a wxBitmap object must be created from the wxImage. This bitmap can then be drawn in a device context by using wxDC:: DrawBitmap. wxImage supports a mask color indicating transparent areas, and it also supports alpha channel data to allow for more sophisticated transparency effects. You can convert between these bitmap objects, though there are platform dependencies on some conversion operations. Note that all image classes are reference-counted, so assignment and copying are very cheap operations because the image data itself is not copied. However, you need to be aware that if you change an image, other image objects that refer to the same image data will also be changed. All image classes use standard wxBitmapType identifiers for loading and saving bitmap data, as described in Table 10-1. Table 10-1 Bitmap Types wxBITMAP_TYPE_BMP A Windows bitmap file (BMP). wxBITMAP_TYPE_BMP_RESOURCE A Windows bitmap to be loaded from the resource part of the executable. wxBITMAP_TYPE_ICO A Windows icon file (ICO). wxBITMAP_TYPE_ICO_RESOURCE A Windows icon to be loaded from the resource part of the executable. wxBITMAP_TYPE_CUR A Windows cursor (CUR). wxBITMAP_TYPE_CUR_RESOURCE A Windows cursor to be loaded from the resource part of the executable. wxBITMAP_TYPE_XBM An XBM monochrome bitmap file, used on Unix. wxBITMAP_TYPE_XBM_DATA An XBM monochrome bitmap, to be constructed from C++ data. wxBITMAP_TYPE_XPM An XPM color bitmap file, a good cross-platform format for small images that can be compiled into the application. wxBITMAP_TYPE_XPM_DATA An XPM color bitmap, to be constructed from C++ data. wxBITMAP_TYPE_TIF A TIFF bitmap file, popular for large images. wxBITMAP_TYPE_GIF A GIF bitmap file, with a maximum 256 colors and optional transparency information. wxBITMAP_TYPE_PNG A PNG bitmap file, a popular file format with optional transparency and alpha channel, and free of patent problems. wxBITMAP_TYPE_JPEG A JPEG bitmap file, a popular compressed format for large images, but it uses lossy compression, so it’s not suitable for multiple saving/loading cycles. wxBITMAP_TYPE_PCX PCX bitmap file. wxBITMAP_TYPE_PICT Mac PICT bitmap file.

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 267 Programming with wxBitmap 267 wxBITMAP_TYPE_PICT_RESOURCE Mac PICT bitmap file to be loaded from the resource part of the executable. wxBITMAP_TYPE_ICON_RESOURCE On Mac OS X only, loads a standard icon (such as wxICON_INFORMATION) or an icon resource. wxBITMAP_TYPE_ANI Windows animated icon file (ANI). wxBITMAP_TYPE_IFF IFF bitmap file. wxBITMAP_TYPE_MACCURSOR Mac cursor file. wxBITMAP_TYPE_MACCURSOR_ Mac cursor, to be loaded from the resource part of RESOURCE the executable. wxBITMAP_TYPE_ANY Tells the image loading code to figure out the type itself. PROGRAMMING WITH WXBITMAP These are some of the things you can do with a wxBitmap:

Draw it on a window via a device context.

Use it as a bitmap label for classes such as wxBitmapButton, wxStaticBitmap, and wxToolBar.

Use it to implement double buffering (drawing into an off-screen wxMemoryDC before drawing to a window). On some platforms (in particular, Windows), the bitmap is a limited resource, so if you have many images to store in memory, you may prefer to work mainly with wxImage objects and convert to a temporary wxBitmap when drawing on a device context. Before discussing how to create wxBitmap and draw with it, let’s summa- rize the main functions (Table 10-2). Table 10-2 wxBitmap Functions wxBitmap A bitmap can be created given a width and height, another bitmap, a wxImage, XPM data (char**), raw data (char[]), or a file name and type. ConvertToImage Converts to a wxImage, preserving transparency. CopyFromIcon Creates the bitmap from a wxIcon. Create Creates the bitmap from data or a given size. GetWidth, GetHeight Returns the bitmap’s size. GetDepth Returns the bitmap’s color depth. GetMask, SetMask Returns the wxMask object or NULL. GetSubBitmap Returns an area of the bitmap as a new bitmap. LoadFile, SaveFile Files can be loaded and (for some formats) saved. Ok Returns true if the bitmap’s data is present.

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 268 268 Programming with Images Chapter 10 Creating a wxBitmap There are several ways to create a wxBitmap object. You can create the object in an uninitialized state (no bitmap data) by using the default constructor. You will need to call Create or LoadFile or assign another bitmap to it to do anything useful with the object. You can create a wxBitmap with a given size and depth. The bitmap will be filled with random data, so for this object to be useful, you will need to draw on it. The following code creates a 200 × 100 pixel bitmap and gives it a white background. // Create a 200x100 bitmap with the current display depth wxBitmap bitmap(200, 100, -1); // Create a memory device context wxMemoryDC dc; // Select the bitmap into the DC dc.SelectObject(bitmap); // Set the background dc.SetBackground(*wxWHITE_BRUSH); // Color the bitmap white dc.Clear(); // Select the bitmap out of the DC dc.SelectObject(wxNullBitmap); You can create a bitmap from an image object, preserving any mask or alpha channel in the original image: // Load an image wxImage image(wxT(“image.png”), wxBITMAP_TYPE_PNG); // Convert it to a bitmap wxBitmap bitmap(image); A bitmap can also be constructed from an icon by using CopyFromIcon: // Load an icon wxIcon icon(wxT(“image.xpm”), wxBITMAP_TYPE_XPM); // Convert it to a bitmap wxBitmap bitmap; bitmap.CopyFromIcon(icon);

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 269 Programming with wxBitmap 269 Or you can load a bitmap from a file: // Load from a file wxBitmap bitmap(wxT(“picture.png”, wxBITMAP_TYPE_PNG); if (!bitmap.Ok()) { wxMessageBox(wxT(“Sorry, could not load file.”)); } wxBitmap can load all the file types that wxImage can (see Table 10-7), by using either wxImage or a more efficient platform-specific implementation for certain file types. Some of the most popular formats are PNG, JPEG, TIFF, BMP, and XPM, which are available on all platforms for both loading and saving, assum- ing that wxWidgets support for these formats has been enabled. On Mac OS X, a PICT resource can also be loaded by specifying wxBITMAP_TYPE_PICT_RESOURCE. If you want to load a bitmap from a platform-dependent source, you can use the wxBITMAP macro. For example: #if !defined(__WXMSW__) && !defined(__WXPM__) #include “picture.xpm” #endif wxBitmap bitmap(wxBITMAP(picture)); This will load the resource named picture from the executable on Windows and OS/2, and on all other platforms, it will load an XPM from the picture_xpm variable. However, the XPM format is supported on all platforms, so use of this macro is not usually necessary. Setting a wxMask Each wxBitmap object can contain a wxMask, a monochrome bitmap that indi- cates the transparent areas of the main bitmap. This will be created automat- ically when you load a transparent image, for example using XPM, PNG, or GIF, but you can also create it programmatically and assign it to a bitmap with SetMask. You can create a wxMask object from a wxBitmap, or a wxBitmap plus a color to indicate the transparent area. The following example creates a monochrome transparent image called mainBitmap, 32 pixels wide by 32 pixels high, from bitmap data (imageBits) and a mask (maskBits) where 1 is black and 0 is white for the bits, and 1 is opaque and 0 is transparent for the mask. static char imageBits[] = { 255, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255, 25, 243,

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 270 270 Programming with Images Chapter 10 255, 255, 19, 249, 255, 255, 7, 252, 255, 255, 15, 254, 255, 255, 31, 255, 255, 255, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; static char maskBits[] = { 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 255, 31, 0, 0, 255, 31, 0, 0, 254, 15, 0, 0, 252, 7, 0, 0, 248, 3, 0, 0, 240, 1, 0, 0, 224, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; wxBitmap mainBitmap(imageBits, 32, 32); wxBitmap maskBitmap(maskBits, 32, 32); mainBitmap.SetMask(new wxMask(maskBitmap)); The XPM Format Where small bitmaps with transparency are needed, for example as toolbar buttons or bitmaps in notebooks and tree controls, wxWidgets programmers often use XPM. One advantage of this format is that it uses C/C++ syntax, and it can either be loaded dynamically or compiled into your program. Here’s an example. // You can also use #include “open.xpm” static char *open_xpm[] = { /* columns rows colors chars-per-pixel */ “16 15 5 1”, “ c None”, “. c Black”, “X c Yellow”, “o c Gray100”, “O c #bfbf00”, /* pixels */ “ “, “ ... “, “ . . .”, “ ..”, “ ... ...”, “ .XoX....... “, “ .oXoXoXoXo. “, “ .XoXoXoXoX. “, “ .oXoX..........”,

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 271 Programming with wxBitmap 271 “ .XoX.OOOOOOOOO.”, “ .oo.OOOOOOOOO. “, “ .X.OOOOOOOOO. “, “ ..OOOOOOOOO. “, “ ........... “, “ “ }; wxBitmap bitmap(open_xpm); As you can see, XPMs are encoded using character data. Before the image data, there is a palette section that maps each character to its color, either as an identifier or as a hash-prefixed six-digit hexadecimal string. Using the identifier None causes this character to represent the transparent area in the bitmap. Although XPM support is uncommon among Windows image manipu- lation programs, you can create images as PNGs and convert to XPM using a tool such as ImageBlocks (bundled with DialogBlocks), or you can simply write your own converter using wxWidgets. Drawing with Bitmaps You can draw with a bitmap in a couple different ways. You can associate it with a memory device context (wxMemoryDC) and then use wxDC::Blit to transfer the contents of the bitmap to another device context. Or, you can use the sim- pler wxDC::DrawBitmap. In either case, if the bitmap is transparent or has an alpha channel, you can specify transparent drawing by passing true to the function. The two methods are illustrated in the following. // Draw a bitmap using a wxMemoryDC wxMemoryDC memDC; memDC.SelectObject(bitmap); // Draw the bitmap at 100, 100 on the destination DC destDC.Blit(100, 100, // Draw at (100, 100) bitmap.GetWidth(), bitmap.GetHeight(), // Draw full bitmap & memDC, // Draw from memDC 0, 0, // Draw from bitmap origin wxCOPY, // Logical operation true); // Take mask into account memDC.SelectObject(wxNullBitmap); // Alternative method: use DrawBitmap destDC.DrawBitmap(bitmap, 100, 100, true); Chapter 5, “Drawing and Printing,” discusses drawing with bitmaps in more detail.

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 272 272 Programming with Images Chapter 10 Packaging Bitmap Resources If you come from a Windows programming background, you are accustomed to loading bitmaps from the resource section of the executable. You can still do this by passing a resource name and the wxBITMAP_TYPE_BMP_RESOURCE type to the constructor, but you are likely to want a less platform-specific method. A portable way to package resources, whether they are bitmaps, HTML files, or other files required by an application, is to store them in a single zip file alongside the executable or in a separate data folder. Then you can use the virtual file system functionality in wxWidgets to load the image directly from the zip file, as the following fragment shows. // Create a new file system object wxFileSystem*fileSystem = new wxFileSystem; wxString archiveURL(wxT(“myapp.bin”)); wxString filename(wxT(“myimage.png”)); wxBitmapType bitmapType = wxBITMAP_TYPE_PNG; // Create a URL wxString combinedURL(archiveURL + wxString(wxT(“#zip:”)) + filename); wxImage image; wxBitmap bitmap; // Open the file in the archive wxFSFile* file = fileSystem->OpenFile(combinedURL); if (file) { wxInputStream* stream = file->GetStream(); // Load and convert to a bitmap if (image.LoadFile(* stream, bitmapType)) bitmap = wxBitmap(image); delete file; } delete fileSystem; if (bitmap.Ok()) { ... } For more information about virtual file systems, please see Chapter 14, “Files and Streams.” PROGRAMMING WITH WXICON A wxIcon is a small bitmap that always has a mask. Its uses include

Setting the icon for a frame or dialog

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 273 Programming with wxIcon 273

Adding icons to a wxTreeCtrl, wxListCtrl, or wxNotebook via the wxImageList class (see more information later in this chapter)

Drawing an icon on a device context with wxDC::DrawIcon Table 10-3 summarizes the major icon functions. Table 10-3 wxIcon Functions wxIcon An icon can be created given another icon, XPM data (char**), raw data (char[]), or a file name and type. CopyFromBitmap Creates the icon from a wxBitmap. GetWidth, GetHeight Returns the icon’s size. GetDepth Returns the icon’s depth. LoadFile Files can be loaded. Ok Returns true if the icon’s data is present. Creating a wxIcon A wxIcon object can be created from XPM data included in the application, from a wxBitmap object, from raw data, or by loading the icon from a file, such as a transparent XPM file. wxWidgets provides the wxICON macro, which is similar to the wxBITMAP macro described earlier; the icon is loaded either from a platform-specific resource or from XPM data. On Windows, LoadFile and the equivalent constructor will work for Windows bitmap (BMP) and icon (ICO) resources and files. If you want to load other formats, load the file into a wxBitmap and convert it to an icon. On Mac OS X and Unix/Linux with GTK+, wxIcon has the same file load- ing capabilities as wxBitmap. The following code fragment shows four different ways to create a wxIcon object. // Method 1: load from XPM data #include “icon1.xpm” wxIcon icon1(icon1_xpm); // Method 2: load from an ICO resource (Window and OS/2 only) wxIcon icon2(wxT(“icon2”)); // Method 3: load from an ICO file (Windows and OS/2 only) // You can specify the desired width since an icon may // contain multiple images. wxIcon icon3(wxT(“icon3.ico”), wxBITMAP_TYPE_ICO, 16, 16); // Method 4: create from a bitmap wxIcon icon4; wxBitmap bitmap(wxT(“icon4.png”), wxBITMAP_TYPE_PNG); icon4.CopyFromBitmap(bitmap);

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 274 274 Programming with Images Chapter 10 Using wxIcon The following code shows three different ways to use wxIcon: setting a frame icon, adding an icon to an image list, and drawing an icon on a device context. #include “myicon.xpm” wxIcon icon(myicon_xpm); // 1: Set a frame icon frame->SetIcon(icon); // 2: Add an icon to a wxImageList wxImageList* imageList = new wxImageList(16, 16); imageList->Add(icon); // 3: Draw the icon at (10, 10) wxClientDC dc(window); dc.DrawIcon(icon, 10, 10); Associating an Icon with an Application Associating an icon with an application (so the operating system can present a clickable graphic to the user) cannot be done from within the wxWidgets toolkit, and this is one of the few areas where you need to use a different tech- nique for each platform. On Windows, you need to add a resource script (extension .rc) to your makefile or project file and add an ICON statement to the .rc file— for example: aardvarkpro ICON aardvarkpro.ico #include “wx/msw/wx.rc” Here, aardvarkpro.ico is the name of a Windows icon file with multiple resolu- tions and depths (typically at 48×48, 32×32 and 16×16 resolutions). When showing the icon in Windows Explorer, Windows looks alphabetically for the first resource, so you may want to prepend a few a’s to your chosen resource, just to make sure it’ll be found first; otherwise, you’ll see an unexpected icon on the desktop or in a folder. On the Mac, you need to prepare an application bundle containing some ICNS files. See the installation section in Chapter 20, “Perfecting Your Application,” for more on bundles; the relevant sections of a bundle’s Info.plist file might look like this: <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeExtensions</key> <array>

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 275 Programming with wxCursor 275 <string>pjd</string> </array> <key>CFBundleTypeIconFile</key> <string>dialogblocks-doc.icns</string> <key>CFBundleTypeName</key> <string>pjdfile</string> <key>CFBundleTypeRole</key> <string>Editor</string> </dict> </array> <key>CFBundleIconFile</key> <string>dialogblocks-app.icns</string> ... The icons for the application and its document types are specified with the CFBundleIconFile and CFBundleTypeIconFile properties. You can create ICNS files with the icon editor that Apple provides. If you predominantly work on another platform, you might want to create a number of different icons in 16×16, 32×32, 48×48, and 128×128 resolutions, save them as PNGs, copy them to the Mac, and then open each file and copy and paste its contents into the appropriate icon editor location. Make sure that each PNG file has a mask that the editor can use to construct the icon. On Linux, the GNOME and KDE environments have their own methods for providing an icon to use with the application, described briefly in Chapter 20. PROGRAMMING WITH WXCURSOR A cursor is used to give feedback on the mouse pointer position. You can change the cursor for a given window—using different cursors gives a cue to the user to expect specific mouse behavior. Like icons, cursors are small, trans- parent images that can be created using platform-specific as well as generic constructors. Some of these constructors take a hotspot position relative to the top-left corner of the cursor image, with which you specify the location of the actual pointer “tip.” Table 10-4 shows the cursor functions. Table 10-4 wxCursor Functions wxCursor A cursor can be created from a wxImage, raw data (char[]), a stock cursor identifier, or a file name and type. Ok Returns true if the cursor’s data is present.

Smart_Ch10f.qxd 6/10/05 2:55 PM Page 276 276 Programming with Images Chapter 10 Creating a wxCursor The easiest way to create a cursor is to pass a stock cursor identifier to the cur- sor constructor, as the following example shows. // Create a cursor from a stock identifier wxCursor cursor(wxCURSOR_WAIT); Table 10-5 lists the available identifiers and their appearances (subject to some variation between platforms). You can also use the predefined cursor pointers wxSTANDARD_CURSOR, wxHOURGLASS_CURSOR, and wxCROSS_CURSOR. Table 10-5 Stock Cursor Identifiers wxCURSOR_ARROW Standard arrow cursor. wxCURSOR_RIGHT_ARROW Standard arrow cursor pointing to the right. wxCURSOR_BLANK Transparent cursor. wxCURSOR_BULLSEYE Bullseye cursor. wxCURSOR_CROSS Cross cursor. wxCURSOR_HAND Hand cursor. wxCURSOR_IBEAM I-beam cursor (vertical line). wxCURSOR_LEFT_BUTTON Represents a mouse with the left button depressed (GTK+ only). wxCURSOR_MAGNIFIER Magnifier cursor. wxCURSOR_MIDDLE_BUTTON Represents a mouse with the middle button depressed (GTK+ only). wxCURSOR_NO_ENTRY No-entry sign cursor. wxCURSOR_PAINT_BRUSH Paintbrush cursor. wxCURSOR_PENCIL Pencil cursor. wxCURSOR_POINT_LEFT A cursor that points left.

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 277 Programming with wxCursor 277 wxCURSOR_POINT_RIGHT A cursor that points right. wxCURSOR_QUESTION_ARROW An arrow and question mark. wxCURSOR_RIGHT_BUTTON Represents a mouse with the right button depressed (GTK+ only). wxCURSOR_SIZENESW Sizing cursor pointing NE-SW. wxCURSOR_SIZENS Sizing cursor pointing N-S. wxCURSOR_SIZENWSE Sizing cursor pointing NW-SE. wxCURSOR_SIZEWE Sizing cursor pointing W-E. wxCURSOR_SIZING General sizing cursor. wxCURSOR_SPRAYCAN Spraycan cursor. wxCURSOR_WAIT Wait cursor. wxCURSOR_WATCH Watch cursor. wxCURSOR_ARROWWAIT Cursor with both an arrow and an hourglass. wxCursor can load a Windows cursor resource on Windows or a Mac OS X cur- sor resource on Mac OS X: // Cursor from a Windows resource wxCursor cursor(wxT(“cursor_resource”), wxBITMAP_TYPE_CUR_RESOURCE, hotSpotX, hotSpotY); // Cursor from a Mac OS cursor resource wxCursor cursor(wxT(“cursor_resource”), wxBITMAP_TYPE_MACCUR_RESOURCE); You can create a custom cursor by specifying a wxImage object. The “hotspot” position needs to be specified using wxImage::SetOptionInt because the actu- al mouse pointer position may not correspond to the top-left corner of the cursor image. For example, a cross-hair cursor would have the hotspot in

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 278 278 Programming with Images Chapter 10 the center of the image. Here’s some code that loads a PNG image and makes a cursor out of it: // Create a cursor from a wxImage wxImage image(wxT(“cursor.png”), wxBITMAP_TYPE_PNG); image.SetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X, 5); image.SetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 5); wxCursor cursor(image); Using wxCursor Each window can have an associated cursor, which will be shown when the mouse pointer moves into the window. If there is no associated cursor, the cur- sor for an ancestor window will be shown, and if there is no ancestor with a cursor set, the standard cursor will be shown. Set the cursor for a window like this: window->SetCursor(wxCursor(wxCURSOR_WAIT)); Using wxSetCursorEvent On Windows and Mac OS X, there is a little wrinkle that you may need to be aware of, particularly if you implement your own container windows. Say you implement your own splitter window that arranges its children such that only a small part of the splitter window is visible; this is used as the draggable divider or “sash.” You then set an appropriate cursor for the splitter (say, wxCURSOR_WE) so the sash indicates that it can be dragged. If the children of the splitter window don’t have cursors specified, they may inappropriately show the parent’s cursor that is intended only for the sash. To indicate that the cursor should only be used when the mouse pointer is over the sash and that no cursor should be set otherwise, you need to define an event handler for wxSetCursorEvent. This event is generated on Windows and Mac OS X when the cursor should be set (normally as the mouse pointer moves over the window). Your event handler should call wxSetCursorEvent::SetCursor if you want to indicate a particular cursor for this window, as follows: BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow) EVT_SET_CURSOR(wxSplitterWindow::OnSetCursor) END_EVENT_TABLE() // Indicate that the cursor should only be set for the sash void wxSplitterWindow::OnSetCursor(wxSetCursorEvent& event) { // If we don’t do this, the resizing cursor might be set for

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 279 Programming with wxImage 279 // child windows. Here, we explicitly say that our cursor // should not be used for child windows that overlap us. if ( SashHitTest(event.GetX(), event.GetY(), 0) ) { // Use default processing event.Skip(); } //else: Do nothing, in particular, don’t call Skip() } In this example, if the mouse pointer is over the sash, SashHitTest returns true, and Skip is called, which makes the event handler “fail.” This is equivalent to defining no cursor event handler at all, and it causes wxWidgets to display the window’s cursor (wxCURSOR_WE) as normal. If SashHitTest returns false, howev- er, it means the cursor is over a child window, and the cursor for the splitter window should not be used. Allowing the handler to “succeed” (by not calling Skip) without setting a cursor indicates to wxWidgets that we should treat the splitter window as having no cursor. As a result, the child window will cor- rectly not inherit the parent’s cursor, even if it has no known cursor of its own. (If the child window is a wxTextCtrl, for example, the native implementation will provide its own cursor, but wxWidgets has no knowledge of it.) PROGRAMMING WITH WXIMAGE Use wxImage when you need to manipulate images in a platform-independent manner, or as an intermediate step for loading or saving image files. Images are stored using a byte per pixel for each of the red, green, and blue channels, plus a further byte per pixel if an alpha channel is present. The major wxImage functions are listed in Table 10-6. Table 10-6 wxImage Functions wxImage An image can be created given a width and height, another image, XPM data, raw data (char[]) and optional alpha data, a file name and type, or an input stream. ConvertAlphaToMask Converts the alpha channel (if any) to a mask. ConvertToMono Converts to a new monochrome image. Copy Returns an identical copy without using reference counting. Create Creates an image of a given size, optionally initializing it from data. Destroy Destroys the internal data if no other object is using it. GetData, SetData Gets and sets its pointer to internal data (unsigned char*). (continues)

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 280 280 Programming with Images Chapter 10 Table 10-6 wxImage Functions (Continued) GetImageCount Returns the number of images in a file or stream. GetOption, GetOptionInt, Gets, sets, and tests for the presence of options. SetOption, HasOption GetSubImage Returns an area of the image as a new image. GetWidth, GetHeight Returns the image size. GetRed, GetGreen, GetBlue, Gets and sets the red, blue, green, and alpha value SetRGB, GetAlpha, SetAlpha for a pixel. HasMask, GetMaskRed, Functions for testing for the presence of a mask GetMaskGreen, GetMaskBlue, and setting and getting the mask color. SetMaskColour LoadFile, SaveFile Files can be loaded and saved using various formats. Mirror Mirrors the image in either orientation, returning a new image. Ok Returns true if the image is initialized. Paste Pastes an image into this image at a given position. Rotate, Rotate90 Rotates the image, returning a new image. SetMaskFromImage Sets a mask, specifying an image and color to use for the transparent area. Scale, Rescale Scales to a new image or scales in place. Loading and Saving Images wxImage can load and save in a variety of formats, using image handlers that plug into wxImage and provide extensibility. The wxImage file-handling capabili- ties are used as a fallback for the other bitmap classes for when they don’t pro- vide appropriate native implementations. Table 10-7 shows the image handlers available on all platforms support- ed by wxWidgets. wxBMPHandler is always installed by default. To use other image formats, install the appropriate handler with wxImage::AddHandler or wxInitAllImageHandlers. Table 10-7 Available Image Handlers wxBMPHandler For loading and saving Windows bitmap files. wxPNGHandler For loading and saving PNG files. Images with transparency or an alpha channel are supported. wxJPEGHandler For loading and saving JPEG files. wxGIFHandler GIF files: only for loading, due to legal issues. wxPCXHandler For loading and saving PCX files. wxPCXHandler will count the number of different colors in the image; if there are 256 or fewer colors, it will save as 8-bit; otherwise it will save as 24-bit.

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 281 Programming with wxImage 281 wxPNMHandler For loading and saving PNM files. Loading PNMs only works for ASCII or raw RGB images. When saving in PNM format, wxPNMHandler will always save as raw RGB. wxTIFFHandler For loading and saving TIFF files. wxIFFHandler For loading IFF files. wxXPMHandler For loading and saving XPM files. wxICOHandler For loading and saving Windows icon files. wxCURHandler For loading and saving Windows cursor files. wxANIHandler For loading Windows animated cursor files. If you will be using a specific set of formats, you might typically have these in your wxApp::OnInit function: #include “wx/image.h” wxImage::AddHandler( new wxPNGHandler ); wxImage::AddHandler( new wxJPEGHandler ); wxImage::AddHandler( new wxGIFHandler ); wxImage::AddHandler( new wxXPMHandler ); Alternatively, you can just call: wxInitAllImageHandlers(); Here are some different ways of loading and saving images from files and streams. Note that when loading files, you should normally use absolute paths instead of depending on the setting of the current directory. // Load image using constructor and specific type wxImage image(wxT(“image.png”), wxBITMAP_TYPE_PNG); if (image.Ok()) { ... } // Leave wxImage to work out the image type wxImage image(wxT(“image.png”)); // Two-step loading wxImage image; if (image.LoadFile(wxT(“image.png”))) { ... } // Two-step loading with an index into a multi-image file: // load image number 2 if available wxImage image; int imageCount = wxImage::GetImageCount(wxT(“image.tif”)); if (imageCount > 2) image.LoadFile(wxT(“image.tif”), wxBITMAP_TYPE_TIFF, 2);

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 282 282 Programming with Images Chapter 10 // Load from a stream wxFileInputStream stream(wxT(“image.tif”)); wxImage image; image.LoadFile(stream, wxBITMAP_TYPE_TIF); // Save to a file image.SaveFile(wxT(“image.png”)), wxBITMAP_TYPE_PNG); // Save to a stream wxFileOutputStream stream(wxT(“image.tif”)); image.SaveFile(stream, wxBITMAP_TYPE_TIF); Images will be saved as 24-bit files, with the exception of XPM and PCX for- mats, whose handlers will count the number of colors and save with the appro- priate depth. JPEG has a quality setting that you can set before saving the file. The setting is a number between 0 and 100, where 0 is poor quality and high compression, and 100 is high quality and poor compression. // Save with reasonable quality and compression image.SetOption(wxIMAGE_OPTION_QUALITY, 80); image.SaveFile(wxT(“picture.jpg”), wxBITMAP_TYPE_JPEG); You also might want to use wxImage::SetOption when saving an XPM to a stream—because no file name is passed, the handler won’t know what name to use for the C variable that is part of the XPM data. For example: // Save XPM to a stream image.SetOption(wxIMAGE_OPTION_FILENAME, wxT(“myimage”)); image.SaveFile(stream, wxBITMAP_TYPE_XPM); Note that it will append _xpm to the file name that you specify. Transparency There are two ways of using transparency with wxImage: masks and alpha channels. One color value of the image may be used as a mask color, which will lead to the automatic creation of a wxMask object when converted to a wxBitmap. wxImage also supports alpha channel data. In addition to a byte for the red, green, and blue color components for each pixel, it also stores a byte rep- resenting the pixel opacity. The alpha value of 0 corresponds to a transparent pixel (zero opacity), and the value of 255 means that the pixel is 100% opaque. Not all images have an alpha channel, and before using GetAlpha, you should determine whether this image contains alpha values with HasAlpha. Currently, only images loaded from PNG files or assigned an alpha channel with SetAlpha can have an alpha channel. Saving images with an alpha chan-

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 283 Programming with wxImage 283 nel is not yet supported. Drawing with the alpha channel is supported by con- verting the image to a wxBitmap and calling wxDC::DrawBitmap or wxDC::Blit. The following code shows how to create a wxImage with a mask. The image will be blue, containing a transparent rectangle. // Create masked image // First, draw on a wxBitmap wxBitmap bitmap(400, 400); wxMemoryDC dc; dc.SelectObject(bitmap); dc.SetBackground(*wxBLUE_BRUSH); dc.Clear(); dc.SetPen(*wxRED_PEN); dc.SetBrush(*wxRED_BRUSH); dc.DrawRectangle(50, 50, 200, 200); dc.SelectObject(wxNullBitmap); // Convert the bitmap to an image wxImage image = bitmap.ConvertToImage(); // Set the mask color to red image.SetMaskColour(255, 0, 0); Another method is to create the mask from another image. In the following example, image.bmp contains the main image, and mask.bmp has black pixels where the transparent area should be. // Load image and its mask wxImage image(wxT(“image.bmp”), wxBITMAP_TYPE_BMP); wxImage maskImage(wxT(“mask.bmp”), wxBITMAP_TYPE_BMP); // Specify black for the transparent area image.SetMaskFromImage(maskImage, 0, 0, 0); If you have loaded a transparent image from disk, you can check for trans- parency and retrieve the mask color: // Load transparent image wxImage image(wxT(“image.png”), wxBITMAP_TYPE_PNG); // Retrieve the mask if (image.HasMask()) { wxColour maskColour(image.GetMaskRed(), image.GetMaskGreen(), image.GetMaskBlue()); }

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 284 284 Programming with Images Chapter 10 Transformations wxImagesupports scaling, rotating, and mirroring transformations. Here are some examples: // Scale an image to 200x200 and assign to another image. // image1 remains unmodified. wxImage image2 = image1.Scale(200, 200); // Rescale an image to 200x200 image1.Rescale(200, 200); // Rotate by a specified number of radians. // image1 remains unmodified. wxImage image2 = image1.Rotate(0.5); // Rotate by 90 degrees in the clockwise direction. // image1 remains unmodified. wxImage image2 = image1.Rotate90(true); // Mirror the image horizontally. // image1 remains unmodified. wxImage image2 = image1.Mirror(true); Color Reduction If you need to reduce the number of colors in an image, you can use the static functions of the wxQuantize class. The main function of interest, Quantize, takes an input image, an output image, an optional wxPalette** to get a new palette containing the reduced colors, and the desired number of colors. You can also pass an unsigned char** variable to retrieve an 8-bit representation of the out- put image and a style for further control of what is returned; see the reference manual for more on these. The following code shows how to reduce an image to a maximum of 256 colors: #include “wx/image.h” #include “wx/quantize.h” wxImage image(wxT(“image.png”)); int maxColorCount = 256; int colors = image.CountColours(); wxPalette* palette = NULL; if (colors > maxColorCount ) { wxImage reducedImage; if (wxQuantize::Quantize(image, reducedImage, & palette, maxColorCount)) { colors = reducedImage.CountColours(); image = reducedImage; } }

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 285 Image Lists and Icon Bundles 285 An image can have a wxPalette associated with it, for example when the image has been loaded from a GIF file. However, the image is still in RGB format, and the palette merely indicates the original mapping between index values and RGB values. wxPalette can also be set for a wxImage so that SaveFile can save it in a format that has a limited number of colors. For example, the Windows BMP handler determines whether the wxBMP_8BPP_PALETTE image option is set and, if so, uses the image’s palette; if the wxBMP_8BPP option is set, it does its own quantization. Some handlers always do their own color reduction, such as PCX, unless they find that the number of unique colors is already low enough. For more on wxPalette, please see “wxPalette” in Chapter 5. Manipulating wxImage Data Directly You can access image data with GetData for faster manipulation than using GetRed, GetBlue, GetGreen, and SetRGB. Here’s an example of converting a color image to a grayscale: void wxImage::ConvertToGrayScale(wxImage& image) { double red2Gray = 0.297; double green2Gray = 0.589; double blue2Gray = 0.114; int w = image.GetWidth(), h = image.GetHeight(); unsigned char *data = image.GetData(); int x,y; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { long pos = (y * w + x) * 3; char g = (char) (data[pos]*red2Gray + data[pos+1]*green2Gray + data[pos+2]*blue2Gray); data[pos] = data[pos+1] = data[pos+2] = g; } } IMAGE LISTS AND ICON BUNDLES Sometimes it’s useful to aggregate a number of images. You can use wxImageList directly in your application or in conjunction with some of the wxWidgets controls that require image lists when setting icons. wxNotebook, wxTreeCtrl, and wxListCtrl all support wxImageList to identify the icons used in the controls. You can also draw an individual image in a wxImageList on a device context.

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 286 286 Programming with Images Chapter 10 Create a wxImageList with the width and height of each image, a boolean to specify whether a mask will be used, and the initial size of the list (purely for internal optimization purposes). Then add one or more wxBitmap or wxIcon images. You can’t add a wxImage directly, but you can pass one to wxBitmap’s con- structor. wxImageList::Add returns an integer index you can use to identify that image; after you have added an image, the original image can be destroyed because wxImageList makes a copy of it. Here are some examples of creating a wxImageList and adding images to it. // Create a wxImageList wxImageList *imageList = new wxImageList(16, 16, true, 1); // Add a bitmap with transparency from a PNG wxBitmap bitmap1(wxT(“image.png”), wxBITMAP_TYPE_PNG); imageList->Add(bitmap1); // Add a bitmap with transparency from another bitmap wxBitmap bitmap2(wxT(“image.bmp”), wxBITMAP_TYPE_BMP); wxBitmap maskBitmap(wxT(“mask.bmp”), wxBITMAP_TYPE_BMP); imageList->Add(bitmap2, maskBitmap); // Add a bitmap with transparency specified with a color wxBitmap bitmap3(wxT(“image.bmp”), wxBITMAP_TYPE_BMP); imageList->Add(bitmap3, *wxRED); // Add an icon #include “folder.xpm” wxIcon icon(folder_xpm); imageList->Add(icon); You can draw an image to a device context, passing flags that determine how the image will be drawn. Pass wxIMAGELIST_DRAW_TRANSPARENT to draw with transparency, and also one of these values to indicate the state that should be drawn: wxIMAGELIST_DRAW_NORMAL, wxIMAGELIST_DRAW_SELECTED, or wxIMAGELIST_DRAW_FOCUSED. // Draw all the images in the list wxClientDC dc(window); size_t i; for (i = 0; i < imageList->GetImageCount(); i++) { imageList->Draw(i, dc, i*16, 0, wxIMAGELIST_DRAW_NORMAL| wxIMAGELIST_DRAW_TRANSPARENT); } To associate icons with notebook tabs, create an image list containing 16×16 icons, and call wxNotebook::SetImageList or wxNotebook::AssignImageList. If you use the first form, the notebook doesn’t delete the image list when it is destroyed; with the second form, the notebook takes over management of the list, and you don’t have to worry about destroying it yourself. Now when you add pages, you can specify the index of an icon to use as the image next to the text label (or instead of it, if the text label is empty). The following code cre- ates a notebook and adds two pages with icons on the tabs.

Smart_Ch10f.qxd 6/13/05 9:12 AM Page 287 Image Lists and Icon Bundles 287 // Create a wxImageList wxImageList *imageList = new wxImageList(16, 16, true, 1); // Add some icons wxBitmap bitmap1(wxT(“folder.png”), wxBITMAP_TYPE_PNG); wxBitmap bitmap2(wxT(“file.png”), wxBITMAP_TYPE_PNG); int folderIndex = imageList->Add(bitmap1); int fileIndex = imageList->Add(bitmap2); // Create a notebook and two pages wxNotebook* notebook = new wxNotebook(parent, wxID_ANY); wxPanel* page1 = new wxPanel(notebook, wxID_ANY); wxPanel* page2 = new wxPanel(notebook, wxID_ANY); // Assign the image list notebook->AssignImageList(imageList); // Add the pages, with icons notebook->AddPage(page1, wxT(“Folder options”), true, folderIndex); notebook->AddPage(page2, wxT(“File options”), false, fileIndex); wxTreeCtrl and wxListCtrl work in a similar way, with the option to assign or set the image list. If you have a lot of icons and find it hard to keep track of icons by inte- ger index, you might want to write a class that maps string names to the image list index. A simple implementation might look like this: #include “wx/hashmap.h” WX_DECLARE_STRING_HASH_MAP(int, IconNameToIndexHashMap); // Class to refer to image indices by name class IconNameToIndex { public: IconNameToIndex() {} // Add a named bitmap to the image list void Add(wxImageList* list, const wxBitmap& bitmap, const wxString& name) { m_hashMap[name] = list->Add(bitmap); } // Add a named icon to the image list void Add(wxImageList* list, const wxIcon& icon, const wxString& name) { m_hashMap[name] = list->Add(icon); } // Find the index from the name int Find(const wxString& name) { return m_hashMap[name]; } private: IconNameToIndexHashMap m_hashMap; };

Smart_Ch10f.qxd 6/13/05 9:12 AM Page 288 288 Programming with Images Chapter 10 The wxIconBundle class also aggregates images, but its purpose is to store an icon in multiple resolutions rather than multiple different images. This enables the system to choose the appropriate resolution for a given purpose. For example, the icon shown in the title bar of a frame may be smaller than the icon shown in a file or task manager. Here are some examples of creating and using an icon bundle. // Create a bundle with a single icon #include “file16x16.xpm” wxIconBundle iconBundle(wxIcon(file16x16_xpm)); // Add a further icon from a file iconBundle.Add(wxIcon(wxT(“file32x32.png”), wxBITMAP_TYPE_PNG)); // Creates an icon bundle from several images in one file wxIconBundle iconBundle2(wxT(“multi-icons.tif”), wxBITMAP_TYPE_TIF); // Gets the icon with the given size, or if not found, one with size // wxSYS_ICON_X, wxSYS_ICON_Y wxIcon icon = iconBundle.GetIcon(wxSize(16,16)); // Associates the icon bundle with a frame wxFrame* frame = new wxFrame(parent, wxID_ANY); frame->SetIcons(iconBundle); Under Windows, SetIcons extracts 16×16 and 32×32 icons from the icon bundle. CUSTOMIZING ART IN WXWIDGETS wxArtProvider is a class that allows you to customize the built-in graphics (“art”) in a wxWidgets application. For example, you might want to replace the standard icons used by the wxWidgets HTML Help viewer or the icons used by the generic dialogs such as the log dialog. wxWidgets provides a standard wxArtProvider object, and parts of the framework that need icons and bitmaps call wxArtProvider::GetBitmap and wxArtProvider::GetIcon to retrieve a graphic. Art is specified by two identifiers: the art identifier (wxArtID) and client identifier (wxArtClient). The client identifier is only a hint in case different windows need different graphics for the same art identifier. As an example, the wxHTML help window uses this code to get a bitmap for the Back toolbar button: wxBitmap bmp = wxArtProvider::GetBitmap(wxART_GO_BACK,wxART_TOOLBAR);

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 289 Customizing Art in wxWidgets 289 Figure 10-1 Art resources browser You can browse the identifiers and graphics that are built into wxWidgets by compiling and running samples/artprov in your wxWidgets distribution. Figure 10-1 shows the browser in action. To provide your own replacements for wxWidgets art, simply derive a new class from wxArtProvider, override CreateBitmap, and call wxArtProvider::PushProvider from your application’s OnInit function to make it known to wxWidgets. Here’s an example that replaces most of the wxHTML help window artwork. // XPMs with the art #include “bitmaps/helpbook.xpm” #include “bitmaps/helppage.xpm” #include “bitmaps/helpback.xpm” #include “bitmaps/helpdown.xpm” #include “bitmaps/helpforward.xpm” #include “bitmaps/helpoptions.xpm” #include “bitmaps/helpsidepanel.xpm” #include “bitmaps/helpup.xpm” #include “bitmaps/helpuplevel.xpm” #include “bitmaps/helpicon.xpm” #include “wx/artprov.h” // The art provider class class MyArtProvider : public wxArtProvider { protected: virtual wxBitmap CreateBitmap(const wxArtID& id, const wxArtClient& client, const wxSize& size); }; // CreateBitmap function wxBitmap MyArtProvider::CreateBitmap(const wxArtID& id, const wxArtClient& client, const wxSize& size)

Smart_Ch10f.qxd 6/10/05 10:26 AM Page 290 290 Programming with Images Chapter 10 { if (id == wxART_HELP_SIDE_PANEL) return wxBitmap(helpsidepanel_xpm); if (id == wxART_HELP_SETTINGS) return wxBitmap(helpoptions_xpm); if (id == wxART_HELP_BOOK) return wxBitmap(helpbook_xpm); if (id == wxART_HELP_FOLDER) return wxBitmap(helpbook_xpm); if (id == wxART_HELP_PAGE) return wxBitmap(helppage_xpm); if (id == wxART_GO_BACK) return wxBitmap(helpback_xpm); if (id == wxART_GO_FORWARD) return wxBitmap(helpforward_xpm); if (id == wxART_GO_UP) return wxBitmap(helpup_xpm); if (id == wxART_GO_DOWN) return wxBitmap(helpdown_xpm); if (id == wxART_GO_TO_PARENT) return wxBitmap(helpuplevel_xpm); if (id == wxART_FRAME_ICON) return wxBitmap(helpicon_xpm); if (id == wxART_HELP) return wxBitmap(helpicon_xpm); // Any wxWidgets icons not implemented here // will be provided by the default art provider. return wxNullBitmap; } // Initialization bool MyApp::OnInit() { ... wxArtProvider::PushProvider(new MyArtProvider); ... return true; } SUMMARY In this chapter, we’ve seen how to use the four major image classes—wxBitmap, wxIcon, wxCursor, and wxImage—and two classes for aggregating images— wxImageList and wxIconBundle. We’ve also looked at how you can replace the standard wxWidgets icons and bitmaps with your own images. For examples of using image classes, see samples/image, samples/listctrl, and samples/ dragimag in your wxWidgets distribution. Next, we’ll tackle the classes that you use to implement the transfer of data objects via the clipboard or drag and drop.

Smart_Ch11f.qxd 6/10/05 11:20 AM Page 291 C H A P T E R 11 Clipboard and Drag and Drop Most applications offer transfer of data to and from the clipboard via copy, cut, and paste. It’s a basic way of implementing interoperation between your appli- cation and others. More sophisticated applications also allow the user to drag objects between windows, either within a single application or between two applications. For example, dragging a file from a file browser to an application window causes the data to fill the window, be added to a list, or some other behavior. This can be a much faster way to associate data with the application than using menus and dialogs to achieve the same thing, and your users will appreciate having it as an option. Clipboard and drag and drop operations share some classes in wxWidgets, reflecting the fact that they both deal with data transfer, and so this chapter deals with both topics together. We’ll see how to use the standard data objects that wxWidgets provides, as well as how to implement our own. DATA OBJECTS The wxDataObject class is at the heart of both clipboard and drag and drop. Instances of classes derived from wxDataObject represent the data that is being dragged by the mouse during a drag and drop operation or copied to or pasted from the clipboard. wxDataObject is a “smart” piece of data because it knows which formats it supports (via GetFormatCount and GetAllFormats) and knows how to render itself in any of them (via GetDataHere). It can also receive its value from outside the application in a format it supports if it implements the SetData method. We’ll see how to do that later in the chapter. Standard data formats such as wxDF_TEXT are identified by integers, and custom data formats are identified by a text string. The wxDataFormat class rep- resents both of these kinds of identifiers by virtue of a constructor for each. Table 11-1 lists the standard data formats. 291

Smart_Ch11f.qxd 6/10/05 11:20 AM Page 292 292 Clipboard and Drag and Drop Chapter 11 Table 11-1 Standard Data Formats wxDF_INVALID An invalid format, used as default argument for functions taking a wxDataFormat argument. wxDF_TEXT Text format. Standard data object: wxTextDataObject. wxDF_BITMAP Bitmap format. Standard data object: wxBitmapDataObject. wxDF_METAFILE Metafile (Windows only). Standard data object: wxMetafileData Object. wxDF_FILENAME A list of file names. Standard data object: wxFileDataObject. You can also create a custom data format by passing an arbitrary string to the wxDataFormat constructor. The format will be registered the first time it is ref- erenced. Both clipboard and drag and drop deal with a source (data provider) and a target (data receiver). These may be in the same application and even the same window when, for example, you drag some text from one position to another in a word processor. Let’s describe what each should do. Data Source Duties The data source is responsible for creating a wxDataObject containing the data to be transferred. Then the data source should either pass the wxDataObject to the clipboard using the SetData function or pass it to a wxDropSource object when dragging starts and call the DoDragDrop function. The main difference from a clipboard operation is that the object for clip- board transfer must always be created on the heap using new and will be freed by the clipboard when it is no longer needed. Indeed, it is not known in advance when, if ever, the data will be pasted from the clipboard. On the other hand, the object for the drag and drop operation must exist only while DoDragDrop executes and may be safely deleted afterwards, so it can be created either on the heap or on the stack (that is, as a local variable). Another small difference is that in the case of a clipboard operation, the application usually knows in advance whether it copies or cuts data. In a clip- board cut, the data is copied and then removed from the object being edited. This usually depends on which menu item the user chose. But for drag and drop, the application can only know this information after DoDragDrop returns. Data Target Duties To receive data from the clipboard (that is, a paste operation), you should cre- ate a wxDataObject derived class that supports the data formats you need and pass it to wxClipboard::GetData. If it returns false, no data in any of the sup- ported formats is available. If it returns true, the data has been successfully transferred to wxDataObject.

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 293 Using the Clipboard 293 For the drag and drop case, the wxDropTarget::OnData virtual function will be called when a data object is dropped, from which the data itself may be requested by calling the wxDropTarget::GetData method. USING THE CLIPBOARD To use the clipboard, you call member functions of the global pointer wxTheClipboard. Before copying or pasting, you must take temporary ownership of the clipboard by calling wxClipboard::Open. If this operation returns true, you now own the clipboard. Call wxClipboard::SetData to put data on the clipboard or wxClipboard::GetData to retrieve data from the clipboard. Call wxClipboard::Close to close the clipboard and relinquish ownership. You should keep the clipboard open only as long as you are using it. wxClipboardLocker is a helpful class that will open the clipboard (if possi- ble) in its constructor and close it in its destructor, so you can write wxClipboardLocker locker; if (!locker) { ... report an error and return ... } ... use the clipboard ... The following code shows how to write text to and read text from the clipboard: // Write some text to the clipboard if (wxTheClipboard->Open()) { // Data objects are held by the clipboard, // so do not delete them in the app. wxTheClipboard->SetData(new wxTextDataObject(wxT(“Some text”))); wxTheClipboard->Close(); } // Read some text if (wxTheClipboard->Open()) { if (wxTheClipboard->IsSupported(wxDF_TEXT)) { wxTextDataObject data; wxTheClipboard->GetData(data); wxMessageBox(data.GetText()); } wxTheClipboard->Close(); }

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 294 294 Clipboard and Drag and Drop Chapter 11 Here’s the same thing, but with bitmaps: // Write a bitmap to the clipboard wxImage image(wxT(“splash.png”), wxBITMAP_TYPE_PNG); wxBitmap bitmap(image.ConvertToBitmap()); if (wxTheClipboard->Open()) { // Data objects are held by the clipboard, // so do not delete them in the app. wxTheClipboard->SetData(new wxBitmapDataObject(bitmap)); wxTheClipboard->Close(); } // Read a bitmap if (wxTheClipboard->Open()) { if (wxTheClipboard->IsSupported(wxDF_BITMAP)) { wxBitmapDataObject data; wxTheClipboard->GetData( data ); bitmap = data.GetBitmap(); } wxTheClipboard->Close(); } If you implement clipboard operations, you will have to update the user inter- face to enable or disable clipboard commands, whether they are menu items, toolbar buttons, or ordinary buttons. This is a job for the wxWidgets user inter- face update mechanism, which sends wxUpdateUIEvent events to your applica- tion in idle time; refer to Chapter 9, “Creating Custom Dialogs,” for details. The idle time updating allows your interface to be updated even when data is copied to the clipboard without your application’s knowledge. Some controls, such as wxTextCtrl, already implement user interface update events. If you use the standard identifiers wxID_CUT, wxID_COPY, and wxID_PASTE for your menus and toolbars and arrange for command events from the focused control to be processed first, the interface will be updated as the user expects. Chapter 20, “Perfecting Your Application,” shows how to direct command events to the focused control by overriding wxFrame::ProcessEvent. IMPLEMENTING DRAG AND DROP You may implement drag sources, drag targets, or both in your application. Implementing a Drag Source To implement a drag source—that is, to provide the data that may be dragged by the user to a target—you use an instance of the wxDropSource class. Note that the following describes what happens after your application has decided that a drag is starting—the logic to detect the mouse motion that indicates the start of a drag is left entirely up to the application. Some controls help you by

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 295 Implementing Drag and Drop 295 generating an event when dragging is starting, so you don’t have to code the logic yourself (which could potentially interfere with the native mouse behav- ior for the control). This chapter provides a summary of when wxWidgets noti- fies you of the start of a drag. The following steps are involved, as seen from the perspective of the drop source. 1. Preparation First of all, a data object must be created and initialized with the data you want to drag. For example: wxTextDataObject myData(wxT(“This text will be dragged.”)); 2. Drag Start To start the dragging process, typically in response to a mouse click, you must create a wxDropSource object and call wxDropSource::DoDragDrop, like this: wxDropSource dragSource(this); dragSource.SetData(myData); wxDragResult result = dragSource.DoDragDrop(wxDrag_AllowMove); The flags you can pass to DoDragDrop are listed in Table 11-2. Table 11-2 Flags for DoDragDrop wxDrag_CopyOnly Only allow copying. wxDrag_AllowMove Allow moving. wxDrag_DefaultMove The default operation is to move the data. When creating the wxDropSource object, you have the option of also specifying the window that initiates the drag, and three cursors for Copy, Move, and Can’t Drop feedback. These are actually icons in GTK+ and cursors on other platforms, so the macro wxDROP_ICON can be used to hide this difference, as we’ll see in our text drop example shortly. 3. Dragging The call to DoDragDrop blocks the program until the user releases the mouse button (unless you override the GiveFeedback function to do something special). When the mouse moves in a window of a program that understands the same drag and drop protocol, the corresponding wxDropTarget methods are called— see the following section, “Implementing a Drop Target.”

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 296 296 Clipboard and Drag and Drop Chapter 11 4. Processing the Result DoDragDrop returns an effect code, which is one of the values of the wxDragResult type, as listed in Table 11-3. Table 11-3 wxDragResult Return Types from DoDragDrop wxDragError An error prevented the drag and drop operation from completing. wxDragNone The drop target didn’t accept the data. wxDragCopy The data was successfully copied. wxDragMove The data was successfully moved (Windows only). wxDragLink This was a link operation. wxDragCancel The user canceled the operation. Respond to the result of DoDragDrop appropriately in your application. If the return value was wxDragMove, it’s normal to delete the data associated with the drop source and update the display. A return value of wxDragNone means that the drag was cancelled. For example: switch (result) { case wxDragCopy: /* Data was copied or linked: do nothing special */ case wxDragLink: break; case wxDragMove: /* Data was moved: delete original */ DeleteMyDraggedData(); break; default: /* Drag was cancelled, or the data was not accepted, or there was an error: do nothing */ break; } Here’s an example showing how to implement a text drop source. DnDWindow contains a member variable m_strText—when the left mouse button is clicked, a drag operation is started using the value of m_strText. The result of the drag operation is reported in a message box. In practice, the drag operation wouldn’t be started until the pointer has been dragged a minimum distance so that sim- ple left-click actions can be distinguished from a drag. void DnDWindow::OnLeftDown(wxMouseEvent& event ) { if ( !m_strText.IsEmpty() ) { // start drag operation wxTextDataObject textData(m_strText); wxDropSource source(textData, this,

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 297 Implementing Drag and Drop 297 wxDROP_ICON(dnd_copy), wxDROP_ICON(dnd_move), wxDROP_ICON(dnd_none)); int flags = 0; if ( m_moveByDefault ) flags |= wxDrag_DefaultMove; else if ( m_moveAllow ) flags |= wxDrag_AllowMove; wxDragResult result = source.DoDragDrop(flags); const wxChar *pc; switch ( result ) { case wxDragError: pc = wxT(“Error!”); break; case wxDragNone: pc = wxT(“Nothing”); break; case wxDragCopy: pc = wxT(“Copied”); break; case wxDragMove: pc = wxT(“Moved”); break; case wxDragCancel: pc = wxT(“Cancelled”); break; default: pc = wxT(“Huh?”); break; } wxMessageBox(wxString(wxT(“Drag result: “)) + pc); } } Implementing a Drop Target To implement a drop target—that is, to receive the data dropped by the user— you associate a wxDropTarget object with a window using wxWindow::SetDrop Target. You must derive your own class from wxDropTarget and override its pure virtual methods. In particular, override OnDragOver to return a wxDragResult code indicating how the cursor should change when it’s over the given point in the window, and override OnData to react to the drop. Alternatively, you may derive from wxTextDropTarget or wxFileDropTarget and override their OnDropText or OnDropFiles method. The following steps happen in a drag and drop operation from the per- spective of a drop target. 1. Initialization wxWindow::SetDropTarget is called during window creation to associate the drop target with the window. At creation, or during subsequent program execu- tion, a data object is associated with the drop target using wxDropTarget:: SetDataObject. This data object will be responsible for the format negotiation between the drag source and the drop target.

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 298 298 Clipboard and Drag and Drop Chapter 11 2. Dragging As the mouse moves over the target during a drag operation, wxDropTarget:: OnEnter, wxDropTarget::OnDragOver and wxDropTarget::OnLeave are called as appropriate, each returning a suitable wxDragResult so that the drag imple- mentation can give suitable visual feedback. 3. Drop When the user releases the mouse button over a window, wxWidgets asks the associated wxDropTarget object if it accepts the data by calling wxDataObject::GetAllFormats. If the data type is accepted, then wxDrop Target::OnData will be called, and the wxDataObject belonging to the drop tar- get can be filled with data. wxDropTarget::OnData returns a wxDragResult, which is then returned from wxDropSource::DoDragDrop. Using Standard Drop Targets wxWidgets provides classes derived from wxDropTarget so that you don’t have to program everything yourself for commonly used cases. You just derive from the class and override a virtual function to get notification of the drop. wxTextDropTarget receives dropped text—just override OnDropText to do something with the dropped text. The following example implements a drop target that appends dropped text to a list box. // A drop target that adds text to a listbox class DnDText : public wxTextDropTarget { public: DnDText(wxListBox *owner) { m_owner = owner; } virtual bool OnDropText(wxCoord x, wxCoord y, const wxString& text) { m_owner->Append(text); return true; } private: wxListBox *m_owner; }; // Set the drop target wxListBox* listBox = new wxListBox(parent, wxID_ANY); listBox->SetDropTarget(new DnDText(listBox)); The next example shows how to use wxFileDropTarget, which accepts files dropped from the system’s file manager (such as Explorer on Windows) and reports the number of files dropped and their names.

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 299 Implementing Drag and Drop 299 // A drop target that adds filenames to a list box class DnDFile : public wxFileDropTarget { public: DnDFile(wxListBox *owner) { m_owner = owner; } virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) { size_t nFiles = filenames.GetCount(); wxString str; str.Printf( wxT(“%d files dropped”), (int) nFiles); m_owner->Append(str); for ( size_t n = 0; n < nFiles; n++ ) { m_owner->Append(filenames[n]); } return true; } private: wxListBox *m_owner; }; // Set the drop target wxListBox* listBox = new wxListBox(parent, wxID_ANY); listBox->SetDropTarget(new DnDFile(listBox)); Creating a Custom Drop Target Now we’ll create a custom drop target that can accept URLs (web addresses). This time we need to override OnData and OnDragOver, and we introduce a vir- tual function OnDropURL that derived classes can override. // A custom drop target that accepts URL objects class URLDropTarget : public wxDropTarget { public: URLDropTarget() { SetDataObject(new wxURLDataObject); } void OnDropURL(wxCoord x, wxCoord y, const wxString& text) { // Of course, a real program would do something more // useful here... wxMessageBox(text, wxT(“URLDropTarget: got URL”), wxICON_INFORMATION | wxOK); } // URLs can’t be moved, only copied virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def) { return wxDragLink; }

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 300 300 Clipboard and Drag and Drop Chapter 11 // translate this to calls to OnDropURL() just for convenience virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def) { if ( !GetData() ) return wxDragNone; OnDropURL(x, y, ((wxURLDataObject *)m_dataObject)->GetURL()); return def; } }; // Set the drop target wxListBox* listBox = new wxListBox(parent, wxID_ANY); listBox->SetDropTarget(new URLDropTarget); More on wxDataObject As we’ve seen, a wxDataObject represents data that can be copied to or from the clipboard or dragged and dropped. The important thing about wxDataObject is that it is a “smart” piece of data, unlike the usual dumb data containers such as memory buffers or files. Being “smart” means that the data object itself should know what data formats it supports and how to render itself in each of its supported formats. A supported format is the format in which the data can be requested from a data object or from which the data object may be set. In the general case, an object may support different formats on input and output, so it may be able to render itself in a given format but not be created from data in this format, or vice versa. Several solutions are available to you when you need to use a wxDataObject class: 1. Use one of the built-in classes. You may use wxTextDataObject, wxBitmap DataObject, or wxFileDataObject in the simplest cases when you only need to support one format and your data is text, a bitmap, or a list of files. 2. Use wxDataObjectSimple. Deriving from wxDataObjectSimple is the sim- plest solution for custom data—you will only support one format, and so you probably won’t be able to communicate with other programs, but data transfer will work in your program (or between different copies of it). 3. Derive from wxCustomDataObject (a subclass of wxDataObjectSimple) for user-defined formats.

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 301 Implementing Drag and Drop 301 4. Use wxDataObjectComposite. This is a simple but powerful solution that allows you to support any number of formats (either standard or custom if you combine it with the previous solutions). 5. Use wxDataObject directly. This is the solution for maximum flexibility and efficiency, but it is also the most difficult to implement. The easiest way to use drag and drop and the clipboard with multiple formats is by using wxDataObjectComposite, but it is not the most efficient one because each wxDataObjectSimple contains all the data in its specific format. Imagine that you want to paste to the clipboard 200 pages of text in your proprietary format, as well as Word, RTF, HTML, Unicode, and plain text. Even today’s computers would struggle to support this task. For efficiency, you will have to derive from wxDataObject directly, make it enumerate its formats, and provide the data in the requested format on demand. The underlying data transfer mechanisms for clipboard and drag and drop don’t copy any data until another application actually requests it. So although a user may think that the data resides in the clipboard after press- ing the application’s Copy command, in reality it may only have been declared to be available. Deriving from wxDataObject Let’s look at what’s needed to derive a new class from wxDataObject. Deriving from the other classes mentioned earlier is similar but easier, so we won’t cover them all here. Each class derived directly from wxDataObject must override and imple- ment all of its functions that are pure virtual in the base class. Data objects that only render their data or only set it (that is, work in only one direction) should return 0 from GetFormatCount for the unsupported direction. GetAllFormats takes an array of wxDataFormat values and a Direction (Get or Set). Copy all the supported formats in the given direction to the formats array. GetFormatCount determines the number of elements in the array. GetDataHere takes a wxDataFormat and a void* buffer, returning true on success and false otherwise. It must write the data for the given format into the buffer. This can be arbitrary binary or text data that need only be recog- nized by SetData. GetDataSize takes a wxDataFormat and returns the data size for the given format. GetFormatCount returns the number of available formats for rendering or setting the data. GetPreferredFormat takes a Direction and returns the preferred wxDataFormat for this direction. SetData takes a wxDataFormat, integer buffer length, and void* buffer. You interpret the data in the buffer in the required way for this object, such as copying it to an internal structure. This function should return true on success and false on failure.

Smart_Ch11f.qxd 6/10/05 3:30 PM Page 302 302 Clipboard and Drag and Drop Chapter 11 The wxWidgets Drag and Drop Sample We’ll use the wxWidgets drag and drop sample in samples/dnd to demonstrate how to write a custom wxDataObject with a user-defined data format. The sam- ple shows a simple shape—a triangle, rectangle, or ellipse—and allows you to edit it, drag it to a new position, and copy it to and paste it back from the clip- board. You can show the shape frame with the New Frame command on the File menu. This window is illustrated in Figure 11-1. Figure 11-1 The wxWidgets drag and drop sample The shapes are modeled with classes derived from DnDShape, and the data object is called DnDShapeDataObject. Before examining the implementation of DndShapeDataObject, let’s see how the application will use them. When a clipboard copy operation is invoked, a DnDShapeDataObject will be added to the clipboard, taking a copy of the current shape in case it’s deleted while the object is still on the clipboard. Here’s the code to do it: void DnDShapeFrame::OnCopyShape(wxCommandEvent& event) { if ( m_shape ) { wxClipboardLocker clipLocker; if ( !clipLocker ) { wxLogError(wxT(“Can’t open the clipboard”)); return; } wxTheClipboard->AddData(new DnDShapeDataObject(m_shape)); } } A clipboard paste is also straightforward, calling wxClipboard::GetData to try to get shape data from the clipboard and then retrieving the shape data from the data object. We also show the UI update handler that will enable the Paste

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 303 Implementing Drag and Drop 303 menu command only if there is shape data on the clipboard. shapeFormatId is a global variable containing the shape format name, wxShape. void DnDShapeFrame::OnPasteShape(wxCommandEvent& event) { wxClipboardLocker clipLocker; if ( !clipLocker ) { wxLogError(wxT(“Can’t open the clipboard”)); return; } DnDShapeDataObject shapeDataObject(NULL); if ( wxTheClipboard->GetData(shapeDataObject) ) { SetShape(shapeDataObject.GetShape()); } else { wxLogStatus(wxT(“No shape on the clipboard”)); } } void DnDShapeFrame::OnUpdateUIPaste(wxUpdateUIEvent& event) { event.Enable( wxTheClipboard-> IsSupported(wxDataFormat(shapeFormatId)) ); } To implement drag and drop, a drop target class is required that will notify the application when data is dropped. Objects of class DnDShapeDropTarget contain a DnDShapeDataObject that is ready to receive data when its OnData member is called. Here’s the declaration (and implementation) of DnDShapeDropTarget: class DnDShapeDropTarget : public wxDropTarget { public: DnDShapeDropTarget(DnDShapeFrame *frame) : wxDropTarget(new DnDShapeDataObject) { m_frame = frame; } // override base class (pure) virtuals virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def) { m_frame->SetStatusText(_T(“Mouse entered the frame”)); return OnDragOver(x, y, def); } virtual void OnLeave() { m_frame->SetStatusText(_T(“Mouse left the frame”)); } virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 304 304 Clipboard and Drag and Drop Chapter 11 { if ( !GetData() ) { wxLogError(wxT(“Failed to get drag and drop data”)); return wxDragNone; } // Notify the frame of the drop m_frame->OnDrop(x, y, ((DnDShapeDataObject *)GetDataObject())->GetShape()); return def; } private: DnDShapeFrame *m_frame; }; The target is set when the shape frame is created during application initial- ization: DnDShapeFrame::DnDShapeFrame(wxFrame *parent) : wxFrame(parent, wxID_ANY, _T(“Shape Frame”)) { ... SetDropTarget(new DnDShapeDropTarget(this)); ... } A drag starts when a left mouse button click is detected, and the event han- dler creates a wxDropSource passing a DnDShapeDataObject before calling DoDragDrop to initiate the drag operation. DndShapeFrame::OnDrag looks like this: void DnDShapeFrame::OnDrag(wxMouseEvent& event) { if ( !m_shape ) { event.Skip(); return; } // start drag operation DnDShapeDataObject shapeData(m_shape); wxDropSource source(shapeData, this); const wxChar *pc = NULL; switch ( source.DoDragDrop(true) ) { default: case wxDragError: wxLogError(wxT(“An error occured during drag and drop”)); break; case wxDragNone:

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 305 Implementing Drag and Drop 305 SetStatusText(_T(“Nothing happened”)); break; case wxDragCopy: pc = _T(“copied”); break; case wxDragMove: pc = _T(“moved”); if ( ms_lastDropTarget != this ) { // don’t delete the shape if we dropped it // on ourselves! SetShape(NULL); } break; case wxDragCancel: SetStatusText(_T(“Drag and drop operation cancelled”)); break; } if ( pc ) { SetStatusText(wxString(_T(“Shape successfully “)) + pc); } //else: status text already set } When the drop is signaled by the user releasing the mouse button, wxWidgets calls DnDShapeDropTarget::OnData, which in turn calls DndShapeFrame::OnDrop with a new DndShape to set at the drop position. This completes the drag and drop operation. void DnDShapeFrame::OnDrop(wxCoord x, wxCoord y, DnDShape *shape) { ms_lastDropTarget = this; wxPoint pt(x, y); wxString s; s.Printf(wxT(“Shape dropped at (%d, %d)”), pt.x, pt.y); SetStatusText(s); shape->Move(pt); SetShape(shape); } The only remaining tricky bit is to implement the custom wxDataObject. We’ll show the implementation in parts for clarity. First, we’ll see the custom format identifier declaration, the DndShapeDataObject class declaration, its constructor and destructor, and its data members.

Smart_Ch11f.qxd 6/10/05 11:21 AM Page 306 306 Clipboard and Drag and Drop Chapter 11 The format identifier is shapeFormatId, and it is a global variable used throughout the sample. The constructor takes a new copy of the shape (if one is passed) by using GetDataHere; the copy could also have been implemented by using a DndShape::Clone function, had one been provided. The DnDShapeData Object destructor will delete this shape object. DndShapeDataObject can provide bitmap and (on supported platforms) metafile renderings of its shape, so it also has wxBitmapDataObject and wxMetaFileDataObject members (and associated flags to indicate whether they’re valid) to cache these formats when asked for them. // Custom format identifier static const wxChar *shapeFormatId = wxT(“wxShape”); class DnDShapeDataObject : public wxDataObject { public: // ctor doesn’t copy the pointer, so it shouldn’t go away // while this object is alive DnDShapeDataObject(DnDShape *shape = (DnDShape *)NULL) { if ( shape ) { // we need to copy the shape because the one // we’ve handled may be deleted while it’s still on // the clipboard (for example) - and we reuse the // serialisation methods here to copy it void *buf = malloc(shape->DnDShape::GetDataSize()); shape->GetDataHere(buf); m_shape = DnDShape::New(buf); free(buf); } else { // nothing to copy m_shape = NULL; } // this string should uniquely identify our for