Get Index of Item Text in MFC CListCtrl - c++

I've got a CString with a Text that also is an Item Text of my CListCtrl. For example:
CString m_SearchThisItemText = _T("Banana");
And in my CListCtrl
m_List.SetItemText (1, 1, _T ("Banana"));
Now I want to find out, on which Index the Text is.
CListCtrl::FindItem
doesnt work. It only searches the name of the Item, not the Text.
I also tried this
for (Index= 0; dlg.GetSearchContentText () == m_List.GetItemText (Index, Spalte); Index++)// HIER IST NOCH EIN FEHLER.
{
if (dlg.GetSearchContentText () == m_List.GetItemText(Index, Spalte))
{
m_List.SetItemState (Zeile, LVIS_SELECTED, LVIS_SELECTED);
m_List.SetFocus();
}
}
But it doesnt work. It stops at Index 0
Can anyone help me, how to find out on which Item the text is.
I hope you understand my question.

Iterate all the items and search in the column you want:
int nCol = 1; // to search in the second column (like your question)
CString m_SearchThisItemText = _T("Banana");
for (int i = 0; i < m_List.GetItemCount(); ++i)
{
CString szText = m_List.GetItemText(i, nCol);
if (szText == m_SearchThisItemText)
{
// found it - do something
break;
}
}

If you mean that you have a list view with several columns and you want to search in other columns than the first one, then FindItem won't help you. You'll have to explicitly write the find code yourself. You must iterate over all the rows in the list, and for each column of a row call GetItemText and compare what you get with the text you have.

Related

How to get row number for a string in qtablewidget

I am using Qt 5.4.0 and created a QTableWidget. The table has several rows and columns.
In my application I want to search a string in that table and if that string exists, I want to know the row number.
I could not find any such api in qt 5.4.0 documentation.
Does anyone has understanding of such api or similar to what I am looking for.
Thanks in advance !
You can use the match() method of the model:
for (int col=0; col<tableWidget->columnCount(); col++){
// return a list of all matching results
QModelIndexList results = tableWidget->model()->match(
tableWidget->model()->index(0, col),
Qt::DisplayRole,
"yourstring",
-1,
Qt::MatchContains
);
// for each result, print the line number
for (QModelIndex idx : results)
qDebug() << idx.row();
}
You can use findItems method:
QString searchtext = "text";
QList<QTableWidgetItem *> items = ui->tableWidget->findItems(searchtext, Qt::MatchExactly);
for(int i=0; i<items.count(); i++)
{
int row = items.at(i)->row();
//...
}
Notice that you can pass an extra argument to findItems, to set one or more match flags.

Move all selected items below specific one with LVM_SORTITEMSEX

