How to change button position in CMFCToolBar? - c++

Can I change position of some button in CMFCToolBar ? For example set third button at begin of toolbar.

Just get the old button, insert it at the new position, delete the old one.
// Get button at position 3 and move it to position 1
auto *pButton = bar.Getbutton(2);
bar.InsertButton(*pButton,0);
bar.RemoveButton(2);
It is save to dereference the pointer because InsertButton creates a copy.

My version for few buttons:
// pIDs - array of buttons IDs
// nIDCounts - size of array
void ArrangeButtons(CMFCToolBar& wnd, const UINT* pIDs, UINT nIDCounts)
{
std::vector<std::unique_ptr<CMFCToolBarButton>> aBtns;
std::transform(pIDs, std::next(pIDs, nIDCounts), std::inserter(aBtns, std::end(aBtns)), [&](UINT nID)-> std::unique_ptr < CMFCToolBarButton > {
std::unique_ptr<CMFCToolBarButton> res;
CMFCToolBarButton* pBtn = wnd.GetButton(wnd.CommandToIndex(nID));
if ((pBtn != nullptr) && pBtn->IsKindOf(RUNTIME_CLASS(CMFCToolBarButton))) {
res.reset(STATIC_DOWNCAST(CMFCToolBarButton, pBtn->GetRuntimeClass()->CreateObject()));
res->CopyFrom(*pBtn);
}
return std::move(res);
});
wnd.RemoveAllButtons();
std::for_each(std::begin(aBtns), std::end(aBtns), [&](std::unique_ptr<CMFCToolBarButton>& btn) {
if (btn) {
wnd.InsertButton(*btn);
}
});
}

Related

How to move QListWidgetItem?

How to move QListWidgetItem and do not miss Item widget?
This works when drag & drop but can't do the same job on button.
removeItemWidget( item(row) );
QCoreApplication::removePostedEvents (p_widget,QEvent::DeferredDelete );
I remove widget which is shown in Item and make a patch in the way that remove delete signal from queue. in this way I can move Widget to another item
int DropList::moveWidget(int row, int pos)
{
const int new_row = row+pos;
if(count()>1 && row>=0 && row <count() && new_row>=0 && new_row<count())
{
// A->B
DropListItem *pA = widgetByRow(row);
DropListItem *pB = widgetByRow(new_row);
if(pB->isMovable() && pA->isMovable())
{
removeItemWidget(item(new_row));
removeItemWidget(item(row));
QCoreApplication::removePostedEvents (pB,QEvent::DeferredDelete );
QCoreApplication::removePostedEvents (pA,QEvent::DeferredDelete );
setItemWidget(item(new_row),pA);
setItemWidget(item(row),pB);
}
emit changed();
return new_row;
}
return 0;
}
parameters are row - is an item row and pos +1, +2 ,-1 ,....

My Debugger Says I Have A Segfault Error But Cannot Tell Me Where, Happens In Very Specific Circumstance

