generic app class to be instantiated... Topic is solved

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
mac
Earned some good credits
Earned some good credits
Posts: 103
Joined: Sat Jul 22, 2006 3:15 am

generic app class to be instantiated...

Post by mac » Sun Oct 26, 2008 3:14 am

Hi, there are many ways to get to the same place and this is just how I get there currently I hope it helps somebody! :) Let me know if you see errors or typos thanks.

So, along with my GUI classes that DialogBlocks made I will use a generic wx app class for things I think all my apps will need and I'll instantiate that to a MyApp class for the app specific stuff for each application. Here is the generic class I wrote so far.

features:
-keeps track of it's version, name, vendor's name
-single instance check (win)
-its rect info, and reads/writes to config)
-check for update
-something I call IsOrphaned
(I use multiple monitors and I've seen some peoples apps not respond well if I switch my screens around or disconnect one. it's possbile to Hide an app! I don't want my apps to ever have that happen to them!)
- Show About....but this is usually going to be overridden (I had as virtual but it will most likely be the last thing I do for each app and it's really easy to pop up a dialog)



AppEngine.h

Code: Select all

/*
	AppEngine.h
        - base class I will derive my specific app's class from with wx
	version: 0.0.6
	created: 10/9/08
        last updated: 10/15/08
	**********
	
	** I prefer to use these conventions:	functions/methods and such... CamelCase
variables/member vars. and such... lower_case_with_underscores
note: above is only for variables I care about...other ones (usually
local ones actually) I usually name as anything like: i,j,s,r,t
*/
#include <wx/wx.h>
#include <wx/snglinst.h>        // wxSingleInstanceChecker
#include <wx/config.h>			// wxConfigBase
#include <wx/tokenzr.h>			// wxStringTokenizer
#include <wx/protocol/http.h>   // wxHTTP
//#include <wx/url.h>				// wxURL::GetError()
#include <wx/sstream.h>			// wxInputStream
#include <wx/display.h>			// wxDisplay

class AppEngine
{
protected:
	static wxString version;
	static wxString vendor_name;
	static wxString app_name;
	wxSingleInstanceChecker* there_can_be_only_one;
	wxRect last_known_rect;
public:
	AppEngine();
	AppEngine(wxString);
    virtual ~AppEngine();

	inline wxString GetVersion() const { return version; }    // returns version string
    wxString SetVersion(wxString);  // sets new version and returns last version
	bool OneInstance();     // call & check in wxApp's OnInit()...if false exit!
	
	inline wxString GetVendorName() const { return vendor_name; }
	wxString SetVendorName(wxString); // for use with wxGetApp().SetVendorName(_T("My Software"));
	
	inline wxString GetAppName() const { return app_name; }
	wxString SetAppName(wxString);
	
	wxRect SetCurrentRect(wxRect);		// for use in wxConfig etc.
	wxRect GetCurrentRect(wxWindow* win);
	inline wxRect GetLastKnownRect() const { return last_known_rect; }
	
	bool WriteConfig(wxWindow*);	// returns true on error
	bool ReadConfig(wxWindow*, int* x, int* y, int* w, int* h);	// returns true if fails
	
	bool CheckForUpdates(wxString domain, int port, wxString page, wxString* MAJOR, wxString* MINOR, wxString* PATCH, wxString UserAgent = _T("CheckForUpdates v.0.0.1"));	// return true on error
	
	bool IsOrphaned(wxWindow*);		// return true if it was orphaned..tries to fix it automatically
	
	void ShowAbout(){};
};


AppEngine.cpp

Code: Select all

/*
	AppEngine.cpp
       - base class I will derive my specific app's class from with wx
	version: 0.0.6
	*****************
*/
#include "AppEngine.h"

wxString AppEngine::version = _T("");     // static version string initiallized to ""
wxString AppEngine::vendor_name = _T(""); 
wxString AppEngine::app_name = _T(""); 

AppEngine::AppEngine()
{
}

AppEngine::AppEngine(wxString v)
{
    //version = v;  // instead...doing in derived class so gcc doesn't complain
}

AppEngine::~AppEngine()
{
}

wxString AppEngine::SetVersion(wxString new_version)
{
    wxString old_version = this->version;
    this->version = new_version;
    
    return old_version;
}

bool AppEngine::OneInstance()
{
#ifndef __WXMAC__
    // check for single instance
    const wxString appname = wxString::Format(version, wxGetUserId().c_str());  // just needs to be unique
    there_can_be_only_one = new wxSingleInstanceChecker(appname);
    
    if ( there_can_be_only_one->IsAnotherRunning() )
    {
        // comment below out normally
        //wxMessageBox(_T("There can be only one!"));
        
        //if another tries to open, check if it returns false in OnInit() and close!
        return false;
    }
#endif
    return true;
}

wxString AppEngine::SetVendorName(wxString vn)
{
	vendor_name = vn;
	
	return vendor_name;
}

wxString AppEngine::SetAppName(wxString an)
{
	app_name = an;
	
	return app_name;
}

wxRect AppEngine::GetCurrentRect(wxWindow* win)
{
	// note: just calling win->GetRect() wasn't correct on vista 64bit anyways...this fixes it
	// using the wxWindow yet actually using a wxFrame wasn't taking it the decoration and menu into account
	wxRect r;
	win->GetPosition(&r.x, &r.y);
	win->GetClientSize(&r.width, &r.height);

	return r;
}

wxRect AppEngine::SetCurrentRect(wxRect r)
{
	last_known_rect = r;
	
	return last_known_rect;
}

bool AppEngine::WriteConfig(wxWindow* w)
{
	// save normal config stuff like x,y,w,h, etc..
	wxConfigBase* config = wxConfigBase::Get();
	
	if (config != NULL)
	{
		wxRect window = GetCurrentRect(w);
		
		config->Write(_T("/rect/x"), window.x);
		config->Write(_T("/rect/y"), window.y);
		config->Write(_T("/rect/w"), window.width);
		config->Write(_T("/rect/h"), window.height);
		
		return false;
	}
	else
	{
		return true;
	}
}

bool AppEngine::ReadConfig(wxWindow* win, int* x, int* y, int* w, int* h)
{
	// read in saved config and read values into pointer params
	// if it doesn't exist...create it with values given from GetCurrentRect()
	wxConfigBase* config = wxConfigBase::Get();
	
	if (config == NULL)
		return true;
		
	wxRect window = GetCurrentRect(win);
	
	*x = config->Read(_T("/rect/x"), window.x);
	*y = config->Read(_T("/rect/y"), window.y);
	*w = config->Read(_T("/rect/w"), window.width);
	*h = config->Read(_T("/rect/h"), window.height);
	
	// got to add IsMaximized!
	
	return false;
}

bool AppEngine::CheckForUpdates(wxString domain, int port, wxString page, wxString* MAJOR, wxString* MINOR, wxString* PATCH, wxString UserAgent)
{
    /*
	check for updates
	call a webserver's page   ie. latest.php
	and get a value like version string for comparison
       the php script sends back one line like so: 1.0.1
	
	example php:
	<?php
		// for check updates function
		
		// normally you'd want to do stuff like keep track or how many or from where etc...
		$remote_ip = $_SERVER['REMOTE_ADDR'];
		$remote_hostname = gethostbyaddr($_SERVER['REMOTE_ADDR']);
		$remote_browser = $_SERVER['HTTP_USER_AGENT'];
		
		// bare minimum
		$latest_version = "1.0.1";
		echo "$latest_version";
	?>
	
	note: you could also just use a plain text file that isn't dynamic that has: 1.0.6
	*/
	
	wxHTTP http;
	wxString update_domain = domain;
	http.SetHeader(_T("User-Agent"), UserAgent);
	
	// note: this version may block the gui while it tries to connect...
	// maybe redo this one day with WaitOnConnect??...this works for me atm
	if (! http.Connect(update_domain, port) )
	{
		return true;	// return error
	}
	
	wxInputStream* in = http.GetInputStream(page);
	wxChar c;
	wxString version;
	
	int RFC2616 = http.GetResponse();
	//wxMessageBox( wxString::Format(_T("%d"),RFC2616) );
	
	if (RFC2616 < 200 || RFC2616 > 300)		// we're usually hoping for a 200 =)
		return true;
		
	while (! in->Eof())
	{
		c = in->GetC();
		version << c;
		
		if (version.Len() > 31)	// only read the first 30 characters no matter what
			break;
	}
	
	version.Trim(false);	// left
	version.Trim();			// default right
	
	if ( version.IsEmpty() || version.Len() < 5 || version.Len() > 31 )
		return true;	// weird unlikely version string
	
	// see if it has atleast 3 '.'
	wxChar delimeter = '.';
	if (version.Freq(delimeter) < 2)
		return true;	//error!
	
	wxStringTokenizer parse(version, _T("."));
	
	for (int i =0; i < 3; i++)
    {
        wxString str = parse.GetNextToken();

        if( i == 0)
            *MAJOR = str;	// decided to send back as wxString instead of ints like my prototype did... wxAtoi(str);
        
        if( i == 1)
            *MINOR = str;
        
        if( i == 2)
            *PATCH = str;
    }
	
	return false;
}

bool AppEngine::IsOrphaned(wxWindow* win)
{
    // NOTE: not working correctly in mac just tested really quickly and doesnt do check right correctly fix later!!!
    
	// for people who use multiple screens windows can get orphaned if their screens change between last open
	// if it is orphaned I'm going to tryand move it back to a visible area
	unsigned int display_total = wxDisplay::GetCount();
	signed int max_left = 0;
    signed int max_right = 0;
    signed int max_top = 0;
    signed int max_bottom = 0;	// had to explicitly sign the ints!!
	signed int primary_x, primary_y = 0;	// on non windows multiscreen this won't necessarily be 0,0
	int fix = 12;
	bool was_orphaned = false;
	
	max_right = 0;
	
	for (unsigned int i=0; i < display_total; i++)
	{
		wxDisplay display(i);
		
		wxRect r( display.GetGeometry() );
		
		if (display.IsPrimary())
		{
			//main = i;
			primary_x = r.GetX();
			primary_y = r.GetY();
		}
		
		if (r.x < max_left)
		{
			max_left = r.x;	// find most left
		}
		
		{
			max_right += r.width;	// totalling for now
		}
		
		if (r.y < max_top)
		{
			max_top = r.y;		// find most top
		}
		
		if (r.height > max_bottom)
		{
			max_bottom = r.GetHeight();
		}
		
		//wxMessageBox(wxString::Format(_T("r.GetWidth(): %d\nr.GetHeight(): %d"), r.GetWidth(), r.GetHeight() ) );
		//wxMessageBox( wxString::Format(_T("display: %d, x: %d, y: %d, width: %d, height: %d\n"), i, r.GetX(), r.GetY(), r.GetWidth(), r.GetHeight()) );
	}

	max_right -= max_left;
	// fix if orphaned just want user to be able to find window..not make it look spectacular
	// they ill move it themselves after we put it back to a visible state :D
	wxRect current_screen = GetCurrentRect(win);
	if (current_screen.GetX() > (max_right - (current_screen.GetWidth() - fix)) )	// if past right edge
	{
		win->Move(max_right - current_screen.GetWidth(), current_screen.GetY());
		was_orphaned = true;
		current_screen = GetCurrentRect(win);
	}
	
	if (current_screen.GetX() < (max_left - fix))	// if past left edge
	{
		win->Move(max_left, current_screen.GetY());
		was_orphaned = true;
		current_screen = GetCurrentRect(win);
	}
	
	if (current_screen.GetY() > max_bottom - (current_screen.GetHeight() + fix))	// if past bottom edge
	{
		win->Move(current_screen.GetX(), max_bottom - (current_screen.GetHeight() + fix));
		was_orphaned = true;
		current_screen = GetCurrentRect(win);
	}
	
	if (current_screen.GetY() < max_top - fix)	// if past top edge
	{
		win->Move(current_screen.GetX(), max_top);
		was_orphaned = true;
	}
	
	//wxMessageBox(wxString::Format(_T("max_right = %d\nmax_bottom = %d\nmax_top = %d\nmax_left = %d"),max_right, max_bottom, max_top, max_left));
	return was_orphaned;
}
Remember you have to now create your own MyApp class (or whatever you name it) and inherit AppEngine and include "AppEngine.h" I've put added a member to my wxApp class of type: MyApp* my_app

so, from then on I'm referring to it as my_app in wxApp or wxGetApp().my_app in wxFame .... just you so understand the next samples :p


and from the GUI wxApp & wxFrame class here are some samples of how to use/call them.

in the app class OnInit()

Code: Select all

// ----------- my app setup stuff  ------

    //my_app = new MyApp();
    //my_app->SetVersion(_T("0.0.1"));
    my_app = new MyApp( _T("0.0.2") );   // quickerthan above
    
    // comment out below line if you want to allow multiple instances of this app
    if(! my_app->OneInstance() ) { return false; }

    
// ------------ end my app ---------------
oh yeah, don't forget to delete the my_app in the OnExit() if you are also using a 'MyApp* my_app'

inside MyFrame constructor:

Code: Select all

    // ======= more config stuff ===================
    
    wxGetApp().SetVendorName( wxGetApp().my_app->SetVendorName(_T("My Software, Inc")) );        // name of company... comment out to just use app name
    wxGetApp().SetAppName( wxGetApp().my_app->SetAppName(_T("AppEngine Class Demo")) );               // name of app

    
    wxRect r;
    
    if (! wxGetApp().my_app->ReadConfig(this, &r.x, &r.y, &r.width, &r.height) )
    {
        // restore
        Move(r.x, r.y); 
        SetClientSize(r.width, r.height); 
    }
    
    bool check = wxGetApp().my_app->IsOrphaned(this);
    


    // =============================================
in the ~MyFrame():

Code: Select all

    wxGetApp().my_app->WriteConfig(this);

more samples:

Code: Select all

void MyFrame::OnMenuitemGetVersionDemoClick( wxCommandEvent& event )
{
    //wxGetApp().my_app->SetVersion(_T("0.0.1"));
    
    out->AppendText( wxGetApp().my_app->GetVersion() + _T("\n"));
}

Code: Select all

void MyFrame::OnMenuRectInfoClick( wxCommandEvent& event )
{
    //set and then show frame's X, Y and width and height
    long x,y,w,h;
    
    // below gets this windows wxRect and also saves it in my_app class for use with wxConfig and stuff
    wxRect tmp = wxGetApp().my_app->SetCurrentRect( wxGetApp().my_app->GetCurrentRect(this) );    // setting and returning at same time
    
    // note: above code is a quicker form of below
    /*
    wxRect a = this->GetRect();   // explicit this
    wxGetApp().my_app->SetCurrentRect( a );
    wxRect tmp = wxGetApp().my_app->GetLastKnownRect();
    */
    
    x = tmp.x;
    y = tmp.y;
    w = tmp.width;
    h = tmp.height;
    
    wxString s;
    
    out->AppendText(s.Format(_T("this window's coords:\nx: %d\ny: %d\nwidth: %d\nheight: %d\n"), x, y, w, h));

}

Code: Select all

void MyFrame::OnMenuCheckForUpdatesClick( wxCommandEvent& event )
{
    wxString major, minor, patch;
    
	if ( wxGetApp().my_app->CheckForUpdates(_T("www.lifeisweird.org"), 80, _T("/updates/latest.php"), &major, &minor, &patch) )
    {
        // error
        out->AppendText( _T("Error trying to check for updates.\n") );
    }
    else
    {
        // here I could compare versions and you know
        // but I'll just echo the major, minor, patch I get from server...
        wxString latest_version;
        
        latest_version << major << _T(" , ") << minor << _T(" , ") << patch;
        out->AppendText( _T("Latest version is: ") + latest_version + _T("\n") );
    }
}

Code: Select all

void MyFrame::OnMenuOrphanCheckClick( wxCommandEvent& event )
{

    bool check = wxGetApp().my_app->IsOrphaned(this);
    
    if (!check)
        out->AppendText(_T("this app isn't orphaned.\n"));
}
I'm working on my MyApp class already so I will only update/change AppEngine later or if I find a huge bug or find I need to add something. I do know there is a bug in the IsOrphaned on Mac...it can still be orphaned to the very right of the screens (I may or not fix that later though) also the CheckUpdates is fragile and won't act right if the url is wrong! and I've only tested all this on Vista 64bit and OS X 10.4! =) So, I'm hoping there aren't too many edits I'll have to do on all the lower win32s and OS X Panther...
I'm not even sure I will make apps for GTK+2 now because I now realize how much friggin work it is just for MSW & MAC!! =/ Almost each of these functions took me 1-3 hours. I probably will though unless it's a lot more work...

Anyways, this is what I use ATM and I hope that makes sense to somebody and helps ^_^
Last edited by mac on Sun Oct 26, 2008 5:33 am, edited 4 times in total.
vista 64bit, OS X 10.4.x, OS X 10.3.9 x 2
(virtualization: vista 32bit, MS XP, MS95, MS98, Debian 3.1, Slackware 12, FreeBSD 6.1, a few more)

wx: 2.8.8/9 (unicode)
compilers: gcc (GTK+2, OS X), vc++6(MSW)

mac
Earned some good credits
Earned some good credits
Posts: 103
Joined: Sat Jul 22, 2006 3:15 am

Re: generic app class to be instantiated...

Post by mac » Sun Oct 26, 2008 3:18 am

I don't know why this form is messing up my code >:/ Its shifting some stuff and putting less or more tabs/spaces where I have it all neat...
vista 64bit, OS X 10.4.x, OS X 10.3.9 x 2
(virtualization: vista 32bit, MS XP, MS95, MS98, Debian 3.1, Slackware 12, FreeBSD 6.1, a few more)

wx: 2.8.8/9 (unicode)
compilers: gcc (GTK+2, OS X), vc++6(MSW)

maninthewind
In need of some credit
In need of some credit
Posts: 6
Joined: Tue Nov 18, 2008 11:19 pm

Post by maninthewind » Thu Feb 05, 2009 11:49 am

thanks mac.
good job.
:)

Post Reply