[slightly OT]3DSMax & wxWidgets

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

Hi!
Altough the code worked, there where some GUI quirks.
I coded a simple test that displays 25 tabs and simple event handling. It works perfectly well.

Code: Select all

BEGIN_EVENT_TABLE(myDialogWithTabs ,wxDialog)
	EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY,myDialogWithTabs::OnPageChanged)
END_EVENT_TABLE()

myDialogWithTabs ::myDialogWithTabs ( class Interface * const inter,wxWindow * parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style )
    : wxDialog( parent, id, title, position, size, style | wxCAPTION | wxRESIZE_BORDER | wxCLIP_CHILDREN | wxSYSTEM_MENU | wxTHICK_FRAME | wxCLOSE_BOX)
        , m_interface(inter)
{
    // TODO: Enter construction code here
        this->CreateMainInterface(this);
		SetSize(size);
        Centre();
}

myDialogWithTabs ::~myDialogWithTabs ()
{
    // TODO: Enter destruction code here
	//Actually there's none needed, as when the parent is deleted, so will be its children.
}
void myDialogWithTabs ::CreateMainInterface(wxWindow *parent)
{
	wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
	m_mainNoteBook = new wxNotebook(parent, wxID_ANY,wxDefaultPosition,wxDefaultSize, wxFULL_REPAINT_ON_RESIZE | wxNB_TOP | wxNB_MULTILINE);


	for(int i=0; i < 25; i++)
	{	wxPanel *panel = new wxPanel(m_mainNoteBook, wxID_ANY);
		m_mainNoteBook->AddPage(panel, wxString::Format("Tab %d",i+1));
		panel->SetBackgroundColour(wxColour(0,i*10,0));
	}
	

	mainSizer->Add(m_mainNoteBook,1,wxEXPAND,0);

	parent->SetSizer( mainSizer );      // use the sizer for layout
	mainSizer->Fit(parent);
    mainSizer->SetSizeHints( parent );
	parent->Layout();
}

void myDialogWithTabs::OnPageChanged(wxNotebookEvent& e)
{
	wxMessageBox(wxString::Format("selection changed from %d to %d.",
				e.GetOldSelection()+1,
				e.GetSelection()+1));
}
Note that I added:
1. mainSizer->Fit(parent);
2. parent->Layout();
3. SetSize(size). (after creating the interface).

The last step is needed as the GUI will "shrink" when using the sizer, so you'll have to resize again.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

I forgot the screenshot to show that I'm not lying :)
Image
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
emeakyl
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Nov 17, 2005 10:49 am

Post by emeakyl »

I didn't say you lied, but because you can think I am lying :), here is the screenshot of your code in 3DSMax:
Image
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

I didn't think you were lying. But I thought that this is so weird that it works using a simple test host and fails when using a "real" host.

Hmmm.... This really is weird...
Some thoughts:
1. Did you try with wxNB_NOPAGETHEME?
2. Did you try not disabling the main window (the dummy)?
3. Did you try with a NULL parent for the dialog?

I wish I had 3DS Max here and could try that myself...
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
emeakyl
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Nov 17, 2005 10:49 am

Post by emeakyl »

none of this suggestions worked; however, as I explained, changing the gs_wndprocNotebook value did the trick...
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

Hmm..
This still is weird. Why should it be changed?

Anyway: I'm sorry that I couldn't help you make it work. If changing the wndproc works, go with that. I can't think of anything else.

Again: Sorry.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
emeakyl
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Nov 17, 2005 10:49 am

Post by emeakyl »

no problem, believe me, your help was *really* usefull!

However, my problem isn't over :cry: : if I compare the wxDLL and 3DSMax wndproc, and change the 3DSMax's one, it works: but I don't know how to find this address in an other way. (Hardcoding? maybe the value is different on another system/computer).
emeakyl
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Nov 17, 2005 10:49 am

Post by emeakyl »

by the way, here is how I think it is happening: 3DSMax changes the way SysTabCtrl is working, in order (maybe) to extend it. wxWidgets uses the control, but in way that is corrupting the 3DSMax tabs behaviour (another plugin, written in win32 and using tabs, has no problem with tem).
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

Ok, I guess I found something that might be helpfull (plus I think I found a bug).

When wxNotebook is created, the following code will be executed if USE_NOTEBOOK_ANTIFLICKER was defined.
(src\msw\notebook.cpp, line 300)

Code: Select all

if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )	
    {
        static ClassRegistrar s_clsNotebook;
        if ( !s_clsNotebook.IsInitialized() )
        {
            // get a copy of standard class and modify it
            WNDCLASS wc;

            if ( ::GetClassInfo(NULL, WC_TABCONTROL, &wc) )
            {
                gs_wndprocNotebook =
                    wx_reinterpret_cast(WXFARPROC, wc.lpfnWndProc);
                wc.lpszClassName = wxT("_wx_SysTabCtl32");
                wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
                wc.hInstance = wxGetInstance();
                wc.lpfnWndProc = wxNotebookWndProc;
                s_clsNotebook.Register(wc);
            }
            else
            {
                wxLogLastError(_T("GetClassInfoEx(SysTabCtl32)"));
            }
        }

        // use our custom class if available but fall back to the standard
        // notebook if we failed to register it
        if ( s_clsNotebook.IsRegistered() )
        {
            // it's ok to use c_str() here as the static s_clsNotebook object
            // has sufficiently long lifetime
            className = s_clsNotebook.GetName().c_str();
        }
    }