My Debugger (gdb) Is Saying That I Have a Segfault Error, But Cannot Tell Me Where It Is, Says ?? () For the Function.
I started Getting an Error in a very specific circumstance where I click on a weapon type item, then click on another usable item where my program segfaults. I removed most pointers from my code because at first I thought that was the problem, but it didn't change anything. I Could Not Find a Similar Bug online And Debugging Wasn't Very Helpful Because It Can't Tell Me Where Exactly The Error Is Coming From.
Relavant Player.h
class Player{
private:
int HP; //player's Health Points
int SP; //player's Special Points
int maxHP; //maximum value for the player's health points
int maxSP; //maximum value for the player's special points
uint32_t money; //how much money the player has
float speed; //speed the player will move at (pixels per frame)
bool menuOpen; //boolean which is true when the inventory Menu is open
sf::Vector2f pos; //player's position
//tests if the player is colliding with any items
//in the room, accepts the memory address of the room
bool checkForItems(Room* r);
std::vector<int> ItemInventory; //the player's inventory of items.
std::vector<TextButton> itemButtons; //vector of TextButtons that correspond with each unique item in the player's inventory
std::map<std::string, int> ItemsList; //map that stores each unique item's name and it's quantity
Control openMenu; //control to open the menu
void ItemAttributes(int id); //makes the items do something depending on what was passed
bool removeItem(int id); //removes an item from the player's inventory
void useItem(Item it); //takes an Item* and passes it's ID to ItemAttributes
void updateButtons(); //updates the buttons based on the player's inventory
Item* EquipedWeapon; //stores the player's currently equipped weapon
//enum to store the player's direction for rotations
enum direction {
up,
down,
left,
right
} currentDirection;
void setRotation();
public:
//constructor for the player, initializes maxHP, maxSP, and their position:
Player(int maxhp, int maxsp, sf::Vector2f position);
//getters:
int ItemButtonsTextSearchC(const std::wstring& text); //returns the index in itemButtons for a TextButton which *contains* the text parameter
int ItemButtonsTextSearch(const std::wstring& text); //returns the index in itemButtons for a TextButton which equals the text parameter
//setters:
void setMoney(int amount); //sets the money variable
void setHP(int hp); //sets the HP variable
void setSP(int sp); //sets the SP variable
void menu(); //inventory menu, handles all things related to the player's inventory
void draw(); //draws the player to the screen
};
Methods In Player.cpp That Cause The Problem:
void Player::menu(){
//iterate through all the items listed in itemsList:
for(auto& p : ItemsList){
//get a pointer to the item by it's name:
Item item = *Materials::getItemFromName(p.first);
//convert the item's name to a wstring for easier use in parameters:
std::wstring wItemName = std::wstring(p.first.begin(), p.first.end());
//get the index of the item's corresponding button
//by searching the itemButtons vector for a button
//whose text matches this item's name:
int bIndex = ItemButtonsTextSearch(wItemName);
if(bIndex != -1){
//store the button's position:
sf::Vector2f buttonPosition = itemButtons.at(bIndex).getPosition();
//default the Y values to 150, we need a separate Y for each one
//because there are 4 columns of buttons for each type of item
float weaponY = 150, usableY = 150, collectibleY = 150, moneyY = 150;
//switch statement to determine the position of the button:
switch(item.getType()){
case Item::Weapon:
buttonPosition.x = 100;
buttonPosition.y = weaponY;
//increment by 20 to give space between buttons:
weaponY += 20.0f;
break;
case Item::Usable:
buttonPosition.x = 375;
buttonPosition.y = usableY;
//increment by 20 to give space between buttons:
usableY += 20.0f;
break;
case Item::Collectible:
buttonPosition.x = 650;
buttonPosition.y = collectibleY;
//increment by 20 to give space between buttons:
collectibleY += 20.0f;
break;
case Item::Money:
buttonPosition.x = 925;
buttonPosition.y = moneyY;
//increment by 20 to give space between buttons:
moneyY += 20.0f;
break;
}
//set the button's position now that it's X has been determined:
itemButtons.at(bIndex).setPosition(buttonPosition);
/*
* below we will set the button's text to represent
* it's corresponding item's name as well as it's
* quantity then draw the button to the screen so
* that the client can see how many of each item
* they have, but then we change it back so that it
* doesn't break any comparisons with the button's
* Text (ItemButtonsTextSearch for example):
*/
//text representing item's quantity to append to the end of the the item's name:
std::string QuantityText = "\tx" + std::to_string(p.second);
//wide string that will be the button's text:
std::wstring wQText = wItemName + std::wstring(QuantityText.begin(), QuantityText.end());
//set the button's text (it takes wchar_t* so we call .c_str() on wQText):
itemButtons.at(bIndex).setText(wQText.c_str());
//draw the button with the temporary text to the screen:
itemButtons.at(bIndex).draw();
//poll if the button was clicked, and if it was,
//we will call useItem on it's corresponding Item:
if(itemButtons.at(bIndex).pollClicked()){
useItem(item);
}
//change the button's text back to what it was, note: there
//is a possibility of the button being removed after calling
//useItem() because when an item's quantity hits 0, the
//button corresponding with that item is removed, therefore
//we need a check after the useItem() call to make sure that
//we don't get an index out of bounds error:
if(ItemButtonsTextSearchC(wQText) != -1)
itemButtons.at(bIndex).setText(wItemName.c_str());
}
}
}
void Player::useItem(Item it){
int itemID = it.getItemID();
ItemAttributes(itemID);
}
void Player::ItemAttributes(int id){
switch(id){
case 0: //sword
//EquipedWeapon = Materials::getItem(id);
break;
case 1: //ultra potion of healing
healHP(50);
removeItem(id);
break;
}
}
bool Player::removeItem(int id){
//this will be set to true as soon as we find the item:
bool found = false;
//loop through ItemInventory and remove the first occurance of id
//if it exists, otherwise found will remain false:
for(int i = 0; i < ItemInventory.size(); i++){
if(ItemInventory.at(i) == id){
ItemInventory.erase(ItemInventory.begin() + i);
found = true;
break;
}
}
//if the item was not found in the inventory, there is no need to
//continue, we can just return false because we know that it isn't
//in the player's inventory so it can't be removed in the first place:
if(!found)
return false;
//get an iterator for the item ID's corresponding name in itemsList:
auto itr = ItemsList.find(Materials::itemNames[id]);
//check to make sure the item is actually listed; it will be
//but this is a safeguard in case something breaks:
if(itr != ItemsList.end()){
//decrement the item's quantity:
itr->second--;
//if there are none remaining, we remove it from the itemsList entirely:
if(itr->second <= 0)
ItemsList.erase(itr);
}
//update the buttons based on the new changes:
updateButtons();
//return true because if it got to this point,
//the item was found and removed:
return true;
}
int Player::ItemButtonsTextSearchC(const std::wstring& text){
if(itemButtons.size() > 0){
for(int i = 0; i < itemButtons.size(); i++){
std::wstring bTxt = itemButtons.at(i).getText();
if(bTxt.find(text) != std::string::npos)
return i;
}
}
return -1;
}
int Player::ItemButtonsTextSearch(const std::wstring& text){
if(itemButtons.size() > 0){
for(int i = 0; i < itemButtons.size(); i++){
std::wstring bTxt = itemButtons.at(i).getText();
if(bTxt == text)
return i;
}
}
return -1;
}
void Player::updateButtons(){
//first we clear the vector of itemButton:
itemButtons.clear();
//loop to go through each unique item in ItemsList map
//and make a button for each one:
for(auto& p : ItemsList){
//convert the item's name into a wstring (textbutton constructor takes wchar_t*, wstring is easier to work with):
std::wstring wName = std::wstring(p.first.begin(), p.first.end());
//add the new button to itemButtons
TextButton btn(sf::Vector2f(0, 0), sf::Color::Magenta, sf::Color::White, wName.c_str(), 18);
//make sure button presses only register once
btn.setWasClicked(true);
//add the button to the itemButtons vector:
itemButtons.push_back(btn);
}
}
bool Player::checkForItems(Room* r){
//itemIndex will == the ID of any item we collided with, if there
//was no item it returns -1
int itemIndex = r->checkForItemCollision(player.getGlobalBounds());
if(itemIndex >= 0){
//get item ID from the item we just collided with:
int itemID = r->getItem(itemIndex).collect();
//remove the item from the room and add it's ID to ItemInventory:
r->removeItem(itemIndex);
ItemInventory.push_back(itemID);
//get the item's name and add it to itemsList if it doesn't exist.
std::string itemName = Materials::itemNames[itemID];
//if the item's name is listed in itemsList, we increment it's
//quantity, else we add it and initialize it's quantity to 1:
if(ItemsList.count(itemName) != 0){
ItemsList.at(itemName)++;
} else {
ItemsList.insert(std::make_pair(itemName, 1));
}
//update the buttons in case a new item was obtained:
updateButtons();
//return true because item was found:
return true;
}
//return false, item wasn't found:
return false;
}
When I click a button, it should delete the first occurance of that item's numeric ID from itemInventory, then in itemsList it should decrement the quantity (the map's value) and if that's <= 0, it should remove that entirely. UpdateButtons clears the entire vector of buttons and makes new ones from itemsList, one button for each key. The Item class has an enum for what type of item it is, the sword (item ID 0) is a weapon, the potion (item ID 1) is a usable item, when i click on the sword (does nothing currently), then add a usable item (the potion) and click the new button that was created for usable, it has an error. This doesn't happen unless I click the sword's button first, in any other circumstance it doesn't error when I use all of the item, then add more to my inventory and use them all again. I suspect it's an error with how i'm updating buttons and removing the keys, but I can't find it. All Item Types are in the same vector of buttons, and the item type pretty much only determines where the button will be positioned.
In Player::menu, you have a loop, for(auto& p : ItemsList). Within that loop, you call a sequence (useItem(item) -> ItemAttributes(itemID) -> removeItem(id) -> ItemsList.erase(itr)) that can modify the ItemsList map you are iterating thru. This invalidates the iterators currently referring to p, so when you try access the next element in the map you get Undefined Behavior because the (internally used) iterator is no longer valid.
One possible remedy is to change your for loop to use your own iterators, and increment the iterator at the top of the loop body (before you modify the map):
for (auto it = ItemsList.begin(); it != ItemsList.end(); ) {
auto &p = *it;
++it;
// rest of for loop
}

