It has been 10 months since I worked on my app due to a death in the family, just started looking at it again and still not sure how to solve the problem.
The project inquires/help started here:
MFC MDI Collecting control states for the "apply" button routine
Since this is a specific focused question, I didn't want to muck up my other thread, so what I'd like to do is change the documents tab styles after the view is loaded. I know that this can be done because the master repository from Microsoft with all the code examples has a project called VCSamples-master\VCSamples-master\VC2010Samples\MFC\Visual C++ 2008 Feature Pack\TabControl which I have looked at. It dawns on me that even though I can follow its code, the calls are from within the MDI window itself where my issue is I'm trying to do this via a property page dialog using OnApply which changes things.
I was able to do part of this properly with the help of the thread above to the OutputPane successfully because I was able to get the Pane handle and execute. I was told that for the MDI tabs after creation that I need to parse the tabs, count them, and then execute. So my issue here is after I capture the tabs......how to change their styles.
Here is the code as it stands:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles (Works 100%)
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
//Get the open file tabs in the MDI
for (POSITION pos = AfxGetApp()->GetFirstDocTemplatePosition(); pos != NULL; )
{
CDocTemplate* pTempl = AfxGetApp()->GetNextDocTemplate(pos);
for (POSITION pos1 = pTempl->GetFirstDocPosition(); pos1 != NULL; )
{
CDocument* pDoc = pTempl->GetNextDoc(pos1);
for (POSITION pos2 = pDoc->GetFirstViewPosition(); pos2 != NULL; )
{
CView* pView = pDoc->GetNextView(pos2);
if (pView->IsKindOf(RUNTIME_CLASS(CTrainView)))
{
// THIS IS WHERE MY ISSUE IS, NOW THAT ALL THE TABS ARE
// CAPTURED, HOW DO I ADDRESS THEM LIKE WHAT IS SHOWN
// ABOVE:
//((CMainFrame*)AfxGetMainWnd())->xxxxxx.yyyyyy.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
}
}
}
}
}
return bResult;
}
If I can figure this last piece out, I'll be basically finished, I just can't seem to find a solution on how to do this via property sheet via OnApply.
Any suggestions or actual code examples I can see to solve my problem?
FYI: No, I haven't had any time to take additional OOP to solve this. I'm hoping someone can provide some guidance so I can move on after getting this sorted.
Thanks,
Chris
EDIT 1:
So I took a closer look at Constantine's suggestion and here is what I came up with:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
CMFCTabCtrl& MDI_STYLES = ((CMainFrame*)AfxGetMainWnd())->GetMDITabs();
MDI_STYLES.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
MDI_STYLES.RecalcLayout();
CMDIFrameWndEx* pMainFrame = DYNAMIC_DOWNCAST(CMDIFrameWndEx, GetTopLevelFrame());
pMainFrame->SetFocus();
pMainFrame->RecalcLayout();
}
return bResult;
}
The m_styles_tabs is getting the index value of 0-8 when I select the radio button. The code compiles and runs and I see the index value change when I break on it, but the tabs for the MDI are still not updating. Does the edited code make sense based on the members shown here:
https://learn.microsoft.com/en-us/cpp/mfc/reference/cmfctabctrl-class?view=msvc-170#modifytabstyle
I think this the right direction, am I missing something?
When I was creating my first tiled map creator in libGDX, I noticed a very strange bug. I creating grid of objects like this:
private static final int GRID_WIDTH=2400;
private static final int GRID_HEIGHT=2400;
private static final int CELL_SIZE=60;
so you can see there are 2400/60x2400/60 objects or cells. I am creating my map like this:
private void createMap(){
cells = new Cell[GRID_WIDTH/CELL_SIZE][GRID_HEIGHT/CELL_SIZE];
for(int i=0;i<GRID_WIDTH/CELL_SIZE;++i){
for(int j=0;j<GRID_HEIGHT/CELL_SIZE;++j){
cells[i][j]=new Cell(textures[0],i*CELL_SIZE,j*CELL_SIZE);
}
}
}
I also have coordinates for my debug in the screen so I know where they started to disappear. Y coordinate is ok there are from 0 to 2400, but on the X they started to disappear at 1500. When I start to draw there some texture every column will be visible to that texture for example (when I start to write texture at x=2100 every disappeared column will be visible to 2100) and when I will delete that texture every column will disappear again to 1500. So the objects are there but they are not visible. It is so annoying does anyone know about this bug?
As you can see coordinates are at the bottom left this is at the beginning:
and this is when I will add there some texture
[Edited] Code with camera:
private float x=GRID_WIDTH/2,y=GRID_HEIGHT/2;
#Override
public void render(float delta) {
batch = new SpriteBatch();
camera=new OrthographicCamera(CAM_WIDTH,CAM_HEIGHT);
viewPos = new Vector3();
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
viewPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(viewPos);
batch.begin();
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT) || Gdx.input.isKeyPressed(Input.Keys.D))
x+=SPEED*Gdx.graphics.getDeltaTime();
if(Gdx.input.isKeyPressed(Input.Keys.LEFT) || Gdx.input.isKeyPressed(Input.Keys.A))
x-=SPEED*Gdx.graphics.getDeltaTime();
if(Gdx.input.isKeyPressed(Input.Keys.UP) || Gdx.input.isKeyPressed(Input.Keys.W))
y+=SPEED*Gdx.graphics.getDeltaTime();
if(Gdx.input.isKeyPressed(Input.Keys.DOWN) || Gdx.input.isKeyPressed(Input.Keys.S))
y-=SPEED*Gdx.graphics.getDeltaTime();
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
camera.position.set(x,y,0);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.end();
}
The camera is correct. The problem is the batch.begin() and batch.end(). As you might know you cannot do batch.begin() and then shaperenderer.begin() directly after each others without closing one of them. Reason for this I am not 100% about. stage works similar. This means we have to close the batch before drawing the stage
batch.end();
stage.draw();
batch.begin();
// draw your batch stuff here
Also it's terrible to do this
batch = new SpriteBatch();
camera=new OrthographicCamera(CAM_WIDTH,CAM_HEIGHT);
inside the render method. Instead, put it into the create() method or some of your own initialize method. The important thing is to not create a new SpriteBatch every frame as the batch isn't collected by the GC. So you have to manually dispose it using batch.dispose() or it will leak so much memory your RAM will be gone in no time.
I hope this helped you out, good luck.
My application is developed using JavaFX. I send a command from the JavaFX client to a jar file on a solaris machine and take the output of that jar file and display it on text area in my JavaFX application using JSCH. Originally, there was only one thread in the jar file present on the solaris machine, so all output was sequential and there was no confusion. But now i have implemented multithreading in the JAR file present on Solaris AND this caused an issue as below:
I want To display the output of each thread differently in the text area. I want to divide the text area into parts equal to the number of threads used in the solaris machines jar file. I have found the way send the number of threads from solaris to my JavaFX application.
How can I divide the text area into parts?
Below is the code where i am printing output of a thread in the text area:
while ((jarOutput = reader.readLine()) != null) {
this.logger.info("Status Update = " + jarOutput);
bw.write(jarOutput);
bw.newLine();
bw.flush();
outputFromUnix.append(jarOutput).append("\n");
// Display in activity log area in realtime.
if (DeploymentTaskController.actLogTArea != null
&& !taskName.equalsIgnoreCase(connectBundle)) {
final String outputStr = outputFromUnix.toString();
Platform.runLater(new Runnable() {
#Override public void run() {
DeploymentTaskController.actLogTArea.setText(outputStr);
DeploymentTaskController.actLogTArea.end();
}
});
}
}
I have an application in which double clicking over an image view area changes the layout of the image view. Also on single click a dot will be placed on the image.
My problem is, both functionality is working when double clicked.
Of course I know that, when a double click occurs the control first goes to LButtonDown. I don't want the dot functionality to work when double click occurs. I have been working around this for more than a week. Please help.
The easiest way to solve this is to build a finite-state machine for handling mouse clicks.
Basically, this will be a singleton object, which takes input from the mouse click events you're currently using.
It's output will be SingleClickDetected, DoubleClickDetected, ....
Red arrows indicate events which you are reporting to the rest of your application.
Parentheses indicate what event you are reporting.
Of course, this state machine will have to be modified if you have to deal directly with MouseDown and MouseUp events, instead of MouseClick events.
It will be slightly larger, but the idea is basically the same.
EDIT:
From the comments, it looks like Windows doesn't cleanly report single- vs double-clicks, and you need to separate them.
A state-machine for this scenario:
This is probably overkill for what you're trying to do, especially since most, if not all GUI-based programs in the history of everything have never ever used a double-click drag.
It does show the basic idea, and shows how you can extend your state machine to handle different types of button clicks.
Furthermore, if you wanted to, you could handle double-right-clicks, a drag that involves both left and right buttons, or any other scenario you could think of and incorporate into your UI.
I wrote the following code and it works.
UINT TimerId;
int clicks;
VOID CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
KillTimer(NULL, TimerId);
if (clicks < 2 && !double_click){
MessageBox(hWnd, L"Show Widget", L"Widget", MB_OK);
}
clicks = 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
UINT uID;
UINT uMouseMsg;
uID = (UINT)wParam;
uMouseMsg = (UINT)lParam;
if (uMouseMsg == WM_LBUTTONDBLCLK){
double_click = true;
MessageBox(hWnd, L"Double click", L"CAPT", MB_OK);
return 0;
}
if (uMouseMsg == WM_LBUTTONDOWN){
double_click = false;
clicks++;
//single click opens context menu
if (clicks == 1){
TimerId = SetTimer(NULL, 0, 500, &TimerProc);
}
return 0;
}
,...
}
Try storing the timestamp of the last LButtonDown; if the time difference between the last timestamp and the timestamp produced in the current event is too short, you can just cancel your operation (but still store the new LButtonDown timestamp)
The only thing you can do is wait for a short amount of time each time you receive click event, and test if in the meantime the equivalent of a double click event doesn't occur before performing the single click response. This may be source to new bugs and unresponsive UI. Maybe try to change the user interaction to toss the problem away.
EDIT: The fact that you are working around this for more than a week is a symptom of bad user interaction design. A "double click" still means two clicks occurs, which means the application naturally should perform the operation of a single click. Check the ui of the apps installed on your desktop to verify this. Have you considered using a different user medium to trigger the UI response? For instance, you can use the right button to put the dot on the image.
#E.T's answer is spot-on. To implement something like that, you really need a timer running along with your message loop.
In my application I wanted to be able to distinguish mouse down/up from double click because I want a double click to not undo a drag operation (imagine selection box with left button drag and double click to zoom to fit).
The following code does this using PreTranslateMessage. I did not add the timer out of laziness. So the UI behaves a bit "funny" if you don't immediately move the mouse after the left button is held down. In my case, this is a minor issue.
BOOL MyWindow::PreTranslateMessage(MSG *pMsg)
{
//From https://msdn.microsoft.com/en-us/library/windows/desktop/ms645606(v=vs.85).aspx
//Double-clicking the left mouse button actually generates a sequence of four messages:
//WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP
//So here's the problem. If an button up message arrives, we can't just
//take it, because it may be followed by a double click message. So we check
//for this.
//Ideally you need a timer - what happens if you don't get any messages
//after the button up or down? But here we get lazy and assume a message
//will come "soon enough".
static bool upMessageStored = false;
static bool dnMessageStored = false;
static MSG upMessage, dnMessage;
static bool processDnNow = false, processUpNow = false;
//This logic sequence absorbs all button up and down messages, storing them to be sent
//if something other than a double click message immediately follows.
if (!(pMsg->message == WM_LBUTTONDBLCLK ||
pMsg->message == WM_LBUTTONDOWN ||
pMsg->message == WM_LBUTTONUP) &&
(upMessageStored || dnMessageStored))
{
//If we receive any message other than double click and we've stored the messages,
//then post them.
Output::Message(L"Stored messages posted.\n");
if (upMessageStored)
{
processUpNow = true;
upMessageStored = false;
this->PostMessage(upMessage.message, upMessage.wParam, upMessage.lParam);
}
if (dnMessageStored)
{
processDnNow = true;
dnMessageStored = false;
this->PostMessage(dnMessage.message, dnMessage.wParam, dnMessage.lParam);
}
return TitlelessWindow::PreTranslateMessage(pMsg);
}
if (pMsg->message == WM_LBUTTONDOWN && !processDnNow)
{
Output::Message(L"WM_LBUTTONDOWN absorbed; message stored\n");
dnMessage = *pMsg;
dnMessageStored = true;
return TRUE; //swallow this message.
}
else if (pMsg->message == WM_LBUTTONUP && !processUpNow)
{
Output::Message(L"WM_LBUTTONUP absorbed; message stored\n");
upMessage = *pMsg;
upMessageStored = true;
return TRUE; //swallow this message.
}
else if (pMsg->message == WM_LBUTTONDBLCLK)
{
Output::Message(L"WM_LBUTTONDBLCLK; stored message discarded\n");
upMessageStored = false;
dnMessageStored = false;
processUpNow = false;
processDnNow = false;
}
//If we get here, we are processing messages normally. Be sure we clear the flags
//for up and down.
processDnNow = false;
processUpNow = false;
return ParentClass::PreTranslateMessage(pMsg);
}
I really liked the finite state machine answer BUT there is a flaw in it.
There is no such thing as "single click time" which you can exceed.
If you look at a the working of your mouse closely you will see that:-
single click not one event but = WM_LBUTTONDOWN, WM_LBUTTONUP which is independent of time in between, the appropriate action will take place anyway
Double-clicking the left mouse button actually generates a sequence of four messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP
so you should use the 3rd flag to your advantage
BTW, I am also working on something like this. Thanks!
I'm trying to add a pop-up message function in my project.And I make it run in a subthread since I need a realtime notification.But I find if the notification dialog is created in my subthread(started by AfxBeginThread),all the elements(buttons,urls....) of the dialog are not shown.The message box is just a blank dialog. If I extract the function and start it by a button then it works fine.Here's the function,it's quite simple.
UINT sendNotification(LPVOID pParam)
{
int x = GetSystemMetrics(SM_CXSCREEN);
int y = GetSystemMetrics(SM_CYSCREEN);
testPopDlg *testPop = new testPopDlg;
testPop->Create(IDD_TEST,0);
CRect lprect(0,0,0,0);
testPop->GetWindowRect(lprect);
int w = lprect.Width();
int h = lprect.Height();
testPop->web.Navigate("http://www.google.com",NULL,NULL,NULL,NULL);
testPop->ShowWindow(SW_SHOW);
for(int k=0;k<20;k++) //slide out
{
testPop->MoveWindow(x-w,y-h*k/20,w,y-h*k/20,1);
Sleep(20);
}
Sleep(5000); //will close after 5 sec
return 0;
}
Why this function doesn't work right in subthread?
To use a dialog on a different thread in MFC you should create it on a UI thread. To create a UI thread you basically have to create a class derived from CWinThread. See also this code project article.