mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
436c437189
add compiler directives
281 lines
9.1 KiB
C++
281 lines
9.1 KiB
C++
//+----------------------------------------------------------------------------+
|
|
//| Description: Magic Set Editor - Program to make card games |
|
|
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
|
|
//| License: GNU General Public License 2 or later (see file COPYING) |
|
|
//+----------------------------------------------------------------------------+
|
|
|
|
// ----------------------------------------------------------------------------- : Includes
|
|
|
|
#include <util/prec.hpp>
|
|
#include <gui/control/item_list.hpp>
|
|
#include <gui/util.hpp>
|
|
#include <wx/imaglist.h>
|
|
|
|
// ----------------------------------------------------------------------------- : ItemList
|
|
|
|
ItemList::ItemList(Window* parent, int id, long additional_style, bool multi_sel)
|
|
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, additional_style | wxLC_REPORT | wxLC_VIRTUAL | (multi_sel ? 0 : wxLC_SINGLE_SEL))
|
|
, selected_item_pos(-1)
|
|
, sort_by_column(-1), sort_ascending(true)
|
|
{
|
|
// create image list
|
|
wxImageList* il = new wxImageList(18,14);
|
|
il->Add(load_resource_image(_("sort_asc")), Color(255,0,255));
|
|
il->Add(load_resource_image(_("sort_desc")), Color(255,0,255));
|
|
AssignImageList(il, wxIMAGE_LIST_SMALL);
|
|
// Theming things wx fails to do for us
|
|
#if defined(__WXMSW__) && defined(LVS_EX_DOUBLEBUFFER)
|
|
// Fancy theming (on windows)
|
|
enable_themed_selection_rectangle(this);
|
|
// Use double buffering
|
|
ListView_SetExtendedListViewStyle(GetHwnd(), ListView_GetExtendedListViewStyle(GetHwnd()) | LVS_EX_DOUBLEBUFFER);
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ItemList : Selection
|
|
|
|
bool ItemList::canSelectPrevious() const {
|
|
return selected_item_pos - 1 >= 0;
|
|
}
|
|
bool ItemList::canSelectNext() const {
|
|
return selected_item_pos >= 0 && static_cast<size_t>(selected_item_pos + 1) < sorted_list.size();
|
|
}
|
|
bool ItemList::canSelectAll() const {
|
|
return sorted_list.size() > 0 && !(GetWindowStyle() & wxLC_SINGLE_SEL);
|
|
}
|
|
void ItemList::selectPrevious() {
|
|
assert(selected_item_pos >= 1);
|
|
focusNone();
|
|
selectItemPos(selected_item_pos - 1, true, true);
|
|
}
|
|
void ItemList::selectNext() {
|
|
assert(selected_item_pos + 1 < (long)sorted_list.size());
|
|
focusNone();
|
|
selectItemPos(selected_item_pos + 1, true, true);
|
|
}
|
|
void ItemList::selectFirst() {
|
|
if (sorted_list.empty()) return;
|
|
selectItemPos(0, true);
|
|
}
|
|
void ItemList::doSelectAll() {
|
|
long count = GetItemCount();
|
|
for (long pos = 0; pos < count; ++pos) {
|
|
Select(pos,true);
|
|
}
|
|
}
|
|
|
|
bool ItemList::doCut() {
|
|
// cut = copy + delete
|
|
if (!canCut()) return false;
|
|
if (!doCopy()) return false;
|
|
doDelete();
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ItemList : Selection (private)
|
|
|
|
void ItemList::selectItem(const VoidP& item, bool focus, bool event) {
|
|
if (item != selected_item && focus) {
|
|
focusNone();
|
|
}
|
|
selected_item = item;
|
|
if (event) sendEvent(); // sending an event will trigger a UI update
|
|
findSelectedItemPos();
|
|
if (focus) focusSelectedItem();
|
|
}
|
|
|
|
void ItemList::selectItemPos(long pos, bool focus, bool force_focus) {
|
|
VoidP item;
|
|
if ((size_t)pos < sorted_list.size()) {
|
|
item = getItem(pos);
|
|
} else if (!sorted_list.empty()) {
|
|
item = sorted_list.back();
|
|
} else {
|
|
// clear selection
|
|
}
|
|
if (item != selected_item) {
|
|
selectItem(item, false, true);
|
|
}
|
|
//!selected_item_pos = pos;
|
|
if (focus) focusSelectedItem(force_focus);
|
|
}
|
|
|
|
void ItemList::findSelectedItemPos() {
|
|
// find the position of the selected item
|
|
long count = GetItemCount();
|
|
selected_item_pos = -1;
|
|
for (long pos = 0 ; pos < count ; ++pos) {
|
|
if (getItem(pos) == selected_item) {
|
|
selected_item_pos = pos;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
long ItemList::findGivenItemPos(const VoidP& item) {
|
|
long count = GetItemCount();
|
|
for (long pos = 0; pos < count; ++pos) {
|
|
if (getItem(pos) == item) {
|
|
return pos;
|
|
}
|
|
}
|
|
}
|
|
void ItemList::focusSelectedItem(bool force_focus) {
|
|
if (GetItemCount() > 0) {
|
|
if (selected_item_pos == -1 || (size_t)selected_item_pos > sorted_list.size()) {
|
|
// deselect currently selected item, if any
|
|
long sel = GetFirstSelected();
|
|
Select(sel, false);
|
|
} else if (selected_item_pos != GetFocusedItem() || force_focus) {
|
|
Select(selected_item_pos);
|
|
Focus (selected_item_pos);
|
|
}
|
|
}
|
|
}
|
|
void ItemList::focusNone() {
|
|
long count = GetItemCount();
|
|
for (long pos = 0 ; pos < count ; ++pos) {
|
|
Select(pos, false);
|
|
}
|
|
}
|
|
void ItemList::focusItem(const VoidP& item, bool focus) {
|
|
long count = GetItemCount();
|
|
for (long pos = 0 ; pos < count ; ++pos) {
|
|
if (getItem(pos) == item) {
|
|
Select(pos, focus);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
long ItemList::focusCount() const {
|
|
long count = GetItemCount();
|
|
long focused = 0;
|
|
for (long pos = 0 ; pos < count ; ++pos) {
|
|
if (const_cast<ItemList*>(this)->IsSelected(pos)) focused++;
|
|
}
|
|
return focused;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ItemList : Building the list
|
|
|
|
// Comparison object for comparing items
|
|
struct ItemList::ItemComparer {
|
|
ItemComparer(ItemList& list) : list(list) {}
|
|
ItemList& list; // 'this' pointer
|
|
// Compare two items using the current criterium and order
|
|
bool operator () (const VoidP& a, const VoidP& b) {
|
|
if (list.sort_ascending) {
|
|
return list.compareItems(a.get(), b.get());
|
|
} else {
|
|
return list.compareItems(b.get(), a.get());
|
|
}
|
|
}
|
|
};
|
|
|
|
void ItemList::refreshList(bool refresh_current_only) {
|
|
// Get all items
|
|
vector<VoidP> old_sorted_list;
|
|
swap(sorted_list, old_sorted_list);
|
|
getItems(sorted_list);
|
|
// Sort the list
|
|
if (sort_by_column >= 0) {
|
|
stable_sort(sorted_list.begin(), sorted_list.end(), ItemComparer(*this));
|
|
}
|
|
// Has the entire list changed?
|
|
if (refresh_current_only && sorted_list == old_sorted_list) {
|
|
if (selected_item_pos >= 0) RefreshItem(selected_item_pos);
|
|
return;
|
|
}
|
|
// refresh
|
|
// Note: Freeze/Thaw makes flicker worse
|
|
long item_count = (long)sorted_list.size();
|
|
SetItemCount(item_count);
|
|
if (item_count == 0) {
|
|
Refresh();
|
|
} else {
|
|
// (re)select current item
|
|
findSelectedItemPos();
|
|
focusNone();
|
|
focusSelectedItem(true);
|
|
// refresh items
|
|
|
|
RefreshItems(0, item_count - 1);
|
|
}
|
|
}
|
|
|
|
void ItemList::sortBy(long column, bool ascending) {
|
|
// Change image in column header
|
|
long count = GetColumnCount();
|
|
for (long i = 0 ; i < count ; ++i) {
|
|
if (i == column) {
|
|
SetColumnImage(i, ascending ? 0 : 1); // arrow up/down
|
|
} else if (i == sort_by_column) {
|
|
SetColumnImage(i, -1);
|
|
}
|
|
}
|
|
// sort list
|
|
sort_by_column = column;
|
|
sort_ascending = ascending;
|
|
refreshList();
|
|
}
|
|
|
|
void ItemList::SetColumnImage(int col, int image) {
|
|
#if defined(__WXMSW__) && defined(HDF_SORTUP)
|
|
if ( wxApp::GetComCtl32Version() >= 470 ) {
|
|
// use built in sort indicator
|
|
HWND header = ListView_GetHeader(GetHwnd());
|
|
HDITEM header_item = {0};
|
|
header_item.mask = HDI_FORMAT;
|
|
Header_GetItem(header, col, &header_item);
|
|
header_item.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
|
|
if (image == 0) header_item.fmt |= HDF_SORTUP;
|
|
if (image == 1) header_item.fmt |= HDF_SORTDOWN;
|
|
Header_SetItem(header, col, &header_item);
|
|
return;
|
|
}
|
|
#endif
|
|
// The wx version of this function is broken,
|
|
// setting the wxLIST_MASK_IMAGE also sets the FORMAT flag, so we lose alignment info
|
|
wxListItem item;
|
|
item.SetMask(wxLIST_MASK_IMAGE | wxLIST_MASK_FORMAT);
|
|
GetColumn(col, item);
|
|
item.SetImage(image);
|
|
SetColumn(col, item);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ItemList : Window events
|
|
|
|
wxSize ItemList::DoGetBestClientSize() const {
|
|
// wxWidgets decided to be 'smart', and calculate the best size of a list view based on the column header width.
|
|
// But this also sets the minimum window size, so the window can't be made any smaller than that, even on small screens.
|
|
// See issue #9
|
|
// So, return some small size instead.
|
|
return wxSize(10,10);
|
|
}
|
|
|
|
void ItemList::onColumnClick(wxListEvent& ev) {
|
|
long new_sort_by_column = ev.GetColumn();
|
|
if (sort_by_column == new_sort_by_column) {
|
|
if (sort_ascending) {
|
|
sort_ascending = false; // 2nd click on same column -> sort descending
|
|
} else if (mustSort()) {
|
|
sort_ascending = true; // 3rd click on same column -> sort ascending again
|
|
} else {
|
|
new_sort_by_column = -1; // 3rd click on same column -> don't sort
|
|
}
|
|
} else {
|
|
sort_ascending = true;
|
|
}
|
|
sortBy(new_sort_by_column, sort_ascending);
|
|
}
|
|
|
|
void ItemList::onItemFocus(wxListEvent& ev) {
|
|
selectItemPos(ev.GetIndex(), false);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ItemList : Event table
|
|
|
|
BEGIN_EVENT_TABLE(ItemList, wxListView)
|
|
EVT_LIST_COL_CLICK (wxID_ANY, ItemList::onColumnClick)
|
|
EVT_LIST_ITEM_FOCUSED (wxID_ANY, ItemList::onItemFocus)
|
|
END_EVENT_TABLE ()
|