Book Image

C++ Windows Programming

By : Stefan Björnander
Book Image

C++ Windows Programming

By: Stefan Björnander

Overview of this book

It is critical that modern developers have the right tools to build practical, user-friendly, and efficient applications in order to compete in today’s market. Through hands-on guidance, this book illustrates and demonstrates C++ best practices and the Small Windows object-oriented class library to ease your development of interactive Windows applications. Begin with a focus on high level application development using Small Windows. Learn how to build four real-world applications which focus on the general problems faced when developing graphical applications. Get essential troubleshooting guidance on drawing, spreadsheet, and word processing applications. Finally finish up with a deep dive into the workings of the Small Windows class library, which will give you all the insights you need to build your own object-oriented class library in C++.
Table of Contents (22 chapters)
C++ Windows Programming
Credits
About the Author
About the Reviewer
www.PacktPub.com
Dedication
Preface
Free Chapter
1
Introduction

The library


This section gives an introduction to Small Windows. The first part of a Small Windows application is the MainWindow function. It corresponds to main in regular C++. Its task is to set the name of the application and create the main window of the application.

In this book we talk about definitions and declarations. A declaration is just a notification for the compiler, while a definition is what defines the feature. Below is the declaration of the MainWindow function. Its definition is left to the user of Small Windows.

void MainWindow(vector<String>argumentList,
                SmallWindows::WindowShow windowShow);

Simply put, in Windows the application does not take any initiative; rather, it waits for messages and reacts when it receives them. Informally speaking, you do not call Windows, Windows calls you.

The most central part of Small Windows is the Application class. In Windows, each event generates a message that is sent to the window that has input focus at the moment. The Application class implements the RunMessageLoop method, which makes sure that each message is sent to the correct window. It also closes the application when a special quit message is sent.

The creation of a window takes place in two steps. In the first step, the RegisterWindowClasses method sets features such as style, color, and appearance. Note that Windows classes are not C++ classes:

class Application { 
  public: 
    static int RunMessageLoop(); 
    static void RegisterWindowClasses(HINSTANCE instanceHandle); 
}; 

