Version 1 of Tk frame in MFC CFrameWnd

Updated 2006-05-04 21:39:33

http://www.tfh-berlin.de/~s716193/pics/TkinMFC.jpg [ author: Sascha Leuthold ]


Comments are welcome considering implementation-details and discussions on the subject itselfs (widget embedding).



TkinMFC.tcl

        set buttonTkcon [button .panel.buttonTkcon \
                                -text "tkcon" -height 3 -width 20 \
                                -command {source C:/home/TkinMfC/tcl/tkcon.tcl}]

        pack $buttonTkcon -padx 5 -pady 5 -side left -anchor n

        set frameAdd [frame .panel.frameAdd -relief sunken -bd 2]

        pack $frameAdd -padx 5 -pady 5 -side left -expand true -fill both
  • note that I have to use .panel, this widget-name is fixed in C++
  • but except this, we have a complete Tk frame

Derive CWinApp to host the application/thread/event-loop

TkinMFC.h

        #ifndef TKINMFC_H
        #define TKINMFC_H

        #include "resource.h"
        #include "stdafx.h"


        class CTkinMfCApp : public CWinApp
        {
        public:
                tktinfo                        *tinfo_panel;
        private:
                Tcl_Interp                *interp;
                char                        **argv;
                int                        argc;

        private:
                bool        Tk_Initialize();
                void        ParseCmdLine(int* argc, char*** argv);

        public:
                CTkinMfCApp();


                //{{AFX_VIRTUAL(CTkinMfCApp)
                public:
                virtual int ExitInstance( );
                virtual BOOL OnIdle(LONG lCount);
                virtual BOOL InitInstance();
                //}}AFX_VIRTUAL

        public:
                //{{AFX_MSG(CTkinMfCApp)
                //}}AFX_MSG
                DECLARE_MESSAGE_MAP()
        };


        #endif // TKINMFC_H