INTRODUCTION:
In order to keep this post as brief as possible, let me just say that I need to move all selected items in the listview below certain (unselected) item.
Browsing through listview documentation I came upon LVM_SORTITEMSEX message.
QUESTION:
How to use the above message to achieve my goal.
MY EFFORTS TO SOLVE THIS:
So far, by using this message, I was able to move all selected items to the bottom of the list -> listview is sorted in such a way that unselected items precede selected ones.
I just can not figure out how to implement moving the selected items below certain item.
Below are the images of what I get, and what I want to achieve:
The left image shows what I get when I use the code submitted below, while the right one shows the result I aim for.
Here are the relevant code snippets:
// compare function -> see the documentation
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM hwnd)
{
LVITEM lvi1 = { 0 }, lvi2 = { 0 };
// get selected state of the first item
lvi1.iItem = (int)lParam1;
lvi1.iSubItem = 0;
lvi1.mask = LVIF_STATE;
lvi1.stateMask = LVIS_SELECTED;
ListView_GetItem((HWND)hwnd, &lvi1);
// get selected state of the second item
lvi2.iItem = (int)lParam2;
lvi2.iSubItem = 0;
lvi2.mask = LVIF_STATE;
lvi2.stateMask = LVIS_SELECTED;
ListView_GetItem((HWND)hwnd, &lvi2);
// if first is selected and second is not selected, swap them
if ((lvi1.state & LVIS_SELECTED) && (0 == (lvi2.state & LVIS_SELECTED)))
return 1;
return 0;
}
// somewhere in code, on button click for example
ListView_SortItemsEx(hwndListView, CompareFunc, hwndListView);
I have passed listview handle as third parameter to ListView_SortItemsEx so I can use ListView_GetItem in CompareFunc.
If I understand this correctly, you want to rearrange items with drag and drop, and you want the sort function to do it. This could get complicated if it is to be done inside the sort proc. Another solution is to find the arrangement first.
Use vector to store items before "redMark"
Add selected items which appear after "redMark"
Add unselected items which appear after "redMark"
Save this order to LVITEM::lParam
Call ListView_SortItems (not ListView_SortItemsEx)
The only problem is that maybe lParam was used for other reasons. We have to save lParam, and then restore it after sort is done.
Also it's better if ListView has LVS_SHOWSELALWAYS.
Note, this method moves items before the "redMark". In your example you should set redMark = 3 to move selection before "Item 60"
int CALLBACK CompareFunc(LPARAM lp1, LPARAM lp2, LPARAM)
{
return lp1 > lp2;
}
void sort()
{
int redMark = 3;
int count = ListView_GetItemCount(hwndListView);
std::vector<int> order;
std::vector<LPARAM> saveLParam(count);
//add everything before redMark
for (int i = 0; i < redMark; i++)
order.push_back(i);
//add highlighted items
for (int i = redMark; i < count; i++)
if (ListView_GetItemState(hwndListView, i, LVIS_SELECTED))
order.push_back(i);
//add the rest
for (int i = redMark; i < count; i++)
if (!ListView_GetItemState(hwndListView, i, LVIS_SELECTED))
order.push_back(i);
if (order.size() != count)
{
assert(0);
return;
}
//set lParam
for (int i = 0; i < count; i++)
{
LVITEM item = { 0 };
item.iItem = order[i];
item.mask = LVIF_PARAM;
//save old LParam value
ListView_GetItem(hwndListView, &item);
saveLParam[i] = item.lParam;
//set new lParam
item.lParam = i;
ListView_SetItem(hwndListView, &item);
}
ListView_SortItems(hwndListView, CompareFunc, 0);
//restore old lParam
for (int i = 0; i < count; i++)
{
LVITEM item = { 0 };
item.iItem = order[i];
item.mask = LVIF_PARAM;
item.lParam = saveLParam[order[i]];
ListView_SetItem(hwndListView, &item);
}
::SetFocus(hwndListView);
}
Introduction:
For LVM_SORTITEMSEX, all items must have unique lParam's
To pass multiple parameters to sorting callback, make a struct and pass pointer to it.
As soon as you start sorting, the original order of items is lost, and you can no longer refer to it if not saved somewhere.
If you can repeat the operation of moving sorted items, then carefully crafted lParam's are also not enough to know the original order of items.
You can mess with lParam's instead of preparing desired order of items separately, but this is rather dirty and prone to errors.
Generic solution
Ensure all items have unique lParam's
Before calling LVM_SORTITEMSEX, prepare a vector of lParam's in desired order:
Enumerate items in list from beginning up to the "red mark" item, add their lParam's into vector.
Enumerate items in list from "red mark" item to the list end, if item is selected, add its lParam into vector.
Enumerate items in list from "red mark" item to the list end, if item is NOT selected, add its lParam into vector.
Now you have the order of lParam's: start of the list, then selected items retaining their original order, then unselected items retaining their original order.
Pass that vector to sorting callback
In it, lookup positions of two lParam's in vector and make answer based on this positions. For example, if you find that first position is less then second, you return a negative number. The typical approach is return (firstPos - secondPos), that will handle all relative orders of firstPos and secondPos in one line of code.
That will have your sorting calllback apply prepared order of items to list.

How to delete selected row of a List Control in MFC?

I want to delete selected row of list control in MFC.
I have created a Delete Button, So If any row (it could be one or more than one row) is/are selected and I press delete button that/those rows should be deleted.
If lets say there are 100 rows and I select rows from 50-60, all the rows in this range should be deleted and rest of rows should have indexes from 1 to 90. means indexing should be proper after deletion also.
Adapted from this MSDN article:
UINT i, uSelectedCount = m_myListCtrl.GetSelectedCount();
int nItem;
if (uSelectedCount > 0)
for (i=0; i < uSelectedCount; i++)
{ nItem = m_myListCtrl.GetNextItem(-1, LVNI_SELECTED);
ASSERT(nItem != -1);
m_myListCtrl.DeleteItem(nItem);
}
When deleting a multiple selection having several items I prefer to do it like this:
int nItem = -1;
while ((nItem = m_list.GetNextItem(nItem, LVNI_SELECTED)) != -1)
{
if (m_list.DeleteItem(nItem))
nItem--;
}
Notice the important nItem--; line
UPDATE
I had to give up from this approach as the ItemData of an element gots fucked up. If I remove the nth element then the n+1 element will be my new nth. That element has a completely screwed up Itemdata.
UPDATE 2
I also tried with
int nItem = -1;
while ((nItem = m_list.GetNextItem(-1, LVNI_SELECTED)) != -1)
{
m_list.DeleteItem(nItem);
}
This approach also has the problem of screwing the Itemdata I reported before.
The following approach worked perfecly for me:
std::stack< int > items;
int nItem = -1;
while ((nItem = myListCtrl.GetNextItem(nItem, LVNI_SELECTED)) != -1)
{
items.push(nItem);
}
bool removed = false;
while (!items.empty())
{
nItem = items.top();
if (myListCtrl.DeleItem(nItem))
removed = true;
items.pop();
}
if (removed)
// update some application state;
Explanation:
When you remove things from the end to the start, you do not have to worry about the validity of positions. As the CListCtrl does not provide a GetPrevItem or any other way to get items in the reverse order, you need to store them in a collection where you can have that reverse order.
The most practical way to do it is to use a stack. Due to the way it works, you will put things in there in the normal order, and when you retrieve things they are automatically in reverse order.

