SWIFTuser Studios

A small game development studio that works on sciency application too

MFC Multi-selection CTreeCtrl

January 15
by anegri 15. January 2014 08:38

Multi select enable tree control

 

Header for custom derived control


class CTreeCtrlX : public CTreeCtrl
{

protected:
    HTREEITEM    m_hItemFirstSel;        // Init to NULL in constructor
    DWORD        m_dwDragStart;
   
    void ClearSelection();
    BOOL SelectItems(HTREEITEM hItemFrom, HTREEITEM hItemTo);
    HTREEITEM GetFirstSelectedItem();
    HTREEITEM GetNextSelectedItem( HTREEITEM hItem );
    HTREEITEM GetPrevSelectedItem( HTREEITEM hItem );

public:
    DECLARE_MESSAGE_MAP()
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
};

 

Implementation Source


BEGIN_MESSAGE_MAP(CTreeCtrlX, CTreeCtrl)
    ON_WM_LBUTTONDOWN()
    ON_WM_KEYDOWN()
END_MESSAGE_MAP()

void CTreeCtrlX::OnLButtonDown(UINT nFlags, CPoint point)
{
    // Set focus to control if key strokes are needed.
    // Focus is not automatically given to control on lbuttondown
 
    m_dwDragStart = GetTickCount();
 
    if(nFlags & MK_CONTROL )
    {
        // Control key is down
        UINT flag;
        HTREEITEM hItem = HitTest( point, &flag );
        if( hItem )
        {
            // Toggle selection state
            UINT uNewSelState =
                GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED ?
                            0 : TVIS_SELECTED;
           
            // Get old selected (focus) item and state
            HTREEITEM hItemOld = GetSelectedItem();
            UINT uOldSelState  = hItemOld ?
                    GetItemState(hItemOld, TVIS_SELECTED) : 0;
           
            // Select new item
            if( GetSelectedItem() == hItem )
                SelectItem( NULL );        // to prevent edit
            CTreeCtrl::OnLButtonDown(nFlags, point);
 
            // Set proper selection (highlight) state for new item
            SetItemState(hItem, uNewSelState,  TVIS_SELECTED);
 
            // Restore state of old selected item
            if (hItemOld && hItemOld != hItem)
                SetItemState(hItemOld, uOldSelState, TVIS_SELECTED);
 
            m_hItemFirstSel = NULL;
 
            return;
        }
    }
    else if(nFlags & MK_SHIFT)
    {
        // Shift key is down
        UINT flag;
        HTREEITEM hItem = HitTest( point, &flag );
 
        // Initialize the reference item if this is the first shift selection
        if( !m_hItemFirstSel )
            m_hItemFirstSel = GetSelectedItem();
 
        // Select new item
        if( GetSelectedItem() == hItem )
            SelectItem( NULL );            // to prevent edit
        CTreeCtrl::OnLButtonDown(nFlags, point);
 
        if( m_hItemFirstSel )
        {
            SelectItems( m_hItemFirstSel, hItem );
            return;
        }
    }
    else
    {
        // Normal - remove all selection and let default
        // handler do the rest
        ClearSelection();
        m_hItemFirstSel = NULL;
    }
 
    CTreeCtrl::OnLButtonDown(nFlags, point);
}

void CTreeCtrlX::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if ( (nChar==VK_UP || nChar==VK_DOWN) && GetKeyState( VK_SHIFT )&0x8000)
    {
        // Initialize the reference item if this is the first shift selection
        if( !m_hItemFirstSel )
        {
            m_hItemFirstSel = GetSelectedItem();
            ClearSelection();
        }
 
        // Find which item is currently selected
        HTREEITEM hItemPrevSel = GetSelectedItem();
 
        HTREEITEM hItemNext;
        if ( nChar==VK_UP )
            hItemNext = GetPrevVisibleItem( hItemPrevSel );
        else
            hItemNext = GetNextVisibleItem( hItemPrevSel );
 
        if ( hItemNext )
        {
            // Determine if we need to reselect previously selected item
            BOOL bReselect =
                !( GetItemState( hItemNext, TVIS_SELECTED ) & TVIS_SELECTED );
 
            // Select the next item - this will also deselect the previous item
            SelectItem( hItemNext );
 
            // Reselect the previously selected item
            if ( bReselect )
                SetItemState( hItemPrevSel, TVIS_SELECTED, TVIS_SELECTED );
        }
        return;
    }
    else if( nChar >= VK_SPACE )
    {
        m_hItemFirstSel = NULL;
        ClearSelection();
    }
    CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CTreeCtrlX::ClearSelection()
{
    // This can be time consuming for very large trees
    // and is called every time the user does a normal selection
    // If performance is an issue, it may be better to maintain
    // a list of selected items
    for ( HTREEITEM hItem=GetRootItem(); hItem!=NULL; hItem=GetNextItem( hItem, TVGN_NEXT ) )
        if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
            SetItemState( hItem, 0, TVIS_SELECTED );
}