The next step is to create an individual window, which is done by the Window class. All virtual methods are empty and are intended to be overridden by sub classes shown as follows:

  class Window { 
    public: 

A window can be visible or invisible, enabled or disabled. When a window is enabled, it accepts mouse, touch, and keyboard input:

      void ShowWindow(bool visible); 
      void EnableWindow(bool enable); 

The OnMove and the OnSize methods are called when the window is moved or resized. The OnHelp method is called when the user presses the F1 key or the Help button in a message box:

      virtual void OnMove(Point topLeft); 
      virtual void OnSize(Size windowSize); 
      virtual void OnHelp(); 

The client area is the part of the window that it is possible to paint in. Informally, the client area is the window minus its frame. The contents of the client area can be zoomed. The default zoom factor is 1.0:

      double GetZoom() const; 
      void SetZoom(double zoom); 

The timer can be set to an interval in milliseconds. The OnTimer method is called on every interval. It is possible to set up several timers, as long as they have different identity numbers:

      void SetTimer(int timerId, unsigned int interval); 
      void DropTimer(int timerId); 
      virtual void OnTimer(int timerId); 

The OnMouseDown, OnMouseUp, and OnDoubleClick methods are called when the user presses, releases, or double-clicks on a mouse button. The OnMouseMove method is called when the user moves the mouse with at least one button pressed. The OnMouseWheel method is called when the user moves the mouse wheel with one click:

      virtual void OnMouseDown(MouseButton mouseButtons, 
                           Point mousePoint, bool shiftPressed, 
                           bool controlPressed); 


      virtual void OnMouseUp(MouseButton mouseButtons, 
                           Point mousePoint, bool shiftPressed, 
                           bool controlPressed); 
      virtual void OnDoubleClick(MouseButton mouseButtons, 
                           Point mousePoint, bool shiftPressed, 
                           bool controlPressed); 
      virtual void OnMouseMove(MouseButton mouseButtons, 
                           Point mousePoint, bool shiftPressed, 
                           bool controlPressed); 
      virtual void OnMouseWheel(WheelDirection direction, 
                           bool shiftPressed, bool controlPressed); 

The OnTouchDown, OnTouchMove, and OnTouchDown methods work in the same way as the mouse methods. However, as the user can touch several points at the same time, the methods takes lists of points rather than an individual point:

    virtual void OnTouchDown(vector<Point> pointList); 
    virtual void OnTouchMove(vector<Point> pointList); 
    virtual void OnTouchUp(vector<Point> pointList); 

The OnKeyDown and OnKeyUp methods are called when the user presses or releases a key. If the user presses a graphical key (a key with an ASCII value between 32 and 127, inclusive), the OnChar method is called in between:

      virtual bool OnKeyDown(WORD key, bool shiftPressed, 
                             bool controlPressed); 
      virtual void OnChar(TCHAR tChar); 
      virtual bool OnKeyUp(WORD key, bool shiftPressed, 
                           bool controlPressed); 

The Invalidate method marks a part of the client area (or the whole client area) to be repainted; the area becomes invalidated. The area is cleared before the painting if clear is true. The UpdateWindow method forces a repainting of the invalidated area. It causes the OnPaint method to be called eventually:

      void Invalidate(Rect areaRect, bool clear = true) const; 
      void Invalidate(bool clear = true) const; 
      void UpdateWindow(); 

The OnPaint method is called when some part of the client area needs to be repainted and the OnPrint method is called when it is sent to a printer. Their default behavior is to call the OnDraw method with Paint or Print as the value of the drawMode parameter:

      virtual void OnPaint(Graphics& graphics) const;
      virtual void OnPrint(Graphics& graphics, int page, 
                           int copy, int totalPages) const;
      virtual void OnDraw(Graphics& graphics, DrawMode drawMode)
                          const;

The OnClose method closes the window if TryClose returns true. The OnDestroy method is called when the window is being closed:

      virtual void OnClose(); 
      virtual bool TryClose(); 
      virtual void OnDestroy(); 

The following methods inspect and modify the size and position of the window. Note that we cannot set the size of the client area; it can only be set indirectly by resizing the window:

      Size GetWindowSize() const; 
      void SetWindowSize(Size windowSize); 
      Point GetWindowPosition() const; 
      void SetWindowPosition(Point topLeft); 
      Size GetClientSize() const; 

In the word processor and spreadsheet programs in this book, we handle text and need to calculate the size of individual characters. The following methods calculate the width of a character with a given font. They also calculate the height, ascent, and average character width of a font:

      int GetCharacterWidth(Font font, TCHAR tChar) const; 
      int GetCharacterHeight(Font font) const; 
      int GetCharacterAscent(Font font) const; 
      int GetCharacterAverageWidth(Font font) const; 

The ascent line separates the upper and lower part of a letter, shown as follows:

Finally, the MessageBox method displays a simple message box in the window:

      Answer MessageBox(String message,
                    String caption = TEXT("Error"),
                    ButtonGroup buttonGroup = Ok,
                    Icon icon = NoIcon, bool help = false) const;
};

The Window class also uses the Graphics class responsible for drawing text and geometrical objects in the window. A reference to a Graphics object is sent to the OnPaint, OnPrint, and OnDraw methods in the Window class. It can be used to draw lines, rectangles, and ellipses and to write text:

  class Graphics { 
    public: 
      void DrawLine(Point startPoint, Point endPoint, 
                    Color penColor, PenStyle penStyle = Solid); 
      void DrawRectangle(Rect rect, Color penColor, 
                         PenStyle = Solid); 
      void FillRectangle(Rect rect, Color penColor, 
                       Color brushColor, PenStyle penStyle=Solid); 
      void DrawEllipse(Rect rect, Color penColor, 
                       PenStyle = Solid); 
      void FillEllipse(Rect rect, Color penColor, 
                       Color brushColor, PenStyle penStyle=Solid); 
      void DrawText(Rect areaRect, String text, Font font, 
                    Color textColor, Color backColor, 
                    bool pointsToMeters = true); 
  }; 

