Logo ROOT  
Reference Guide
REveSelection.cxx
Go to the documentation of this file.
1// @(#)root/eve7:$Id$
2// Author: Matevz Tadel 2007
3
4/*************************************************************************
5 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
14#include <ROOT/REveCompound.hxx>
15#include <ROOT/REveManager.hxx>
17
18#include "TClass.h"
19#include "TColor.h"
20
21#include <iostream>
22#include <regex>
23
24#include <nlohmann/json.hpp>
25
26using namespace ROOT::Experimental;
27namespace REX = ROOT::Experimental;
28
29/** \class REveSelection
30\ingroup REve
31Make sure there is a SINGLE running REveSelection for each
32selection type (select/highlight).
33*/
34
35////////////////////////////////////////////////////////////////////////////////
36/// Constructor.
37
38REveSelection::REveSelection(const std::string& n, const std::string& t,
39 Color_t col_visible, Color_t col_hidden) :
40 REveElement (n, t),
41 fVisibleEdgeColor (col_visible),
42 fHiddenEdgeColor (col_hidden),
43 fActive (kTRUE),
44 fIsMaster (kTRUE)
45{
46 // Managing complete selection state on element level.
47 //
48 // Method pointers for propagation of selected / implied selected state
49 // to elements. This has to be done differently now -- and kept within
50 // REveSelection.
51 //
52 // Also, see REveManager::PreDeleteElement. We might need some sort of
53 // implied-selected-count after all (global, for all selections,
54 // highlights) ... and traverse all selections if the element gets zapped.
55 // Yup, we have it ...
56 // XXXX but ... we can also go up to master and check there directly !!!!!
57
61}
62
63////////////////////////////////////////////////////////////////////////////////
64/// Destructor
65
67{
70}
71
72////////////////////////////////////////////////////////////////////////////////
73/// Set visible highlight color
74
76{
79}
80
81////////////////////////////////////////////////////////////////////////////////
82/// Set hidden highlight color
84{
87}
88
89////////////////////////////////////////////////////////////////////////////////
90/// Set to 'highlight' mode.
91
93{
94 // Most importantly, this sets the pointers-to-function-members in
95 // REveElement that are used to mark elements as (un)selected and
96 // implied-(un)selected.
97
99}
100
101////////////////////////////////////////////////////////////////////////////////
102/// Select element indicated by the entry and fill its
103/// implied-selected set.
104
106{
107 Set_t &imp_set = entry->second.f_implied;
108
109 entry->first->FillImpliedSelectedSet(imp_set);
110
111 auto i = imp_set.begin();
112 while (i != imp_set.end())
113 {
114 if ((*i)->GetElementId() == 0)
115 {
116 if (gDebug > 0)
117 {
118 Info("REveSelection::DoElementSelect",
119 "Element '%s' [%s] with 0 id detected and removed.",
120 (*i)->GetCName(), (*i)->IsA()->GetName());
121 }
122 auto j = i++;
123 imp_set.erase(j);
124 }
125 else
126 {
127 (*i)->IncImpliedSelected();
128 ++i;
129 }
130 }
131}
132
133////////////////////////////////////////////////////////////////////////////////
134/// Deselect element indicated by the entry and clear its
135/// implied-selected set.
136
138{
139 Set_t &imp_set = entry->second.f_implied;
140
141 for (auto &imp_el: imp_set) imp_el->DecImpliedSelected();
142
143 imp_set.clear();
144}
145
146////////////////////////////////////////////////////////////////////////////////
147/// Check if elemenet el is selected (not implied selected).
148
150{
151 return fMap.find(el) != fMap.end();
152}
153
154////////////////////////////////////////////////////////////////////////////////
155/// Check if any elements are selected.
156
158{
159 return ! fMap.empty();
160}
161
162////////////////////////////////////////////////////////////////////////////////
163/// Pre-addition check. Deny addition if el is already selected.
164/// Virtual from REveAunt.
165
167{
168 return el != this && fMap.find(el) == fMap.end() &&
169 el->IsA()->InheritsFrom(TClass::GetClass<REveSelection>()) == kFALSE;
170}
171
172////////////////////////////////////////////////////////////////////////////////
173/// Add an element into selection, virtual from REveAunt
174
176{
177 auto res = fMap.emplace(el, Record(el));
178 if (fActive) {
179 DoElementSelect(res.first);
180 SelectionAdded(el);
181 }
183}
184
185////////////////////////////////////////////////////////////////////////////////
186/// Virtual from REveAunt.
187
189{
190 auto i = fMap.find(el);
191
192 if (i != fMap.end())
193 {
194 if (fActive)
195 {
198 }
199 fMap.erase(i);
201 }
202 else
203 {
204 Warning("REveSelection::RemoveNieceLocal", "element not found in map.");
205 }
206}
207
208////////////////////////////////////////////////////////////////////////////////
209/// Add an element into selection, virtual from REveAunt.
210/// Overriden here just so that a signal can be emitted.
211
213{
214 if (IsEmpty()) return;
215
216 for (auto i = fMap.begin(); i != fMap.end(); ++i)
217 {
218 i->first->RemoveAunt(this);
220 }
221 fMap.clear();
224}
225
226////////////////////////////////////////////////////////////////////////////////
227/// Remove element from all implied-selected sets.
228///
229/// This is called as part of the element destruction from
230/// REveManager::PreDeleteElement() and should not be called
231/// directly.
232
234{
235 bool changed = false;
236
237 for (auto &i : fMap)
238 {
239 auto j = i.second.f_implied.find(el);
240 if (j != i.second.f_implied.end())
241 {
242 i.second.f_implied.erase(j);
243 changed = true;
244 }
245 }
246
247 if (changed) StampObjPropsPreChk();
248}
249
250////////////////////////////////////////////////////////////////////////////////
251/// Recalculate implied-selected state for given selection entry.
252/// Add new elements to implied-selected set and increase their
253/// implied-selected count.
254
256{
257 bool changed = false;
258 Set_t set;
259 smi->first->FillImpliedSelectedSet(set);
260 for (auto &i: set)
261 {
262 if (smi->second.f_implied.find(i) == smi->second.f_implied.end())
263 {
264 smi->second.f_implied.insert(i);
265 i->IncImpliedSelected();
266 changed = true;
267 }
268 }
269
270 if (changed) StampObjPropsPreChk();
271}
272
273////////////////////////////////////////////////////////////////////////////////
274/// If given element is selected or implied-selected within this
275/// selection then recheck implied-set for given selection entry.
276
278{
279 // Top-level selected.
280 {
281 auto i = fMap.find(el);
282 if (i != fMap.end())
284 }
285
286 // Implied selected (we can not tell if by this selection or some other),
287 // then we need to loop over all.
288 if (el->GetImpliedSelected() > 0)
289 {
290 for (auto i = fMap.begin(); i != fMap.end(); ++i)
291 {
292 if (i->second.f_implied.find(el) != i->second.f_implied.end())
294 }
295 }
296}
297
298////////////////////////////////////////////////////////////////////////////////
299/// Emit SelectionAdded signal.
300
302{
303 // XXXX
304 // Emit("SelectionAdded(REveElement*)", (Long_t)el);
305}
306
307////////////////////////////////////////////////////////////////////////////////
308/// Emit SelectionRemoved signal.
309
311{
312 // XXXX
313 // Emit("SelectionRemoved(REveElement*)", (Long_t)el);
314}
315
316////////////////////////////////////////////////////////////////////////////////
317/// Emit SelectionCleared signal.
318
320{
321 // XXXX
322 // Emit("SelectionCleared()");
323}
324
325////////////////////////////////////////////////////////////////////////////////
326/// Emit SelectionRepeated signal.
327
329{
330 // XXXX
331 // Emit("SelectionRepeated(REveElement*)", (Long_t)el);
332}
333
334////////////////////////////////////////////////////////////////////////////////
335/// Activate this selection.
336
338{
339 if (fActive) return;
340
341 fActive = kTRUE;
342 for (auto i = fMap.begin(); i != fMap.end(); ++i) {
344 SelectionAdded(i->first);
345 }
346}
347
348////////////////////////////////////////////////////////////////////////////////
349/// Deactivate this selection.
350
352{
353 if (!fActive) return;
354
355 for (auto i = fMap.begin(); i != fMap.end(); ++i) {
357 }
359 fActive = kFALSE;
360}
361
362////////////////////////////////////////////////////////////////////////////////
363/// Given element el that was picked or clicked by the user, find
364/// the parent/ancestor element that should actually become the main
365/// selected element according to current selection mode.
366
368{
369 if (el == nullptr)
370 return nullptr;
371
372 for (int pick_to_select : fPickToSelect)
373 {
374 switch (pick_to_select)
375 {
376 case kPS_Ignore:
377 {
378 return nullptr;
379 }
380 case kPS_Element:
381 {
382 return el;
383 }
384 case kPS_Projectable:
385 {
386 REveProjected* pted = dynamic_cast<REveProjected*>(el);
387 if (pted)
388 return dynamic_cast<REveElement*>(pted->GetProjectable());
389 break;
390 }
391 case kPS_Compound:
392 {
393 REveElement* cmpnd = el->GetCompound();
394 if (cmpnd)
395 return cmpnd;
396 break;
397 }
399 {
400 REveProjected* pted = dynamic_cast<REveProjected*>(el);
401 if (pted)
402 el = dynamic_cast<REveElement*>(pted->GetProjectable());
403 REveElement* cmpnd = el->GetCompound();
404 if (cmpnd)
405 return cmpnd;
406 if (pted)
407 return el;
408 break;
409 }
410 case kPS_Master:
411 {
412 REveElement* mstr = el->GetSelectionMaster();
413 if (mstr)
414 return mstr;
415 break;
416 }
417 }
418 }
419
420 return el;
421}
422
423////////////////////////////////////////////////////////////////////////////////
424/// Called when user picks/clicks on an element. If multi is true,
425/// the user is requiring a multiple selection (usually this is
426/// associated with control-key being pressed at the time of pick
427/// event).
428/// XXXX Old interface, not used in EVE-7.
429
431{
432 el = MapPickedToSelected(el);
433
434 if (el || NotEmpty())
435 {
436 if ( ! multi)
437 RemoveNieces();
438 if (el)
439 {
440 if (HasNiece(el))
441 RemoveNiece(el);
442 else
443 AddNiece(el);
444 }
446 }
447}
448
449////////////////////////////////////////////////////////////////////////////////
450/// Called when element selection is repeated.
451/// XXXX Old interface, not used in EVE-7.
452
454{
455 el = MapPickedToSelected(el);
456 if (el && HasNiece(el))
457 {
460 }
461}
462
463////////////////////////////////////////////////////////////////////////////////
464/// Called when an element is unselected.
465/// XXXX Old interface, not used in EVE-7.
466
468{
469 el = MapPickedToSelected(el);
470 if (el && HasNiece(el))
471 {
472 RemoveNiece(el);
474 }
475}
476
477//==============================================================================
478
479////////////////////////////////////////////////////////////////////////////////
480/// Called from GUI when user picks or un-picks an element.
481
482void REveSelection::NewElementPicked(ElementId_t id, bool multi, bool secondary, const std::set<int>& in_secondary_idcs)
483{
484 static const REveException eh("REveSelection::NewElementPicked ");
485
486 REveElement *pel = nullptr, *el = nullptr;
487
488 // AMT the forth/last argument is optional and therefore need to be constant
489 std::set<int> secondary_idcs = in_secondary_idcs;
490
491 if (id > 0)
492 {
493 pel = REX::gEve->FindElementById(id);
494
495 if ( ! pel) throw eh + "picked element id=" + id + " not found.";
496
497 el = MapPickedToSelected(pel);
498
499 if (el != pel) {
500 REveSecondarySelectable* ss = dynamic_cast<REveSecondarySelectable*>(el);
501 if (!secondary && ss) {
502 secondary = true;
503 secondary_idcs = ss->RefSelectedSet();
504 }
505 }
506 }
507
508 if (gDebug > 0) {
509 std::string debug_secondary;
510 if (secondary) {
511 debug_secondary = " {";
512 for (auto si : secondary_idcs) {
513 debug_secondary.append(" ");
514 debug_secondary.append(std::to_string(si));
515 }
516 debug_secondary.append(" }");
517 }
518 ::Info("REveSelection::NewElementPicked", "%p -> %p, multi: %d, secondary: %d %s", pel, el, multi, secondary, debug_secondary.c_str());
519 }
520
521 Record *rec = find_record(el);
522
523 bool changed = true;
524
525 if (multi)
526 {
527 if (el)
528 {
529 if (rec)
530 {
531 assert(secondary == rec->is_secondary());
532 if (secondary || rec->is_secondary())
533 {
534 std::set<int> dup;
535 for (auto &ns : secondary_idcs)
536 {
537 int nsi = ns;
538 auto ir = rec->f_sec_idcs.insert(nsi);
539 if (!ir.second)
540 dup.insert(nsi);
541 }
542
543 // erase duplicates
544 for (auto &dit : dup)
545 rec->f_sec_idcs.erase(dit);
546
547 secondary_idcs = rec->f_sec_idcs;
548 if (!secondary_idcs.empty()) {
549 AddNiece(el);
550 rec = find_record(el);
551 rec->f_is_sec = true;
552 rec->f_sec_idcs = secondary_idcs;
553 }
554 }
555 else
556 {
557 RemoveNiece(el);
558 }
559 }
560 else
561 {
562 AddNiece(el);
563 rec = find_record(el);
564 rec->f_is_sec = true;
565 rec->f_sec_idcs = secondary_idcs;
566 }
567 }
568 else
569 {
570 // Multiple selection with 0 element ... do nothing, I think.
571 changed = false;
572 }
573 }
574 else // single selection (not multi)
575 {
576 if (el)
577 {
578 if (rec)
579 {
580 if (secondary)
581 {
582 bool modified = (rec->f_sec_idcs != secondary_idcs);
583 RemoveNieces();
584 // re-adding is needed to refresh implied selected
585 if (modified) {
586 AddNiece(el);
587 rec = find_record(el);
588 rec->f_is_sec = true;
589 rec->f_sec_idcs = secondary_idcs;
590 }
591 }
592 else
593 {
594 RemoveNiece(el);
595 }
596 }
597 else
598 {
599 if (HasNieces()) RemoveNieces();
600 AddNiece(el);
601 if (secondary)
602 {
603 rec = find_record(el);
604 rec->f_is_sec = true;
605 rec->f_sec_idcs = secondary_idcs;
606 }
607 }
608 }
609 else // Single selection with zero element --> clear selection.
610 {
611 if (HasNieces())
612 RemoveNieces();
613 else
614 changed = false;
615 }
616 }
617
618 if (changed)
620}
621
622///////////////////////////////////////////////////////////////////////////////////
623/// Wrapper for NewElementPickedStr that takes secondary indices as C-style string.
624/// Needed to be able to use TMethodCall interface.
625
626void REveSelection::NewElementPickedStr(ElementId_t id, bool multi, bool secondary, const char* secondary_idcs)
627{
628 static const REveException eh("REveSelection::NewElementPickedStr ");
629
630 if (secondary_idcs == 0 || secondary_idcs[0] == 0)
631 {
632 NewElementPicked(id, multi, secondary);
633 return;
634 }
635
636 static const std::regex comma_re("\\s*,\\s*", std::regex::optimize);
637 std::string str(secondary_idcs);
638 std::set<int> sis;
639 std::sregex_token_iterator itr(str.begin(), str.end(), comma_re, -1);
640 std::sregex_token_iterator end;
641
642 try {
643 while (itr != end) sis.insert(std::stoi(*itr++));
644 }
645 catch (const std::invalid_argument& ia) {
646 throw eh + "invalid secondary index argument '" + *itr + "' - must be int.";
647 }
648
649 NewElementPicked(id, multi, secondary, sis);
650}
651
652////////////////////////////////////////////////////////////////////////////////
653/// Clear selection if not empty.
654
656{
657 if (HasNieces())
658 {
659 RemoveNieces();
661 }
662}
663
664//==============================================================================
665
666////////////////////////////////////////////////////////////////////////////////
667/// Remove pointers to el from implied selected sets.
668
670{
671 int count = 0;
672
673 for (auto &i : fMap)
674 {
675 auto j = i.second.f_implied.find(el);
676
677 if (j != i.second.f_implied.end())
678 {
679 i.second.f_implied.erase(j);
680 el->DecImpliedSelected();
681 ++count;
682 }
683 }
684
685 return count;
686}
687
688////////////////////////////////////////////////////////////////////////////////
689/// Write core json. If rnr_offset negative, render data will not be written
690
692{
694
695 j["fVisibleEdgeColor"] = fVisibleEdgeColor;
696 j["fHiddenEdgeColor"] = fHiddenEdgeColor;
697
698 nlohmann::json sel_list = nlohmann::json::array();
699
700 for (auto &i : fMap)
701 {
702 nlohmann::json rec = {}, imp = nlohmann::json::array(), sec = nlohmann::json::array();
703
704 rec["primary"] = i.first->GetElementId();
705
706 // XXX if not empty / f_is_sec is false ???
707 for (auto &sec_id : i.second.f_sec_idcs)
708 sec.push_back(sec_id);
709
710 // XXX if not empty ???
711 for (auto &imp_el : i.second.f_implied) {
712 imp.push_back(imp_el->GetElementId());
713 imp_el->FillExtraSelectionData(rec["extra"], sec);
714
715 }
716 rec["implied"] = imp;
717
718
719 if (i.first->RequiresExtraSelectionData()) {
720 i.first->FillExtraSelectionData(rec["extra"], sec);
721 }
722
723 rec["sec_idcs"] = sec;
724
725 // stream tooltip in highlight type
726 if (!fIsMaster)
727 rec["tooltip"] = i.first->GetHighlightTooltip(i.second.f_sec_idcs);
728
729 sel_list.push_back(rec);
730 }
731
732 j["sel_list"] = sel_list;
733
734 j["UT_PostStream"] = "UT_Selection_Refresh_State"; // XXXX to be canonized
735
736 // std::cout << j.dump(4) << std::endl;
737
738 return 0;
739}
ROOT::R::TRInterface & r
Definition: Object.C:4
#define b(i)
Definition: RSha256.hxx:100
#define g(i)
Definition: RSha256.hxx:105
int Int_t
Definition: RtypesCore.h:45
unsigned char UChar_t
Definition: RtypesCore.h:38
const Bool_t kFALSE
Definition: RtypesCore.h:101
bool Bool_t
Definition: RtypesCore.h:63
short Color_t
Definition: RtypesCore.h:92
const Bool_t kTRUE
Definition: RtypesCore.h:100
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition: TError.cxx:220
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition: TError.cxx:231
R__EXTERN TEveManager * gEve
Definition: TEveManager.h:243
Int_t gDebug
Definition: TROOT.cxx:592
virtual void AddNiece(REveElement *el)
virtual void RemoveNiece(REveElement *el)
TClass * IsA() const
Return class for this element.
virtual REveElement * GetSelectionMaster()
Returns the master element - that is:
virtual Int_t WriteCoreJson(nlohmann::json &cj, Int_t rnr_offset)
Write core json.
std::set< REveElement * > Set_t
Definition: REveElement.hxx:64
REveException Exception-type thrown by Eve classes.
Definition: REveTypes.hxx:41
REveProjectable * GetProjectable() const
void RemoveNieceInternal(REveElement *el) override
Virtual from REveAunt.
void NewElementPicked(ElementId_t id, bool multi, bool secondary, const std::set< int > &secondary_idcs={})
Called from GUI when user picks or un-picks an element.
bool HasNiece(REveElement *el) const override
Check if elemenet el is selected (not implied selected).
bool AcceptNiece(REveElement *el) override
Pre-addition check.
void AddNieceInternal(REveElement *el) override
Add an element into selection, virtual from REveAunt.
REveElement * MapPickedToSelected(REveElement *el)
Given element el that was picked or clicked by the user, find the parent/ancestor element that should...
void RemoveImpliedSelected(REveElement *el)
Remove element from all implied-selected sets.
int RemoveImpliedSelectedReferencesTo(REveElement *el)
Remove pointers to el from implied selected sets.
void SelectionRemoved(REveElement *el)
Emit SelectionRemoved signal.
void SetHiddenEdgeColorRGB(UChar_t r, UChar_t g, UChar_t b)
Set hidden highlight color.
virtual ~REveSelection()
Destructor.
REveSelection(const REveSelection &)=delete
void SetVisibleEdgeColorRGB(UChar_t r, UChar_t g, UChar_t b)
Set visible highlight color.
virtual void ActivateSelection()
Activate this selection.
bool HasNieces() const override
Check if any elements are selected.
void SetHighlightMode()
Set to 'highlight' mode.
void SelectionCleared()
Emit SelectionCleared signal.
virtual void DeactivateSelection()
Deactivate this selection.
void DoElementSelect(SelMap_i &entry)
Select element indicated by the entry and fill its implied-selected set.
Int_t WriteCoreJson(nlohmann::json &cj, Int_t rnr_offset) override
Write core json. If rnr_offset negative, render data will not be written.
Record * find_record(REveElement *el)
virtual void UserRePickedElement(REveElement *el)
Called when element selection is repeated.
virtual void UserPickedElement(REveElement *el, Bool_t multi=kFALSE)
Called when user picks/clicks on an element.
void RecheckImpliedSetForElement(REveElement *el)
If given element is selected or implied-selected within this selection then recheck implied-set for g...
void SelectionRepeated(REveElement *el)
Emit SelectionRepeated signal.
void RecheckImpliedSet(SelMap_i &entry)
Recalculate implied-selected state for given selection entry.
void SelectionAdded(REveElement *el)
Emit SelectionAdded signal.
void NewElementPickedStr(ElementId_t id, bool multi, bool secondary, const char *secondary_idcs="")
Wrapper for NewElementPickedStr that takes secondary indices as C-style string.
void RemoveNieces() override
Add an element into selection, virtual from REveAunt.
void DoElementUnselect(SelMap_i &entry)
Deselect element indicated by the entry and clear its implied-selected set.
void ClearSelection()
Clear selection if not empty.
virtual void UserUnPickedElement(REveElement *el)
Called when an element is unselected.
Bool_t InheritsFrom(const char *cl) const
Return kTRUE if this class inherits from a class with name "classname".
Definition: TClass.cxx:4867
static Int_t GetColor(const char *hexcolor)
Static method returning color number for color specified by hex color string of form: "#rrggbb",...
Definition: TColor.cxx:1822
const Int_t n
Definition: legend1.C:16
unsigned int ElementId_t
Definition: REveTypes.hxx:25
static constexpr double ns
basic_json< std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, adl_serializer, std::vector< std::uint8_t > > json
Definition: REveElement.hxx:36
bool f_is_sec
! is secondary-selected – XXXX do i need it ????