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;
}
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 ---------------
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);
// =============================================
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 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 ^_^