How to open an image file and put it in the rectangle when a button is clicked? I use winapi and c programming language, please help and thanks in advance
Global Variable :
HDC hdc, hdcMem;
HBITMAP hLogo, hGambar;
OPENFILENAME ofn = {0};
char szNamaFile[MAX_PATH] = {0}
WM_CREATE :
HWND btnLoad;
btnLoad = CreateWindow("Button", "&Load", WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_FLAT, 149, 421, 70, 25, hWnd, (HMENU)IDB_LOAD, GetModuleHandle(NULL), NULL);
if(btnLoad == NULL)
{
MessageBox(NULL, "Gagal Membuat Button!", "Error Uy!", MB_ICONEXCLAMATION);
exit(EXIT_FAILURE);
}
hGambar = (HBITMAP)LoadImage(hInst, szNamaFile, IMAGE_BITMAP, 5, 5, LR_LOADFROMFILE);
SendMessage(btnLoad, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hGambar);
WM_COMMAND:
if(HIWORD(wParam) != BN_CLICKED)
break;
switch(LOWORD(wParam))
{
case IDB_LOAD:
BukaFormDialog(hWnd);
break;
}
break;
WM_PAINT:
PAINTSTRUCT ps ;
InvalidateRect (hWnd, NULL, TRUE);
RECT rctWindow;
GetClientRect(hWnd, &rctWindow);
RECT rctPct;
rctPct.left = 3;
rctPct.top = rctKiri.top + 3;
rctPct.right = rctKiri.right - 3;
rctPct.bottom = rctKiri.bottom- 75;
hdc = BeginPaint(hWnd, &ps);
/*------------Picture Box--------------*/
Rectangle(hdc, rctPct.left, rctPct.top, rctPct.right, rctPct.bottom);
/*-------------------------------------*/
EndPaint(hWnd, &ps);
break;
Procedure :
char BukaFormDialog(HWND hWnd)
{
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFilter = "File JPG (*.JPG)\0*.JPG\0File BMP (*.BMP)\0*.BMP\0File PNG(*.PNG)\0*.PNG\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = szNamaFile;
ofn.lpstrTitle = "Pilih Gambar ...";
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
if(GetOpenFileName(&ofn))
{
hMem = CreateCompatibleDC(hdc);
SelectObject(hMem, hGambar);
BitBlt(hdc, 4, 4, 50,50, hMem, 0, 0, SRCCOPY);
DeleteDC(hMem);
DeleteObject(hGambar);
}
else
MessageBox(hWnd, "Gagal Membuka File", "Error", MB_ICONERROR | MB_OK);
return (0);
}
you have four problems here
1st) LoadImage(..,IMAGE_BITMAP,...) does only support BMP files.
2nd) you are deleting the wrong Handle (hGambar)
if(GetOpenFileName(&ofn))
{
hMem = CreateCompatibleDC(hdc);
hGambar=SelectObject(hMem, hGambar); //save the original hBmp
BitBlt(hdc, 4, 4, 50,50, hMem, 0, 0, SRCCOPY);
hGambar=SelectObject(hMem, hGambar); // Select Back the original hBmp
DeleteDC(hMem);
DeleteObject(hGambar); // now hGambar is back delete it
}
3rd) your above drawing is not persistent, it will be erased by any overlapping window. move it to WM_PAINT.
4th) Do not call InvalidateRect or InvalidateRgn in WM_PAINT.
here is a full working code
#include <windows.h>
HDC hdc, hdcMem;
HBITMAP hLogo, hGambar=NULL;
#ifndef IDB_LOAD
#define IDB_LOAD 1000
#endif
#define APP_CLASS TEXT("APP_CLASS")
char szNamaFile[MAX_PATH];
/*____________________________________________________________________________________________________________
*/
int BukaFormDialog(HWND hWnd)
{
OPENFILENAME ofn={sizeof(ofn)};
ofn.hwndOwner = hWnd;
ofn.lpstrFilter = "File BMP (*.BMP)\0\0";
ofn.lpstrFile = szNamaFile;
ofn.lpstrTitle = "Pilih Gambar ...";
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_FILEMUSTEXIST;
if(GetOpenFileName(&ofn))
{
HBITMAP hTmp;
DeleteObject(hGambar);
hGambar=LoadImage(NULL,szNamaFile,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
if(hGambar){
InvalidateRect(hWnd,NULL,1);
return 1;
}
MessageBox(hWnd, "Bad Image", "Error", MB_ICONERROR | MB_OK);
}
else{
//User pressed Cancel*/;
}
return (0);
}
/*____________________________________________________________________________________________________________
*/
int WinProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
switch(uMsg){
case WM_CREATE:
CreateWindow(
TEXT("Button"),
TEXT("&Load"),
WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_FLAT,
149, 421, 70, 25,
hWnd, (HMENU)IDB_LOAD, NULL, NULL);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDB_LOAD:
BukaFormDialog(hWnd);
break;
}
break;
case WM_PAINT:{
PAINTSTRUCT ps;
HDC hMem;
BeginPaint(hWnd,&ps);
if(hGambar){
if(hMem=CreateCompatibleDC(ps.hdc)){
hGambar=SelectObject(hMem,hGambar); /*select & swap*/
BitBlt(ps.hdc, 4, 4, 50,50, hMem, 0, 0, SRCCOPY);
hGambar=SelectObject(hMem,hGambar);/*select & swap back*/
DeleteDC(hMem);
}
}
EndPaint(hWnd,&ps);
}
break;
case WM_DESTROY:
DeleteObject(hGambar);
break;
default:
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
return 0;
}
/*____________________________________________________________________________________________________________
*/
int WinMain(HINSTANCE hInstance,HINSTANCE hPrev,LPSTR lpCmd,int nShow){
WNDCLASS wc={
0,
WinProc,
0,
0,
hInstance,
NULL,
LoadCursor(NULL,IDC_ARROW),
(HBRUSH) (COLOR_WINDOW+1),
NULL,
APP_CLASS};
if(RegisterClass(&wc)){
if(CreateWindow(APP_CLASS,TEXT("Title"),
WS_OVERLAPPEDWINDOW|WS_VISIBLE,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,NULL,NULL)){
MSG msg;
while(GetMessage(&msg,NULL,0,0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
return 0;
}
Related
From my understanding, BeginPaint and EndPaint helps paint the screen and validates the rectangle that it paints, this also prevents WM_PAINT messages from being spammed since there will be no more invalid regions in the window.
With my understanding, I thought that theoretically speaking, if I were to use BitBlt() to paint the window in WM_CREATE, then use ValidateRect to Validate the entire client region, the code would work. However, I tried it and it doesn't, suggesting that there could be something possibly wrong with my understanding, or code. Could someone please explain to me where I went wrong?
Here is the code that I used, IDB_BITMAP1 can be replaced with any appropriate Bitmap. Do note the comment in WM_PAINT to reproduce the issue. Thanks for reading!
#include <windows.h>
#include "resource.h"
HBITMAP hBitMap = NULL;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
BITMAP infoBM;
hBitMap = LoadBitmap(GetModuleHandle(NULL), IDB_BITMAP1);
if (hBitMap == NULL)
{
MessageBoxA(NULL, "COULD NOT LOAD CAT BITMAP", "ERROR", MB_OK);
}
HDC winDC = GetDC(hwnd);
HDC hMem = CreateCompatibleDC(winDC);
GetObject(hBitMap, sizeof(infoBM), &infoBM);
HBITMAP hMemOld = SelectObject(hMem, hBitMap);
BitBlt(winDC, 0, 0, infoBM.bmWidth, infoBM.bmHeight, hMem, 0, 0, SRCCOPY);
SelectObject(hMem, hMemOld);
DeleteDC(hMem);
ReleaseDC(hwnd,winDC);
break;
}
case WM_PAINT: //<<<--------- I replaced the entire segment here with ValidateRect(hwnd,NULL);break; And the whole window doesnt show anymore, why is that so?
{
BITMAP infoBM;
PAINTSTRUCT ps;
HDC winDC = BeginPaint(hwnd, &ps);
GetObject(hBitMap, sizeof(infoBM), &infoBM);
HDC hMem = CreateCompatibleDC(winDC);
HBITMAP hOldMem = SelectObject(hMem, hBitMap);
BitBlt(winDC, 0, 0, infoBM.bmWidth, infoBM.bmHeight, hMem, 0, 0, SRCCOPY);
SelectObject(hMem, hOldMem);
DeleteObject(hMem);
EndPaint(hwnd, &ps);
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:return DefWindowProcA(hwnd, msg, wParam, lParam);
}
return 0;
}
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc = { sizeof(WNDCLASSEX),0,WindowProc,0,0,hInstance,NULL,LoadCursor(NULL,IDC_ARROW),(HBRUSH)(COLOR_WINDOW+1),NULL,"WindowClass",LoadIcon(NULL,IDI_APPLICATION)};
if (!RegisterClassEx(&wc))
{
MessageBoxA(NULL, "WINDOW REGISTRATION FAILED", "ERROR", MB_OK);
}
HWND hwnd = CreateWindowEx(WS_EX_LAYERED, "WindowClass", "Title", WS_POPUP, 200, 200, 300, 300, NULL, NULL, hInstance, NULL);
if (!hwnd)
{
MessageBoxA(NULL, "WINDOW CREATION FAILED", "ERROR", MB_OK);
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
if (!SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA))
{
MessageBoxA(NULL, "WINDOW LAYERED ATTRIBUTES FAILED", "ERROR", MB_OK);
}
MSG msg;
while (GetMessage(&msg, hwnd, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Typically,
You use the GetDC function to carry out drawing that must occur
instantly rather than when a WM_PAINT message is
processing.
And if you'd like, you can ValidateRect the entire client area instead of real painting when a WM_PAINT message is processing.
The following code is adapted from the thread.
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int i = 0;
switch (message)
{
case WM_PAINT:
{
if (i++ < 100)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
COLORREF clrbak = SetBkColor(hdc, RGB(255, 0, 0));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
int x = (rect.right - rect.left) / 8;
int y = (rect.bottom - rect.top) / 8;
rect.left += x; rect.right -= x;
rect.top += y; rect.bottom -= y;
SetBkColor(hdc, RGB(0, 255, 0));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
rect.left += x; rect.right -= x;
rect.top += y; rect.bottom -= y;
SetBkColor(hdc, RGB(0, 0, 255));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
rect.left += x; rect.right -= x;
rect.top += y; rect.bottom -= y;
SetBkColor(hdc, RGB(0, 0, 0));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
SetBkColor(hdc, clrbak);
EndPaint(hWnd, &ps);
}
else
{
ValidateRect(hWnd,NULL);
}
}
break;
case WM_ERASEBKGND:
return 1;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Below I have a code snippet showing what I have tried.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#define WC_MAIN "MainClass"
#define WC_NUMBER "NumberClass"
LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void InitClasses(void)
{
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpfnWndProc = MainWndProc;
wc.lpszClassName = WC_MAIN;
RegisterClass(&wc);
wc.lpfnWndProc = NumberProc;
wc.lpszClassName = WC_NUMBER;
RegisterClass(&wc);
}
#define NUMBER_SPEED 2
#define NUMBER_TICK_SPEED 25
#define NUMBER_TICKS 55
typedef struct {
UINT ticks;
HBITMAP buffer;
} NUMBERINFO;
HWND CreateNumber(HWND parent, const char *text, int x, int y, COLORREF color)
{
NUMBERINFO *ni = malloc(sizeof(*ni));
HDC hdc = GetDC(NULL);
HFONT oldFont = NULL;
SIZE s;
GetTextExtentPoint32(hdc, text, strlen(text), &s);
SelectObject(hdc, oldFont);
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = s.cx;
bmi.bmiHeader.biHeight = s.cy;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // four 8-bit components
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = s.cx * s.cy * 4;
COLORREF *pvBits;
HBITMAP buffer = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &pvBits, NULL, 0);
HDC bufferDc = CreateCompatibleDC(hdc);
HBITMAP oldBmp = SelectObject(bufferDc, buffer);
oldFont = NULL;
SetTextAlign(bufferDc, TA_TOP | TA_LEFT);
SetBkMode(bufferDc, TRANSPARENT);
SetTextColor(bufferDc, color);
TextOut(bufferDc, 0, 0, text, strlen(text));
SelectObject(bufferDc, oldFont);
SelectObject(bufferDc, oldBmp);
DeleteDC(bufferDc);
ReleaseDC(NULL, hdc);
ni->buffer = buffer;
return CreateWindow(WC_NUMBER, text, WS_VISIBLE | WS_CHILD, x - (s.cx >> 1), y - (s.cy >> 1), s.cx, s.cy, parent, NULL, NULL, ni);
}
LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
NUMBERINFO *info = (NUMBERINFO*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
switch(msg)
{
case WM_CREATE:
info = ((CREATESTRUCT*) lParam)->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) info);
SetTimer(hWnd, 0, NUMBER_TICK_SPEED, NULL);
info->ticks = NUMBER_TICKS;
return 0;
case WM_DESTROY: return 0;
case WM_TIMER:
{
RECT rc;
GetWindowRect(hWnd, &rc);
HWND parent = GetParent(hWnd);
MapWindowPoints(HWND_DESKTOP, parent, (POINT*) &rc, 2);
rc.top -= NUMBER_SPEED;
if(!--info->ticks)
{
DestroyWindow(hWnd);
return 0;
}
SetWindowPos(hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top - NUMBER_SPEED, SWP_NOREDRAW | SWP_NOCOPYBITS);
// redraw parent call erases last shown number
RedrawWindow(GetParent(hWnd), &rc, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
return 0;
}
case WM_ERASEBKGND: return 1;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT r;
GetClientRect(hWnd, &r);
HDC bufferDc = CreateCompatibleDC(hdc);
HBITMAP oldBmp = SelectObject(bufferDc, info->buffer);
BLENDFUNCTION bfn;
bfn.BlendOp = AC_SRC_OVER;
bfn.BlendFlags = 0;
bfn.SourceConstantAlpha = info->ticks * 0xFF / NUMBER_TICKS;
bfn.AlphaFormat = 0;
//TransparentBlt(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, r.right, r.bottom, 0);
AlphaBlend(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, r.right, r.bottom, bfn);
SelectObject(bufferDc, oldBmp);
DeleteDC(bufferDc);
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY: PostQuitMessage(0); return 0;
case WM_ERASEBKGND: return 1;
case WM_PAINT:
{
InvalidateRect(hWnd, NULL, FALSE);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// draw gradient (red to green)
RECT r;
GetClientRect(hWnd, &r);
GRADIENT_RECT gr;
gr.UpperLeft = 0;
gr.LowerRight = 1;
TRIVERTEX pVertex[2] = {
{ 0, 0, 0xFF00, 0x0000, 0x0000, 0xFF00 },
{ r.right, r.bottom, 0x0000, 0xFF00, 0x0000, 0xFF00 }
};
GradientFill(hdc, pVertex, 2, &gr, 2, GRADIENT_FILL_RECT_H);
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int main()
{
InitClasses();
HWND mainWindow = CreateWindow(WC_MAIN, "Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL);
ShowWindow(mainWindow, 1);
UpdateWindow(mainWindow);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// adding a number at mouse position if pressed (testing)
if(msg.message == WM_LBUTTONDOWN)
{
RECT r;
GetClientRect(mainWindow, &r);
int x = GET_X_LPARAM(msg.lParam);
int y = GET_Y_LPARAM(msg.lParam);
CreateNumber(mainWindow, "123", x, y, 0xFFFF00);
}
}
return 0;
}
This draws some text with any color and every time a timer ticks, the text becomes more faded (more transparent), but the black background of the bitmap the text was drawn on, is drawn, but I want no background to be present, just the text.
I would assume I need a combination of TransparentBlt and AlphaBlend.
How would I go on to solve this?
The solution was quite simple. It was to use AC_SRC_ALPHA for the AlphaFormat member of BLENDFUNCTION and in order for that to work, set the alpha values of the HBITMAP, the buffer.
After the buffer (the one inside of NUMBERINFO) is created (in CreateNumber; check question code for reference), one must loop over all the colors and set the alpha value to 255 where needed.
for(int i = s.cx * s.cy; i--; pvBits++)
{
if(*pvBits)
*pvBits |= 0xFF000000; // make non black pixels opaque
}
And setting the flag in WM_PAINT: bfn.AlphaFormat = AC_SRC_ALPHA;
I'm trying to implement a text highlighting and undo in RichEdit. The code below marks 5 and 6 chars but undo doesn't work (the text doesn't change) though Edit_CanUndo returns 1. If I turn off a updateHighlighting then undo works but it rolls back too much at once (e.g. input a long text, paste a text by Ctrl+V, input another text - it's required only 3 undo to rollback all).
What is wrong with my code? Maybe I should return a specific value from updateHighlighting?
#define UNICODE
#define _UNICODE
#include <tchar.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <richedit.h>
#define IDC_EDITOR 1000
#define IDC_BUTTON 1001
HWND hEditorWnd;
bool updateHighlighting(HWND hWnd) {
int carriagePos = 0;
SendMessage(hWnd, EM_GETSEL, (WPARAM)&carriagePos, (WPARAM)&carriagePos);
CHARFORMAT cf = {0};
cf.cbSize = sizeof(CHARFORMAT) ;
cf.dwMask = CFM_COLOR | CFM_BOLD;
cf.crTextColor = RGB(0, 0, 200);
cf.dwEffects = CFM_BOLD;
SendMessage(hWnd, EM_SETSEL, 0, -1);
SendMessage(hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
cf.dwMask = CFM_COLOR | CFM_BOLD;
cf.dwEffects = cf.dwEffects & ~CFM_BOLD;
cf.crTextColor = RGB(200, 0, 0);
SendMessage(hWnd, EM_SETSEL, 4, 6);
SendMessage(hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
SendMessage(hWnd, EM_SETSEL, carriagePos, carriagePos);
_tprintf(TEXT("End highlight\n"));
return true;
}
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_COMMAND: {
if (LOWORD(wParam) == IDC_EDITOR && HIWORD(wParam) == EN_CHANGE)
return updateHighlighting(hEditorWnd);
if (LOWORD(wParam) == IDC_BUTTON && HIWORD(wParam) == BN_CLICKED)
_tprintf(TEXT("UNDO: can %i => result: %i\n"), Edit_CanUndo(hEditorWnd), Edit_Undo(hEditorWnd));
}
break;
default:
return DefWindowProc (hWnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) {
InitCommonControls();
LoadLibrary(TEXT("msftedit.dll"));
HWND hWnd;
MSG msg;
WNDCLASSEX wincl;
wincl.hInstance = hThisInstance;
wincl.lpszClassName = TEXT("AppClass");
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof (WNDCLASSEX);
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if (!RegisterClassEx (&wincl))
return 0;
hWnd = CreateWindowEx (0, TEXT("AppClass"), TEXT("Test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 300, 400, 310, 375, HWND_DESKTOP, NULL, hThisInstance, NULL);
CreateWindowEx(0, WC_BUTTON , L"UNDO", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 10, 310, 280, 30, hWnd, (HMENU)IDC_BUTTON, hThisInstance, NULL);
hEditorWnd = CreateWindowEx(0, TEXT("RICHEDIT50W"), NULL, WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP | ES_NOHIDESEL, 0, 0, 300, 300, hWnd, (HMENU)IDC_EDITOR, hThisInstance, NULL);
SendMessage(hEditorWnd, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_UPDATE | ENM_KEYEVENTS);
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
P.S. I use Code::Block 17.12 + gcc5.1 on Win7x64 (but build a 32bit app).
What you mean exactly with "doesn't work" or with when it "does work" is unclear, however, changing color is something that can be undone too so if you do not want that, then you have to turn off undo before coloring and resume it after coloring.
I don't know if there are easier ways to do it, but in C I use the following functions:
//#include <windows, richedit, ...>
#include <TOM.h>
extern HWND ghEditWnd;
static IUnknown *pUnk;
static ITextDocument *pDoc;
static const IID IID_ITextDocument = {
0x8CC497C0, 0xA1DF, 0x11CE,
{0x80,0x98,0x00,0xAA,0x00,0x47,0xBE,0x5D}
};
static int tomInit(void)
{
if (!pUnk) SendMessage(ghEditWnd, EM_GETOLEINTERFACE, 0, (LPARAM)&pUnk);
if (!pUnk || pUnk->lpVtbl->QueryInterface(pUnk,&IID_ITextDocument, &pDoc) != NOERROR)
{pUnk= 0; return FALSE;}
return TRUE;
}
void undoSuspend(void)
{
if (!pUnk) if (!tomInit()) return;
pDoc->lpVtbl->Undo(pDoc,tomSuspend,0); //Suspends Undo.
}
void undoResume(void)
{
if (!pUnk) if (!tomInit()) return;
pDoc->lpVtbl->Undo(pDoc,tomResume,0); // resume Undo.
}
void releaseTOM(void)
{
if (pUnk) {
pUnk->lpVtbl->Release(pUnk); pUnk= 0; pDoc= 0;
}
}
See also https://learn.microsoft.com/en-us/windows/win32/api/tom/
EDIT: in question How to disable Undo in Rich Text Box in WPF? the suggestion was to set UndoLimit to zero. It is not clear if that would clear the existing undo stack too.
Undo can be suspended and resumed in RichEdit 3.0, Microsoft says.
Here is the working example (undo removes symbols char-by-char as I want) based on Paul Ogilvie's reply and codepilot answer.
Hint: use freeze/unfreeze to prevent flickering. That is similar to SetWindowRedraw(hWnd, TRUE/FALSE) but the last operation arouse unnecessary EN_SELCHANGE notification messages.
#define UNICODE
#define _UNICODE
#include <tchar.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <richedit.h>
// Code::Block doesn't have a tom.h :(
// https://github.com/kinke/mingw-w64-crt/blob/master/mingw-w64-headers/include/tom.h
#include "tom.h"
#include <richole.h>
#include <unknwn.h>
IUnknown *tr_code = NULL;
ITextDocument *td_code;
#define DEFINE_GUIDXXX(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
DEFINE_GUIDXXX(IID_ITextDocument,0x8CC497C0,0xA1DF,0x11CE,0x80,0x98, 0x00,0xAA,0x00,0x47,0xBE,0x5D);
#define IDC_EDITOR 1000
#define IDC_BUTTON 1001
HWND hEditorWnd;
bool updateHighlighting(HWND hWnd) {
int carriagePos = 0;
SendMessage(hWnd, EM_GETSEL, (WPARAM)&carriagePos, (WPARAM)&carriagePos);
CHARFORMAT cf = {0};
cf.cbSize = sizeof(CHARFORMAT) ;
cf.dwMask = CFM_COLOR | CFM_BOLD;
cf.crTextColor = RGB(0, 0, 200);
cf.dwEffects = CFM_BOLD;
SendMessage(hWnd, EM_SETSEL, 0, -1);
SendMessage(hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
cf.dwMask = CFM_COLOR | CFM_BOLD;
cf.dwEffects = cf.dwEffects & ~CFM_BOLD;
cf.crTextColor = RGB(200, 0, 0);
SendMessage(hWnd, EM_SETSEL, 4, 6);
SendMessage(hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
SendMessage(hWnd, EM_SETSEL, carriagePos, carriagePos);
_tprintf(TEXT("End highlight\n"));
return true;
}
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage (0);
break;
case WM_COMMAND: {
if (LOWORD(wParam) == IDC_BUTTON && HIWORD(wParam) == BN_CLICKED)
_tprintf(TEXT("UNDO: can %i => result: %i\n"), Edit_CanUndo(hEditorWnd), Edit_Undo(hEditorWnd));
if (LOWORD(wParam) == IDC_EDITOR && HIWORD(wParam) == EN_CHANGE) {
long cnt = 0;
td_code->Undo(tomSuspend, NULL); // <---
td_code->Freeze(&cnt); // <---
updateHighlighting(hEditorWnd);
td_code->Unfreeze(&cnt); // <---
td_code->Undo(tomResume, NULL); // <---
return 1;
}
}
break;
default:
return DefWindowProc (hWnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) {
InitCommonControls();
LoadLibrary(TEXT("msftedit.dll"));
HWND hWnd;
MSG msg;
WNDCLASSEX wincl;
wincl.hInstance = hThisInstance;
wincl.lpszClassName = TEXT("AppClass");
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof (WNDCLASSEX);
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if (!RegisterClassEx (&wincl))
return 0;
hWnd = CreateWindowEx (0, TEXT("AppClass"), TEXT("Test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 300, 400, 310, 375, HWND_DESKTOP, NULL, hThisInstance, NULL);
CreateWindowEx(0, WC_BUTTON , L"UNDO", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 10, 310, 280, 30, hWnd, (HMENU)IDC_BUTTON, hThisInstance, NULL);
hEditorWnd = CreateWindowEx(0, TEXT("RICHEDIT50W"), NULL, WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP | ES_NOHIDESEL, 0, 0, 300, 300, hWnd, (HMENU)IDC_EDITOR, hThisInstance, NULL);
SendMessage(hEditorWnd, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_UPDATE | ENM_KEYEVENTS);
// \/
SendMessage(hEditorWnd, EM_GETOLEINTERFACE, 0, (LPARAM)&tr_code);
if(tr_code == (IRichEditOle*)NULL) {
MessageBox(0, TEXT("Error when trying to get RichEdit OLE Object"), NULL, 0);
return 0;
}
tr_code->QueryInterface(IID_ITextDocument,(void**)&td_code);
// /\
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
I am trying to use glClearBufferfv(GL_COLOR, 0, red) to draw a red screen.
The program displays a white screen and the loading icon on the cursor is continually rotating.
I am using glew. I am also using visual studio and i think i have linked to all the necessary libraries. I employed the whole create a temporary context to use the wglCreateContextAttribsARB extension thing.
I have 2 functions for setting things up. the first one creates the window and sets up the pfd: (the editor is not formatting my code correctly so i will leave out the function names)
int pf;
HDC hDC;
HWND hWnd;
WNDCLASS wc;
PIXELFORMATDESCRIPTOR pfd;
static HINSTANCE hInstance = 0;
if (!hInstance) {
hInstance = GetModuleHandle(NULL);
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"OpenGL";
if (!RegisterClass(&wc)) {
MessageBox(NULL, (LPCWSTR)L"RegisterClass() failed: ", (LPCWSTR)L"Error", MB_OK);
return NULL;
}
}
hWnd = CreateWindow(TEXT("OpenGL"), TEXT("Pie"), WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
x, y, width, height, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
MessageBox(NULL, TEXT("CreateWindow() failed: Cannot create a window."),
TEXT("Error"), MB_OK);
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 32;
pfd.iLayerType = PFD_MAIN_PLANE;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
MessageBox(NULL, L"ChoosePixelFormat() failed: "
"Cannot find a suitable pixel format.", L"Error", MB_OK);
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
MessageBox(NULL, L"SetPixelFormat() failed: "
"Cannot set format specified.", L"Error", MB_OK);
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
ReleaseDC(hWnd, hDC);
return hWnd;
The second one creates the context and starts the message loop:
HDC hDC;
HGLRC hRCt, hRC;
HWND hWnd;
MSG msg;
hWnd = CreateOpenGLWindow("minimal", 0, 0, 256, 256, PFD_TYPE_RGBA, 0);
if (hWnd == NULL)
exit(1);
hDC = GetDC(hWnd);
hRCt = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRCt);
glewExperimental = true;
glewInit();
int attribs[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, 5,
WGL_CONTEXT_FLAGS_ARB, 0,
0
};
hRC = wglCreateContextAttribsARB(hDC, 0, attribs);
wglMakeCurrent(hDC, NULL);
wglDeleteContext(hRCt);
wglMakeCurrent(hDC, hRC);
ShowWindow(hWnd, nCmdShow);
while (GetMessage(&msg, hWnd, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, hDC);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
return msg.wParam;
Here is my wndproc:
static PAINTSTRUCT ps;
switch (uMsg) {
case WM_PAINT:
display();
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_SIZE:
//glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
switch (wParam) {
case 27: /* ESC key */
PostQuitMessage(0);
break;
}
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
and here is my display function:
glClearBufferfv(GL_COLOR, 0, red);
glFlush();
red is a global variable defined as:
GLfloat red[4] = {1, 0, 0, 1};
Any help on why it's not drawing to the screen?
Please put display() between BeginPaint and EndPaint.
Start the painting operation by calling the BeginPaint function.
This function fills in the PAINTSTRUCT structure with information on
the repaint request.
After you are done painting, call the EndPaint function. This function
clears the update region, which signals to Windows that the window has
completed painting itself.
Refer: Painting the Window
Use an example to quickly restore the problem:
HDC hdc = GetDC(hWnd);
case WM_PAINT:
{
PAINTSTRUCT ps;
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 2));
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
Debug:
HDC hdc = GetDC(hWnd);
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 2));
EndPaint(hWnd, &ps);
}
Debug:
The shape is drawn with the Polyline() function.
The relevant code is here:
void DoDrawing(HWND hwnd) {
LOGBRUSH brush;
COLORREF col = RGB(0, 0, 0);
DWORD pen_style = PS_SOLID | PS_JOIN_MITER | PS_GEOMETRIC;
brush.lbStyle = BS_SOLID;
brush.lbColor = col;
brush.lbHatch = 0;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen1 = ExtCreatePen(pen_style, 8, &brush, 0, NULL);
HPEN holdPen = SelectObject(hdc, hPen1);
POINT points[5] = { { 10, 30 }, { 100, 30 }, { 100, 100 }, { 10, 100 }, {10, 30}};
Polyline(hdc, points, 5);
DeleteObject(hPen1);
SelectObject(hdc, holdPen);
EndPaint(hwnd, &ps);
}
The PS_JOIN_MITER is applied on three corners but not on the top-left corner. On that corner the default PS_JOIN_ROUND is used. How to fix this?
The following is a full working example:
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DoDrawing(HWND);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Pens";
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Line joins",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 180, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_PAINT:
DoDrawing(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void DoDrawing(HWND hwnd) {
LOGBRUSH brush;
COLORREF col = RGB(0, 0, 0);
DWORD pen_style = PS_SOLID | PS_JOIN_MITER | PS_GEOMETRIC;
brush.lbStyle = BS_SOLID;
brush.lbColor = col;
brush.lbHatch = 0;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen1 = ExtCreatePen(pen_style, 8, &brush, 0, NULL);
HPEN holdPen = SelectObject(hdc, hPen1);
POINT points[5] = { { 10, 30 }, { 100, 30 }, { 100, 100 }, { 10, 100 }, {10, 30}};
Polyline(hdc, points, 5);
DeleteObject(hPen1);
SelectObject(hdc, holdPen);
EndPaint(hwnd, &ps);
}
Polyline does not join the first and last point.
Use Polygon(hdc, points, 5) instead of Polyline
Also, select the oldPen in to DC before deleting the existing pen, in this order:
SelectObject(hdc, holdPen);
DeleteObject(hPen1);
(although Windows will forgive you if you don't do it in the right order)