/* The original version of this program can be found at http://damb.dk */
#define _WIN32_IE 0x0500
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <vector>
#include <fstream>
#include <iostream>
#include <istream>
HINSTANCE InstanceHandle;
HWND MainWindow;
HWND StatusBarWindow;
HWND ToolBarWindow;
COLORREF CurrentColor = RGB(255, 0, 0);
enum CurrentObjectType
{
LineObj,
RectangleObj
};
CurrentObjectType CurrentObject = LineObj;
char CurrentFileName[256];
enum MenuCommand
{
FileOpenCmd = 1000,
FileSaveCmd,
FileSaveBmpCmd,
FileCloseCmd,
DrawLineCmd,
DrawRectangleCmd,
DrawRedCmd,
DrawBlueCmd,
DrawGreenCmd,
AboutCmd
};
enum WindowId
{
StatusBarId = 128,
ToolBarId
};
class ObjectBaseClass
{
public:
virtual void SetEnd(HDC &aDc, POINT aEnd) = 0;
virtual void Draw(HDC &dc, bool aMoving = false) = 0;
virtual void Write(std::ostream &os) = 0;
virtual bool Read(std::istream &is) = 0;
};
class LineClass : public ObjectBaseClass
{
public:
LineClass(){}
LineClass(POINT aStart, COLORREF aColor) :
Start(aStart),
End(aStart),
Color(aColor),
First(true)
{}
void SetEnd(HDC &aDc, POINT aEnd)
{
Draw(aDc, true);
End = aEnd;
Draw(aDc, true);
}
void Draw(HDC &dc, bool aMoving = false);
void Write(std::ostream &os);
bool Read(std::istream &is);
private:
POINT Start;
POINT End;
COLORREF Color;
bool First;
};
class RectangleClass : public ObjectBaseClass
{
public:
RectangleClass(){}
RectangleClass(POINT aStart, COLORREF aColor) :
Start(aStart),
End(aStart),
Color(aColor),
First(true)
{}
void SetEnd(HDC &aDc, POINT aEnd)
{
Draw(aDc, true);
End = aEnd;
Draw(aDc, true);
}
void Draw(HDC &dc, bool aMoving = false);
void Write(std::ostream &os);
bool Read(std::istream &is);
private:
POINT Start;
POINT End;
COLORREF Color;
bool First;
};
std::ostream &operator << (std::ostream &os, const POINT aP)
{
os << aP.x << " " << aP.y;
return os;
}
std::istream &operator >> (std::istream &is, POINT &aP)
{
is >> aP.x;
is >> aP.y;
return is;
}
void LineClass::Draw(HDC &aDc, bool aMoving)
{
if(First)
{
First = false;
return;
}
if(aMoving)
{
SetROP2(aDc, R2_NOTXORPEN);
}
else
{
SetROP2(aDc, R2_COPYPEN);
}
HPEN Pen = CreatePen(PS_SOLID, 1, Color);
HPEN OldPen = (HPEN )SelectObject(aDc, Pen);
MoveToEx(aDc, Start.x, Start.y, 0);
LineTo(aDc, End.x, End.y);
SelectObject(aDc, OldPen);
DeleteObject(Pen);
}
void LineClass::Write(std::ostream &os)
{
os << LineObj << std::endl;
os << Start << std::endl;
os << End << std::endl;
os << Color << std::endl;
}
bool LineClass::Read(std::istream &is)
{
is >> Start;
is >> End;
is >> Color;
return is;
}
void RectangleClass::Write(std::ostream &os)
{
os << RectangleObj << std::endl;
os << Start << std::endl;
os << End << std::endl;
os << Color << std::endl;
}
bool RectangleClass::Read(std::istream &is)
{
is >> Start;
is >> End;
is >> Color;
return is;
}
void RectangleClass::Draw(HDC &aDc, bool aMoving)
{
if(First)
{
First = false;
return;
}
if(aMoving)
{
SetROP2(aDc, R2_NOTXORPEN);
}
else
{
SetROP2(aDc, R2_COPYPEN);
}
HPEN Pen = CreatePen(PS_SOLID, 1, Color);
HPEN OldPen = (HPEN )SelectObject(aDc, Pen);
MoveToEx(aDc, Start.x, Start.y, 0);
LineTo(aDc, End.x, Start.y);
LineTo(aDc, End.x, End.y);
LineTo(aDc, Start.x, End.y);
LineTo(aDc, Start.x, Start.y);
SelectObject(aDc, OldPen);
DeleteObject(Pen);
}
std::vector<ObjectBaseClass *>Objects;
bool IsDrawing;
void OnLButtonDown(HWND aHwnd, int aX, int aY)
{
POINT P;
P.x = aX;
P.y = aY;
ObjectBaseClass *Object;
if(CurrentObject == LineObj)
Object = new LineClass(P, CurrentColor);
else
Object = new RectangleClass(P, CurrentColor);
Objects.push_back(Object);
IsDrawing = true;
}
void OnMouseMove(HWND aHwnd, int aX, int aY)
{
if(IsDrawing)
{
POINT P;
P.x = aX;
P.y = aY;
HDC dc = GetDC(aHwnd);
ObjectBaseClass *Last = Objects.back();
Last->SetEnd(dc, P);
}
}
void OnLButtonUp(HWND aHwnd, int aX, int aY)
{
if(IsDrawing)
{
POINT P;
P.x = aX;
P.y = aY;
ObjectBaseClass *Last = Objects.back();
HDC dc = GetDC(aHwnd);
Last->SetEnd(dc, P);
Last->Draw(dc);
ReleaseDC(aHwnd, dc);
IsDrawing = false;
}
}
void OnPaint(HWND aHwnd)
{
PAINTSTRUCT PaintStruct;
BeginPaint(aHwnd, &PaintStruct);
size_t i;
for(i = 0; i < Objects.size(); i++)
Objects[i]->Draw(PaintStruct.hdc);
EndPaint(aHwnd, &PaintStruct);
}
void SetStatusBarWidths(HWND ParentWindow)
{
int BoxRight[3];
RECT ParentRect;
GetClientRect(ParentWindow, &ParentRect);
int Width = ParentRect.right - ParentRect.left;
BoxRight[0] = 100;
BoxRight[1] = Width - 100;
BoxRight[2] = -1;
SendMessage(StatusBarWindow, SB_SETPARTS, 3, (LPARAM )BoxRight);
}
void SetStatusBarText(const char *Text, WORD PartNumber)
{
SendMessage(StatusBarWindow, SB_SETTEXT, PartNumber, (LPARAM )Text);
}
bool CreateToolBar(HWND ParentWindow)
{
const int NumButton = 7;
HBITMAP ToolBarBitmap = (HBITMAP )LoadImage(0,
"toolbar.bmp",
IMAGE_BITMAP,
80,
16,
LR_LOADFROMFILE);
if(!ToolBarBitmap)
{
MessageBox(ParentWindow, "Failed to load toolbar bitmap!", "Whatever", MB_OK | MB_ICONEXCLAMATION);
return false;
}
ToolBarWindow = CreateWindowEx(0,
TOOLBARCLASSNAME,
0,
WS_CHILD|WS_BORDER,
0, 0, 0, 0,
ParentWindow,
(HMENU )ToolBarId,
InstanceHandle,
0);
SendMessage(ToolBarWindow,
TB_BUTTONSTRUCTSIZE,
(WPARAM )sizeof(TBBUTTON),
0);
TBADDBITMAP TBaddBitmap;
TBaddBitmap.hInst = 0;
TBaddBitmap.nID = (UINT )ToolBarBitmap;
SendMessage(ToolBarWindow,
TB_ADDBITMAP,
(WPARAM )NumButton,
(LPARAM )&TBaddBitmap);
TBBUTTON ButtonInfo[NumButton];
memset(ButtonInfo, 0, sizeof(ButtonInfo));
int idx;
idx = SendMessage(ToolBarWindow,
TB_ADDSTRING,
0,
(LPARAM )"");
ButtonInfo[0].iString = idx;
ButtonInfo[0].iBitmap = 0;
ButtonInfo[0].idCommand = 0;
ButtonInfo[0].fsState = TBSTATE_ENABLED;
ButtonInfo[0].fsStyle = BTNS_SEP;
idx = SendMessage(ToolBarWindow,
TB_ADDSTRING,
0,
(LPARAM )"Rectangle");
ButtonInfo[1].iString = idx;
ButtonInfo[1].iBitmap = 0;
ButtonInfo[1].idCommand = DrawRectangleCmd;
ButtonInfo[1].fsState = TBSTATE_ENABLED;
ButtonInfo[1].fsStyle = TBSTYLE_CHECKGROUP;
idx = SendMessage(ToolBarWindow,
TB_ADDSTRING,
0,
(LPARAM )"Line");
ButtonInfo[2].iString = idx;
ButtonInfo[2].iBitmap = 1;
ButtonInfo[2].idCommand = DrawLineCmd;
ButtonInfo[2].fsState = TBSTATE_ENABLED | TBSTATE_CHECKED;
ButtonInfo[2].fsStyle = TBSTYLE_CHECKGROUP;
idx = SendMessage(ToolBarWindow,
TB_ADDSTRING,
0,
(LPARAM )"");
ButtonInfo[3].iString = idx;
ButtonInfo[3].iBitmap = 0;
ButtonInfo[3].idCommand = 0;
ButtonInfo[3].fsState = TBSTATE_ENABLED;
ButtonInfo[3].fsStyle = BTNS_SEP;
idx = SendMessage(ToolBarWindow,
TB_ADDSTRING,
0,
(LPARAM )"Red");
ButtonInfo[4].iString = idx;
ButtonInfo[4].iBitmap = 2;
ButtonInfo[4].idCommand = DrawRedCmd;
ButtonInfo[4].fsState = TBSTATE_ENABLED | TBSTATE_CHECKED;
ButtonInfo[4].fsStyle = TBSTYLE_CHECKGROUP;
idx = SendMessage(ToolBarWindow,
TB_ADDSTRING,
0,
(LPARAM )"Green");
ButtonInfo[5].iString = idx;
ButtonInfo[5].iBitmap = 3;
ButtonInfo[5].idCommand = DrawGreenCmd;
ButtonInfo[5].fsState = TBSTATE_ENABLED;
ButtonInfo[5].fsStyle = TBSTYLE_CHECKGROUP;
idx = SendMessage(ToolBarWindow,
TB_ADDSTRING,
0,
(LPARAM )"Blue");
ButtonInfo[6].iString = idx;
ButtonInfo[6].iBitmap = 4;
ButtonInfo[6].idCommand = DrawBlueCmd;
ButtonInfo[6].fsState = TBSTATE_ENABLED;
ButtonInfo[6].fsStyle = TBSTYLE_CHECKGROUP;
SendMessage(ToolBarWindow,
TB_ADDBUTTONS,
NumButton,
(LPARAM)&ButtonInfo);
SendMessage(ToolBarWindow, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS);
ShowWindow(ToolBarWindow, SW_SHOW);
return true;
}
void OnSave()
{
static char Filter[] = "dat (*.dat)\0*.dat\0All (*.*)\0*.*\0";
OPENFILENAME OpenFilename;
memset(&OpenFilename, 0, sizeof(OpenFilename));
OpenFilename.lStructSize = sizeof(OpenFilename);
OpenFilename.hwndOwner = MainWindow;
OpenFilename.hInstance = InstanceHandle;
OpenFilename.lpstrFilter = Filter;
OpenFilename.lpstrFile = CurrentFileName;
OpenFilename.nMaxFile = sizeof(CurrentFileName);
OpenFilename.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
OpenFilename.lpstrDefExt = "dat";
if(GetSaveFileName(&OpenFilename))
{
std::ofstream File(CurrentFileName);
if(File)
{
size_t i;
for(i = 0; i < Objects.size(); i++)
{
Objects[i]->Write(File);
}
SetStatusBarText(CurrentFileName, 1);
}
else
{
std::string Msg = "Failed to open:\r\n";
Msg += CurrentFileName;
MessageBox(MainWindow, Msg.c_str(), "Whatever", MB_OK | MB_ICONWARNING);
}
}
}
void OnOpen()
{
size_t i;
for(i = 0; i < Objects.size(); i++)
delete Objects[i];
Objects.clear();
static char Filter[] = "dat (*.dat)\0*.dat\0All (*.*)\0*.*\0";
OPENFILENAME OpenFilename;
memset(&OpenFilename, 0, sizeof(OpenFilename));
OpenFilename.lStructSize = sizeof(OpenFilename);
OpenFilename.hwndOwner = MainWindow;
OpenFilename.hInstance = InstanceHandle;
OpenFilename.lpstrFilter = Filter;
OpenFilename.lpstrFile = CurrentFileName;
OpenFilename.nMaxFile = sizeof(CurrentFileName);
OpenFilename.Flags = OFN_FILEMUSTEXIST;
OpenFilename.lpstrDefExt = "dat";
if(GetOpenFileName(&OpenFilename))
{
std::ifstream File(CurrentFileName);
if(File)
{
int ObjType;
while(File >> ObjType)
{
ObjectBaseClass *Obj;
if(ObjType == LineObj)
Obj = new LineClass();
else if(ObjType == RectangleObj)
Obj = new RectangleClass;
else
MessageBox(MainWindow, "Unknown Object Type", "Whatever", MB_OK);
Obj->Read(File);
Objects.push_back(Obj);
}
SetStatusBarText(CurrentFileName, 1);
}
else
{
std::string Msg = "Failed to open:\r\n";
Msg += CurrentFileName;
MessageBox(MainWindow, Msg.c_str(), "Whatever", MB_OK | MB_ICONWARNING);
}
}
InvalidateRect(MainWindow, 0, TRUE);
}
BITMAPINFOHEADER WriteHeader(std::ostream &os, int aWidth, int aHeight, WORD aBitPixel)
{
BITMAPFILEHEADER BitmapFileHeader;
BITMAPINFOHEADER BitmapInfoHeader;
unsigned int WordsPerLine = (aWidth*aBitPixel + 31)/32;
BitmapFileHeader.bfType = (('M' << 8) | 'B');
BitmapFileHeader.bfSize = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + 4*WordsPerLine*aHeight;
BitmapFileHeader.bfReserved1 = 0;
BitmapFileHeader.bfReserved2 = 0;
BitmapFileHeader.bfOffBits = 0x36 + 8;
BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfoHeader.biWidth = aWidth;
BitmapInfoHeader.biHeight = aHeight;
BitmapInfoHeader.biPlanes = 1;
BitmapInfoHeader.biBitCount = aBitPixel;
BitmapInfoHeader.biCompression = BI_RGB;
BitmapInfoHeader.biSizeImage = 0;
BitmapInfoHeader.biXPelsPerMeter = 0;
BitmapInfoHeader.biYPelsPerMeter = 0;
BitmapInfoHeader.biClrUsed = 0;
BitmapInfoHeader.biClrImportant = 0;
os.write((char *)&BitmapFileHeader, sizeof(BitmapFileHeader));
os.write((char *)&BitmapInfoHeader, sizeof(BitmapInfoHeader));
return BitmapInfoHeader;
};
void OnSaveBmp()
{
static char Filter[] = "bmp (*.bmp)\0*.bmp\0All (*.*)\0*.*\0";
OPENFILENAME OpenFilename;
memset(&OpenFilename, 0, sizeof(OpenFilename));
OpenFilename.lStructSize = sizeof(OpenFilename);
OpenFilename.hwndOwner = MainWindow;
OpenFilename.hInstance = InstanceHandle;
OpenFilename.lpstrFilter = Filter;
OpenFilename.lpstrFile = CurrentFileName;
OpenFilename.nMaxFile = sizeof(CurrentFileName);
OpenFilename.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
OpenFilename.lpstrDefExt = "bmp";
if(GetSaveFileName(&OpenFilename))
{
std::ofstream File(CurrentFileName);
if(File)
{
HDC dc = GetDC(MainWindow);
HDC MemDc = CreateCompatibleDC(dc);
RECT Rect;
GetClientRect(MainWindow, &Rect);
LONG Width = Rect.right - Rect.left;
LONG Height = Rect.bottom - Rect.top;
HBITMAP Bitmap = CreateCompatibleBitmap(dc, Width, Height);
HGDIOBJ OldBitmap = SelectObject(MemDc, Bitmap);
HBRUSH Brush = CreateSolidBrush(RGB(255, 255, 255));
FillRect(MemDc, &Rect, Brush);
RECT Temp;
GetWindowRect(ToolBarWindow, &Temp);
LONG ToolBarHeight = Temp.bottom - Temp.top;
GetWindowRect(StatusBarWindow, &Temp);
LONG StatusBarHeight = Temp.bottom - Temp.top;
SetViewportOrgEx(MemDc, 0, -ToolBarHeight, 0);
size_t i;
for(i = 0; i < Objects.size(); i++)
Objects[i]->Draw(MemDc);
Height -= StatusBarHeight + ToolBarHeight;
BITMAP Bm;
GetObject(Bitmap, sizeof(Bm), &Bm);
BITMAPINFOHEADER Header = WriteHeader(File, Width, Height, Bm.bmBitsPixel);
char *Buffer = new char [Width*Height*Bm.bmBitsPixel/8];
GetDIBits(MemDc, Bitmap, 0, Height, Buffer, (BITMAPINFO *)&Header, DIB_RGB_COLORS);
File.write(Buffer, Height*Width*Bm.bmBitsPixel/8);
delete [] Buffer;
SelectObject(MemDc, OldBitmap);
DeleteDC(MemDc);
DeleteDC(dc);
DeleteObject(Brush);
DeleteObject(Bitmap);
}
}
}
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SIZE:
SendMessage(StatusBarWindow, msg, wParam, lParam);
SendMessage(ToolBarWindow, msg, wParam, lParam);
SetStatusBarWidths(hwnd);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case FileOpenCmd:
OnOpen();
break;
case FileSaveCmd:
OnSave();
break;
case FileSaveBmpCmd:
OnSaveBmp();
break;
case FileCloseCmd:
MessageBox(hwnd, "Close", "Test", MB_OK);
break;
case AboutCmd:
MessageBox(hwnd, "About", "Test", MB_OK);
break;
case DrawRedCmd:
CurrentColor = RGB(255, 0, 0);
break;
case DrawBlueCmd:
CurrentColor = RGB(0, 0, 255);
break;
case DrawGreenCmd:
CurrentColor = RGB(0, 255, 0);
break;
case DrawRectangleCmd:
CurrentObject = RectangleObj;
break;
case DrawLineCmd:
CurrentObject = LineObj;
break;
}
break;
case WM_LBUTTONDOWN:
OnLButtonDown(hwnd, LOWORD(lParam), HIWORD(lParam));
break;
case WM_MOUSEMOVE:
OnMouseMove(hwnd, LOWORD(lParam), HIWORD(lParam));
break;
case WM_LBUTTONUP:
OnLButtonUp(hwnd, LOWORD(lParam), HIWORD(lParam));
break;
case WM_PAINT:
OnPaint(hwnd);
break;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
HWND CreateMainWindow()
{
WNDCLASS wc;
memset(&wc, 0, sizeof(WNDCLASS));
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;
wc.lpfnWndProc = (WNDPROC )MainWndProc;
wc.hInstance = InstanceHandle;
wc.hbrBackground = (HBRUSH )(COLOR_WINDOW + 1);
wc.lpszClassName = "SimpleWinWndClass";
wc.lpszMenuName = 0;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClass(&wc))
return 0;
HMENU FileMenu = CreateMenu();
AppendMenu(FileMenu, MF_STRING, FileOpenCmd, "Open");
AppendMenu(FileMenu, MF_STRING, FileSaveCmd, "Save");
AppendMenu(FileMenu, MF_STRING, FileSaveBmpCmd, "Save BMP");
AppendMenu(FileMenu, MF_SEPARATOR, 0 , 0);
AppendMenu(FileMenu, MF_STRING, FileCloseCmd, "Close");
HMENU HelpMenu = CreateMenu();
AppendMenu(HelpMenu, MF_STRING, AboutCmd, "About");
HMENU MainMenu = CreateMenu();
AppendMenu(MainMenu, MF_POPUP, (UINT )FileMenu, "File");
AppendMenu(MainMenu, MF_POPUP, (UINT )HelpMenu, "Help");
return CreateWindow("SimpleWinWndClass",
"Simple-Window",
WS_MINIMIZEBOX | WS_VISIBLE |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZEBOX |
WS_CAPTION | WS_BORDER | WS_SYSMENU | WS_THICKFRAME,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
0,
MainMenu,
InstanceHandle,
0);
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
INT nCmdShow)
{
InstanceHandle = hInstance;
if((MainWindow = CreateMainWindow()) == (HWND )0)
{
MessageBox(0, "Failed to create MainWindow!", "Warning", MB_OK);
return 0;
}
StatusBarWindow = CreateStatusWindow(WS_CHILD|WS_VISIBLE|WS_BORDER|SBARS_SIZEGRIP,
"Ready",
MainWindow,
StatusBarId);
SetStatusBarWidths(MainWindow);
CreateToolBar(MainWindow);
ShowWindow(MainWindow, SW_SHOW);
SetStatusBarText("First", 0);
SetStatusBarText("Middle", 1);
SetStatusBarText("Right", 2);
MSG Msg;
while(GetMessage(&Msg, 0, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
size_t i;
for(i = 0; i < Objects.size(); i++)
delete Objects[i];
return Msg.wParam;
}