Audacity 3.2.0
PluginDataViewCtrl.cpp
Go to the documentation of this file.
2
3#include <wx/renderer.h>
4
5#include "PluginDataModel.h"
6
7BEGIN_EVENT_TABLE(PluginDataViewCtrl, wxDataViewCtrl)
10
11#if defined(wxHAS_GENERIC_DATAVIEWCTRL) || defined(__WXGTK__)
12class PluginDataViewRenderer : public wxDataViewCustomRenderer
13{
14public:
15 using wxDataViewCustomRenderer::wxDataViewCustomRenderer;
16
17 bool ActivateCell(const wxRect& cell,
18 wxDataViewModel* model,
19 const wxDataViewItem& item,
20 unsigned col,
21 const wxMouseEvent* mouseEvent) final
22 {
23 if(mouseEvent == nullptr)
24 {
25 wxCHECK(GetView(), false);
26
27 wxVariant value;
28 model->GetValue(value, item, PluginDataModel::ColumnState);
29 value = !value.GetBool();
30
31 wxDataViewItemArray sel;
32 GetView()->GetSelections(sel);
33 if(!sel.empty())
34 {
35 for(const auto& other : sel)
36 model->ChangeValue(value, other, PluginDataModel::ColumnState);
37 }
38 else
39 model->ChangeValue(value, item, PluginDataModel::ColumnState);
40
41 #if wxUSE_ACCESSIBILITY
42 if(const auto ctrl = dynamic_cast<PluginDataViewCtrl*>(GetView()))
43 {
44 wxAccessible::NotifyEvent(
45 wxACC_EVENT_OBJECT_NAMECHANGE,
46 ctrl,
47 wxOBJID_CLIENT,
48 ctrl->GetRowByItem(item) + 1);
49 }
50 #endif
51 return true;
52 }
53 return OnCellClicked(cell, model, item, col, mouseEvent);
54 }
55
56protected:
57 virtual bool OnCellClicked(const wxRect& cell,
58 wxDataViewModel* model,
59 const wxDataViewItem& item,
60 unsigned col,
61 const wxMouseEvent* mouseEvent)
62 {
63 return false;
64 }
65};
66
68{
69 wxString mText;
70public:
71
73 : PluginDataViewRenderer("string", wxDATAVIEW_CELL_ACTIVATABLE)
74 {
75
76 }
77
78 bool SetValue(const wxVariant& value) override
79 {
80 mText = value.GetString();
81 return true;
82 }
83
84 bool GetValue(wxVariant&) const override
85 {
86 return false;
87 }
88
89 bool Render(wxRect cell, wxDC* dc, int state) override
90 {
91 if(!mText.empty())
92 RenderText(mText, 0, cell, dc, state);
93 return true;
94 }
95
96 wxSize GetSize() const override
97 {
98 if(!mText.empty())
99 return GetTextExtent(mText);
100 return GetView()->FromDIP(wxSize(wxDVC_DEFAULT_RENDERER_SIZE,
101 wxDVC_DEFAULT_RENDERER_SIZE));
102 }
103
104#if wxUSE_ACCESSIBILITY
105 wxString GetAccessibleDescription() const override
106 {
107 return mText;
108 }
109#endif
110};
111
112// Activated cell also toggles all other cells that are currently selected
114{
115 wxVariant mValue;
116public:
117
119 : PluginDataViewRenderer("bool", wxDATAVIEW_CELL_ACTIVATABLE)
120 { }
121
122 bool SetValue(const wxVariant& value) override
123 {
124 mValue = value;
125 return true;
126 }
127 bool GetValue(wxVariant&) const override
128 {
129 return false;
130 }
131 bool Render(wxRect cell, wxDC* dc, int state) override
132 {
133 if(mValue.IsNull())
134 return false;
135 //Duplicated from wxDataViewToggleRenderer
136 int flags = 0;
137 if (mValue.GetBool())
138 flags |= wxCONTROL_CHECKED;
139 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE ||
140 !(GetOwner()->GetOwner()->IsEnabled() && GetEnabled()))
141 {
142 flags |= wxCONTROL_DISABLED;
143 }
144 wxSize size = cell.GetSize();
145 size.IncTo(GetSize());
146 cell.SetSize(size);
147
148 wxRendererNative& renderer = wxRendererNative::Get();
149 wxWindow* const win = GetOwner()->GetOwner();
150 renderer.DrawCheckBox(win, *dc, cell, flags);
151 return true;
152 }
153
154 wxSize GetSize() const override
155 {
156 return wxRendererNative::Get().GetCheckBoxSize(GetView());
157 }
158
159#if wxUSE_ACCESSIBILITY
160 wxString GetAccessibleDescription() const override
161 {
162 if(!mValue.IsNull())
163 return mValue.GetBool() ? _("Enabled") : _("Disabled");
164 return {};
165 }
166#endif
167
168private:
169 bool OnCellClicked(const wxRect&,
170 wxDataViewModel* model,
171 const wxDataViewItem& item,
172 unsigned col,
173 const wxMouseEvent*) override
174 {
175 if(!mValue.IsNull())
176 {
177 model->ChangeValue(!mValue.GetBool(), item, col);
178 return true;
179 }
180 return false;
181 }
182};
183#else
184class PluginDataViewStateRenderer final : public wxDataViewToggleRenderer
185{
186public:
188 : wxDataViewToggleRenderer(GetDefaultType(), wxDATAVIEW_CELL_ACTIVATABLE, wxDVR_DEFAULT_ALIGNMENT)
189 { }
190};
191
192class PluginDataViewTextRenderer final : public wxDataViewTextRenderer
193{
194public:
196 : wxDataViewTextRenderer(GetDefaultType(), wxDATAVIEW_CELL_INERT, wxDVR_DEFAULT_ALIGNMENT)
197 { }
198};
199
200#endif
201
202#if defined(wxHAS_GENERIC_DATAVIEWCTRL) && wxUSE_ACCESSIBILITY
203class PluginsDataViewCtrlAx final : public wxAccessible
204{
205public:
206 PluginsDataViewCtrlAx(PluginDataViewCtrl* parent)
207 : wxAccessible(parent)
208 {
209 }
210
211 wxAccStatus GetChild(int childId, wxAccessible** accessible) override
212 {
213 *accessible = childId == wxACC_SELF ? this : nullptr;
214 return wxACC_OK;
215 }
216
217 wxAccStatus GetChildCount(int* count) override
218 {
219 if(const auto model = GetModel())
220 *count = model->GetRowCount();
221 else
222 *count = 0;
223
224 return wxACC_OK;
225 }
226
227 wxAccStatus GetName(int childId, wxString* name) override
228 {
229 const auto ctrl = GetCtrl();
230 wxCHECK(ctrl, wxACC_FAIL);
231
232 name->clear();
233
234 if(childId == wxACC_SELF)
235 {
236 *name = ctrl->GetName();
237 return wxACC_OK;
238 }
239
240 const auto model = GetModel();
241 if(model == nullptr)
242 return wxACC_INVALID_ARG;
243
244 const auto item = ctrl->GetItemByRow(childId - 1);
245 if(!item.IsOk())
246 return wxACC_INVALID_ARG;
247
248 const auto plugin = model->GetPlugin(item);
249 if(plugin == nullptr)
250 return wxACC_INVALID_ARG;
251
252 wxVariant state;
253 model->GetValue(state, item, PluginDataModel::ColumnState);
254
255 //separate type and path parts with comma so that
256 //screen reader would make a short pause between them
257 *name = wxString::Format("%s %s %s, %s",
258 plugin->GetPluginType() == PluginTypeEffect
259 ? plugin->GetSymbol().Translation()
260 : wxFileName(plugin->GetPath()).GetName(),
261 state.GetBool()
262 ? _("Enabled")
263 : _("Disabled"),
264 plugin->GetEffectFamily(),
265 plugin->GetPath());
266
267 return wxACC_OK;
268 }
269
270 wxAccStatus GetFocus(int* childId, wxAccessible** accessible) override
271 {
272 const auto ctrl = GetCtrl();
273 wxCHECK(ctrl, wxACC_FAIL);
274
275 *childId = wxACC_SELF;
276 *accessible = nullptr;
277
278 const auto currentItem = ctrl->GetCurrentItem();
279 if(!currentItem.IsOk())
280 return wxACC_OK;
281
282 *childId = ctrl->GetRowByItem(currentItem) + 1;
283
284 return wxACC_OK;
285 }
286
287 wxAccStatus GetLocation(wxRect& rect, int childId) override
288 {
289 const auto ctrl = GetCtrl();
290 wxCHECK(ctrl, wxACC_FAIL);
291
292 if(childId == wxACC_SELF)
293 {
294 rect = ctrl->GetScreenRect();
295 return wxACC_OK;
296 }
297
298 const auto item = ctrl->GetItemByRow(childId - 1);
299 if (!item.IsOk())
300 return wxACC_INVALID_ARG;
301
302 rect = ctrl->GetItemRect(item, nullptr);
303 // Indentation and expander column should be included here and therefore
304 // reported row width should by the same as the width of the client area.
305 rect.width += rect.x;
306 rect.x = 0;
307 rect.SetPosition(ctrl->ClientToScreen(rect.GetPosition()));
308
309 return wxACC_OK;
310 }
311
312 wxAccStatus GetDefaultAction(int childId, wxString* action) override
313 {
314 const auto ctrl = GetCtrl();
315 wxCHECK(ctrl, wxACC_FAIL);
316
317 action->clear();
318
319 if(childId == wxACC_SELF)
320 return wxACC_OK;
321
322 const auto model = GetModel();
323 if(model == nullptr)
324 return wxACC_INVALID_ARG;
325
326 const auto item = ctrl->GetItemByRow(childId - 1);
327
328 if(!item.IsOk())
329 return wxACC_INVALID_ARG;
330
331 wxVariant enabled;
332 model->GetValue(enabled, item, PluginDataModel::ColumnState);
333 *action = enabled.GetBool()
334 ? _("Disable")
335 : _("Enable");
336
337 return wxACC_OK;
338 }
339
340 wxAccStatus DoDefaultAction(int childId) override
341 {
342 const auto ctrl = GetCtrl();
343 wxCHECK(ctrl, wxACC_FAIL);
344
345 if(childId == wxACC_SELF)
346 return wxACC_NOT_SUPPORTED;
347
348 const auto model = GetModel();
349 if(model == nullptr)
350 return wxACC_NOT_SUPPORTED;
351
352 const auto item = ctrl->GetItemByRow(childId - 1);
353 if(!item.IsOk())
354 return wxACC_INVALID_ARG;
355
356 wxVariant enabled;
357 model->GetValue(enabled, item, PluginDataModel::ColumnState);
358 model->SetValue(wxVariant(!enabled.GetBool()), item, PluginDataModel::ColumnState);
359
360 return wxACC_OK;
361 }
362
363 wxAccStatus GetSelections(wxVariant* selections) override
364 {
365 const auto ctrl = GetCtrl();
366 wxCHECK(ctrl, wxACC_FAIL);
367
368 wxDataViewItemArray sel;
369 ctrl->GetSelections(sel);
370
371 if(sel.IsEmpty())
372 {
373 selections->MakeNull();
374 return wxACC_OK;
375 }
376
377 if(sel.size() == 1)
378 {
379 const auto row = ctrl->GetRowByItem(sel[0]);
380 *selections = static_cast<wxLongLong>(row + 1);
381 }
382 else
383 {
384 wxVariant list(wxVariantList{});
385 for(size_t i = 0; i < sel.GetCount(); ++i)
386 {
387 const auto row = ctrl->GetRowByItem(sel[i]);
388 list.Append(wxVariant(static_cast<wxLongLong>(row + 1)));
389 }
390 *selections = list;
391 }
392 return wxACC_OK;
393 }
394
395 wxAccStatus GetState(int childId, long* state) override
396 {
397 const auto ctrl = GetCtrl();
398 wxCHECK(ctrl, wxACC_FAIL);
399
400 if(childId == wxACC_SELF)
401 {
402 *state = wxACC_STATE_SYSTEM_FOCUSABLE;
403 return wxACC_OK;
404 }
405
406 const auto model = GetModel();
407 if(model == nullptr)
408 return wxACC_INVALID_ARG;
409
410 *state = wxACC_STATE_SYSTEM_FOCUSABLE | wxACC_STATE_SYSTEM_SELECTABLE
411 | wxACC_STATE_SYSTEM_MULTISELECTABLE | wxACC_STATE_SYSTEM_EXTSELECTABLE;
412
413 const auto row = childId - 1;
414 if(row < ctrl->GetFirstVisibleRow() || row > ctrl->GetLastVisibleRow())
415 *state |= wxACC_STATE_SYSTEM_OFFSCREEN;
416
417 if(ctrl->IsSelected(ctrl->GetCurrentItem()))
418 *state |= wxACC_STATE_SYSTEM_FOCUSED;
419 if(ctrl->IsSelected(ctrl->GetItemByRow(row)))
420 *state |= wxACC_STATE_SYSTEM_SELECTED;
421
422 return wxACC_OK;
423 }
424
425 wxAccStatus GetRole(int childId, wxAccRole* role) override
426 {
427 *role = childId == wxACC_SELF
428 ? wxROLE_SYSTEM_LIST
429 : wxROLE_SYSTEM_LISTITEM;
430 return wxACC_OK;
431 }
432
433private:
434
435 PluginDataViewCtrl* GetCtrl()
436 {
437 return wxDynamicCast(GetWindow(), PluginDataViewCtrl);
438 }
439
440 PluginDataModel* GetModel()
441 {
442 if(const auto ctrl = GetCtrl())
443 return dynamic_cast<PluginDataModel*>(ctrl->GetModel());
444 return nullptr;
445 }
446};
447
448#endif
449
451{
452#if defined(wxHAS_GENERIC_DATAVIEWCTRL)
453 //Do not allow wxDataViewMainWindow handle TAB key...
454 GetChildren()[0]->Bind(wxEVT_CHAR_HOOK, &PluginDataViewCtrl::OnTableCharHook, this);
455#elif defined(__WXOSX__)
456 //Implements group toggle behaviour when multiple
457 //lines are selected and Enter key pressed.
458 Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [=](wxDataViewEvent& evt) {
459 evt.Skip();
460 const auto item = evt.GetItem();
461 if(!item.IsOk())
462 return;
463
464 const auto model = GetModel();
465 if(model == nullptr)
466 return;
467
468 wxVariant value;
469 model->GetValue(value, item, PluginDataModel::ColumnState);
470 if(value.IsNull())
471 return;
472
473 value = !value.GetBool();
474
475 wxDataViewItemArray sel;
476 GetSelections(sel);
477 if(!sel.empty())
478 {
479 for(const auto& item : sel)
480 model->ChangeValue(value, item, PluginDataModel::ColumnState);
481 }
482 else
483 model->ChangeValue(value, item, PluginDataModel::ColumnState);
484 evt.Skip(false);
485 });
486#endif
487 AppendColumn(safenew wxDataViewColumn(
490 wxDVC_DEFAULT_WIDTH,
491 wxALIGN_NOT,
492 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE)
493 );
494 AppendColumn(safenew wxDataViewColumn(
497 wxDVC_DEFAULT_WIDTH,
498 wxALIGN_NOT,
499 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE)
500 );
501 AppendColumn(safenew wxDataViewColumn(
504 wxDVC_DEFAULT_WIDTH,
505 wxALIGN_NOT,
506 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE)
507 );
508 AppendColumn(safenew wxDataViewColumn(
509 _("Enabled"),
512 wxDVC_DEFAULT_WIDTH,
513 wxALIGN_CENTER,
514 wxDATAVIEW_COL_SORTABLE
515 ));
516}
517
519{
520 evt.Skip();
521#if defined(wxHAS_GENERIC_DATAVIEWCTRL)
522 //
523 if(evt.GetKeyCode() == WXK_CONTROL_A &&
524 evt.GetModifiers() | WXK_COMMAND)
525 {
526 SelectAll();
527 evt.Skip(false);
528 return;
529 }
530 if(evt.GetKeyCode() == WXK_SPACE ||
531 evt.GetKeyCode() == WXK_RETURN ||
532 evt.GetKeyCode() == WXK_NUMPAD_ENTER)
533 {
534 return;
535 }
536
537 const auto ch = evt.GetUnicodeKey();
538 if(ch == WXK_NONE)
539 return;
540
541 auto item = GetCurrentItem();
542 if(!item.IsOk())
543 return;
544
545 //if item.IsOk(), then there is at least 1 item in the table
546 const auto searchStartRow = GetRowByItem(item);
547 auto row = searchStartRow + 1;
548 while(true)
549 {
550 item = GetItemByRow(row);
551
552 if(!item.IsOk())
553 {
554 if(row > searchStartRow)
555 {
556 //start from the beginning, until we reach
557 //searchStartRow again
558 row = 0;
559 continue;
560 }
561 break;
562 }
563
564 wxVariant data;
565 GetModel()->GetValue(data, item, PluginDataModel::ColumnName);
566 const auto name = data.GetString();
567 if(!name.empty() && name.Left(1).IsSameAs(ch, false))
568 {
569 wxDataViewItemArray sel;
570 sel.push_back(item);
571 SetSelections(sel);
572 SetCurrentItem(sel[0]);
573 EnsureVisible(sel[0]);
574 break;
575 }
576 if(row == searchStartRow)
577 break;
578 ++row;
579 }
580 evt.Skip(false);
581#else
582 evt.Skip();
583#endif
584}
585
586#if defined(wxHAS_GENERIC_DATAVIEWCTRL)
587
588int PluginDataViewCtrl::GetFirstVisibleRow() const
589{
590 return GetRowAt({0, 0});
591}
592
593int PluginDataViewCtrl::GetLastVisibleRow() const
594{
595 const auto size = GetClientSize();
596 return GetRowAt({ 0, size.y - 1 });
597}
598
599int PluginDataViewCtrl::GetRowAt(const wxPoint& point) const
600{
601 wxDataViewItem item;
602 wxDataViewColumn* column;
603 HitTest(point, item, column);
604 return item.IsOk()
605 ? GetRowByItem(item)
606 : 0;
607}
608#endif
609
610#if wxUSE_ACCESSIBILITY
611wxAccessible* PluginDataViewCtrl::CreateAccessible()
612{
613#if defined(wxHAS_GENERIC_DATAVIEWCTRL)
614 return safenew PluginsDataViewCtrlAx(this);
615#else
616 return wxDataViewCtrl::CreateAccessible();
617#endif
618}
619#endif
620
621#if defined(wxHAS_GENERIC_DATAVIEWCTRL)
622void PluginDataViewCtrl::OnTableCharHook(wxKeyEvent& evt)
623{
624 if(evt.GetKeyCode() == WXK_TAB)
625 GetParent()->NavigateIn((evt.GetModifiers() & WXK_COMMAND) == 0);
626 else
627 evt.Skip(evt.GetKeyCode() != WXK_RIGHT && evt.GetKeyCode() != WXK_LEFT);
628}
629
631{
632 if(!GetCurrentItem().IsOk())
633 {
634 wxDataViewItemArray sel;
635 sel.push_back(GetItemByRow(0));
636 if(sel[0].IsOk())
637 {
638 SetCurrentItem(sel[0]);
639 SetSelections(sel);
640 }
641 }
643#if wxUSE_ACCESSIBILITY
644 if(GetCurrentItem().IsOk())
645 {
646 wxAccessible::NotifyEvent(
647 wxACC_EVENT_OBJECT_FOCUS,
648 this,
649 wxOBJID_CLIENT,
650 GetRowByItem(GetCurrentItem()) + 1
651 );
652 wxAccessible::NotifyEvent(
653 wxACC_EVENT_OBJECT_SELECTIONWITHIN,
654 this,
655 wxOBJID_CLIENT,
656 wxACC_SELF
657 );
658 }
659#endif
660}
661#endif
662
END_EVENT_TABLE()
const TranslatableString name
Definition: Distortion.cpp:76
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:10
@ PluginTypeEffect
!brief A plugins list model that can be attached to wxDataViewCtrl
void OnCharEvent(wxKeyEvent &evt)
friend class PluginsDataViewCtrlAx
virtual bool OnCellClicked(const wxRect &cell, wxDataViewModel *model, const wxDataViewItem &item, unsigned col, const wxMouseEvent *mouseEvent)
bool ActivateCell(const wxRect &cell, wxDataViewModel *model, const wxDataViewItem &item, unsigned col, const wxMouseEvent *mouseEvent) final
wxSize GetSize() const override
bool SetValue(const wxVariant &value) override
bool OnCellClicked(const wxRect &, wxDataViewModel *model, const wxDataViewItem &item, unsigned col, const wxMouseEvent *) override
bool Render(wxRect cell, wxDC *dc, int state) override
bool GetValue(wxVariant &) const override
bool Render(wxRect cell, wxDC *dc, int state) override
bool GetValue(wxVariant &) const override
bool SetValue(const wxVariant &value) override
wxSize GetSize() const override
void SetFocus(const WindowPlacement &focus)
Set the window that accepts keyboard input.
Definition: BasicUI.h:384
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:200
bool HitTest(const RectangleArgs &args, const wxPoint &mousePos)