So it creates a new WNDCLASS and registers it. It will be used later when creating the actual control. Note the line

Code: Select all

 wc.lpfnWndProc = wxNotebookWndProc;
I found this before, that's why I suggested using wxFULL_REPAINT_ON_RESIZE, as this shouldn't create the new WNDCLASS. But, funny story, when debugging I found that the class will be created regardless if wxFULL_REPAINT_ON_RESIZE was set or not.
The bug is that HasFlag() will check the internal variable m_windowStyle of wxWindow with the given flag. But in that case m_windowStyle hasn't been set yet.

There are two possible solutions to try:
1. add the line m_windowStyle = style; right before the HasFlag() check.
2. disable USE_NOTEBOOK_ANTIFLICKER.

To set the flag, open the core project, add USE_NOTEBOOK_ANTIFLICKER=0 and recompile.

The other solution should be added as a patch.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
emeakyl
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Nov 17, 2005 10:49 am

Post by emeakyl »

Hi!

upCASE, what you suggested failed in my case... However, I have an interesting result.

When the DLL is loaded, I take the WNDPROC address for the tabs class and keep it for later use (in the wndproc global variable).

I have created this class:

Code: Select all

class MyClass : public wxNotebook
{
public: 
    MyClass( wxWindow *parent, wxWindowID id = -1, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0 ) 
    : wxNotebook(parent, id, pos, size, style)
	{
	} 
    virtual ~MyClass()
	{
	}

	void OnPaint(wxPaintEvent &evt)
	{
		/* Don't uncomment me! 
		{ wxPaintDC dc(this); }*/
		
		::CallWindowProc((WNDPROC)wndproc, (HWND)this->GetHandle(), WM_PAINT, 
			(WPARAM)::GetWindowDC((HWND)this->GetHandle()), 0);

	}

private: 
    DECLARE_EVENT_TABLE() 
};

BEGIN_EVENT_TABLE(MyClass,wxNotebook)
	EVT_PAINT(MyClass::OnPaint)
END_EVENT_TABLE()
If I run the program like this, I can see the tabs... however, the background isn't drawn (which is logical). If I uncomment the commented line( or replace it by just "wxPaintDC dc(this);"), the tabs are not drawn anymore. So I think that wxPaintDC modifiies the DC in a way 3DSMax doesn't like... Any idea?
emeakyl
Earned a small fee
Earned a small fee
Posts: 14
Joined: Thu Nov 17, 2005 10:49 am

Post by emeakyl »

Ok, it is working now... Here is how (and why). 3DSMax *always* redraws in the wxPaintDC (it has it by calling the BeginPaint function). The notebook implementation first draws the widget in a wxMemoryDC, the put it in the wxPaintDC... Of course, with 3DSMax, this has for effect to "skip" the drawing of the tabs.

So here is my solution: create a new class, heriting from wxNotebook. (there is a slight flicking, but it looks good, and the code can be optimized)

Code: Select all

class myNotebook : public wxNotebook
{
public: 
    myNotebook( wxWindow *parent, wxWindowID id = -1,
        const wxPoint& pos = wxDefaultPosition,
        const wxSize& size = wxDefaultSize,
		long style = 0 ) : wxNotebook(parent, id, pos, size, style)
	{
	} 
    virtual ~myNotebook()
	{
	}

	void OnPaint(wxPaintEvent &evt)
	{
		{
			HDC hDC = ::GetDC((HWND)this->GetHandle());
			RECT rc;
			::GetClientRect(GetHwnd(), &rc);

			// if there is no special brush just use the solid background colour
			HBRUSH hbr = (HBRUSH)m_hbrBackground;
			wxBrush brush;
			if ( !hbr )
			{
				brush = wxBrush(GetBackgroundColour());
				hbr = GetHbrushOf(brush);
			}

			::FillRect(hDC, &rc, hbr);

			::ReleaseDC((HWND)this->GetHandle(), hDC);
			// Needed, else the widget isn't draw
			InvalidateRect((HWND)this->GetHandle(),NULL,TRUE);
		}
		
		::CallWindowProc((WNDPROC)wndprocNotebook, (HWND)this->GetHandle(), WM_PAINT, 
			(WPARAM)::GetWindowDC((HWND)this->GetHandle()), 0);
	}
private: 
    DECLARE_EVENT_TABLE() 
};

BEGIN_EVENT_TABLE(myNotebook,wxNotebook)
	EVT_PAINT(myNotebook::OnPaint)
END_EVENT_TABLE()
Thanks to those who helped me!
Post Reply