How to Enable/Disable Menu Item 2 in OnUpdate Handler of Menu Item 1?

I have two menu items. When item 1 is disabled, I want item 2 to be disabled as well. In the OnUpdate handler of menu item 1, I have tried to use "t_pMenu = pCmdUI->m_pMenu;", "t_pMenu = pCmdUI->m_pSubMenu;" and "t_pMenu = pCmdUI->m_pParentMenu;" but I always get NULL t_pMenu. How can I achieve this purpose?
void CDummyView::OnUpdateMenuItem1(CCmdUI* pCmdUI)
{
if(m_bShowMenuItem1)
{
pCmdUI->SetText("Hide Features")
CMenu * t_pMenu = pCmdUI->m_pSubMenu;
if(t_pMenu != NULL)
t_pMenu->EnableMenuItem(ID_MENU_ITEM2, MF_ENABLED);
}
else
{
pCmdUI->SetText("Show Features")
CMenu * t_pMenu = pCmdUI->m_pParentMenu;
if(t_pMenu != NULL)
t_pMenu->EnableMenuItem(ID_MENU_ITEM2, MF_GRAYED);
}
}
void CDummyView::OnUpdateMenuItem2(CCmdUI* pCmdUI)
{
...
}
Never handle to different command IDs in one handler.
Each handler is called more than once if there are buttons and menu items. Also you don't know the sequence. When you alter the item2 in Item1 handler it may be enabled again when the handler for Item2 is called later.
When you have a flag named m_bShowMenuItem1 just use it.
void CDummyView::OnUpdateMenuItem1(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowMenuItem1);
}
void CDummyView::OnUpdateMenuItem2(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowMenuItem1);
}
I got it work. Below is the code I tried. Two flag variables m_bShowFeatures and m_bShowSmallFetures are initialized to be TRUE.
void CDummyView::OnMenuItem1()
{
m_bShowFeatures = !m_bShowFeatures;
m_pDoc->UpdateAllViews(NULL, SHOW_HIDE_ALL_FEATURES);
}
void CDummyView::OnUpdateMenuItem1(CCmdUI* pCmdUI)
{
if(m_bShowFeatures)
pCmdUI->SetText("Hide Features")
else
pCmdUI->SetText("Show Features")
}
void CDummyView::OnMenuItem2()
{
m_bShowSmallFetures= !m_bShowSmallFetures;
m_pDoc->UpdateAllViews(NULL, SHOW_HIDE_SMALL_FEATURES);
}
void CDummyView::OnUpdateMenuItem2(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowFetures)
if(m_bShowSmallFetures)
pCmdUI->SetText("Hide Small Features")
else
pCmdUI->SetText("Show Small Features")
}
So OnUpdateMenuItem2() does get invoked after OnMenuItem1() is called when Menu Item 1 is clicked. I didn't expect that.

