Hello World (big)

This is a program to show most of the features being used together.

// $Revision: 1.25 $
/* Copyright (c) 2005 - Thomas Hansen
 * Part of the SmartWin++ library example code
 * Permission to use under the same terms as the actual library
 * The license to the library can be found at http://smartwinlib.org
 * License for library is Open Source and BSD
 */
// Standard C++ of 1998 includes
#include "SmartWin.h"     // Fundamental include
#include <iostream>
#include <fstream>
using namespace SmartWin; // Lets you skip the prefix.

// hello.cpp
// Show the SmartWin++ features with lots of comments for beginners.
//
//

enum
{ ITEMS = 3
}; // The numer of items in lists
enum widg_page
{ SINGLE_WIDGETS = 0, MULTI_WIDGETS = 1
}; // Which of 2 tabbed pages

// HelloWinClass defines a window with native controls based on the Win32 API.
//
// For our purposes, use this pattern for simple windows:
// class YourClass
//     : public WidgetFactory< WidgetWindow, YourClass >
//
//
class HelloWinClass
                        : public WidgetFactory< WidgetWindow, HelloWinClass >

                        // Detailed explanation of the above C++ class template declaration syntax:
                        //   HelloWinClass is derived from WidgetFactory.
                        //   WidgetFactory has 3 template parameters: Base, Parent, and MessageMapPolicy
                        //
                        //  template< template<class, class> class Base,
                        //            class Parent,
                        //            class MessageMapPolicy = MessageMapPolicyNormalWidget>
                        //  class WidgetFactory
                        //
                        // So HelloWinClass has template instance values of
                        //   WidgetWindow, HelloWinClass, and MessageMapPolicyNormalWidget for
                        //           Base,        Parent, and MessageMapPolicy
                        //
                        // Hold on, where did MessageMapPolicyNormalWidget come from ?
                        // If you look at WidgetFactory.h you will see MessageMapPolicyNormalWidget is the default.
                        // for MessageMapPolicy.
                        //
                        // This means that HelloWinClass can use the public part of WidgetFactory
                        // and that the particular instance of WidgetFactory can refer to a Parent class
                        // at compile time, and actually mean HelloWinClass at runtime.
{
private:
        // Hello will have two menus; a conventional top of window one,
        // and a popup that is invoked by right-clicking on the window area.
        WidgetMenuPtr itsMainMenu, itsPopupMenu;

        // Below are the definitions for the Widgets used in this example.
        // They look like simple pointers, but they are actually templated typedefs in WidgetFactory.
        // For our purposes, just declare WidgetXxxx *itsXxxx, initialize with itsXxxx= createXxxx()
        // and be happy that SmartWin handles the rest, including deleting them at the right time.
        //
        // By convention, we prefix all member variables with "its".
        //
        WidgetButtonPtr itsButton;
        WidgetCheckBoxPtr itsCheckBox;
        WidgetComboBoxPtr itsComboBox;
        WidgetDataGridPtr itsList;
        WidgetGroupBoxPtr itsRadioGroupBox;
        WidgetRadioButtonPtr itsRadios[ ITEMS ];
        WidgetTextBoxPtr itsTextBoxSource;
        WidgetSliderPtr itsHorizSlider, itsVertSlider;
        WidgetSpinnerPtr itsSpinner;
        WidgetStaticPtr itsPromptA, itsPromptB;
        WidgetTabSheetPtr itsTabSheet;
        WidgetTextBoxPtr itsTextA, itsTextB;
        WidgetTreeViewPtr itsTree;

        // The Widgets are organized on two child windows
        // which correspond to the two tabs of the WidgetTabSheet
        //
        WidgetChildWindowPtr itsMulti, itsSingle;

        // We have a little graphics state in the class.
        double itsGraphicsPart; // The fraction of the window to devote to graphics.
        COLORREF itsColor; // COLORREF is a Win32 type which is a 24 bit RGB value.
        Point itsEllipseSize; // A Point has x and y, which we interpret as sizes here.

public:

        HelloWinClass()
        {}

        // This is the response function from a menu option which was created as:
        // itsPopupMenu->appendItem( m++, _T("&Choose color"), &HelloWinClass::menuChooseColor );
        //
        // menu is the WidgetMenu created above but discarded.
        // The item has the value of m++.
        //
        // The result of this function is to change itsColor if the user selects a new color.
        // Then the paint function is redone to draw the graphics with the new color.
        //
        void menuChooseColor( WidgetMenuPtr menu, unsigned item )
        {
                WidgetChooseColor dlg = createChooseColor(); // Built in dialog.
                WidgetChooseColor::ColorParams params = dlg.showDialog( false ); // Initial color
                if ( params.userPressedOk() )
                {
                        itsColor = params.getColor();

                        UpdateCanvas uc( this );
                        paint( uc );
                }
        }

        // Lets the user choose the font for the controls.
        // Then the window is redone.
        // How to set the Font for the Menus ??
        //
        void menuChooseFont( WidgetMenuPtr menu, unsigned item )
        {
#ifndef WINCE
                WidgetChooseFont widget = createChooseFont(); // Built in dialog.
                FontPtr font = widget.showDialog();
                if ( font.get() != 0 )
                {
                        FontPtr choosenFont( font );
                        itsTabSheet->setFont( choosenFont );

                        itsButton->setFont( choosenFont );
                        itsTextBoxSource->setFont( choosenFont );
                        itsCheckBox->setFont( choosenFont );
                        itsPromptA->setFont( choosenFont ); itsTextA->setFont( choosenFont );
                        itsPromptB->setFont( choosenFont ); itsTextB->setFont( choosenFont );

                        itsTree->setFont( choosenFont );
                        itsComboBox->setFont( choosenFont );
                        itsList->setFont( choosenFont );
                        itsRadioGroupBox->setFont( choosenFont );
                        for ( int i = 0; i < ITEMS; i++ ) itsRadios[ i ] ->setFont( choosenFont );

                        updateWidget(); // Redo everything with the new font.
                        layout(); // to show how the new Font effects the layout.
                }
#endif
        }

        void menuCloseEvent( WidgetMenuPtr menu, unsigned item )
        {
                close(); // Exit the window.  All Widgets are automatically removed.
        }

        // A mouse event response function
        // mouse.pos uses window relative coordinates.
        // The trackPopupMenu takes a screen position,
        // so we add mouse.pos to the window position.
        //
        void PopupMenuViaRightClick( const MouseEventResult & mouse )
        {
                SmartWin::Rectangle possize = getBounds();
                int x = possize.pos.x + mouse.pos.x;
                int y = possize.pos.y + mouse.pos.y;

                itsPopupMenu->trackPopupMenu( this, x, y );
        }

        void copyTextToClipboard( WidgetMenuPtr menu, unsigned item )
        {
                SmartUtil::tstring selection = itsTextBoxSource->getSelection();
                ClipBoardString::instance().setClipBoardData( selection, this );
        }

        // The fundamental button click response function.
        // The passed parameter is the button that was clicked, in case you want
        // the same response function for multiple buttons.
        //
        void buttonClicked( WidgetButtonPtr button )
        {
                WidgetMessageBox mb = createMessageBox();
                switch ( mb.show( _T( "Do you like SmartWin++ ?" ),
                                                  _T( "Survey" ), WidgetMessageBox::BOX_YESNOCANCEL ) )
                {
                        case WidgetMessageBox::RETBOX_YES :
                                mb.show( _T( "Then, tell us how to be even better !" ) );
                                break;

                        case WidgetMessageBox::RETBOX_NO :
                                mb.show( _T( "Then, tell us how to improve !" ) );
                                break;

                        case WidgetMessageBox::RETBOX_CANCEL :
                                break;
                }
        }

        void checkclicked( WidgetCheckBoxPtr chk )
        {
                if ( chk->getChecked() )
                {
                        chk->setText( _T( "Please uncheck me" ) );
                }
                else
                {
                        chk->setText( _T( "Check me" ) );
                }
                layout(); // to reflect the different text value in the checkbox.
        }

        void scrollHoriz( WidgetSliderPtr slider )
        {
                itsEllipseSize.x = slider->getPosition();
                UpdateCanvas uc( this );
                paint( uc );
        }

        void scrollVert( WidgetSliderPtr slider )
        {
                itsEllipseSize.y = slider->getPosition();
                UpdateCanvas uc( this );
                paint( uc );
        }

        void spinVert( WidgetSpinnerPtr spinner )
        {
                itsEllipseSize.y = spinner->getValue() * 10;
                UpdateCanvas uc( this );
                paint( uc );
        }

        void radioClicked( WidgetRadioButtonPtr btn )
        {
                for ( int i = 0; i < ITEMS; i++ )
                {
                        if ( btn == itsRadios[ i ] )
                        {
                                itsComboBox->setSelectedIndex( i );
                        }
                }
        }

        void selectionChanged( WidgetComboBoxPtr comboBox )
        {
                createMessageBox().show( _T( "New selection: " ) + comboBox->getSelectedValue() );
        }

        void treeNodeSelChanged( WidgetTreeViewPtr tree )
        {
                SmartUtil::tstring itemText = tree->getSelectedItemText();
                int dex = tree->getSelectedIndex();
                if ( - 1 == dex )
                        createMessageBox().show( itemText );
                else
                {
                        itsComboBox->setSelectedIndex( dex );
                }
        }

        // Read in the ASCII file a line at a time.
        //
        void FetchFile( SmartUtil::tstring filePath, SmartUtil::tstring & filedata )
        {
                std::basic_ifstream< TCHAR > file( filePath.c_str() );
                if ( !file.good() )
                        return;

                const int bsize = 65536;
                TCHAR buff[ bsize ];
                while ( file.getline( buff, bsize ) )
                {
                        filedata += buff;
                        filedata += _T( "\r\n" );
                }
        }

        void createSingleItems()
        {
                // The inputs and outputs of the dialog
                WidgetStatic::Seed staticCS;
                WidgetTextBox::Seed textBoxCS;

                staticCS.style |= ES_RIGHT; // Make the prompts right justified.
                textBoxCS.style |= ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL;

                staticCS.caption = _T( "First prompt: " );
                itsPromptA = itsSingle->createStatic( staticCS );
                textBoxCS.caption = _T( "Bugs Bunny" );
                itsTextA = itsSingle->createTextBox( textBoxCS );

                staticCS.caption = _T( "Second prompt: " );
                itsPromptB = itsSingle->createStatic( staticCS );
                textBoxCS.caption.clear();
                itsTextB = itsSingle->createTextBox( textBoxCS );

                itsButton = itsSingle->createButton();
                itsButton->setText( _T( "Display a message" ) );
                itsButton->onClicked( & HelloWinClass::buttonClicked );

                itsCheckBox = itsSingle->createCheckBox();
                itsCheckBox->setText( _T( "&Check me" ) );
                itsCheckBox->onClicked( & HelloWinClass::checkclicked );

                itsTextBoxSource = itsSingle->createTextBox( textBoxCS );
                SmartUtil::tstring filedata;
                FetchFile( _T( "hello.cpp" ), filedata );
                itsTextBoxSource->setText( filedata );
                onRightMouseDown( & HelloWinClass::PopupMenuViaRightClick );

                itsSpinner = itsSingle->createSpinner();
                itsSpinner->setRange( 0, 10 );
                itsSpinner->setValue( 10 );
                itsSpinner->onScrollVert( & HelloWinClass::spinVert );

                itsHorizSlider = itsSingle->createSlider();
                itsHorizSlider->setAutoTicks( true );
                itsHorizSlider->setHorizontal( true );
                itsHorizSlider->setShowTicksBoth( true );
                itsHorizSlider->setRange( 0, 100 );
                itsHorizSlider->setPosition( 100 );
                itsHorizSlider->setTickFrequency( 10 );
                itsHorizSlider->onScrollHorz( & HelloWinClass::scrollHoriz );

                itsVertSlider = itsSingle->createSlider();
                itsVertSlider->setAutoTicks( true );
                itsVertSlider->setHorizontal( false );
                itsVertSlider->setShowTicksBoth( true );
                itsVertSlider->setRange( 0, 100 );
                itsVertSlider->setPosition( 100 );
                itsVertSlider->setTickFrequency( 10 );
                itsVertSlider->onScrollVert( & HelloWinClass::scrollVert );
        }

        void createMultiItems()
        {
                TCHAR * cols[] =
                { _T( "Alpha" ), _T( "Beta" ), _T( "Charlie" )
                };
                TCHAR * labels[] =
                { _T( "Zero" ), _T( "One" ), _T( "Two" )
                };
                int labN = sizeof( labels ) / sizeof( char * );
                int colN = sizeof( cols ) / sizeof( char * );

                itsRadioGroupBox = itsMulti->createGroupBox();
                itsRadioGroupBox->setText( _T( "Radio buttons" ) );
                for ( int i = 0; i < labN; i++ )
                {
                        itsRadios[ i ] = createRadioButton( itsRadioGroupBox );
                        itsRadios[ i ] ->setText( labels[ i ] );
                        itsRadios[ i ] ->onClicked( & HelloWinClass::radioClicked );
                }

                // Combo Boxes
                itsComboBox = itsMulti->createComboBox();
                for ( int i = 0; i < labN; i++ )
                {
                        itsComboBox->addValue( labels[ i ] );
                }
                itsComboBox->setSelectedIndex( 2 );
                itsComboBox->onSelectionChanged( & HelloWinClass::selectionChanged );

                // List or grid Widget
                itsList = itsMulti->createDataGrid();
                std::vector< SmartUtil::tstring > columns;
                for ( int i = 0; i < colN; i++ ) columns.push_back( cols[ i ] );
                itsList->createColumns( columns );

                std::vector< SmartUtil::tstring > values;
                for ( int i = 0; i < labN; i++ ) values.push_back( labels[ i ] );
                itsList->insertRow( values );
                itsList->insertRow( values );

                // Creating tree view
                itsTree = itsMulti->createTreeView();
                itsTree->setHasButtons( true );
                itsTree->setHasLines( true );
                itsTree->setLinesAtRoot( true );
                itsTree->setTrackSelect( true );
                itsTree->setEditLabels();
                itsTree->onSelectionChanged( & HelloWinClass::treeNodeSelChanged );

                // Inserting items into tree view
                TreeViewNode root = itsTree->insertNode( _T( "Root" ), TreeViewNode(), static_cast< unsigned int >( - 1 ) );
                for ( int i = 0; i < colN; i++ ) itsTree->insertNode( labels[ i ], root, i );
        }

        void initAndCreate()
        {
                createWindow();
                setText( _T( "Hello SmartWin" ) ); // Title

                SmartWin::Rectangle desktop( getDesktopSize() );
                desktop.pos.y += 25;
                desktop.size.y -= 25;
                setBounds( desktop.top( 0.95 ) ); // or maximize();

                itsGraphicsPart = 0.25;
                itsColor = 0x808000; // Defaulting to yellow...
                itsEllipseSize = Point( 100, 100 );

                // Creating main menu
                itsMainMenu = createMenu();
                WidgetMenuPtr file = itsMainMenu->appendPopup( _T( "&File" ) );
                int m = 1;
                file->appendItem( m++, _T( "Choose &font" ), & HelloWinClass::menuChooseFont );
                file->appendItem( m++, _T( "&Copy text selection" ), & HelloWinClass::copyTextToClipboard );

                // Creating a right click pop-up menu
                itsPopupMenu = file->appendPopup( _T( "&Graphics" ) );
                itsPopupMenu->appendItem( m++, _T( "&Choose color" ), & HelloWinClass::menuChooseColor );

                file->appendItem( m++, _T( "&Exit" ), & HelloWinClass::menuCloseEvent );
#ifndef WINCE
                itsMainMenu->attach( this ); // Attaching main menu to main window
#endif

                itsTabSheet = createTabSheet();
                itsTabSheet->setHotTrack();
                itsTabSheet->setButtonStyle( false );
                itsTabSheet->setTabsAtBottom( false );
                itsTabSheet->addPage( _T( "Simple" ), SINGLE_WIDGETS );
                itsTabSheet->addPage( _T( "Complex" ), MULTI_WIDGETS );
                itsTabSheet->onSelectionChanged( & HelloWinClass::tabSelectionChanged );
                itsTabSheet->setSelectedIndex( SINGLE_WIDGETS );

                itsMulti = createWidgetChildWindow();
                createMultiItems();
                itsSingle = createWidgetChildWindow();
                createSingleItems();

                onPainting( & HelloWinClass::painting );

                layout(); // for the first time, and only AFTER all Widgets are created.
                onSized( & HelloWinClass::isResized );

                // Since tab sheet will hide underlaying controls we need to position it at the BOTTOM of the Z-order window chain
                ::SetWindowPos( itsTabSheet->handle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
                updateWidget();
        }

        void isResized( const WidgetSizedEventResult & sz )
        {
                layout(); // to reflect the change in window size by the user.
                updateWidget(); // Tell painting part that it needs to redraw.
        }

        void tabSelectionChanged( WidgetTabSheetPtr sheet )
        {
                layout(); // according to the new tab selection.
        }

        // Common routine to do layout the first time, and after each resize event.
        // We divide the available space into:   Tabbed Area | Graphics Area
        //
        void layout()
        {
                SmartWin::Rectangle r( getClientAreaSize() );

                r = r.left( 1.0 - itsGraphicsPart ); // The non-graphics side.
                itsTabSheet->setBounds( r );

                Point tabLabelSize = itsTabSheet->getTextSize( _T( "Complex" ) );
                tabLabelSize.x = 0; // Only concerned with the label's height
                tabLabelSize.y += 10;
                r = r.upperLeftAdjust( tabLabelSize ); // Tab labels
                r = r.shrinkWidth( 5 ); // , and have a x border

                itsSingle->setBounds( r );
                itsMulti->setBounds( r );

                // Coordinates for Widgets inside child windows are zero based.
                r = SmartWin::Rectangle( r.size );

                enum widg_page page = ( widg_page ) itsTabSheet->getSelectedIndex();
                layout_single( SINGLE_WIDGETS == page, r );
                layout_multi( MULTI_WIDGETS == page, r );
        }

        // The scheme is:
        //
        //  itsPromptA     itsTextA  \
        //  itsPromptB     itsTextB  /  Top 30%
        //
        //  button slider checkbox   >  Bottom 65%
        //
        // We achieve the layout using a combination of
        // Rectangle functions and the Place class.
        //
        void layout_single( bool isVisible, SmartWin::Rectangle rect )
        {
                if ( isVisible )
                {
                        SmartWin::Rectangle highrect( rect.top( 0.3 ) );
                        SmartWin::Rectangle lowrect( rect.bottom( 0.65 ) );

                        Place p;
                        p.setBoundsBorders( highrect, 4, 4 ); // X, Y border between Widgets

                        int rows = 2, cols = 2; // For sizing purposes, we divide into a grid.

                        // The first row.
                        itsPromptA->setSizeAsGridPerPlace( p, rows, cols );
                        itsTextA->setSizeAsGridPerPlace( p, rows, cols );

                        // The second row.
                        // The Place class notices that we have reached the end of a row and starts the next one.
                        itsPromptB->setSizeAsGridPerPlace( p, rows, cols );
                        itsTextB->setSizeAsGridPerPlace( p, rows, cols );

                        // The idea above was to use all the space available
                        // and expand the Widgets to fit into the space.

                        // For the Widgets below, we will use fixed sizes,
                        // and use the Place class to position them
                        // one after another according to thier sizes.
                        //
                        // Place::positionToRight( Rectangle r ) works like this:
                        //  It sets r.pos to the current position that traverses the Rectangle
                        //  set in Place::setBoundsBorders().  Then it adjusts the current
                        //  position taking into account the r.size and the borders.
                        //  If it needs to, it will move to a new "line" of Widgets.
                        //
                        p.setBoundsBorders( lowrect, 2, 4 ); // X, Y border between Widgets
                        SmartWin::Rectangle pos_size;

                        // Fixed sizes.
                        pos_size.size = Point( 30, 50 );
                        p.positionToRight( pos_size ); // Sets the pos_size.pos, and adjusts for pos_size.size.
                        itsSpinner->setBounds( pos_size ); // The spinner's size will be 30 by 50 pixels

                        pos_size.size = Point( 40, 50 );
                        p.positionToRight( pos_size );
                        itsVertSlider->setBounds( pos_size );

                        pos_size.size = Point( 70, 50 );
                        p.positionToRight( pos_size );
                        itsHorizSlider->setBounds( pos_size );

                        // These two will have enough size to fit their text in the current font.
                        itsCheckBox->setSizePerTextPerPlace( p, itsCheckBox->getText(), 40, 4 );
                        itsButton->setSizePerTextPerPlace( p, itsButton->getText(), 40, 4 );

                        p.newRow();
                        p.sizeForRemainingSpace( pos_size );
                        itsTextBoxSource->setBounds( pos_size );
                }

                itsSingle->setVisible( isVisible );
        }

        void layout_multi( bool isVisible, SmartWin::Rectangle rect )
        {
                if ( isVisible )
                {
                        Place p;
                        p.setBoundsBorders( rect, 6, 8 ); // X, Y border between Widgets

                        int rows = 2, cols = 3; // For sizing purposes, we divide into a grid.
                        itsComboBox->setSizeAsGridPerPlace( p, rows, cols );
                        itsTree->setSizeAsGridPerPlace( p, rows, cols );

                        SmartWin::Rectangle radiorect;
                        p.sizeOfCell( rows, cols, radiorect.size );
                        p.positionToRight( radiorect );
                        itsRadioGroupBox->setBounds( radiorect );
                        Point groupsize = itsRadioGroupBox->getTextSize( itsRadioGroupBox->getText() );

                        int radiorows = ITEMS;
                        int rowdex;
                        SmartWin::Rectangle radio1inside( radiorect.size ); // Start at 0,0 again.
                        radio1inside = radio1inside.upperLeftAdjust( Point( 0, groupsize.y ) ); // Allow room for Group label
                        radio1inside = radio1inside.shrink( 4, 4 );
                        rowdex = 0;
                        for ( int rowdex = 0; rowdex < ITEMS; rowdex++ )
                        {
                                itsRadios[ rowdex ] ->setSizeAsCol( radio1inside, radiorows, rowdex );
                        }

                        // List Grid takes the rest ...
                        SmartWin::Rectangle ListGridRect;
                        p.sizeForRemainingSpace( ListGridRect );
                        itsList->setBounds( ListGridRect );
                }

                itsMulti->setVisible( isVisible );
        }

        void painting( Canvas & pc )
        {
                paint( pc );
        }

        void paint( Canvas & c )
        {
                Pen pen( c, itsColor );
                Brush brush( c, itsColor );
                TextPen textpen( c, itsColor );
                COLORREF background;

                SmartWin::Rectangle rc( getClientAreaSize() ); // Establish 0,0 based SmartWin::Rectangle
                SmartWin::Rectangle area( rc.right( itsGraphicsPart ) ); // Use the Graphics side.
                area.shrink( 5, 3 ); // Allocate a border in pixels
                background = c.getSysColor( COLOR_BTNFACE ); //     c.getBkColor();

                int r = 0, rows = 5; // A column of graphics elements

                SmartWin::Rectangle texArea( area.row( r++, rows ) );
                c.setBkColor( background );
                c.extTextOut( _T( "Hello Graphical World" ), texArea.pos.x, texArea.pos.y );

                SmartWin::Rectangle ellipseArea( area.row( r++, rows ) );
                {
                        Pen backpen( c, background );
                        Brush backbrush( c, background );
                        c.rectangle( ellipseArea ); // Always erase the ellipse area first.
                }

                ellipseArea.size.x = ( ellipseArea.size.x * itsEllipseSize.x ) / 100;
                ellipseArea.size.y = ( ellipseArea.size.y * itsEllipseSize.y ) / 100;
                c.ellipse( ellipseArea );

                c.line( area.row( r++, rows ) );
                {
                        TextPen purplepen( c, 0x800080 );
                        c.drawText( _T( "Hello Purple World" ), area.row( r++, rows ), DT_SINGLELINE | DT_CENTER | DT_VCENTER );
                } // purplepen is now out of scope, so we revert to old color.

                c.rectangle( area.row( r++, rows ) );
        }
};

// The main creates an instance of the class and then starts the SmartWin application.
//
int SmartWinMain( Application & app )
{
        HelloWinClass * testHello = new HelloWinClass;
        testHello->initAndCreate();
        return app.run();
}

This program compiles into an executable of 268 KB without any extra DLLs needed.

helloSmartWin.jpg
Back to SmartWin website
SourceForge.net Logo