CTabCtrl DeleteItem does not work in every case - c++

i have a function which loops through map (the map contains the index of my CTabCtrl-Tab and the ID of the Document which is shown in the Tab) and if the ID of the current selected Tab is not similar to the ID of the current looped tab, the tab should be removed.
int deleteTab = -1;
for (auto i : tabIndexToFBNR)
{
deleteTab = i.first;
if (i.second == m_pDlgSubFBs.at(m_AktTab)->m_pRecFB->m_ID)
deleteTab = -1;
if (deleteTab != -1)
m_tabSubFB.DeleteItem(deleteTab);
}
Problem is: Some tabs are removed, some are not. The return code of DeleteItem is always "1".
Any ideas?

Related

If-else condition in loop: future iterations overwrite results of previous iterations

I try to highlight a selectedItem and its children among a list of items.
const QList<Item *> items = /* ... */;
Item *selectedItem = /* ... */;
Q_FOREACH( Item *item, items ) {
if ( selectedItem == item ) {
item->setHighlightEnabled(true); // Highlight selected item
} else {
item->setHighlightEnabled(false); // De-highlight other items
}
}
The item->setHighlightEnabled method does the same for children recursively:
void Item::setHighlightEnabled(bool enabled)
{
if (enabled) {
/* highlight item */
} else {
/* de-highlight item */
}
// Go through all children and highlight them too
Q_FOREACH (Item *child, children())
child->setHighlightEnabled(enabled);
}
Works fine but there is a bug. We loop over all items. When a parent is selected, parent and its children are highlighted. But then loop continues iterating over children. Since children are NOT selected, therefore they are de-highlighted (overwriting highlight in previous loop iterations). I wonder what is the best practice to fix it.
As far as i understand your problem, you could make to for-loops. In the first one you de-highlight all elements. And the second one you leve as is and just stop it with a break; statement, once it found the selected item.
De-highlight everything first, then highlight the current selection.
If selectedItem already is a pointer to the only element you want to highlight, you don't need to search for it, you only need:
Q_FOREACH (Item *item, items)
item->setHighlightEnabled(false);
selectedItem->setHighlightEnabled(true)

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
}

MFC/C++ ComboBox: disable drawing of Dropdown closing & opening (UI freeze)

