reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

listview.cpp (17631B)


      1 /* ReaPack: Package manager for REAPER
      2  * Copyright (C) 2015-2025  Christian Fillion
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU Lesser General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU Lesser General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 #include "listview.hpp"
     19 
     20 #include "iconlist.hpp"
     21 #include "menu.hpp"
     22 #include "time.hpp"
     23 #include "version.hpp"
     24 #include "win32.hpp"
     25 
     26 #include <boost/algorithm/string/case_conv.hpp>
     27 #include <cassert>
     28 #include <reaper_plugin_secrets.h>
     29 
     30 static int adjustWidth(const int points)
     31 {
     32 #ifdef _WIN32
     33   if(points < 1)
     34     return points;
     35   else // magic number to make pretty sizes...
     36     return static_cast<int>(std::ceil(points * 0.863));
     37 #else
     38   return points;
     39 #endif
     40 }
     41 
     42 ListView::ListView(HWND handle, const Columns &columns)
     43   : Control(handle), m_dirty(0), m_customizable(false), m_sort(), m_defaultSort()
     44 {
     45   for(const Column &col : columns)
     46     addColumn(col);
     47 
     48   int style = LVS_EX_FULLROWSELECT;
     49 
     50 #ifdef LVS_EX_LABELTIP
     51   // unsupported by SWELL, but always enabled on macOS anyway
     52   style |= LVS_EX_LABELTIP;
     53 #endif
     54 
     55 #ifdef LVS_EX_DOUBLEBUFFER
     56   style |= LVS_EX_DOUBLEBUFFER;
     57 #endif
     58 
     59   setExStyle(style);
     60 }
     61 
     62 void ListView::setExStyle(const int style, const bool enable)
     63 {
     64   ListView_SetExtendedListViewStyleEx(handle(), style, enable ? style : 0);
     65 }
     66 
     67 int ListView::addColumn(const Column &col)
     68 {
     69   assert(m_rows.empty());
     70 
     71   LVCOLUMN item{};
     72 
     73   item.mask |= LVCF_WIDTH;
     74   item.cx = col.test(CollapseFlag) ? 0 : adjustWidth(col.width);
     75 
     76   const auto &&label = Win32::widen(col.label);
     77   if(!col.test(NoLabelFlag)) {
     78     item.mask |= LVCF_TEXT;
     79     item.pszText = const_cast<Win32::char_type *>(label.c_str());
     80   }
     81 
     82   const int index = columnCount();
     83   ListView_InsertColumn(handle(), index, &item);
     84   m_cols.push_back(col);
     85 
     86   if(m_sort && m_sort->column == index)
     87     setSortArrow(true);
     88 
     89   return index;
     90 }
     91 
     92 ListView::Row *ListView::createRow(void *data)
     93 {
     94   const int index = rowCount();
     95   insertItem(index, index);
     96 
     97   return m_rows.emplace_back(std::make_unique<Row>(data, this)).get();
     98 }
     99 
    100 void ListView::insertItem(const int viewIndex, const int rowIndex)
    101 {
    102   LVITEM item{};
    103   item.iItem = viewIndex;
    104 
    105   item.mask |= LVIF_PARAM;
    106   item.lParam = rowIndex;
    107 
    108   ListView_InsertItem(handle(), &item);
    109 }
    110 
    111 void ListView::updateCell(int row, int cell)
    112 {
    113   const int viewRowIndex = translate(row);
    114   const auto &&text = Win32::widen(m_rows[row]->cell(cell).value);
    115 
    116   ListView_SetItemText(handle(), viewRowIndex, cell,
    117     const_cast<Win32::char_type *>(text.c_str()));
    118 
    119   if(m_sort && m_sort->column == cell)
    120     m_dirty |= NeedSortFlag;
    121 
    122   m_dirty |= NeedFilterFlag;
    123 }
    124 
    125 void ListView::enableIcons()
    126 {
    127   static IconList list({IconList::UncheckedIcon, IconList::CheckedIcon});
    128 
    129   // NOTE: the list must have the LVS_SHAREIMAGELISTS style to prevent
    130   // it from taking ownership of the image list
    131   ListView_SetImageList(handle(), list.handle(), LVSIL_SMALL);
    132 }
    133 
    134 void ListView::setRowIcon(const int row, const int image)
    135 {
    136   LVITEM item{};
    137   item.iItem = translate(row);
    138   item.iImage = image;
    139   item.mask |= LVIF_IMAGE;
    140 
    141   ListView_SetItem(handle(), &item);
    142 }
    143 
    144 void ListView::removeRow(const int userIndex)
    145 {
    146   // translate to view index before fixing lParams
    147   const int viewIndex = translate(userIndex);
    148 
    149   // shift lParam and userIndex of subsequent rows to reflect the new indexes
    150   const int size = rowCount();
    151   for(int i = userIndex + 1; i < size; i++) {
    152     m_rows[i]->userIndex = i - 1;
    153 
    154     LVITEM item{};
    155     item.iItem = translate(i);
    156     item.mask |= LVIF_PARAM;
    157     item.lParam = m_rows[i]->userIndex;
    158     ListView_SetItem(handle(), &item);
    159   }
    160 
    161   ListView_DeleteItem(handle(), viewIndex);
    162   m_rows.erase(m_rows.begin() + userIndex);
    163 
    164   reindexVisible(); // do it now so further removeRow will work as expected
    165 }
    166 
    167 void ListView::resizeColumn(const int index, const int width)
    168 {
    169   ListView_SetColumnWidth(handle(), index, adjustWidth(width));
    170 }
    171 
    172 int ListView::columnWidth(const int index) const
    173 {
    174   return ListView_GetColumnWidth(handle(), index);
    175 }
    176 
    177 int ListView::columnCount() const
    178 {
    179 #ifdef __GNUC__
    180   // workaround for Walloc-size-larger-than in GCC 14 when LTO is enabled
    181   if(m_cols.size() > INT_MAX)
    182     __builtin_unreachable();
    183 #endif
    184 
    185   return static_cast<int>(m_cols.size());
    186 }
    187 
    188 void ListView::sort()
    189 {
    190   static const auto compare = [](LPARAM aRow, LPARAM bRow, LPARAM param)
    191   {
    192     const int indexDiff = static_cast<int>(aRow - bRow);
    193 
    194     ListView *view = reinterpret_cast<ListView *>(param);
    195 
    196     if(!view->m_sort)
    197       return indexDiff;
    198 
    199     const int columnIndex = view->m_sort->column;
    200     const Column &column = view->m_cols[columnIndex];
    201 
    202     int ret = column.compare(view->row(aRow)->cell(columnIndex),
    203       view->row(bRow)->cell(columnIndex));
    204 
    205     if(view->m_sort->order == DescendingOrder)
    206       ret = -ret;
    207 
    208     return ret ? ret : indexDiff;
    209   };
    210 
    211   ListView_SortItems(handle(), compare, reinterpret_cast<LPARAM>(this));
    212 
    213   m_dirty = (m_dirty | NeedReindexFlag) & ~NeedSortFlag;
    214 }
    215 
    216 void ListView::sortByColumn(const int index, const SortOrder order, const bool user)
    217 {
    218   if(m_sort)
    219     setSortArrow(false);
    220 
    221   const Sort settings{index, order};
    222 
    223   if(!user)
    224     m_defaultSort = settings;
    225 
    226   m_sort = settings;
    227   m_dirty |= NeedSortFlag;
    228 
    229   setSortArrow(true);
    230 }
    231 
    232 void ListView::setSortArrow(const bool set)
    233 {
    234   if(!m_sort)
    235     return;
    236 
    237   HWND header = ListView_GetHeader(handle());
    238 
    239   HDITEM item{};
    240   item.mask |= HDI_FORMAT;
    241 
    242   if(!Header_GetItem(header, m_sort->column, &item))
    243     return;
    244 
    245   item.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP); // clear
    246 
    247   if(set) {
    248     switch(m_sort->order) {
    249     case AscendingOrder:
    250       item.fmt |= HDF_SORTUP;
    251       break;
    252     case DescendingOrder:
    253       item.fmt |= HDF_SORTDOWN;
    254     }
    255   }
    256 
    257   Header_SetItem(header, m_sort->column, &item);
    258 }
    259 
    260 void ListView::filter()
    261 {
    262   std::vector<int> hide;
    263 
    264   for(int ri = 0; ri < rowCount(); ++ri) {
    265     const auto &row = m_rows[ri];
    266 
    267     if(m_filter.match(row->filterValues())) {
    268       if(row->viewIndex == -1) {
    269         row->viewIndex = visibleRowCount();
    270         insertItem(row->viewIndex, ri);
    271 
    272         for(int ci = 0; ci < columnCount(); ++ci)
    273           updateCell(ri, ci);
    274 
    275         m_dirty |= NeedSortFlag;
    276       }
    277     }
    278     else if(row->viewIndex > -1) {
    279       hide.emplace_back(row->viewIndex);
    280       row->viewIndex = -1;
    281     }
    282   }
    283 
    284   std::sort(hide.begin(), hide.end());
    285   for(int i = 0; i < static_cast<int>(hide.size()); ++i) {
    286     ListView_DeleteItem(handle(), hide[i] - i);
    287     m_dirty |= NeedReindexFlag;
    288   }
    289 
    290   m_dirty &= ~NeedFilterFlag;
    291 }
    292 
    293 void ListView::setFilter(const std::string &newFilter)
    294 {
    295   m_filter = newFilter;
    296   m_dirty |= NeedFilterFlag;
    297 }
    298 
    299 void ListView::reindexVisible()
    300 {
    301   const int visibleCount = visibleRowCount();
    302   for(int viewIndex = 0; viewIndex < visibleCount; viewIndex++) {
    303     LVITEM item{};
    304     item.iItem = viewIndex;
    305     item.mask |= LVIF_PARAM;
    306     ListView_GetItem(handle(), &item);
    307 
    308     row(item.lParam)->viewIndex = viewIndex;
    309   }
    310 
    311   m_dirty &= ~NeedReindexFlag;
    312 }
    313 
    314 void ListView::endEdit()
    315 {
    316   if(m_dirty & NeedFilterFlag)
    317     filter(); // filter may set NeedSortFlag
    318   if(m_dirty & NeedSortFlag)
    319     sort(); // sort may set NeedReindexFlag
    320   if(m_dirty & NeedReindexFlag)
    321     reindexVisible();
    322 
    323   assert(!m_dirty);
    324 }
    325 
    326 void ListView::clear()
    327 {
    328   ListView_DeleteAllItems(handle());
    329 #ifdef __APPLE__
    330   // NSTableView preverves the previous selection when removing rows after
    331   // beginUpdates is called (via WM_SETREDRAW=0 in SWELL)
    332   unselectAll();
    333 #endif
    334 
    335   m_rows.clear();
    336 }
    337 
    338 void ListView::reset()
    339 {
    340   clear();
    341 
    342   for(int i = columnCount(); i > 0; i--)
    343     ListView_DeleteColumn(handle(), i - 1);
    344 
    345   m_cols.clear();
    346 
    347   m_customizable = false;
    348   m_sort = std::nullopt;
    349   m_defaultSort = std::nullopt;
    350 }
    351 
    352 void ListView::setSelected(const int index, const bool select)
    353 {
    354   ListView_SetItemState(handle(), translate(index),
    355     select ? LVIS_SELECTED : 0, LVIS_SELECTED);
    356 }
    357 
    358 void ListView::selectAll()
    359 {
    360   InhibitControl inhibit(this);
    361   select(-1);
    362 }
    363 
    364 void ListView::unselectAll()
    365 {
    366   InhibitControl inhibit(this);
    367   unselect(-1);
    368 }
    369 
    370 int ListView::visibleRowCount() const
    371 {
    372   return ListView_GetItemCount(handle());
    373 }
    374 
    375 int ListView::currentIndex() const
    376 {
    377   const int internalIndex = ListView_GetNextItem(handle(), -1, LVNI_SELECTED);
    378 
    379   if(internalIndex < 0)
    380     return -1;
    381   else
    382     return translateBack(internalIndex);
    383 }
    384 
    385 std::vector<int> ListView::selection(const bool sort) const
    386 {
    387   int index = -1;
    388   std::vector<int> selectedIndexes;
    389 
    390   while((index = ListView_GetNextItem(handle(), index, LVNI_SELECTED)) != -1)
    391     selectedIndexes.push_back(translateBack(index));
    392 
    393   if(sort)
    394     std::sort(selectedIndexes.begin(), selectedIndexes.end());
    395 
    396   return selectedIndexes;
    397 }
    398 
    399 int ListView::selectionSize() const
    400 {
    401   return ListView_GetSelectedCount(handle());
    402 }
    403 
    404 bool ListView::headerHitTest(const int x, const int y) const
    405 {
    406 #ifdef _WIN32
    407   RECT rect;
    408   GetWindowRect(ListView_GetHeader(handle()), &rect);
    409 
    410   const int headerHeight = rect.bottom - rect.top;
    411 #elif !defined(__APPLE__)
    412   const int headerHeight = SWELL_GetListViewHeaderHeight(handle());
    413 #endif
    414 
    415   POINT point{x, y};
    416   ScreenToClient(handle(), &point);
    417 
    418 #ifdef __APPLE__
    419   // This was broken on Linux and used a hard-coded header height on Windows
    420   // Fixed in REAPER v6.03
    421   return ListView_HeaderHitTest(handle(), point);
    422 #else
    423   return point.y <= headerHeight;
    424 #endif
    425 }
    426 
    427 int ListView::itemUnder(const int x, const int y, bool *overIcon) const
    428 {
    429   LVHITTESTINFO test{{x, y}};
    430   ScreenToClient(handle(), &test.pt);
    431   ListView_SubItemHitTest(handle(), &test);
    432 
    433   if(overIcon) {
    434     *overIcon = test.iSubItem == 0 &&
    435       (test.flags & (LVHT_ONITEMICON | LVHT_ONITEMSTATEICON)) != 0 &&
    436       (test.flags & LVHT_ONITEMLABEL) == 0;
    437   }
    438 
    439   return translateBack(test.iItem);
    440 }
    441 
    442 int ListView::itemUnderMouse(bool *overIcon) const
    443 {
    444   POINT point;
    445   GetCursorPos(&point);
    446   return itemUnder(point.x, point.y, overIcon);
    447 }
    448 
    449 int ListView::scroll() const
    450 {
    451   return ListView_GetTopIndex(handle());
    452 }
    453 
    454 void ListView::setScroll(const int index)
    455 {
    456 #ifdef ListView_GetItemPosition
    457   if(index < 0)
    458     return;
    459 
    460   RECT rect;
    461   ListView_GetViewRect(handle(), &rect);
    462 
    463   POINT itemPos{};
    464   if(ListView_GetItemPosition(handle(), index - 1, &itemPos))
    465     ListView_Scroll(handle(), abs(rect.left) + itemPos.x, abs(rect.top) + itemPos.y);
    466 #endif
    467 }
    468 
    469 void ListView::autoSizeHeader()
    470 {
    471 #ifdef LVSCW_AUTOSIZE_USEHEADER
    472   resizeColumn(columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER);
    473 #endif
    474 }
    475 
    476 void ListView::onNotify(LPNMHDR info, LPARAM lParam)
    477 {
    478   switch(info->code) {
    479   case LVN_ITEMCHANGED:
    480     onItemChanged(lParam);
    481     break;
    482   case NM_CLICK:
    483   case NM_DBLCLK:
    484     onClick(info->code == NM_DBLCLK);
    485     break;
    486   case LVN_COLUMNCLICK:
    487     onColumnClick(lParam);
    488     break;
    489   };
    490 }
    491 
    492 bool ListView::onContextMenu(HWND dialog, int x, int y)
    493 {
    494   SetFocus(handle());
    495 
    496   const bool keyboardTrigger = x == -1 && y == -1;
    497 
    498   if(!keyboardTrigger && headerHitTest(x, y)) {
    499     if(m_customizable) // show menu only if header is customizable
    500       headerMenu(x, y);
    501     return true;
    502   }
    503 
    504 #ifdef ListView_GetItemPosition // unsuported by SWELL
    505   int index;
    506 
    507   // adjust the context menu's position when using Shift+F10 on Windows
    508   if(keyboardTrigger) {
    509     index = currentIndex();
    510 
    511     // find the location of the current item or of the first item
    512     POINT itemPos{};
    513     ListView_GetItemPosition(handle(), translate(std::max(0, index)), &itemPos);
    514     ClientToScreen(handle(), &itemPos);
    515 
    516     RECT controlRect;
    517     GetWindowRect(handle(), &controlRect);
    518 
    519     x = std::max(controlRect.left, std::min(itemPos.x, controlRect.right));
    520     y = std::max(controlRect.top, std::min(itemPos.y, controlRect.bottom));
    521   }
    522   else
    523     index = itemUnder(x, y);
    524 #else
    525   const int index = itemUnder(x, y);
    526 #endif
    527 
    528   Menu menu;
    529 
    530   if(!onFillContextMenu(menu, index).value_or(false))
    531     return false;
    532 
    533   menu.show(x, y, dialog);
    534 
    535   return true;
    536 }
    537 
    538 void ListView::onItemChanged(const LPARAM lParam)
    539 {
    540 #ifdef _WIN32
    541   const auto info = reinterpret_cast<LPNMLISTVIEW>(lParam);
    542 
    543   if(info->uChanged & LVIF_STATE)
    544 #endif
    545     onSelect();
    546 }
    547 
    548 void ListView::onClick(const bool dbclick)
    549 {
    550   bool overIcon;
    551 
    552   if(itemUnderMouse(&overIcon) > -1 && currentIndex() > -1) {
    553     if(dbclick)
    554       onActivate();
    555     else if(overIcon)
    556       onIconClick();
    557   }
    558 }
    559 
    560 void ListView::onColumnClick(const LPARAM lParam)
    561 {
    562   const auto info = reinterpret_cast<LPNMLISTVIEW>(lParam);
    563   const int col = info->iSubItem;
    564   SortOrder order = AscendingOrder;
    565 
    566   if(m_sort && col == m_sort->column) {
    567     switch(m_sort->order) {
    568     case AscendingOrder:
    569       order = DescendingOrder;
    570       break;
    571     case DescendingOrder:
    572       order = AscendingOrder;
    573       break;
    574     }
    575   }
    576 
    577   sortByColumn(col, order, true);
    578   endEdit();
    579 }
    580 
    581 int ListView::translate(const int userIndex) const
    582 {
    583   if(!m_sort || userIndex < 0)
    584     return userIndex;
    585   else
    586     return row(userIndex)->viewIndex;
    587 }
    588 
    589 int ListView::translateBack(const int internalIndex) const
    590 {
    591   if(!m_sort || internalIndex < 0)
    592     return internalIndex;
    593 
    594   LVITEM item{};
    595   item.iItem = internalIndex;
    596   item.mask |= LVIF_PARAM;
    597 
    598   if(ListView_GetItem(handle(), &item))
    599     return static_cast<int>(item.lParam);
    600   else
    601     return -1;
    602 }
    603 
    604 void ListView::headerMenu(const int x, const int y)
    605 {
    606   enum { ACTION_RESTORE = 800 };
    607 
    608   Menu menu;
    609   menu.disable(menu.addAction("Visible columns:", 0));
    610 
    611   for(int i = 0; i < columnCount(); i++) {
    612     const auto id = menu.addAction(m_cols[i].label.c_str(), i | (1 << 8));
    613 
    614     if(columnWidth(i))
    615       menu.check(id);
    616   }
    617 
    618   menu.addSeparator();
    619   menu.addAction("Reset columns", ACTION_RESTORE);
    620 
    621   const int cmd = menu.show(x, y, handle());
    622 
    623   if(cmd == ACTION_RESTORE)
    624     resetColumns();
    625   else if(cmd >> 8 == 1) {
    626     const int col = cmd & 0xff;
    627     resizeColumn(col, columnWidth(col) ? 0 : m_cols[col].width);
    628   }
    629 }
    630 
    631 void ListView::resetColumns()
    632 {
    633   std::vector<int> order(columnCount());
    634 
    635   for(int i = 0; i < columnCount(); i++) {
    636     order[i] = i;
    637 
    638     const Column &col = m_cols[i];
    639     resizeColumn(i, col.test(CollapseFlag) ? 0 : col.width);
    640   }
    641 
    642   ListView_SetColumnOrderArray(handle(), columnCount(), order.data());
    643 
    644   if(m_sort) {
    645     setSortArrow(false);
    646     m_sort = m_defaultSort;
    647     setSortArrow(true);
    648 
    649     m_dirty |= NeedSortFlag;
    650     endEdit();
    651   }
    652 }
    653 
    654 void ListView::restoreState(Serializer::Data &data)
    655 {
    656   m_customizable = true;
    657   setExStyle(LVS_EX_HEADERDRAGDROP); // enable column reordering
    658 
    659   if(data.empty())
    660     return;
    661 
    662   int col = -1;
    663   std::vector<int> order(columnCount());
    664 
    665   while(col < columnCount() && !data.empty()) {
    666     const auto &[left, right] = data.front();
    667 
    668     switch(col) {
    669     case -1: // sort
    670       if(left >= 0 && left < columnCount())
    671         sortByColumn(left, right == 0 ? AscendingOrder : DescendingOrder, true);
    672       break;
    673     default: // column
    674       order[col] = left;
    675       // raw size should not go through adjustSize (via resizeColumn)
    676       ListView_SetColumnWidth(handle(), col, right);
    677       break;
    678     }
    679 
    680     data.pop_front(); // deletes rec
    681     ++col;
    682   }
    683 
    684   // fill default values for any columns whose state wasn't saved
    685   // (col can't be -1 at this point, the loop above is always run at least once)
    686   while(col < columnCount()) {
    687     order[col] = col;
    688     ++col;
    689   }
    690 
    691   ListView_SetColumnOrderArray(handle(), columnCount(), order.data());
    692 }
    693 
    694 void ListView::saveState(Serializer::Data &data) const
    695 {
    696   const Sort &sort = m_sort.value_or(Sort{});
    697   std::vector<int> order(columnCount());
    698   ListView_GetColumnOrderArray(handle(), columnCount(), order.data());
    699 
    700   data.push_back({sort.column, sort.order});
    701 
    702   for(int i = 0; i < columnCount(); i++)
    703     data.push_back({order[i], columnWidth(i)});
    704 }
    705 
    706 int ListView::Column::compare(const ListView::Cell &cl, const ListView::Cell &cr) const
    707 {
    708   if(dataType) {
    709     if(!cl.userData)
    710       return -1;
    711     else if(!cr.userData)
    712       return 1;
    713   }
    714 
    715   switch(dataType) {
    716   case UserType: { // arbitrary data or no data: sort by visible text
    717     std::string l = cl.value, r = cr.value;
    718 
    719     boost::algorithm::to_lower(l);
    720     boost::algorithm::to_lower(r);
    721 
    722     return l.compare(r);
    723   }
    724   case VersionType:
    725     return static_cast<const VersionName *>(cl.userData)->compare(
    726       *static_cast<const VersionName *>(cr.userData));
    727   case TimeType:
    728     return static_cast<const Time *>(cl.userData)->compare(
    729       *static_cast<const Time *>(cr.userData));
    730   }
    731 
    732   return 0; // to make MSVC happy
    733 }
    734 
    735 ListView::Row::Row(void *data, ListView *list)
    736   : userData(data), viewIndex(list->rowCount()), userIndex(viewIndex),
    737   m_list(list), m_cells(new Cell[m_list->columnCount()])
    738 {
    739 }
    740 
    741 void ListView::Row::setCell(const int i, const std::string &val, void *data)
    742 {
    743   Cell &cell = m_cells[i];
    744   cell.value = val;
    745   cell.userData = data;
    746 
    747   m_list->updateCell(userIndex, i);
    748 }
    749 
    750 void ListView::Row::setChecked(bool checked)
    751 {
    752   m_list->setRowIcon(userIndex, checked);
    753 }
    754 
    755 std::vector<std::string> ListView::Row::filterValues() const
    756 {
    757   std::vector<std::string> values;
    758 
    759   for(int ci = 0; ci < m_list->columnCount(); ++ci) {
    760     if(m_list->column(ci).test(FilterFlag))
    761       values.push_back(m_cells[ci].value);
    762   }
    763 
    764   return values;
    765 }