The Document class extends the Window class with some functionality common to document-based applications. The scroll thumbs are automatically set to reflect the visible part of the document. The mouse wheel moves the vertical scroll bar one line-height for each click. The height of a line is set by the constructor. The code snippet for it is shown as follows:

  class Document : public Window { 
    public: 

The dirty flag is true when the user has made a change in the document and it needs to be saved. In Document, the dirty flag is set manually, but in the following StandardDocument subclass it is handled by the framework:

      bool IsDirty() const; 
      void SetDirty(bool dirty); 

The caret is the blinking marker that indicates to the user where they should input the next character. The keyboard can be set (with the Insert key) to insert or overwrite mode. The caret is often a thin vertical bar in insert mode and a block with the width of an average character in overwrite mode.

The caret can be set or cleared. For instance, in the word processor, the caret is visible when the user writes text and invisible when the user marks text. When the window gains focus, the caret becomes visible if it has earlier been set. When the window loses focus, the caret becomes invisible, regardless of whether it has earlier been set:

      void SetCaret(Rect caretRect); 
      void ClearCaret(); 
      void OnGainFocus(); 
      void OnLoseFocus(); 

A document may hold a menu bar, which is set by the SetMenuBar method:

      void SetMenuBar(Menu& menuBar); 

The OnDropFiles method is called when the user drops one or several files in the window. Their paths are stored in the path list:

      virtual void OnDropFile(vector<String> pathList); 

The keyboard mode of a document can be set to insert or overwrite as follows:

      KeyboardMode GetKeyboardMode() const; 
      void SetKeyboardMode(KeyboardMode mode); 

The OnHorizontalScroll and OnVerticalScroll methods are called when the user scrolls the bar by clicking on the scroll bar arrows or the scroll bar fields, or dragging the scroll thumbs. The code snippet for it is shown as follows:

      virtual void OnHorizontalScroll(WORD flags,WORD thumbPos=0); 
      virtual void OnVerticalScroll(WORD flags, WORD thumbPos =0); 

There is a large set of methods for inspecting or changing scroll bar settings. The size of a line or page is set by the constructor:

      void SetHorizontalScrollPosition(int scrollPos); 
      int GetHorizontalScrollPosition() const; 
      void SetVerticalScrollPosition(int scrollPos); 
      int GetVerticalScrollPosition() const; 
 
      void SetHorizontalScrollLineWidth(int lineWidth); 
      int GetHorizontalScrollLineHeight() const; 
      void SetVerticalScrollLineHeight(int lineHeight); 
      int GetVerticalScrollLineHeight() const; 
 
      void SetHorizontalScrollPageWidth(int pageWidth); 
      int GetHorizontalScrollPageWidth() const; 
      void SetVerticalScrollPageHeight(int pageHeight); 
      int GetVerticalScrollPageHeight() const; 


 
      void SetHorizontalScrollTotalWidth(int scrollWidth); 
      int GetHorizontalScrollTotalWidth() const; 
      void SetVerticalScrollTotalHeight(int scrollHeight); 
      int GetVerticalScrollTotalHeight() const; 
  }; 

The Menu class handles the menu bar, a menu, a menu item, or a menu item separator (a horizontal bar) in the document. The selection listener is called when the user selects the menu item. The enable, check, and radio listeners are called (unless they are null) when the item is about to become visible. If they return true, the item is enabled or annotated with a check box or radio button:

  class Menu { 
    public: 
      void AddMenu(Menu& menu); 
      void AddSeparator(); 
      void AddItem(String text, VoidListener selection, 
                   BoolListener enable = nullptr, 
                   BoolListener check = nullptr, 
                   BoolListener radio = nullptr); 
  }; 

An accelerator is a shortcut command. For instance, often, the Open item in the File menu is annotated with the text Ctrl+O. This means that you can obtain the same result by pressing the Ctrl key and the O key at the same time, just as if you selected the Open menu item. In both cases, the Open dialog is displayed.

The Accelerator class holds only the TextToAccelerator method. It interprets the menu item text and adds the accelerator, if present, to the accelerator set:

class Accelerator { 
    public: 
      static void TextToAccelerator(String& text, int idemId, 
                                    list<ACCEL>& acceleratorSet); 
  }; 