TkinMFC.cpp

        #include "stdafx.h"
        #include "TkinMfC.h"
        #include "MainFrm.h"


        BEGIN_MESSAGE_MAP(CTkinMfCApp, CWinApp)
                //{{AFX_MSG_MAP(CTkinMfCApp)
                //}}AFX_MSG_MAP
        END_MESSAGE_MAP()


        CTkinMfCApp::CTkinMfCApp()
        {
                tinfo_panel = new tktinfo;
                tinfo_panel->tk_path = "";
        }

        CTkinMfCApp theApp;

        bool CTkinMfCApp::Tk_Initialize()
        {

                ParseCmdLine(&argc,&argv);

                Tcl_FindExecutable(argv[0]);

                interp = Tcl_CreateInterp(); 

                /*
  • argc, argv0 and argv
                 */
                char *args;
                Tcl_DString argString;
                char buf[TCL_INTEGER_SPACE];

                args = Tcl_Merge(argc, (CONST char **)argv);
                Tcl_ExternalToUtfDString(NULL, args, -1, &argString);
                Tcl_SetVar(interp, "argv", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);
                Tcl_DStringFree(&argString);
                ckfree(args);
                sprintf(buf, "%d", argc);
                Tcl_ExternalToUtfDString(NULL, argv[0], -1, &argString);
                Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY);
                Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);


                /*
  • Initialize packages
                 */
                if (Tcl_Init(interp) == TCL_ERROR) {
                        return TCL_ERROR;
                }
                if (Tk_Init(interp) == TCL_ERROR) {
                        return TCL_ERROR;
                }

                if (Tcl_Eval(interp, "wm withdraw .") != TCL_OK) {
                        return false;
                }

                CWnd* cwndPtr = AfxGetMainWnd( );
                CMainFrame* cmainPtr = static_cast<CMainFrame*>(cwndPtr);
                HWND DlgBar_hWnd = cmainPtr->m_wndDlgBar;

                char tkwin_panel[] = ".panel";
                tinfo_panel->tk_path = tkwin_panel;
                tinfo_panel->interp = interp;
                SetProp(DlgBar_hWnd, "tktinfo", static_cast<HANDLE>(tinfo_panel));

                char buffer[1000];
                _snprintf(buffer, sizeof(buffer), "toplevel %s -use 0x%x", tkwin_panel, DlgBar_hWnd);
                if (Tcl_Eval(interp, buffer) != TCL_OK) {
                        return false;
                }

                Tk_Window tkmain_panel = Tk_MainWindow(interp);
                tinfo_panel->tktkwin = Tk_NameToWindow(interp, static_cast<char *>(tkwin_panel), tkmain_panel);
                RECT topsz_panel;
                GetClientRect(DlgBar_hWnd, &topsz_panel);
                Tk_MoveResizeWindow(tinfo_panel->tktkwin, 0, 0, topsz_panel.right, topsz_panel.bottom);

                int i;
                _snprintf(buffer, sizeof(buffer), "winfo id %s", tinfo_panel->tk_path);
                Tcl_Eval(interp, buffer);
                Tcl_GetInt(interp, interp->result, &i);
                tinfo_panel->tkhwnd = reinterpret_cast<HWND>(i);

                Tcl_Eval(interp, "source [file join C:/ home TkinMfC tcl TkinMfC.tcl]");

                Tcl_PkgProvide(interp, "tkinmfc", "1.1");

                return true;
        }

        BOOL CTkinMfCApp::InitInstance()
        {
                SetRegistryKey(_T("Local AppWizard-Generated Applications"));


                CMainFrame* pFrame = new CMainFrame;
                m_pMainWnd = pFrame;


                pFrame->LoadFrame(IDR_MAINFRAME);


                pFrame->ShowWindow(SW_SHOW);
                pFrame->UpdateWindow();

                if (!Tk_Initialize())
                        return FALSE;

                return TRUE;
        }

        int CTkinMfCApp::ExitInstance( )
        {
                delete tinfo_panel;

                CWinApp::ExitInstance();

                return 0;
        }

        BOOL CTkinMfCApp::OnIdle(LONG lCount)
        {

                while(Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT))
                {
                }
                return 0;
        }

        void CTkinMfCApp::ParseCmdLine(int* argcPtr, char*** argvPtr)
        {

        /*
         * tclAppInit.c --
         *
         *        Provides a default version of the main program and Tcl_AppInit
         *        procedure for Tcl applications (without Tk).  Note that this
         *        program must be built in Win32 console mode to work properly.
         *
         * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
         * Copyright (c) 1998-1999 by Scriptics Corporation.
         *
         * See the file "license.terms" for information on usage and redistribution
         * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
         *
         * RCS: @(#) $Id: 15861,v 1.2 2006-05-05 06:00:32 jcw Exp $

        static void
        setargv(argcPtr, argvPtr)
                int *argcPtr;                Filled with number of argument strings.
                char ***argvPtr;        Filled with argument strings (malloc'd). )

        Code from tcl-distribution
        */

                char *cmdLine, *p, *arg, *argSpace;
                char **argv;
                int argc, size, inquote, copy, slashes;

                /*
  • Set argv0 to *.exe
                 */
                char buffer[MAX_PATH+1];
                GetModuleFileName(m_hInstance, buffer, sizeof(buffer));

                for (p = buffer; *p != '\0'; p++) {
                        if (*p == '\\') {
                                *p = '/';
                        }
                }

                for (p = buffer; ; p++) {
                        if (*p == '\0') {
                                *p = ' ';

                                /*
  • Append the command line options
                                 */
                                for (int i = 0; ; i++) {
                                        if ( m_lpCmdLine[i] == '\0')
                                                break;
                                        p++;
                                        *p = m_lpCmdLine[i];
                                }
                                p++;
                                *p = '\0';
                                break;

                        }

                }

                cmdLine = buffer;

                /*
  • Precompute an overly pessimistic guess at the number of arguments
  • in the command line by counting non-space spans.
                 */

                size = 2;
                for (p = cmdLine; *p != '\0'; p++) {
                if ((*p == ' ') || (*p == '\t')) {        /* INTL: ISO space. */
                        size++;
                        while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
                        p++;
                        }
                        if (*p == '\0') {
                        break;
                        }
                }
                }
                argSpace = (char *) Tcl_Alloc(
                        (unsigned) (size * sizeof(char *) + strlen(cmdLine) + 1));
                argv = (char **) argSpace;
                argSpace += size * sizeof(char *);
                size--;

                p = cmdLine;
                for (argc = 0; argc < size; argc++) {
                argv[argc] = arg = argSpace;
                while ((*p == ' ') || (*p == '\t')) {        /* INTL: ISO space. */
                        p++;
                }
                if (*p == '\0') {
                        break;
                }

                inquote = 0;
                slashes = 0;
                while (1) {
                        copy = 1;
                        while (*p == '\\') {
                        slashes++;
                        p++;
                        }
                        if (*p == '"') {
                        if ((slashes & 1) == 0) {
                                copy = 0;
                                if ((inquote) && (p[1] == '"')) {
                                p++;
                                copy = 1;
                                } else {
                                inquote = !inquote;
                                }
                                        }
                                        slashes >>= 1;
                                }

                                while (slashes) {
                        *arg = '\\';
                        arg++;
                        slashes--;
                        }

                        if ((*p == '\0')
                                || (!inquote && ((*p == ' ') || (*p == '\t')))) { /* INTL: ISO space. */
                        break;
                        }
                        if (copy != 0) {
                        *arg = *p;
                        arg++;
                        }
                        p++;
                        }
                *arg = '\0';
                argSpace = arg + 1;
                }
                argv[argc] = NULL;


                *argcPtr = argc;
                *argvPtr = argv;

        } 