// SelectItems    - Selects items from hItemFrom to hItemTo. Does not
//        - select child item if parent is collapsed. Removes
//        - selection from all other items
// hItemFrom    - item to start selecting from
// hItemTo    - item to end selection at.
BOOL CTreeCtrlX::SelectItems(HTREEITEM hItemFrom, HTREEITEM hItemTo)
{
    HTREEITEM hItem = GetRootItem();
 
    // Clear selection upto the first item
    while ( hItem && hItem!=hItemFrom && hItem!=hItemTo )
    {
        hItem = GetNextVisibleItem( hItem );
        SetItemState( hItem, 0, TVIS_SELECTED );
    }
 
    if ( !hItem )
        return FALSE;    // Item is not visible
 
    SelectItem( hItemTo );
 
    // Rearrange hItemFrom and hItemTo so that hItemFirst is at top
    if( hItem == hItemTo )
    {
        hItemTo = hItemFrom;
        hItemFrom = hItem;
    }
 
 
    // Go through remaining visible items
    BOOL bSelect = TRUE;
    while ( hItem )
    {
        // Select or remove selection depending on whether item
        // is still within the range.
        SetItemState( hItem, bSelect ? TVIS_SELECTED : 0, TVIS_SELECTED );
 
        // Do we need to start removing items from selection
        if( hItem == hItemTo )
            bSelect = FALSE;
 
        hItem = GetNextVisibleItem( hItem );
    }
 
    return TRUE;
}

HTREEITEM CTreeCtrlX::GetFirstSelectedItem()
{
    for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_NEXT ) )
        if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
            return hItem;
 
    return NULL;
}
 
HTREEITEM CTreeCtrlX::GetNextSelectedItem( HTREEITEM hItem )
{
    for ( hItem = GetNextItem( hItem, TVGN_NEXT ); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_NEXT ) )
        if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
            return hItem;
 
    return NULL;
}
 
HTREEITEM CTreeCtrlX::GetPrevSelectedItem( HTREEITEM hItem )
{
    for ( hItem = GetNextItem( hItem, TVGN_PREVIOUS ); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_PREVIOUS ) )
        if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
            return hItem;
 
    return NULL;
}

 

based from this article http://www.codeguru.com/cpp/controls/treeview/misc-advanced/article.php/c723/Allowing-multiple-selection.htm

CMFCToolBar with Checked Button

January 14
by anegri 14. January 2014 00:13

