Introduction
The application is realized using the Qt framework. Qt provides you with all the functionality needed to develop advanced GUI applications on desktop and embedded platforms. Qt uses the native graphics APIs of each platform it supports, taking full advantage of system resources and ensuring that applications have the native look and feel. Using Qt is very easy and intuitive and, in my opinion, is the next best thing after Microsoft .NET Framework for Windows based applications. It has the advantage that the same code can be compiled for many platforms (Windows, Linux, Mac OS), is written entirely in C++ (some projects may require only C++), and is free (you can choose from LGPL/GPL/Commercial license).
Background
While working, I find it very useful to have a calendar at hand. I have tried to use some existing applications, but none could display the calendar week number. So I decided to have my own calendar gadget. The idea was to be able to use my gadget also on Windows XP. If you want to start the program together with Windows, just create a shortcut to your Startup folder.
Using the Code
In order to compile the code, you need to have the Qt framework and the Qt Visual Studio Add-in (in case you compile using Visual Studio). You can download them from http://qt.nokia.com. Visual Studio 2008 or QtCreator 2.0.1 can be used to compile the code.
The Qt framework is compiled without SSL support. This is required in order to establish a Google connection and to get the authentication code for the calendar service. OpenSLL is the best choice since it is free. You can get a precompiled version from http://www.openssl.org/related/binaries.html.
To make the application installer, I used InnoSetup. This is an Open Source project used for creating Windows installers. You can download this from http://www.jrsoftware.org/isinfo.php.
Points of Interest
The Gadget
class offers the basic functionality of a gadget: a nice frame, some fade in/out animation, and the most important feature is that it keeps the window on the desktop when you choose “Show desktop”.
Normally, when the user chooses to show the desktop, all windows are hidden. Unfortunately, there is no flag or style to avoid this behavior, and after a few hours of documentation on the net, I found the way to do this. All that is required is to have a global event hook by providing a callback function.
Keep the ID for each object.
HWINEVENTHOOK globalHookId;
Create the event hook when the actual window is shown.
globalHookId = SetWinEventHook(EVENT_SYSTEM_FOREGROUND,
EVENT_SYSTEM_FOREGROUND, NULL,
(WINEVENTPROC)HandleWinEvent,0,0,0);
Remove the event hook from the system when the object is no longer required (in the destructor).
UnhookWinEvent(globalHookId);
HandleWinEvent
is a global function that will loop through all the existing gadgets on the desktop and show them.
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
LONG idObject, LONG idChild, DWORD dwEventThread,
DWORD dwmsEventTime)
{
if (event == EVENT_SYSTEM_FOREGROUND)
{
TCHAR tmp[255];
int r = GetClassName(hwnd, tmp, 250);
if (wcscmp(tmp, L"WorkerW")==0)
{
Gadget::assureVisibility();
}
}
}
The CalendarService
class is used to get all-day-events from your Google Calendar using the Google Calendar API. This is done in three steps: obtain an authentication code using the user name and password, get a session ID, query the calendar for events:
- For the authentication, a post needs to be set to "https://www.google.com/accounts/ClientLogin", and in return, the auth code will be received. This is kept and used for all other requests. The authentication part is done in
AuthService
. Details about the login can be read on http://code.google.com/intl/en/apis/gdata/docs/auth/overview.html#ClientLogin. - For the authentication, a post needs to be set to "https://www.google.com/accounts/ClientLogin", and in return, the auth code will be received. This is kept and used for all other requests. The authentication part is done in
AuthService
. - Trying to get the data directly will not work because all requests will redirected. Because of this, a get request must be performed at "http://www.google.com/calendar/feeds/default/private/full" using "X-If-No-Redirect" set to "1" in order to get a session ID. "X-Redirect-Location" will be read from the reply. Details about the Calendar API can be read on http://code.google.com/intl/en/apis/calendar/data/2.0/reference.html.
QNetworkRequest request = QNetworkRequest();
...
request.setRawHeader("X-If-No-Redirect", "1");
- In order to limit the results, a query is made using a start time and an end time. The start and end are the values represented by the first and last date shown in the calendar.
QUrl address("http://www.google.com/calendar/feeds/default/private/full");
address.addQueryItem("gsessionid", session);
address.addQueryItem("start-min",
QString("%1T00:00:00").arg(newStartDate.toString("yyyy-MM-dd")));
address.addQueryItem("start-max",
QString("%1T23:59:59").arg(newEndDate.toString("yyyy-MM-dd")));
I wanted to start the application together with Windows so the password is also saved in the application settings. I did not want to save the password in plain text, so I used the Windows Crypto API to do a simple RC4 encryption. This is by no means safe, but it protects my password from simple view. Here is the basic workflow (check out the full implementation in the Settings
class):
HCRYPTPROV CryptoProv = NULL;
if( !CryptAcquireContext(&CryptoProv, NULL, NULL, PROV_RSA_FULL, 0))
if (!CryptAcquireContext(&CryptoProv, NULL, NULL,
PROV_RSA_FULL, CRYPT_NEWKEYSET))
if (!CryptAcquireContext(&CryptoProv, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
return "";
HCRYPTKEY ExponentOfOneKey = NULL;
CryptImportKey(CryptoProv, PrivateKeyWithExponentOfOne,
sizeof(PrivateKeyWithExponentOfOne), 0, 0, &ExponentOfOneKey);
HCRYPTKEY RC4Key = NULL;
DWORD cbBlob = sizeof(RC4KeyBlock);
CryptImportKey(CryptoProv, RC4KeyBlock, cbBlob, ExponentOfOneKey, 0, &RC4Key);
CryptEncrypt( RC4Key, 0, 1, 0, data, &length, length);
CryptDestroyKey(RC4Key);
CryptDestroyKey(ExponentOfOneKey);
CryptReleaseContext(CryptoProv,0);
History
- Version 1.0.1
- Version 1.0.2
- Gadget windows stay on top after "Show desktop"
- Version 1.0.3
- Fixed email link in about
- Version 1.0.4
- Version 1.0.5
- Show all day events from Google Calendar
- Version 1.0.6
- Upgraded to Qt 4.7.0
- ConnectDialog refactored
- Button refactored using the state machine mechanism
- Password remembered in Settings.ini
- Setup added
- Version 1.0.7
- HTTP replaced with QNetworkManager
- Proxy
- Animations added
- Code comments
My programming experience starts from 1998 in High School. I have graduated “Politehnica” University of Timisoara and worked as a developer since 2005. Currently I work as a C/C++ developer in the automotive business.
I’m interested in windows/linux based development using different technologies(.Net, MFC, Qt) and signal processing algorithms.