Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Loading...
Searching...
No Matches
RBrowser.cxx
Go to the documentation of this file.
1// Authors: Bertrand Bellenot <bertrand.bellenot@cern.ch> Sergey Linev <S.Linev@gsi.de>
2// Date: 2019-02-28
3// Warning: This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome!
4
5/*************************************************************************
6 * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#include <ROOT/RBrowser.hxx>
14
18
19#include <ROOT/RLogger.hxx>
20#include <ROOT/RFileDialog.hxx>
22
23#include "RBrowserWidget.hxx"
24
25#include "TVirtualPad.h"
26#include "TString.h"
27#include "TSystem.h"
28#include "TError.h"
29#include "TTimer.h"
30#include "TROOT.h"
31#include "TBufferJSON.h"
32#include "TApplication.h"
33#include "TRint.h"
34#include "Getline.h"
35
36#include <sstream>
37#include <iostream>
38#include <algorithm>
39#include <memory>
40#include <mutex>
41#include <thread>
42#include <fstream>
43
44using namespace std::string_literals;
45
46namespace ROOT {
47
48class RBrowserTimer : public TTimer {
49public:
50 RBrowser &fBrowser; ///!< browser processing postponed requests
51
52 /// constructor
54
55 /// timeout handler
56 /// used to process postponed requests in main ROOT thread
58};
59
60
62public:
63
64 bool fIsEditor{true}; ///<! either editor or image viewer
65 std::string fTitle;
66 std::string fFileName;
67 std::string fContent;
68 bool fFirstSend{false}; ///<! if editor content was send at least once
69 std::string fItemPath; ///<! item path in the browser
70
71 RBrowserEditorWidget(const std::string &name, bool is_editor = true) : RBrowserWidget(name), fIsEditor(is_editor) {}
72 ~RBrowserEditorWidget() override = default;
73
74 void ResetConn() override { fFirstSend = false; }
75
76 std::string GetKind() const override { return fIsEditor ? "editor"s : "image"s; }
77 std::string GetTitle() override { return fTitle; }
78
79 bool DrawElement(std::shared_ptr<Browsable::RElement> &elem, const std::string & = "") override
80 {
81 if (fIsEditor && elem->IsCapable(Browsable::RElement::kActEdit)) {
82 auto code = elem->GetContent("text");
83 if (!code.empty()) {
84 fFirstSend = false;
85 fContent = code;
86 fTitle = elem->GetName();
87 fFileName = elem->GetContent("filename");
88 } else {
89 auto json = elem->GetContent("json");
90 if (!json.empty()) {
91 fFirstSend = false;
92 fContent = json;
93 fTitle = elem->GetName() + ".json";
94 fFileName = "";
95 }
96 }
97 if (!fContent.empty()) {
98 // page->fItemPath = item_path;
99 return true;
100 }
101 }
102
103 if (!fIsEditor && elem->IsCapable(Browsable::RElement::kActImage)) {
104 auto img = elem->GetContent("image64");
105 if (!img.empty()) {
106 fFirstSend = false;
107 fContent = img;
108 fTitle = elem->GetName();
109 fFileName = elem->GetContent("filename");
110 // fItemPath = item_path;
111
112 return true;
113 }
114 }
115
116 return false;
117 }
118
119 std::string SendWidgetContent() override
120 {
121 if (fFirstSend) return ""s;
122
123 fFirstSend = true;
124 std::vector<std::string> args = { GetName(), fTitle, fFileName, fContent };
125
126 std::string msg = fIsEditor ? "EDITOR:"s : "IMAGE:"s;
127 msg += TBufferJSON::ToJSON(&args).Data();
128 return msg;
129 }
130
131};
132
133
135public:
136
137 enum { kMaxContentLen = 10000000 };
138
139 std::string fTitle;
140 std::string fContent;
141 bool fFirstSend{false}; ///<! if editor content was send at least once
142
144 {
145 fTitle = "Cling info"s;
146 Refresh();
147 }
148
149 ~RBrowserInfoWidget() override = default;
150
151 void ResetConn() override { fFirstSend = false; }
152
153 std::string GetKind() const override { return "info"s; }
154 std::string GetTitle() override { return fTitle; }
155
156 bool DrawElement(std::shared_ptr<Browsable::RElement> &, const std::string & = "") override { return false; }
157
158 void Refresh()
159 {
160 fFirstSend = false;
161 fContent = "";
162
163 std::ostringstream pathtmp;
164 pathtmp << gSystem->TempDirectory() << "/info." << gSystem->GetPid() << ".log";
165
166 std::ofstream ofs(pathtmp.str(), std::ofstream::out | std::ofstream::app);
167 ofs << "";
168 ofs.close();
169
170 gSystem->RedirectOutput(pathtmp.str().c_str(), "a");
171 gROOT->ProcessLine(".g");
172 gSystem->RedirectOutput(nullptr);
173
174 std::ifstream infile(pathtmp.str());
175 if (infile) {
176 std::string line;
177 while (std::getline(infile, line) && (fContent.length() < kMaxContentLen)) {
178 fContent.append(line);
179 fContent.append("\n");
180 }
181 }
182
183 gSystem->Unlink(pathtmp.str().c_str());
184 }
185
186 void RefreshFromLogs(const std::string &promt, const std::vector<std::string> &logs)
187 {
188 int indx = 0, last_prompt = -1;
189 for (auto &line : logs) {
190 if (line == promt)
192 indx++;
193 }
194
195 if (last_prompt < 0) {
196 Refresh();
197 return;
198 }
199
200 fFirstSend = false;
201 fContent = "";
202
203 indx = 0;
204 for (auto &line : logs) {
205 if ((indx++ > last_prompt) && (fContent.length() < kMaxContentLen)) {
206 fContent.append(line);
207 fContent.append("\n");
208 }
209 }
210 }
211
212
213 std::string SendWidgetContent() override
214 {
215 if (fFirstSend)
216 return ""s;
217
218 if (fContent.empty())
219 Refresh();
220
221 fFirstSend = true;
222 std::vector<std::string> args = { GetName(), fTitle, fContent };
223
224 return "INFO:"s + TBufferJSON::ToJSON(&args).Data();
225 }
226
227};
228
229
231public:
232
233 RWebWindow *fWindow{nullptr}; // catched widget, TODO: to be changed to shared_ptr
234 std::string fCatchedKind; // kind of catched widget
235
236 std::string GetKind() const override { return "catched"s; }
237
238 std::string GetUrl() override { return fWindow ? ".."s + fWindow->GetUrl(false) : ""s; }
239
240 std::string GetTitle() override { return fCatchedKind; }
241
242 bool IsValid() override { return fWindow != nullptr; }
243
244 RBrowserCatchedWidget(const std::string &name, RWebWindow *win, const std::string &kind) :
246 fWindow(win),
247 fCatchedKind(kind)
248 {
249 }
250};
251
252} // namespace ROOT
253
254using namespace ROOT;
255
256
257/** \class ROOT::RBrowser
258\ingroup rbrowser
259\ingroup webwidgets
260
261\brief Web-based %ROOT files and objects browser
262
263\image html v7_rbrowser.png
264
265*/
266
267//////////////////////////////////////////////////////////////////////////////////////////////
268/// constructor
269
271{
272 if (gROOT->IsWebDisplayBatch()) {
273 ::Warning("RBrowser::RBrowser", "The RBrowser cannot run in web batch mode");
274 return;
275 }
276
277 std::ostringstream pathtmp;
278 pathtmp << gSystem->TempDirectory() << "/command." << gSystem->GetPid() << ".log";
280
282
284
285 fTimer = std::make_unique<RBrowserTimer>(10, kTRUE, *this);
286
288 fWebWindow->SetDefaultPage("file:rootui5sys/browser/browser.html");
289
290 // this is call-back, invoked when message received via websocket
291 fWebWindow->SetCallBacks([this](unsigned connid) { fConnId = connid; SendInitMsg(connid); },
292 [this](unsigned connid, const std::string &arg) { ProcessMsg(connid, arg); });
293 fWebWindow->SetGeometry(1200, 700); // configure predefined window geometry
294 fWebWindow->SetConnLimit(1); // the only connection is allowed
295 fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue
296
297 fWebWindow->GetManager()->SetShowCallback([this](RWebWindow &win, const RWebDisplayArgs &args) -> bool {
298
299 std::string kind;
300
301 if (args.GetWidgetKind() == "RCanvas")
302 kind = "rcanvas";
303 else if (args.GetWidgetKind() == "TCanvas")
304 kind = "tcanvas";
305 else if (args.GetWidgetKind() == "RGeomViewer")
306 kind = "geom";
307 else if (args.GetWidgetKind() == "RTreeViewer")
308 kind = "tree";
309
310 if (!fWebWindow || !fCatchWindowShow || kind.empty())
311 return false;
312
313 // before create new widget check if other disappear
315
317 if (widget) {
318 widget->fBrowser = this;
319 fWidgets.emplace_back(widget);
320 fActiveWidgetName = widget->GetName();
321 } else {
322 widget = AddCatchedWidget(&win, kind);
323 }
324
325 if (widget && fWebWindow && (fWebWindow->NumConnections() > 0))
326 fWebWindow->Send(0, NewWidgetMsg(widget));
327
328 return widget ? true : false;
329 });
330
331 fWebWindow->GetManager()->SetDeleteCallback([this](RWebWindow &win) -> void {
332 for (auto &widget : fWidgets) {
333 auto catched = dynamic_cast<RBrowserCatchedWidget *>(widget.get());
334 if (catched && (catched->fWindow == &win))
335 catched->fWindow = nullptr;
336 }
337
338 if (fWebWindow)
340 });
341
342 Show();
343}
344
345//////////////////////////////////////////////////////////////////////////////////////////////
346/// destructor
347
349{
350 if (fWebWindow) {
351 fWebWindow->GetManager()->SetShowCallback(nullptr);
352 fWebWindow->GetManager()->SetDeleteCallback(nullptr);
353 fWebWindow->Reset();
354 }
355}
356
357//////////////////////////////////////////////////////////////////////////////////////////////
358/// Process browser request
359
360std::string RBrowser::ProcessBrowserRequest(const std::string &msg)
361{
362 std::unique_ptr<RBrowserRequest> request;
363
364 if (msg.empty()) {
365 request = std::make_unique<RBrowserRequest>();
366 request->first = 0;
367 request->number = 100;
368 } else {
369 request = TBufferJSON::FromJSON<RBrowserRequest>(msg);
370 }
371
372 if (!request)
373 return ""s;
374
375 if (request->path.empty() && fWidgets.empty() && fBrowsable.GetWorkingPath().empty())
377
378 return "BREPL:"s + fBrowsable.ProcessRequest(*request.get());
379}
380
381/////////////////////////////////////////////////////////////////////////////////
382/// Process file save command in the editor
383
384void RBrowser::ProcessSaveFile(const std::string &fname, const std::string &content)
385{
386 if (fname.empty()) return;
387 R__LOG_DEBUG(0, BrowserLog()) << "SaveFile " << fname << " content length " << content.length();
388 std::ofstream f(fname);
389 f << content;
390}
391
392/////////////////////////////////////////////////////////////////////////////////
393/// Process run macro command in the editor
394
395void RBrowser::ProcessRunMacro(const std::string &file_path)
396{
397 if (file_path.rfind(".py") == file_path.length() - 3) {
398 TString exec;
399 exec.Form("TPython::ExecScript(\"%s\");", file_path.c_str());
400 gROOT->ProcessLine(exec.Data());
401 } else {
402 gInterpreter->ExecuteMacro(file_path.c_str());
403 }
404}
405
406/////////////////////////////////////////////////////////////////////////////////
407/// Process dbl click on browser item
408
409std::string RBrowser::ProcessDblClick(unsigned connid, std::vector<std::string> &args)
410{
411 args.pop_back(); // remove exec string, not used now
412
413 std::string opt = args.back();
414 args.pop_back(); // remove option
415
416 auto path = fBrowsable.GetWorkingPath();
417 path.insert(path.end(), args.begin(), args.end());
418
419 R__LOG_DEBUG(0, BrowserLog()) << "DoubleClick " << Browsable::RElement::GetPathAsString(path);
420
421 auto elem = fBrowsable.GetSubElement(path);
422 if (!elem) return ""s;
423
424 auto dflt_action = elem->GetDefaultAction();
425
426 // special case when canvas is clicked - always start new widget
428 std::string widget_kind;
429
430 if (elem->IsCapable(Browsable::RElement::kActDraw7))
431 widget_kind = "rcanvas";
432 else
433 widget_kind = "tcanvas";
434
435 std::string name = widget_kind + std::to_string(++fWidgetCnt);
436
438
439 if (!new_widget)
440 return ""s;
441
442 // assign back pointer
443 new_widget->fBrowser = this;
444 fWidgets.emplace_back(new_widget);
445 fActiveWidgetName = new_widget->GetName();
446
447 return NewWidgetMsg(new_widget);
448 }
449
450 // before display tree or geometry ensure that they read and cached inside element
452 elem->GetChildsIter();
453 }
454
456 Browsable::RProvider::ProgressHandle handle(elem.get(), [this, connid](float progress, void *) {
457 SendProgress(connid, progress);
458 });
459
460 auto widget = GetActiveWidget();
461 if (widget && widget->DrawElement(elem, opt)) {
462 widget->SetPath(path);
463 return widget->SendWidgetContent();
464 }
465
466 // check if element was drawn in other widget and just activate that widget
467 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(),
468 [path](const std::shared_ptr<RBrowserWidget> &wg) { return path == wg->GetPath(); });
469
470 if (iter != fWidgets.end())
471 return "SELECT_WIDGET:"s + (*iter)->GetName();
472
473 // check if object can be drawn in RCanvas even when default action is drawing in TCanvas
476
477 std::string widget_kind;
478 switch(dflt_action) {
479 case Browsable::RElement::kActDraw6: widget_kind = "tcanvas"; break;
480 case Browsable::RElement::kActDraw7: widget_kind = "rcanvas"; break;
481 case Browsable::RElement::kActEdit: widget_kind = "editor"; break;
482 case Browsable::RElement::kActImage: widget_kind = "image"; break;
483 case Browsable::RElement::kActTree: widget_kind = "tree"; break;
484 case Browsable::RElement::kActGeom: widget_kind = "geom"; break;
485 default: widget_kind.clear();
486 }
487
488 if (!widget_kind.empty()) {
490 if (new_widget) {
491 // draw object before client side is created - should not be a problem
492 // after widget add in browser, connection will be established and data provided
493 if (new_widget->DrawElement(elem, opt))
494 new_widget->SetPath(path);
495 return NewWidgetMsg(new_widget);
496 }
497 }
498
499 if (elem->IsCapable(Browsable::RElement::kActBrowse) && (elem->GetNumChilds() > 0)) {
500 // remove extra index in subitems name
501 for (auto &pathelem : path)
505 }
506
507 return ""s;
508}
509
510/////////////////////////////////////////////////////////////////////////////////
511/// Process drop of item in the current tab
512
513std::string RBrowser::ProcessDrop(unsigned connid, std::vector<std::string> &args)
514{
515 auto path = fBrowsable.GetWorkingPath();
516 path.insert(path.end(), args.begin(), args.end());
517
518 R__LOG_DEBUG(0, BrowserLog()) << "DoubleClick " << Browsable::RElement::GetPathAsString(path);
519
520 auto elem = fBrowsable.GetSubElement(path);
521 if (!elem) return ""s;
522
524 Browsable::RProvider::ProgressHandle handle(elem.get(), [this, connid](float progress, void *) {
525 SendProgress(connid, progress);
526 });
527
528 auto widget = GetActiveWidget();
529 if (widget && widget->DrawElement(elem, "<append>")) {
530 widget->SetPath(path);
531 return widget->SendWidgetContent();
532 }
533
534 return ""s;
535}
536
537
538/////////////////////////////////////////////////////////////////////////////////
539/// Show or update RBrowser in web window
540/// If web window already started - just refresh it like "reload" button does
541/// If no web window exists or \param always_start_new_browser configured, starts new window
542/// \param args display arguments
543
545{
546 if (!fWebWindow->NumConnections() || always_start_new_browser) {
547 fWebWindow->Show(args);
548 }
549}
550
551///////////////////////////////////////////////////////////////////////////////////////////////////////
552/// Hide ROOT Browser
553
555{
556 if (fWebWindow)
557 fWebWindow->CloseConnections();
558}
559
560///////////////////////////////////////////////////////////////////////////////////////////////////////
561/// Return URL parameter for the window showing ROOT Browser
562/// See \ref ROOT::RWebWindow::GetUrl docu for more details
563
565{
566 if (fWebWindow)
567 return fWebWindow->GetUrl(remote);
568
569 return ""s;
570}
571
572
573//////////////////////////////////////////////////////////////////////////////////////////////
574/// Creates new widget
575
576std::shared_ptr<RBrowserWidget> RBrowser::AddWidget(const std::string &kind)
577{
578 std::string name = kind + std::to_string(++fWidgetCnt);
579
580 std::shared_ptr<RBrowserWidget> widget;
581
582 if (kind == "editor"s)
583 widget = std::make_shared<RBrowserEditorWidget>(name, true);
584 else if (kind == "image"s)
585 widget = std::make_shared<RBrowserEditorWidget>(name, false);
586 else if (kind == "info"s)
587 widget = std::make_shared<RBrowserInfoWidget>(name);
588 else
590
591 if (!widget) {
592 R__LOG_ERROR(BrowserLog()) << "Fail to create widget of kind " << kind;
593 return nullptr;
594 }
595
596 widget->fBrowser = this;
597 fWidgets.emplace_back(widget);
599
600 return widget;
601}
602
603//////////////////////////////////////////////////////////////////////////////////////////////
604/// Add widget catched from external scripts
605
606std::shared_ptr<RBrowserWidget> RBrowser::AddCatchedWidget(RWebWindow *win, const std::string &kind)
607{
608 if (!win || kind.empty())
609 return nullptr;
610
611 std::string name = "catched"s + std::to_string(++fWidgetCnt);
612
613 auto widget = std::make_shared<RBrowserCatchedWidget>(name, win, kind);
614
615 fWidgets.emplace_back(widget);
616
618
619 return widget;
620}
621
622
623//////////////////////////////////////////////////////////////////////////////////////////////
624/// Create new widget and send init message to the client
625
626void RBrowser::AddInitWidget(const std::string &kind)
627{
628 auto widget = AddWidget(kind);
629 if (widget && fWebWindow && (fWebWindow->NumConnections() > 0))
630 fWebWindow->Send(0, NewWidgetMsg(widget));
631}
632
633//////////////////////////////////////////////////////////////////////////////////////////////
634/// Find widget by name or kind
635
636std::shared_ptr<RBrowserWidget> RBrowser::FindWidget(const std::string &name, const std::string &kind) const
637{
638 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(),
639 [name, kind](const std::shared_ptr<RBrowserWidget> &widget) {
640 return kind.empty() ? name == widget->GetName() : kind == widget->GetKind();
641 });
642
643 if (iter != fWidgets.end())
644 return *iter;
645
646 return nullptr;
647}
648
649//////////////////////////////////////////////////////////////////////////////////////////////
650/// Close and delete specified widget
651
652void RBrowser::CloseTab(const std::string &name)
653{
654 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(), [name](std::shared_ptr<RBrowserWidget> &widget) { return name == widget->GetName(); });
655 if (iter != fWidgets.end())
656 fWidgets.erase(iter);
657
658 if (fActiveWidgetName == name)
659 fActiveWidgetName.clear();
660}
661
662//////////////////////////////////////////////////////////////////////////////////////////////
663/// Get content of history file
664
665std::vector<std::string> RBrowser::GetRootHistory()
666{
667 std::vector<std::string> arr;
668
669 std::string path = gSystem->UnixPathName(gSystem->HomeDirectory());
670 path += "/.root_hist" ;
671 std::ifstream infile(path);
672
673 if (infile) {
674 std::string line;
675 while (std::getline(infile, line) && (arr.size() < 1000)) {
676 if(!(std::find(arr.begin(), arr.end(), line) != arr.end())) {
677 arr.emplace_back(line);
678 }
679 }
680 }
681
682 return arr;
683}
684
685//////////////////////////////////////////////////////////////////////////////////////////////
686/// Get content of log file
687
688std::vector<std::string> RBrowser::GetRootLogs()
689{
690 std::vector<std::string> arr;
691
692 std::ifstream infile(fPromptFileOutput);
693 if (infile) {
694 std::string line;
695 while (std::getline(infile, line) && (arr.size() < 10000)) {
696 arr.emplace_back(line);
697 }
698 }
699
700 return arr;
701}
702
703//////////////////////////////////////////////////////////////////////////////////////////////
704/// Process client connect
705
706void RBrowser::SendInitMsg(unsigned connid)
707{
708 std::vector<std::vector<std::string>> reply;
709
710 reply.emplace_back(fBrowsable.GetWorkingPath()); // first element is current path
711
712 for (auto &widget : fWidgets) {
713 widget->ResetConn();
714 reply.emplace_back(std::vector<std::string>({ widget->GetKind(), widget->GetUrl(), widget->GetName(), widget->GetTitle() }));
715 }
716
717 if (!fActiveWidgetName.empty())
718 reply.emplace_back(std::vector<std::string>({ "active"s, fActiveWidgetName }));
719
720 auto history = GetRootHistory();
721 if (history.size() > 0) {
722 history.insert(history.begin(), "history"s);
723 reply.emplace_back(history);
724 }
725
726 auto logs = GetRootLogs();
727 if (logs.size() > 0) {
728 logs.insert(logs.begin(), "logs"s);
729 reply.emplace_back(logs);
730 }
731
732 reply.emplace_back(std::vector<std::string>({
733 "drawoptions"s,
737 }));
738
739 std::string msg = "INMSG:";
741
742 fWebWindow->Send(connid, msg);
743}
744
745//////////////////////////////////////////////////////////////////////////////////////////////
746/// Send generic progress message to the web window
747/// Should show progress bar on client side
748
749void RBrowser::SendProgress(unsigned connid, float progr)
750{
751 long long millisec = gSystem->Now();
752
753 // let process window events
754 fWebWindow->Sync();
755
756 if ((!fLastProgressSendTm || millisec > fLastProgressSendTm - 200) && (progr > fLastProgressSend + 0.04) && fWebWindow->CanSend(connid)) {
757 fWebWindow->Send(connid, "PROGRESS:"s + std::to_string(progr));
758
761 }
762}
763
764
765//////////////////////////////////////////////////////////////////////////////////////////////
766/// Return the current directory of ROOT
767
769{
770 return "WORKPATH:"s + TBufferJSON::ToJSON(&fBrowsable.GetWorkingPath()).Data();
771}
772
773//////////////////////////////////////////////////////////////////////////////////////////////
774/// Create message which send to client to create new widget
775
776std::string RBrowser::NewWidgetMsg(std::shared_ptr<RBrowserWidget> &widget)
777{
778 std::vector<std::string> arr = { widget->GetKind(), widget->GetUrl(), widget->GetName(), widget->GetTitle(),
780 return "NEWWIDGET:"s + TBufferJSON::ToJSON(&arr, TBufferJSON::kNoSpaces).Data();
781}
782
783//////////////////////////////////////////////////////////////////////////////////////////////
784/// Check if any widget was modified and update if necessary
785
787{
788 std::vector<std::string> del_names;
789
790 for (auto &widget : fWidgets)
791 if (!widget->IsValid())
792 del_names.push_back(widget->GetName());
793
794 if (!del_names.empty())
795 fWebWindow->Send(connid, "CLOSE_WIDGETS:"s + TBufferJSON::ToJSON(&del_names, TBufferJSON::kNoSpaces).Data());
796
797 for (auto name : del_names)
798 CloseTab(name);
799
800 for (auto &widget : fWidgets)
801 widget->CheckModified();
802}
803
804//////////////////////////////////////////////////////////////////////////////////////////////
805/// Process postponed requests - decouple from websocket handling
806/// Only requests which can take longer time should be postponed
807
809{
810 if (fPostponed.empty())
811 return;
812
813 auto arr = fPostponed[0];
814 fPostponed.erase(fPostponed.begin(), fPostponed.begin()+1);
815 if (fPostponed.empty())
816 fTimer->TurnOff();
817
818 std::string reply;
819 unsigned connid = std::stoul(arr.back()); arr.pop_back();
820 std::string kind = arr.back(); arr.pop_back();
821
822 if (kind == "DBLCLK") {
823 reply = ProcessDblClick(connid, arr);
824 if (reply.empty()) reply = "NOPE";
825 } else if (kind == "DROP") {
826 reply = ProcessDrop(connid, arr);
827 if (reply.empty()) reply = "NOPE";
828 }
829
830 if (!reply.empty())
831 fWebWindow->Send(connid, reply);
832}
833
834
835//////////////////////////////////////////////////////////////////////////////////////////////
836/// Process received message from the client
837
838void RBrowser::ProcessMsg(unsigned connid, const std::string &arg0)
839{
840 R__LOG_DEBUG(0, BrowserLog()) << "ProcessMsg len " << arg0.length() << " substr(30) " << arg0.substr(0, 30);
841
842 std::string kind, msg;
843 auto pos = arg0.find(":");
844 if (pos == std::string::npos) {
845 kind = arg0;
846 } else {
847 kind = arg0.substr(0, pos);
848 msg = arg0.substr(pos+1);
849 }
850
851 if (kind == "QUIT_ROOT") {
852
853 fWebWindow->TerminateROOT();
854
855 } else if (kind == "BRREQ") {
856 // central place for processing browser requests
858 if (!json.empty()) fWebWindow->Send(connid, json);
859
860 } else if (kind == "DBLCLK") {
861
862 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
863 if (arr && (arr->size() > 2)) {
864 arr->push_back(kind);
865 arr->push_back(std::to_string(connid));
866 fPostponed.push_back(*arr);
867 if (fPostponed.size() == 1)
868 fTimer->TurnOn();
869 } else {
870 fWebWindow->Send(connid, "NOPE");
871 }
872
873 } else if (kind == "DROP") {
874
875 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
876 if (arr && arr->size()) {
877 arr->push_back(kind);
878 arr->push_back(std::to_string(connid));
879 fPostponed.push_back(*arr);
880 if (fPostponed.size() == 1)
881 fTimer->TurnOn();
882 } else {
883 fWebWindow->Send(connid, "NOPE");
884 }
885
886 } else if (kind == "WIDGET_SELECTED") {
888 auto widget = GetActiveWidget();
889 if (widget) {
890 auto reply = widget->SendWidgetContent();
891 if (!reply.empty()) fWebWindow->Send(connid, reply);
892 }
893 } else if (kind == "CLOSE_TAB") {
894 CloseTab(msg);
895 } else if (kind == "GETWORKPATH") {
896 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
897 } else if (kind == "CHPATH") {
898 auto path = TBufferJSON::FromJSON<Browsable::RElementPath_t>(msg);
899 if (path) fBrowsable.SetWorkingPath(*path);
900 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
901 } else if (kind == "CMD") {
902 std::string sPrompt = "root []";
903 TApplication *app = gROOT->GetApplication();
904 if (app->InheritsFrom("TRint")) {
905 sPrompt = ((TRint*)gROOT->GetApplication())->GetPrompt();
906 Gl_histadd((char *)msg.c_str());
907 }
908
909 std::ofstream ofs(fPromptFileOutput, std::ofstream::out | std::ofstream::app);
910 ofs << sPrompt << msg << std::endl;
911 ofs.close();
912
914 gROOT->ProcessLine(msg.c_str());
915 gSystem->RedirectOutput(nullptr);
916
917 if (msg == ".g"s) {
918 auto widget = std::dynamic_pointer_cast<RBrowserInfoWidget>(FindWidget(""s, "info"s));
919 if (!widget) {
920 auto new_widget = AddWidget("info"s);
921 fWebWindow->Send(connid, NewWidgetMsg(new_widget));
922 widget = std::dynamic_pointer_cast<RBrowserInfoWidget>(new_widget);
923 } else if (fActiveWidgetName != widget->GetName()) {
924 fWebWindow->Send(connid, "SELECT_WIDGET:"s + widget->GetName());
925 fActiveWidgetName = widget->GetName();
926 }
927
928 if (widget)
929 widget->RefreshFromLogs(sPrompt + msg, GetRootLogs());
930 }
931
932 CheckWidgtesModified(connid);
933 } else if (kind == "GETHISTORY") {
934
935 auto history = GetRootHistory();
936
937 fWebWindow->Send(connid, "HISTORY:"s + TBufferJSON::ToJSON(&history, TBufferJSON::kNoSpaces).Data());
938 } else if (kind == "GETLOGS") {
939
940 auto logs = GetRootLogs();
941 fWebWindow->Send(connid, "LOGS:"s + TBufferJSON::ToJSON(&logs, TBufferJSON::kNoSpaces).Data());
942
944
946
947 } else if (kind == "SYNCEDITOR") {
948 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
949 if (arr && (arr->size() > 4)) {
950 auto editor = std::dynamic_pointer_cast<RBrowserEditorWidget>(FindWidget(arr->at(0)));
951 if (editor) {
952 editor->fFirstSend = true;
953 editor->fTitle = arr->at(1);
954 editor->fFileName = arr->at(2);
955 if (!arr->at(3).empty()) editor->fContent = arr->at(4);
956 if ((arr->size() == 6) && (arr->at(5) == "SAVE"))
957 ProcessSaveFile(editor->fFileName, editor->fContent);
958 if ((arr->size() == 6) && (arr->at(5) == "RUN")) {
959 ProcessSaveFile(editor->fFileName, editor->fContent);
960 ProcessRunMacro(editor->fFileName);
961 CheckWidgtesModified(connid);
962 }
963 }
964 }
965 } else if (kind == "GETINFO") {
966 auto info = std::dynamic_pointer_cast<RBrowserInfoWidget>(FindWidget(msg));
967 if (info) {
968 info->Refresh();
969 fWebWindow->Send(connid, info->SendWidgetContent());
970 }
971 } else if (kind == "NEWWIDGET") {
972 auto widget = AddWidget(msg);
973 if (widget)
974 fWebWindow->Send(connid, NewWidgetMsg(widget));
975 } else if (kind == "NEWCHANNEL") {
976 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
977 if (arr && (arr->size() == 2)) {
978 auto widget = FindWidget((*arr)[0]);
979 if (widget)
980 RWebWindow::ShowWindow(widget->GetWindow(), { fWebWindow, connid, std::stoi((*arr)[1]) });
981 }
982 } else if (kind == "CDWORKDIR") {
986 } else {
988 }
989 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
990 } else if (kind == "OPTIONS") {
991 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
992 if (arr && (arr->size() == 3)) {
996 }
997 }
998}
999
1000//////////////////////////////////////////////////////////////////////////////////////////////
1001/// Set working path in the browser
1002
1003void RBrowser::SetWorkingPath(const std::string &path)
1004{
1005 auto p = Browsable::RElement::ParsePath(path);
1007 if (elem) {
1009 if (fWebWindow && (fWebWindow->NumConnections() > 0))
1011 }
1012}
1013
1014//////////////////////////////////////////////////////////////////////////////////////////////
1015/// Activate widget in RBrowser
1016/// One should specify title and (optionally) kind of widget like "tcanvas" or "geom"
1017
1018bool RBrowser::ActivateWidget(const std::string &title, const std::string &kind)
1019{
1020 if (title.empty())
1021 return false;
1022
1023 for (auto &widget : fWidgets) {
1024
1025 if (widget->GetTitle() != title)
1026 continue;
1027
1028 if (!kind.empty() && (widget->GetKind() != kind))
1029 continue;
1030
1031 if (fWebWindow)
1032 fWebWindow->Send(0, "SELECT_WIDGET:"s + widget->GetName());
1033 else
1034 fActiveWidgetName = widget->GetName();
1035 return true;
1036 }
1037
1038 return false;
1039}
1040
1041//////////////////////////////////////////////////////////////////////////////////////////////
1042/// Set handle which will be cleared when connection is closed
1043
1044void RBrowser::ClearOnClose(const std::shared_ptr<void> &handle)
1045{
1046 fWebWindow->SetClearOnClose(handle);
1047}
nlohmann::json json
#define R__LOG_ERROR(...)
Definition RLogger.hxx:357
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition RLogger.hxx:360
#define f(i)
Definition RSha256.hxx:104
long Long_t
Definition RtypesCore.h:54
constexpr Bool_t kTRUE
Definition RtypesCore.h:93
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:229
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t win
Option_t Option_t TPoint TPoint const char mode
char name[80]
Definition TGX11.cxx:110
#define gInterpreter
#define gROOT
Definition TROOT.h:406
R__EXTERN TSystem * gSystem
Definition TSystem.h:572
static int ExtractItemIndex(std::string &name)
Extract index from name Index coded by client with ###<indx>$$$ suffix Such coding used by browser to...
Definition RElement.cxx:178
@ kActImage
can be shown in image viewer, can provide image
Definition RElement.hxx:54
@ kActDraw6
can be drawn inside ROOT6 canvas
Definition RElement.hxx:55
@ kActCanvas
indicate that it is canvas and should be drawn directly
Definition RElement.hxx:57
@ kActTree
can be shown in tree viewer
Definition RElement.hxx:58
@ kActGeom
can be shown in geometry viewer
Definition RElement.hxx:59
@ kActBrowse
just browse (expand) item
Definition RElement.hxx:52
@ kActEdit
can provide data for text editor
Definition RElement.hxx:53
@ kActDraw7
can be drawn inside ROOT7 canvas
Definition RElement.hxx:56
static std::string GetPathAsString(const RElementPath_t &path)
Converts element path back to string.
Definition RElement.cxx:160
static RElementPath_t ParsePath(const std::string &str)
Parse string path to produce RElementPath_t One should avoid to use string pathes as much as possible...
Definition RElement.cxx:116
static bool SetClassDrawOption(const ClassArg &, const std::string &)
Set draw option for the class Return true if entry for the class exists.
static std::string GetClassDrawOption(const ClassArg &)
Return configured draw option for the class.
static RElementPath_t GetWorkingPath(const std::string &workdir="")
Return working path in browser hierarchy.
Definition RSysFile.cxx:571
RBrowserCatchedWidget(const std::string &name, RWebWindow *win, const std::string &kind)
Definition RBrowser.cxx:244
std::string GetUrl() override
Definition RBrowser.cxx:238
std::string GetKind() const override
Definition RBrowser.cxx:236
std::string GetTitle() override
Definition RBrowser.cxx:240
std::shared_ptr< Browsable::RElement > GetSubElement(const Browsable::RElementPath_t &path)
Returns sub-element starting from top, using cached data.
void ClearCache()
Clear internal objects cache.
std::string ProcessRequest(const RBrowserRequest &request)
Process browser request, returns string with JSON of RBrowserReply data.
void SetWorkingPath(const Browsable::RElementPath_t &path)
set working directory relative to top element
const Browsable::RElementPath_t & GetWorkingPath() const
void CreateDefaultElements()
Create default elements shown in the RBrowser.
std::string GetTitle() override
Definition RBrowser.cxx:77
std::string fItemPath
! item path in the browser
Definition RBrowser.cxx:69
void ResetConn() override
Definition RBrowser.cxx:74
std::string GetKind() const override
Definition RBrowser.cxx:76
bool fFirstSend
! if editor content was send at least once
Definition RBrowser.cxx:68
RBrowserEditorWidget(const std::string &name, bool is_editor=true)
Definition RBrowser.cxx:71
bool fIsEditor
! either editor or image viewer
Definition RBrowser.cxx:64
bool DrawElement(std::shared_ptr< Browsable::RElement > &elem, const std::string &="") override
Definition RBrowser.cxx:79
~RBrowserEditorWidget() override=default
std::string SendWidgetContent() override
Definition RBrowser.cxx:119
void RefreshFromLogs(const std::string &promt, const std::vector< std::string > &logs)
Definition RBrowser.cxx:186
void ResetConn() override
Definition RBrowser.cxx:151
RBrowserInfoWidget(const std::string &name)
Definition RBrowser.cxx:143
std::string GetTitle() override
Definition RBrowser.cxx:154
std::string GetKind() const override
Definition RBrowser.cxx:153
bool fFirstSend
! if editor content was send at least once
Definition RBrowser.cxx:141
std::string SendWidgetContent() override
Definition RBrowser.cxx:213
~RBrowserInfoWidget() override=default
bool DrawElement(std::shared_ptr< Browsable::RElement > &, const std::string &="") override
Definition RBrowser.cxx:156
RBrowser & fBrowser
Definition RBrowser.cxx:50
RBrowserTimer(Long_t milliSec, Bool_t mode, RBrowser &br)
!< browser processing postponed requests
Definition RBrowser.cxx:53
void Timeout() override
timeout handler used to process postponed requests in main ROOT thread
Definition RBrowser.cxx:57
static std::shared_ptr< RBrowserWidget > DetectCatchedWindow(const std::string &kind, RWebWindow &win)
Check if catch window can be identified and normal widget can be created Used for TCanvas created in ...
static std::shared_ptr< RBrowserWidget > CreateWidgetFor(const std::string &kind, const std::string &name, std::shared_ptr< Browsable::RElement > &element)
Create specified widget for existing object.
static std::shared_ptr< RBrowserWidget > CreateWidget(const std::string &kind, const std::string &name)
Create specified widget.
Abstract Web-based widget, which can be used in the RBrowser Used to embed canvas,...
const std::string & GetName() const
Web-based ROOT files and objects browser.
Definition RBrowser.hxx:27
std::unique_ptr< RBrowserTimer > fTimer
! timer to handle postponed requests
Definition RBrowser.hxx:48
RBrowserData fBrowsable
! central browsing element
Definition RBrowser.hxx:47
std::shared_ptr< RBrowserWidget > AddWidget(const std::string &kind)
Creates new widget.
Definition RBrowser.cxx:576
std::vector< std::string > GetRootHistory()
Get content of history file.
Definition RBrowser.cxx:665
void AddInitWidget(const std::string &kind)
Create new widget and send init message to the client.
Definition RBrowser.cxx:626
std::vector< std::vector< std::string > > fPostponed
! postponed messages, handled in timer
Definition RBrowser.hxx:49
std::shared_ptr< RWebWindow > fWebWindow
! web window to browser
Definition RBrowser.hxx:45
std::string ProcessDrop(unsigned connid, std::vector< std::string > &args)
Process drop of item in the current tab.
Definition RBrowser.cxx:513
int fWidgetCnt
! counter for created widgets
Definition RBrowser.hxx:40
std::shared_ptr< RBrowserWidget > GetActiveWidget() const
Definition RBrowser.hxx:54
std::string ProcessDblClick(unsigned connid, std::vector< std::string > &args)
Process dbl click on browser item.
Definition RBrowser.cxx:409
void ClearOnClose(const std::shared_ptr< void > &handle)
Set handle which will be cleared when connection is closed.
std::string fActiveWidgetName
! name of active widget
Definition RBrowser.hxx:38
RBrowser(bool use_rcanvas=false)
constructor
Definition RBrowser.cxx:270
void SetWorkingPath(const std::string &path)
Set working path in the browser.
void Hide()
hide Browser
Definition RBrowser.cxx:554
std::string NewWidgetMsg(std::shared_ptr< RBrowserWidget > &widget)
Create message which send to client to create new widget.
Definition RBrowser.cxx:776
bool fCatchWindowShow
! if arbitrary RWebWindow::Show calls should be catched by browser
Definition RBrowser.hxx:37
std::string fPromptFileOutput
! file name for prompt output
Definition RBrowser.hxx:41
void Show(const RWebDisplayArgs &args="", bool always_start_new_browser=false)
show Browser in specified place
Definition RBrowser.cxx:544
std::string GetCurrentWorkingDirectory()
Return the current directory of ROOT.
Definition RBrowser.cxx:768
void SetUseRCanvas(bool on=true)
Definition RBrowser.hxx:84
std::shared_ptr< RBrowserWidget > FindWidget(const std::string &name, const std::string &kind="") const
Find widget by name or kind.
Definition RBrowser.cxx:636
std::shared_ptr< RBrowserWidget > AddCatchedWidget(RWebWindow *win, const std::string &kind)
Add widget catched from external scripts.
Definition RBrowser.cxx:606
bool GetUseRCanvas() const
Definition RBrowser.hxx:83
std::vector< std::shared_ptr< RBrowserWidget > > fWidgets
! all browser widgets
Definition RBrowser.hxx:39
virtual ~RBrowser()
destructor
Definition RBrowser.cxx:348
void ProcessSaveFile(const std::string &fname, const std::string &content)
Process file save command in the editor.
Definition RBrowser.cxx:384
float fLastProgressSend
! last value of send progress
Definition RBrowser.hxx:42
std::string GetWindowUrl(bool remote)
Return URL parameter for the window showing ROOT Browser See ROOT::RWebWindow::GetUrl docu for more d...
Definition RBrowser.cxx:564
std::string ProcessBrowserRequest(const std::string &msg)
Process browser request.
Definition RBrowser.cxx:360
std::vector< std::string > GetRootLogs()
Get content of log file.
Definition RBrowser.cxx:688
void ProcessMsg(unsigned connid, const std::string &arg)
Process received message from the client.
Definition RBrowser.cxx:838
void CheckWidgtesModified(unsigned connid)
Check if any widget was modified and update if necessary.
Definition RBrowser.cxx:786
void CloseTab(const std::string &name)
Close and delete specified widget.
Definition RBrowser.cxx:652
void ProcessPostponedRequests()
Process postponed requests - decouple from websocket handling Only requests which can take longer tim...
Definition RBrowser.cxx:808
unsigned fConnId
! default connection id
Definition RBrowser.hxx:34
bool ActivateWidget(const std::string &title, const std::string &kind="")
Activate widget in RBrowser One should specify title and (optionally) kind of widget like "tcanvas" o...
void SendInitMsg(unsigned connid)
Process client connect.
Definition RBrowser.cxx:706
void SendProgress(unsigned connid, float progr)
Send generic progress message to the web window Should show progress bar on client side.
Definition RBrowser.cxx:749
long long fLastProgressSendTm
! time when last progress message was send
Definition RBrowser.hxx:43
void ProcessRunMacro(const std::string &file_path)
Process run macro command in the editor.
Definition RBrowser.cxx:395
static bool IsMessageToStartDialog(const std::string &msg)
Check if this could be the message send by client to start new file dialog If returns true,...
static std::shared_ptr< RFileDialog > Embed(const std::shared_ptr< RWebWindow > &window, unsigned connid, const std::string &args)
Create dialog instance to use as embedded dialog inside other widget Embedded dialog started on the c...
const_iterator begin() const
const_iterator end() const
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
const std::string & GetWidgetKind() const
returns widget kind
Represents web window, which can be shown in web browser or any other supported environment.
std::string GetUrl(bool remote=true)
Return URL string to connect web window URL typically includes extra parameters required for connecti...
static std::shared_ptr< RWebWindow > Create()
Create new RWebWindow Using default RWebWindowsManager.
static unsigned ShowWindow(std::shared_ptr< RWebWindow > window, const RWebDisplayArgs &args="")
Static method to show web window Has to be used instead of RWebWindow::Show() when window potentially...
This class creates the ROOT Application Environment that interfaces to the windowing system eventloop...
static TString ToJSON(const T *obj, Int_t compact=0, const char *member_name=nullptr)
Definition TBufferJSON.h:75
@ kNoSpaces
no new lines plus remove all spaces around "," and ":" symbols
Definition TBufferJSON.h:39
Definition TRint.h:31
Basic string class.
Definition TString.h:139
const char * Data() const
Definition TString.h:376
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2356
virtual Int_t RedirectOutput(const char *name, const char *mode="a", RedirectHandle_t *h=nullptr)
Redirect standard output (stdout, stderr) to the specified file.
Definition TSystem.cxx:1727
virtual int GetPid()
Get process id.
Definition TSystem.cxx:718
virtual TTime Now()
Get current time in milliseconds since 0:00 Jan 1 1995.
Definition TSystem.cxx:463
virtual const char * UnixPathName(const char *unixpathname)
Convert from a local pathname to a Unix pathname.
Definition TSystem.cxx:1075
virtual const char * HomeDirectory(const char *userName=nullptr)
Return the user's home directory.
Definition TSystem.cxx:899
virtual int Unlink(const char *name)
Unlink, i.e.
Definition TSystem.cxx:1393
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
Definition TSystem.cxx:1494
Handles synchronous and a-synchronous timer events.
Definition TTimer.h:51
TLine * line
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
ROOT::RLogChannel & BrowserLog()
Log channel for Browser diagnostics.