Working on an internal tool and little things creep up.  Here is an easy way to add your own CMFCToolBar to an MFC SDI/MDI application.  Step by step:

 

  1. Insert Toolbar, in the Resource View of VS 2008.
  2. Draw you button.
  3. Give it a unique ID_TEST
  4. Add a string to the String Table:

    IDS_TOOLBAR_TEST => "This is a test" 
  5. To the header file of your choice (for this project it was my MainFrm.h) add a reference to the toold bar and a variable to hold its state:

        CMFCToolBar m_wndToolBarTest;
        BOOL m_blToolBarTestState[1]; 
  6. Add the message map functions:

        afx_msg void OnTest();
        afx_msg void OnUpdateTest(CCmdUI* pCmdUI); 
  7. On the message map of the source file (MainFrm.cpp) add the following messages:

        ON_COMMAND(ID_TEST, &CMainFrame::OnTest)
        ON_UPDATE_COMMAND_UI(ID_TEST, &CMainFrame::OnUpdateTest) 
  8. On the OnCreate method create your toolbar:
        if (!m_wndToolBarTest.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
            !m_wndToolBarTest.LoadToolBar(theApp.m_bHiColorIcons ? IDR_ALIGN_256 : IDR_ALIGN))
        {
            TRACE0("Failed to create test toolbar\n");
            return -1;      // fail to create
        }

        BOOL bNameValid = strToolBarName.LoadString(IDS_TOOLBAR_TEST);
        ASSERT(bNameValid);   
        m_wndToolBarTest.SetWindowText(strToolBarName);
        ...
        m_wndToolBarTest.EnableDocking(CBRS_ALIGN_ANY);
        EnableDocking(CBRS_ALIGN_ANY);
        DockPane(&m_wndToolBarTest);
  9. Initialize array somwhere either on the constructor or in the create method:
    m_blToolBarTestState[0] = FALSE;
  10. Implement the button command:

    void CMainFrame::OnTest()
    {
        m_blToolBarTestState[0] = !m_blToolBarTestState[0];
  11. Implement the update command ui:

    void CMainFrame:: OnUpdateTest(CCmdUI* pCmdUI)
    {
        pCmdUI-> SetCheck(m_blToolBarTestState[0]);
    }

And we are done... hope it helps anyone that works on MFC and needs a step by step guide on this!

Dealing with CORS/cross domain security exception

December 26
by anegri 26. December 2013 23:21

During an integration process with a web service using AngularJS and Chrome as a browser I tried almost every different way to consume a JSON web service.  What work best was to consume a web serivce through an angular service as follows:

 


'use strict';

 

myApp.factory('WebSrv', ['$http', '$timeout', function($http, $timeout) {

 

  return {

    get: function(callback) {

      $http.get('http://some-json-service.com',

        {transformResponse:function(data) {

 

           // transform becomes in handy 

            console.log("'" + data + "'");

            if (data != undefined && data != null && data != "") {

              return JSON.parse(data);

            }

            return [];

          }

        }

      )

      .success(function(data, status) {

        callback(data);

      })

      .error(function (data, status) {

        console.log('Service could not be reached ' + status);

      })

    }

  }

 

}]);


 

Once you have a proper connection to the web service and you are getting the CORS issue there are only three ways to fix it:

 

  1. Use a proxy from your server that reads the webservice and then feeds your web app the result (handle the errors on your side).
  2. Have the service use the right headers, this can be difficult as some developers will not modify their web serices with the requried headers, and will mention how their service works fine when they consume it on their own web apps hosted on the same server.
  3. Modify the target of the Chrome shurtcut to be as follows: "C:\<path to executable" --args --disable-web-service

The thrid option is not recomended and only good for special cases when testing, but you should really do 1 or 2 (if possible).

 

 

 

JSON CPP

March 25
by anegri 25. March 2013 20:37

Currently I started migrating level files from XML to JSON and decided to use JSONCPP.  I made a build with Qt Creator and figured I would share the projcet on the 0.6 build.

https://dl.dropbox.com/u/101523245/jsoncpp.rar

 

Behavior Trees

August 31
by anegri 31. August 2012 05:53

I have been working on implementing a behavior tree for driving the story in a scripted manner and have been following what has been done by Joost, Dustin Watson, Christian Werler and Alex Champandard for a project in my full time job.  It has been a rough week, but I finally finished a basic prototype using SDL and C++!  There is still a lot more to do, but I am hoping it all works out. 

X-Plane Plugin

August 02
by anegri 2. August 2012 17:58

Last six months have been very busy working at my real job developing a plug-in for X-Plane.  I have been working on creating a training scenario that borrows a lot of elements from game development, it has been great since I am back on C++ development!  I am incorporating second generation behavior trees for the training scenarios and have a great sate maching to load different parts of the training (from the splash screen to menus and pilot training).  I also just finished incorporating a speech API similar to dragon naturally speaking to incorporate dialog trees within the application... so far so good on this.

Also I have stopped using haXe/NME for mobile development... I grew very frustrated with the compiling tools for the WEB OS devices not supporting version 1.4.5, the textures would not load and it was very hard to find a working stable version.  I liked the simplicity of the language, testing in Flash was great too! It seemed that I was wasting too much time trying to get things to work, so I dropped the languange and the dev environment.  I did switched to WP7 and have been loving using the XNA platform on it and will be focusing on that platform, I also tried developing on android as well (hated the emulator and the fragmented versions/hardware)... I really wished a new phone came out with WebOS, but that is probably not going to happen.

C++ Lightweight Logger

December 19
by anegri 19. December 2011 07:01

While working on a COM object for a small project I required a lightweight logger framework for C++... looking around took me to the Dr. Dobbs article for creating your own custom multithreaded logger.  After digging a bit deeper I found some unwelcomed dependencies on other third party tools, some more digging took me to logog an AWESOME lightweight C++ logger optimized for games! Out of the box it requires CMake, but with very little elbow grease I have it working with QMake!  

AT&T Mobile App Hackathon

November 18
by anegri 18. November 2011 00:23

I am attending the AT&T Mobile App Hackathon in Boston Saturday 11/19/2011 and hopefully getting some good feedback on the games I have so far halfway done in haXe along with some time to port Missile Defender to haXe.


Tags:

Giving back to the community

September 28
by anegri 28. September 2011 20:49

Today I posted the UndoPureMVC component I ported from AS3 to Java (you can download it from the link, make sure you are registered on the PureMVC forums).  I have been using it already on two different projects and figured it would be good to give back, I have been using the Java version of PureMVC already for 4 different projects and it has been working really well.

Quick Status Update

April 28
by anegri 28. April 2011 20:52

I have been experimenting more with haXe and have created a couple of small games based on it working on the palm-pre.  I have not posted much in a while, sales on Missile Defender were a lot lower than I expected.  However I did get good suggestions for the next update of the game thanks to my feedback option.  The lag on Missile Defender seem to discourage some players, at this point I am sure I will re-write it on haXe for the next release and port it over to Android and iOS.