How to delete all rows from QTableWidget

I am trying to delete all rows from a QTableWidget . Here is what I tried.
for ( int i = 0; i < mTestTable->rowCount(); ++i )
{
mTestTable->removeRow(i);
}
I had two rows in my table. But this just deleted a single row. A reason could be that I did not create the the table with a fixed table size. The Qt Documentation for rowCount() says,
This property holds the number of rows in the table.
By default, for a table constructed without row and column counts,
this property contains a value of 0.
So if that is the case, what is the best way to remove all rows from table?
Just set the row count to 0 with:
mTestTable->setRowCount(0);
it will delete the QTableWidgetItems automatically, by calling removeRows as you can see in QTableWidget internal model code:
void QTableModel::setRowCount(int rows)
{
int rc = verticalHeaderItems.count();
if (rows < 0 || rc == rows)
return;
if (rc < rows)
insertRows(qMax(rc, 0), rows - rc);
else
removeRows(qMax(rows, 0), rc - rows);
}
I don't know QTableWidget but your code seems to have a logic flaw. You are forgetting that as you go round the loop you are decreasing the value of mTestTable->rowCount(). After you have removed one row, i will be one and mTestTable->rowCount() will also be one, so your loop stops.
I would do it like this
while (mTestTable->rowCount() > 0)
{
mTestTable->removeRow(0);
}
AFAIK setRowCount(0) removes nothing. Objects are still there, but no more visible.
yourtable->model()->removeRows(0, yourtable->rowCount());
QTableWidget test;
test.clear();
test.setRowCount( 0);
The simple way to delete rows is to set the row count to zero. This uses removeRows() internally.
table->setRowCount(0);
You could also clear the content and then remove all rows.
table->clearContents();
table->model()->removeRows(0, table->rowCount());
Both snippets leave the headers untouched!
If you need to get rid of headers, too, you could switch from clearContents() to clear().
In order to prevent an app crash, disconnect all signals from the QTableView.
// Deselects all selected items
ui->tableWidget->clearSelection();
// Disconnect all signals from table widget ! important !
ui->tableWidget->disconnect();
// Remove all items
ui->tableWidget->clearContents();
// Set row count to 0 (remove rows)
ui->tableWidget->setRowCount(0);
Look this post : http://forum.qt.io/topic/1715/qtablewidget-how-to-delete-a-row
QList<QTableWidgetItem*> items = table.findItems(.....);
QMap<int, int> rowsMap;
for(int i = 0; i < items.count(); i++{
rowsMap[items.at(i).row()] = -1; //garbage value
}
QList<int> rowsList = rowsMap.uniqueKeys();
qSort(rowsList);
//Now go through your table and delete rows in descending order as content would shift up and hence cannot do it in ascending order with ease.
for(int i = rowList.count() - 1; i >= 0; i--){
table.removeRow(rowList.at(i));
}
You can just add empty item model (QStandardItemModel) to your QTableView (myTableView):
itemModel = new QStandardItemModel;
ui->myTableView->setModel(itemModel);
In python you can just set the rowCount to zero and that'll work!
tableWidget.setRowCount(0)
This code is tested with PySide6. Hope it will work for PyQt5 and PyQt6 too.
Your code does not delete last row.
Try this one.
int totalRow = mTestTable->rowCount();
for ( int i = 0; i < totalRow ; ++i )
{
mTestTable->removeRow(i);
}
In your code, on the first time, rowCount() have value 2 and value of the i is 0, so its delete 1st row,
But on the second time value of i incremented with 1, but rowCount() return the updated row count which is now 1, so, it does not delete the last row.
Hope now you ll be clear.
Removes all items not in the headers from the view. This will also remove all selections. The table dimensions stay the same.
void QTableWidget::clearContents()
Removes all items in the view. This will also remove all selections and headers.
void QTableWidget::clear()
This works for me:
for i in reversed(range(self.tableWidget.rowCount())):
self.tableWidget.removeRow(i)

Filling column in List Contol with setItemText

I'm trying to fill my column in List control (m_listCtrl) in MFC. I want to do it in that way :
void CMy1domacanalogaRadixDlg::sort()
{
// some code here
for (int i = 0; i <size; i++)
{
counter++;
a[i] = b[i];
niz.Format(_T("%d"), a[i]);
// here choose column and add numbers
m_listCtrl.SetItemText(i,counter,niz);
}
}
I want to create columns dynamically, because I want to add every for loop iteration in own column, but the problem is that I need to press button "Sort" on my form twice to display numbers in column. It's all work fine, I have all numbers in column but not on first button press. Thanks for help.