mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 13:37:00 -04:00
Change tabs to two spaces.
This commit is contained in:
+285
-285
@@ -23,159 +23,159 @@ DECLARE_TYPEOF_COLLECTION(ValueViewer*);
|
||||
// ----------------------------------------------------------------------------- : DataEditor
|
||||
|
||||
DataEditor::DataEditor(Window* parent, int id, long style)
|
||||
: CardViewer(parent, id, style | wxWANTS_CHARS)
|
||||
, next_in_tab_order(nullptr)
|
||||
, current_viewer(nullptr)
|
||||
, current_editor(nullptr)
|
||||
, hovered_viewer(nullptr)
|
||||
: CardViewer(parent, id, style | wxWANTS_CHARS)
|
||||
, next_in_tab_order(nullptr)
|
||||
, current_viewer(nullptr)
|
||||
, current_editor(nullptr)
|
||||
, hovered_viewer(nullptr)
|
||||
{
|
||||
// Create a caret
|
||||
SetCaret(new wxCaret(this,1,1));
|
||||
// Create a caret
|
||||
SetCaret(new wxCaret(this,1,1));
|
||||
}
|
||||
|
||||
ValueViewerP DataEditor::makeViewer(const StyleP& style) {
|
||||
return style->makeEditor(*this, style);
|
||||
return style->makeEditor(*this, style);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Utility for ValueViewers
|
||||
|
||||
DrawWhat DataEditor::drawWhat(const ValueViewer* viewer) const {
|
||||
int what = DRAW_NORMAL
|
||||
| DRAW_ACTIVE * viewerIsCurrent(viewer);
|
||||
if (nativeLook()) {
|
||||
what |= DRAW_BOXES | DRAW_EDITING | DRAW_NATIVELOOK | DRAW_ERRORS;
|
||||
} else {
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(set->stylesheetFor(card));
|
||||
what |= DRAW_BORDERS * ss.card_borders()
|
||||
| (DRAW_BOXES | DRAW_EDITING) * ss.card_draw_editing()
|
||||
| DRAW_ERRORS;
|
||||
}
|
||||
return (DrawWhat)what;
|
||||
int what = DRAW_NORMAL
|
||||
| DRAW_ACTIVE * viewerIsCurrent(viewer);
|
||||
if (nativeLook()) {
|
||||
what |= DRAW_BOXES | DRAW_EDITING | DRAW_NATIVELOOK | DRAW_ERRORS;
|
||||
} else {
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(set->stylesheetFor(card));
|
||||
what |= DRAW_BORDERS * ss.card_borders()
|
||||
| (DRAW_BOXES | DRAW_EDITING) * ss.card_draw_editing()
|
||||
| DRAW_ERRORS;
|
||||
}
|
||||
return (DrawWhat)what;
|
||||
}
|
||||
|
||||
bool DataEditor::viewerIsCurrent(const ValueViewer* viewer) const {
|
||||
return viewer == current_viewer && FindFocus() == this;
|
||||
return viewer == current_viewer && FindFocus() == this;
|
||||
}
|
||||
|
||||
|
||||
void DataEditor::addAction(Action* action) {
|
||||
set->actions.addAction(action);
|
||||
set->actions.addAction(action);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Selection
|
||||
|
||||
bool DataEditor::AcceptsFocus() const {
|
||||
return wxWindow::AcceptsFocus();
|
||||
return wxWindow::AcceptsFocus();
|
||||
}
|
||||
|
||||
void DataEditor::select(ValueViewer* v) {
|
||||
ValueEditor* old_editor = current_editor;
|
||||
current_viewer = v;
|
||||
current_editor = v->getEditor();
|
||||
if (current_editor != old_editor) {
|
||||
// selection has changed
|
||||
if (old_editor) old_editor->onLoseFocus();
|
||||
if (current_editor) current_editor->onFocus();
|
||||
onChange();
|
||||
}
|
||||
ValueEditor* old_editor = current_editor;
|
||||
current_viewer = v;
|
||||
current_editor = v->getEditor();
|
||||
if (current_editor != old_editor) {
|
||||
// selection has changed
|
||||
if (old_editor) old_editor->onLoseFocus();
|
||||
if (current_editor) current_editor->onFocus();
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
|
||||
void DataEditor::selectFirst() {
|
||||
selectByTabPos(0, true);
|
||||
selectByTabPos(0, true);
|
||||
}
|
||||
void DataEditor::selectLast() {
|
||||
selectByTabPos((int)by_tab_index.size() - 1, false);
|
||||
selectByTabPos((int)by_tab_index.size() - 1, false);
|
||||
}
|
||||
bool DataEditor::selectNext() {
|
||||
return selectByTabPos(currentTabPos() + 1, true);
|
||||
return selectByTabPos(currentTabPos() + 1, true);
|
||||
}
|
||||
bool DataEditor::selectPrevious() {
|
||||
return selectByTabPos(currentTabPos() - 1, false);
|
||||
return selectByTabPos(currentTabPos() - 1, false);
|
||||
}
|
||||
|
||||
bool DataEditor::selectByTabPos(int tab_pos, bool forward) {
|
||||
while (tab_pos >= 0 && (size_t)tab_pos < by_tab_index.size()) {
|
||||
ValueViewer* v = by_tab_index[tab_pos];
|
||||
if (v->getField()->editable && v->getStyle()->isVisible()) {
|
||||
select(v);
|
||||
return true;
|
||||
}
|
||||
// not enabled, maybe the next one?
|
||||
tab_pos += forward ? 1 : -1;
|
||||
}
|
||||
// deselect
|
||||
if (current_editor) {
|
||||
current_editor->onLoseFocus();
|
||||
onChange();
|
||||
}
|
||||
current_viewer = nullptr;
|
||||
current_editor = nullptr;
|
||||
return false;
|
||||
while (tab_pos >= 0 && (size_t)tab_pos < by_tab_index.size()) {
|
||||
ValueViewer* v = by_tab_index[tab_pos];
|
||||
if (v->getField()->editable && v->getStyle()->isVisible()) {
|
||||
select(v);
|
||||
return true;
|
||||
}
|
||||
// not enabled, maybe the next one?
|
||||
tab_pos += forward ? 1 : -1;
|
||||
}
|
||||
// deselect
|
||||
if (current_editor) {
|
||||
current_editor->onLoseFocus();
|
||||
onChange();
|
||||
}
|
||||
current_viewer = nullptr;
|
||||
current_editor = nullptr;
|
||||
return false;
|
||||
}
|
||||
int DataEditor::currentTabPos() const {
|
||||
int i = 0;
|
||||
FOR_EACH_CONST(v, by_tab_index) {
|
||||
if (v == current_viewer) return i;
|
||||
++i;
|
||||
}
|
||||
return -1;
|
||||
int i = 0;
|
||||
FOR_EACH_CONST(v, by_tab_index) {
|
||||
if (v == current_viewer) return i;
|
||||
++i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct CompareTabIndex {
|
||||
bool operator() (ValueViewer* a, ValueViewer* b) {
|
||||
Style& as = *a->getStyle(), &bs = *b->getStyle();
|
||||
Field& af = *as.fieldP, &bf = *bs.fieldP;
|
||||
if (af.tab_index < bf.tab_index) return true;
|
||||
if (af.tab_index > bf.tab_index) return false;
|
||||
if (fabs(as.top - bs.top) < 15) {
|
||||
// the fields are almost on the same 'row'
|
||||
// compare horizontally first
|
||||
if (as.left < bs.left) return true; // horizontal sorting
|
||||
if (as.left > bs.left) return false;
|
||||
if (as.top < bs.top) return true; // vertical sorting
|
||||
} else {
|
||||
// compare vertically first
|
||||
if (as.top < bs.top) return true; // vertical sorting
|
||||
if (as.top > bs.top) return false;
|
||||
if (as.left < bs.left) return true; // horizontal sorting
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator() (ValueViewer* a, ValueViewer* b) {
|
||||
Style& as = *a->getStyle(), &bs = *b->getStyle();
|
||||
Field& af = *as.fieldP, &bf = *bs.fieldP;
|
||||
if (af.tab_index < bf.tab_index) return true;
|
||||
if (af.tab_index > bf.tab_index) return false;
|
||||
if (fabs(as.top - bs.top) < 15) {
|
||||
// the fields are almost on the same 'row'
|
||||
// compare horizontally first
|
||||
if (as.left < bs.left) return true; // horizontal sorting
|
||||
if (as.left > bs.left) return false;
|
||||
if (as.top < bs.top) return true; // vertical sorting
|
||||
} else {
|
||||
// compare vertically first
|
||||
if (as.top < bs.top) return true; // vertical sorting
|
||||
if (as.top > bs.top) return false;
|
||||
if (as.left < bs.left) return true; // horizontal sorting
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
void DataEditor::createTabIndex() {
|
||||
by_tab_index.clear();
|
||||
FOR_EACH(v, viewers) {
|
||||
ValueEditor* e = v->getEditor();
|
||||
if (e) {
|
||||
by_tab_index.push_back(v.get());
|
||||
}
|
||||
}
|
||||
stable_sort(by_tab_index.begin(), by_tab_index.end(), CompareTabIndex());
|
||||
by_tab_index.clear();
|
||||
FOR_EACH(v, viewers) {
|
||||
ValueEditor* e = v->getEditor();
|
||||
if (e) {
|
||||
by_tab_index.push_back(v.get());
|
||||
}
|
||||
}
|
||||
stable_sort(by_tab_index.begin(), by_tab_index.end(), CompareTabIndex());
|
||||
}
|
||||
void DataEditor::onInit() {
|
||||
createTabIndex();
|
||||
current_viewer = nullptr;
|
||||
current_editor = nullptr;
|
||||
hovered_viewer = nullptr;
|
||||
// hide caret if it is shown
|
||||
wxCaret* caret = GetCaret();
|
||||
if (caret->IsVisible()) caret->Hide();
|
||||
createTabIndex();
|
||||
current_viewer = nullptr;
|
||||
current_editor = nullptr;
|
||||
hovered_viewer = nullptr;
|
||||
// hide caret if it is shown
|
||||
wxCaret* caret = GetCaret();
|
||||
if (caret->IsVisible()) caret->Hide();
|
||||
}
|
||||
// ----------------------------------------------------------------------------- : Search / replace
|
||||
|
||||
bool DataEditor::search(FindInfo& find, bool from_start) {
|
||||
bool include = from_start;
|
||||
for (size_t i = 0 ; i < by_tab_index.size() ; ++i) {
|
||||
ValueViewer& viewer = *by_tab_index[find.forward() ? i : by_tab_index.size() - i - 1];
|
||||
if (&viewer == current_viewer) include = true;
|
||||
if (include && viewer.getField()->editable && viewer.getStyle()->isVisible()) {
|
||||
ValueEditor* editor = viewer.getEditor();
|
||||
if (editor && editor->search(find, from_start || &viewer != current_viewer)) {
|
||||
return true; // done
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // not done
|
||||
bool include = from_start;
|
||||
for (size_t i = 0 ; i < by_tab_index.size() ; ++i) {
|
||||
ValueViewer& viewer = *by_tab_index[find.forward() ? i : by_tab_index.size() - i - 1];
|
||||
if (&viewer == current_viewer) include = true;
|
||||
if (include && viewer.getField()->editable && viewer.getStyle()->isVisible()) {
|
||||
ValueEditor* editor = viewer.getEditor();
|
||||
if (editor && editor->search(find, from_start || &viewer != current_viewer)) {
|
||||
return true; // done
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // not done
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Clipboard & Formatting
|
||||
@@ -193,244 +193,244 @@ void DataEditor::doFormat(int type) { if (current_editor) current_ed
|
||||
|
||||
|
||||
wxMenu* DataEditor::getMenu(int type) const {
|
||||
if (current_editor) {
|
||||
return current_editor->getMenu(type);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
if (current_editor) {
|
||||
return current_editor->getMenu(type);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
void DataEditor::onCommand(int id) {
|
||||
if (current_editor) {
|
||||
current_editor->onCommand(id);
|
||||
}
|
||||
if (current_editor) {
|
||||
current_editor->onCommand(id);
|
||||
}
|
||||
}
|
||||
|
||||
void DataEditor::insert(const String& text, const String& action_name) {
|
||||
if (current_editor) current_editor->insert(text, action_name);
|
||||
if (current_editor) current_editor->insert(text, action_name);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Mouse events
|
||||
|
||||
void DataEditor::onLeftDown(wxMouseEvent& ev) {
|
||||
ev.Skip(); // for focus
|
||||
CaptureMouse();
|
||||
// change selection?
|
||||
selectField(ev, &ValueEditor::onLeftDown);
|
||||
ev.Skip(); // for focus
|
||||
CaptureMouse();
|
||||
// change selection?
|
||||
selectField(ev, &ValueEditor::onLeftDown);
|
||||
}
|
||||
void DataEditor::onLeftUp(wxMouseEvent& ev) {
|
||||
if (HasCapture()) ReleaseMouse();
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
if (current_viewer->containsPoint(pos)) {
|
||||
current_editor->onLeftUp(pos, ev);
|
||||
}
|
||||
}
|
||||
if (HasCapture()) ReleaseMouse();
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
if (current_viewer->containsPoint(pos)) {
|
||||
current_editor->onLeftUp(pos, ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
void DataEditor::onLeftDClick(wxMouseEvent& ev) {
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
if (current_viewer->containsPoint(pos)) {
|
||||
current_editor->onLeftDClick(pos, ev);
|
||||
}
|
||||
}
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
if (current_viewer->containsPoint(pos)) {
|
||||
current_editor->onLeftDClick(pos, ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
void DataEditor::onRightDown(wxMouseEvent& ev) {
|
||||
ev.Skip(); // for context menu
|
||||
// change selection?
|
||||
selectField(ev, &ValueEditor::onRightDown);
|
||||
ev.Skip(); // for context menu
|
||||
// change selection?
|
||||
selectField(ev, &ValueEditor::onRightDown);
|
||||
}
|
||||
void DataEditor::onMouseWheel(wxMouseEvent& ev) {
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
if (current_viewer->containsPoint(pos)) {
|
||||
if (current_editor->onMouseWheel(pos, ev)) return;
|
||||
}
|
||||
}
|
||||
ev.Skip();
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
if (current_viewer->containsPoint(pos)) {
|
||||
if (current_editor->onMouseWheel(pos, ev)) return;
|
||||
}
|
||||
}
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void DataEditor::onMotion(wxMouseEvent& ev) {
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
current_editor->onMotion(pos, ev);
|
||||
}
|
||||
if (!HasCapture()) {
|
||||
// find editor under mouse
|
||||
ValueViewer* new_hovered_viewer = nullptr;
|
||||
FOR_EACH_REVERSE(v,viewers) { // find high z index fields first
|
||||
RealPoint pos = mousePoint(ev, *v);
|
||||
if (v->containsPoint(pos) && v->getField()->editable) {
|
||||
new_hovered_viewer = v.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hovered_viewer && hovered_viewer != new_hovered_viewer) {
|
||||
ValueEditor* e = hovered_viewer->getEditor();
|
||||
RealPoint pos = mousePoint(ev, *hovered_viewer);
|
||||
if (e) e->onMouseLeave(pos, ev);
|
||||
}
|
||||
hovered_viewer = new_hovered_viewer;
|
||||
// change cursor and set status text
|
||||
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
|
||||
if (hovered_viewer) {
|
||||
ValueEditor* e = hovered_viewer->getEditor();
|
||||
RealPoint pos = mousePoint(ev, *hovered_viewer);
|
||||
wxCursor c;
|
||||
if (e) c = e->cursor(pos);
|
||||
if (c.Ok()) SetCursor(c);
|
||||
else SetCursor(wxCURSOR_ARROW);
|
||||
if (frame) frame->SetStatusText(hovered_viewer->getField()->description);
|
||||
} else {
|
||||
// no field under cursor
|
||||
SetCursor(wxCURSOR_ARROW);
|
||||
if (frame) frame->SetStatusText(wxEmptyString);
|
||||
}
|
||||
}
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
current_editor->onMotion(pos, ev);
|
||||
}
|
||||
if (!HasCapture()) {
|
||||
// find editor under mouse
|
||||
ValueViewer* new_hovered_viewer = nullptr;
|
||||
FOR_EACH_REVERSE(v,viewers) { // find high z index fields first
|
||||
RealPoint pos = mousePoint(ev, *v);
|
||||
if (v->containsPoint(pos) && v->getField()->editable) {
|
||||
new_hovered_viewer = v.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hovered_viewer && hovered_viewer != new_hovered_viewer) {
|
||||
ValueEditor* e = hovered_viewer->getEditor();
|
||||
RealPoint pos = mousePoint(ev, *hovered_viewer);
|
||||
if (e) e->onMouseLeave(pos, ev);
|
||||
}
|
||||
hovered_viewer = new_hovered_viewer;
|
||||
// change cursor and set status text
|
||||
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
|
||||
if (hovered_viewer) {
|
||||
ValueEditor* e = hovered_viewer->getEditor();
|
||||
RealPoint pos = mousePoint(ev, *hovered_viewer);
|
||||
wxCursor c;
|
||||
if (e) c = e->cursor(pos);
|
||||
if (c.Ok()) SetCursor(c);
|
||||
else SetCursor(wxCURSOR_ARROW);
|
||||
if (frame) frame->SetStatusText(hovered_viewer->getField()->description);
|
||||
} else {
|
||||
// no field under cursor
|
||||
SetCursor(wxCURSOR_ARROW);
|
||||
if (frame) frame->SetStatusText(wxEmptyString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataEditor::onMouseLeave(wxMouseEvent& ev) {
|
||||
// on mouse leave for editor
|
||||
if (hovered_viewer) {
|
||||
ValueEditor* e = hovered_viewer->getEditor();
|
||||
if (e) e->onMouseLeave(mousePoint(ev,*hovered_viewer), ev);
|
||||
hovered_viewer = nullptr;
|
||||
}
|
||||
// clear status text
|
||||
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
|
||||
if (frame) frame->SetStatusText(wxEmptyString);
|
||||
// on mouse leave for editor
|
||||
if (hovered_viewer) {
|
||||
ValueEditor* e = hovered_viewer->getEditor();
|
||||
if (e) e->onMouseLeave(mousePoint(ev,*hovered_viewer), ev);
|
||||
hovered_viewer = nullptr;
|
||||
}
|
||||
// clear status text
|
||||
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
|
||||
if (frame) frame->SetStatusText(wxEmptyString);
|
||||
}
|
||||
|
||||
void DataEditor::selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&)) {
|
||||
// change viewer/editor
|
||||
ValueEditor* old_editor = current_editor;
|
||||
selectFieldNoEvents(ev);
|
||||
if (old_editor != current_editor) {
|
||||
// selection has changed, send focus events
|
||||
if (old_editor) old_editor->onLoseFocus();
|
||||
if (current_editor) current_editor->onFocus();
|
||||
}
|
||||
// pass event
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
if (current_viewer->containsPoint(pos)) {
|
||||
(current_editor->*event)(pos, ev);
|
||||
}
|
||||
}
|
||||
// refresh?
|
||||
if (old_editor != current_editor) {
|
||||
// selection has changed, refresh viewers
|
||||
// NOTE: after passing mouse down event, otherwise opening combo box produces flicker
|
||||
onChange();
|
||||
}
|
||||
// change viewer/editor
|
||||
ValueEditor* old_editor = current_editor;
|
||||
selectFieldNoEvents(ev);
|
||||
if (old_editor != current_editor) {
|
||||
// selection has changed, send focus events
|
||||
if (old_editor) old_editor->onLoseFocus();
|
||||
if (current_editor) current_editor->onFocus();
|
||||
}
|
||||
// pass event
|
||||
if (current_editor && current_viewer) {
|
||||
RealPoint pos = mousePoint(ev, *current_viewer);
|
||||
if (current_viewer->containsPoint(pos)) {
|
||||
(current_editor->*event)(pos, ev);
|
||||
}
|
||||
}
|
||||
// refresh?
|
||||
if (old_editor != current_editor) {
|
||||
// selection has changed, refresh viewers
|
||||
// NOTE: after passing mouse down event, otherwise opening combo box produces flicker
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
void DataEditor::selectFieldNoEvents(const wxMouseEvent& ev) {
|
||||
FOR_EACH_EDITOR_REVERSE { // find high z index fields first
|
||||
int y;
|
||||
if (v->getField()->editable && (v->containsPoint(mousePoint(ev,*v)) ||
|
||||
(nativeLook() && (y = ev.GetY() + GetScrollPos(wxVERTICAL)) >= v->getStyle()->top
|
||||
&& y < v->getStyle()->bottom) )) {
|
||||
current_viewer = v.get();
|
||||
current_editor = e;
|
||||
return;
|
||||
}
|
||||
}
|
||||
FOR_EACH_EDITOR_REVERSE { // find high z index fields first
|
||||
int y;
|
||||
if (v->getField()->editable && (v->containsPoint(mousePoint(ev,*v)) ||
|
||||
(nativeLook() && (y = ev.GetY() + GetScrollPos(wxVERTICAL)) >= v->getStyle()->top
|
||||
&& y < v->getStyle()->bottom) )) {
|
||||
current_viewer = v.get();
|
||||
current_editor = e;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RealPoint DataEditor::mousePoint(const wxMouseEvent& ev, const ValueViewer& viewer) {
|
||||
Rotation rot = getRotation();
|
||||
Rotater r(rot,viewer.getRotation());
|
||||
return rot.trInv(RealPoint(ev.GetX(), ev.GetY()));
|
||||
Rotation rot = getRotation();
|
||||
Rotater r(rot,viewer.getRotation());
|
||||
return rot.trInv(RealPoint(ev.GetX(), ev.GetY()));
|
||||
}
|
||||
|
||||
void DataEditor::onLoseCapture(wxMouseCaptureLostEvent&) {
|
||||
// We already test for wrong release with HasCapture()
|
||||
// but stupid wxwidget people decided to throw assertion failures
|
||||
// We already test for wrong release with HasCapture()
|
||||
// but stupid wxwidget people decided to throw assertion failures
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Keyboard events
|
||||
|
||||
void DataEditor::onChar(wxKeyEvent& ev) {
|
||||
if (ev.GetKeyCode() == WXK_TAB) {
|
||||
if (!ev.ShiftDown()) {
|
||||
// try to select the next editor
|
||||
if (selectNext()) return;
|
||||
// send a navigation event to our parent, to select another control
|
||||
wxNavigationKeyEvent evt;
|
||||
GetParent()->HandleWindowEvent(evt);
|
||||
} else {
|
||||
// try to select the previos editor
|
||||
if (selectPrevious()) return;
|
||||
// send a navigation event to our parent, to select another control
|
||||
wxNavigationKeyEvent evt;
|
||||
evt.SetDirection(false);
|
||||
GetParent()->HandleWindowEvent(evt);
|
||||
}
|
||||
} else if (current_editor) {
|
||||
current_editor->onChar(ev);
|
||||
}
|
||||
if (ev.GetKeyCode() == WXK_TAB) {
|
||||
if (!ev.ShiftDown()) {
|
||||
// try to select the next editor
|
||||
if (selectNext()) return;
|
||||
// send a navigation event to our parent, to select another control
|
||||
wxNavigationKeyEvent evt;
|
||||
GetParent()->HandleWindowEvent(evt);
|
||||
} else {
|
||||
// try to select the previos editor
|
||||
if (selectPrevious()) return;
|
||||
// send a navigation event to our parent, to select another control
|
||||
wxNavigationKeyEvent evt;
|
||||
evt.SetDirection(false);
|
||||
GetParent()->HandleWindowEvent(evt);
|
||||
}
|
||||
} else if (current_editor) {
|
||||
current_editor->onChar(ev);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Menu events
|
||||
|
||||
void DataEditor::onContextMenu(wxContextMenuEvent& ev) {
|
||||
if (current_editor) {
|
||||
IconMenu m;
|
||||
m.Append(ID_EDIT_CUT, _("cut"), _MENU_("cut"), _HELP_("cut"));
|
||||
m.Append(ID_EDIT_COPY, _("copy"), _MENU_("copy"), _HELP_("copy"));
|
||||
m.Append(ID_EDIT_PASTE, _("paste"), _MENU_("paste"), _HELP_("paste"));
|
||||
m.Enable(ID_EDIT_CUT, canCut());
|
||||
m.Enable(ID_EDIT_COPY, canCopy());
|
||||
m.Enable(ID_EDIT_PASTE, canPaste());
|
||||
if (current_editor->onContextMenu(m, ev)) {
|
||||
PopupMenu(&m);
|
||||
}
|
||||
}
|
||||
if (current_editor) {
|
||||
IconMenu m;
|
||||
m.Append(ID_EDIT_CUT, _("cut"), _MENU_("cut"), _HELP_("cut"));
|
||||
m.Append(ID_EDIT_COPY, _("copy"), _MENU_("copy"), _HELP_("copy"));
|
||||
m.Append(ID_EDIT_PASTE, _("paste"), _MENU_("paste"), _HELP_("paste"));
|
||||
m.Enable(ID_EDIT_CUT, canCut());
|
||||
m.Enable(ID_EDIT_COPY, canCopy());
|
||||
m.Enable(ID_EDIT_PASTE, canPaste());
|
||||
if (current_editor->onContextMenu(m, ev)) {
|
||||
PopupMenu(&m);
|
||||
}
|
||||
}
|
||||
}
|
||||
void DataEditor::onMenu(wxCommandEvent& ev) {
|
||||
if (current_editor) {
|
||||
if (!current_editor->onCommand(ev.GetId())) {
|
||||
ev.Skip();
|
||||
}
|
||||
} else {
|
||||
ev.Skip();
|
||||
}
|
||||
if (current_editor) {
|
||||
if (!current_editor->onCommand(ev.GetId())) {
|
||||
ev.Skip();
|
||||
}
|
||||
} else {
|
||||
ev.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Focus events
|
||||
|
||||
void DataEditor::onFocus(wxFocusEvent& ev) {
|
||||
if (current_editor) {
|
||||
current_editor->onFocus();
|
||||
onChange();
|
||||
} else {
|
||||
if (ev.GetWindow() && ev.GetWindow() == next_in_tab_order) {
|
||||
selectLast();
|
||||
} else {
|
||||
selectFirst();
|
||||
}
|
||||
}
|
||||
if (current_editor) {
|
||||
current_editor->onFocus();
|
||||
onChange();
|
||||
} else {
|
||||
if (ev.GetWindow() && ev.GetWindow() == next_in_tab_order) {
|
||||
selectLast();
|
||||
} else {
|
||||
selectFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
void DataEditor::onLoseFocus(wxFocusEvent& ev) {
|
||||
if (current_editor) {
|
||||
current_editor->onLoseFocus();
|
||||
onChange();
|
||||
}
|
||||
if (current_editor) {
|
||||
current_editor->onLoseFocus();
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Event table
|
||||
|
||||
BEGIN_EVENT_TABLE(DataEditor, CardViewer)
|
||||
EVT_LEFT_DOWN (DataEditor::onLeftDown)
|
||||
EVT_LEFT_UP (DataEditor::onLeftUp)
|
||||
EVT_LEFT_DCLICK (DataEditor::onLeftDClick)
|
||||
EVT_RIGHT_DOWN (DataEditor::onRightDown)
|
||||
EVT_MOTION (DataEditor::onMotion)
|
||||
EVT_MOUSEWHEEL (DataEditor::onMouseWheel)
|
||||
EVT_LEAVE_WINDOW (DataEditor::onMouseLeave)
|
||||
EVT_CONTEXT_MENU (DataEditor::onContextMenu)
|
||||
EVT_MENU (wxID_ANY, DataEditor::onMenu)
|
||||
EVT_CHAR (DataEditor::onChar)
|
||||
EVT_SET_FOCUS (DataEditor::onFocus)
|
||||
EVT_KILL_FOCUS (DataEditor::onLoseFocus)
|
||||
EVT_MOUSE_CAPTURE_LOST(DataEditor::onLoseCapture)
|
||||
EVT_LEFT_DOWN (DataEditor::onLeftDown)
|
||||
EVT_LEFT_UP (DataEditor::onLeftUp)
|
||||
EVT_LEFT_DCLICK (DataEditor::onLeftDClick)
|
||||
EVT_RIGHT_DOWN (DataEditor::onRightDown)
|
||||
EVT_MOTION (DataEditor::onMotion)
|
||||
EVT_MOUSEWHEEL (DataEditor::onMouseWheel)
|
||||
EVT_LEAVE_WINDOW (DataEditor::onMouseLeave)
|
||||
EVT_CONTEXT_MENU (DataEditor::onContextMenu)
|
||||
EVT_MENU (wxID_ANY, DataEditor::onMenu)
|
||||
EVT_CHAR (DataEditor::onChar)
|
||||
EVT_SET_FOCUS (DataEditor::onFocus)
|
||||
EVT_KILL_FOCUS (DataEditor::onLoseFocus)
|
||||
EVT_MOUSE_CAPTURE_LOST(DataEditor::onLoseCapture)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
+119
-119
@@ -20,121 +20,121 @@ class FindInfo;
|
||||
/// An editor for data values (usually a card)
|
||||
class DataEditor : public CardViewer {
|
||||
public:
|
||||
DataEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
// --------------------------------------------------- : Utility for ValueViewers/Editors
|
||||
|
||||
virtual DrawWhat drawWhat(const ValueViewer*) const;
|
||||
virtual bool viewerIsCurrent(const ValueViewer*) const;
|
||||
|
||||
virtual void addAction(Action* action);
|
||||
inline SetP getSetForActions() { return set; }
|
||||
|
||||
// --------------------------------------------------- : Selection
|
||||
|
||||
/// Select the given viewer, sends focus events
|
||||
void select(ValueViewer* v);
|
||||
/// Select the first editable and visible editor (by tab index)
|
||||
void selectFirst();
|
||||
/// Select the last editable and visible editor (by tab index)
|
||||
void selectLast();
|
||||
/// Select the next editable editor, returns false if the current editor is the last one
|
||||
bool selectNext();
|
||||
/// Select the previous editable editor, returns false if the current editor is the first one
|
||||
bool selectPrevious();
|
||||
|
||||
virtual bool AcceptsFocus() const;
|
||||
|
||||
/// The next window in the tab order (optional)
|
||||
const wxWindow* next_in_tab_order;
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
bool canCut() const;
|
||||
bool canCopy() const;
|
||||
bool canPaste() const;
|
||||
void doCut();
|
||||
void doCopy();
|
||||
void doPaste();
|
||||
|
||||
// --------------------------------------------------- : Formatting
|
||||
|
||||
bool canFormat(int type) const;
|
||||
bool hasFormat(int type) const;
|
||||
void doFormat (int type);
|
||||
/// Get a special menu, events should be sent to onCommand
|
||||
wxMenu* getMenu(int type) const;
|
||||
/// A menu item from getMenu was selected
|
||||
void onCommand(int id);
|
||||
|
||||
// --------------------------------------------------- : Search/replace
|
||||
|
||||
/// Do a search or replace action for the given FindInfo
|
||||
/** If from_start == false: searches only from the current selection onward (or backward)
|
||||
* If from_start == true: searches everything
|
||||
*
|
||||
* Returns true if we are done and searching should be ended.
|
||||
*/
|
||||
bool search(FindInfo& find, bool from_start);
|
||||
|
||||
// --------------------------------------------------- : Selection in editor
|
||||
|
||||
/// Insert 'text' into the current editor, using an action with the given name
|
||||
void insert(const String& text, const String& action_name);
|
||||
|
||||
// --------------------------------------------------- : ValueViewers
|
||||
|
||||
DataEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
// --------------------------------------------------- : Utility for ValueViewers/Editors
|
||||
|
||||
virtual DrawWhat drawWhat(const ValueViewer*) const;
|
||||
virtual bool viewerIsCurrent(const ValueViewer*) const;
|
||||
|
||||
virtual void addAction(Action* action);
|
||||
inline SetP getSetForActions() { return set; }
|
||||
|
||||
// --------------------------------------------------- : Selection
|
||||
|
||||
/// Select the given viewer, sends focus events
|
||||
void select(ValueViewer* v);
|
||||
/// Select the first editable and visible editor (by tab index)
|
||||
void selectFirst();
|
||||
/// Select the last editable and visible editor (by tab index)
|
||||
void selectLast();
|
||||
/// Select the next editable editor, returns false if the current editor is the last one
|
||||
bool selectNext();
|
||||
/// Select the previous editable editor, returns false if the current editor is the first one
|
||||
bool selectPrevious();
|
||||
|
||||
virtual bool AcceptsFocus() const;
|
||||
|
||||
/// The next window in the tab order (optional)
|
||||
const wxWindow* next_in_tab_order;
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
bool canCut() const;
|
||||
bool canCopy() const;
|
||||
bool canPaste() const;
|
||||
void doCut();
|
||||
void doCopy();
|
||||
void doPaste();
|
||||
|
||||
// --------------------------------------------------- : Formatting
|
||||
|
||||
bool canFormat(int type) const;
|
||||
bool hasFormat(int type) const;
|
||||
void doFormat (int type);
|
||||
/// Get a special menu, events should be sent to onCommand
|
||||
wxMenu* getMenu(int type) const;
|
||||
/// A menu item from getMenu was selected
|
||||
void onCommand(int id);
|
||||
|
||||
// --------------------------------------------------- : Search/replace
|
||||
|
||||
/// Do a search or replace action for the given FindInfo
|
||||
/** If from_start == false: searches only from the current selection onward (or backward)
|
||||
* If from_start == true: searches everything
|
||||
*
|
||||
* Returns true if we are done and searching should be ended.
|
||||
*/
|
||||
bool search(FindInfo& find, bool from_start);
|
||||
|
||||
// --------------------------------------------------- : Selection in editor
|
||||
|
||||
/// Insert 'text' into the current editor, using an action with the given name
|
||||
void insert(const String& text, const String& action_name);
|
||||
|
||||
// --------------------------------------------------- : ValueViewers
|
||||
|
||||
protected:
|
||||
/// Create an editor for the given style (as opposed to a normal viewer)
|
||||
virtual ValueViewerP makeViewer(const StyleP&);
|
||||
|
||||
virtual void onInit();
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
ValueViewer* current_viewer; ///< The currently selected viewer
|
||||
ValueEditor* current_editor; ///< The currently selected editor, corresponding to the viewer
|
||||
ValueViewer* hovered_viewer; ///< The editor under the mouse cursor
|
||||
vector<ValueViewer*> by_tab_index; ///< The editable viewers, sorted by tab index
|
||||
|
||||
/// Create an editor for the given style (as opposed to a normal viewer)
|
||||
virtual ValueViewerP makeViewer(const StyleP&);
|
||||
|
||||
virtual void onInit();
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
ValueViewer* current_viewer; ///< The currently selected viewer
|
||||
ValueEditor* current_editor; ///< The currently selected editor, corresponding to the viewer
|
||||
ValueViewer* hovered_viewer; ///< The editor under the mouse cursor
|
||||
vector<ValueViewer*> by_tab_index; ///< The editable viewers, sorted by tab index
|
||||
|
||||
private:
|
||||
// --------------------------------------------------- : Events
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
|
||||
void onLeftDown (wxMouseEvent&);
|
||||
void onLeftUp (wxMouseEvent&);
|
||||
void onLeftDClick(wxMouseEvent&);
|
||||
void onRightDown (wxMouseEvent&);
|
||||
void onMotion (wxMouseEvent&);
|
||||
void onMouseWheel(wxMouseEvent&);
|
||||
void onMouseLeave(wxMouseEvent&);
|
||||
void onLoseCapture(wxMouseCaptureLostEvent&);
|
||||
|
||||
void onChar (wxKeyEvent&);
|
||||
|
||||
void onContextMenu(wxContextMenuEvent&);
|
||||
void onMenu (wxCommandEvent&);
|
||||
|
||||
void onFocus (wxFocusEvent&);
|
||||
void onLoseFocus(wxFocusEvent&);
|
||||
|
||||
// --------------------------------------------------- : Functions
|
||||
|
||||
/// Changes the selection to the field at the specified coordinates
|
||||
/** Sends an event to the event function of the current viewer */
|
||||
void selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&));
|
||||
// selectField, but don't send events
|
||||
void selectFieldNoEvents(const wxMouseEvent&);
|
||||
/// Convert mouse coordinates to internal coordinates
|
||||
RealPoint mousePoint(const wxMouseEvent&, const ValueViewer& viewer);
|
||||
|
||||
// Create tab index ordering of the (editable) viewers
|
||||
void createTabIndex();
|
||||
/// Select the field with the given position in the by_tab_index list
|
||||
/** Returns success */
|
||||
bool selectByTabPos(int tab_pos, bool forward = true);
|
||||
/// Find the tab pos of the current viewer, returns -1 if not found
|
||||
int currentTabPos() const;
|
||||
// --------------------------------------------------- : Events
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
|
||||
void onLeftDown (wxMouseEvent&);
|
||||
void onLeftUp (wxMouseEvent&);
|
||||
void onLeftDClick(wxMouseEvent&);
|
||||
void onRightDown (wxMouseEvent&);
|
||||
void onMotion (wxMouseEvent&);
|
||||
void onMouseWheel(wxMouseEvent&);
|
||||
void onMouseLeave(wxMouseEvent&);
|
||||
void onLoseCapture(wxMouseCaptureLostEvent&);
|
||||
|
||||
void onChar (wxKeyEvent&);
|
||||
|
||||
void onContextMenu(wxContextMenuEvent&);
|
||||
void onMenu (wxCommandEvent&);
|
||||
|
||||
void onFocus (wxFocusEvent&);
|
||||
void onLoseFocus(wxFocusEvent&);
|
||||
|
||||
// --------------------------------------------------- : Functions
|
||||
|
||||
/// Changes the selection to the field at the specified coordinates
|
||||
/** Sends an event to the event function of the current viewer */
|
||||
void selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&));
|
||||
// selectField, but don't send events
|
||||
void selectFieldNoEvents(const wxMouseEvent&);
|
||||
/// Convert mouse coordinates to internal coordinates
|
||||
RealPoint mousePoint(const wxMouseEvent&, const ValueViewer& viewer);
|
||||
|
||||
// Create tab index ordering of the (editable) viewers
|
||||
void createTabIndex();
|
||||
/// Select the field with the given position in the by_tab_index list
|
||||
/** Returns success */
|
||||
bool selectByTabPos(int tab_pos, bool forward = true);
|
||||
/// Find the tab pos of the current viewer, returns -1 if not found
|
||||
int currentTabPos() const;
|
||||
};
|
||||
|
||||
/// By default a DataEditor edits cards
|
||||
@@ -142,12 +142,12 @@ typedef DataEditor CardEditor;
|
||||
|
||||
// ----------------------------------------------------------------------------- : Utility
|
||||
|
||||
#define FOR_EACH_EDITOR \
|
||||
FOR_EACH(v, viewers) \
|
||||
if (ValueEditor* e = v->getEditor())
|
||||
#define FOR_EACH_EDITOR_REVERSE \
|
||||
FOR_EACH_REVERSE(v, viewers) \
|
||||
if (ValueEditor* e = v->getEditor())
|
||||
#define FOR_EACH_EDITOR \
|
||||
FOR_EACH(v, viewers) \
|
||||
if (ValueEditor* e = v->getEditor())
|
||||
#define FOR_EACH_EDITOR_REVERSE \
|
||||
FOR_EACH_REVERSE(v, viewers) \
|
||||
if (ValueEditor* e = v->getEditor())
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
|
||||
+267
-267
@@ -36,15 +36,15 @@ DEFINE_EVENT_TYPE(EVENT_CARD_SELECT);
|
||||
DEFINE_EVENT_TYPE(EVENT_CARD_ACTIVATE);
|
||||
|
||||
CardP CardSelectEvent::getCard() const {
|
||||
return getTheCardList()->getCard();
|
||||
return getTheCardList()->getCard();
|
||||
}
|
||||
|
||||
void CardSelectEvent::getSelection(vector<CardP>& out) const {
|
||||
getTheCardList()->getSelection(out);
|
||||
getTheCardList()->getSelection(out);
|
||||
}
|
||||
|
||||
CardListBase* CardSelectEvent::getTheCardList() const {
|
||||
return static_cast<CardListBase*>(GetEventObject());
|
||||
return static_cast<CardListBase*>(GetEventObject());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : CardListBase
|
||||
@@ -52,95 +52,95 @@ CardListBase* CardSelectEvent::getTheCardList() const {
|
||||
vector<CardListBase*> CardListBase::card_lists;
|
||||
|
||||
CardListBase::CardListBase(Window* parent, int id, long additional_style)
|
||||
: ItemList(parent, id, additional_style, true)
|
||||
: ItemList(parent, id, additional_style, true)
|
||||
{
|
||||
// add to the list of card lists
|
||||
card_lists.push_back(this);
|
||||
// add to the list of card lists
|
||||
card_lists.push_back(this);
|
||||
}
|
||||
|
||||
CardListBase::~CardListBase() {
|
||||
storeColumns();
|
||||
// remove from list of card lists
|
||||
card_lists.erase(remove(card_lists.begin(), card_lists.end(), this));
|
||||
storeColumns();
|
||||
// remove from list of card lists
|
||||
card_lists.erase(remove(card_lists.begin(), card_lists.end(), this));
|
||||
}
|
||||
|
||||
void CardListBase::onBeforeChangeSet() {
|
||||
storeColumns();
|
||||
storeColumns();
|
||||
}
|
||||
void CardListBase::onChangeSet() {
|
||||
rebuild();
|
||||
rebuild();
|
||||
}
|
||||
|
||||
struct Freezer{
|
||||
Window* window;
|
||||
Freezer(Window* window) : window(window) { window->Freeze(); }
|
||||
~Freezer() { window->Thaw(); }
|
||||
Window* window;
|
||||
Freezer(Window* window) : window(window) { window->Freeze(); }
|
||||
~Freezer() { window->Thaw(); }
|
||||
};
|
||||
|
||||
void CardListBase::onAction(const Action& action, bool undone) {
|
||||
TYPE_CASE(action, AddCardAction) {
|
||||
Freezer freeze(this);
|
||||
if (action.action.adding != undone) {
|
||||
// select the new cards
|
||||
focusNone();
|
||||
selectItem(action.action.steps.front().item, false, true);
|
||||
refreshList();
|
||||
FOR_EACH_CONST(s, action.action.steps) focusItem(s.item); // focus all the new cards
|
||||
} else {
|
||||
long pos = selected_item_pos;
|
||||
// adjust focus for all the removed cards
|
||||
refreshList();
|
||||
if (!allowModify()) {
|
||||
// Let some other card list do the selecting, otherwise we get conflicting events
|
||||
return;
|
||||
}
|
||||
if (selected_item_pos == -1) {
|
||||
// selected item was deleted, select something else
|
||||
selectItemPos(pos, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_CASE(action, ReorderCardsAction) {
|
||||
if (sort_by_column >= 0) return; // nothing changes for us
|
||||
TYPE_CASE(action, AddCardAction) {
|
||||
Freezer freeze(this);
|
||||
if (action.action.adding != undone) {
|
||||
// select the new cards
|
||||
focusNone();
|
||||
selectItem(action.action.steps.front().item, false, true);
|
||||
refreshList();
|
||||
FOR_EACH_CONST(s, action.action.steps) focusItem(s.item); // focus all the new cards
|
||||
} else {
|
||||
long pos = selected_item_pos;
|
||||
// adjust focus for all the removed cards
|
||||
refreshList();
|
||||
if (!allowModify()) {
|
||||
// Let some other card list do the selecting, otherwise we get conflicting events
|
||||
return;
|
||||
}
|
||||
if (selected_item_pos == -1) {
|
||||
// selected item was deleted, select something else
|
||||
selectItemPos(pos, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_CASE(action, ReorderCardsAction) {
|
||||
if (sort_by_column >= 0) return; // nothing changes for us
|
||||
if ((long)action.card_id1 < 0 || (long)action.card_id2 >= (long)sorted_list.size()) return;
|
||||
if ((long)action.card_id1 == selected_item_pos || (long)action.card_id2 == selected_item_pos) {
|
||||
// Selected card has moved; also move in the sorted card list
|
||||
swap(sorted_list[action.card_id1], sorted_list[action.card_id2]);
|
||||
// reselect the current card, it has moved
|
||||
selected_item_pos = (long)action.card_id1 == selected_item_pos ? (long)action.card_id2 : (long)action.card_id1;
|
||||
// select the right card
|
||||
focusSelectedItem();
|
||||
}
|
||||
RefreshItem((long)action.card_id1);
|
||||
RefreshItem((long)action.card_id2);
|
||||
}
|
||||
TYPE_CASE_(action, ScriptValueEvent) {
|
||||
// No refresh needed, a ScriptValueEvent is only generated in response to a ValueAction
|
||||
return;
|
||||
}
|
||||
TYPE_CASE(action, ValueAction) {
|
||||
if (action.card) refreshList(true);
|
||||
}
|
||||
if ((long)action.card_id1 == selected_item_pos || (long)action.card_id2 == selected_item_pos) {
|
||||
// Selected card has moved; also move in the sorted card list
|
||||
swap(sorted_list[action.card_id1], sorted_list[action.card_id2]);
|
||||
// reselect the current card, it has moved
|
||||
selected_item_pos = (long)action.card_id1 == selected_item_pos ? (long)action.card_id2 : (long)action.card_id1;
|
||||
// select the right card
|
||||
focusSelectedItem();
|
||||
}
|
||||
RefreshItem((long)action.card_id1);
|
||||
RefreshItem((long)action.card_id2);
|
||||
}
|
||||
TYPE_CASE_(action, ScriptValueEvent) {
|
||||
// No refresh needed, a ScriptValueEvent is only generated in response to a ValueAction
|
||||
return;
|
||||
}
|
||||
TYPE_CASE(action, ValueAction) {
|
||||
if (action.card) refreshList(true);
|
||||
}
|
||||
}
|
||||
|
||||
void CardListBase::getItems(vector<VoidP>& out) const {
|
||||
FOR_EACH(c, set->cards) {
|
||||
out.push_back(c);
|
||||
}
|
||||
FOR_EACH(c, set->cards) {
|
||||
out.push_back(c);
|
||||
}
|
||||
}
|
||||
void CardListBase::sendEvent(int type) {
|
||||
CardSelectEvent ev(type);
|
||||
ev.SetEventObject(this);
|
||||
ProcessEvent(ev);
|
||||
CardSelectEvent ev(type);
|
||||
ev.SetEventObject(this);
|
||||
ProcessEvent(ev);
|
||||
}
|
||||
|
||||
void CardListBase::getSelection(vector<CardP>& out) const {
|
||||
long count = GetItemCount();
|
||||
for (long pos = 0 ; pos < count ; ++pos) {
|
||||
if (const_cast<CardListBase*>(this)->IsSelected(pos)) {
|
||||
out.push_back(getCard(pos));
|
||||
}
|
||||
}
|
||||
long count = GetItemCount();
|
||||
for (long pos = 0 ; pos < count ; ++pos) {
|
||||
if (const_cast<CardListBase*>(this)->IsSelected(pos)) {
|
||||
out.push_back(getCard(pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : CardListBase : Clipboard
|
||||
@@ -148,269 +148,269 @@ void CardListBase::getSelection(vector<CardP>& out) const {
|
||||
bool CardListBase::canCut() const { return canDelete(); }
|
||||
bool CardListBase::canCopy() const { return focusCount() > 0; }
|
||||
bool CardListBase::canPaste() const {
|
||||
return allowModify() && wxTheClipboard->IsSupported(CardsDataObject::format);
|
||||
return allowModify() && wxTheClipboard->IsSupported(CardsDataObject::format);
|
||||
}
|
||||
bool CardListBase::canDelete() const {
|
||||
return allowModify() && focusCount() > 0; // TODO: check for selection?
|
||||
return allowModify() && focusCount() > 0; // TODO: check for selection?
|
||||
}
|
||||
|
||||
bool CardListBase::doCopy() {
|
||||
if (!canCopy()) return false;
|
||||
// cards to copy
|
||||
vector<CardP> cards_to_copy;
|
||||
getSelection(cards_to_copy);
|
||||
if (cards_to_copy.empty()) return false;
|
||||
// put on clipboard
|
||||
if (!wxTheClipboard->Open()) return false;
|
||||
bool ok = wxTheClipboard->SetData(new CardsOnClipboard(set, cards_to_copy)); // ignore result
|
||||
wxTheClipboard->Close();
|
||||
return ok;
|
||||
if (!canCopy()) return false;
|
||||
// cards to copy
|
||||
vector<CardP> cards_to_copy;
|
||||
getSelection(cards_to_copy);
|
||||
if (cards_to_copy.empty()) return false;
|
||||
// put on clipboard
|
||||
if (!wxTheClipboard->Open()) return false;
|
||||
bool ok = wxTheClipboard->SetData(new CardsOnClipboard(set, cards_to_copy)); // ignore result
|
||||
wxTheClipboard->Close();
|
||||
return ok;
|
||||
}
|
||||
bool CardListBase::doPaste() {
|
||||
// get data
|
||||
if (!canPaste()) return false;
|
||||
if (!wxTheClipboard->Open()) return false;
|
||||
CardsDataObject data;
|
||||
bool ok = wxTheClipboard->GetData(data);
|
||||
wxTheClipboard->Close();
|
||||
if (!ok) return false;
|
||||
// get cards
|
||||
vector<CardP> new_cards;
|
||||
ok = data.getCards(set, new_cards);
|
||||
if (!ok) return false;
|
||||
// add card to set
|
||||
set->actions.addAction(new AddCardAction(ADD, *set, new_cards));
|
||||
return true;
|
||||
// get data
|
||||
if (!canPaste()) return false;
|
||||
if (!wxTheClipboard->Open()) return false;
|
||||
CardsDataObject data;
|
||||
bool ok = wxTheClipboard->GetData(data);
|
||||
wxTheClipboard->Close();
|
||||
if (!ok) return false;
|
||||
// get cards
|
||||
vector<CardP> new_cards;
|
||||
ok = data.getCards(set, new_cards);
|
||||
if (!ok) return false;
|
||||
// add card to set
|
||||
set->actions.addAction(new AddCardAction(ADD, *set, new_cards));
|
||||
return true;
|
||||
}
|
||||
bool CardListBase::doDelete() {
|
||||
// cards to delete
|
||||
vector<CardP> cards_to_delete;
|
||||
getSelection(cards_to_delete);
|
||||
if (cards_to_delete.empty()) return false;
|
||||
// delete cards
|
||||
set->actions.addAction(new AddCardAction(REMOVE, *set, cards_to_delete));
|
||||
return true;
|
||||
// cards to delete
|
||||
vector<CardP> cards_to_delete;
|
||||
getSelection(cards_to_delete);
|
||||
if (cards_to_delete.empty()) return false;
|
||||
// delete cards
|
||||
set->actions.addAction(new AddCardAction(REMOVE, *set, cards_to_delete));
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : CardListBase : Building the list
|
||||
|
||||
// Comparison object for comparing cards
|
||||
bool CardListBase::compareItems(void* a, void* b) const {
|
||||
FieldP sort_field = column_fields[sort_by_column];
|
||||
ValueP va = reinterpret_cast<Card*>(a)->data[sort_field];
|
||||
ValueP vb = reinterpret_cast<Card*>(b)->data[sort_field];
|
||||
assert(va && vb);
|
||||
// compare sort keys
|
||||
int cmp = smart_compare( va->getSortKey(), vb->getSortKey() );
|
||||
if (cmp != 0) return cmp < 0;
|
||||
// equal values, compare alternate sort key
|
||||
if (alternate_sort_field) {
|
||||
ValueP va = reinterpret_cast<Card*>(a)->data[alternate_sort_field];
|
||||
ValueP vb = reinterpret_cast<Card*>(b)->data[alternate_sort_field];
|
||||
int cmp = smart_compare( va->getSortKey(), vb->getSortKey() );
|
||||
if (cmp != 0) return cmp < 0;
|
||||
}
|
||||
return false;
|
||||
FieldP sort_field = column_fields[sort_by_column];
|
||||
ValueP va = reinterpret_cast<Card*>(a)->data[sort_field];
|
||||
ValueP vb = reinterpret_cast<Card*>(b)->data[sort_field];
|
||||
assert(va && vb);
|
||||
// compare sort keys
|
||||
int cmp = smart_compare( va->getSortKey(), vb->getSortKey() );
|
||||
if (cmp != 0) return cmp < 0;
|
||||
// equal values, compare alternate sort key
|
||||
if (alternate_sort_field) {
|
||||
ValueP va = reinterpret_cast<Card*>(a)->data[alternate_sort_field];
|
||||
ValueP vb = reinterpret_cast<Card*>(b)->data[alternate_sort_field];
|
||||
int cmp = smart_compare( va->getSortKey(), vb->getSortKey() );
|
||||
if (cmp != 0) return cmp < 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CardListBase::rebuild() {
|
||||
ClearAll();
|
||||
column_fields.clear();
|
||||
selected_item_pos = -1;
|
||||
onRebuild();
|
||||
if (!set) return;
|
||||
// init stuff
|
||||
set->game->initCardListColorScript();
|
||||
// determine column order
|
||||
map<int,FieldP> new_column_fields;
|
||||
FOR_EACH(f, set->game->card_fields) {
|
||||
ColumnSettings& cs = settings.columnSettingsFor(*set->game, *f);
|
||||
if (cs.visible && f->card_list_allow) {
|
||||
new_column_fields[cs.position] = f;
|
||||
}
|
||||
}
|
||||
// add columns
|
||||
FOR_EACH(f, new_column_fields) {
|
||||
ColumnSettings& cs = settings.columnSettingsFor(*set->game, *f.second);
|
||||
int align;
|
||||
if (f.second->card_list_align & ALIGN_RIGHT) align = wxLIST_FORMAT_RIGHT;
|
||||
else if (f.second->card_list_align & ALIGN_CENTER) align = wxLIST_FORMAT_CENTRE;
|
||||
else align = wxLIST_FORMAT_LEFT;
|
||||
InsertColumn((long)column_fields.size(),
|
||||
tr(*set->game, f.second->card_list_name, capitalize),
|
||||
align, cs.width);
|
||||
column_fields.push_back(f.second);
|
||||
}
|
||||
// determine sort settings
|
||||
GameSettings& gs = settings.gameSettingsFor(*set->game);
|
||||
sort_ascending = gs.sort_cards_ascending;
|
||||
sort_by_column = -1;
|
||||
long i = 0;
|
||||
FOR_EACH(f, column_fields) {
|
||||
if (f->name == gs.sort_cards_by) {
|
||||
// we are sorting by this column
|
||||
sort_by_column = i;
|
||||
// and display an arrow in the header
|
||||
SetColumnImage(i, sort_ascending ? 0 : 1);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
// determine alternate sortImageFieldP ImageCardList::findImageField() {
|
||||
alternate_sort_field = FieldP();
|
||||
FOR_EACH(f, set->game->card_fields) {
|
||||
if (f->identifying) {
|
||||
alternate_sort_field = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// refresh
|
||||
refreshList();
|
||||
ClearAll();
|
||||
column_fields.clear();
|
||||
selected_item_pos = -1;
|
||||
onRebuild();
|
||||
if (!set) return;
|
||||
// init stuff
|
||||
set->game->initCardListColorScript();
|
||||
// determine column order
|
||||
map<int,FieldP> new_column_fields;
|
||||
FOR_EACH(f, set->game->card_fields) {
|
||||
ColumnSettings& cs = settings.columnSettingsFor(*set->game, *f);
|
||||
if (cs.visible && f->card_list_allow) {
|
||||
new_column_fields[cs.position] = f;
|
||||
}
|
||||
}
|
||||
// add columns
|
||||
FOR_EACH(f, new_column_fields) {
|
||||
ColumnSettings& cs = settings.columnSettingsFor(*set->game, *f.second);
|
||||
int align;
|
||||
if (f.second->card_list_align & ALIGN_RIGHT) align = wxLIST_FORMAT_RIGHT;
|
||||
else if (f.second->card_list_align & ALIGN_CENTER) align = wxLIST_FORMAT_CENTRE;
|
||||
else align = wxLIST_FORMAT_LEFT;
|
||||
InsertColumn((long)column_fields.size(),
|
||||
tr(*set->game, f.second->card_list_name, capitalize),
|
||||
align, cs.width);
|
||||
column_fields.push_back(f.second);
|
||||
}
|
||||
// determine sort settings
|
||||
GameSettings& gs = settings.gameSettingsFor(*set->game);
|
||||
sort_ascending = gs.sort_cards_ascending;
|
||||
sort_by_column = -1;
|
||||
long i = 0;
|
||||
FOR_EACH(f, column_fields) {
|
||||
if (f->name == gs.sort_cards_by) {
|
||||
// we are sorting by this column
|
||||
sort_by_column = i;
|
||||
// and display an arrow in the header
|
||||
SetColumnImage(i, sort_ascending ? 0 : 1);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
// determine alternate sortImageFieldP ImageCardList::findImageField() {
|
||||
alternate_sort_field = FieldP();
|
||||
FOR_EACH(f, set->game->card_fields) {
|
||||
if (f->identifying) {
|
||||
alternate_sort_field = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// refresh
|
||||
refreshList();
|
||||
}
|
||||
|
||||
void CardListBase::sortBy(long column, bool ascending) {
|
||||
// sort all card lists for this game
|
||||
FOR_EACH(card_list, card_lists) {
|
||||
if (card_list->set && card_list->set->game == set->game) {
|
||||
card_list->ItemList::sortBy(column, ascending);
|
||||
}
|
||||
}
|
||||
// sort all card lists for this game
|
||||
FOR_EACH(card_list, card_lists) {
|
||||
if (card_list->set && card_list->set->game == set->game) {
|
||||
card_list->ItemList::sortBy(column, ascending);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : CardListBase : Columns
|
||||
|
||||
void CardListBase::storeColumns() {
|
||||
if (!set) return;
|
||||
// store column widths
|
||||
int i = 0;
|
||||
FOR_EACH(f, column_fields) {
|
||||
ColumnSettings& cs = settings.columnSettingsFor(*set->game, *f);
|
||||
cs.width = GetColumnWidth(i++);
|
||||
}
|
||||
// store sorting
|
||||
GameSettings& gs = settings.gameSettingsFor(*set->game);
|
||||
if (sort_by_column >= 0) gs.sort_cards_by = column_fields.at(sort_by_column)->name;
|
||||
else gs.sort_cards_by = wxEmptyString;
|
||||
gs.sort_cards_ascending = sort_ascending;
|
||||
if (!set) return;
|
||||
// store column widths
|
||||
int i = 0;
|
||||
FOR_EACH(f, column_fields) {
|
||||
ColumnSettings& cs = settings.columnSettingsFor(*set->game, *f);
|
||||
cs.width = GetColumnWidth(i++);
|
||||
}
|
||||
// store sorting
|
||||
GameSettings& gs = settings.gameSettingsFor(*set->game);
|
||||
if (sort_by_column >= 0) gs.sort_cards_by = column_fields.at(sort_by_column)->name;
|
||||
else gs.sort_cards_by = wxEmptyString;
|
||||
gs.sort_cards_ascending = sort_ascending;
|
||||
}
|
||||
void CardListBase::selectColumns() {
|
||||
CardListColumnSelectDialog wnd(this, set->game);
|
||||
if (wnd.ShowModal() == wxID_OK) {
|
||||
// rebuild all card lists for this game
|
||||
storeColumns();
|
||||
FOR_EACH(card_list, card_lists) {
|
||||
if (card_list->set && card_list->set->game == set->game) {
|
||||
card_list->rebuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
CardListColumnSelectDialog wnd(this, set->game);
|
||||
if (wnd.ShowModal() == wxID_OK) {
|
||||
// rebuild all card lists for this game
|
||||
storeColumns();
|
||||
FOR_EACH(card_list, card_lists) {
|
||||
if (card_list->set && card_list->set->game == set->game) {
|
||||
card_list->rebuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : CardListBase : Item 'events'
|
||||
|
||||
String CardListBase::OnGetItemText(long pos, long col) const {
|
||||
if (col < 0 || (size_t)col >= column_fields.size()) {
|
||||
// wx may give us non existing columns!
|
||||
return wxEmptyString;
|
||||
}
|
||||
ValueP val = getCard(pos)->data[column_fields[col]];
|
||||
if (val) return val->toString();
|
||||
else return wxEmptyString;
|
||||
if (col < 0 || (size_t)col >= column_fields.size()) {
|
||||
// wx may give us non existing columns!
|
||||
return wxEmptyString;
|
||||
}
|
||||
ValueP val = getCard(pos)->data[column_fields[col]];
|
||||
if (val) return val->toString();
|
||||
else return wxEmptyString;
|
||||
}
|
||||
|
||||
int CardListBase::OnGetItemImage(long pos) const {
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
wxListItemAttr* CardListBase::OnGetItemAttr(long pos) const {
|
||||
if (!set->game->card_list_color_script) return nullptr;
|
||||
Context& ctx = set->getContext(getCard(pos));
|
||||
item_attr.SetTextColour(*set->game->card_list_color_script.invoke(ctx));
|
||||
return &item_attr;
|
||||
if (!set->game->card_list_color_script) return nullptr;
|
||||
Context& ctx = set->getContext(getCard(pos));
|
||||
item_attr.SetTextColour(*set->game->card_list_color_script.invoke(ctx));
|
||||
return &item_attr;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : CardListBase : Window events
|
||||
|
||||
void CardListBase::onColumnRightClick(wxListEvent&) {
|
||||
// show menu
|
||||
wxMenu m;
|
||||
m.Append(ID_SELECT_COLUMNS, _MENU_("card list columns"), _HELP_("card list columns"));
|
||||
PopupMenu(&m);
|
||||
// show menu
|
||||
wxMenu m;
|
||||
m.Append(ID_SELECT_COLUMNS, _MENU_("card list columns"), _HELP_("card list columns"));
|
||||
PopupMenu(&m);
|
||||
}
|
||||
void CardListBase::onColumnResize(wxListEvent& ev) {
|
||||
storeColumns();
|
||||
int col = ev.GetColumn();
|
||||
int width = GetColumnWidth(col);
|
||||
FOR_EACH(card_list, card_lists) {
|
||||
if (card_list != this && card_list->set && card_list->set->game == set->game) {
|
||||
card_list->SetColumnWidth(col, width);
|
||||
}
|
||||
}
|
||||
storeColumns();
|
||||
int col = ev.GetColumn();
|
||||
int width = GetColumnWidth(col);
|
||||
FOR_EACH(card_list, card_lists) {
|
||||
if (card_list != this && card_list->set && card_list->set->game == set->game) {
|
||||
card_list->SetColumnWidth(col, width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CardListBase::onSelectColumns(wxCommandEvent&) {
|
||||
selectColumns();
|
||||
selectColumns();
|
||||
}
|
||||
|
||||
void CardListBase::onChar(wxKeyEvent& ev) {
|
||||
if (ev.GetKeyCode() == WXK_DELETE && allowModify()) {
|
||||
doDelete();
|
||||
} else if (ev.GetKeyCode() == WXK_TAB) {
|
||||
// send a navigation event to our parent, to select another control
|
||||
// we need this because tabs are not handled on the cards panel
|
||||
wxNavigationKeyEvent nev;
|
||||
nev.SetDirection(!ev.ShiftDown());
|
||||
GetParent()->HandleWindowEvent(nev);
|
||||
} else {
|
||||
ev.Skip();
|
||||
}
|
||||
if (ev.GetKeyCode() == WXK_DELETE && allowModify()) {
|
||||
doDelete();
|
||||
} else if (ev.GetKeyCode() == WXK_TAB) {
|
||||
// send a navigation event to our parent, to select another control
|
||||
// we need this because tabs are not handled on the cards panel
|
||||
wxNavigationKeyEvent nev;
|
||||
nev.SetDirection(!ev.ShiftDown());
|
||||
GetParent()->HandleWindowEvent(nev);
|
||||
} else {
|
||||
ev.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
void CardListBase::onDrag(wxMouseEvent& ev) {
|
||||
ev.Skip();
|
||||
if (!allowModify()) return;
|
||||
if (ev.Dragging() && selected_item && sort_by_column < 0) {
|
||||
// reorder card list
|
||||
int flags;
|
||||
long item = HitTest(ev.GetPosition(), flags);
|
||||
if (flags & wxLIST_HITTEST_ONITEM) {
|
||||
if (item > 0) EnsureVisible(item-1);
|
||||
if (item < GetItemCount()-1) EnsureVisible(item+1);
|
||||
findSelectedItemPos();
|
||||
if (item != selected_item_pos) {
|
||||
// move card in the set
|
||||
set->actions.addAction(new ReorderCardsAction(*set, item, selected_item_pos));
|
||||
}
|
||||
ev.Skip(false);
|
||||
}
|
||||
}
|
||||
ev.Skip();
|
||||
if (!allowModify()) return;
|
||||
if (ev.Dragging() && selected_item && sort_by_column < 0) {
|
||||
// reorder card list
|
||||
int flags;
|
||||
long item = HitTest(ev.GetPosition(), flags);
|
||||
if (flags & wxLIST_HITTEST_ONITEM) {
|
||||
if (item > 0) EnsureVisible(item-1);
|
||||
if (item < GetItemCount()-1) EnsureVisible(item+1);
|
||||
findSelectedItemPos();
|
||||
if (item != selected_item_pos) {
|
||||
// move card in the set
|
||||
set->actions.addAction(new ReorderCardsAction(*set, item, selected_item_pos));
|
||||
}
|
||||
ev.Skip(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CardListBase::onContextMenu(wxContextMenuEvent&) {
|
||||
if (allowModify()) {
|
||||
IconMenu m;
|
||||
m.Append(wxID_CUT, _("cut"), _CONTEXT_MENU_("cut"), _HELP_("cut card"));
|
||||
m.Append(wxID_COPY, _("copy"), _CONTEXT_MENU_("copy"), _HELP_("copy card"));
|
||||
m.Append(wxID_PASTE, _("paste"), _CONTEXT_MENU_("paste"), _HELP_("paste card"));
|
||||
m.AppendSeparator();
|
||||
m.Append(ID_CARD_ADD, _("card_add"), _CONTEXT_MENU_("add card"), _HELP_("add card"));
|
||||
m.Append(ID_CARD_REMOVE,_("card_del"), _CONTEXT_MENU_("remove card"), _HELP_("remove card"));
|
||||
PopupMenu(&m);
|
||||
}
|
||||
if (allowModify()) {
|
||||
IconMenu m;
|
||||
m.Append(wxID_CUT, _("cut"), _CONTEXT_MENU_("cut"), _HELP_("cut card"));
|
||||
m.Append(wxID_COPY, _("copy"), _CONTEXT_MENU_("copy"), _HELP_("copy card"));
|
||||
m.Append(wxID_PASTE, _("paste"), _CONTEXT_MENU_("paste"), _HELP_("paste card"));
|
||||
m.AppendSeparator();
|
||||
m.Append(ID_CARD_ADD, _("card_add"), _CONTEXT_MENU_("add card"), _HELP_("add card"));
|
||||
m.Append(ID_CARD_REMOVE,_("card_del"), _CONTEXT_MENU_("remove card"), _HELP_("remove card"));
|
||||
PopupMenu(&m);
|
||||
}
|
||||
}
|
||||
|
||||
void CardListBase::onItemActivate(wxListEvent& ev) {
|
||||
selectItemPos(ev.GetIndex(), false);
|
||||
sendEvent(EVENT_CARD_ACTIVATE);
|
||||
selectItemPos(ev.GetIndex(), false);
|
||||
sendEvent(EVENT_CARD_ACTIVATE);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : CardListBase : Event table
|
||||
|
||||
BEGIN_EVENT_TABLE(CardListBase, ItemList)
|
||||
EVT_LIST_COL_RIGHT_CLICK (wxID_ANY, CardListBase::onColumnRightClick)
|
||||
EVT_LIST_COL_END_DRAG (wxID_ANY, CardListBase::onColumnResize)
|
||||
EVT_LIST_ITEM_ACTIVATED (wxID_ANY, CardListBase::onItemActivate)
|
||||
EVT_CHAR ( CardListBase::onChar)
|
||||
EVT_MOTION ( CardListBase::onDrag)
|
||||
EVT_MENU (ID_SELECT_COLUMNS, CardListBase::onSelectColumns)
|
||||
EVT_CONTEXT_MENU ( CardListBase::onContextMenu)
|
||||
EVT_LIST_COL_RIGHT_CLICK (wxID_ANY, CardListBase::onColumnRightClick)
|
||||
EVT_LIST_COL_END_DRAG (wxID_ANY, CardListBase::onColumnResize)
|
||||
EVT_LIST_ITEM_ACTIVATED (wxID_ANY, CardListBase::onItemActivate)
|
||||
EVT_CHAR ( CardListBase::onChar)
|
||||
EVT_MOTION ( CardListBase::onDrag)
|
||||
EVT_MENU (ID_SELECT_COLUMNS, CardListBase::onSelectColumns)
|
||||
EVT_CONTEXT_MENU ( CardListBase::onContextMenu)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
+100
-100
@@ -24,29 +24,29 @@ DECLARE_LOCAL_EVENT_TYPE(EVENT_CARD_SELECT, <not used>)
|
||||
DECLARE_LOCAL_EVENT_TYPE(EVENT_CARD_ACTIVATE, <not used>)
|
||||
|
||||
/// Handle EVENT_CARD_SELECT events
|
||||
#define EVT_CARD_SELECT(id, handler) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(EVENT_CARD_SELECT, id, -1, \
|
||||
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) \
|
||||
(void (wxEvtHandler::*)(CardSelectEvent&)) (&handler), (wxObject*) NULL),
|
||||
#define EVT_CARD_SELECT(id, handler) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(EVENT_CARD_SELECT, id, -1, \
|
||||
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) \
|
||||
(void (wxEvtHandler::*)(CardSelectEvent&)) (&handler), (wxObject*) NULL),
|
||||
|
||||
/// Handle EVENT_CARD_ACTIVATE events
|
||||
#define EVT_CARD_ACTIVATE(id, handler) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(EVENT_CARD_ACTIVATE, id, -1, \
|
||||
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) \
|
||||
(void (wxEvtHandler::*)(CardSelectEvent&)) (&handler), (wxObject*) NULL),
|
||||
#define EVT_CARD_ACTIVATE(id, handler) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(EVENT_CARD_ACTIVATE, id, -1, \
|
||||
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) \
|
||||
(void (wxEvtHandler::*)(CardSelectEvent&)) (&handler), (wxObject*) NULL),
|
||||
|
||||
/// The event of selecting a card
|
||||
struct CardSelectEvent : public wxCommandEvent {
|
||||
inline CardSelectEvent(int type = EVENT_CARD_SELECT)
|
||||
: wxCommandEvent(type)
|
||||
{}
|
||||
|
||||
/// The selected card
|
||||
CardP getCard() const;
|
||||
/// All focused cards
|
||||
void getSelection(vector<CardP>& out) const;
|
||||
inline CardSelectEvent(int type = EVENT_CARD_SELECT)
|
||||
: wxCommandEvent(type)
|
||||
{}
|
||||
|
||||
/// The selected card
|
||||
CardP getCard() const;
|
||||
/// All focused cards
|
||||
void getSelection(vector<CardP>& out) const;
|
||||
private:
|
||||
CardListBase* getTheCardList() const;
|
||||
CardListBase* getTheCardList() const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : CardListBase
|
||||
@@ -60,94 +60,94 @@ struct CardSelectEvent : public wxCommandEvent {
|
||||
*/
|
||||
class CardListBase : public ItemList, public SetView {
|
||||
public:
|
||||
CardListBase(Window* parent, int id, long additional_style = 0);
|
||||
~CardListBase();
|
||||
|
||||
// --------------------------------------------------- : Selection
|
||||
|
||||
inline CardP getCard() const { return static_pointer_cast<Card>(selected_item); }
|
||||
inline void setCard(const CardP& card) { selectItem(card, true, false); }
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
bool canCut() const;
|
||||
bool canCopy() const;
|
||||
bool canPaste() const;
|
||||
bool canDelete() const;
|
||||
// Try to perform a clipboard operation, return success
|
||||
bool doCopy();
|
||||
bool doPaste();
|
||||
bool doDelete();
|
||||
|
||||
// --------------------------------------------------- : Set actions
|
||||
|
||||
virtual void onBeforeChangeSet();
|
||||
virtual void onChangeSet();
|
||||
virtual void onAction(const Action&, bool undone);
|
||||
|
||||
// --------------------------------------------------- : The cards
|
||||
CardListBase(Window* parent, int id, long additional_style = 0);
|
||||
~CardListBase();
|
||||
|
||||
// --------------------------------------------------- : Selection
|
||||
|
||||
inline CardP getCard() const { return static_pointer_cast<Card>(selected_item); }
|
||||
inline void setCard(const CardP& card) { selectItem(card, true, false); }
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
bool canCut() const;
|
||||
bool canCopy() const;
|
||||
bool canPaste() const;
|
||||
bool canDelete() const;
|
||||
// Try to perform a clipboard operation, return success
|
||||
bool doCopy();
|
||||
bool doPaste();
|
||||
bool doDelete();
|
||||
|
||||
// --------------------------------------------------- : Set actions
|
||||
|
||||
virtual void onBeforeChangeSet();
|
||||
virtual void onChangeSet();
|
||||
virtual void onAction(const Action&, bool undone);
|
||||
|
||||
// --------------------------------------------------- : The cards
|
||||
public:
|
||||
/// Return the card at the given position in the sorted card list
|
||||
inline CardP getCard(long pos) const { return static_pointer_cast<Card>(getItem(pos)); }
|
||||
/// Get a list of all focused cards
|
||||
void getSelection(vector<CardP>& out) const;
|
||||
/// Return the card at the given position in the sorted card list
|
||||
inline CardP getCard(long pos) const { return static_pointer_cast<Card>(getItem(pos)); }
|
||||
/// Get a list of all focused cards
|
||||
void getSelection(vector<CardP>& out) const;
|
||||
protected:
|
||||
/// Get a list of all cards
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
|
||||
/// Rebuild the card list (clear all vectors and fill them again)
|
||||
void rebuild();
|
||||
/// Do some additional updating before rebuilding the list
|
||||
virtual void onRebuild() {}
|
||||
/// Can the card list be modified?
|
||||
virtual bool allowModify() const { return false; }
|
||||
/// Sort all card lists
|
||||
virtual void sortBy(long column, bool ascending);
|
||||
|
||||
/// Send an 'item selected' event for the currently selected item (selected_item)
|
||||
virtual void sendEvent() { sendEvent(EVENT_CARD_SELECT); }
|
||||
void sendEvent(int type = EVENT_CARD_SELECT);
|
||||
/// Compare cards
|
||||
virtual bool compareItems(void* a, void* b) const;
|
||||
|
||||
// --------------------------------------------------- : Item 'events'
|
||||
|
||||
/// Get the text of an item in a specific column
|
||||
/** Overrides a function from wxListCtrl */
|
||||
virtual String OnGetItemText (long pos, long col) const;
|
||||
/// Get the image of an item, by default no image is used
|
||||
/** Overrides a function from wxListCtrl */
|
||||
virtual int OnGetItemImage(long pos) const;
|
||||
/// Get the color for an item
|
||||
virtual wxListItemAttr* OnGetItemAttr(long pos) const;
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
/// Get a list of all cards
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
|
||||
/// Rebuild the card list (clear all vectors and fill them again)
|
||||
void rebuild();
|
||||
/// Do some additional updating before rebuilding the list
|
||||
virtual void onRebuild() {}
|
||||
/// Can the card list be modified?
|
||||
virtual bool allowModify() const { return false; }
|
||||
/// Sort all card lists
|
||||
virtual void sortBy(long column, bool ascending);
|
||||
|
||||
/// Send an 'item selected' event for the currently selected item (selected_item)
|
||||
virtual void sendEvent() { sendEvent(EVENT_CARD_SELECT); }
|
||||
void sendEvent(int type = EVENT_CARD_SELECT);
|
||||
/// Compare cards
|
||||
virtual bool compareItems(void* a, void* b) const;
|
||||
|
||||
// --------------------------------------------------- : Item 'events'
|
||||
|
||||
/// Get the text of an item in a specific column
|
||||
/** Overrides a function from wxListCtrl */
|
||||
virtual String OnGetItemText (long pos, long col) const;
|
||||
/// Get the image of an item, by default no image is used
|
||||
/** Overrides a function from wxListCtrl */
|
||||
virtual int OnGetItemImage(long pos) const;
|
||||
/// Get the color for an item
|
||||
virtual wxListItemAttr* OnGetItemAttr(long pos) const;
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
private:
|
||||
// display stuff
|
||||
vector<FieldP> column_fields; ///< The field to use for each column (by column index)
|
||||
FieldP alternate_sort_field; ///< Second field to sort by, if the column doesn't suffice
|
||||
|
||||
mutable wxListItemAttr item_attr; // for OnGetItemAttr
|
||||
|
||||
// display stuff
|
||||
vector<FieldP> column_fields; ///< The field to use for each column (by column index)
|
||||
FieldP alternate_sort_field; ///< Second field to sort by, if the column doesn't suffice
|
||||
|
||||
mutable wxListItemAttr item_attr; // for OnGetItemAttr
|
||||
|
||||
public:
|
||||
/// Open a dialog for selecting columns to be shown
|
||||
void selectColumns();
|
||||
/// Open a dialog for selecting columns to be shown
|
||||
void selectColumns();
|
||||
private:
|
||||
/// Store the column sizes in the settings
|
||||
void storeColumns();
|
||||
/// All card lists; used to exchange column sizes
|
||||
static vector<CardListBase*> card_lists;
|
||||
|
||||
// --------------------------------------------------- : Window events
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onColumnRightClick(wxListEvent&);
|
||||
void onColumnResize (wxListEvent&);
|
||||
void onItemActivate (wxListEvent&);
|
||||
void onSelectColumns (wxCommandEvent&);
|
||||
void onChar (wxKeyEvent&);
|
||||
void onDrag (wxMouseEvent&);
|
||||
void onContextMenu (wxContextMenuEvent&);
|
||||
/// Store the column sizes in the settings
|
||||
void storeColumns();
|
||||
/// All card lists; used to exchange column sizes
|
||||
static vector<CardListBase*> card_lists;
|
||||
|
||||
// --------------------------------------------------- : Window events
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onColumnRightClick(wxListEvent&);
|
||||
void onColumnResize (wxListEvent&);
|
||||
void onItemActivate (wxListEvent&);
|
||||
void onSelectColumns (wxCommandEvent&);
|
||||
void onChar (wxKeyEvent&);
|
||||
void onDrag (wxMouseEvent&);
|
||||
void onContextMenu (wxContextMenuEvent&);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -18,142 +18,142 @@ DECLARE_TYPEOF_COLLECTION(CardListColumnSelectDialog::ColumnSettingsF);
|
||||
// ----------------------------------------------------------------------------- : CardListColumnSelectDialog
|
||||
|
||||
CardListColumnSelectDialog::CardListColumnSelectDialog(Window* parent, const GameP& game)
|
||||
: wxDialog(parent, wxID_ANY, _TITLE_("select columns"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||
, game(game)
|
||||
: wxDialog(parent, wxID_ANY, _TITLE_("select columns"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||
, game(game)
|
||||
{
|
||||
// Create controls
|
||||
list = new wxCheckListBox(this, wxID_ANY);
|
||||
// Create sizer
|
||||
wxSizer* s = new wxBoxSizer(wxVERTICAL);
|
||||
s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("select columns")), 0, wxALL, 8);
|
||||
s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("columns") ), 0, wxALL & ~wxBOTTOM, 8);
|
||||
wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL);
|
||||
s2->Add(list, 1, wxEXPAND | wxLEFT | wxRIGHT, 4);
|
||||
wxSizer* s3 = new wxBoxSizer(wxVERTICAL);
|
||||
s3->Add(new wxButton(this, ID_MOVE_UP, _BUTTON_("move up")), 0, wxEXPAND, 2);
|
||||
s3->Add(new wxButton(this, ID_MOVE_DOWN, _BUTTON_("move down")), 0, wxEXPAND | wxTOP, 2);
|
||||
s3->Add(new wxButton(this, ID_SHOW, _BUTTON_("show")), 0, wxEXPAND | wxTOP, 2);
|
||||
s3->Add(new wxButton(this, ID_HIDE, _BUTTON_("hide")), 0, wxEXPAND | wxTOP, 2);
|
||||
s2->Add(s3, 0, wxEXPAND | (wxALL & ~wxTOP), 4);
|
||||
s->Add(s2 , 1, wxEXPAND | wxALL, 4);
|
||||
s->Add(CreateButtonSizer(wxOK | wxCANCEL) , 0, wxEXPAND | wxALL, 8);
|
||||
s->SetSizeHints(this);
|
||||
SetSizer(s);
|
||||
// Set default size
|
||||
SetSize(350, 450);
|
||||
// Initialize order list
|
||||
initColumns();
|
||||
initList();
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
// Create controls
|
||||
list = new wxCheckListBox(this, wxID_ANY);
|
||||
// Create sizer
|
||||
wxSizer* s = new wxBoxSizer(wxVERTICAL);
|
||||
s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("select columns")), 0, wxALL, 8);
|
||||
s->Add(new wxStaticText(this, wxID_ANY, _LABEL_("columns") ), 0, wxALL & ~wxBOTTOM, 8);
|
||||
wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL);
|
||||
s2->Add(list, 1, wxEXPAND | wxLEFT | wxRIGHT, 4);
|
||||
wxSizer* s3 = new wxBoxSizer(wxVERTICAL);
|
||||
s3->Add(new wxButton(this, ID_MOVE_UP, _BUTTON_("move up")), 0, wxEXPAND, 2);
|
||||
s3->Add(new wxButton(this, ID_MOVE_DOWN, _BUTTON_("move down")), 0, wxEXPAND | wxTOP, 2);
|
||||
s3->Add(new wxButton(this, ID_SHOW, _BUTTON_("show")), 0, wxEXPAND | wxTOP, 2);
|
||||
s3->Add(new wxButton(this, ID_HIDE, _BUTTON_("hide")), 0, wxEXPAND | wxTOP, 2);
|
||||
s2->Add(s3, 0, wxEXPAND | (wxALL & ~wxTOP), 4);
|
||||
s->Add(s2 , 1, wxEXPAND | wxALL, 4);
|
||||
s->Add(CreateButtonSizer(wxOK | wxCANCEL) , 0, wxEXPAND | wxALL, 8);
|
||||
s->SetSizeHints(this);
|
||||
SetSizer(s);
|
||||
// Set default size
|
||||
SetSize(350, 450);
|
||||
// Initialize order list
|
||||
initColumns();
|
||||
initList();
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
}
|
||||
|
||||
struct SortByPosition {
|
||||
SortByPosition(const Game& game) : game(game) {}
|
||||
const Game& game;
|
||||
bool operator() (const CardListColumnSelectDialog::ColumnSettingsF& a, const CardListColumnSelectDialog::ColumnSettingsF& b){
|
||||
return a.settings.position < b.settings.position;
|
||||
}
|
||||
SortByPosition(const Game& game) : game(game) {}
|
||||
const Game& game;
|
||||
bool operator() (const CardListColumnSelectDialog::ColumnSettingsF& a, const CardListColumnSelectDialog::ColumnSettingsF& b){
|
||||
return a.settings.position < b.settings.position;
|
||||
}
|
||||
};
|
||||
|
||||
void CardListColumnSelectDialog::initColumns() {
|
||||
// order is a list of all columns that may be shown
|
||||
FOR_EACH(f, game->card_fields) {
|
||||
if (f->card_list_allow) {
|
||||
columns.push_back(ColumnSettingsF(f, settings.columnSettingsFor(*game, *f)));
|
||||
}
|
||||
}
|
||||
// sorted by position
|
||||
sort(columns.begin(), columns.end(), SortByPosition(*game));
|
||||
// force unique position
|
||||
int min = 0;
|
||||
FOR_EACH(c, columns) {
|
||||
if (c.settings.position < min) c.settings.position = min;
|
||||
min = c.settings.position + 1;
|
||||
}
|
||||
// order is a list of all columns that may be shown
|
||||
FOR_EACH(f, game->card_fields) {
|
||||
if (f->card_list_allow) {
|
||||
columns.push_back(ColumnSettingsF(f, settings.columnSettingsFor(*game, *f)));
|
||||
}
|
||||
}
|
||||
// sorted by position
|
||||
sort(columns.begin(), columns.end(), SortByPosition(*game));
|
||||
// force unique position
|
||||
int min = 0;
|
||||
FOR_EACH(c, columns) {
|
||||
if (c.settings.position < min) c.settings.position = min;
|
||||
min = c.settings.position + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CardListColumnSelectDialog::initList() {
|
||||
// Init items
|
||||
Color window_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
FOR_EACH(c, columns) {
|
||||
list->Append(tr(*game, c.field->card_list_name, capitalize));
|
||||
// check
|
||||
int i = list->GetCount() - 1;
|
||||
list->Check(i, c.settings.visible);
|
||||
#ifdef __WXMSW__
|
||||
// fix the background color
|
||||
list->GetItem(i)->SetBackgroundColour(window_color);
|
||||
#endif
|
||||
}
|
||||
// Init items
|
||||
Color window_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
FOR_EACH(c, columns) {
|
||||
list->Append(tr(*game, c.field->card_list_name, capitalize));
|
||||
// check
|
||||
int i = list->GetCount() - 1;
|
||||
list->Check(i, c.settings.visible);
|
||||
#ifdef __WXMSW__
|
||||
// fix the background color
|
||||
list->GetItem(i)->SetBackgroundColour(window_color);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CardListColumnSelectDialog::refreshItem(int i) {
|
||||
list->Check (i, columns[i].settings.visible);
|
||||
list->SetString(i, tr(*game, columns[i].field->card_list_name, capitalize) );
|
||||
list->Check (i, columns[i].settings.visible);
|
||||
list->SetString(i, tr(*game, columns[i].field->card_list_name, capitalize) );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Events
|
||||
|
||||
void CardListColumnSelectDialog::onSelect(wxCommandEvent& ev) {
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
}
|
||||
|
||||
void CardListColumnSelectDialog::onCheck(wxCommandEvent& ev) {
|
||||
int i = ev.GetSelection();
|
||||
columns[i].settings.visible = list->IsChecked(i);
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
int i = ev.GetSelection();
|
||||
columns[i].settings.visible = list->IsChecked(i);
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
}
|
||||
|
||||
void CardListColumnSelectDialog::onMove(wxCommandEvent& ev) {
|
||||
int i = list->GetSelection();
|
||||
int delta = ev.GetId() == ID_MOVE_UP ? -1 : 1;
|
||||
if (i == wxNOT_FOUND || i + delta < 0 || i + delta >= (int)columns.size()) return;
|
||||
list->SetSelection(i + delta);
|
||||
// swap the columns and positions
|
||||
swap(columns[i], columns[i + delta]);
|
||||
swap(columns[i].settings.position, columns[i + delta].settings.position);
|
||||
refreshItem(i);
|
||||
refreshItem(i + delta);
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
int i = list->GetSelection();
|
||||
int delta = ev.GetId() == ID_MOVE_UP ? -1 : 1;
|
||||
if (i == wxNOT_FOUND || i + delta < 0 || i + delta >= (int)columns.size()) return;
|
||||
list->SetSelection(i + delta);
|
||||
// swap the columns and positions
|
||||
swap(columns[i], columns[i + delta]);
|
||||
swap(columns[i].settings.position, columns[i + delta].settings.position);
|
||||
refreshItem(i);
|
||||
refreshItem(i + delta);
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
}
|
||||
|
||||
void CardListColumnSelectDialog::onShowHide(wxCommandEvent& ev) {
|
||||
int i = list->GetSelection();
|
||||
if (i == wxNOT_FOUND) return;
|
||||
columns[i].settings.visible = ev.GetId() == ID_SHOW;
|
||||
refreshItem(i);
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
int i = list->GetSelection();
|
||||
if (i == wxNOT_FOUND) return;
|
||||
columns[i].settings.visible = ev.GetId() == ID_SHOW;
|
||||
refreshItem(i);
|
||||
UpdateWindowUI(wxUPDATE_UI_RECURSE);
|
||||
}
|
||||
|
||||
void CardListColumnSelectDialog::onOk(wxCommandEvent&) {
|
||||
// store column settings
|
||||
FOR_EACH(c, columns) {
|
||||
settings.columnSettingsFor(*game, *c.field) = c.settings;
|
||||
}
|
||||
// close dialog
|
||||
EndModal(wxID_OK);
|
||||
|
||||
// store column settings
|
||||
FOR_EACH(c, columns) {
|
||||
settings.columnSettingsFor(*game, *c.field) = c.settings;
|
||||
}
|
||||
// close dialog
|
||||
EndModal(wxID_OK);
|
||||
|
||||
}
|
||||
|
||||
void CardListColumnSelectDialog::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||
int i = list->GetSelection();
|
||||
switch (ev.GetId()) {
|
||||
case ID_MOVE_UP: ev.Enable(i != wxNOT_FOUND && i - 1 >= 0); break;
|
||||
case ID_MOVE_DOWN: ev.Enable(i != wxNOT_FOUND && i + 1 < (int)columns.size()); break;
|
||||
case ID_SHOW: ev.Enable(i != wxNOT_FOUND && !columns[i].settings.visible); break;
|
||||
case ID_HIDE: ev.Enable(i != wxNOT_FOUND && columns[i].settings.visible); break;
|
||||
}
|
||||
int i = list->GetSelection();
|
||||
switch (ev.GetId()) {
|
||||
case ID_MOVE_UP: ev.Enable(i != wxNOT_FOUND && i - 1 >= 0); break;
|
||||
case ID_MOVE_DOWN: ev.Enable(i != wxNOT_FOUND && i + 1 < (int)columns.size()); break;
|
||||
case ID_SHOW: ev.Enable(i != wxNOT_FOUND && !columns[i].settings.visible); break;
|
||||
case ID_HIDE: ev.Enable(i != wxNOT_FOUND && columns[i].settings.visible); break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Event table
|
||||
|
||||
BEGIN_EVENT_TABLE(CardListColumnSelectDialog, wxDialog)
|
||||
EVT_LISTBOX (wxID_ANY, CardListColumnSelectDialog::onSelect)
|
||||
EVT_CHECKLISTBOX (wxID_ANY, CardListColumnSelectDialog::onCheck)
|
||||
EVT_BUTTON (ID_MOVE_UP, CardListColumnSelectDialog::onMove)
|
||||
EVT_BUTTON (ID_MOVE_DOWN, CardListColumnSelectDialog::onMove)
|
||||
EVT_BUTTON (ID_SHOW, CardListColumnSelectDialog::onShowHide)
|
||||
EVT_BUTTON (ID_HIDE, CardListColumnSelectDialog::onShowHide)
|
||||
EVT_BUTTON (wxID_OK, CardListColumnSelectDialog::onOk)
|
||||
EVT_UPDATE_UI (wxID_ANY, CardListColumnSelectDialog::onUpdateUI)
|
||||
EVT_LISTBOX (wxID_ANY, CardListColumnSelectDialog::onSelect)
|
||||
EVT_CHECKLISTBOX (wxID_ANY, CardListColumnSelectDialog::onCheck)
|
||||
EVT_BUTTON (ID_MOVE_UP, CardListColumnSelectDialog::onMove)
|
||||
EVT_BUTTON (ID_MOVE_DOWN, CardListColumnSelectDialog::onMove)
|
||||
EVT_BUTTON (ID_SHOW, CardListColumnSelectDialog::onShowHide)
|
||||
EVT_BUTTON (ID_HIDE, CardListColumnSelectDialog::onShowHide)
|
||||
EVT_BUTTON (wxID_OK, CardListColumnSelectDialog::onOk)
|
||||
EVT_UPDATE_UI (wxID_ANY, CardListColumnSelectDialog::onUpdateUI)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
@@ -27,39 +27,39 @@ DECLARE_POINTER_TYPE(Field);
|
||||
*/
|
||||
class CardListColumnSelectDialog : public wxDialog {
|
||||
public:
|
||||
CardListColumnSelectDialog(Window* parent, const GameP& game);
|
||||
|
||||
CardListColumnSelectDialog(Window* parent, const GameP& game);
|
||||
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
// gui items
|
||||
wxCheckListBox* list;
|
||||
// other info
|
||||
GameP game; ///< The game we are changing
|
||||
public: struct ColumnSettingsF {
|
||||
ColumnSettingsF(const FieldP& field, const ColumnSettings& settings)
|
||||
: field(field)
|
||||
, settings(settings)
|
||||
{}
|
||||
FieldP field;
|
||||
ColumnSettings settings;
|
||||
};
|
||||
private: vector<ColumnSettingsF> columns; ///< Settings of the fields, in order
|
||||
|
||||
// initialize columns
|
||||
void initColumns();
|
||||
// intialize the list box
|
||||
void initList();
|
||||
// refresh list item i
|
||||
void refreshItem(int i);
|
||||
|
||||
void onSelect (wxCommandEvent&);
|
||||
void onCheck (wxCommandEvent&);
|
||||
void onMove (wxCommandEvent&);
|
||||
void onShowHide(wxCommandEvent&);
|
||||
void onOk (wxCommandEvent&);
|
||||
void onUpdateUI(wxUpdateUIEvent&);
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
// gui items
|
||||
wxCheckListBox* list;
|
||||
// other info
|
||||
GameP game; ///< The game we are changing
|
||||
public: struct ColumnSettingsF {
|
||||
ColumnSettingsF(const FieldP& field, const ColumnSettings& settings)
|
||||
: field(field)
|
||||
, settings(settings)
|
||||
{}
|
||||
FieldP field;
|
||||
ColumnSettings settings;
|
||||
};
|
||||
private: vector<ColumnSettingsF> columns; ///< Settings of the fields, in order
|
||||
|
||||
// initialize columns
|
||||
void initColumns();
|
||||
// intialize the list box
|
||||
void initList();
|
||||
// refresh list item i
|
||||
void refreshItem(int i);
|
||||
|
||||
void onSelect (wxCommandEvent&);
|
||||
void onCheck (wxCommandEvent&);
|
||||
void onMove (wxCommandEvent&);
|
||||
void onShowHide(wxCommandEvent&);
|
||||
void onOk (wxCommandEvent&);
|
||||
void onUpdateUI(wxUpdateUIEvent&);
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -20,135 +20,135 @@ DEFINE_EVENT_TYPE(EVENT_SIZE_CHANGE);
|
||||
// ----------------------------------------------------------------------------- : CardViewer
|
||||
|
||||
CardViewer::CardViewer(Window* parent, int id, long style)
|
||||
: wxControl(parent, id, wxDefaultPosition, wxDefaultSize, style)
|
||||
, up_to_date(false)
|
||||
: wxControl(parent, id, wxDefaultPosition, wxDefaultSize, style)
|
||||
, up_to_date(false)
|
||||
{}
|
||||
|
||||
wxSize CardViewer::DoGetBestSize() const {
|
||||
wxSize ws = GetSize(), cs = GetClientSize();
|
||||
if (set) {
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
wxSize size(int(stylesheet->card_width * ss.card_zoom()), int(stylesheet->card_height * ss.card_zoom()));
|
||||
if (is_sideways(deg_to_rad(ss.card_angle()))) swap(size.x, size.y);
|
||||
return size + ws - cs;
|
||||
}
|
||||
return cs;
|
||||
wxSize ws = GetSize(), cs = GetClientSize();
|
||||
if (set) {
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
wxSize size(int(stylesheet->card_width * ss.card_zoom()), int(stylesheet->card_height * ss.card_zoom()));
|
||||
if (is_sideways(deg_to_rad(ss.card_angle()))) swap(size.x, size.y);
|
||||
return size + ws - cs;
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
void CardViewer::redraw(const ValueViewer& v) {
|
||||
// Don't refresh if we OR ANOTHER CardViewer is drawing
|
||||
// drawing another viewer causes styles to be updated for its active card, which may be different,
|
||||
// causing the two viewers to continously refresh.
|
||||
if (drawing_card()) return;
|
||||
up_to_date = false;
|
||||
RefreshRect(getRotation().trRectToBB(v.boundingBox()), false);
|
||||
// Don't refresh if we OR ANOTHER CardViewer is drawing
|
||||
// drawing another viewer causes styles to be updated for its active card, which may be different,
|
||||
// causing the two viewers to continously refresh.
|
||||
if (drawing_card()) return;
|
||||
up_to_date = false;
|
||||
RefreshRect(getRotation().trRectToBB(v.boundingBox()), false);
|
||||
}
|
||||
|
||||
void CardViewer::onChange() {
|
||||
redraw();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void CardViewer::redraw() {
|
||||
if (drawing_card()) return;
|
||||
up_to_date = false;
|
||||
Refresh(false);
|
||||
if (drawing_card()) return;
|
||||
up_to_date = false;
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
void CardViewer::onChangeSize() {
|
||||
wxSize ws = GetSize(), cs = GetClientSize();
|
||||
wxSize desired_cs = (wxSize)getRotation().getExternalSize() + ws - cs;
|
||||
if (desired_cs != cs) {
|
||||
wxCommandEvent ev(EVENT_SIZE_CHANGE, GetId());
|
||||
ProcessEvent(ev);
|
||||
}
|
||||
wxSize ws = GetSize(), cs = GetClientSize();
|
||||
wxSize desired_cs = (wxSize)getRotation().getExternalSize() + ws - cs;
|
||||
if (desired_cs != cs) {
|
||||
wxCommandEvent ev(EVENT_SIZE_CHANGE, GetId());
|
||||
ProcessEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
DECLARE_DYNAMIC_ARG(bool, inOnPaint);
|
||||
IMPLEMENT_DYNAMIC_ARG(bool, inOnPaint, false);
|
||||
DECLARE_DYNAMIC_ARG(bool, inOnPaint);
|
||||
IMPLEMENT_DYNAMIC_ARG(bool, inOnPaint, false);
|
||||
#endif
|
||||
|
||||
void CardViewer::onPaint(wxPaintEvent&) {
|
||||
#ifdef _DEBUG
|
||||
// we don't want recursion
|
||||
if (inOnPaint()) {
|
||||
wxTrap();
|
||||
}
|
||||
WITH_DYNAMIC_ARG(inOnPaint, true);
|
||||
#endif
|
||||
wxSize cs = GetClientSize();
|
||||
if (!buffer.Ok() || buffer.GetWidth() != cs.GetWidth() || buffer.GetHeight() != cs.GetHeight()) {
|
||||
buffer = Bitmap(cs.GetWidth(), cs.GetHeight());
|
||||
up_to_date = false;
|
||||
}
|
||||
wxBufferedPaintDC dc(this, buffer);
|
||||
// scrolling
|
||||
// int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
// dc.SetDeviceOrigin(-dx, -dy);
|
||||
wxRegion clip = GetUpdateRegion();
|
||||
// clip.Offset(dx, dy);
|
||||
dc.SetDeviceClippingRegion(clip);
|
||||
// draw
|
||||
if (!up_to_date) {
|
||||
up_to_date = true;
|
||||
try {
|
||||
draw(dc);
|
||||
} CATCH_ALL_ERRORS(false); // don't show message boxes in onPaint!
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
// we don't want recursion
|
||||
if (inOnPaint()) {
|
||||
wxTrap();
|
||||
}
|
||||
WITH_DYNAMIC_ARG(inOnPaint, true);
|
||||
#endif
|
||||
wxSize cs = GetClientSize();
|
||||
if (!buffer.Ok() || buffer.GetWidth() != cs.GetWidth() || buffer.GetHeight() != cs.GetHeight()) {
|
||||
buffer = Bitmap(cs.GetWidth(), cs.GetHeight());
|
||||
up_to_date = false;
|
||||
}
|
||||
wxBufferedPaintDC dc(this, buffer);
|
||||
// scrolling
|
||||
// int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
// dc.SetDeviceOrigin(-dx, -dy);
|
||||
wxRegion clip = GetUpdateRegion();
|
||||
// clip.Offset(dx, dy);
|
||||
dc.SetDeviceClippingRegion(clip);
|
||||
// draw
|
||||
if (!up_to_date) {
|
||||
up_to_date = true;
|
||||
try {
|
||||
draw(dc);
|
||||
} CATCH_ALL_ERRORS(false); // don't show message boxes in onPaint!
|
||||
}
|
||||
}
|
||||
|
||||
void CardViewer::drawViewer(RotatedDC& dc, ValueViewer& v) {
|
||||
if (shouldDraw(v)) v.draw(dc);
|
||||
if (shouldDraw(v)) v.draw(dc);
|
||||
}
|
||||
|
||||
bool CardViewer::shouldDraw(const ValueViewer& v) const {
|
||||
// int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
// wxRegion clip = GetUpdateRegion();
|
||||
// clip.Offset(dx, dy);
|
||||
return GetUpdateRegion().Contains(getRotation().trRectToBB(v.boundingBox().toRect()).toRect()) != wxOutRegion;
|
||||
// int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
// wxRegion clip = GetUpdateRegion();
|
||||
// clip.Offset(dx, dy);
|
||||
return GetUpdateRegion().Contains(getRotation().trRectToBB(v.boundingBox().toRect()).toRect()) != wxOutRegion;
|
||||
}
|
||||
|
||||
// helper class for overdrawDC()
|
||||
class CardViewer::OverdrawDC_aux : private wxClientDC {
|
||||
protected:
|
||||
wxBufferedDC bufferedDC;
|
||||
|
||||
OverdrawDC_aux(CardViewer* window)
|
||||
: wxClientDC(window)
|
||||
{
|
||||
bufferedDC.Init((wxClientDC*)this, window->buffer);
|
||||
}
|
||||
wxBufferedDC bufferedDC;
|
||||
|
||||
OverdrawDC_aux(CardViewer* window)
|
||||
: wxClientDC(window)
|
||||
{
|
||||
bufferedDC.Init((wxClientDC*)this, window->buffer);
|
||||
}
|
||||
};
|
||||
class CardViewer::OverdrawDC : private OverdrawDC_aux, public RotatedDC {
|
||||
public:
|
||||
OverdrawDC(CardViewer* window)
|
||||
: OverdrawDC_aux(window)
|
||||
, RotatedDC(bufferedDC, window->getRotation(), QUALITY_LOW)
|
||||
{}
|
||||
OverdrawDC(CardViewer* window)
|
||||
: OverdrawDC_aux(window)
|
||||
, RotatedDC(bufferedDC, window->getRotation(), QUALITY_LOW)
|
||||
{}
|
||||
};
|
||||
|
||||
shared_ptr<RotatedDC> CardViewer::overdrawDC() {
|
||||
#ifdef _DEBUG
|
||||
// don't call from onPaint
|
||||
if (inOnPaint()) {
|
||||
wxTrap();
|
||||
}
|
||||
#endif
|
||||
return shared_ptr<RotatedDC>(new OverdrawDC(this));
|
||||
#ifdef _DEBUG
|
||||
// don't call from onPaint
|
||||
if (inOnPaint()) {
|
||||
wxTrap();
|
||||
}
|
||||
#endif
|
||||
return shared_ptr<RotatedDC>(new OverdrawDC(this));
|
||||
}
|
||||
|
||||
Rotation CardViewer::getRotation() const {
|
||||
// Same as DataViewer::getRotation, only taking into account scrolling
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
return Rotation(deg_to_rad(ss.card_angle()), stylesheet->getCardRect().move(-dx,-dy,0,0), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
|
||||
// Same as DataViewer::getRotation, only taking into account scrolling
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
return Rotation(deg_to_rad(ss.card_angle()), stylesheet->getCardRect().move(-dx,-dy,0,0), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Event table
|
||||
|
||||
BEGIN_EVENT_TABLE(CardViewer, wxControl)
|
||||
EVT_PAINT (CardViewer::onPaint)
|
||||
EVT_ERASE_BACKGROUND(CardViewer::onEraseBackground)
|
||||
EVT_PAINT (CardViewer::onPaint)
|
||||
EVT_ERASE_BACKGROUND(CardViewer::onEraseBackground)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
@@ -24,45 +24,45 @@ DECLARE_LOCAL_EVENT_TYPE(EVENT_SIZE_CHANGE, <not used>)
|
||||
/// A control to view a single card
|
||||
class CardViewer : public wxControl, public DataViewer {
|
||||
public:
|
||||
CardViewer(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Get a dc to draw on the card outside onPaint
|
||||
/** May NOT be called while in onPaint/draw */
|
||||
shared_ptr<RotatedDC> overdrawDC();
|
||||
|
||||
/// Invalidate and redraw the entire viewer
|
||||
void redraw();
|
||||
/// Invalidate and redraw (the area of) a single value viewer
|
||||
virtual void redraw(const ValueViewer&);
|
||||
|
||||
/// The rotation to use
|
||||
virtual Rotation getRotation() const;
|
||||
|
||||
virtual bool AcceptsFocus() const { return false; }
|
||||
|
||||
CardViewer(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Get a dc to draw on the card outside onPaint
|
||||
/** May NOT be called while in onPaint/draw */
|
||||
shared_ptr<RotatedDC> overdrawDC();
|
||||
|
||||
/// Invalidate and redraw the entire viewer
|
||||
void redraw();
|
||||
/// Invalidate and redraw (the area of) a single value viewer
|
||||
virtual void redraw(const ValueViewer&);
|
||||
|
||||
/// The rotation to use
|
||||
virtual Rotation getRotation() const;
|
||||
|
||||
virtual bool AcceptsFocus() const { return false; }
|
||||
|
||||
protected:
|
||||
/// Return the desired size of control
|
||||
virtual wxSize DoGetBestSize() const;
|
||||
|
||||
virtual void onChange();
|
||||
virtual void onChangeSize();
|
||||
|
||||
/// Should the given viewer be drawn?
|
||||
bool shouldDraw(const ValueViewer&) const;
|
||||
|
||||
virtual void drawViewer(RotatedDC& dc, ValueViewer& v);
|
||||
|
||||
/// Return the desired size of control
|
||||
virtual wxSize DoGetBestSize() const;
|
||||
|
||||
virtual void onChange();
|
||||
virtual void onChangeSize();
|
||||
|
||||
/// Should the given viewer be drawn?
|
||||
bool shouldDraw(const ValueViewer&) const;
|
||||
|
||||
virtual void drawViewer(RotatedDC& dc, ValueViewer& v);
|
||||
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onEraseBackground(wxEraseEvent&) {}
|
||||
void onPaint(wxPaintEvent&);
|
||||
|
||||
Bitmap buffer; ///< Off-screen buffer we draw to
|
||||
bool up_to_date; ///< Is the buffer up to date?
|
||||
|
||||
class OverdrawDC;
|
||||
class OverdrawDC_aux;
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onEraseBackground(wxEraseEvent&) {}
|
||||
void onPaint(wxPaintEvent&);
|
||||
|
||||
Bitmap buffer; ///< Off-screen buffer we draw to
|
||||
bool up_to_date; ///< Is the buffer up to date?
|
||||
|
||||
class OverdrawDC;
|
||||
class OverdrawDC_aux;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
/// A drop down list of recent choices, for autocomplete
|
||||
class DropDownMRUList : public DropDownList {
|
||||
public:
|
||||
DropDownMRUList(Window* parent, vector<String> const& choices)
|
||||
: DropDownList(parent)
|
||||
, choices(choices)
|
||||
{}
|
||||
|
||||
vector<String> choices;
|
||||
|
||||
DropDownMRUList(Window* parent, vector<String> const& choices)
|
||||
: DropDownList(parent)
|
||||
, choices(choices)
|
||||
{}
|
||||
|
||||
vector<String> choices;
|
||||
|
||||
protected:
|
||||
virtual size_t selection() const { return NO_SELECTION; }
|
||||
virtual size_t itemCount() const { return choices.size(); }
|
||||
virtual String itemText(size_t item) const { return choices.at(item); }
|
||||
virtual void select(size_t item);
|
||||
virtual size_t selection() const { return NO_SELECTION; }
|
||||
virtual size_t itemCount() const { return choices.size(); }
|
||||
virtual String itemText(size_t item) const { return choices.at(item); }
|
||||
virtual void select(size_t item);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : FilterControl
|
||||
@@ -35,126 +35,126 @@ class DropDownMRUList : public DropDownList {
|
||||
/// Text control that forwards focus events to the parent
|
||||
class TextCtrlWithFocus : public wxTextCtrl {
|
||||
public:
|
||||
DECLARE_EVENT_TABLE();
|
||||
void forwardFocusEvent(wxFocusEvent&);
|
||||
void forwardKeyEvent(wxKeyEvent&);
|
||||
DECLARE_EVENT_TABLE();
|
||||
void forwardFocusEvent(wxFocusEvent&);
|
||||
void forwardKeyEvent(wxKeyEvent&);
|
||||
};
|
||||
|
||||
FilterCtrl::FilterCtrl(wxWindow* parent, int id, String const& placeholder)
|
||||
: wxControl(parent, id, wxDefaultPosition, wxSize(160,41), wxSTATIC_BORDER)
|
||||
, changing(false)
|
||||
, placeholder(placeholder)
|
||||
: wxControl(parent, id, wxDefaultPosition, wxSize(160,41), wxSTATIC_BORDER)
|
||||
, changing(false)
|
||||
, placeholder(placeholder)
|
||||
{
|
||||
wxColour bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
SetBackgroundColour(bg);
|
||||
SetCursor(wxCURSOR_IBEAM);
|
||||
filter_ctrl = new TextCtrlWithFocus();
|
||||
filter_ctrl->Create(this, wxID_ANY, _(""), wxDefaultPosition, wxSize(130,-1), wxNO_BORDER);
|
||||
clear_button = new HoverButton(this, wxID_ANY, _("btn_clear_filter"), bg, false);
|
||||
clear_button->SetCursor(*wxSTANDARD_CURSOR);
|
||||
onSize();
|
||||
update();
|
||||
wxColour bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
SetBackgroundColour(bg);
|
||||
SetCursor(wxCURSOR_IBEAM);
|
||||
filter_ctrl = new TextCtrlWithFocus();
|
||||
filter_ctrl->Create(this, wxID_ANY, _(""), wxDefaultPosition, wxSize(130,-1), wxNO_BORDER);
|
||||
clear_button = new HoverButton(this, wxID_ANY, _("btn_clear_filter"), bg, false);
|
||||
clear_button->SetCursor(*wxSTANDARD_CURSOR);
|
||||
onSize();
|
||||
update();
|
||||
}
|
||||
|
||||
void FilterCtrl::setFilter(const String& new_value, bool event) {
|
||||
if (this->value == new_value) return;
|
||||
// update ui
|
||||
this->value = new_value;
|
||||
update();
|
||||
// send event
|
||||
if (event) {
|
||||
wxCommandEvent ev(wxEVT_COMMAND_TEXT_UPDATED, GetId());
|
||||
GetParent()->HandleWindowEvent(ev);
|
||||
}
|
||||
if (this->value == new_value) return;
|
||||
// update ui
|
||||
this->value = new_value;
|
||||
update();
|
||||
// send event
|
||||
if (event) {
|
||||
wxCommandEvent ev(wxEVT_COMMAND_TEXT_UPDATED, GetId());
|
||||
GetParent()->HandleWindowEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
void FilterCtrl::update() {
|
||||
changing = true;
|
||||
if (!value.empty() || hasFocus()) {
|
||||
filter_ctrl->SetValue(value);
|
||||
wxColour fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
filter_ctrl->SetDefaultStyle(wxTextAttr(fg));
|
||||
filter_ctrl->SetForegroundColour(fg);
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
filter_ctrl->SetFont(font);
|
||||
} else {
|
||||
filter_ctrl->SetValue(placeholder);
|
||||
wxColour fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
wxColour bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
filter_ctrl->SetDefaultStyle(wxTextAttr(lerp(fg,bg,0.5)));
|
||||
filter_ctrl->SetForegroundColour(lerp(fg,bg,0.5));
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
font.SetStyle(wxFONTSTYLE_ITALIC);
|
||||
filter_ctrl->SetFont(font);
|
||||
}
|
||||
clear_button->Show(!value.empty());
|
||||
changing = false;
|
||||
changing = true;
|
||||
if (!value.empty() || hasFocus()) {
|
||||
filter_ctrl->SetValue(value);
|
||||
wxColour fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
filter_ctrl->SetDefaultStyle(wxTextAttr(fg));
|
||||
filter_ctrl->SetForegroundColour(fg);
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
filter_ctrl->SetFont(font);
|
||||
} else {
|
||||
filter_ctrl->SetValue(placeholder);
|
||||
wxColour fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
wxColour bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
filter_ctrl->SetDefaultStyle(wxTextAttr(lerp(fg,bg,0.5)));
|
||||
filter_ctrl->SetForegroundColour(lerp(fg,bg,0.5));
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
font.SetStyle(wxFONTSTYLE_ITALIC);
|
||||
filter_ctrl->SetFont(font);
|
||||
}
|
||||
clear_button->Show(!value.empty());
|
||||
changing = false;
|
||||
}
|
||||
|
||||
void FilterCtrl::onChangeEvent(wxCommandEvent&) {
|
||||
if (!changing) {
|
||||
setFilter(filter_ctrl->GetValue(),true);
|
||||
}
|
||||
if (!changing) {
|
||||
setFilter(filter_ctrl->GetValue(),true);
|
||||
}
|
||||
}
|
||||
void FilterCtrl::onChar(wxKeyEvent& ev) {
|
||||
if (ev.GetKeyCode() == WXK_ESCAPE) {
|
||||
// escape clears the filter box
|
||||
clearFilter(true);
|
||||
} else {
|
||||
ev.Skip();
|
||||
}
|
||||
if (ev.GetKeyCode() == WXK_ESCAPE) {
|
||||
// escape clears the filter box
|
||||
clearFilter(true);
|
||||
} else {
|
||||
ev.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
void FilterCtrl::onClear(wxCommandEvent&) {
|
||||
clearFilter(true);
|
||||
clearFilter(true);
|
||||
}
|
||||
|
||||
void FilterCtrl::onSizeEvent(wxSizeEvent&) {
|
||||
onSize();
|
||||
onSize();
|
||||
}
|
||||
void FilterCtrl::onSize() {
|
||||
wxSize s = GetClientSize();
|
||||
wxSize fs = filter_ctrl->GetBestSize();
|
||||
wxSize cs = clear_button->GetBestSize();
|
||||
int margin = 2;
|
||||
filter_ctrl ->SetSize(margin, max(margin,(s.y-fs.y)/2), s.x - cs.x - 3*margin, fs.y);
|
||||
clear_button->SetSize(s.x - cs.x - margin, (s.y-cs.y)/2, cs.x, cs.y);
|
||||
wxSize s = GetClientSize();
|
||||
wxSize fs = filter_ctrl->GetBestSize();
|
||||
wxSize cs = clear_button->GetBestSize();
|
||||
int margin = 2;
|
||||
filter_ctrl ->SetSize(margin, max(margin,(s.y-fs.y)/2), s.x - cs.x - 3*margin, fs.y);
|
||||
clear_button->SetSize(s.x - cs.x - margin, (s.y-cs.y)/2, cs.x, cs.y);
|
||||
}
|
||||
|
||||
void FilterCtrl::onSetFocus(wxFocusEvent&) {
|
||||
filter_ctrl->SetFocus();
|
||||
update();
|
||||
filter_ctrl->SetFocus();
|
||||
update();
|
||||
}
|
||||
void FilterCtrl::onKillFocus(wxFocusEvent&) {
|
||||
update();
|
||||
update();
|
||||
}
|
||||
|
||||
bool FilterCtrl::hasFocus() {
|
||||
wxWindow* focus = wxWindow::FindFocus();
|
||||
return focus == this || focus == filter_ctrl || focus == clear_button;
|
||||
wxWindow* focus = wxWindow::FindFocus();
|
||||
return focus == this || focus == filter_ctrl || focus == clear_button;
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(FilterCtrl, wxControl)
|
||||
EVT_BUTTON (wxID_ANY, FilterCtrl::onClear)
|
||||
EVT_TEXT (wxID_ANY, FilterCtrl::onChangeEvent)
|
||||
EVT_SIZE (FilterCtrl::onSizeEvent)
|
||||
EVT_SET_FOCUS (FilterCtrl::onSetFocus)
|
||||
EVT_KILL_FOCUS(FilterCtrl::onKillFocus)
|
||||
EVT_CHAR (FilterCtrl::onChar)
|
||||
EVT_BUTTON (wxID_ANY, FilterCtrl::onClear)
|
||||
EVT_TEXT (wxID_ANY, FilterCtrl::onChangeEvent)
|
||||
EVT_SIZE (FilterCtrl::onSizeEvent)
|
||||
EVT_SET_FOCUS (FilterCtrl::onSetFocus)
|
||||
EVT_KILL_FOCUS(FilterCtrl::onKillFocus)
|
||||
EVT_CHAR (FilterCtrl::onChar)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextCtrlWithFocus
|
||||
|
||||
void TextCtrlWithFocus::forwardFocusEvent(wxFocusEvent& ev) {
|
||||
GetParent()->HandleWindowEvent(ev);
|
||||
GetParent()->HandleWindowEvent(ev);
|
||||
}
|
||||
void TextCtrlWithFocus::forwardKeyEvent(wxKeyEvent& ev) {
|
||||
GetParent()->HandleWindowEvent(ev);
|
||||
GetParent()->HandleWindowEvent(ev);
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(TextCtrlWithFocus, wxTextCtrl)
|
||||
EVT_SET_FOCUS (TextCtrlWithFocus::forwardFocusEvent)
|
||||
EVT_KILL_FOCUS(TextCtrlWithFocus::forwardFocusEvent)
|
||||
EVT_CHAR (TextCtrlWithFocus::forwardKeyEvent)
|
||||
EVT_SET_FOCUS (TextCtrlWithFocus::forwardFocusEvent)
|
||||
EVT_KILL_FOCUS(TextCtrlWithFocus::forwardFocusEvent)
|
||||
EVT_CHAR (TextCtrlWithFocus::forwardKeyEvent)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
@@ -20,41 +20,41 @@ class TextCtrlWithFocus;
|
||||
/// A search/filter textbox
|
||||
class FilterCtrl : public wxControl {
|
||||
public:
|
||||
FilterCtrl(wxWindow* parent, int id, String const& placeholder);
|
||||
|
||||
/// Set the filter text
|
||||
void setFilter(const String& filter, bool send_event = false);
|
||||
void clearFilter(bool send_event = false) { setFilter(String(),send_event); }
|
||||
bool hasFilter() const { return !value.empty(); }
|
||||
String const& getFilterString() const { return value; }
|
||||
|
||||
template <typename T>
|
||||
intrusive_ptr<Filter<T> > getFilter() const {
|
||||
if (hasFilter()) {
|
||||
return intrusive(new QuickFilter<T>(getFilterString()));
|
||||
} else {
|
||||
return intrusive_ptr<Filter<T> >();
|
||||
}
|
||||
}
|
||||
|
||||
FilterCtrl(wxWindow* parent, int id, String const& placeholder);
|
||||
|
||||
/// Set the filter text
|
||||
void setFilter(const String& filter, bool send_event = false);
|
||||
void clearFilter(bool send_event = false) { setFilter(String(),send_event); }
|
||||
bool hasFilter() const { return !value.empty(); }
|
||||
String const& getFilterString() const { return value; }
|
||||
|
||||
template <typename T>
|
||||
intrusive_ptr<Filter<T> > getFilter() const {
|
||||
if (hasFilter()) {
|
||||
return intrusive(new QuickFilter<T>(getFilterString()));
|
||||
} else {
|
||||
return intrusive_ptr<Filter<T> >();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
bool changing;
|
||||
String value;
|
||||
String placeholder;
|
||||
TextCtrlWithFocus* filter_ctrl;
|
||||
HoverButton* clear_button;
|
||||
|
||||
void update();
|
||||
bool hasFocus();
|
||||
// wxWidgets appears to have developed an overload allergy
|
||||
void onChangeEvent(wxCommandEvent&);
|
||||
void onClear(wxCommandEvent&);
|
||||
void onSizeEvent(wxSizeEvent&);
|
||||
void onChar(wxKeyEvent&);
|
||||
void onSize();
|
||||
void onSetFocus(wxFocusEvent&);
|
||||
void onKillFocus(wxFocusEvent&);
|
||||
DECLARE_EVENT_TABLE();
|
||||
bool changing;
|
||||
String value;
|
||||
String placeholder;
|
||||
TextCtrlWithFocus* filter_ctrl;
|
||||
HoverButton* clear_button;
|
||||
|
||||
void update();
|
||||
bool hasFocus();
|
||||
// wxWidgets appears to have developed an overload allergy
|
||||
void onChangeEvent(wxCommandEvent&);
|
||||
void onClear(wxCommandEvent&);
|
||||
void onSizeEvent(wxSizeEvent&);
|
||||
void onChar(wxKeyEvent&);
|
||||
void onSize();
|
||||
void onSetFocus(wxFocusEvent&);
|
||||
void onKillFocus(wxFocusEvent&);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -14,22 +14,22 @@ DECLARE_TYPEOF_COLLECTION(CardP);
|
||||
// ----------------------------------------------------------------------------- : FilteredCardList
|
||||
|
||||
FilteredCardList::FilteredCardList(Window* parent, int id, long additional_style)
|
||||
: CardListBase(parent, id, additional_style)
|
||||
: CardListBase(parent, id, additional_style)
|
||||
{}
|
||||
|
||||
void FilteredCardList::setFilter(const CardListFilterP& filter) {
|
||||
this->filter = filter;
|
||||
rebuild();
|
||||
this->filter = filter;
|
||||
rebuild();
|
||||
}
|
||||
|
||||
void FilteredCardList::onChangeSet() {
|
||||
// clear filter before changing set, the filter might not make sense for a different set
|
||||
filter = CardListFilterP();
|
||||
CardListBase::onChangeSet();
|
||||
// clear filter before changing set, the filter might not make sense for a different set
|
||||
filter = CardListFilterP();
|
||||
CardListBase::onChangeSet();
|
||||
}
|
||||
|
||||
void FilteredCardList::getItems(vector<VoidP>& out) const {
|
||||
if (filter) {
|
||||
filter->getItems(set->cards,out);
|
||||
}
|
||||
if (filter) {
|
||||
filter->getItems(set->cards,out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,19 +20,19 @@ typedef intrusive_ptr<Filter<Card> > CardListFilterP;
|
||||
/// A card list that lists a subset of the cards in the set
|
||||
class FilteredCardList : public CardListBase {
|
||||
public:
|
||||
FilteredCardList(Window* parent, int id, long additional_style = 0);
|
||||
|
||||
/// Change the filter to use
|
||||
void setFilter(const CardListFilterP& filter);
|
||||
|
||||
FilteredCardList(Window* parent, int id, long additional_style = 0);
|
||||
|
||||
/// Change the filter to use
|
||||
void setFilter(const CardListFilterP& filter);
|
||||
|
||||
protected:
|
||||
/// Get only the subset of the cards
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
|
||||
virtual void onChangeSet();
|
||||
|
||||
private:
|
||||
CardListFilterP filter; ///< Filter with which this.cards is made
|
||||
/// Get only the subset of the cards
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
|
||||
virtual void onChangeSet();
|
||||
|
||||
private:
|
||||
CardListFilterP filter; ///< Filter with which this.cards is made
|
||||
};
|
||||
|
||||
|
||||
|
||||
+257
-257
@@ -22,332 +22,332 @@ DEFINE_EVENT_TYPE(EVENT_GALLERY_ACTIVATE);
|
||||
// ----------------------------------------------------------------------------- : GalleryList
|
||||
|
||||
GalleryList::GalleryList(Window* parent, int id, int direction, bool always_focused)
|
||||
: wxPanel(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME | wxWANTS_CHARS | (direction == wxHORIZONTAL ? wxHSCROLL : wxVSCROLL) )
|
||||
, active_subcolumn(0)
|
||||
, direction(direction)
|
||||
, column_count(1)
|
||||
, always_focused(always_focused)
|
||||
, visible_start(0)
|
||||
: wxPanel(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME | wxWANTS_CHARS | (direction == wxHORIZONTAL ? wxHSCROLL : wxVSCROLL) )
|
||||
, active_subcolumn(0)
|
||||
, direction(direction)
|
||||
, column_count(1)
|
||||
, always_focused(always_focused)
|
||||
, visible_start(0)
|
||||
{
|
||||
SubColumn col;
|
||||
col.can_select = true;
|
||||
col.selection = NO_SELECTION;
|
||||
subcolumns.push_back(col);
|
||||
enable_themed_selection_rectangle(this);
|
||||
SubColumn col;
|
||||
col.can_select = true;
|
||||
col.selection = NO_SELECTION;
|
||||
subcolumns.push_back(col);
|
||||
enable_themed_selection_rectangle(this);
|
||||
}
|
||||
|
||||
void GalleryList::selectSubColumn(size_t subcol) {
|
||||
if (subcol >= subcolumns.size()) return;
|
||||
if (!subcolumns[subcol].can_select) return;
|
||||
if (active_subcolumn == subcol) return;
|
||||
RefreshItem(subcolumns[active_subcolumn].selection);
|
||||
RefreshItem(subcolumns[subcol ].selection);
|
||||
active_subcolumn = subcol;
|
||||
if (subcol >= subcolumns.size()) return;
|
||||
if (!subcolumns[subcol].can_select) return;
|
||||
if (active_subcolumn == subcol) return;
|
||||
RefreshItem(subcolumns[active_subcolumn].selection);
|
||||
RefreshItem(subcolumns[subcol ].selection);
|
||||
active_subcolumn = subcol;
|
||||
}
|
||||
|
||||
void GalleryList::select(size_t item, size_t subcolumn, bool event) {
|
||||
if (item >= itemCount()) return;
|
||||
// select column
|
||||
size_t old_active_subcolumn = active_subcolumn;
|
||||
selectSubColumn(subcolumn);
|
||||
SubColumn& col = subcolumns[active_subcolumn];
|
||||
// filter?
|
||||
bool changes = col.selection != item;
|
||||
onSelect(item, old_active_subcolumn, changes);
|
||||
// select
|
||||
size_t old_sel = col.selection;
|
||||
col.selection = item;
|
||||
changes |= col.selection != old_sel;
|
||||
// ensure visible
|
||||
if (itemStart(col.selection) < visible_start) {
|
||||
scrollTo(itemStart(col.selection));
|
||||
} else if (itemEnd(col.selection) > visibleEnd()) {
|
||||
scrollTo(itemEnd(col.selection) + visible_start - visibleEnd());
|
||||
} else if (col.selection != old_sel) {
|
||||
RefreshItem(old_sel);
|
||||
RefreshItem(col.selection);
|
||||
}
|
||||
// send event
|
||||
if (event && changes) {
|
||||
sendEvent(EVENT_GALLERY_SELECT);
|
||||
}
|
||||
if (item >= itemCount()) return;
|
||||
// select column
|
||||
size_t old_active_subcolumn = active_subcolumn;
|
||||
selectSubColumn(subcolumn);
|
||||
SubColumn& col = subcolumns[active_subcolumn];
|
||||
// filter?
|
||||
bool changes = col.selection != item;
|
||||
onSelect(item, old_active_subcolumn, changes);
|
||||
// select
|
||||
size_t old_sel = col.selection;
|
||||
col.selection = item;
|
||||
changes |= col.selection != old_sel;
|
||||
// ensure visible
|
||||
if (itemStart(col.selection) < visible_start) {
|
||||
scrollTo(itemStart(col.selection));
|
||||
} else if (itemEnd(col.selection) > visibleEnd()) {
|
||||
scrollTo(itemEnd(col.selection) + visible_start - visibleEnd());
|
||||
} else if (col.selection != old_sel) {
|
||||
RefreshItem(old_sel);
|
||||
RefreshItem(col.selection);
|
||||
}
|
||||
// send event
|
||||
if (event && changes) {
|
||||
sendEvent(EVENT_GALLERY_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
void GalleryList::update() {
|
||||
// ensure selection is visible
|
||||
SubColumn col = subcolumns[active_subcolumn];
|
||||
if (col.selection != NO_SELECTION) {
|
||||
if (itemStart(col.selection) < visible_start) {
|
||||
scrollTo(itemStart(col.selection), false);
|
||||
} else if (itemEnd(col.selection) > visibleEnd()) {
|
||||
scrollTo(itemEnd(col.selection) + visible_start - visibleEnd(), false);
|
||||
}
|
||||
}
|
||||
updateScrollbar();
|
||||
Refresh(false);
|
||||
// ensure selection is visible
|
||||
SubColumn col = subcolumns[active_subcolumn];
|
||||
if (col.selection != NO_SELECTION) {
|
||||
if (itemStart(col.selection) < visible_start) {
|
||||
scrollTo(itemStart(col.selection), false);
|
||||
} else if (itemEnd(col.selection) > visibleEnd()) {
|
||||
scrollTo(itemEnd(col.selection) + visible_start - visibleEnd(), false);
|
||||
}
|
||||
}
|
||||
updateScrollbar();
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
size_t GalleryList::findItem(const wxMouseEvent& ev) const {
|
||||
int x = ev.GetX();
|
||||
int y = ev.GetY();
|
||||
int w = item_size.x + SPACING;
|
||||
int h = item_size.y + SPACING;
|
||||
if (direction == wxHORIZONTAL) {
|
||||
x += visible_start;
|
||||
return (size_t)(max(0, x - MARGIN) / w) * column_count
|
||||
+ (size_t)min(max(0, y - MARGIN) / h, (int)column_count-1);
|
||||
} else {
|
||||
y += visible_start;
|
||||
return (size_t)(max(0, y - MARGIN) / h) * column_count
|
||||
+ (size_t)min(max(0, x - MARGIN) / w, (int)column_count-1);
|
||||
}
|
||||
int x = ev.GetX();
|
||||
int y = ev.GetY();
|
||||
int w = item_size.x + SPACING;
|
||||
int h = item_size.y + SPACING;
|
||||
if (direction == wxHORIZONTAL) {
|
||||
x += visible_start;
|
||||
return (size_t)(max(0, x - MARGIN) / w) * column_count
|
||||
+ (size_t)min(max(0, y - MARGIN) / h, (int)column_count-1);
|
||||
} else {
|
||||
y += visible_start;
|
||||
return (size_t)(max(0, y - MARGIN) / h) * column_count
|
||||
+ (size_t)min(max(0, x - MARGIN) / w, (int)column_count-1);
|
||||
}
|
||||
}
|
||||
|
||||
wxPoint GalleryList::itemPos(size_t item) const {
|
||||
if (direction == wxHORIZONTAL) {
|
||||
int x = (int)(item / column_count) * (item_size.x + SPACING);
|
||||
int y = (int)(item % column_count) * (item_size.y + SPACING);
|
||||
return wxPoint(x + MARGIN + BORDER - visible_start, y + MARGIN + BORDER);
|
||||
} else {
|
||||
int x = (int)(item % column_count) * (item_size.x + SPACING);
|
||||
int y = (int)(item / column_count) * (item_size.y + SPACING);
|
||||
return wxPoint(x + MARGIN + BORDER, y + MARGIN + BORDER - visible_start);
|
||||
}
|
||||
if (direction == wxHORIZONTAL) {
|
||||
int x = (int)(item / column_count) * (item_size.x + SPACING);
|
||||
int y = (int)(item % column_count) * (item_size.y + SPACING);
|
||||
return wxPoint(x + MARGIN + BORDER - visible_start, y + MARGIN + BORDER);
|
||||
} else {
|
||||
int x = (int)(item % column_count) * (item_size.x + SPACING);
|
||||
int y = (int)(item / column_count) * (item_size.y + SPACING);
|
||||
return wxPoint(x + MARGIN + BORDER, y + MARGIN + BORDER - visible_start);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Scrolling & sizing
|
||||
|
||||
void GalleryList::scrollTo(int top, bool update_scrollbar) {
|
||||
wxSize cs = GetClientSize();
|
||||
int total_height = itemEnd(itemCount() - 1);
|
||||
top = min(total_height - mainSize(cs), top);
|
||||
top = max(0, top);
|
||||
// scroll
|
||||
if (top == visible_start) return;
|
||||
visible_start = top;
|
||||
if (update_scrollbar) {
|
||||
// scroll bar
|
||||
updateScrollbar();
|
||||
// scroll actual window content
|
||||
Refresh(false);
|
||||
}
|
||||
wxSize cs = GetClientSize();
|
||||
int total_height = itemEnd(itemCount() - 1);
|
||||
top = min(total_height - mainSize(cs), top);
|
||||
top = max(0, top);
|
||||
// scroll
|
||||
if (top == visible_start) return;
|
||||
visible_start = top;
|
||||
if (update_scrollbar) {
|
||||
// scroll bar
|
||||
updateScrollbar();
|
||||
// scroll actual window content
|
||||
Refresh(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GalleryList::updateScrollbar() {
|
||||
scrollTo(visible_start, false);
|
||||
scrollTo(visible_start, false);
|
||||
// how many lines fit on the screen?
|
||||
int screen_height = mainSize(GetClientSize());
|
||||
int total_height = itemEnd(itemCount() - 1);
|
||||
// set the scrollbar parameters to reflect this
|
||||
int total_height = itemEnd(itemCount() - 1);
|
||||
// set the scrollbar parameters to reflect this
|
||||
SetScrollbar(direction, visible_start, screen_height, total_height);
|
||||
}
|
||||
|
||||
void GalleryList::RefreshItem(size_t item) {
|
||||
if (item >= itemCount()) return;
|
||||
RefreshRect(wxRect(itemPos(item),item_size).Inflate(BORDER,BORDER), false);
|
||||
if (item >= itemCount()) return;
|
||||
RefreshRect(wxRect(itemPos(item),item_size).Inflate(BORDER,BORDER), false);
|
||||
}
|
||||
void GalleryList::RefreshSelection() {
|
||||
FOR_EACH(col,subcolumns) RefreshItem(col.selection);
|
||||
FOR_EACH(col,subcolumns) RefreshItem(col.selection);
|
||||
}
|
||||
|
||||
void GalleryList::onScroll(wxScrollWinEvent& ev) {
|
||||
wxEventType type = ev.GetEventType();
|
||||
if (type == wxEVT_SCROLLWIN_TOP) {
|
||||
scrollTo(0);
|
||||
} else if (type == wxEVT_SCROLLWIN_BOTTOM) {
|
||||
scrollTo(INT_MAX);
|
||||
} else if (type == wxEVT_SCROLLWIN_LINEUP) {
|
||||
scrollTo(visible_start - (mainSize(item_size) + SPACING));
|
||||
} else if (type == wxEVT_SCROLLWIN_LINEDOWN) {
|
||||
scrollTo(visible_start + (mainSize(item_size) + SPACING));
|
||||
} else if (type == wxEVT_SCROLLWIN_PAGEUP) {
|
||||
scrollTo(visible_start - visibleEnd() + mainSize(item_size));
|
||||
} else if (type == wxEVT_SCROLLWIN_PAGEDOWN) {
|
||||
scrollTo(visibleEnd() - mainSize(item_size));
|
||||
} else {
|
||||
scrollTo(ev.GetPosition());
|
||||
}
|
||||
wxEventType type = ev.GetEventType();
|
||||
if (type == wxEVT_SCROLLWIN_TOP) {
|
||||
scrollTo(0);
|
||||
} else if (type == wxEVT_SCROLLWIN_BOTTOM) {
|
||||
scrollTo(INT_MAX);
|
||||
} else if (type == wxEVT_SCROLLWIN_LINEUP) {
|
||||
scrollTo(visible_start - (mainSize(item_size) + SPACING));
|
||||
} else if (type == wxEVT_SCROLLWIN_LINEDOWN) {
|
||||
scrollTo(visible_start + (mainSize(item_size) + SPACING));
|
||||
} else if (type == wxEVT_SCROLLWIN_PAGEUP) {
|
||||
scrollTo(visible_start - visibleEnd() + mainSize(item_size));
|
||||
} else if (type == wxEVT_SCROLLWIN_PAGEDOWN) {
|
||||
scrollTo(visibleEnd() - mainSize(item_size));
|
||||
} else {
|
||||
scrollTo(ev.GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
void GalleryList::onSize(wxSizeEvent& ev) {
|
||||
update();
|
||||
ev.Skip();
|
||||
update();
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void GalleryList::onMouseWheel(wxMouseEvent& ev) {
|
||||
scrollTo(visible_start - (mainSize(item_size) + SPACING) * ev.GetWheelRotation() / ev.GetWheelDelta());
|
||||
scrollTo(visible_start - (mainSize(item_size) + SPACING) * ev.GetWheelRotation() / ev.GetWheelDelta());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Events
|
||||
|
||||
void GalleryList::onLeftDown(wxMouseEvent& ev) {
|
||||
size_t item = findItem(ev);
|
||||
if (item < itemCount()) {
|
||||
// find column
|
||||
wxPoint pos = itemPos(item);
|
||||
int x = ev.GetX() - pos.x;
|
||||
int y = ev.GetY() - pos.y;
|
||||
size_t subcolumn = active_subcolumn;
|
||||
for (size_t j = 0 ; j < subcolumns.size() ; ++j) {
|
||||
SubColumn& col = subcolumns[j];
|
||||
if (x >= col.offset.x && y >= col.offset.y && x < col.size.x + col.offset.x && y < col.size.y + col.offset.y) {
|
||||
// clicked on this column
|
||||
subcolumn = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
select(item, subcolumn);
|
||||
}
|
||||
ev.Skip(); // focus
|
||||
size_t item = findItem(ev);
|
||||
if (item < itemCount()) {
|
||||
// find column
|
||||
wxPoint pos = itemPos(item);
|
||||
int x = ev.GetX() - pos.x;
|
||||
int y = ev.GetY() - pos.y;
|
||||
size_t subcolumn = active_subcolumn;
|
||||
for (size_t j = 0 ; j < subcolumns.size() ; ++j) {
|
||||
SubColumn& col = subcolumns[j];
|
||||
if (x >= col.offset.x && y >= col.offset.y && x < col.size.x + col.offset.x && y < col.size.y + col.offset.y) {
|
||||
// clicked on this column
|
||||
subcolumn = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
select(item, subcolumn);
|
||||
}
|
||||
ev.Skip(); // focus
|
||||
}
|
||||
|
||||
void GalleryList::onLeftDClick(wxMouseEvent& ev) {
|
||||
sendEvent(EVENT_GALLERY_ACTIVATE);
|
||||
sendEvent(EVENT_GALLERY_ACTIVATE);
|
||||
}
|
||||
|
||||
void GalleryList::onChar(wxKeyEvent& ev) {
|
||||
SubColumn& col = subcolumns[active_subcolumn];
|
||||
switch (ev.GetKeyCode()) {
|
||||
case WXK_LEFT:
|
||||
if (direction == wxHORIZONTAL) {
|
||||
select(col.selection - column_count);
|
||||
} else if (column_count > 1) {
|
||||
select(col.selection - 1);
|
||||
} else {
|
||||
selectSubColumn(active_subcolumn - 1);
|
||||
}
|
||||
break;
|
||||
case WXK_RIGHT:
|
||||
if (direction == wxHORIZONTAL) {
|
||||
select(col.selection + column_count);
|
||||
} else if (column_count > 1) {
|
||||
select(col.selection + 1);
|
||||
} else {
|
||||
selectSubColumn(active_subcolumn + 1);
|
||||
}
|
||||
break;
|
||||
case WXK_UP:
|
||||
if (direction == wxVERTICAL) {
|
||||
select(col.selection - column_count);
|
||||
} else if (column_count > 1) {
|
||||
select(col.selection - 1);
|
||||
} else {
|
||||
selectSubColumn(active_subcolumn - 1);
|
||||
}
|
||||
break;
|
||||
case WXK_DOWN:
|
||||
if (direction == wxVERTICAL) {
|
||||
select(col.selection + column_count);
|
||||
} else if (column_count > 1) {
|
||||
select(col.selection + 1);
|
||||
} else {
|
||||
selectSubColumn(active_subcolumn + 1);
|
||||
}
|
||||
break;
|
||||
case WXK_TAB: {
|
||||
// send a navigation event to our parent, to select another control
|
||||
// we need this because tabs of wxWANTS_CHARS
|
||||
wxNavigationKeyEvent nev;
|
||||
nev.SetDirection(!ev.ShiftDown());
|
||||
GetParent()->HandleWindowEvent(nev);
|
||||
} break;
|
||||
case WXK_RETURN: {
|
||||
// same thing: press dialog box default button
|
||||
wxTopLevelWindow* tlw = wxDynamicCast(GetParent(), wxTopLevelWindow);
|
||||
wxButton* btn = tlw ? wxDynamicCast(tlw->GetDefaultItem(), wxButton) : nullptr;
|
||||
if ( btn && btn->IsEnabled() ) {
|
||||
// if we do have a default button, do press it
|
||||
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, btn->GetId());
|
||||
btn->HandleWindowEvent(evt);
|
||||
}
|
||||
}break;
|
||||
|
||||
}
|
||||
SubColumn& col = subcolumns[active_subcolumn];
|
||||
switch (ev.GetKeyCode()) {
|
||||
case WXK_LEFT:
|
||||
if (direction == wxHORIZONTAL) {
|
||||
select(col.selection - column_count);
|
||||
} else if (column_count > 1) {
|
||||
select(col.selection - 1);
|
||||
} else {
|
||||
selectSubColumn(active_subcolumn - 1);
|
||||
}
|
||||
break;
|
||||
case WXK_RIGHT:
|
||||
if (direction == wxHORIZONTAL) {
|
||||
select(col.selection + column_count);
|
||||
} else if (column_count > 1) {
|
||||
select(col.selection + 1);
|
||||
} else {
|
||||
selectSubColumn(active_subcolumn + 1);
|
||||
}
|
||||
break;
|
||||
case WXK_UP:
|
||||
if (direction == wxVERTICAL) {
|
||||
select(col.selection - column_count);
|
||||
} else if (column_count > 1) {
|
||||
select(col.selection - 1);
|
||||
} else {
|
||||
selectSubColumn(active_subcolumn - 1);
|
||||
}
|
||||
break;
|
||||
case WXK_DOWN:
|
||||
if (direction == wxVERTICAL) {
|
||||
select(col.selection + column_count);
|
||||
} else if (column_count > 1) {
|
||||
select(col.selection + 1);
|
||||
} else {
|
||||
selectSubColumn(active_subcolumn + 1);
|
||||
}
|
||||
break;
|
||||
case WXK_TAB: {
|
||||
// send a navigation event to our parent, to select another control
|
||||
// we need this because tabs of wxWANTS_CHARS
|
||||
wxNavigationKeyEvent nev;
|
||||
nev.SetDirection(!ev.ShiftDown());
|
||||
GetParent()->HandleWindowEvent(nev);
|
||||
} break;
|
||||
case WXK_RETURN: {
|
||||
// same thing: press dialog box default button
|
||||
wxTopLevelWindow* tlw = wxDynamicCast(GetParent(), wxTopLevelWindow);
|
||||
wxButton* btn = tlw ? wxDynamicCast(tlw->GetDefaultItem(), wxButton) : nullptr;
|
||||
if ( btn && btn->IsEnabled() ) {
|
||||
// if we do have a default button, do press it
|
||||
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, btn->GetId());
|
||||
btn->HandleWindowEvent(evt);
|
||||
}
|
||||
}break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
wxSize GalleryList::DoGetBestSize() const {
|
||||
wxSize ws = GetSize(), cs = GetClientSize();
|
||||
const int w = item_size.x + 2*MARGIN + 2*BORDER;
|
||||
const int h = item_size.y + 2*MARGIN + 2*BORDER;
|
||||
if (direction == wxHORIZONTAL) {
|
||||
return wxSize(w, h * (int)column_count) + ws - cs;
|
||||
} else {
|
||||
return wxSize(w * (int)column_count, h) + ws - cs;
|
||||
}
|
||||
wxSize ws = GetSize(), cs = GetClientSize();
|
||||
const int w = item_size.x + 2*MARGIN + 2*BORDER;
|
||||
const int h = item_size.y + 2*MARGIN + 2*BORDER;
|
||||
if (direction == wxHORIZONTAL) {
|
||||
return wxSize(w, h * (int)column_count) + ws - cs;
|
||||
} else {
|
||||
return wxSize(w * (int)column_count, h) + ws - cs;
|
||||
}
|
||||
}
|
||||
|
||||
void GalleryList::onPaint(wxPaintEvent&) {
|
||||
wxBufferedPaintDC dc(this);
|
||||
try {
|
||||
OnDraw(dc);
|
||||
} CATCH_ALL_ERRORS(false); // don't show message boxes in onPaint!
|
||||
wxBufferedPaintDC dc(this);
|
||||
try {
|
||||
OnDraw(dc);
|
||||
} CATCH_ALL_ERRORS(false); // don't show message boxes in onPaint!
|
||||
}
|
||||
void GalleryList::OnDraw(DC& dc) {
|
||||
wxSize cs = GetClientSize();
|
||||
size_t start, end; // items to draw
|
||||
// number of visble items
|
||||
start = (size_t) max(0, visible_start / (mainSize(item_size) + SPACING)) * column_count;
|
||||
end = (size_t) max(0, visibleEnd() / (mainSize(item_size) + SPACING) + 1) * column_count;
|
||||
end = min(end, itemCount());
|
||||
// clear background
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
dc.DrawRectangle(0, 0, cs.x, cs.y);
|
||||
// draw all visible items
|
||||
Color unselected = lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), 0.1);
|
||||
Color background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
bool has_focus = always_focused || FindFocus() == this;
|
||||
for (size_t i = start ; i < end ; ++i) {
|
||||
wxPoint pos = itemPos(i);
|
||||
// draw selection rectangle
|
||||
for (size_t j = 0 ; j < subcolumns.size() ; ++j) {
|
||||
const SubColumn& col = subcolumns[j];
|
||||
bool selected = i == col.selection;
|
||||
bool focused = has_focus && j == active_subcolumn;
|
||||
wxRect rect(pos.x + col.offset.x - BORDER, pos.y + col.offset.y - BORDER,
|
||||
col.size.x + 2*BORDER, col.size.y + 2*BORDER);
|
||||
#if 0
|
||||
if (selected) {
|
||||
draw_selection_rectangle(this,dc,rect, selected,focused);
|
||||
}
|
||||
#else
|
||||
Color c = selected ? ( focused
|
||||
? wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)
|
||||
: lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), subcolumnActivity(j))
|
||||
)
|
||||
: unselected;
|
||||
dc.SetPen(c);
|
||||
dc.SetBrush(lerp(background, c, 0.3));
|
||||
dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height);
|
||||
#endif
|
||||
}
|
||||
// draw item
|
||||
drawItem(dc, pos.x, pos.y, i);
|
||||
}
|
||||
wxSize cs = GetClientSize();
|
||||
size_t start, end; // items to draw
|
||||
// number of visble items
|
||||
start = (size_t) max(0, visible_start / (mainSize(item_size) + SPACING)) * column_count;
|
||||
end = (size_t) max(0, visibleEnd() / (mainSize(item_size) + SPACING) + 1) * column_count;
|
||||
end = min(end, itemCount());
|
||||
// clear background
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
dc.DrawRectangle(0, 0, cs.x, cs.y);
|
||||
// draw all visible items
|
||||
Color unselected = lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), 0.1);
|
||||
Color background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
bool has_focus = always_focused || FindFocus() == this;
|
||||
for (size_t i = start ; i < end ; ++i) {
|
||||
wxPoint pos = itemPos(i);
|
||||
// draw selection rectangle
|
||||
for (size_t j = 0 ; j < subcolumns.size() ; ++j) {
|
||||
const SubColumn& col = subcolumns[j];
|
||||
bool selected = i == col.selection;
|
||||
bool focused = has_focus && j == active_subcolumn;
|
||||
wxRect rect(pos.x + col.offset.x - BORDER, pos.y + col.offset.y - BORDER,
|
||||
col.size.x + 2*BORDER, col.size.y + 2*BORDER);
|
||||
#if 0
|
||||
if (selected) {
|
||||
draw_selection_rectangle(this,dc,rect, selected,focused);
|
||||
}
|
||||
#else
|
||||
Color c = selected ? ( focused
|
||||
? wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)
|
||||
: lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), subcolumnActivity(j))
|
||||
)
|
||||
: unselected;
|
||||
dc.SetPen(c);
|
||||
dc.SetBrush(lerp(background, c, 0.3));
|
||||
dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height);
|
||||
#endif
|
||||
}
|
||||
// draw item
|
||||
drawItem(dc, pos.x, pos.y, i);
|
||||
}
|
||||
}
|
||||
|
||||
void GalleryList::onFocus(wxFocusEvent&) {
|
||||
if (!always_focused) Refresh(false);
|
||||
if (!always_focused) Refresh(false);
|
||||
}
|
||||
|
||||
void GalleryList::sendEvent(WXTYPE type) {
|
||||
wxCommandEvent ev(type, GetId());
|
||||
ProcessEvent(ev);
|
||||
wxCommandEvent ev(type, GetId());
|
||||
ProcessEvent(ev);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Event table
|
||||
|
||||
BEGIN_EVENT_TABLE(GalleryList, wxPanel)
|
||||
EVT_LEFT_DOWN (GalleryList::onLeftDown)
|
||||
EVT_LEFT_DCLICK (GalleryList::onLeftDClick)
|
||||
EVT_MOUSEWHEEL (GalleryList::onMouseWheel)
|
||||
EVT_CHAR (GalleryList::onChar)
|
||||
EVT_SET_FOCUS (GalleryList::onFocus)
|
||||
EVT_KILL_FOCUS (GalleryList::onFocus)
|
||||
EVT_PAINT (GalleryList::onPaint)
|
||||
EVT_SIZE (GalleryList::onSize)
|
||||
EVT_SCROLLWIN (GalleryList::onScroll)
|
||||
EVT_ERASE_BACKGROUND(GalleryList::onEraseBackground)
|
||||
EVT_LEFT_DOWN (GalleryList::onLeftDown)
|
||||
EVT_LEFT_DCLICK (GalleryList::onLeftDClick)
|
||||
EVT_MOUSEWHEEL (GalleryList::onMouseWheel)
|
||||
EVT_CHAR (GalleryList::onChar)
|
||||
EVT_SET_FOCUS (GalleryList::onFocus)
|
||||
EVT_KILL_FOCUS (GalleryList::onFocus)
|
||||
EVT_PAINT (GalleryList::onPaint)
|
||||
EVT_SIZE (GalleryList::onSize)
|
||||
EVT_SCROLLWIN (GalleryList::onScroll)
|
||||
EVT_ERASE_BACKGROUND(GalleryList::onEraseBackground)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
+101
-101
@@ -29,113 +29,113 @@ DECLARE_LOCAL_EVENT_TYPE(EVENT_GALLERY_ACTIVATE, <not used>)
|
||||
*/
|
||||
class GalleryList : public wxPanel {
|
||||
public:
|
||||
GalleryList(Window* parent, int id, int direction = wxHORIZONTAL, bool always_focused = true);
|
||||
|
||||
/// Select the given column
|
||||
void selectSubColumn(size_t subcol);
|
||||
/// Select the given item in the given column (or in the active column)
|
||||
void select(size_t item, size_t subcol = NO_SELECTION, bool event = true);
|
||||
/// Is there an item selected?
|
||||
inline bool hasSelection(size_t subcol = 0) const { return subcolumns[subcol].selection < itemCount(); }
|
||||
/// Get the id of the selected item, throws if there is no selection
|
||||
inline size_t getSelectionId(size_t subcol = 0) const {
|
||||
if (!hasSelection(subcol)) throw Error(_("No selection"));
|
||||
return subcolumns[subcol].selection;
|
||||
}
|
||||
/// Is the given item selected?
|
||||
inline bool isSelected(size_t item, size_t subcol = 0) const {
|
||||
return subcol < subcolumns.size() && subcolumns[subcol].selection == item;
|
||||
}
|
||||
|
||||
/// Redraw only the selected items
|
||||
void RefreshSelection();
|
||||
|
||||
GalleryList(Window* parent, int id, int direction = wxHORIZONTAL, bool always_focused = true);
|
||||
|
||||
/// Select the given column
|
||||
void selectSubColumn(size_t subcol);
|
||||
/// Select the given item in the given column (or in the active column)
|
||||
void select(size_t item, size_t subcol = NO_SELECTION, bool event = true);
|
||||
/// Is there an item selected?
|
||||
inline bool hasSelection(size_t subcol = 0) const { return subcolumns[subcol].selection < itemCount(); }
|
||||
/// Get the id of the selected item, throws if there is no selection
|
||||
inline size_t getSelectionId(size_t subcol = 0) const {
|
||||
if (!hasSelection(subcol)) throw Error(_("No selection"));
|
||||
return subcolumns[subcol].selection;
|
||||
}
|
||||
/// Is the given item selected?
|
||||
inline bool isSelected(size_t item, size_t subcol = 0) const {
|
||||
return subcol < subcolumns.size() && subcolumns[subcol].selection == item;
|
||||
}
|
||||
|
||||
/// Redraw only the selected items
|
||||
void RefreshSelection();
|
||||
|
||||
protected:
|
||||
static const size_t NO_SELECTION = (size_t)-1;
|
||||
size_t active_subcolumn; ///< The active subcolumn
|
||||
wxSize item_size; ///< The total size of a single item (over all columns)
|
||||
int direction; ///< Direction of the list, can be wxHORIZONTAL or wxVERTICAL
|
||||
size_t column_count; ///< Number of major level columns (if vertical) or rows (if horizontal)
|
||||
bool always_focused; ///< Always draw as if focused
|
||||
|
||||
/// Redraw the list after changing the selection or the number of items
|
||||
void update();
|
||||
|
||||
/// Return how many items there are in the list
|
||||
virtual size_t itemCount() const = 0;
|
||||
/// Draw an item
|
||||
virtual void drawItem(DC& dc, int x, int y, size_t item) = 0;
|
||||
/// How 'salient' should the selection in the given subcolumn be?
|
||||
virtual double subcolumnActivity(size_t col) const { return 0.7; }
|
||||
|
||||
/// Filter calls to select, or apply some extra operaions
|
||||
virtual void onSelect(size_t item, size_t col, bool& changes) {}
|
||||
|
||||
/// Return the desired size of control
|
||||
virtual wxSize DoGetBestSize() const;
|
||||
|
||||
/// Information on the subcolumns. These are columns inside items
|
||||
struct SubColumn {
|
||||
wxPoint offset;
|
||||
wxSize size;
|
||||
bool can_select;
|
||||
size_t selection;
|
||||
};
|
||||
vector<SubColumn> subcolumns;
|
||||
|
||||
static const size_t NO_SELECTION = (size_t)-1;
|
||||
size_t active_subcolumn; ///< The active subcolumn
|
||||
wxSize item_size; ///< The total size of a single item (over all columns)
|
||||
int direction; ///< Direction of the list, can be wxHORIZONTAL or wxVERTICAL
|
||||
size_t column_count; ///< Number of major level columns (if vertical) or rows (if horizontal)
|
||||
bool always_focused; ///< Always draw as if focused
|
||||
|
||||
/// Redraw the list after changing the selection or the number of items
|
||||
void update();
|
||||
|
||||
/// Return how many items there are in the list
|
||||
virtual size_t itemCount() const = 0;
|
||||
/// Draw an item
|
||||
virtual void drawItem(DC& dc, int x, int y, size_t item) = 0;
|
||||
/// How 'salient' should the selection in the given subcolumn be?
|
||||
virtual double subcolumnActivity(size_t col) const { return 0.7; }
|
||||
|
||||
/// Filter calls to select, or apply some extra operaions
|
||||
virtual void onSelect(size_t item, size_t col, bool& changes) {}
|
||||
|
||||
/// Return the desired size of control
|
||||
virtual wxSize DoGetBestSize() const;
|
||||
|
||||
/// Information on the subcolumns. These are columns inside items
|
||||
struct SubColumn {
|
||||
wxPoint offset;
|
||||
wxSize size;
|
||||
bool can_select;
|
||||
size_t selection;
|
||||
};
|
||||
vector<SubColumn> subcolumns;
|
||||
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onLeftDown (wxMouseEvent& ev);
|
||||
void onLeftDClick(wxMouseEvent& ev);
|
||||
void onMouseWheel(wxMouseEvent& ev);
|
||||
void onChar(wxKeyEvent& ev);
|
||||
void onFocus(wxFocusEvent&);
|
||||
void onPaint(wxPaintEvent&);
|
||||
void onEraseBackground(wxEraseEvent&) {}
|
||||
void onSize(wxSizeEvent&);
|
||||
void onScroll(wxScrollWinEvent&);
|
||||
void OnDraw(DC& dc);
|
||||
|
||||
/// Find the item corresponding to the given location
|
||||
size_t findItem(const wxMouseEvent&) const;
|
||||
/// Find the coordinates of an item
|
||||
wxPoint itemPos(size_t item) const;
|
||||
|
||||
/// Scroll to the given position (note: 'top' can also mean 'left')
|
||||
void scrollTo(int top, bool update_scrollbar = true);
|
||||
/// Update the scrollbar(s)
|
||||
void updateScrollbar();
|
||||
/// Redraw just a single item
|
||||
void RefreshItem(size_t item);
|
||||
|
||||
/// First visible pixel position
|
||||
int visible_start;
|
||||
/// First no-longer-visible pixel position
|
||||
inline int visibleEnd() const {
|
||||
return visible_start + mainSize(GetClientSize());
|
||||
}
|
||||
/// Pixel position of an item
|
||||
inline int itemStart(size_t item) const {
|
||||
return (int)(item / column_count) * (mainSize(item_size) + SPACING);
|
||||
}
|
||||
inline int itemEnd(size_t item) const {
|
||||
return (int)(item / column_count + 1) * (mainSize(item_size) + SPACING) + MARGIN;
|
||||
}
|
||||
/// Main component of a size (i.e. in the direction of this list)
|
||||
inline int mainSize(wxSize s) const {
|
||||
return direction == wxHORIZONTAL ? s.x : s.y;
|
||||
}
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onLeftDown (wxMouseEvent& ev);
|
||||
void onLeftDClick(wxMouseEvent& ev);
|
||||
void onMouseWheel(wxMouseEvent& ev);
|
||||
void onChar(wxKeyEvent& ev);
|
||||
void onFocus(wxFocusEvent&);
|
||||
void onPaint(wxPaintEvent&);
|
||||
void onEraseBackground(wxEraseEvent&) {}
|
||||
void onSize(wxSizeEvent&);
|
||||
void onScroll(wxScrollWinEvent&);
|
||||
void OnDraw(DC& dc);
|
||||
|
||||
/// Find the item corresponding to the given location
|
||||
size_t findItem(const wxMouseEvent&) const;
|
||||
/// Find the coordinates of an item
|
||||
wxPoint itemPos(size_t item) const;
|
||||
|
||||
/// Scroll to the given position (note: 'top' can also mean 'left')
|
||||
void scrollTo(int top, bool update_scrollbar = true);
|
||||
/// Update the scrollbar(s)
|
||||
void updateScrollbar();
|
||||
/// Redraw just a single item
|
||||
void RefreshItem(size_t item);
|
||||
|
||||
/// First visible pixel position
|
||||
int visible_start;
|
||||
/// First no-longer-visible pixel position
|
||||
inline int visibleEnd() const {
|
||||
return visible_start + mainSize(GetClientSize());
|
||||
}
|
||||
/// Pixel position of an item
|
||||
inline int itemStart(size_t item) const {
|
||||
return (int)(item / column_count) * (mainSize(item_size) + SPACING);
|
||||
}
|
||||
inline int itemEnd(size_t item) const {
|
||||
return (int)(item / column_count + 1) * (mainSize(item_size) + SPACING) + MARGIN;
|
||||
}
|
||||
/// Main component of a size (i.e. in the direction of this list)
|
||||
inline int mainSize(wxSize s) const {
|
||||
return direction == wxHORIZONTAL ? s.x : s.y;
|
||||
}
|
||||
|
||||
public:
|
||||
typedef SubColumn SubColumn_for_typeof;
|
||||
typedef SubColumn SubColumn_for_typeof;
|
||||
protected:
|
||||
/// Send an event
|
||||
void sendEvent(WXTYPE type);
|
||||
/// Send an event
|
||||
void sendEvent(WXTYPE type);
|
||||
|
||||
static const int MARGIN = 1; // margin between items (excluding border)
|
||||
static const int BORDER = 0; // border around items
|
||||
static const int SPACING = MARGIN + 2*BORDER; // distance between items
|
||||
static const int MARGIN = 1; // margin between items (excluding border)
|
||||
static const int BORDER = 0; // border around items
|
||||
static const int SPACING = MARGIN + 2*BORDER; // distance between items
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+981
-981
File diff suppressed because it is too large
Load Diff
+211
-211
@@ -32,304 +32,304 @@ DECLARE_LOCAL_EVENT_TYPE(EVENT_GRAPH_SELECT, <not used>)
|
||||
/** A group is rendered as a single bar or pie slice */
|
||||
class GraphGroup : public IntrusivePtrBase<GraphGroup> {
|
||||
public:
|
||||
GraphGroup(const String& name, UInt size, const Color& color = *wxBLACK)
|
||||
: name(name), color(color), size(size)
|
||||
{}
|
||||
|
||||
String name; ///< Name of this position
|
||||
Color color; ///< Associated color
|
||||
UInt size; ///< Number of elements in this group
|
||||
GraphGroup(const String& name, UInt size, const Color& color = *wxBLACK)
|
||||
: name(name), color(color), size(size)
|
||||
{}
|
||||
|
||||
String name; ///< Name of this position
|
||||
Color color; ///< Associated color
|
||||
UInt size; ///< Number of elements in this group
|
||||
};
|
||||
|
||||
/// Automatic coloring mode
|
||||
enum AutoColor
|
||||
{ AUTO_COLOR_NO
|
||||
, AUTO_COLOR_EVEN
|
||||
, AUTO_COLOR_WEIGHTED
|
||||
{ AUTO_COLOR_NO
|
||||
, AUTO_COLOR_EVEN
|
||||
, AUTO_COLOR_WEIGHTED
|
||||
};
|
||||
|
||||
/// An axis in a graph, consists of a list of groups
|
||||
/** The sum of groups.sum = sum of all elements in the data */
|
||||
class GraphAxis : public IntrusivePtrBase<GraphAxis> {
|
||||
public:
|
||||
GraphAxis(const String& name, AutoColor auto_color = AUTO_COLOR_EVEN, bool numeric = false, double bin_size = 0, const map<String,Color>* colors = nullptr, const vector<String>* order = nullptr)
|
||||
: name(name)
|
||||
, auto_color(auto_color)
|
||||
, numeric(numeric), bin_size(bin_size)
|
||||
, max(0)
|
||||
, total(0)
|
||||
, mean_value(0), max_value(-numeric_limits<double>::infinity())
|
||||
, colors(colors)
|
||||
, order(order)
|
||||
{}
|
||||
|
||||
String name; ///< Name/label of this axis
|
||||
AutoColor auto_color; ///< Automatically assign colors to the groups on this axis
|
||||
vector<GraphGroup> groups; ///< Groups along this axis
|
||||
bool numeric; ///< Numeric axis?
|
||||
double bin_size; ///< Group numeric values into bins of this size
|
||||
UInt max; ///< Maximum size of the groups
|
||||
UInt total; ///< Sum of the size of all groups
|
||||
double mean_value; ///< Mean value, only for numeric axes
|
||||
double max_value; ///< Maximal value, only for numeric axes
|
||||
const map<String,Color>* colors; ///< Colors for each choice (optional)
|
||||
const vector<String>* order; ///< Order of the items (optional)
|
||||
|
||||
/// Add a graph group
|
||||
void addGroup(const String& name, UInt size);
|
||||
GraphAxis(const String& name, AutoColor auto_color = AUTO_COLOR_EVEN, bool numeric = false, double bin_size = 0, const map<String,Color>* colors = nullptr, const vector<String>* order = nullptr)
|
||||
: name(name)
|
||||
, auto_color(auto_color)
|
||||
, numeric(numeric), bin_size(bin_size)
|
||||
, max(0)
|
||||
, total(0)
|
||||
, mean_value(0), max_value(-numeric_limits<double>::infinity())
|
||||
, colors(colors)
|
||||
, order(order)
|
||||
{}
|
||||
|
||||
String name; ///< Name/label of this axis
|
||||
AutoColor auto_color; ///< Automatically assign colors to the groups on this axis
|
||||
vector<GraphGroup> groups; ///< Groups along this axis
|
||||
bool numeric; ///< Numeric axis?
|
||||
double bin_size; ///< Group numeric values into bins of this size
|
||||
UInt max; ///< Maximum size of the groups
|
||||
UInt total; ///< Sum of the size of all groups
|
||||
double mean_value; ///< Mean value, only for numeric axes
|
||||
double max_value; ///< Maximal value, only for numeric axes
|
||||
const map<String,Color>* colors; ///< Colors for each choice (optional)
|
||||
const vector<String>* order; ///< Order of the items (optional)
|
||||
|
||||
/// Add a graph group
|
||||
void addGroup(const String& name, UInt size);
|
||||
};
|
||||
|
||||
/// A single data point of a graph
|
||||
class GraphElement : public IntrusivePtrBase<GraphElement> {
|
||||
public:
|
||||
GraphElement(size_t original_index) : original_index(original_index) {}
|
||||
|
||||
size_t original_index; ///< Corresponding index in the original input
|
||||
vector<String> values; ///< Group name for each axis
|
||||
GraphElement(size_t original_index) : original_index(original_index) {}
|
||||
|
||||
size_t original_index; ///< Corresponding index in the original input
|
||||
vector<String> values; ///< Group name for each axis
|
||||
};
|
||||
|
||||
/// Data to be displayed in a graph, not processed yet
|
||||
class GraphDataPre {
|
||||
public:
|
||||
vector<GraphAxisP> axes;
|
||||
vector<GraphElementP> elements;
|
||||
/// Split compound elements, "a,b,c" -> "a" and "b" and "c"
|
||||
void splitList(size_t axis);
|
||||
vector<GraphAxisP> axes;
|
||||
vector<GraphElementP> elements;
|
||||
/// Split compound elements, "a,b,c" -> "a" and "b" and "c"
|
||||
void splitList(size_t axis);
|
||||
};
|
||||
|
||||
/// A single data point of a graph
|
||||
struct GraphDataElement {
|
||||
size_t original_index;
|
||||
int group_nrs[1]; ///< Group number for each axis
|
||||
size_t original_index;
|
||||
int group_nrs[1]; ///< Group number for each axis
|
||||
};
|
||||
|
||||
/// Data to be displayed in a graph
|
||||
class GraphData : public IntrusivePtrBase<GraphData> {
|
||||
public:
|
||||
GraphData(const GraphDataPre&);
|
||||
~GraphData();
|
||||
|
||||
vector<GraphAxisP> axes; ///< The axes in the data
|
||||
vector<GraphDataElement*> values; ///< All elements, with the group number for each axis, or -1
|
||||
UInt size; ///< Total number of elements
|
||||
|
||||
/// Create a cross table for two axes
|
||||
void crossAxis(size_t axis1, size_t axis2, vector<UInt>& out) const;
|
||||
/// Create a cross table for three axes
|
||||
void crossAxis(size_t axis1, size_t axis2, size_t axis3, vector<UInt>& out) const;
|
||||
/// Count the number of elements with the given values, -1 is a wildcard
|
||||
UInt count(const vector<int>& match) const;
|
||||
/// Get the original_indices of elements matching the selection
|
||||
void indices(const vector<int>& match, vector<size_t>& out) const;
|
||||
GraphData(const GraphDataPre&);
|
||||
~GraphData();
|
||||
|
||||
vector<GraphAxisP> axes; ///< The axes in the data
|
||||
vector<GraphDataElement*> values; ///< All elements, with the group number for each axis, or -1
|
||||
UInt size; ///< Total number of elements
|
||||
|
||||
/// Create a cross table for two axes
|
||||
void crossAxis(size_t axis1, size_t axis2, vector<UInt>& out) const;
|
||||
/// Create a cross table for three axes
|
||||
void crossAxis(size_t axis1, size_t axis2, size_t axis3, vector<UInt>& out) const;
|
||||
/// Count the number of elements with the given values, -1 is a wildcard
|
||||
UInt count(const vector<int>& match) const;
|
||||
/// Get the original_indices of elements matching the selection
|
||||
void indices(const vector<int>& match, vector<size_t>& out) const;
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Graph
|
||||
|
||||
enum DrawLayer
|
||||
{ LAYER_BOTTOM = 0
|
||||
, LAYER_SELECTION = 0
|
||||
, LAYER_AXES
|
||||
, LAYER_VALUES
|
||||
, LAYER_COUNT
|
||||
{ LAYER_BOTTOM = 0
|
||||
, LAYER_SELECTION = 0
|
||||
, LAYER_AXES
|
||||
, LAYER_VALUES
|
||||
, LAYER_COUNT
|
||||
};
|
||||
|
||||
/// A type of graph
|
||||
/** It is rendered into a sub-rectangle of the screen */
|
||||
class Graph : public IntrusivePtrVirtualBase {
|
||||
public:
|
||||
/// Determine the size of this graph viewer, return -1 if the viewer stretches
|
||||
virtual RealSize determineSize(RotatedDC& dc) const { return RealSize(-1,-1); }
|
||||
/// Draw this graph, filling the internalRect() of the dc.
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const = 0;
|
||||
/// Find the item at the given position, the rectangle gives the screen size
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const { return false; }
|
||||
/// Change the data
|
||||
virtual void setData(const GraphDataP& d) { data = d; }
|
||||
/// Get the data
|
||||
inline const GraphDataP& getData() const { return data; }
|
||||
|
||||
/// Determine the size of this graph viewer, return -1 if the viewer stretches
|
||||
virtual RealSize determineSize(RotatedDC& dc) const { return RealSize(-1,-1); }
|
||||
/// Draw this graph, filling the internalRect() of the dc.
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const = 0;
|
||||
/// Find the item at the given position, the rectangle gives the screen size
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const { return false; }
|
||||
/// Change the data
|
||||
virtual void setData(const GraphDataP& d) { data = d; }
|
||||
/// Get the data
|
||||
inline const GraphDataP& getData() const { return data; }
|
||||
|
||||
protected:
|
||||
/// Data of the graph
|
||||
GraphDataP data;
|
||||
/// Data of the graph
|
||||
GraphDataP data;
|
||||
};
|
||||
|
||||
/// Base class for 1 dimensional graph components
|
||||
class Graph1D : public Graph {
|
||||
public:
|
||||
inline Graph1D(size_t axis) : axis(axis) {}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
inline Graph1D(size_t axis) : axis(axis) {}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
protected:
|
||||
size_t axis;
|
||||
/// Find an item, return the position along the axis, or -1 if not found
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const { return -1; }
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const = 0;
|
||||
inline GraphAxis& axis_data() const { return *data->axes.at(axis); }
|
||||
size_t axis;
|
||||
/// Find an item, return the position along the axis, or -1 if not found
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const { return -1; }
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const = 0;
|
||||
inline GraphAxis& axis_data() const { return *data->axes.at(axis); }
|
||||
};
|
||||
|
||||
/// Base class for 2 dimensional graph components
|
||||
class Graph2D : public Graph {
|
||||
public:
|
||||
inline Graph2D(size_t axis1, size_t axis2) : axis1(axis1), axis2(axis2) {}
|
||||
virtual void setData(const GraphDataP& d);
|
||||
inline Graph2D(size_t axis1, size_t axis2) : axis1(axis1), axis2(axis2) {}
|
||||
virtual void setData(const GraphDataP& d);
|
||||
protected:
|
||||
size_t axis1, axis2;
|
||||
vector<UInt> values; // axis1.size * axis2.size array
|
||||
inline GraphAxis& axis1_data() const { return *data->axes.at(axis1); }
|
||||
inline GraphAxis& axis2_data() const { return *data->axes.at(axis2); }
|
||||
size_t axis1, axis2;
|
||||
vector<UInt> values; // axis1.size * axis2.size array
|
||||
inline GraphAxis& axis1_data() const { return *data->axes.at(axis1); }
|
||||
inline GraphAxis& axis2_data() const { return *data->axes.at(axis2); }
|
||||
};
|
||||
|
||||
/// A bar graph
|
||||
class BarGraph : public Graph1D {
|
||||
public:
|
||||
inline BarGraph(size_t axis) : Graph1D(axis) {}
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const;
|
||||
inline BarGraph(size_t axis) : Graph1D(axis) {}
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const;
|
||||
};
|
||||
|
||||
// A bar graph with stacked bars
|
||||
class BarGraph2D : public Graph2D {
|
||||
public:
|
||||
inline BarGraph2D(size_t axis_h, size_t axis_v) : Graph2D(axis_h, axis_v) {}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
inline BarGraph2D(size_t axis_h, size_t axis_v) : Graph2D(axis_h, axis_v) {}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
};
|
||||
|
||||
/// A pie graph
|
||||
class PieGraph : public Graph1D {
|
||||
public:
|
||||
inline PieGraph(size_t axis) : Graph1D(axis) {}
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const;
|
||||
inline PieGraph(size_t axis) : Graph1D(axis) {}
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const;
|
||||
};
|
||||
|
||||
/// A scatter plot
|
||||
class ScatterGraph : public Graph2D {
|
||||
public:
|
||||
inline ScatterGraph(size_t axis1, size_t axis2) : Graph2D(axis1, axis2) {}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
inline ScatterGraph(size_t axis1, size_t axis2) : Graph2D(axis1, axis2) {}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
protected:
|
||||
UInt max_value;
|
||||
double max_value_x, max_value_y; ///< highest sum of two adjacent scaled values (radii)
|
||||
static double scale(double x); ///< nonlinear scaling
|
||||
UInt max_value;
|
||||
double max_value_x, max_value_y; ///< highest sum of two adjacent scaled values (radii)
|
||||
static double scale(double x); ///< nonlinear scaling
|
||||
};
|
||||
|
||||
/// A scatter plot with an extra dimension
|
||||
class ScatterGraphPlus : public ScatterGraph {
|
||||
public:
|
||||
inline ScatterGraphPlus(size_t axis1, size_t axis2, size_t axis3) : ScatterGraph(axis1, axis2), axis3(axis3) {}
|
||||
virtual void setData(const GraphDataP& d);
|
||||
inline ScatterGraphPlus(size_t axis1, size_t axis2, size_t axis3) : ScatterGraph(axis1, axis2), axis3(axis3) {}
|
||||
virtual void setData(const GraphDataP& d);
|
||||
protected:
|
||||
size_t axis3;
|
||||
vector<UInt> values3D; // axis1.size * axis2.size * axis3.size array
|
||||
inline GraphAxis& axis3_data() const { return *data->axes.at(axis3); }
|
||||
size_t axis3;
|
||||
vector<UInt> values3D; // axis1.size * axis2.size * axis3.size array
|
||||
inline GraphAxis& axis3_data() const { return *data->axes.at(axis3); }
|
||||
};
|
||||
|
||||
/// A scatter plot with a pie graph for the third dimension
|
||||
class ScatterPieGraph : public ScatterGraphPlus {
|
||||
public:
|
||||
inline ScatterPieGraph(size_t axis1, size_t axis2, size_t axis3) : ScatterGraphPlus(axis1, axis2, axis3) {}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
inline ScatterPieGraph(size_t axis1, size_t axis2, size_t axis3) : ScatterGraphPlus(axis1, axis2, axis3) {}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
};
|
||||
|
||||
/// The legend, used for pie graphs
|
||||
class GraphLegend : public Graph1D {
|
||||
public:
|
||||
inline GraphLegend(size_t axis, Alignment alignment, bool reverse = false)
|
||||
: Graph1D(axis), alignment(alignment), reverse(reverse)
|
||||
{}
|
||||
virtual RealSize determineSize(RotatedDC& dc) const;
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const;
|
||||
inline GraphLegend(size_t axis, Alignment alignment, bool reverse = false)
|
||||
: Graph1D(axis), alignment(alignment), reverse(reverse)
|
||||
{}
|
||||
virtual RealSize determineSize(RotatedDC& dc) const;
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const;
|
||||
private:
|
||||
mutable RealSize size, item_size;
|
||||
Alignment alignment;
|
||||
bool reverse;
|
||||
mutable RealSize size, item_size;
|
||||
Alignment alignment;
|
||||
bool reverse;
|
||||
};
|
||||
|
||||
/// Simple statistics like the mean
|
||||
class GraphStats : public Graph1D {
|
||||
public:
|
||||
inline GraphStats(size_t axis, Alignment alignment)
|
||||
: Graph1D(axis), alignment(alignment)
|
||||
{}
|
||||
virtual RealSize determineSize(RotatedDC& dc) const;
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
inline GraphStats(size_t axis, Alignment alignment)
|
||||
: Graph1D(axis), alignment(alignment)
|
||||
{}
|
||||
virtual RealSize determineSize(RotatedDC& dc) const;
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
private:
|
||||
mutable RealSize size, item_size;
|
||||
mutable double label_width;
|
||||
Alignment alignment;
|
||||
vector<pair<String,String> > values;
|
||||
mutable RealSize size, item_size;
|
||||
mutable double label_width;
|
||||
Alignment alignment;
|
||||
vector<pair<String,String> > values;
|
||||
};
|
||||
|
||||
//class GraphTable {
|
||||
//};
|
||||
|
||||
enum DrawLines
|
||||
{ DRAW_LINES_NO
|
||||
, DRAW_LINES_BETWEEN
|
||||
, DRAW_LINES_MID
|
||||
{ DRAW_LINES_NO
|
||||
, DRAW_LINES_BETWEEN
|
||||
, DRAW_LINES_MID
|
||||
};
|
||||
|
||||
/// Draws a horizontal/vertical axis for group labels
|
||||
class GraphLabelAxis : public Graph1D {
|
||||
public:
|
||||
inline GraphLabelAxis(size_t axis, Direction direction, bool rotate = false, DrawLines draw_lines = DRAW_LINES_NO, bool label = false)
|
||||
: Graph1D(axis), direction(direction), rotate(rotate), draw_lines(draw_lines), label(label)
|
||||
{}
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const;
|
||||
inline GraphLabelAxis(size_t axis, Direction direction, bool rotate = false, DrawLines draw_lines = DRAW_LINES_NO, bool label = false)
|
||||
: Graph1D(axis), direction(direction), rotate(rotate), draw_lines(draw_lines), label(label)
|
||||
{}
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
virtual int findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight) const;
|
||||
private:
|
||||
Direction direction;
|
||||
int levels;
|
||||
bool rotate;
|
||||
DrawLines draw_lines;
|
||||
bool label;
|
||||
Direction direction;
|
||||
int levels;
|
||||
bool rotate;
|
||||
DrawLines draw_lines;
|
||||
bool label;
|
||||
};
|
||||
|
||||
/// Draws an a vertical axis for counts
|
||||
class GraphValueAxis : public Graph1D {
|
||||
public:
|
||||
inline GraphValueAxis(size_t axis, bool highlight_value) : Graph1D(axis), highlight_value(highlight_value) {}
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
inline GraphValueAxis(size_t axis, bool highlight_value) : Graph1D(axis), highlight_value(highlight_value) {}
|
||||
virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const;
|
||||
private:
|
||||
bool highlight_value;
|
||||
bool highlight_value;
|
||||
};
|
||||
|
||||
/// A graph with margins
|
||||
class GraphWithMargins : public Graph {
|
||||
public:
|
||||
inline GraphWithMargins(const GraphP& graph,
|
||||
double margin_left, double margin_top, double margin_right, double margin_bottom,
|
||||
bool upside_down = false)
|
||||
: graph(graph)
|
||||
, margin_left(margin_left), margin_top(margin_top), margin_right(margin_right), margin_bottom(margin_bottom)
|
||||
, upside_down(upside_down)
|
||||
{}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
inline GraphWithMargins(const GraphP& graph,
|
||||
double margin_left, double margin_top, double margin_right, double margin_bottom,
|
||||
bool upside_down = false)
|
||||
: graph(graph)
|
||||
, margin_left(margin_left), margin_top(margin_top), margin_right(margin_right), margin_bottom(margin_bottom)
|
||||
, upside_down(upside_down)
|
||||
{}
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
private:
|
||||
const GraphP graph;
|
||||
double margin_left, margin_top, margin_right, margin_bottom;
|
||||
bool upside_down; // put the coordinate system upside down, since graphs are usually bottom-to-top
|
||||
const GraphP graph;
|
||||
double margin_left, margin_top, margin_right, margin_bottom;
|
||||
bool upside_down; // put the coordinate system upside down, since graphs are usually bottom-to-top
|
||||
};
|
||||
|
||||
/// A display containing multiple graphs
|
||||
class GraphContainer : public Graph {
|
||||
public:
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
|
||||
void add(const GraphP& graph);
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& screen_rect, bool tight, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
|
||||
void add(const GraphP& graph);
|
||||
private:
|
||||
vector<GraphP> items;
|
||||
vector<GraphP> items;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Graph control
|
||||
@@ -337,48 +337,48 @@ class GraphContainer : public Graph {
|
||||
/// A control showing statistics in a graphical form
|
||||
class GraphControl : public wxControl {
|
||||
public:
|
||||
/// Create a graph control
|
||||
GraphControl(Window* parent, int id);
|
||||
|
||||
/// Set the type of graph used, from a number of predefined choices
|
||||
void setLayout(GraphType type, bool force_refresh = false);
|
||||
/// Update the data in the graph
|
||||
void setData(const GraphDataPre& data);
|
||||
/// Update the data in the graph
|
||||
void setData(const GraphDataP& data);
|
||||
/// Retrieve the data in the graph
|
||||
GraphDataP getData() const;
|
||||
|
||||
/// Is there a selection on the given axis?
|
||||
bool hasSelection(size_t axis) const;
|
||||
/// Get the current item along the given axis
|
||||
String getSelection(size_t axis) const;
|
||||
/// Get the current item along each axis
|
||||
vector<int> getSelectionIndices() const;
|
||||
|
||||
/// Get the current layout
|
||||
GraphType getLayout() const;
|
||||
/// Get the current dimensionality
|
||||
size_t getDimensionality() const;
|
||||
|
||||
/// Create a graph control
|
||||
GraphControl(Window* parent, int id);
|
||||
|
||||
/// Set the type of graph used, from a number of predefined choices
|
||||
void setLayout(GraphType type, bool force_refresh = false);
|
||||
/// Update the data in the graph
|
||||
void setData(const GraphDataPre& data);
|
||||
/// Update the data in the graph
|
||||
void setData(const GraphDataP& data);
|
||||
/// Retrieve the data in the graph
|
||||
GraphDataP getData() const;
|
||||
|
||||
/// Is there a selection on the given axis?
|
||||
bool hasSelection(size_t axis) const;
|
||||
/// Get the current item along the given axis
|
||||
String getSelection(size_t axis) const;
|
||||
/// Get the current item along each axis
|
||||
vector<int> getSelectionIndices() const;
|
||||
|
||||
/// Get the current layout
|
||||
GraphType getLayout() const;
|
||||
/// Get the current dimensionality
|
||||
size_t getDimensionality() const;
|
||||
|
||||
private:
|
||||
/// Graph object
|
||||
GraphP graph;
|
||||
GraphType layout; /// < The current layout
|
||||
/// The selected item per axis, or an empty vector if there is no selection
|
||||
/** If the value for an axis is -1, then all groups on that axis are selected */
|
||||
vector<int> current_item;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onPaint(wxPaintEvent&);
|
||||
void onEraseBackground(wxEraseEvent&) {}
|
||||
void onSize (wxSizeEvent&);
|
||||
void onMouseDown(wxMouseEvent& ev);
|
||||
void onMotion(wxMouseEvent& ev);
|
||||
void onChar(wxKeyEvent& ev);
|
||||
|
||||
void onSelectionChange();
|
||||
/// Graph object
|
||||
GraphP graph;
|
||||
GraphType layout; /// < The current layout
|
||||
/// The selected item per axis, or an empty vector if there is no selection
|
||||
/** If the value for an axis is -1, then all groups on that axis are selected */
|
||||
vector<int> current_item;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onPaint(wxPaintEvent&);
|
||||
void onEraseBackground(wxEraseEvent&) {}
|
||||
void onSize (wxSizeEvent&);
|
||||
void onMouseDown(wxMouseEvent& ev);
|
||||
void onMotion(wxMouseEvent& ev);
|
||||
void onChar(wxKeyEvent& ev);
|
||||
|
||||
void onSelectionChange();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -20,122 +20,122 @@ DECLARE_TYPEOF_COLLECTION(FieldP);
|
||||
// ----------------------------------------------------------------------------- : ImageCardList
|
||||
|
||||
ImageCardList::ImageCardList(Window* parent, int id, long additional_style)
|
||||
: CardListBase(parent, id, additional_style)
|
||||
: CardListBase(parent, id, additional_style)
|
||||
{}
|
||||
|
||||
ImageCardList::~ImageCardList() {
|
||||
thumbnail_thread.abort(this);
|
||||
thumbnail_thread.abort(this);
|
||||
}
|
||||
void ImageCardList::onRebuild() {
|
||||
image_field = findImageField();
|
||||
image_field = findImageField();
|
||||
}
|
||||
void ImageCardList::onBeforeChangeSet() {
|
||||
CardListBase::onBeforeChangeSet();
|
||||
// remove all but the first two (sort asc/desc) images from image list
|
||||
wxImageList* il = GetImageList(wxIMAGE_LIST_SMALL);
|
||||
while (il && il->GetImageCount() > 2) {
|
||||
il->Remove(2);
|
||||
}
|
||||
thumbnail_thread.abort(this);
|
||||
thumbnails.clear();
|
||||
CardListBase::onBeforeChangeSet();
|
||||
// remove all but the first two (sort asc/desc) images from image list
|
||||
wxImageList* il = GetImageList(wxIMAGE_LIST_SMALL);
|
||||
while (il && il->GetImageCount() > 2) {
|
||||
il->Remove(2);
|
||||
}
|
||||
thumbnail_thread.abort(this);
|
||||
thumbnails.clear();
|
||||
}
|
||||
|
||||
ImageFieldP ImageCardList::findImageField() {
|
||||
FOR_EACH(f, set->game->card_fields) {
|
||||
ImageFieldP imgf = dynamic_pointer_cast<ImageField>(f);
|
||||
if (imgf) return imgf;
|
||||
}
|
||||
return ImageFieldP();
|
||||
FOR_EACH(f, set->game->card_fields) {
|
||||
ImageFieldP imgf = dynamic_pointer_cast<ImageField>(f);
|
||||
if (imgf) return imgf;
|
||||
}
|
||||
return ImageFieldP();
|
||||
}
|
||||
|
||||
/// A request for a thumbnail of a card image
|
||||
class CardThumbnailRequest : public ThumbnailRequest {
|
||||
public:
|
||||
CardThumbnailRequest(ImageCardList* parent, const String& filename)
|
||||
: ThumbnailRequest(
|
||||
parent,
|
||||
_("card") + parent->set->absoluteFilename() + _("-") + filename,
|
||||
wxDateTime::Now()) // TODO: Find mofication time of card image
|
||||
, filename(filename)
|
||||
{}
|
||||
virtual Image generate() {
|
||||
try {
|
||||
ImageCardList* parent = (ImageCardList*)owner;
|
||||
Image image;
|
||||
if (image.LoadFile(*parent->set->openIn(filename))) {
|
||||
// two step anti aliased resampling
|
||||
image.Rescale(36, 28); // step 1: no anti aliassing
|
||||
return resample(image, 18, 14); // step 2: with anti aliassing
|
||||
} else {
|
||||
return Image();
|
||||
}
|
||||
} catch (...) {
|
||||
return Image();
|
||||
}
|
||||
}
|
||||
virtual void store(const Image& img) {
|
||||
// add finished bitmap to the imagelist
|
||||
ImageCardList* parent = (ImageCardList*)owner;
|
||||
if (img.Ok()) {
|
||||
wxImageList* il = parent->GetImageList(wxIMAGE_LIST_SMALL);
|
||||
int id = il->Add(wxBitmap(img));
|
||||
parent->thumbnails.insert(make_pair(filename, id));
|
||||
parent->Refresh(false);
|
||||
}
|
||||
}
|
||||
CardThumbnailRequest(ImageCardList* parent, const String& filename)
|
||||
: ThumbnailRequest(
|
||||
parent,
|
||||
_("card") + parent->set->absoluteFilename() + _("-") + filename,
|
||||
wxDateTime::Now()) // TODO: Find mofication time of card image
|
||||
, filename(filename)
|
||||
{}
|
||||
virtual Image generate() {
|
||||
try {
|
||||
ImageCardList* parent = (ImageCardList*)owner;
|
||||
Image image;
|
||||
if (image.LoadFile(*parent->set->openIn(filename))) {
|
||||
// two step anti aliased resampling
|
||||
image.Rescale(36, 28); // step 1: no anti aliassing
|
||||
return resample(image, 18, 14); // step 2: with anti aliassing
|
||||
} else {
|
||||
return Image();
|
||||
}
|
||||
} catch (...) {
|
||||
return Image();
|
||||
}
|
||||
}
|
||||
virtual void store(const Image& img) {
|
||||
// add finished bitmap to the imagelist
|
||||
ImageCardList* parent = (ImageCardList*)owner;
|
||||
if (img.Ok()) {
|
||||
wxImageList* il = parent->GetImageList(wxIMAGE_LIST_SMALL);
|
||||
int id = il->Add(wxBitmap(img));
|
||||
parent->thumbnails.insert(make_pair(filename, id));
|
||||
parent->Refresh(false);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool threadSafe() const {return true;}
|
||||
virtual bool threadSafe() const {return true;}
|
||||
private:
|
||||
String filename;
|
||||
String filename;
|
||||
};
|
||||
|
||||
int ImageCardList::OnGetItemImage(long pos) const {
|
||||
if (image_field) {
|
||||
// Image = thumbnail of first image field of card
|
||||
ImageValue& val = static_cast<ImageValue&>(*getCard(pos)->data[image_field]);
|
||||
if (!val.filename) return -1; // no image
|
||||
// is there already a thumbnail?
|
||||
map<String,int>::const_iterator it = thumbnails.find(val.filename);
|
||||
if (it != thumbnails.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
// request a thumbnail
|
||||
thumbnail_thread.request(intrusive(new CardThumbnailRequest(const_cast<ImageCardList*>(this), val.filename)));
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
if (image_field) {
|
||||
// Image = thumbnail of first image field of card
|
||||
ImageValue& val = static_cast<ImageValue&>(*getCard(pos)->data[image_field]);
|
||||
if (!val.filename) return -1; // no image
|
||||
// is there already a thumbnail?
|
||||
map<String,int>::const_iterator it = thumbnails.find(val.filename);
|
||||
if (it != thumbnails.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
// request a thumbnail
|
||||
thumbnail_thread.request(intrusive(new CardThumbnailRequest(const_cast<ImageCardList*>(this), val.filename)));
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ImageCardList::onIdle(wxIdleEvent&) {
|
||||
thumbnail_thread.done(this);
|
||||
thumbnail_thread.done(this);
|
||||
}
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(ImageCardList, CardListBase)
|
||||
EVT_IDLE (ImageCardList::onIdle)
|
||||
EVT_IDLE (ImageCardList::onIdle)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
// ----------------------------------------------------------------------------- : FilteredImageCardList
|
||||
|
||||
FilteredImageCardList::FilteredImageCardList(Window* parent, int id, long additional_style)
|
||||
: ImageCardList(parent, id, additional_style)
|
||||
: ImageCardList(parent, id, additional_style)
|
||||
{}
|
||||
|
||||
void FilteredImageCardList::setFilter(const CardListFilterP& filter) {
|
||||
this->filter = filter;
|
||||
rebuild();
|
||||
this->filter = filter;
|
||||
rebuild();
|
||||
}
|
||||
|
||||
void FilteredImageCardList::onChangeSet() {
|
||||
// clear filter before changing set, the filter might not make sense for a different set
|
||||
filter = CardListFilterP();
|
||||
CardListBase::onChangeSet();
|
||||
// clear filter before changing set, the filter might not make sense for a different set
|
||||
filter = CardListFilterP();
|
||||
CardListBase::onChangeSet();
|
||||
}
|
||||
|
||||
void FilteredImageCardList::getItems(vector<VoidP>& out) const {
|
||||
if (filter) {
|
||||
filter->getItems(set->cards,out);
|
||||
} else {
|
||||
ImageCardList::getItems(out);
|
||||
}
|
||||
if (filter) {
|
||||
filter->getItems(set->cards,out);
|
||||
} else {
|
||||
ImageCardList::getItems(out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,41 +21,41 @@ DECLARE_POINTER_TYPE(ImageField);
|
||||
/** This card list also allows the list to be modified */
|
||||
class ImageCardList : public CardListBase {
|
||||
public:
|
||||
~ImageCardList();
|
||||
ImageCardList(Window* parent, int id, long additional_style = 0);
|
||||
~ImageCardList();
|
||||
ImageCardList(Window* parent, int id, long additional_style = 0);
|
||||
protected:
|
||||
virtual int OnGetItemImage(long pos) const;
|
||||
virtual void onRebuild();
|
||||
virtual void onBeforeChangeSet();
|
||||
virtual bool allowModify() const { return true; }
|
||||
virtual int OnGetItemImage(long pos) const;
|
||||
virtual void onRebuild();
|
||||
virtual void onBeforeChangeSet();
|
||||
virtual bool allowModify() const { return true; }
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
void onIdle(wxIdleEvent&);
|
||||
|
||||
ImageFieldP image_field; ///< Field to use for card images
|
||||
mutable map<String,int> thumbnails; ///< image thumbnails, based on image_field
|
||||
|
||||
ImageFieldP findImageField();
|
||||
|
||||
friend class CardThumbnailRequest;
|
||||
DECLARE_EVENT_TABLE();
|
||||
void onIdle(wxIdleEvent&);
|
||||
|
||||
ImageFieldP image_field; ///< Field to use for card images
|
||||
mutable map<String,int> thumbnails; ///< image thumbnails, based on image_field
|
||||
|
||||
ImageFieldP findImageField();
|
||||
|
||||
friend class CardThumbnailRequest;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : FilteredImageCardList
|
||||
|
||||
class FilteredImageCardList : public ImageCardList {
|
||||
public:
|
||||
FilteredImageCardList(Window* parent, int id, long additional_style = 0);
|
||||
|
||||
/// Change the filter to use, if null then don't use a filter
|
||||
void setFilter(const CardListFilterP& filter);
|
||||
|
||||
FilteredImageCardList(Window* parent, int id, long additional_style = 0);
|
||||
|
||||
/// Change the filter to use, if null then don't use a filter
|
||||
void setFilter(const CardListFilterP& filter);
|
||||
|
||||
protected:
|
||||
/// Get only the subset of the cards
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
virtual void onChangeSet();
|
||||
|
||||
private:
|
||||
CardListFilterP filter; ///< Filter with which this.cards is made
|
||||
/// Get only the subset of the cards
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
virtual void onChangeSet();
|
||||
|
||||
private:
|
||||
CardListFilterP filter; ///< Filter with which this.cards is made
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+173
-173
@@ -14,241 +14,241 @@
|
||||
// ----------------------------------------------------------------------------- : 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)
|
||||
: 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
|
||||
// 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;
|
||||
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();
|
||||
return selected_item_pos >= 0 && static_cast<size_t>(selected_item_pos + 1) < sorted_list.size();
|
||||
}
|
||||
void ItemList::selectPrevious() {
|
||||
assert(selected_item_pos >= 1);
|
||||
focusNone();
|
||||
selectItemPos(selected_item_pos - 1, true, true);
|
||||
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);
|
||||
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);
|
||||
if (sorted_list.empty()) return;
|
||||
selectItemPos(0, true);
|
||||
}
|
||||
|
||||
bool ItemList::doCut() {
|
||||
// cut = copy + delete
|
||||
if (!canCut()) return false;
|
||||
if (!doCopy()) return false;
|
||||
doDelete();
|
||||
return true;
|
||||
// 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();
|
||||
findSelectedItemPos();
|
||||
if (focus) focusSelectedItem();
|
||||
if (item != selected_item && focus) {
|
||||
focusNone();
|
||||
}
|
||||
selected_item = item;
|
||||
if (event) sendEvent();
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 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;
|
||||
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());
|
||||
}
|
||||
}
|
||||
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);
|
||||
// (re)select current item
|
||||
findSelectedItemPos();
|
||||
focusNone();
|
||||
focusSelectedItem(true);
|
||||
// refresh items
|
||||
if (item_count == 0) {
|
||||
Refresh();
|
||||
} else {
|
||||
RefreshItems(0, item_count - 1);
|
||||
}
|
||||
// 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);
|
||||
// (re)select current item
|
||||
findSelectedItemPos();
|
||||
focusNone();
|
||||
focusSelectedItem(true);
|
||||
// refresh items
|
||||
if (item_count == 0) {
|
||||
Refresh();
|
||||
} else {
|
||||
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, sort_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();
|
||||
// Change image in column header
|
||||
long count = GetColumnCount();
|
||||
for (long i = 0 ; i < count ; ++i) {
|
||||
if (i == column) {
|
||||
SetColumnImage(i, sort_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);
|
||||
#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
|
||||
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
EVT_LIST_COL_CLICK (wxID_ANY, ItemList::onColumnClick)
|
||||
EVT_LIST_ITEM_FOCUSED (wxID_ANY, ItemList::onItemFocus)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
@@ -25,91 +25,91 @@
|
||||
*/
|
||||
class ItemList : public wxListView {
|
||||
public:
|
||||
ItemList(Window* parent, int id, long additional_style = 0, bool multi_sel = false);
|
||||
|
||||
// --------------------------------------------------- : Selection
|
||||
|
||||
/// Is there a previous item to select?
|
||||
bool canSelectPrevious() const;
|
||||
/// Is there a next item to select?
|
||||
bool canSelectNext() const;
|
||||
/// Move the selection to the previous item (if possible)
|
||||
void selectPrevious();
|
||||
/// Move the selection to the next item (if possible)
|
||||
void selectNext();
|
||||
/// Move the selection to the first item (if possible)
|
||||
void selectFirst();
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
virtual bool canCut() const { return canCopy() && canDelete(); }
|
||||
virtual bool canCopy() const { return false; }
|
||||
virtual bool canPaste() const { return false; }
|
||||
virtual bool canDelete() const { return false; }
|
||||
// Try to perform a clipboard operation, return success
|
||||
virtual bool doCut();
|
||||
virtual bool doCopy() { return false; }
|
||||
virtual bool doPaste() { return false; }
|
||||
virtual bool doDelete() { return false; }
|
||||
|
||||
// --------------------------------------------------- : Virtual interface
|
||||
ItemList(Window* parent, int id, long additional_style = 0, bool multi_sel = false);
|
||||
|
||||
// --------------------------------------------------- : Selection
|
||||
|
||||
/// Is there a previous item to select?
|
||||
bool canSelectPrevious() const;
|
||||
/// Is there a next item to select?
|
||||
bool canSelectNext() const;
|
||||
/// Move the selection to the previous item (if possible)
|
||||
void selectPrevious();
|
||||
/// Move the selection to the next item (if possible)
|
||||
void selectNext();
|
||||
/// Move the selection to the first item (if possible)
|
||||
void selectFirst();
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
virtual bool canCut() const { return canCopy() && canDelete(); }
|
||||
virtual bool canCopy() const { return false; }
|
||||
virtual bool canPaste() const { return false; }
|
||||
virtual bool canDelete() const { return false; }
|
||||
// Try to perform a clipboard operation, return success
|
||||
virtual bool doCut();
|
||||
virtual bool doCopy() { return false; }
|
||||
virtual bool doPaste() { return false; }
|
||||
virtual bool doDelete() { return false; }
|
||||
|
||||
// --------------------------------------------------- : Virtual interface
|
||||
protected:
|
||||
/// Get a list of all items
|
||||
virtual void getItems(vector<VoidP>& out) const = 0;
|
||||
|
||||
/// Send an 'item selected' event for the currently selected item (selected_item)
|
||||
virtual void sendEvent() = 0;
|
||||
|
||||
/// Is sorting required?
|
||||
virtual bool mustSort() const { return false; }
|
||||
/// Compare two items for < based on sort_by_column (not on sort_ascending)
|
||||
virtual bool compareItems(void* a, void* b) const = 0;
|
||||
|
||||
// --------------------------------------------------- : Protected interface
|
||||
/// Return the card at the given position in the sorted list
|
||||
inline const VoidP& getItem(long pos) const { return sorted_list[pos]; }
|
||||
/// Sort by the given column
|
||||
virtual void sortBy(long column, bool ascending);
|
||||
/// Refresh the card list (resort, refresh and reselect current item)
|
||||
void refreshList(bool refresh_current_only = false);
|
||||
/// Set the image of a column header (fixes wx bug)
|
||||
void SetColumnImage(int col, int image);
|
||||
|
||||
/// Select an item, send an event to the parent
|
||||
/** If focus then the item is also focused and selected in the actual control.
|
||||
* This should not be done when the item is selected because it was focused (leading to a loop).
|
||||
*/
|
||||
void selectItem(const VoidP& item, bool focus, bool event);
|
||||
/// Select a item at the specified position
|
||||
void selectItemPos(long pos, bool focus, bool force_focus = false);
|
||||
/// Find the position for the selected_item
|
||||
void findSelectedItemPos();
|
||||
/// Actually select the item at selected_item_pos in the control
|
||||
/** if force_focus == true, then the item is highlighted again if it already is focused. */
|
||||
void focusSelectedItem(bool force_focus = false);
|
||||
/// Deselect everything in the control
|
||||
void focusNone();
|
||||
/// Actually select a certain item in the control
|
||||
void focusItem(const VoidP& item, bool focus = true);
|
||||
/// Count the number of focused items
|
||||
long focusCount() const;
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
VoidP selected_item; ///< The currently selected item
|
||||
long selected_item_pos; ///< Position of the selected item in the sorted_list, or -1 if no card is selected
|
||||
long sort_by_column; ///< Column to use for sorting, or -1 if not sorted
|
||||
bool sort_ascending; ///< Sort order
|
||||
vector<VoidP> sorted_list; ///< Sorted list of items, can be considered a map: pos->item
|
||||
|
||||
/// Get a list of all items
|
||||
virtual void getItems(vector<VoidP>& out) const = 0;
|
||||
|
||||
/// Send an 'item selected' event for the currently selected item (selected_item)
|
||||
virtual void sendEvent() = 0;
|
||||
|
||||
/// Is sorting required?
|
||||
virtual bool mustSort() const { return false; }
|
||||
/// Compare two items for < based on sort_by_column (not on sort_ascending)
|
||||
virtual bool compareItems(void* a, void* b) const = 0;
|
||||
|
||||
// --------------------------------------------------- : Protected interface
|
||||
/// Return the card at the given position in the sorted list
|
||||
inline const VoidP& getItem(long pos) const { return sorted_list[pos]; }
|
||||
/// Sort by the given column
|
||||
virtual void sortBy(long column, bool ascending);
|
||||
/// Refresh the card list (resort, refresh and reselect current item)
|
||||
void refreshList(bool refresh_current_only = false);
|
||||
/// Set the image of a column header (fixes wx bug)
|
||||
void SetColumnImage(int col, int image);
|
||||
|
||||
/// Select an item, send an event to the parent
|
||||
/** If focus then the item is also focused and selected in the actual control.
|
||||
* This should not be done when the item is selected because it was focused (leading to a loop).
|
||||
*/
|
||||
void selectItem(const VoidP& item, bool focus, bool event);
|
||||
/// Select a item at the specified position
|
||||
void selectItemPos(long pos, bool focus, bool force_focus = false);
|
||||
/// Find the position for the selected_item
|
||||
void findSelectedItemPos();
|
||||
/// Actually select the item at selected_item_pos in the control
|
||||
/** if force_focus == true, then the item is highlighted again if it already is focused. */
|
||||
void focusSelectedItem(bool force_focus = false);
|
||||
/// Deselect everything in the control
|
||||
void focusNone();
|
||||
/// Actually select a certain item in the control
|
||||
void focusItem(const VoidP& item, bool focus = true);
|
||||
/// Count the number of focused items
|
||||
long focusCount() const;
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
VoidP selected_item; ///< The currently selected item
|
||||
long selected_item_pos; ///< Position of the selected item in the sorted_list, or -1 if no card is selected
|
||||
long sort_by_column; ///< Column to use for sorting, or -1 if not sorted
|
||||
bool sort_ascending; ///< Sort order
|
||||
vector<VoidP> sorted_list; ///< Sorted list of items, can be considered a map: pos->item
|
||||
|
||||
private:
|
||||
struct ItemComparer; // for comparing items
|
||||
|
||||
// --------------------------------------------------- : Window events
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onColumnClick(wxListEvent& ev);
|
||||
void onItemFocus (wxListEvent& ev);
|
||||
void onContextMenu(wxContextMenuEvent&);
|
||||
struct ItemComparer; // for comparing items
|
||||
|
||||
// --------------------------------------------------- : Window events
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onColumnClick(wxListEvent& ev);
|
||||
void onItemFocus (wxListEvent& ev);
|
||||
void onContextMenu(wxContextMenuEvent&);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+152
-152
@@ -31,72 +31,72 @@ DEFINE_EVENT_TYPE(EVENT_KEYWORD_SELECT);
|
||||
// ----------------------------------------------------------------------------- : KeywordList
|
||||
|
||||
KeywordList::KeywordList(Window* parent, int id, long additional_style)
|
||||
: ItemList(parent, id, additional_style)
|
||||
: ItemList(parent, id, additional_style)
|
||||
{
|
||||
// Add columns
|
||||
InsertColumn(0, _LABEL_("keyword"), wxLIST_FORMAT_LEFT, 0);
|
||||
InsertColumn(1, _LABEL_("match"), wxLIST_FORMAT_LEFT, 200);
|
||||
InsertColumn(2, _LABEL_("mode"), wxLIST_FORMAT_LEFT, 60);
|
||||
InsertColumn(3, _LABEL_("uses"), wxLIST_FORMAT_RIGHT, 50);
|
||||
InsertColumn(4, _LABEL_("reminder"), wxLIST_FORMAT_LEFT, 300);
|
||||
// Add columns
|
||||
InsertColumn(0, _LABEL_("keyword"), wxLIST_FORMAT_LEFT, 0);
|
||||
InsertColumn(1, _LABEL_("match"), wxLIST_FORMAT_LEFT, 200);
|
||||
InsertColumn(2, _LABEL_("mode"), wxLIST_FORMAT_LEFT, 60);
|
||||
InsertColumn(3, _LABEL_("uses"), wxLIST_FORMAT_RIGHT, 50);
|
||||
InsertColumn(4, _LABEL_("reminder"), wxLIST_FORMAT_LEFT, 300);
|
||||
}
|
||||
|
||||
KeywordList::~KeywordList() {
|
||||
storeColumns();
|
||||
storeColumns();
|
||||
}
|
||||
void KeywordList::storeColumns() {
|
||||
// TODO
|
||||
// TODO
|
||||
}
|
||||
|
||||
void KeywordList::onBeforeChangeSet() {
|
||||
storeColumns();
|
||||
storeColumns();
|
||||
}
|
||||
void KeywordList::onChangeSet() {
|
||||
updateUsageStatistics();
|
||||
refreshList();
|
||||
updateUsageStatistics();
|
||||
refreshList();
|
||||
}
|
||||
|
||||
void KeywordList::setFilter(const KeywordListFilterP& filter) {
|
||||
this->filter = filter;
|
||||
refreshList();
|
||||
this->filter = filter;
|
||||
refreshList();
|
||||
}
|
||||
|
||||
void KeywordList::onAction(const Action& action, bool undone) {
|
||||
TYPE_CASE(action, AddKeywordAction) {
|
||||
if (action.action.adding != undone) {
|
||||
// select the new keyword
|
||||
selectItem(action.action.steps[0].item, false /*list will be refreshed anyway*/, true);
|
||||
refreshList();
|
||||
} else {
|
||||
long pos = selected_item_pos;
|
||||
refreshList();
|
||||
if (selected_item_pos == -1) {
|
||||
// selected keyword was deleted, select the next
|
||||
selectItemPos(pos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_CASE(action, ValueAction) {
|
||||
if (!action.card) {
|
||||
KeywordTextValue* value = dynamic_cast<KeywordTextValue*>(action.valueP.get());
|
||||
if (value) {
|
||||
// this is indeed an action on a keyword, refresh
|
||||
refreshList(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_CASE_(action, ChangeKeywordModeAction) {
|
||||
refreshList();
|
||||
}
|
||||
TYPE_CASE(action, AddKeywordAction) {
|
||||
if (action.action.adding != undone) {
|
||||
// select the new keyword
|
||||
selectItem(action.action.steps[0].item, false /*list will be refreshed anyway*/, true);
|
||||
refreshList();
|
||||
} else {
|
||||
long pos = selected_item_pos;
|
||||
refreshList();
|
||||
if (selected_item_pos == -1) {
|
||||
// selected keyword was deleted, select the next
|
||||
selectItemPos(pos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_CASE(action, ValueAction) {
|
||||
if (!action.card) {
|
||||
KeywordTextValue* value = dynamic_cast<KeywordTextValue*>(action.valueP.get());
|
||||
if (value) {
|
||||
// this is indeed an action on a keyword, refresh
|
||||
refreshList(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_CASE_(action, ChangeKeywordModeAction) {
|
||||
refreshList();
|
||||
}
|
||||
}
|
||||
|
||||
void KeywordList::updateUsageStatistics() {
|
||||
usage_statistics.clear();
|
||||
FOR_EACH_CONST(card, set->cards) {
|
||||
for (KeywordUsageStatistics::const_iterator it = card->keyword_usage.begin() ; it != card->keyword_usage.end() ; ++it) {
|
||||
usage_statistics[it->second]++;
|
||||
}
|
||||
}
|
||||
usage_statistics.clear();
|
||||
FOR_EACH_CONST(card, set->cards) {
|
||||
for (KeywordUsageStatistics::const_iterator it = card->keyword_usage.begin() ; it != card->keyword_usage.end() ; ++it) {
|
||||
usage_statistics[it->second]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Clipboard
|
||||
@@ -104,152 +104,152 @@ void KeywordList::updateUsageStatistics() {
|
||||
bool KeywordList::canDelete() const { return !getKeyword()->fixed; }
|
||||
bool KeywordList::canCopy() const { return !!selected_item; }
|
||||
bool KeywordList::canPaste() const {
|
||||
return wxTheClipboard->IsSupported(KeywordDataObject::format);
|
||||
return wxTheClipboard->IsSupported(KeywordDataObject::format);
|
||||
}
|
||||
|
||||
bool KeywordList::doCopy() {
|
||||
if (!canCopy()) return false;
|
||||
if (!wxTheClipboard->Open()) return false;
|
||||
bool ok = wxTheClipboard->SetData(new KeywordDataObject(set, getKeyword())); // ignore result
|
||||
wxTheClipboard->Close();
|
||||
return ok;
|
||||
if (!canCopy()) return false;
|
||||
if (!wxTheClipboard->Open()) return false;
|
||||
bool ok = wxTheClipboard->SetData(new KeywordDataObject(set, getKeyword())); // ignore result
|
||||
wxTheClipboard->Close();
|
||||
return ok;
|
||||
}
|
||||
bool KeywordList::doCut() {
|
||||
// cut = copy + delete
|
||||
if (!canCut()) return false;
|
||||
if (!doCopy()) return false;
|
||||
doDelete();
|
||||
return true;
|
||||
// cut = copy + delete
|
||||
if (!canCut()) return false;
|
||||
if (!doCopy()) return false;
|
||||
doDelete();
|
||||
return true;
|
||||
}
|
||||
bool KeywordList::doPaste() {
|
||||
// get data
|
||||
if (!canPaste()) return false;
|
||||
if (!wxTheClipboard->Open()) return false;
|
||||
KeywordDataObject data;
|
||||
bool ok = wxTheClipboard->GetData(data);
|
||||
wxTheClipboard->Close();
|
||||
if (!ok) return false;
|
||||
// add keyword to set
|
||||
KeywordP keyword = data.getKeyword(set);
|
||||
if (keyword) {
|
||||
set->actions.addAction(new AddKeywordAction(ADD, *set, keyword));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// get data
|
||||
if (!canPaste()) return false;
|
||||
if (!wxTheClipboard->Open()) return false;
|
||||
KeywordDataObject data;
|
||||
bool ok = wxTheClipboard->GetData(data);
|
||||
wxTheClipboard->Close();
|
||||
if (!ok) return false;
|
||||
// add keyword to set
|
||||
KeywordP keyword = data.getKeyword(set);
|
||||
if (keyword) {
|
||||
set->actions.addAction(new AddKeywordAction(ADD, *set, keyword));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool KeywordList::doDelete() {
|
||||
set->actions.addAction(new AddKeywordAction(REMOVE, *set, getKeyword()));
|
||||
return true;
|
||||
set->actions.addAction(new AddKeywordAction(REMOVE, *set, getKeyword()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : KeywordListBase : for ItemList
|
||||
|
||||
String match_string(const Keyword& a) {
|
||||
return untag(replace_all(replace_all(
|
||||
a.match,
|
||||
_("<atom-param>"), LEFT_ANGLE_BRACKET),
|
||||
_("</atom-param>"), RIGHT_ANGLE_BRACKET)
|
||||
);
|
||||
return untag(replace_all(replace_all(
|
||||
a.match,
|
||||
_("<atom-param>"), LEFT_ANGLE_BRACKET),
|
||||
_("</atom-param>"), RIGHT_ANGLE_BRACKET)
|
||||
);
|
||||
}
|
||||
|
||||
void KeywordList::getItems(vector<VoidP>& out) const {
|
||||
FOR_EACH(k, set->keywords) {
|
||||
k->fixed = false;
|
||||
if (!filter || filter->keep(*k)) {
|
||||
out.push_back(k);
|
||||
}
|
||||
}
|
||||
FOR_EACH(k, set->game->keywords) {
|
||||
k->fixed = true;
|
||||
if (!filter || filter->keep(*k)) {
|
||||
out.push_back(k);
|
||||
}
|
||||
}
|
||||
FOR_EACH(k, set->keywords) {
|
||||
k->fixed = false;
|
||||
if (!filter || filter->keep(*k)) {
|
||||
out.push_back(k);
|
||||
}
|
||||
}
|
||||
FOR_EACH(k, set->game->keywords) {
|
||||
k->fixed = true;
|
||||
if (!filter || filter->keep(*k)) {
|
||||
out.push_back(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
void KeywordList::sendEvent() {
|
||||
KeywordSelectEvent ev(getKeyword());
|
||||
ProcessEvent(ev);
|
||||
KeywordSelectEvent ev(getKeyword());
|
||||
ProcessEvent(ev);
|
||||
}
|
||||
bool KeywordList::compareItems(void* a, void* b) const {
|
||||
const Keyword& ka = *(Keyword*)a;
|
||||
const Keyword& kb = *(Keyword*)b;
|
||||
switch(sort_by_column) {
|
||||
case 0: return ka.keyword < kb.keyword;
|
||||
case 1: return ka.match < kb.match;
|
||||
case 2: return ka.mode < kb.mode;
|
||||
case 3: return usage(ka) < usage(kb);
|
||||
case 4: return ka.reminder.getUnparsed() < kb.reminder.getUnparsed();
|
||||
default: // TODO: 3
|
||||
return ka.keyword < kb.keyword;
|
||||
}
|
||||
const Keyword& ka = *(Keyword*)a;
|
||||
const Keyword& kb = *(Keyword*)b;
|
||||
switch(sort_by_column) {
|
||||
case 0: return ka.keyword < kb.keyword;
|
||||
case 1: return ka.match < kb.match;
|
||||
case 2: return ka.mode < kb.mode;
|
||||
case 3: return usage(ka) < usage(kb);
|
||||
case 4: return ka.reminder.getUnparsed() < kb.reminder.getUnparsed();
|
||||
default: // TODO: 3
|
||||
return ka.keyword < kb.keyword;
|
||||
}
|
||||
}
|
||||
|
||||
int KeywordList::usage(const Keyword& kw) const {
|
||||
map<const Keyword*,int>::const_iterator it = usage_statistics.find(&kw);
|
||||
if (it == usage_statistics.end()) return 0;
|
||||
else return it->second;
|
||||
map<const Keyword*,int>::const_iterator it = usage_statistics.find(&kw);
|
||||
if (it == usage_statistics.end()) return 0;
|
||||
else return it->second;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : KeywordList : Item text
|
||||
|
||||
String KeywordList::OnGetItemText (long pos, long col) const {
|
||||
const Keyword& kw = *getKeyword(pos);
|
||||
switch(col) {
|
||||
case 0: return kw.keyword;
|
||||
case 1: return match_string(kw);
|
||||
case 2: return kw.mode;
|
||||
case 3: return String::Format(_("%d"), usage(kw));
|
||||
case 4: {
|
||||
// convert all whitespace to ' '
|
||||
String formatted;
|
||||
bool seen_space = false;
|
||||
for (size_t i = 0; i < kw.reminder.getUnparsed().size(); ++i) {
|
||||
Char c = kw.reminder.getUnparsed().GetChar(i);
|
||||
if (isSpace(c)) {
|
||||
seen_space = true;
|
||||
} else {
|
||||
if (seen_space) {
|
||||
formatted += _(' ');
|
||||
seen_space = false;
|
||||
}
|
||||
formatted += c;
|
||||
}
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
default: return wxEmptyString;
|
||||
}
|
||||
const Keyword& kw = *getKeyword(pos);
|
||||
switch(col) {
|
||||
case 0: return kw.keyword;
|
||||
case 1: return match_string(kw);
|
||||
case 2: return kw.mode;
|
||||
case 3: return String::Format(_("%d"), usage(kw));
|
||||
case 4: {
|
||||
// convert all whitespace to ' '
|
||||
String formatted;
|
||||
bool seen_space = false;
|
||||
for (size_t i = 0; i < kw.reminder.getUnparsed().size(); ++i) {
|
||||
Char c = kw.reminder.getUnparsed().GetChar(i);
|
||||
if (isSpace(c)) {
|
||||
seen_space = true;
|
||||
} else {
|
||||
if (seen_space) {
|
||||
formatted += _(' ');
|
||||
seen_space = false;
|
||||
}
|
||||
formatted += c;
|
||||
}
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
default: return wxEmptyString;
|
||||
}
|
||||
}
|
||||
int KeywordList::OnGetItemImage(long pos) const {
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
wxListItemAttr* KeywordList::OnGetItemAttr(long pos) const {
|
||||
// black for set keywords, grey for game keywords (uneditable)
|
||||
const Keyword& kw = *getKeyword(pos);
|
||||
if (!kw.fixed && kw.valid) return nullptr;
|
||||
if (!kw.valid) {
|
||||
item_attr.SetTextColour(*wxRED);
|
||||
} else if (kw.fixed) {
|
||||
item_attr.SetTextColour(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.5));
|
||||
}
|
||||
return &item_attr;
|
||||
// black for set keywords, grey for game keywords (uneditable)
|
||||
const Keyword& kw = *getKeyword(pos);
|
||||
if (!kw.fixed && kw.valid) return nullptr;
|
||||
if (!kw.valid) {
|
||||
item_attr.SetTextColour(*wxRED);
|
||||
} else if (kw.fixed) {
|
||||
item_attr.SetTextColour(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.5));
|
||||
}
|
||||
return &item_attr;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : KeywordList : Context menu
|
||||
|
||||
void KeywordList::onContextMenu(wxContextMenuEvent&) {
|
||||
IconMenu m;
|
||||
m.Append(ID_EDIT_CUT, _("cut"), _CONTEXT_MENU_("cut"), _HELP_("cut keyword"));
|
||||
m.Append(ID_EDIT_COPY, _("copy"), _CONTEXT_MENU_("copy"), _HELP_("copy keyword"));
|
||||
m.Append(ID_EDIT_PASTE, _("paste"), _CONTEXT_MENU_("paste"), _HELP_("paste keyword"));
|
||||
m.AppendSeparator();
|
||||
m.Append(ID_KEYWORD_ADD, _("keyword_add"), _CONTEXT_MENU_("add keyword"), _HELP_("add keyword"));
|
||||
m.Append(ID_KEYWORD_REMOVE, _("keyword_del"), _CONTEXT_MENU_("remove keyword"), _HELP_("remove keyword"));
|
||||
PopupMenu(&m);
|
||||
IconMenu m;
|
||||
m.Append(ID_EDIT_CUT, _("cut"), _CONTEXT_MENU_("cut"), _HELP_("cut keyword"));
|
||||
m.Append(ID_EDIT_COPY, _("copy"), _CONTEXT_MENU_("copy"), _HELP_("copy keyword"));
|
||||
m.Append(ID_EDIT_PASTE, _("paste"), _CONTEXT_MENU_("paste"), _HELP_("paste keyword"));
|
||||
m.AppendSeparator();
|
||||
m.Append(ID_KEYWORD_ADD, _("keyword_add"), _CONTEXT_MENU_("add keyword"), _HELP_("add keyword"));
|
||||
m.Append(ID_KEYWORD_REMOVE, _("keyword_del"), _CONTEXT_MENU_("remove keyword"), _HELP_("remove keyword"));
|
||||
PopupMenu(&m);
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(KeywordList, ItemList)
|
||||
EVT_CONTEXT_MENU(KeywordList::onContextMenu)
|
||||
EVT_CONTEXT_MENU(KeywordList::onContextMenu)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
@@ -21,17 +21,17 @@ typedef intrusive_ptr<Filter<Keyword> > KeywordListFilterP;
|
||||
|
||||
DECLARE_LOCAL_EVENT_TYPE(EVENT_KEYWORD_SELECT, <not used>)
|
||||
/// Handle KeywordSelectEvents
|
||||
#define EVT_KEYWORD_SELECT(id, handler) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(EVENT_KEYWORD_SELECT, id, -1, \
|
||||
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) \
|
||||
(void (wxEvtHandler::*)(KeywordSelectEvent&)) (&handler), (wxObject*) NULL),
|
||||
#define EVT_KEYWORD_SELECT(id, handler) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(EVENT_KEYWORD_SELECT, id, -1, \
|
||||
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) \
|
||||
(void (wxEvtHandler::*)(KeywordSelectEvent&)) (&handler), (wxObject*) NULL),
|
||||
|
||||
/// The event of selecting a keyword
|
||||
struct KeywordSelectEvent : public wxCommandEvent {
|
||||
KeywordP keyword; ///< The selected keyword
|
||||
inline KeywordSelectEvent(const KeywordP& keyword)
|
||||
: wxCommandEvent(EVENT_KEYWORD_SELECT), keyword(keyword)
|
||||
{}
|
||||
KeywordP keyword; ///< The selected keyword
|
||||
inline KeywordSelectEvent(const KeywordP& keyword)
|
||||
: wxCommandEvent(EVENT_KEYWORD_SELECT), keyword(keyword)
|
||||
{}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : KeywordList
|
||||
@@ -39,70 +39,70 @@ struct KeywordSelectEvent : public wxCommandEvent {
|
||||
/// A control that lists the keywords in a set and its game
|
||||
class KeywordList : public ItemList, public SetView {
|
||||
public:
|
||||
KeywordList(Window* parent, int id, long additional_style = 0);
|
||||
~KeywordList();
|
||||
|
||||
// --------------------------------------------------- : Set stuff
|
||||
|
||||
virtual void onBeforeChangeSet();
|
||||
virtual void onChangeSet();
|
||||
virtual void onAction(const Action&, bool);
|
||||
void updateUsageStatistics();
|
||||
|
||||
// --------------------------------------------------- : Selection
|
||||
|
||||
inline KeywordP getKeyword() const { return static_pointer_cast<Keyword>(selected_item); }
|
||||
inline void setKeyword(const KeywordP& kw) { selectItem(kw, true, false); }
|
||||
|
||||
/// Change the filter to use, can be null
|
||||
void setFilter(const KeywordListFilterP& filter);
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
bool canDelete() const;
|
||||
bool canCopy() const;
|
||||
bool canPaste() const;
|
||||
// Try to perform a clipboard operation, return success
|
||||
bool doCut();
|
||||
bool doCopy();
|
||||
bool doPaste();
|
||||
bool doDelete();
|
||||
|
||||
// --------------------------------------------------- : The keywords
|
||||
KeywordList(Window* parent, int id, long additional_style = 0);
|
||||
~KeywordList();
|
||||
|
||||
// --------------------------------------------------- : Set stuff
|
||||
|
||||
virtual void onBeforeChangeSet();
|
||||
virtual void onChangeSet();
|
||||
virtual void onAction(const Action&, bool);
|
||||
void updateUsageStatistics();
|
||||
|
||||
// --------------------------------------------------- : Selection
|
||||
|
||||
inline KeywordP getKeyword() const { return static_pointer_cast<Keyword>(selected_item); }
|
||||
inline void setKeyword(const KeywordP& kw) { selectItem(kw, true, false); }
|
||||
|
||||
/// Change the filter to use, can be null
|
||||
void setFilter(const KeywordListFilterP& filter);
|
||||
|
||||
// --------------------------------------------------- : Clipboard
|
||||
|
||||
bool canDelete() const;
|
||||
bool canCopy() const;
|
||||
bool canPaste() const;
|
||||
// Try to perform a clipboard operation, return success
|
||||
bool doCut();
|
||||
bool doCopy();
|
||||
bool doPaste();
|
||||
bool doDelete();
|
||||
|
||||
// --------------------------------------------------- : The keywords
|
||||
protected:
|
||||
/// Get a list of all keywords
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
/// Return the keyword at the given position in the sorted keyword list
|
||||
inline KeywordP getKeyword(long pos) const { return static_pointer_cast<Keyword>(getItem(pos)); }
|
||||
|
||||
/// Send an 'item selected' event for the currently selected item (selected_item)
|
||||
virtual void sendEvent();
|
||||
/// Compare keywords
|
||||
virtual bool compareItems(void* a, void* b) const;
|
||||
|
||||
/// Get the text of an item in a specific column
|
||||
/** Overrides a function from wxListCtrl */
|
||||
virtual String OnGetItemText (long pos, long col) const;
|
||||
/// Get the image of an item, by default no image is used
|
||||
/** Overrides a function from wxListCtrl */
|
||||
virtual int OnGetItemImage(long pos) const;
|
||||
/// Get the color for an item
|
||||
virtual wxListItemAttr* OnGetItemAttr(long pos) const;
|
||||
|
||||
/// Get a list of all keywords
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
/// Return the keyword at the given position in the sorted keyword list
|
||||
inline KeywordP getKeyword(long pos) const { return static_pointer_cast<Keyword>(getItem(pos)); }
|
||||
|
||||
/// Send an 'item selected' event for the currently selected item (selected_item)
|
||||
virtual void sendEvent();
|
||||
/// Compare keywords
|
||||
virtual bool compareItems(void* a, void* b) const;
|
||||
|
||||
/// Get the text of an item in a specific column
|
||||
/** Overrides a function from wxListCtrl */
|
||||
virtual String OnGetItemText (long pos, long col) const;
|
||||
/// Get the image of an item, by default no image is used
|
||||
/** Overrides a function from wxListCtrl */
|
||||
virtual int OnGetItemImage(long pos) const;
|
||||
/// Get the color for an item
|
||||
virtual wxListItemAttr* OnGetItemAttr(long pos) const;
|
||||
|
||||
private:
|
||||
void storeColumns();
|
||||
|
||||
mutable wxListItemAttr item_attr; // for OnGetItemAttr
|
||||
KeywordListFilterP filter; ///< Which keywords to show?
|
||||
|
||||
/// How often is a keyword used in the set?
|
||||
int usage(const Keyword&) const;
|
||||
map<const Keyword*,int> usage_statistics;
|
||||
|
||||
// --------------------------------------------------- : Window events
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onContextMenu (wxContextMenuEvent&);
|
||||
void storeColumns();
|
||||
|
||||
mutable wxListItemAttr item_attr; // for OnGetItemAttr
|
||||
KeywordListFilterP filter; ///< Which keywords to show?
|
||||
|
||||
/// How often is a keyword used in the set?
|
||||
int usage(const Keyword&) const;
|
||||
map<const Keyword*,int> usage_statistics;
|
||||
|
||||
// --------------------------------------------------- : Window events
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onContextMenu (wxContextMenuEvent&);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -20,228 +20,228 @@ DECLARE_TYPEOF_NO_REV(IndexMap<FieldP COMMA StyleP>);
|
||||
// ----------------------------------------------------------------------------- : NativeLookEditor
|
||||
|
||||
NativeLookEditor::NativeLookEditor(Window* parent, int id, long style)
|
||||
: DataEditor(parent, id, style)
|
||||
: DataEditor(parent, id, style)
|
||||
{}
|
||||
|
||||
Rotation NativeLookEditor::getRotation() const {
|
||||
int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
return Rotation(0, RealRect(RealPoint(-dx,-dy),GetClientSize()));
|
||||
int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
return Rotation(0, RealRect(RealPoint(-dx,-dy),GetClientSize()));
|
||||
}
|
||||
|
||||
void NativeLookEditor::draw(DC& dc) {
|
||||
RotatedDC rdc(dc, getRotation(), QUALITY_LOW);
|
||||
DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
|
||||
RotatedDC rdc(dc, getRotation(), QUALITY_LOW);
|
||||
DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
|
||||
}
|
||||
void NativeLookEditor::drawViewer(RotatedDC& dc, ValueViewer& v) {
|
||||
if (!shouldDraw(v)) return;
|
||||
ValueEditor* e = v.getEditor();
|
||||
if (!e || e->drawLabel()) {
|
||||
// draw control border and box
|
||||
Style& s = *v.getStyle();
|
||||
draw_control_box(this, dc.getDC(), dc.trRectToBB(s.getInternalRect().grow(1)), current_editor == e, e != nullptr);
|
||||
// draw label
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
||||
// TODO : tr using stylesheet or using game?
|
||||
dc.DrawText(tr(getStylePackage(), s.fieldP->name, capitalize_sentence),
|
||||
RealPoint(margin_left - s.left, 1));
|
||||
}
|
||||
// draw viewer
|
||||
v.draw(dc);
|
||||
if (!shouldDraw(v)) return;
|
||||
ValueEditor* e = v.getEditor();
|
||||
if (!e || e->drawLabel()) {
|
||||
// draw control border and box
|
||||
Style& s = *v.getStyle();
|
||||
draw_control_box(this, dc.getDC(), dc.trRectToBB(s.getInternalRect().grow(1)), current_editor == e, e != nullptr);
|
||||
// draw label
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
||||
// TODO : tr using stylesheet or using game?
|
||||
dc.DrawText(tr(getStylePackage(), s.fieldP->name, capitalize_sentence),
|
||||
RealPoint(margin_left - s.left, 1));
|
||||
}
|
||||
// draw viewer
|
||||
v.draw(dc);
|
||||
}
|
||||
|
||||
void NativeLookEditor::resizeViewers() {
|
||||
// size stuff
|
||||
double y = margin;
|
||||
int w, h;
|
||||
GetClientSize(&w, &h);
|
||||
const int default_height = 17;
|
||||
// Determine label width
|
||||
{
|
||||
label_width = 0;
|
||||
wxClientDC dc(this);
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
FOR_EACH(v, viewers) {
|
||||
ValueEditor* e = v->getEditor();
|
||||
if (!e || e->drawLabel()) {
|
||||
// width of the label string
|
||||
int w;
|
||||
Style& s = *v->getStyle();
|
||||
String text = tr(getStylePackage(), s.fieldP->name, capitalize_sentence);
|
||||
dc.GetTextExtent(text,&w,nullptr);
|
||||
label_width = max(label_width, w + label_margin);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set editor sizes
|
||||
FOR_EACH(v, viewers) {
|
||||
StyleP s = v->getStyle();
|
||||
ValueEditor* e = v->getEditor();
|
||||
if (!e || e->drawLabel()) {
|
||||
s->left = margin + label_width;
|
||||
} else {
|
||||
s->left = margin;
|
||||
}
|
||||
s->top = y;
|
||||
s->width = w - s->left - margin;
|
||||
s->height = default_height;
|
||||
if (e) e->determineSize();
|
||||
y += s->height + vspace;
|
||||
}
|
||||
y = y - vspace + margin;
|
||||
SetVirtualSize(w, (int)y);
|
||||
SetScrollbar(wxVERTICAL, 0, h, (int)y);
|
||||
if (y >= h) {
|
||||
// Doesn't fit vertically, add scrollbar and resize
|
||||
/*
|
||||
y = margin;
|
||||
FOR_EACH(v, viewers) {
|
||||
StyleP s = v->getStyle();
|
||||
ValueEditor* e = v->getEditor();
|
||||
s->top = y;
|
||||
s->width = s->width - wxSystemSettings::GetMetric(wxSYS_VSCROLL_X, this);
|
||||
if (e) e->determineSize();
|
||||
y += s->height + vspace;
|
||||
}
|
||||
*/
|
||||
// create scrollbar
|
||||
}
|
||||
// size stuff
|
||||
double y = margin;
|
||||
int w, h;
|
||||
GetClientSize(&w, &h);
|
||||
const int default_height = 17;
|
||||
// Determine label width
|
||||
{
|
||||
label_width = 0;
|
||||
wxClientDC dc(this);
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
FOR_EACH(v, viewers) {
|
||||
ValueEditor* e = v->getEditor();
|
||||
if (!e || e->drawLabel()) {
|
||||
// width of the label string
|
||||
int w;
|
||||
Style& s = *v->getStyle();
|
||||
String text = tr(getStylePackage(), s.fieldP->name, capitalize_sentence);
|
||||
dc.GetTextExtent(text,&w,nullptr);
|
||||
label_width = max(label_width, w + label_margin);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set editor sizes
|
||||
FOR_EACH(v, viewers) {
|
||||
StyleP s = v->getStyle();
|
||||
ValueEditor* e = v->getEditor();
|
||||
if (!e || e->drawLabel()) {
|
||||
s->left = margin + label_width;
|
||||
} else {
|
||||
s->left = margin;
|
||||
}
|
||||
s->top = y;
|
||||
s->width = w - s->left - margin;
|
||||
s->height = default_height;
|
||||
if (e) e->determineSize();
|
||||
y += s->height + vspace;
|
||||
}
|
||||
y = y - vspace + margin;
|
||||
SetVirtualSize(w, (int)y);
|
||||
SetScrollbar(wxVERTICAL, 0, h, (int)y);
|
||||
if (y >= h) {
|
||||
// Doesn't fit vertically, add scrollbar and resize
|
||||
/*
|
||||
y = margin;
|
||||
FOR_EACH(v, viewers) {
|
||||
StyleP s = v->getStyle();
|
||||
ValueEditor* e = v->getEditor();
|
||||
s->top = y;
|
||||
s->width = s->width - wxSystemSettings::GetMetric(wxSYS_VSCROLL_X, this);
|
||||
if (e) e->determineSize();
|
||||
y += s->height + vspace;
|
||||
}
|
||||
*/
|
||||
// create scrollbar
|
||||
}
|
||||
}
|
||||
|
||||
void NativeLookEditor::onInit() {
|
||||
DataEditor::onInit();
|
||||
// Give viewers a chance to show/hide controls (scrollbar) when selecting other editors
|
||||
FOR_EACH_EDITOR {
|
||||
e->onShow(true);
|
||||
}
|
||||
resizeViewers();
|
||||
DataEditor::onInit();
|
||||
// Give viewers a chance to show/hide controls (scrollbar) when selecting other editors
|
||||
FOR_EACH_EDITOR {
|
||||
e->onShow(true);
|
||||
}
|
||||
resizeViewers();
|
||||
}
|
||||
|
||||
wxSize NativeLookEditor::DoGetBestSize() const {
|
||||
return wxSize(200, 200);
|
||||
return wxSize(200, 200);
|
||||
}
|
||||
void NativeLookEditor::onSize(wxSizeEvent& ev) {
|
||||
resizeViewers();
|
||||
Refresh(false);
|
||||
resizeViewers();
|
||||
Refresh(false);
|
||||
}
|
||||
void NativeLookEditor::onScroll(wxScrollWinEvent& ev) {
|
||||
if (ev.GetOrientation() == wxVERTICAL) {
|
||||
int y = GetScrollPos(wxVERTICAL);
|
||||
int page = GetClientSize().y; // view size
|
||||
// determine new y offset
|
||||
// NOTE: can't use case, these are not constants
|
||||
if (ev.GetEventType() == wxEVT_SCROLLWIN_TOP) {
|
||||
y = 0;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_BOTTOM) {
|
||||
y = numeric_limits<int>::max();
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_LINEUP) {
|
||||
y = y - 10;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN) {
|
||||
y = y + 10;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_PAGEUP) {
|
||||
y = y - page;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_PAGEDOWN) {
|
||||
y = y + page;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_THUMBTRACK ||
|
||||
ev.GetEventType() == wxEVT_SCROLLWIN_THUMBRELEASE) {
|
||||
y = ev.GetPosition();
|
||||
}
|
||||
scrollTo(wxVERTICAL, y);
|
||||
}
|
||||
if (ev.GetOrientation() == wxVERTICAL) {
|
||||
int y = GetScrollPos(wxVERTICAL);
|
||||
int page = GetClientSize().y; // view size
|
||||
// determine new y offset
|
||||
// NOTE: can't use case, these are not constants
|
||||
if (ev.GetEventType() == wxEVT_SCROLLWIN_TOP) {
|
||||
y = 0;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_BOTTOM) {
|
||||
y = numeric_limits<int>::max();
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_LINEUP) {
|
||||
y = y - 10;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN) {
|
||||
y = y + 10;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_PAGEUP) {
|
||||
y = y - page;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_PAGEDOWN) {
|
||||
y = y + page;
|
||||
} else if (ev.GetEventType() == wxEVT_SCROLLWIN_THUMBTRACK ||
|
||||
ev.GetEventType() == wxEVT_SCROLLWIN_THUMBRELEASE) {
|
||||
y = ev.GetPosition();
|
||||
}
|
||||
scrollTo(wxVERTICAL, y);
|
||||
}
|
||||
}
|
||||
void NativeLookEditor::onMouseWheel(wxMouseEvent& ev) {
|
||||
// send scroll event to field under cursor
|
||||
FOR_EACH_EDITOR_REVERSE { // find high z index fields first
|
||||
RealPoint pos = mousePoint(ev, *v);
|
||||
if (v->containsPoint(pos) && v->getField()->editable) {
|
||||
bool scrolled = e->onMouseWheel(pos, ev);
|
||||
if (scrolled) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// scroll entire window
|
||||
int toScroll = 10 * ev.GetWheelRotation() * ev.GetLinesPerAction() / ev.GetWheelDelta(); // note: up is positive
|
||||
int y = GetScrollPos(wxVERTICAL);
|
||||
scrollTo(wxVERTICAL, y - toScroll);
|
||||
// send scroll event to field under cursor
|
||||
FOR_EACH_EDITOR_REVERSE { // find high z index fields first
|
||||
RealPoint pos = mousePoint(ev, *v);
|
||||
if (v->containsPoint(pos) && v->getField()->editable) {
|
||||
bool scrolled = e->onMouseWheel(pos, ev);
|
||||
if (scrolled) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// scroll entire window
|
||||
int toScroll = 10 * ev.GetWheelRotation() * ev.GetLinesPerAction() / ev.GetWheelDelta(); // note: up is positive
|
||||
int y = GetScrollPos(wxVERTICAL);
|
||||
scrollTo(wxVERTICAL, y - toScroll);
|
||||
}
|
||||
|
||||
void NativeLookEditor::scrollTo(int direction, int pos) {
|
||||
if (direction == wxVERTICAL) {
|
||||
int y = GetScrollPos(wxVERTICAL);
|
||||
int height = GetVirtualSize().y; // height
|
||||
int page = GetClientSize().y; // view size
|
||||
int bottom = max(0, height - page);
|
||||
pos = max(0, min(bottom, pos));
|
||||
if (pos != y) {
|
||||
SetScrollPos(wxVERTICAL, pos);
|
||||
if (direction == wxVERTICAL) {
|
||||
int y = GetScrollPos(wxVERTICAL);
|
||||
int height = GetVirtualSize().y; // height
|
||||
int page = GetClientSize().y; // view size
|
||||
int bottom = max(0, height - page);
|
||||
pos = max(0, min(bottom, pos));
|
||||
if (pos != y) {
|
||||
SetScrollPos(wxVERTICAL, pos);
|
||||
|
||||
// move child controls
|
||||
FOR_EACH(v, viewers) {
|
||||
ValueEditor* e = v->getEditor();
|
||||
if (e) e->determineSize();
|
||||
}
|
||||
}
|
||||
// redraw
|
||||
onChange();
|
||||
}
|
||||
// move child controls
|
||||
FOR_EACH(v, viewers) {
|
||||
ValueEditor* e = v->getEditor();
|
||||
if (e) e->determineSize();
|
||||
}
|
||||
}
|
||||
// redraw
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(NativeLookEditor, DataEditor)
|
||||
EVT_SIZE (NativeLookEditor::onSize)
|
||||
EVT_SCROLLWIN (NativeLookEditor::onScroll)
|
||||
EVT_MOUSEWHEEL (NativeLookEditor::onMouseWheel)
|
||||
EVT_SIZE (NativeLookEditor::onSize)
|
||||
EVT_SCROLLWIN (NativeLookEditor::onScroll)
|
||||
EVT_MOUSEWHEEL (NativeLookEditor::onMouseWheel)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : SetInfoEditor
|
||||
|
||||
SetInfoEditor::SetInfoEditor(Window* parent, int id, long style)
|
||||
: NativeLookEditor(parent, id, style)
|
||||
: NativeLookEditor(parent, id, style)
|
||||
{}
|
||||
|
||||
void SetInfoEditor::onChangeSet() {
|
||||
setStyles(set->stylesheet, set->stylesheet->set_info_style);
|
||||
setData(set->data);
|
||||
setStyles(set->stylesheet, set->stylesheet->set_info_style);
|
||||
setData(set->data);
|
||||
}
|
||||
|
||||
Package& SetInfoEditor::getStylePackage() const {
|
||||
return DataEditor::getStylePackage();
|
||||
// TODO: Use the game
|
||||
//return getGame();
|
||||
return DataEditor::getStylePackage();
|
||||
// TODO: Use the game
|
||||
//return getGame();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : StylingEditor
|
||||
|
||||
StylingEditor::StylingEditor(Window* parent, int id, long style)
|
||||
: NativeLookEditor(parent, id, style)
|
||||
: NativeLookEditor(parent, id, style)
|
||||
{}
|
||||
|
||||
void StylingEditor::showStylesheet(const StyleSheetP& stylesheet) {
|
||||
setStyles(stylesheet, stylesheet->styling_style);
|
||||
setData(set->stylingDataFor(*stylesheet));
|
||||
setStyles(stylesheet, stylesheet->styling_style);
|
||||
setData(set->stylingDataFor(*stylesheet));
|
||||
}
|
||||
void StylingEditor::showCard(const CardP& card) {
|
||||
StyleSheetP stylesheet = set->stylesheetForP(card);
|
||||
setStyles(stylesheet, stylesheet->styling_style);
|
||||
setData(set->stylingDataFor(card));
|
||||
StyleSheetP stylesheet = set->stylesheetForP(card);
|
||||
setStyles(stylesheet, stylesheet->styling_style);
|
||||
setData(set->stylingDataFor(card));
|
||||
}
|
||||
|
||||
void StylingEditor::onChangeSet() {
|
||||
showStylesheet(set->stylesheet);
|
||||
showStylesheet(set->stylesheet);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : ExportOptionsEditor
|
||||
|
||||
ExportOptionsEditor::ExportOptionsEditor(Window* parent, int id, long style)
|
||||
: NativeLookEditor(parent, id, style)
|
||||
: NativeLookEditor(parent, id, style)
|
||||
{}
|
||||
|
||||
void ExportOptionsEditor::showExport(const ExportTemplateP& export_template) {
|
||||
this->export_template = export_template;
|
||||
setStyles(set->stylesheet, export_template->option_style);
|
||||
setData(settings.exportOptionsFor(*export_template));
|
||||
this->export_template = export_template;
|
||||
setStyles(set->stylesheet, export_template->option_style);
|
||||
setData(settings.exportOptionsFor(*export_template));
|
||||
}
|
||||
|
||||
Package& ExportOptionsEditor::getStylePackage() const {
|
||||
return *export_template;
|
||||
return *export_template;
|
||||
}
|
||||
|
||||
@@ -19,35 +19,35 @@ DECLARE_POINTER_TYPE(ExportTemplate);
|
||||
/// A data editor with a platform native look
|
||||
class NativeLookEditor : public DataEditor {
|
||||
public:
|
||||
NativeLookEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Uses a native look
|
||||
virtual bool nativeLook() const { return true; }
|
||||
virtual Rotation getRotation() const;
|
||||
|
||||
virtual void draw(DC& dc);
|
||||
virtual void drawViewer(RotatedDC& dc, ValueViewer& v);
|
||||
|
||||
NativeLookEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Uses a native look
|
||||
virtual bool nativeLook() const { return true; }
|
||||
virtual Rotation getRotation() const;
|
||||
|
||||
virtual void draw(DC& dc);
|
||||
virtual void drawViewer(RotatedDC& dc, ValueViewer& v);
|
||||
|
||||
protected:
|
||||
// Best size doesn't really matter, as long as it is not too small
|
||||
virtual wxSize DoGetBestSize() const;
|
||||
virtual void onInit();
|
||||
|
||||
// Best size doesn't really matter, as long as it is not too small
|
||||
virtual wxSize DoGetBestSize() const;
|
||||
virtual void onInit();
|
||||
|
||||
private:
|
||||
static const int margin = 6;
|
||||
static const int margin_left = 4;
|
||||
static const int vspace = 10;
|
||||
static const int label_margin = 10;
|
||||
int label_width;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onSize(wxSizeEvent&);
|
||||
void onScroll(wxScrollWinEvent&);
|
||||
void onMouseWheel(wxMouseEvent&);
|
||||
void scrollTo(int direction, int pos);
|
||||
/// Resize the viewers so they match with this control
|
||||
void resizeViewers();
|
||||
static const int margin = 6;
|
||||
static const int margin_left = 4;
|
||||
static const int vspace = 10;
|
||||
static const int label_margin = 10;
|
||||
int label_width;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onSize(wxSizeEvent&);
|
||||
void onScroll(wxScrollWinEvent&);
|
||||
void onMouseWheel(wxMouseEvent&);
|
||||
void scrollTo(int direction, int pos);
|
||||
/// Resize the viewers so they match with this control
|
||||
void resizeViewers();
|
||||
};
|
||||
|
||||
|
||||
@@ -56,11 +56,11 @@ class NativeLookEditor : public DataEditor {
|
||||
/// Editor for set.data
|
||||
class SetInfoEditor : public NativeLookEditor {
|
||||
public:
|
||||
SetInfoEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
virtual Package& getStylePackage() const;
|
||||
SetInfoEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
virtual Package& getStylePackage() const;
|
||||
protected:
|
||||
virtual void onChangeSet();
|
||||
virtual void onChangeSet();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : StylingEditor
|
||||
@@ -68,14 +68,14 @@ class SetInfoEditor : public NativeLookEditor {
|
||||
/// Editor for styling data
|
||||
class StylingEditor : public NativeLookEditor {
|
||||
public:
|
||||
StylingEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Show the styling for given stylesheet in the editor
|
||||
void showStylesheet(const StyleSheetP& stylesheet);
|
||||
/// Show the styling for given card
|
||||
void showCard(const CardP& card);
|
||||
StylingEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Show the styling for given stylesheet in the editor
|
||||
void showStylesheet(const StyleSheetP& stylesheet);
|
||||
/// Show the styling for given card
|
||||
void showCard(const CardP& card);
|
||||
protected:
|
||||
virtual void onChangeSet();
|
||||
virtual void onChangeSet();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : ExportOptionsEditor
|
||||
@@ -83,14 +83,14 @@ class StylingEditor : public NativeLookEditor {
|
||||
/// Editor for export options
|
||||
class ExportOptionsEditor : public NativeLookEditor {
|
||||
public:
|
||||
ExportOptionsEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Show the options for given export template
|
||||
void showExport(const ExportTemplateP& export_template);
|
||||
|
||||
virtual Package& getStylePackage() const;
|
||||
ExportOptionsEditor(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Show the options for given export template
|
||||
void showExport(const ExportTemplateP& export_template);
|
||||
|
||||
virtual Package& getStylePackage() const;
|
||||
private:
|
||||
ExportTemplateP export_template;
|
||||
ExportTemplateP export_template;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -17,97 +17,97 @@ DECLARE_TYPEOF_COLLECTION(PackagedP);
|
||||
// ----------------------------------------------------------------------------- : PackageList
|
||||
|
||||
PackageList::PackageList(Window* parent, int id, int direction, bool always_focused)
|
||||
: GalleryList(parent, id, direction, always_focused)
|
||||
: GalleryList(parent, id, direction, always_focused)
|
||||
{
|
||||
item_size = subcolumns[0].size = wxSize(125, 150);
|
||||
SetThemeEnabled(true);
|
||||
item_size = subcolumns[0].size = wxSize(125, 150);
|
||||
SetThemeEnabled(true);
|
||||
}
|
||||
|
||||
size_t PackageList::itemCount() const {
|
||||
return packages.size();
|
||||
return packages.size();
|
||||
}
|
||||
|
||||
void PackageList::drawItem(DC& dc, int x, int y, size_t item) {
|
||||
dc.SetClippingRegion(x+1, y+2, item_size.x-2, item_size.y-2);
|
||||
PackageData& d = packages.at(item);
|
||||
RealRect rect(RealPoint(x,y),item_size);
|
||||
RealPoint pos;
|
||||
int w, h;
|
||||
// draw image
|
||||
if (d.image.Ok()) {
|
||||
dc.DrawBitmap(d.image, x + int(align_delta_x(ALIGN_CENTER, item_size.x, d.image.GetWidth())), y + 3, true);
|
||||
}
|
||||
// draw short name
|
||||
dc.SetFont(wxFont(12,wxSWISS,wxNORMAL,wxBOLD,false,_("Arial")));
|
||||
dc.GetTextExtent(capitalize(d.package->short_name), &w, &h);
|
||||
pos = align_in_rect(ALIGN_CENTER, RealSize(w,h), rect);
|
||||
dc.DrawText(capitalize(d.package->short_name), max(x+1,(int)pos.x), (int)pos.y + 110);
|
||||
// draw name
|
||||
dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||
dc.GetTextExtent(d.package->full_name, &w, &h);
|
||||
RealPoint text_pos = align_in_rect(ALIGN_CENTER, RealSize(w,h), rect);
|
||||
dc.DrawText(d.package->full_name, max(x+1,(int)text_pos.x), (int)text_pos.y + 130);
|
||||
dc.DestroyClippingRegion();
|
||||
dc.SetClippingRegion(x+1, y+2, item_size.x-2, item_size.y-2);
|
||||
PackageData& d = packages.at(item);
|
||||
RealRect rect(RealPoint(x,y),item_size);
|
||||
RealPoint pos;
|
||||
int w, h;
|
||||
// draw image
|
||||
if (d.image.Ok()) {
|
||||
dc.DrawBitmap(d.image, x + int(align_delta_x(ALIGN_CENTER, item_size.x, d.image.GetWidth())), y + 3, true);
|
||||
}
|
||||
// draw short name
|
||||
dc.SetFont(wxFont(12,wxSWISS,wxNORMAL,wxBOLD,false,_("Arial")));
|
||||
dc.GetTextExtent(capitalize(d.package->short_name), &w, &h);
|
||||
pos = align_in_rect(ALIGN_CENTER, RealSize(w,h), rect);
|
||||
dc.DrawText(capitalize(d.package->short_name), max(x+1,(int)pos.x), (int)pos.y + 110);
|
||||
// draw name
|
||||
dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||
dc.GetTextExtent(d.package->full_name, &w, &h);
|
||||
RealPoint text_pos = align_in_rect(ALIGN_CENTER, RealSize(w,h), rect);
|
||||
dc.DrawText(d.package->full_name, max(x+1,(int)text_pos.x), (int)text_pos.y + 130);
|
||||
dc.DestroyClippingRegion();
|
||||
}
|
||||
|
||||
struct PackageList::ComparePackagePosHint {
|
||||
bool operator () (const PackageData& a, const PackageData& b) {
|
||||
// use position_hints to determine order
|
||||
if (a.package->position_hint < b.package->position_hint) return true;
|
||||
if (a.package->position_hint > b.package->position_hint) return false;
|
||||
// ensure a deterministic order: use the names
|
||||
return a.package->name() < b.package->name();
|
||||
}
|
||||
bool operator () (const PackageData& a, const PackageData& b) {
|
||||
// use position_hints to determine order
|
||||
if (a.package->position_hint < b.package->position_hint) return true;
|
||||
if (a.package->position_hint > b.package->position_hint) return false;
|
||||
// ensure a deterministic order: use the names
|
||||
return a.package->name() < b.package->name();
|
||||
}
|
||||
};
|
||||
|
||||
void PackageList::showData(const String& pattern) {
|
||||
// clear
|
||||
packages.clear();
|
||||
// find matching packages
|
||||
vector<PackagedP> matching;
|
||||
{
|
||||
PROFILER(_("find matching packages"));
|
||||
package_manager.findMatching(pattern, matching);
|
||||
}
|
||||
FOR_EACH(p, matching) {
|
||||
// open image
|
||||
PROFILER(_("load package image"));
|
||||
InputStreamP stream = p->openIconFile();
|
||||
Image img;
|
||||
Bitmap bmp;
|
||||
if (stream && img.LoadFile(*stream)) {
|
||||
bmp = Bitmap(img);
|
||||
}
|
||||
// add to list
|
||||
packages.push_back(PackageData(p, bmp));
|
||||
}
|
||||
// sort list
|
||||
sort(packages.begin(), packages.end(), ComparePackagePosHint());
|
||||
// update list
|
||||
update();
|
||||
// clear
|
||||
packages.clear();
|
||||
// find matching packages
|
||||
vector<PackagedP> matching;
|
||||
{
|
||||
PROFILER(_("find matching packages"));
|
||||
package_manager.findMatching(pattern, matching);
|
||||
}
|
||||
FOR_EACH(p, matching) {
|
||||
// open image
|
||||
PROFILER(_("load package image"));
|
||||
InputStreamP stream = p->openIconFile();
|
||||
Image img;
|
||||
Bitmap bmp;
|
||||
if (stream && img.LoadFile(*stream)) {
|
||||
bmp = Bitmap(img);
|
||||
}
|
||||
// add to list
|
||||
packages.push_back(PackageData(p, bmp));
|
||||
}
|
||||
// sort list
|
||||
sort(packages.begin(), packages.end(), ComparePackagePosHint());
|
||||
// update list
|
||||
update();
|
||||
}
|
||||
|
||||
void PackageList::clear() {
|
||||
packages.clear();
|
||||
update();
|
||||
packages.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
void PackageList::select(const String& name, bool send_event) {
|
||||
for (vector<PackageData>::const_iterator it = packages.begin() ; it != packages.end() ; ++it) {
|
||||
if (it->package->name() == name) {
|
||||
subcolumns[0].selection = it - packages.begin();
|
||||
update();
|
||||
if (send_event) {
|
||||
sendEvent(EVENT_GALLERY_SELECT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
subcolumns[0].selection = NO_SELECTION;
|
||||
update();
|
||||
return;
|
||||
for (vector<PackageData>::const_iterator it = packages.begin() ; it != packages.end() ; ++it) {
|
||||
if (it->package->name() == name) {
|
||||
subcolumns[0].selection = it - packages.begin();
|
||||
update();
|
||||
if (send_event) {
|
||||
sendEvent(EVENT_GALLERY_SELECT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
subcolumns[0].selection = NO_SELECTION;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
int PackageList::requiredWidth() const {
|
||||
return (item_size.x + SPACING) * (int)itemCount();
|
||||
return (item_size.x + SPACING) * (int)itemCount();
|
||||
}
|
||||
|
||||
@@ -20,58 +20,58 @@ DECLARE_POINTER_TYPE(Packaged);
|
||||
/// A list of Packages of a specific type
|
||||
class PackageList : public GalleryList {
|
||||
public:
|
||||
PackageList(Window* parent, int id, int direction = wxHORIZONTAL, bool always_focused = true);
|
||||
|
||||
/// Shows packages that match a specific patern, and that are of the given type
|
||||
template <typename T>
|
||||
void showData(const String& pattern = _("*")) {
|
||||
showData(pattern + _(".mse-") + T::typeNameStatic());
|
||||
}
|
||||
|
||||
/// Shows packages that match a specific patern
|
||||
void showData(const String& pattern = _("*"));
|
||||
|
||||
/// Clears this list
|
||||
void clear();
|
||||
|
||||
/// Get the selected package, T should be the same type used for showData
|
||||
/** @pre hasSelection()
|
||||
* Throws if the selection is not of type T */
|
||||
template <typename T>
|
||||
intrusive_ptr<T> getSelection(bool load_fully = true) const {
|
||||
intrusive_ptr<T> ret = dynamic_pointer_cast<T>(packages.at(getSelectionId()).package);
|
||||
if (!ret) throw InternalError(_("PackageList: Selected package has the wrong type"));
|
||||
if (load_fully) ret->loadFully();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Select the package with the given name, if it is not found, selects nothing
|
||||
void select(const String& name, bool send_event = true);
|
||||
|
||||
/// Required width to show all items
|
||||
int requiredWidth() const;
|
||||
using GalleryList::column_count;
|
||||
|
||||
PackageList(Window* parent, int id, int direction = wxHORIZONTAL, bool always_focused = true);
|
||||
|
||||
/// Shows packages that match a specific patern, and that are of the given type
|
||||
template <typename T>
|
||||
void showData(const String& pattern = _("*")) {
|
||||
showData(pattern + _(".mse-") + T::typeNameStatic());
|
||||
}
|
||||
|
||||
/// Shows packages that match a specific patern
|
||||
void showData(const String& pattern = _("*"));
|
||||
|
||||
/// Clears this list
|
||||
void clear();
|
||||
|
||||
/// Get the selected package, T should be the same type used for showData
|
||||
/** @pre hasSelection()
|
||||
* Throws if the selection is not of type T */
|
||||
template <typename T>
|
||||
intrusive_ptr<T> getSelection(bool load_fully = true) const {
|
||||
intrusive_ptr<T> ret = dynamic_pointer_cast<T>(packages.at(getSelectionId()).package);
|
||||
if (!ret) throw InternalError(_("PackageList: Selected package has the wrong type"));
|
||||
if (load_fully) ret->loadFully();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Select the package with the given name, if it is not found, selects nothing
|
||||
void select(const String& name, bool send_event = true);
|
||||
|
||||
/// Required width to show all items
|
||||
int requiredWidth() const;
|
||||
using GalleryList::column_count;
|
||||
|
||||
protected:
|
||||
/// Draw an item
|
||||
virtual void drawItem(DC& dc, int x, int y, size_t item);
|
||||
/// Return how many items there are in the list
|
||||
virtual size_t itemCount() const;
|
||||
|
||||
/// Draw an item
|
||||
virtual void drawItem(DC& dc, int x, int y, size_t item);
|
||||
/// Return how many items there are in the list
|
||||
virtual size_t itemCount() const;
|
||||
|
||||
private:
|
||||
// The default icon to use
|
||||
// wxIcon default_icon;
|
||||
|
||||
// Information about a package
|
||||
struct PackageData {
|
||||
PackageData() {}
|
||||
PackageData(const PackagedP& package, const Bitmap& image) : package(package), image(image) {}
|
||||
PackagedP package;
|
||||
Bitmap image;
|
||||
};
|
||||
struct ComparePackagePosHint;
|
||||
/// The displayed packages
|
||||
vector<PackageData> packages;
|
||||
// The default icon to use
|
||||
// wxIcon default_icon;
|
||||
|
||||
// Information about a package
|
||||
struct PackageData {
|
||||
PackageData() {}
|
||||
PackageData(const PackagedP& package, const Bitmap& image) : package(package), image(image) {}
|
||||
PackagedP package;
|
||||
Bitmap image;
|
||||
};
|
||||
struct ComparePackagePosHint;
|
||||
/// The displayed packages
|
||||
vector<PackageData> packages;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
@@ -17,104 +17,104 @@ DECLARE_TYPEOF_COLLECTION(CardP);
|
||||
// ----------------------------------------------------------------------------- : SelectCardList
|
||||
|
||||
SelectCardList::SelectCardList(Window* parent, int id, long additional_style)
|
||||
: CardListBase(parent, id, additional_style)
|
||||
: CardListBase(parent, id, additional_style)
|
||||
{
|
||||
// create image list
|
||||
wxImageList* il = new wxImageList(15,15);
|
||||
il->Add(load_resource_image(_("sort_asc")), Color(255,0,255));
|
||||
il->Add(load_resource_image(_("sort_desc")), Color(255,0,255));
|
||||
il->Add(load_resource_image(_("deselected")));
|
||||
il->Add(load_resource_image(_("selected")));
|
||||
AssignImageList(il, wxIMAGE_LIST_SMALL);
|
||||
// create image list
|
||||
wxImageList* il = new wxImageList(15,15);
|
||||
il->Add(load_resource_image(_("sort_asc")), Color(255,0,255));
|
||||
il->Add(load_resource_image(_("sort_desc")), Color(255,0,255));
|
||||
il->Add(load_resource_image(_("deselected")));
|
||||
il->Add(load_resource_image(_("selected")));
|
||||
AssignImageList(il, wxIMAGE_LIST_SMALL);
|
||||
}
|
||||
SelectCardList::~SelectCardList() {}
|
||||
|
||||
void SelectCardList::selectAll() {
|
||||
FOR_EACH_CONST(c, set->cards) {
|
||||
selected.insert(c);
|
||||
}
|
||||
Refresh(false);
|
||||
FOR_EACH_CONST(c, set->cards) {
|
||||
selected.insert(c);
|
||||
}
|
||||
Refresh(false);
|
||||
}
|
||||
void SelectCardList::selectNone() {
|
||||
selected.clear();
|
||||
Refresh(false);
|
||||
selected.clear();
|
||||
Refresh(false);
|
||||
}
|
||||
bool SelectCardList::isSelected(const CardP& card) const {
|
||||
return selected.find(card) != selected.end();
|
||||
return selected.find(card) != selected.end();
|
||||
}
|
||||
|
||||
void SelectCardList::getSelection(vector<CardP>& out) const {
|
||||
FOR_EACH_CONST(card, set->cards) {
|
||||
if (isSelected(card)) out.push_back(card);
|
||||
}
|
||||
FOR_EACH_CONST(card, set->cards) {
|
||||
if (isSelected(card)) out.push_back(card);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectCardList::setSelection(const vector<CardP>& cards) {
|
||||
selected.clear();
|
||||
copy(cards.begin(), cards.end(), inserter(selected, selected.begin()));
|
||||
selected.clear();
|
||||
copy(cards.begin(), cards.end(), inserter(selected, selected.begin()));
|
||||
}
|
||||
|
||||
|
||||
void SelectCardList::onChangeSet() {
|
||||
CardListBase::onChangeSet();
|
||||
// init selected list: select all
|
||||
selected.clear();
|
||||
selectAll();
|
||||
CardListBase::onChangeSet();
|
||||
// init selected list: select all
|
||||
selected.clear();
|
||||
selectAll();
|
||||
}
|
||||
|
||||
int SelectCardList::OnGetItemImage(long pos) const {
|
||||
return isSelected(getCard(pos)) ? 3 : 2;
|
||||
return isSelected(getCard(pos)) ? 3 : 2;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Events
|
||||
|
||||
void SelectCardList::toggle(const CardP& card) {
|
||||
if (isSelected(card)) {
|
||||
selected.erase(card);
|
||||
} else {
|
||||
selected.insert(card);
|
||||
}
|
||||
if (isSelected(card)) {
|
||||
selected.erase(card);
|
||||
} else {
|
||||
selected.insert(card);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectCardList::onKeyDown(wxKeyEvent& ev) {
|
||||
if (selected_item_pos == -1 || !selected_item) {
|
||||
// no selection
|
||||
ev.Skip();
|
||||
return;
|
||||
}
|
||||
switch (ev.GetKeyCode()) {
|
||||
case WXK_SPACE: {
|
||||
toggle(getCard());
|
||||
RefreshItem(selected_item_pos);
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_ADD: case '+': {
|
||||
selected.insert(getCard());
|
||||
RefreshItem(selected_item_pos);
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_SUBTRACT: case '-': {
|
||||
selected.erase(getCard());
|
||||
RefreshItem(selected_item_pos);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ev.Skip();
|
||||
}
|
||||
if (selected_item_pos == -1 || !selected_item) {
|
||||
// no selection
|
||||
ev.Skip();
|
||||
return;
|
||||
}
|
||||
switch (ev.GetKeyCode()) {
|
||||
case WXK_SPACE: {
|
||||
toggle(getCard());
|
||||
RefreshItem(selected_item_pos);
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_ADD: case '+': {
|
||||
selected.insert(getCard());
|
||||
RefreshItem(selected_item_pos);
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_SUBTRACT: case '-': {
|
||||
selected.erase(getCard());
|
||||
RefreshItem(selected_item_pos);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ev.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
void SelectCardList::onLeftDown(wxMouseEvent& ev) {
|
||||
int flags;
|
||||
long item = HitTest(wxPoint(ev.GetX(), ev.GetY()), flags);
|
||||
if (flags == wxLIST_HITTEST_ONITEMICON) {
|
||||
// only clicking the icon toggles
|
||||
toggle(getCard(item));
|
||||
RefreshItem(item);
|
||||
}
|
||||
ev.Skip();
|
||||
int flags;
|
||||
long item = HitTest(wxPoint(ev.GetX(), ev.GetY()), flags);
|
||||
if (flags == wxLIST_HITTEST_ONITEMICON) {
|
||||
// only clicking the icon toggles
|
||||
toggle(getCard(item));
|
||||
RefreshItem(item);
|
||||
}
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(SelectCardList, CardListBase)
|
||||
EVT_KEY_DOWN (SelectCardList::onKeyDown)
|
||||
EVT_LEFT_DOWN (SelectCardList::onLeftDown)
|
||||
EVT_KEY_DOWN (SelectCardList::onKeyDown)
|
||||
EVT_LEFT_DOWN (SelectCardList::onLeftDown)
|
||||
END_EVENT_TABLE ()
|
||||
|
||||
@@ -18,31 +18,31 @@
|
||||
/// A card list with check boxes
|
||||
class SelectCardList : public CardListBase {
|
||||
public:
|
||||
SelectCardList(Window* parent, int id, long additional_style = 0);
|
||||
~SelectCardList();
|
||||
/// Select all cards
|
||||
void selectAll();
|
||||
/// Deselect all cards
|
||||
void selectNone();
|
||||
/// Is the given card selected?
|
||||
bool isSelected(const CardP& card) const;
|
||||
/// Get a list of all selected cards
|
||||
void getSelection(vector<CardP>& out) const;
|
||||
/// Change which cards are selected
|
||||
void setSelection(const vector<CardP>& cards);
|
||||
|
||||
SelectCardList(Window* parent, int id, long additional_style = 0);
|
||||
~SelectCardList();
|
||||
/// Select all cards
|
||||
void selectAll();
|
||||
/// Deselect all cards
|
||||
void selectNone();
|
||||
/// Is the given card selected?
|
||||
bool isSelected(const CardP& card) const;
|
||||
/// Get a list of all selected cards
|
||||
void getSelection(vector<CardP>& out) const;
|
||||
/// Change which cards are selected
|
||||
void setSelection(const vector<CardP>& cards);
|
||||
|
||||
protected:
|
||||
virtual int OnGetItemImage(long pos) const;
|
||||
virtual void onChangeSet();
|
||||
virtual int OnGetItemImage(long pos) const;
|
||||
virtual void onChangeSet();
|
||||
private:
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
std::set<CardP> selected; ///< which cards are selected?
|
||||
|
||||
void toggle(const CardP& card);
|
||||
|
||||
void onKeyDown(wxKeyEvent&);
|
||||
void onLeftDown(wxMouseEvent&);
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
std::set<CardP> selected; ///< which cards are selected?
|
||||
|
||||
void toggle(const CardP& card);
|
||||
|
||||
void onKeyDown(wxKeyEvent&);
|
||||
void onLeftDown(wxMouseEvent&);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -18,118 +18,118 @@ DECLARE_TYPEOF_COLLECTION(ValueViewerP);
|
||||
// ----------------------------------------------------------------------------- : TextCtrl
|
||||
|
||||
TextCtrl::TextCtrl(Window* parent, int id, bool multi_line, long style)
|
||||
: DataEditor(parent, id, style)
|
||||
, multi_line(multi_line)
|
||||
: DataEditor(parent, id, style)
|
||||
, multi_line(multi_line)
|
||||
{}
|
||||
TextCtrl::~TextCtrl() {}
|
||||
|
||||
Rotation TextCtrl::getRotation() const {
|
||||
return Rotation(0, RealRect(RealPoint(0,0),GetClientSize()));
|
||||
return Rotation(0, RealRect(RealPoint(0,0),GetClientSize()));
|
||||
}
|
||||
|
||||
void TextCtrl::draw(DC& dc) {
|
||||
RotatedDC rdc(dc, getRotation(), QUALITY_LOW);
|
||||
if (viewers.empty() || !static_cast<FakeTextValue&>(*viewers.front()->getValue()).editable) {
|
||||
DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
|
||||
} else {
|
||||
DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
}
|
||||
RotatedDC rdc(dc, getRotation(), QUALITY_LOW);
|
||||
if (viewers.empty() || !static_cast<FakeTextValue&>(*viewers.front()->getValue()).editable) {
|
||||
DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
|
||||
} else {
|
||||
DataViewer::draw(rdc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
}
|
||||
}
|
||||
|
||||
bool TextCtrl::AcceptsFocus() const {
|
||||
return wxWindow::AcceptsFocus() &&
|
||||
!viewers.empty() &&
|
||||
static_cast<FakeTextValue&>(*viewers.front()->getValue()).editable;
|
||||
return wxWindow::AcceptsFocus() &&
|
||||
!viewers.empty() &&
|
||||
static_cast<FakeTextValue&>(*viewers.front()->getValue()).editable;
|
||||
}
|
||||
|
||||
|
||||
TextStyle& TextCtrl::getStyle() {
|
||||
assert(!viewers.empty());
|
||||
return static_cast<TextStyle&>(*viewers.front()->getStyle());
|
||||
assert(!viewers.empty());
|
||||
return static_cast<TextStyle&>(*viewers.front()->getStyle());
|
||||
}
|
||||
TextField& TextCtrl::getField() {
|
||||
assert(!viewers.empty());
|
||||
return static_cast<TextField&>(*viewers.front()->getField());
|
||||
assert(!viewers.empty());
|
||||
return static_cast<TextField&>(*viewers.front()->getField());
|
||||
}
|
||||
TextFieldP TextCtrl::getFieldP() {
|
||||
assert(!viewers.empty());
|
||||
return static_pointer_cast<TextField>(viewers.front()->getField());
|
||||
assert(!viewers.empty());
|
||||
return static_pointer_cast<TextField>(viewers.front()->getField());
|
||||
}
|
||||
void TextCtrl::updateSize() {
|
||||
wxSize cs = GetClientSize();
|
||||
Style& style = getStyle();
|
||||
style.width = cs.GetWidth() - 2;
|
||||
style.height = cs.GetHeight() - 2;
|
||||
viewers.front()->getEditor()->determineSize(true);
|
||||
wxSize cs = GetClientSize();
|
||||
Style& style = getStyle();
|
||||
style.width = cs.GetWidth() - 2;
|
||||
style.height = cs.GetHeight() - 2;
|
||||
viewers.front()->getEditor()->determineSize(true);
|
||||
}
|
||||
|
||||
void TextCtrl::setValue(String* value, bool untagged) {
|
||||
setValue(intrusive(new FakeTextValue(getFieldP(), value, true, untagged)));
|
||||
setValue(intrusive(new FakeTextValue(getFieldP(), value, true, untagged)));
|
||||
}
|
||||
void TextCtrl::setValue(const FakeTextValueP& value) {
|
||||
value->retrieve();
|
||||
viewers.front()->setValue(value);
|
||||
updateSize();
|
||||
onChange();
|
||||
value->retrieve();
|
||||
viewers.front()->setValue(value);
|
||||
updateSize();
|
||||
onChange();
|
||||
}
|
||||
|
||||
void TextCtrl::onChangeSet() {
|
||||
DataEditor::onChangeSet();
|
||||
// initialize
|
||||
if (viewers.empty()) {
|
||||
// create a field, style and value
|
||||
TextFieldP field(new TextField);
|
||||
TextStyleP style(new TextStyle(field));
|
||||
TextValueP value(new FakeTextValue(field, nullptr, false, false));
|
||||
// set stuff
|
||||
field->index = 0;
|
||||
field->multi_line = multi_line;
|
||||
style->width = 100;
|
||||
style->height = 20;
|
||||
style->left = 1;
|
||||
style->top = 1;
|
||||
style->font.color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
// assign to this control
|
||||
IndexMap<FieldP,StyleP> styles; styles.add(field, style);
|
||||
IndexMap<FieldP,ValueP> values; values.add(field, value);
|
||||
setStyles(set->stylesheet, styles);
|
||||
setData(values);
|
||||
updateSize();
|
||||
onChange();
|
||||
} else {
|
||||
setValue(nullptr);
|
||||
}
|
||||
// select the one and only editor
|
||||
current_viewer = viewers.front().get();
|
||||
current_editor = current_viewer->getEditor();
|
||||
DataEditor::onChangeSet();
|
||||
// initialize
|
||||
if (viewers.empty()) {
|
||||
// create a field, style and value
|
||||
TextFieldP field(new TextField);
|
||||
TextStyleP style(new TextStyle(field));
|
||||
TextValueP value(new FakeTextValue(field, nullptr, false, false));
|
||||
// set stuff
|
||||
field->index = 0;
|
||||
field->multi_line = multi_line;
|
||||
style->width = 100;
|
||||
style->height = 20;
|
||||
style->left = 1;
|
||||
style->top = 1;
|
||||
style->font.color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
// assign to this control
|
||||
IndexMap<FieldP,StyleP> styles; styles.add(field, style);
|
||||
IndexMap<FieldP,ValueP> values; values.add(field, value);
|
||||
setStyles(set->stylesheet, styles);
|
||||
setData(values);
|
||||
updateSize();
|
||||
onChange();
|
||||
} else {
|
||||
setValue(nullptr);
|
||||
}
|
||||
// select the one and only editor
|
||||
current_viewer = viewers.front().get();
|
||||
current_editor = current_viewer->getEditor();
|
||||
}
|
||||
|
||||
void TextCtrl::onInit() {
|
||||
// Give viewers a chance to show/hide controls (scrollbar) when selecting other editors
|
||||
FOR_EACH_EDITOR {
|
||||
e->onShow(true);
|
||||
}
|
||||
// also init the DataEditor
|
||||
DataEditor::onInit();
|
||||
// Give viewers a chance to show/hide controls (scrollbar) when selecting other editors
|
||||
FOR_EACH_EDITOR {
|
||||
e->onShow(true);
|
||||
}
|
||||
// also init the DataEditor
|
||||
DataEditor::onInit();
|
||||
}
|
||||
|
||||
void TextCtrl::onSize(wxSizeEvent&) {
|
||||
if (!viewers.empty()) {
|
||||
updateSize();
|
||||
onChange();
|
||||
}
|
||||
if (!viewers.empty()) {
|
||||
updateSize();
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
wxSize TextCtrl::DoGetBestSize() const {
|
||||
if (multi_line || viewers.empty()) {
|
||||
// flexible size
|
||||
return wxSize(1,1);
|
||||
} else {
|
||||
wxSize ws = GetSize(), cs = GetClientSize();
|
||||
Style& style = *viewers.front()->getStyle();
|
||||
return wxSize(style.width, style.height) + ws - cs;
|
||||
}
|
||||
if (multi_line || viewers.empty()) {
|
||||
// flexible size
|
||||
return wxSize(1,1);
|
||||
} else {
|
||||
wxSize ws = GetSize(), cs = GetClientSize();
|
||||
Style& style = *viewers.front()->getStyle();
|
||||
return wxSize(style.width, style.height) + ws - cs;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_EVENT_TABLE(TextCtrl, DataEditor)
|
||||
EVT_SIZE (TextCtrl::onSize)
|
||||
EVT_SIZE (TextCtrl::onSize)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
@@ -29,45 +29,45 @@ DECLARE_POINTER_TYPE(FakeTextValue);
|
||||
*/
|
||||
class TextCtrl : public DataEditor {
|
||||
public:
|
||||
TextCtrl(Window* parent, int id, bool multi_line, long style = wxBORDER_THEME);
|
||||
~TextCtrl();
|
||||
|
||||
/// Set the value that is being edited
|
||||
/** value can be a nullptr*/
|
||||
void setValue(String* value, bool untagged = false);
|
||||
/// Set the value that is being edited
|
||||
void setValue(const FakeTextValueP& value);
|
||||
|
||||
/// Update the size, for example after changing the style
|
||||
void updateSize();
|
||||
|
||||
/// Get access to the field used by the control
|
||||
TextField& getField();
|
||||
/// Get access to the field used by the control
|
||||
TextFieldP getFieldP();
|
||||
/// Get access to the style used by the control
|
||||
TextStyle& getStyle();
|
||||
|
||||
/// Uses a native look
|
||||
virtual bool nativeLook() const { return true; }
|
||||
virtual Rotation getRotation() const;
|
||||
|
||||
virtual void draw(DC& dc);
|
||||
|
||||
virtual bool AcceptsFocus() const;
|
||||
|
||||
virtual void onChangeSet();
|
||||
|
||||
TextCtrl(Window* parent, int id, bool multi_line, long style = wxBORDER_THEME);
|
||||
~TextCtrl();
|
||||
|
||||
/// Set the value that is being edited
|
||||
/** value can be a nullptr*/
|
||||
void setValue(String* value, bool untagged = false);
|
||||
/// Set the value that is being edited
|
||||
void setValue(const FakeTextValueP& value);
|
||||
|
||||
/// Update the size, for example after changing the style
|
||||
void updateSize();
|
||||
|
||||
/// Get access to the field used by the control
|
||||
TextField& getField();
|
||||
/// Get access to the field used by the control
|
||||
TextFieldP getFieldP();
|
||||
/// Get access to the style used by the control
|
||||
TextStyle& getStyle();
|
||||
|
||||
/// Uses a native look
|
||||
virtual bool nativeLook() const { return true; }
|
||||
virtual Rotation getRotation() const;
|
||||
|
||||
virtual void draw(DC& dc);
|
||||
|
||||
virtual bool AcceptsFocus() const;
|
||||
|
||||
virtual void onChangeSet();
|
||||
|
||||
protected:
|
||||
virtual void onInit();
|
||||
virtual wxSize DoGetBestSize() const;
|
||||
|
||||
virtual void onInit();
|
||||
virtual wxSize DoGetBestSize() const;
|
||||
|
||||
private:
|
||||
bool multi_line; ///< Multi line text control?
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onSize(wxSizeEvent&);
|
||||
bool multi_line; ///< Multi line text control?
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onSize(wxSizeEvent&);
|
||||
};
|
||||
|
||||
|
||||
|
||||
+247
-247
@@ -17,265 +17,265 @@ DECLARE_TYPEOF_COLLECTION(TreeList::ItemP);
|
||||
// ----------------------------------------------------------------------------- : TreeList : item managment
|
||||
|
||||
void TreeList::rebuild(bool full) {
|
||||
if (full) initItems();
|
||||
calcItemCount();
|
||||
UpdateScrollbar();
|
||||
Refresh(false);
|
||||
if (full) initItems();
|
||||
calcItemCount();
|
||||
UpdateScrollbar();
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
bool TreeList::hasChildren(size_t item) const {
|
||||
return item + 1 < items.size() && items[item]->level < items[item+1]->level;
|
||||
return item + 1 < items.size() && items[item]->level < items[item+1]->level;
|
||||
}
|
||||
|
||||
void TreeList::expand(size_t item, bool expand) {
|
||||
if (hasChildren(item) && items[item]->expanded != expand) {
|
||||
items[item]->expanded = expand;
|
||||
rebuild(false);
|
||||
}
|
||||
if (hasChildren(item) && items[item]->expanded != expand) {
|
||||
items[item]->expanded = expand;
|
||||
rebuild(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TreeList::expandAll(bool expand) {
|
||||
for (size_t item = 0 ; item < items.size() ; ++item) {
|
||||
if (hasChildren(item)) {
|
||||
items[item]->expanded = expand;
|
||||
}
|
||||
}
|
||||
rebuild(false);
|
||||
for (size_t item = 0 ; item < items.size() ; ++item) {
|
||||
if (hasChildren(item)) {
|
||||
items[item]->expanded = expand;
|
||||
}
|
||||
}
|
||||
rebuild(false);
|
||||
}
|
||||
|
||||
void TreeList::select(size_t item, bool event) {
|
||||
if (item >= items.size() || selection == item) return;
|
||||
// select
|
||||
size_t oldpos = selection < items.size() ? items[selection]->position : 0;
|
||||
selection = item;
|
||||
size_t pos = items[selection]->position;
|
||||
// event
|
||||
if (event) {
|
||||
wxCommandEvent ev(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
|
||||
ProcessEvent(ev);
|
||||
}
|
||||
// redraw
|
||||
if (pos < first_line) {
|
||||
ScrollToLine(pos);
|
||||
} else if (pos >= first_line + visible_lines_t) {
|
||||
ScrollToLine(pos - visible_lines_t + 1);
|
||||
} else {
|
||||
if (oldpos != NOTHING) RefreshLine(oldpos);
|
||||
RefreshLine(pos);
|
||||
}
|
||||
if (item >= items.size() || selection == item) return;
|
||||
// select
|
||||
size_t oldpos = selection < items.size() ? items[selection]->position : 0;
|
||||
selection = item;
|
||||
size_t pos = items[selection]->position;
|
||||
// event
|
||||
if (event) {
|
||||
wxCommandEvent ev(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
|
||||
ProcessEvent(ev);
|
||||
}
|
||||
// redraw
|
||||
if (pos < first_line) {
|
||||
ScrollToLine(pos);
|
||||
} else if (pos >= first_line + visible_lines_t) {
|
||||
ScrollToLine(pos - visible_lines_t + 1);
|
||||
} else {
|
||||
if (oldpos != NOTHING) RefreshLine(oldpos);
|
||||
RefreshLine(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void TreeList::calcItemCount() {
|
||||
// item count
|
||||
total_lines = 0;
|
||||
int visible_level = 0;
|
||||
FOR_EACH(i,items) {
|
||||
if (i->level <= visible_level) {
|
||||
i->position = total_lines;
|
||||
++total_lines;
|
||||
if (i->expanded) visible_level = i->level + 1;
|
||||
else visible_level = i->level;
|
||||
} else {
|
||||
i->position = NOTHING;
|
||||
}
|
||||
}
|
||||
// update lines
|
||||
UInt lines = 0;
|
||||
FOR_EACH_REVERSE(i,items) {
|
||||
if (i->visible()) {
|
||||
i->lines = lines;
|
||||
lines &= (1 << i->level) - 1;
|
||||
lines |= 1 << i->level;
|
||||
}
|
||||
}
|
||||
// selection hidden? move to first visible item before it
|
||||
if (selection < items.size()) {
|
||||
for ( ; selection + 1 > 0 ; --selection) {
|
||||
if (items[selection]->visible()) break; // visible
|
||||
}
|
||||
if (selection >= items.size()) selection = 0;
|
||||
}
|
||||
// item count
|
||||
total_lines = 0;
|
||||
int visible_level = 0;
|
||||
FOR_EACH(i,items) {
|
||||
if (i->level <= visible_level) {
|
||||
i->position = total_lines;
|
||||
++total_lines;
|
||||
if (i->expanded) visible_level = i->level + 1;
|
||||
else visible_level = i->level;
|
||||
} else {
|
||||
i->position = NOTHING;
|
||||
}
|
||||
}
|
||||
// update lines
|
||||
UInt lines = 0;
|
||||
FOR_EACH_REVERSE(i,items) {
|
||||
if (i->visible()) {
|
||||
i->lines = lines;
|
||||
lines &= (1 << i->level) - 1;
|
||||
lines |= 1 << i->level;
|
||||
}
|
||||
}
|
||||
// selection hidden? move to first visible item before it
|
||||
if (selection < items.size()) {
|
||||
for ( ; selection + 1 > 0 ; --selection) {
|
||||
if (items[selection]->visible()) break; // visible
|
||||
}
|
||||
if (selection >= items.size()) selection = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t TreeList::findItemByPos(int y) const {
|
||||
if (y < header_height) return false;
|
||||
return findItem(first_line + (y - header_height) / item_height);
|
||||
if (y < header_height) return false;
|
||||
return findItem(first_line + (y - header_height) / item_height);
|
||||
}
|
||||
size_t TreeList::findItem(size_t line, size_t start) const {
|
||||
for (size_t i = start ; i < items.size() ; ++i) {
|
||||
if (items[i]->visible() && items[i]->position >= line) return i;
|
||||
}
|
||||
return items.size();
|
||||
for (size_t i = start ; i < items.size() ; ++i) {
|
||||
if (items[i]->visible() && items[i]->position >= line) return i;
|
||||
}
|
||||
return items.size();
|
||||
}
|
||||
size_t TreeList::findLastItem(size_t start) const {
|
||||
for (size_t i = min(items.size(), start) - 1 ; i + 1 > 0 ; --i) {
|
||||
if (items[i]->visible()) return i;
|
||||
}
|
||||
return items.size();
|
||||
for (size_t i = min(items.size(), start) - 1 ; i + 1 > 0 ; --i) {
|
||||
if (items[i]->visible()) return i;
|
||||
}
|
||||
return items.size();
|
||||
}
|
||||
size_t TreeList::findParent(size_t start) const {
|
||||
int level = items[start]->level;
|
||||
for (size_t i = start - 1 ; i + 1 > 0 ; --i) {
|
||||
if (items[i]->visible() && items[i]->level < level) return i;
|
||||
}
|
||||
return items.size();
|
||||
int level = items[start]->level;
|
||||
for (size_t i = start - 1 ; i + 1 > 0 ; --i) {
|
||||
if (items[i]->visible() && items[i]->level < level) return i;
|
||||
}
|
||||
return items.size();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : TreeList : UI
|
||||
|
||||
TreeList::TreeList(Window* parent, int id, long style)
|
||||
: wxPanel(parent, id, wxDefaultPosition, wxDefaultSize, style | wxWANTS_CHARS | wxVSCROLL)
|
||||
, selection(NOTHING)
|
||||
, total_lines(0)
|
||||
, first_line(0)
|
||||
: wxPanel(parent, id, wxDefaultPosition, wxDefaultSize, style | wxWANTS_CHARS | wxVSCROLL)
|
||||
, selection(NOTHING)
|
||||
, total_lines(0)
|
||||
, first_line(0)
|
||||
{
|
||||
// determine item size
|
||||
wxClientDC dc(this);
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
int h;
|
||||
dc.GetTextExtent(_("X"), 0, &h);
|
||||
item_height = h + 2;
|
||||
// determine item size
|
||||
wxClientDC dc(this);
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
int h;
|
||||
dc.GetTextExtent(_("X"), 0, &h);
|
||||
item_height = h + 2;
|
||||
}
|
||||
|
||||
void TreeList::onPaint(wxPaintEvent& ev) {
|
||||
wxBufferedPaintDC dc(this);
|
||||
size_t cols = columnCount();
|
||||
wxRendererNative& rn = wxRendererNative::GetDefault();
|
||||
// clear background
|
||||
wxSize cs = GetClientSize();
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
||||
dc.DrawRectangle(0,0,cs.x,header_height);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
dc.DrawRectangle(0,header_height,cs.x,cs.y-header_height);
|
||||
// draw header
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
||||
int x = 0, y = 0;
|
||||
for (size_t j = 0 ; j < cols ; ++j) {
|
||||
int w = columnWidth(j);
|
||||
wxRect rect(x,0,w-1,header_height-1);
|
||||
rn.DrawHeaderButton(this, dc, rect);
|
||||
dc.DrawText(columnText(j),x+3,2);
|
||||
x += w;
|
||||
}
|
||||
y += header_height;
|
||||
// draw items
|
||||
size_t start = findItem(first_line);
|
||||
size_t end = findItem(first_line + visible_lines);
|
||||
for (size_t i = start ; i < end ; ++i) {
|
||||
const Item& item = *items[i];
|
||||
if (!item.visible()) continue; // invisible
|
||||
x = level_width * (item.level + 1);
|
||||
// line below
|
||||
dc.SetPen(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.2));
|
||||
dc.DrawLine(x,y+item_height-1,cs.x,y+item_height-1);
|
||||
// draw lines
|
||||
dc.SetPen(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.4));
|
||||
for (int j = 0 ; j < 32 && j <= item.level ; ++j) {
|
||||
if (item.lines & (1 << j)) {
|
||||
// draw line
|
||||
dc.DrawLine(8 + level_width*j,y,8 + level_width*j,y+item_height);
|
||||
}
|
||||
}
|
||||
dc.DrawLine(x-1, y + item_height/2, x - 9, y + item_height/2);
|
||||
if (!(item.lines & (1 << item.level))) {
|
||||
dc.DrawLine(8 + level_width*item.level,y,8 + level_width*item.level,y+item_height/2+1);
|
||||
}
|
||||
// draw expand button
|
||||
if (hasChildren(i)) {
|
||||
wxRect rect(x - 13, y + (item_height - 9)/2, 9, 9);
|
||||
rn.DrawTreeItemButton(this, dc, rect, item.expanded ? wxCONTROL_EXPANDED : 0);
|
||||
}
|
||||
if (selection == i) {
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
|
||||
dc.DrawRectangle(x, y, cs.x-x, item_height-1);
|
||||
}
|
||||
// draw text(s)
|
||||
for (size_t j = 0 ; j < cols ; ++j) {
|
||||
int w = columnWidth(j);
|
||||
drawItem(dc, i, j, x+1, y, selection == i);
|
||||
if (j == 0) x = 0;
|
||||
x += w;
|
||||
}
|
||||
y += item_height;
|
||||
}
|
||||
wxBufferedPaintDC dc(this);
|
||||
size_t cols = columnCount();
|
||||
wxRendererNative& rn = wxRendererNative::GetDefault();
|
||||
// clear background
|
||||
wxSize cs = GetClientSize();
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
||||
dc.DrawRectangle(0,0,cs.x,header_height);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
dc.DrawRectangle(0,header_height,cs.x,cs.y-header_height);
|
||||
// draw header
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
||||
int x = 0, y = 0;
|
||||
for (size_t j = 0 ; j < cols ; ++j) {
|
||||
int w = columnWidth(j);
|
||||
wxRect rect(x,0,w-1,header_height-1);
|
||||
rn.DrawHeaderButton(this, dc, rect);
|
||||
dc.DrawText(columnText(j),x+3,2);
|
||||
x += w;
|
||||
}
|
||||
y += header_height;
|
||||
// draw items
|
||||
size_t start = findItem(first_line);
|
||||
size_t end = findItem(first_line + visible_lines);
|
||||
for (size_t i = start ; i < end ; ++i) {
|
||||
const Item& item = *items[i];
|
||||
if (!item.visible()) continue; // invisible
|
||||
x = level_width * (item.level + 1);
|
||||
// line below
|
||||
dc.SetPen(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.2));
|
||||
dc.DrawLine(x,y+item_height-1,cs.x,y+item_height-1);
|
||||
// draw lines
|
||||
dc.SetPen(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
|
||||
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.4));
|
||||
for (int j = 0 ; j < 32 && j <= item.level ; ++j) {
|
||||
if (item.lines & (1 << j)) {
|
||||
// draw line
|
||||
dc.DrawLine(8 + level_width*j,y,8 + level_width*j,y+item_height);
|
||||
}
|
||||
}
|
||||
dc.DrawLine(x-1, y + item_height/2, x - 9, y + item_height/2);
|
||||
if (!(item.lines & (1 << item.level))) {
|
||||
dc.DrawLine(8 + level_width*item.level,y,8 + level_width*item.level,y+item_height/2+1);
|
||||
}
|
||||
// draw expand button
|
||||
if (hasChildren(i)) {
|
||||
wxRect rect(x - 13, y + (item_height - 9)/2, 9, 9);
|
||||
rn.DrawTreeItemButton(this, dc, rect, item.expanded ? wxCONTROL_EXPANDED : 0);
|
||||
}
|
||||
if (selection == i) {
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
|
||||
dc.DrawRectangle(x, y, cs.x-x, item_height-1);
|
||||
}
|
||||
// draw text(s)
|
||||
for (size_t j = 0 ; j < cols ; ++j) {
|
||||
int w = columnWidth(j);
|
||||
drawItem(dc, i, j, x+1, y, selection == i);
|
||||
if (j == 0) x = 0;
|
||||
x += w;
|
||||
}
|
||||
y += item_height;
|
||||
}
|
||||
}
|
||||
|
||||
void TreeList::onChar(wxKeyEvent& ev) {
|
||||
switch (ev.GetKeyCode()) {
|
||||
case WXK_UP: {
|
||||
select(findLastItem(selection));
|
||||
break;
|
||||
} case WXK_DOWN: {
|
||||
select(findItem(0, selection + 1));
|
||||
break;
|
||||
} case WXK_LEFT: {
|
||||
if (selection < items.size()) {
|
||||
if (hasChildren(selection) && items[selection]->expanded) {
|
||||
expand(selection, false);
|
||||
} else {
|
||||
// select parent
|
||||
select(findParent(selection));
|
||||
}
|
||||
}
|
||||
break;
|
||||
} case WXK_RIGHT: {
|
||||
if (selection < items.size() && hasChildren(selection)) {
|
||||
if (items[selection]->expanded) {
|
||||
// select first child
|
||||
select(selection+1);
|
||||
Refresh(false);
|
||||
} else {
|
||||
expand(selection, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} case WXK_PAGEUP: {
|
||||
ScrollToLine(first_line > visible_lines_t ? first_line - visible_lines_t : 0);
|
||||
break;
|
||||
} case WXK_PAGEDOWN: {
|
||||
ScrollToLine(first_line + visible_lines_t);
|
||||
break;
|
||||
} case WXK_HOME: {
|
||||
select(findItem(0));
|
||||
break;
|
||||
} case WXK_END: {
|
||||
select(findLastItem(items.size()));
|
||||
break;
|
||||
} case WXK_TAB: {
|
||||
// send a navigation event to our parent, to select another control
|
||||
// we need this because of wxWANTS_CHARS
|
||||
wxNavigationKeyEvent nev;
|
||||
nev.SetDirection(!ev.ShiftDown());
|
||||
GetParent()->HandleWindowEvent(nev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (ev.GetKeyCode()) {
|
||||
case WXK_UP: {
|
||||
select(findLastItem(selection));
|
||||
break;
|
||||
} case WXK_DOWN: {
|
||||
select(findItem(0, selection + 1));
|
||||
break;
|
||||
} case WXK_LEFT: {
|
||||
if (selection < items.size()) {
|
||||
if (hasChildren(selection) && items[selection]->expanded) {
|
||||
expand(selection, false);
|
||||
} else {
|
||||
// select parent
|
||||
select(findParent(selection));
|
||||
}
|
||||
}
|
||||
break;
|
||||
} case WXK_RIGHT: {
|
||||
if (selection < items.size() && hasChildren(selection)) {
|
||||
if (items[selection]->expanded) {
|
||||
// select first child
|
||||
select(selection+1);
|
||||
Refresh(false);
|
||||
} else {
|
||||
expand(selection, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} case WXK_PAGEUP: {
|
||||
ScrollToLine(first_line > visible_lines_t ? first_line - visible_lines_t : 0);
|
||||
break;
|
||||
} case WXK_PAGEDOWN: {
|
||||
ScrollToLine(first_line + visible_lines_t);
|
||||
break;
|
||||
} case WXK_HOME: {
|
||||
select(findItem(0));
|
||||
break;
|
||||
} case WXK_END: {
|
||||
select(findLastItem(items.size()));
|
||||
break;
|
||||
} case WXK_TAB: {
|
||||
// send a navigation event to our parent, to select another control
|
||||
// we need this because of wxWANTS_CHARS
|
||||
wxNavigationKeyEvent nev;
|
||||
nev.SetDirection(!ev.ShiftDown());
|
||||
GetParent()->HandleWindowEvent(nev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeList::onLeftDown(wxMouseEvent& ev) {
|
||||
size_t i = findItemByPos(ev.GetY());
|
||||
if (i >= items.size()) return;
|
||||
int left = items[i]->level * level_width;
|
||||
if (hasChildren(i) && ev.GetX() >= left && ev.GetX() < left + level_width) {
|
||||
expand(i, !items[i]->expanded);
|
||||
} else {
|
||||
select(i);
|
||||
}
|
||||
ev.Skip();
|
||||
size_t i = findItemByPos(ev.GetY());
|
||||
if (i >= items.size()) return;
|
||||
int left = items[i]->level * level_width;
|
||||
if (hasChildren(i) && ev.GetX() >= left && ev.GetX() < left + level_width) {
|
||||
expand(i, !items[i]->expanded);
|
||||
} else {
|
||||
select(i);
|
||||
}
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void TreeList::onLeftDClick(wxMouseEvent& ev) {
|
||||
size_t i = findItemByPos(ev.GetY());
|
||||
if (i >= items.size()) return;
|
||||
if (hasChildren(i)) {
|
||||
expand(i, !items[i]->expanded);
|
||||
}
|
||||
ev.Skip();
|
||||
size_t i = findItemByPos(ev.GetY());
|
||||
if (i >= items.size()) return;
|
||||
if (hasChildren(i)) {
|
||||
expand(i, !items[i]->expanded);
|
||||
}
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : TreeList : Copy of VScrolledWindow
|
||||
@@ -284,19 +284,19 @@ void TreeList::ScrollToLine(size_t line) {
|
||||
// determine the real first line to scroll to: we shouldn't scroll beyond the end
|
||||
line = (size_t)max((int)line, 0);
|
||||
line = (size_t)min((int)line, max(0, (int)(total_lines - visible_lines_t)));
|
||||
// nothing to do?
|
||||
// nothing to do?
|
||||
if (line == first_line) return;
|
||||
first_line = line;
|
||||
UpdateScrollbar();
|
||||
Refresh(false);
|
||||
Refresh(false);
|
||||
}
|
||||
|
||||
void TreeList::UpdateScrollbar() {
|
||||
// how many lines fit on the screen?
|
||||
int h = GetClientSize().y - header_height;
|
||||
visible_lines = (h + item_height - 1) / item_height;
|
||||
visible_lines_t = h / item_height;
|
||||
// set the scrollbar parameters to reflect this
|
||||
visible_lines = (h + item_height - 1) / item_height;
|
||||
visible_lines_t = h / item_height;
|
||||
// set the scrollbar parameters to reflect this
|
||||
SetScrollbar(wxVERTICAL, (int)first_line, (int)visible_lines_t, (int)total_lines);
|
||||
}
|
||||
|
||||
@@ -315,31 +315,31 @@ void TreeList::RefreshLine(size_t line) {
|
||||
void TreeList::onScroll(wxScrollWinEvent& ev) {
|
||||
wxEventType type = ev.GetEventType();
|
||||
if (type == wxEVT_SCROLLWIN_TOP) {
|
||||
ScrollToLine(0);
|
||||
} else if (type == wxEVT_SCROLLWIN_BOTTOM) {
|
||||
ScrollToLine(total_lines);
|
||||
} else if (type == wxEVT_SCROLLWIN_LINEUP) {
|
||||
ScrollToLine(first_line > 0 ? first_line - 1 : 0);
|
||||
} else if (type == wxEVT_SCROLLWIN_LINEDOWN) {
|
||||
ScrollToLine(first_line + 1);
|
||||
} else if (type == wxEVT_SCROLLWIN_PAGEUP) {
|
||||
ScrollToLine(first_line > visible_lines_t ? first_line - visible_lines_t : 0);
|
||||
} else if (type == wxEVT_SCROLLWIN_PAGEDOWN) {
|
||||
ScrollToLine(first_line + visible_lines_t);
|
||||
} else {
|
||||
ScrollToLine(ev.GetPosition());
|
||||
ScrollToLine(0);
|
||||
} else if (type == wxEVT_SCROLLWIN_BOTTOM) {
|
||||
ScrollToLine(total_lines);
|
||||
} else if (type == wxEVT_SCROLLWIN_LINEUP) {
|
||||
ScrollToLine(first_line > 0 ? first_line - 1 : 0);
|
||||
} else if (type == wxEVT_SCROLLWIN_LINEDOWN) {
|
||||
ScrollToLine(first_line + 1);
|
||||
} else if (type == wxEVT_SCROLLWIN_PAGEUP) {
|
||||
ScrollToLine(first_line > visible_lines_t ? first_line - visible_lines_t : 0);
|
||||
} else if (type == wxEVT_SCROLLWIN_PAGEDOWN) {
|
||||
ScrollToLine(first_line + visible_lines_t);
|
||||
} else {
|
||||
ScrollToLine(ev.GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
void TreeList::onSize(wxSizeEvent& ev) {
|
||||
UpdateScrollbar();
|
||||
Refresh(false);
|
||||
ev.Skip();
|
||||
UpdateScrollbar();
|
||||
Refresh(false);
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void TreeList::onMouseWheel(wxMouseEvent& ev) {
|
||||
int delta = -ev.GetWheelRotation() * ev.GetLinesPerAction() / ev.GetWheelDelta();
|
||||
ScrollToLine(first_line + delta);
|
||||
int delta = -ev.GetWheelRotation() * ev.GetLinesPerAction() / ev.GetWheelDelta();
|
||||
ScrollToLine(first_line + delta);
|
||||
}
|
||||
|
||||
|
||||
@@ -347,12 +347,12 @@ void TreeList::onMouseWheel(wxMouseEvent& ev) {
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(TreeList, wxPanel)
|
||||
EVT_PAINT (TreeList::onPaint)
|
||||
EVT_SIZE (TreeList::onSize)
|
||||
EVT_SCROLLWIN (TreeList::onScroll)
|
||||
EVT_CHAR (TreeList::onChar)
|
||||
EVT_LEFT_DOWN (TreeList::onLeftDown)
|
||||
EVT_LEFT_DCLICK(TreeList::onLeftDClick)
|
||||
EVT_MOUSEWHEEL (TreeList::onMouseWheel)
|
||||
EVT_PAINT (TreeList::onPaint)
|
||||
EVT_SIZE (TreeList::onSize)
|
||||
EVT_SCROLLWIN (TreeList::onScroll)
|
||||
EVT_CHAR (TreeList::onChar)
|
||||
EVT_LEFT_DOWN (TreeList::onLeftDown)
|
||||
EVT_LEFT_DCLICK(TreeList::onLeftDClick)
|
||||
EVT_MOUSEWHEEL (TreeList::onMouseWheel)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
@@ -17,90 +17,90 @@
|
||||
/// A combination of a TreeCtrl and a ListCtrl. A tree with multiple columns.
|
||||
class TreeList : public wxPanel {
|
||||
public:
|
||||
TreeList(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Expand/collapse an item
|
||||
void expand(size_t item, bool expand = true);
|
||||
/// Expand/collapse all items
|
||||
void expandAll(bool expand = true);
|
||||
/// Select an item
|
||||
void select(size_t item, bool event = true);
|
||||
|
||||
/// (re)build the list
|
||||
void rebuild(bool full = true);
|
||||
|
||||
TreeList(Window* parent, int id, long style = wxBORDER_THEME);
|
||||
|
||||
/// Expand/collapse an item
|
||||
void expand(size_t item, bool expand = true);
|
||||
/// Expand/collapse all items
|
||||
void expandAll(bool expand = true);
|
||||
/// Select an item
|
||||
void select(size_t item, bool event = true);
|
||||
|
||||
/// (re)build the list
|
||||
void rebuild(bool full = true);
|
||||
|
||||
public:
|
||||
|
||||
/// An item in the tree list
|
||||
class Item : public IntrusivePtrBase<Item> {
|
||||
public:
|
||||
Item() : level(0), expanded(false) {}
|
||||
virtual ~Item() {}
|
||||
|
||||
int level;
|
||||
bool expanded;
|
||||
inline bool visible() const { return position != NOTHING; }
|
||||
|
||||
private:
|
||||
friend class TreeList;
|
||||
size_t position; // NOTHING if invisible, otherwise the line the item is on
|
||||
UInt lines; // lines in front of this item (bit set)
|
||||
};
|
||||
typedef intrusive_ptr<Item> ItemP;
|
||||
|
||||
|
||||
/// An item in the tree list
|
||||
class Item : public IntrusivePtrBase<Item> {
|
||||
public:
|
||||
Item() : level(0), expanded(false) {}
|
||||
virtual ~Item() {}
|
||||
|
||||
int level;
|
||||
bool expanded;
|
||||
inline bool visible() const { return position != NOTHING; }
|
||||
|
||||
private:
|
||||
friend class TreeList;
|
||||
size_t position; // NOTHING if invisible, otherwise the line the item is on
|
||||
UInt lines; // lines in front of this item (bit set)
|
||||
};
|
||||
typedef intrusive_ptr<Item> ItemP;
|
||||
|
||||
protected:
|
||||
|
||||
/// The items in the tree list
|
||||
vector<ItemP> items;
|
||||
|
||||
static const size_t NOTHING = (size_t)-1;
|
||||
size_t selection;
|
||||
|
||||
/// Initialize the items
|
||||
virtual void initItems() = 0;
|
||||
|
||||
/// Draw the text of an item
|
||||
virtual void drawItem(DC& dc, size_t index, size_t column, int x, int y, bool selected) const = 0;
|
||||
|
||||
/// The number of columns
|
||||
virtual size_t columnCount() const = 0;
|
||||
/// The text of a column
|
||||
virtual String columnText(size_t column) const = 0;
|
||||
/// The width of a column in pixels
|
||||
virtual int columnWidth(size_t column) const = 0;
|
||||
|
||||
int item_height;
|
||||
static const int header_height = 17;
|
||||
static const int level_width = 17;
|
||||
|
||||
|
||||
/// The items in the tree list
|
||||
vector<ItemP> items;
|
||||
|
||||
static const size_t NOTHING = (size_t)-1;
|
||||
size_t selection;
|
||||
|
||||
/// Initialize the items
|
||||
virtual void initItems() = 0;
|
||||
|
||||
/// Draw the text of an item
|
||||
virtual void drawItem(DC& dc, size_t index, size_t column, int x, int y, bool selected) const = 0;
|
||||
|
||||
/// The number of columns
|
||||
virtual size_t columnCount() const = 0;
|
||||
/// The text of a column
|
||||
virtual String columnText(size_t column) const = 0;
|
||||
/// The width of a column in pixels
|
||||
virtual int columnWidth(size_t column) const = 0;
|
||||
|
||||
int item_height;
|
||||
static const int header_height = 17;
|
||||
static const int level_width = 17;
|
||||
|
||||
private:
|
||||
size_t total_lines; // number of shown items
|
||||
size_t first_line; // first visible line
|
||||
size_t visible_lines; // number of (partially) visible lines
|
||||
size_t visible_lines_t; // number of totally visible lines
|
||||
|
||||
void calcItemCount();
|
||||
size_t findItemByPos(int y) const;
|
||||
size_t findItem(size_t line, size_t start = 0) const;
|
||||
size_t findLastItem(size_t end) const;
|
||||
size_t findParent(size_t item) const;
|
||||
bool hasChildren(size_t item) const;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onPaint(wxPaintEvent&);
|
||||
void onChar(wxKeyEvent& ev);
|
||||
void onLeftDown(wxMouseEvent& ev);
|
||||
void onLeftDClick(wxMouseEvent& ev);
|
||||
|
||||
// VScrolledWindow clone
|
||||
|
||||
void onSize(wxSizeEvent& ev);
|
||||
void onScroll(wxScrollWinEvent& ev);
|
||||
void onMouseWheel(wxMouseEvent& ev);
|
||||
void ScrollToLine(size_t line);
|
||||
void UpdateScrollbar();
|
||||
void RefreshLine(size_t line);
|
||||
size_t total_lines; // number of shown items
|
||||
size_t first_line; // first visible line
|
||||
size_t visible_lines; // number of (partially) visible lines
|
||||
size_t visible_lines_t; // number of totally visible lines
|
||||
|
||||
void calcItemCount();
|
||||
size_t findItemByPos(int y) const;
|
||||
size_t findItem(size_t line, size_t start = 0) const;
|
||||
size_t findLastItem(size_t end) const;
|
||||
size_t findParent(size_t item) const;
|
||||
bool hasChildren(size_t item) const;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
|
||||
void onPaint(wxPaintEvent&);
|
||||
void onChar(wxKeyEvent& ev);
|
||||
void onLeftDown(wxMouseEvent& ev);
|
||||
void onLeftDClick(wxMouseEvent& ev);
|
||||
|
||||
// VScrolledWindow clone
|
||||
|
||||
void onSize(wxSizeEvent& ev);
|
||||
void onScroll(wxScrollWinEvent& ev);
|
||||
void onMouseWheel(wxMouseEvent& ev);
|
||||
void ScrollToLine(size_t line);
|
||||
void UpdateScrollbar();
|
||||
void RefreshLine(size_t line);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
Reference in New Issue
Block a user