Derive CFrameWnd

MainFrame.h

        #ifndef MAINFRM_H
        #define MAINFRM_H


        class CMainFrame : public CFrameWnd
        {

        public:
                CMainFrame();
        protected: 
                DECLARE_DYNAMIC(CMainFrame)

        public:
                virtual ~CMainFrame();

                CDialogBar  m_wndDlgBar;

        protected:
                //{{AFX_MSG(CMainFrame)
                afx_msg int         OnCreate(LPCREATESTRUCT lpCreateStruct);
                afx_msg void OnPaint();
                afx_msg void OnSize(UINT nType, int cx, int cy);
                //}}AFX_MSG
                DECLARE_MESSAGE_MAP()
        };

        #endif // MAINFRM_H

MainFrame.cpp

        #include "stdafx.h"
        #include "TkinMfC.h"
        #include "MainFrm.h"

        IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)

        BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
                //{{AFX_MSG_MAP(CMainFrame)
                ON_WM_CREATE()
                ON_WM_PAINT()
                ON_WM_SIZE()
                //}}AFX_MSG_MAP
        END_MESSAGE_MAP()

        CMainFrame::CMainFrame()
        {
        }

        CMainFrame::~CMainFrame()
        {
        }

        int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
        {
                if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
                        return -1;

                if (!m_wndDlgBar.Create(this, IDD_DIALOG1,
                                CBRS_BOTTOM|CBRS_TOOLTIPS|CBRS_FLYBY, IDD_DIALOG1))
                        return -1;

                return 0;
        }

        void CMainFrame::OnSize(UINT nType, int cx, int cy)
        {
                CFrameWnd::OnSize(nType, cx, cy);

                CRect rect;
                m_wndDlgBar.GetClientRect(&rect);
                rect.right = cx;

                rect.top = cy - rect.bottom;
                rect.bottom = cy;

                m_wndDlgBar.MoveWindow(rect);

                CTkinMfCApp* appPtr = static_cast<CTkinMfCApp*>(AfxGetApp());
                if(appPtr->tinfo_panel->tk_path != "")
                        Tk_MoveResizeWindow(appPtr->tinfo_panel->tktkwin, 0, 0, cx, rect.bottom - rect.top);

        }

        void CMainFrame::OnPaint() 
        {

                CPaintDC dc(this);
                CRect rect,rectTk;
                GetClientRect(&rect);
                m_wndDlgBar.GetClientRect(&rectTk);

                rect.bottom = rect.bottom - rectTk.bottom;

                dc.TextOut(rect.right/2-50,rect.bottom/2,"MFC Control");

                rect.left = 10;
                rect.top = 10;
                rect.bottom = rect.bottom-10;
                rect.right = rect.right-10;

                CBrush brush;
                brush.CreateSolidBrush(RGB(0,0,255));
                dc.FrameRect(&rect,&brush);
        }
  • OnSize is a little bit tricky, you have to resize the Tk frame accoring to the CDialog container

stdafx.h

        #ifndef STDAFX_H
        #define STDAFX_H

        #define VC_EXTRALEAN

        #include <afxwin.h> 
        #include <afxext.h>


        #define NO_CONST
        #include <tk.h>

        struct tktinfo {
                char *tk_path;
                HWND tkhwnd;
                Tk_Window tktkwin;
                Tcl_Interp *interp;
        };

        #endif // STDAFX_H

What do you think? Is it possible to achieve the same easier?


[ Category GUI | Category Windows | Category Example ]