How to disable middle button functionality of QTextEdit?

I don't want mouse middle button to paste text in my QTextEdit. This code doesn't work. TextEdit inherits QTextEdit. After mouse middle button pastes it pastes copied text.
void TextEdit::mousePressEvent ( QMouseEvent * e ) {
if (e->button() == Qt::MidButton) {
e->accept();
return;
};
QTextEdit::mousePressEvent(e);
}
As mouse clicks are usually registered when the button is released, you should redefine the mouseReleaseEvent function.
You don't even need to redefine mousePressEvent, because the middle button isn't handled at all by that function.
I'm assuming you're using Linux here; right clicking in the window is likely to be triggering an insertion of mime data before you get to handle the mouse event, which is why it is still pasting text.
Therefore, according to Qt docs for paste: - " to modify what QTextEdit can paste and how it is being pasted, reimplement the virtual canInsertFromMimeData() and insertFromMimeData() functions."
I've been in the same case, that is to say: having parts of my CustomQTextEdit required to be non-editable.
As I truly love the middle mouse button paste feature, I did not wanted to disable it. So, here is the (more or less quick and dirty coded) workaround I used:
void QTextEditHighlighter::mouseReleaseEvent(QMouseEvent *e)
{
QString prev_text;
if (e->button() == Qt::MidButton) {
// Backup the text as it is before middle button click
prev_text = this->toPlainText();
// And let the paste operation occure...
// e->accept();
// return;
}
// !!!!
QTextEdit::mouseReleaseEvent(e);
// !!!!
if (e->button() == Qt::MidButton) {
/*
* Keep track of the editbale ranges (up to you).
* My way is a single one range inbetween the unique
* tags "//# BEGIN_EDIT" and "//# END_EDIT"...
*/
QRegExp begin_regexp = QRegExp("(^|\n)(\\s)*//# BEGIN_EDIT[^\n]*(?=\n|$)");
QRegExp end_regexp = QRegExp("(^|\n)(\\s)*//# END_EDIT[^\n]*(?=\n|$)");
QTextCursor from = QTextCursor(this->document());
from.movePosition(QTextCursor::Start);
QTextCursor cursor_begin = this->document()->find(begin_regexp, from);
QTextCursor cursor_end = this->document()->find(end_regexp, from);
cursor_begin.movePosition(QTextCursor::EndOfBlock);
cursor_end.movePosition(QTextCursor::StartOfBlock);
int begin_pos = cursor_begin.position();
int end_pos = cursor_end.position();
if (!(cursor_begin.isNull() || cursor_end.isNull())) {
// Deduce the insertion index by finding the position
// of the first character that changed between previous
// text and the current "after-paste" text
int insert_pos; //, end_insert_pos;
std::string s_cur = this->toPlainText().toStdString();
std::string s_prev = prev_text.toStdString();
int i_max = std::min(s_cur.length(), s_prev.length());
for (insert_pos=0; insert_pos < i_max; insert_pos++) {
if (s_cur[insert_pos] != s_prev[insert_pos])
break;
}
// If the insertion point is not in my editable area: just restore the
// text as it was before the paste occured
if (insert_pos < begin_pos+1 || insert_pos > end_pos) {
// Restore text (ghostly)
((MainWindow *)this->topLevelWidget())->disconnect(this, SIGNAL(textChanged()), ((MainWindow *)this->topLevelWidget()), SLOT(on_textEdit_CustomMacro_textChanged()));
this->setText(prev_text);
((MainWindow *)this->topLevelWidget())->connect(this, SIGNAL(textChanged()), ((MainWindow *)this->topLevelWidget()), SLOT(on_textEdit_CustomMacro_textChanged()));
}
}
}
}

