Change tabs to two spaces.

This commit is contained in:
Lymia Aluysia
2017-01-18 08:43:21 -06:00
parent d7f5f0dc3b
commit d2c635f739
329 changed files with 41307 additions and 41496 deletions
+285 -285
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+100 -100
View File
@@ -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 ()
+32 -32
View File
@@ -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
+85 -85
View File
@@ -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 ()
+37 -37
View File
@@ -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
+88 -88
View File
@@ -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()
+34 -34
View File
@@ -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
+9 -9
View File
@@ -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);
}
}
+12 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+211 -211
View File
@@ -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
+77 -77
View File
@@ -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);
}
}
+26 -26
View File
@@ -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
View File
@@ -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 ()
+83 -83
View File
@@ -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
View File
@@ -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 ()
+70 -70
View File
@@ -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
+158 -158
View File
@@ -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;
}
+45 -45
View File
@@ -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
+71 -71
View File
@@ -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();
}
+50 -50
View File
@@ -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
+64 -64
View File
@@ -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 ()
+23 -23
View File
@@ -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&);
};
+75 -75
View File
@@ -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()
+37 -37
View File
@@ -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
View File
@@ -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()
+81 -81
View File
@@ -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