The StandardDocument class extends the Document class and sets up a framework that takes care of all traditional tasks, such as load and save, and cut, copy, and paste, in a document-based application:

  class StandardDocument : public Document { 
    public: 

The StandardDocument class comes equipped with the common File, Edit, and Help menus. The File menu can optionally (if the print parameter is true) be equipped with menu items for printing and print previewing:

      Menu StandardFileMenu(bool print); 
      Menu StandardEditMenu(); 
      Menu StandardHelpMenu(); 

The ClearDocument method is called when the user selects the New menu item; its task is to clear the document. The WriteDocumentToStream method is called when the user selects the Save or Save As menu item and the ReadDocumentFromStream method is called when the user selects the Open menu item:

      virtual void ClearDocument(); 
      virtual bool WriteDocumentToStream(String name, 
                                         ostream& outStream)const; 
      virtual bool ReadDocumentFromStream(String name, 
                                          istream& inStream); 

The CopyAscii, CopyUnicode, and CopyGeneric methods are called when the user selects the Cut or Copy menu item and the corresponding ready method returns true. The code snippet for it is shown as follows:

      virtual void CopyAscii(vector<String>& textList) const; 
      virtual bool IsCopyAsciiReady() const; 
      virtual void CopyUnicode(vector<String>& textList) const; 
      virtual bool IsCopyUnicodeReady() const; 
      virtual void CopyGeneric(int format, InfoList& infoList)  
                               const; 
      virtual bool IsCopyGenericReady(int format) const; 

In the same way, the PasteAscii, PasteUnicode, and PasteGeneric methods are called when the user selects the Paste menu item and the corresponding ready method returns true:

      virtual void PasteAscii(const vector<String>& textList); 
      virtual bool IsPasteAsciiReady 
                   (const vector<String>& textList) const; 
      virtual void PasteUnicode(const vector<String>& textList); 
      virtual bool IsPasteUnicodeReady 
                   (const vector<String>& textList) const; 
      virtual void PasteGeneric(int format, InfoList& infoList); 
      virtual bool IsPasteGenericReady(int format, 
                                       InfoList& infoList) const; 

The OnDropFile method checks the path list and accepts the drop if exactly one file has the suffix of the document type of the application (set by the constructor):

      void OnDropFile(vector<String> pathList); 
  }; 

In Small Windows, we do not care about the pixel size. Instead, we use logical units that stay the same, regardless of the physical resolution of the screen. We can choose from the following three coordinate systems:

  • LogicalWithScroll: A logical unit is one hundredth of a millimeter, with the current scroll bar settings taken into account. The drawing program and word processor use this system.

  • LogicalWithoutScroll: A logical unit is one hundredth of a millimeter also in this case, but the current scroll bar settings are ignored. The spreadsheet program uses this system.

  • PreviewCoordinate: The client area of the window is set to a fixed logical size when the window is created. This means that the size of the logical units changes when the user changes the window size. The Tetris game and the PreviewDocument class uses this system.

Besides the StandardDocument class, there is also the PrintPreviewDocument, which class that also extends the Document class. It displays one of the pages of a standard document. It is possible for the user to change the page by using the arrow keys and the Page Up and Page Down keys or by using the vertical scroll bar:

  class PrintPreviewDocument : Document { 
    public: 
      PrintPreviewDocument(StandardDocument* parentDocument, 
                  int page = 1, Size pageSize = USLetterPortrait); 
      bool OnKeyDown(WORD key, bool shiftPressed, 
                     bool controlPressed); 
      void OnVerticalScroll(WORD flags, WORD thumbPos = 0); 
      void OnPaint(Graphics& graphics) const; 
  }; 

There are also the simple auxiliary classes:

  • Point: It holds a two-dimensional point (x and y)

  • Size: It holds two-dimensional width and height

  • Rect: It holds the four corners of a rectangle

  • DynamicList: It holds a dynamic list

  • Tree: It holds a tree structure

  • InfoList: It holds a list of generic information that can be transformed into a memory block

The Registry class holds an interface to the Windows Registry, the database in the Windows system that we can use to store values in between the execution of our applications. The Clipboard class holds an interface to the Windows Clipboard, an area in Windows intended for short-term data storage that we can use to store information cut, copied, and pasted between applications.

The Dialog class is designed for customized dialogs. The Control class is the root class for the controls of the dialog. The CheckBox, RadioButton, PushButton, ListBox, and ComboBox classes are classes for the specific controls. The TextField class holds a text field that can be translated to different types by the Converter class. Finally, the PageSetupDialog class extends the Dialog class and implements a dialog with controls and converters.