CTreeCtrl - getting an item position

Is there a way of getting the position (index) of an item in a CTreeCtrl?
I am interested in the index of a node at its particular level.
I was thinking to maintain the item positions within the item "data" field, but the problem is that my tree is sorted and I cannot predict the position an item will receive (well, only if I sort the items in advance which I would like to avoid).
I don't think you can. I assumed that maybe the control could be treated as an array (maybe it still can but I can't find a reference).
Anyways, there are no member functions (according to the MFC API) that give you access to that information
/// there is another way if you "Use Unicode Character Set" (visual studio)
/// Properties->General->Character Set
CPoint point;
GetCursorPos(&point);
m_Tree.ScreenToClient(&point);
UINT nHitFlags;
HTREEITEM hItem = m_Tree.HitTest(point, &nHitFlags);
int idx = m_Tree.MapItemToAccId(hItem);
Get the node handle and then iterate over the elem
Iterate over all the elements, while you count the elements, till you reach the right item?
int GetIndex(const CString & a_Cstr)
{
int idx = 0;
std::vector<CString>::const_iterator _begIt = m_RulesVec.begin();
std::vector<CString>::const_iterator _PosIt = find(m_RulesVec.begin(), m_RulesVec.end(), a_Cstr);
if (_PosIt == m_RulesVec.end()) {
return -1;
}
else {
while (_begIt != _PosIt) {
++idx;
++_begIt;
}
return idx;
}
}
/// it can(must) be done in this function
/// OnNMClickRulesTree(NMHDR *pNMHDR, LRESULT *pResult)
// Create vector like this
std::vector<CString> Vec{"first", "second", "third" };
// OnInit insert items to CtreeCtrl like this
m_Tree.InsertItem("first", hItem);
m_Tree.InsertItem("second", hItem);
m_Tree.InsertItem("third", hItem);
// then get cur selected item like this
CPoint point;
GetCursorPos(&point);
m_Tree.ScreenToClient(&point);
UINT nHitFlags;
HTREEITEM hItem = m_Tree.HitTest(point, &nHitFlags);
// get item text
CString Cstr = m_Tree.GetItemText(hKid);
int idx = GetIndex(Cstr);