I've just added an Item-Filter-Feature to a CComboBox derived class called
ComboBoxFbp in an old MFC application.
BOOL CComboBoxFbp::OnEditChange()
{
CString csText;
if (m_wFbpMode & _FbpMode_UserTextFiltersList) {
GetWindowText(csText);
// This makes the DropDown "flicker"
// ShowDropDown(false);
// Just insert items that match
FilterItems(csText);
// Open DropDown (does nothing if already open)
ShowDropDown(true);
}
return FALSE; // Notification weiterleiten
}
void CComboBoxFbp::FilterItems(CString csFilterText)
{
CString csCurText;
int nCurItem;
DWORD wCurCursor;
// Text/selection/cursos restore
GetWindowText(csCurText);
nCurItem = GetCurSel();
if (nCurItem != CB_ERR && nCurItem >= 0 && nCurItem < GetCount()) {
CString csCurItemText;
GetLBText(nCurItem, csCurItemText);
if (csCurItemText == csCurText) csCurText = csCurItemText;
else nCurItem = CB_ERR;
} else {
nCurItem = CB_ERR;
}
wCurCursor = GetEditSel();
// Delete all items
ResetContent();
csFilterText.MakeLower();
// Add just the items (from the vector of all possibles) that fit
for (auto item : m_vItems)
{
CString csItemText = item.first;
csItemText.MakeLower();
if (!csFilterText.IsEmpty() && csItemText.Find(csFilterText) < 0)
continue;
const int i = AddString(item.first);
SetItemData(i, item.second);
}
// Text/selection/cursos restore
if (nCurItem != CB_ERR) SelectString(-1, csCurText);
else SetWindowText(csCurText);
SetEditSel(LOWORD(wCurCursor), HIWORD(wCurCursor));
}
So when the user types, the long list of items in the DropDown gets filtered accordingly. Everything's fine so far.
The size/height of the ListBox/DropDown doesn't change once its open. It does change accordingly when die DropDown opens. Meaning if there are only 2 items the DropDown is only 2 items high.
My issue
When the user enters a text where just one item fits the DropDown is only 1 item in height (this happens with some user workflows, i.e. user manually closes & opens the DropDown).
Now when the user now changes the text so multiple items are fitting the height stays 1 item and it looks weird as even the scrollbar doesn't look correct as it doesn't fit.
What I've tried so far
I cannot use CComboBox::SetMinVisibleItems (or the MSG behind it) as it only works in a Unicode CharacterSet (which I'm not able to change in this old application) and from WinVista onwards (app runs on WinXP).
The only other option is to close and open the DropDown so it gets redrawn correctly with the correct height (see // This makes the DropDown "flicker" in Source Code above).
Now going with option 2 I don't want the user to see the closing and opening ("flicker") of the DropDown after every key he is pressing.
To prevent this I've tried a couple of solutions I've found but none works in my case with a ComboBox-DropDown. Here's a list of methods I've put just before the ShowDropDown(false) and just after the ShowDropDown(true).
EnableWindow(false/true);
(Un)LockWindowUpdate();
SendMessage(WM_SETREDRAW, FALSE/TRUE, 0)
With all three calls I still see the DropDown closing/opening.
Do you guys have other ideas how I can prevent this flicker?
Thanks in advance
Soko
This is an XY question.
It should be easier to use the following approach to adjust the height of the ComboBox
Use GetComboBoxInfo to get the handle of the list control.
Use OnChildNotify or ON_CONTROL_REFLECT and capture CBN_DROPDOWN.
In the handler of the message resize the window as needed Use SetWindowPos and just change the size.

Sitecore Update Placeholder Key (Programming)

I'd like to update the value of placeholder(PH) key assigned into each page item.
The problem is I changed the value of PH key in master template (actually combined two templates to make only one template) and a number of pages should be updated with new assigned PH key.
How to update placeholder key without clicking each item and changing the value in presentation? If I do like this, it takes a lot of time.
What I want to do in program is:
Set initial path (/sitecore/home/robot/)
Check each item (with each item's sub-item) in initial path
Retrieve each item's assigned controls in presentation
If there is "Breadcrumbs" control with "breadcrumbs" key name
Then, change the value to "/template/dynamic/breadcrumbs"
Do until it retrives all items in the initial path
See the code below. What it does, it gets rendering references for the selected items, checks their placeholders and rendering names and updates xml value of the __Renderings field of selected item, based on the unique id of selected renderings. Then it fires same code for all descendants recursively.
This code
does not update placeholders for components which are inherited from __Standard Values
does not publish changed items automatically.
is case sensitive
requires that user has write access for the items that you want to change
public void Start()
{
string initialPath = "/sitecore/home/robot";
Item root = Database.GetDatabase("master").GetItem(initialPath);
UpdatePlaceholderName(root, "Breadcrumbs", "breadcrumbs", "/template/dynamic/breadcrumbs");
}
private void UpdatePlaceholderName(Item item, string componentName, string placeholderName, string newPlaceholderName)
{
if (item != null)
{
List<RenderingReference> renderings = item.Visualization.GetRenderings(Sitecore.Context.Device, false)
.Where(r => r.Placeholder == placeholderName && r.RenderingItem.Name == componentName).ToList();
if (renderings.Any())
{
string renderingsXml = item["__Renderings"];
item.Editing.BeginEdit();
foreach (RenderingReference rendering in renderings)
{
string[] strings = renderingsXml.Split(new [] {"<r"}, StringSplitOptions.None);
foreach (string renderingXml in strings)
{
if (renderingXml.Contains("s:ph=\"" + placeholderName + "\"") && renderingXml.Contains("uid=\"" + rendering.UniqueId + "\""))
{
renderingsXml = renderingsXml.Replace(renderingXml, renderingXml.Replace("s:ph=\"" + placeholderName + "\"", "s:ph=\"" + newPlaceholderName + "\""));
}
}
}
item["__Renderings"] = renderingsXml;
item.Editing.EndEdit();
}
foreach (Item child in item.GetChildren())
{
UpdatePlaceholderName(child, componentName, placeholderName, newPlaceholderName);
}
}
}

When deleting item in Combobox the secong one pop-up MFC

I have combo box and delete button. I want to make next combo box item pop-up when delete button pressed and when last item deleted clean combo box selected item.
I tried several methods with indexes but even one wont help me.
there is my code:
if(IDYES == MessageBox(L"Delete save?",L"Delete", MB_YESNO|MB_ICONQUESTION)){
CString pFileName = L"Save\\"+str+".dat";
CFile::Remove(pFileName);
CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_SAVE);
pComboBox->ResetContent();
}
How I can to make next combo box item pop-up when delete button pressed and when last item deleted clean combo box selected item?
I found a solution:
void CL2HamsterDlg::OnBnClickedButtonDelete(){
if(Validate()){
if(IDYES == MessageBox(L"Delete save?",L"Delete", MB_YESNO|MB_ICONQUESTION)){
CString pFileName = L"Save\\"+str+".dat";
CFile::Remove(pFileName);
CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_SAVE);
lookforfile();
int nIndex = pComboBox->GetCurSel();
if (nIndex == CB_ERR)
pComboBox->SetCurSel(0);
else{
pComboBox->SetEditSel(0, -1);
pComboBox->Clear();
}
}
LoadSave(false);
}else
AfxMessageBox(L"Please select or write correct name!");
}
the function look for file refreshes index
void CL2HamsterDlg::lookforfile()
{
Value.GetWindowText(str);
CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_SAVE);
pComboBox->ResetContent();
GetCurrentDirectory(MAX_PATH,curWorkingDir);
_tcscat_s(curWorkingDir, MAX_PATH, _T("\\Save\\*.dat"));
BOOL bWorking = finder.FindFile(curWorkingDir);
while (bWorking){
bWorking = finder.FindNextFile();
if (!finder.IsDots())
pComboBox->AddString(finder.GetFileTitle());
}
GetDlgItem(IDC_COMBO_SAVE)->SetWindowText(str);
}
so, in this case you do not need to use ResetContent(). Provided you already know the currently selected Item in the combobox (I think somewhere along the track you would have used the line int iSel = pComboBox->GetCurSel();) you could use this code IN PLACE OF YOUR pComboBox->ResetContent();:
pComboBox->DeleteString(iSel);
if(iSel < pComboBox->GetCount())
pComboBox->SetCurSel(iSel);
else if(iSel > 0)
pComboBox->SetCurSel(iSel-1);
However, I think this will not be necessary. I think the item will move by itself. So, forget about the code above, just use this:
pComboBox->DeleteString(pComboBox->GetCurSel())