Logo ROOT  
Reference Guide
 
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 "TString.h"
26#include "TSystem.h"
27#include "TError.h"
28#include "TTimer.h"
29#include "TROOT.h"
30#include "TBufferJSON.h"
31#include "TApplication.h"
32#include "TRint.h"
33#include "Getline.h"
34
35#include <sstream>
36#include <iostream>
37#include <algorithm>
38#include <memory>
39#include <mutex>
40#include <thread>
41#include <fstream>
42
43using namespace std::string_literals;
44
45namespace ROOT {
46
47class RBrowserTimer : public TTimer {
48public:
49 RBrowser &fBrowser; ///!< browser processing postponed requests
50
51 /// constructor
52 RBrowserTimer(Long_t milliSec, Bool_t mode, RBrowser &br) : TTimer(milliSec, mode), fBrowser(br) {}
53
54 /// timeout handler
55 /// used to process postponed requests in main ROOT thread
57};
58
59
61public:
62
63 bool fIsEditor{true}; ///<! either editor or image viewer
64 std::string fTitle;
65 std::string fFileName;
66 std::string fContent;
67 bool fFirstSend{false}; ///<! if editor content was send at least once
68 std::string fItemPath; ///<! item path in the browser
69
70 RBrowserEditorWidget(const std::string &name, bool is_editor = true) : RBrowserWidget(name), fIsEditor(is_editor) {}
71 virtual ~RBrowserEditorWidget() = default;
72
73 void ResetConn() override { fFirstSend = false; }
74
75 std::string GetKind() const override { return fIsEditor ? "editor"s : "image"s; }
76 std::string GetTitle() override { return fTitle; }
77 std::string GetUrl() override { return ""s; }
78
79 void Show(const std::string &) override {}
80
81 bool DrawElement(std::shared_ptr<Browsable::RElement> &elem, const std::string & = "") override
82 {
83 if (fIsEditor && elem->IsCapable(Browsable::RElement::kActEdit)) {
84 auto code = elem->GetContent("text");
85 if (!code.empty()) {
86 fFirstSend = false;
87 fContent = code;
88 fTitle = elem->GetName();
89 fFileName = elem->GetContent("filename");
90 } else {
91 auto json = elem->GetContent("json");
92 if (!json.empty()) {
93 fFirstSend = false;
94 fContent = json;
95 fTitle = elem->GetName() + ".json";
96 fFileName = "";
97 }
98 }
99 if (!fContent.empty()) {
100 // page->fItemPath = item_path;
101 return true;
102 }
103 }
104
105 if (!fIsEditor && elem->IsCapable(Browsable::RElement::kActImage)) {
106 auto img = elem->GetContent("image64");
107 if (!img.empty()) {
108 fFirstSend = false;
109 fContent = img;
110 fTitle = elem->GetName();
111 fFileName = elem->GetContent("filename");
112 // fItemPath = item_path;
113
114 return true;
115 }
116 }
117
118 return false;
119 }
120
121 std::string SendWidgetContent() override
122 {
123 if (fFirstSend) return ""s;
124
125 fFirstSend = true;
126 std::vector<std::string> args = { GetName(), fTitle, fFileName, fContent };
127
128 std::string msg = fIsEditor ? "EDITOR:"s : "IMAGE:"s;
129 msg += TBufferJSON::ToJSON(&args).Data();
130 return msg;
131 }
132
133};
134
135
137public:
138
139 enum { kMaxContentLen = 10000000 };
140
141 std::string fTitle;
142 std::string fContent;
143 bool fFirstSend{false}; ///<! if editor content was send at least once
144
146 {
147 fTitle = "Cling info"s;
148 Refresh();
149 }
150
151 virtual ~RBrowserInfoWidget() = default;
152
153 void ResetConn() override { fFirstSend = false; }
154
155 std::string GetKind() const override { return "info"s; }
156 std::string GetTitle() override { return fTitle; }
157 std::string GetUrl() override { return ""s; }
158
159 void Show(const std::string &) override {}
160
161 bool DrawElement(std::shared_ptr<Browsable::RElement> &, const std::string & = "") override { return false; }
162
163 void Refresh()
164 {
165 fFirstSend = false;
166 fContent = "";
167
168 std::ostringstream pathtmp;
169 pathtmp << gSystem->TempDirectory() << "/info." << gSystem->GetPid() << ".log";
170
171 std::ofstream ofs(pathtmp.str(), std::ofstream::out | std::ofstream::app);
172 ofs << "";
173 ofs.close();
174
175 gSystem->RedirectOutput(pathtmp.str().c_str(), "a");
176 gROOT->ProcessLine(".g");
177 gSystem->RedirectOutput(nullptr);
178
179 std::ifstream infile(pathtmp.str());
180 if (infile) {
181 std::string line;
182 while (std::getline(infile, line) && (fContent.length() < kMaxContentLen)) {
183 fContent.append(line);
184 fContent.append("\n");
185 }
186 }
187
188 gSystem->Unlink(pathtmp.str().c_str());
189 }
190
191 void RefreshFromLogs(const std::string &promt, const std::vector<std::string> &logs)
192 {
193 int indx = 0, last_prompt = -1;
194 for (auto &line : logs) {
195 if (line == promt)
196 last_prompt = indx;
197 indx++;
198 }
199
200 if (last_prompt < 0) {
201 Refresh();
202 return;
203 }
204
205 fFirstSend = false;
206 fContent = "";
207
208 indx = 0;
209 for (auto &line : logs) {
210 if ((indx++ > last_prompt) && (fContent.length() < kMaxContentLen)) {
211 fContent.append(line);
212 fContent.append("\n");
213 }
214 }
215 }
216
217
218 std::string SendWidgetContent() override
219 {
220 if (fFirstSend)
221 return ""s;
222
223 if (fContent.empty())
224 Refresh();
225
226 fFirstSend = true;
227 std::vector<std::string> args = { GetName(), fTitle, fContent };
228
229 return "INFO:"s + TBufferJSON::ToJSON(&args).Data();
230 }
231
232};
233
234
236public:
237
238 RWebWindow *fWindow{nullptr}; // catched widget, TODO: to be changed to shared_ptr
239 std::string fCatchedKind; // kind of catched widget
240
241 void Show(const std::string &) override {}
242
243 std::string GetKind() const override { return "catched"s; }
244
245 std::string GetUrl() override { return fWindow ? fWindow->GetUrl(false) : ""s; }
246
247 std::string GetTitle() override { return fCatchedKind; }
248
249 bool IsValid() override { return fWindow != nullptr; }
250
251 RBrowserCatchedWidget(const std::string &name, RWebWindow *win, const std::string &kind) :
253 fWindow(win),
254 fCatchedKind(kind)
255 {
256 }
257};
258
259} // namespace ROOT
260
261using namespace ROOT;
262
263
264/** \class ROOT::RBrowser
265\ingroup rbrowser
266\ingroup webwidgets
267
268\brief Web-based %ROOT files and objects browser
269
270\image html v7_rbrowser.png
271
272*/
273
274//////////////////////////////////////////////////////////////////////////////////////////////
275/// constructor
276
277RBrowser::RBrowser(bool use_rcanvas)
278{
279 if (gROOT->IsWebDisplayBatch()) {
280 ::Warning("RBrowser::RBrowser", "The RBrowser cannot run in web batch mode");
281 return;
282 }
283
284 std::ostringstream pathtmp;
285 pathtmp << gSystem->TempDirectory() << "/command." << gSystem->GetPid() << ".log";
286 fPromptFileOutput = pathtmp.str();
287
288 SetUseRCanvas(use_rcanvas);
289
291
292 fTimer = std::make_unique<RBrowserTimer>(10, kTRUE, *this);
293
295 fWebWindow->SetDefaultPage("file:rootui5sys/browser/browser.html");
296
297 // this is call-back, invoked when message received via websocket
298 fWebWindow->SetCallBacks([this](unsigned connid) { fConnId = connid; SendInitMsg(connid); },
299 [this](unsigned connid, const std::string &arg) { ProcessMsg(connid, arg); });
300 fWebWindow->SetGeometry(1200, 700); // configure predefined window geometry
301 fWebWindow->SetConnLimit(1); // the only connection is allowed
302 fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue
303
304 fWebWindow->GetManager()->SetShowCallback([this](RWebWindow &win, const RWebDisplayArgs &args) -> bool {
305
306 std::string kind;
307
308 if (args.GetWidgetKind() == "RCanvas")
309 kind = "rcanvas";
310 else if (args.GetWidgetKind() == "TCanvas")
311 kind = "tcanvas";
312 else if (args.GetWidgetKind() == "RGeomViewer")
313 kind = "geom";
314 else if (args.GetWidgetKind() == "RTreeViewer")
315 kind = "tree";
316
317 if (!fWebWindow || !fCatchWindowShow || kind.empty()) return false;
318
319 auto widget = AddCatchedWidget(&win, kind);
320
321 if (widget && fWebWindow && (fWebWindow->NumConnections() > 0))
322 fWebWindow->Send(0, NewWidgetMsg(widget));
323
324 return widget ? true : false;
325 });
326
327 fWebWindow->GetManager()->SetDeleteCallback([this](RWebWindow &win) -> void {
328 for (auto &widget : fWidgets) {
329 auto catched = dynamic_cast<RBrowserCatchedWidget *>(widget.get());
330 if (catched && (catched->fWindow == &win))
331 catched->fWindow = nullptr;
332 }
333 });
334
335 Show();
336
337 // add first canvas by default
338
339 //if (GetUseRCanvas())
340 // AddWidget("rcanvas");
341 //else
342 // AddWidget("tcanvas");
343
344 // AddWidget("geom"); // add geometry viewer at the beginning
345
346 // AddWidget("editor"); // one can add empty editor if necessary
347}
348
349//////////////////////////////////////////////////////////////////////////////////////////////
350/// destructor
351
353{
354 if (fWebWindow) {
355 fWebWindow->GetManager()->SetShowCallback(nullptr);
356 fWebWindow->GetManager()->SetDeleteCallback(nullptr);
357 }
358}
359
360//////////////////////////////////////////////////////////////////////////////////////////////
361/// Process browser request
362
363std::string RBrowser::ProcessBrowserRequest(const std::string &msg)
364{
365 std::unique_ptr<RBrowserRequest> request;
366
367 if (msg.empty()) {
368 request = std::make_unique<RBrowserRequest>();
369 request->first = 0;
370 request->number = 100;
371 } else {
372 request = TBufferJSON::FromJSON<RBrowserRequest>(msg);
373 }
374
375 if (!request)
376 return ""s;
377
378 if (request->path.empty() && fWidgets.empty() && fBrowsable.GetWorkingPath().empty())
380
381 return "BREPL:"s + fBrowsable.ProcessRequest(*request.get());
382}
383
384/////////////////////////////////////////////////////////////////////////////////
385/// Process file save command in the editor
386
387void RBrowser::ProcessSaveFile(const std::string &fname, const std::string &content)
388{
389 if (fname.empty()) return;
390 R__LOG_DEBUG(0, BrowserLog()) << "SaveFile " << fname << " content length " << content.length();
391 std::ofstream f(fname);
392 f << content;
393}
394
395/////////////////////////////////////////////////////////////////////////////////
396/// Process run macro command in the editor
397
398void RBrowser::ProcessRunMacro(const std::string &file_path)
399{
400 if (file_path.rfind(".py") == file_path.length() - 3) {
401 TString exec;
402 exec.Form("TPython::ExecScript(\"%s\");", file_path.c_str());
403 gROOT->ProcessLine(exec.Data());
404 } else {
405 gInterpreter->ExecuteMacro(file_path.c_str());
406 }
407}
408
409/////////////////////////////////////////////////////////////////////////////////
410/// Process dbl click on browser item
411
412std::string RBrowser::ProcessDblClick(unsigned connid, std::vector<std::string> &args)
413{
414 args.pop_back(); // remove exec string, not used now
415
416 std::string opt = args.back();
417 args.pop_back(); // remove option
418
419 auto path = fBrowsable.GetWorkingPath();
420 path.insert(path.end(), args.begin(), args.end());
421
422 R__LOG_DEBUG(0, BrowserLog()) << "DoubleClick " << Browsable::RElement::GetPathAsString(path);
423
424 auto elem = fBrowsable.GetSubElement(path);
425 if (!elem) return ""s;
426
427 auto dflt_action = elem->GetDefaultAction();
428
429 // special case when canvas is clicked - always start new widget
430 if (dflt_action == Browsable::RElement::kActCanvas) {
431 std::string widget_kind;
432
433 if (elem->IsCapable(Browsable::RElement::kActDraw7))
434 widget_kind = "rcanvas";
435 else
436 widget_kind = "tcanvas";
437
438 std::string name = widget_kind + std::to_string(++fWidgetCnt);
439
440 auto new_widget = RBrowserWidgetProvider::CreateWidgetFor(widget_kind, name, elem);
441
442 if (!new_widget)
443 return ""s;
444
445 // assign back pointer
446 new_widget->fBrowser = this;
447
448 new_widget->Show("embed");
449 fWidgets.emplace_back(new_widget);
450 fActiveWidgetName = new_widget->GetName();
451
452 return NewWidgetMsg(new_widget);
453 }
454
455 // before display tree or geometry ensure that they read and cached inside element
456 if (elem->IsCapable(Browsable::RElement::kActGeom) || elem->IsCapable(Browsable::RElement::kActTree)) {
457 elem->GetChildsIter();
458 }
459
461 Browsable::RProvider::ProgressHandle handle(elem.get(), [this, connid](float progress, void *) {
462 SendProgress(connid, progress);
463 });
464
465 auto widget = GetActiveWidget();
466 if (widget && widget->DrawElement(elem, opt)) {
467 widget->SetPath(path);
468 return widget->SendWidgetContent();
469 }
470
471 // check if element was drawn in other widget and just activate that widget
472 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(),
473 [path](const std::shared_ptr<RBrowserWidget> &wg) { return path == wg->GetPath(); });
474
475 if (iter != fWidgets.end())
476 return "SELECT_WIDGET:"s + (*iter)->GetName();
477
478 // check if object can be drawn in RCanvas even when default action is drawing in TCanvas
479 if ((dflt_action == Browsable::RElement::kActDraw6) && GetUseRCanvas() && elem->IsCapable(Browsable::RElement::kActDraw7))
480 dflt_action = Browsable::RElement::kActDraw7;
481
482 std::string widget_kind;
483 switch(dflt_action) {
484 case Browsable::RElement::kActDraw6: widget_kind = "tcanvas"; break;
485 case Browsable::RElement::kActDraw7: widget_kind = "rcanvas"; break;
486 case Browsable::RElement::kActEdit: widget_kind = "editor"; break;
487 case Browsable::RElement::kActImage: widget_kind = "image"; break;
488 case Browsable::RElement::kActTree: widget_kind = "tree"; break;
489 case Browsable::RElement::kActGeom: widget_kind = "geom"; break;
490 default: widget_kind.clear();
491 }
492
493 if (!widget_kind.empty()) {
494 auto new_widget = AddWidget(widget_kind);
495 if (new_widget) {
496 // draw object before client side is created - should not be a problem
497 // after widget add in browser, connection will be established and data provided
498 if (new_widget->DrawElement(elem, opt))
499 new_widget->SetPath(path);
500 return NewWidgetMsg(new_widget);
501 }
502 }
503
504 if (elem->IsCapable(Browsable::RElement::kActBrowse) && (elem->GetNumChilds() > 0)) {
505 // remove extra index in subitems name
506 for (auto &pathelem : path)
510 }
511
512 return ""s;
513}
514
515/////////////////////////////////////////////////////////////////////////////////
516/// Show or update RBrowser in web window
517/// If web window already started - just refresh it like "reload" button does
518/// If no web window exists or \param always_start_new_browser configured, starts new window
519/// \param args display arguments
520
521void RBrowser::Show(const RWebDisplayArgs &args, bool always_start_new_browser)
522{
523 if (!fWebWindow->NumConnections() || always_start_new_browser) {
524 fWebWindow->Show(args);
525 } else {
526 SendInitMsg(0);
527 }
528}
529
530///////////////////////////////////////////////////////////////////////////////////////////////////////
531/// Hide ROOT Browser
532
534{
535 if (fWebWindow)
536 fWebWindow->CloseConnections();
537}
538
539///////////////////////////////////////////////////////////////////////////////////////////////////////
540/// Return URL parameter for the window showing ROOT Browser
541/// See \ref ROOT::RWebWindow::GetUrl docu for more details
542
543std::string RBrowser::GetWindowUrl(bool remote)
544{
545 if (fWebWindow)
546 return fWebWindow->GetUrl(remote);
547
548 return ""s;
549}
550
551
552//////////////////////////////////////////////////////////////////////////////////////////////
553/// Creates new widget
554
555std::shared_ptr<RBrowserWidget> RBrowser::AddWidget(const std::string &kind)
556{
557 std::string name = kind + std::to_string(++fWidgetCnt);
558
559 std::shared_ptr<RBrowserWidget> widget;
560
561 if (kind == "editor"s)
562 widget = std::make_shared<RBrowserEditorWidget>(name, true);
563 else if (kind == "image"s)
564 widget = std::make_shared<RBrowserEditorWidget>(name, false);
565 else if (kind == "info"s)
566 widget = std::make_shared<RBrowserInfoWidget>(name);
567 else
569
570 if (!widget) {
571 R__LOG_ERROR(BrowserLog()) << "Fail to create widget of kind " << kind;
572 return nullptr;
573 }
574
575 widget->fBrowser = this;
576 widget->Show("embed");
577 fWidgets.emplace_back(widget);
578
580
581 return widget;
582}
583
584//////////////////////////////////////////////////////////////////////////////////////////////
585/// Add widget catched from external scripts
586
587std::shared_ptr<RBrowserWidget> RBrowser::AddCatchedWidget(RWebWindow *win, const std::string &kind)
588{
589 if (!win || kind.empty()) return nullptr;
590
591 std::string name = "catched"s + std::to_string(++fWidgetCnt);
592
593 auto widget = std::make_shared<RBrowserCatchedWidget>(name, win, kind);
594
595 fWidgets.emplace_back(widget);
596
598
599 return widget;
600}
601
602
603//////////////////////////////////////////////////////////////////////////////////////////////
604/// Create new widget and send init message to the client
605
606void RBrowser::AddInitWidget(const std::string &kind)
607{
608 auto widget = AddWidget(kind);
609 if (widget && fWebWindow && (fWebWindow->NumConnections() > 0))
610 fWebWindow->Send(0, NewWidgetMsg(widget));
611}
612
613//////////////////////////////////////////////////////////////////////////////////////////////
614/// Find widget by name or kind
615
616std::shared_ptr<RBrowserWidget> RBrowser::FindWidget(const std::string &name, const std::string &kind) const
617{
618 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(),
619 [name, kind](const std::shared_ptr<RBrowserWidget> &widget) {
620 return kind.empty() ? name == widget->GetName() : kind == widget->GetKind();
621 });
622
623 if (iter != fWidgets.end())
624 return *iter;
625
626 return nullptr;
627}
628
629//////////////////////////////////////////////////////////////////////////////////////////////
630/// Close and delete specified widget
631
632void RBrowser::CloseTab(const std::string &name)
633{
634 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(), [name](std::shared_ptr<RBrowserWidget> &widget) { return name == widget->GetName(); });
635 if (iter != fWidgets.end())
636 fWidgets.erase(iter);
637
638 if (fActiveWidgetName == name)
639 fActiveWidgetName.clear();
640}
641
642//////////////////////////////////////////////////////////////////////////////////////////////
643/// Get content of history file
644
645std::vector<std::string> RBrowser::GetRootHistory()
646{
647 std::vector<std::string> arr;
648
649 std::string path = gSystem->UnixPathName(gSystem->HomeDirectory());
650 path += "/.root_hist" ;
651 std::ifstream infile(path);
652
653 if (infile) {
654 std::string line;
655 while (std::getline(infile, line) && (arr.size() < 1000)) {
656 if(!(std::find(arr.begin(), arr.end(), line) != arr.end())) {
657 arr.emplace_back(line);
658 }
659 }
660 }
661
662 return arr;
663}
664
665//////////////////////////////////////////////////////////////////////////////////////////////
666/// Get content of log file
667
668std::vector<std::string> RBrowser::GetRootLogs()
669{
670 std::vector<std::string> arr;
671
672 std::ifstream infile(fPromptFileOutput);
673 if (infile) {
674 std::string line;
675 while (std::getline(infile, line) && (arr.size() < 10000)) {
676 arr.emplace_back(line);
677 }
678 }
679
680 return arr;
681}
682
683//////////////////////////////////////////////////////////////////////////////////////////////
684/// Process client connect
685
686void RBrowser::SendInitMsg(unsigned connid)
687{
688 std::vector<std::vector<std::string>> reply;
689
690 reply.emplace_back(fBrowsable.GetWorkingPath()); // first element is current path
691
692 for (auto &widget : fWidgets) {
693 widget->ResetConn();
694 reply.emplace_back(std::vector<std::string>({ widget->GetKind(), ".."s + widget->GetUrl(), widget->GetName(), widget->GetTitle() }));
695 }
696
697 if (!fActiveWidgetName.empty())
698 reply.emplace_back(std::vector<std::string>({ "active"s, fActiveWidgetName }));
699
700 auto history = GetRootHistory();
701 if (history.size() > 0) {
702 history.insert(history.begin(), "history"s);
703 reply.emplace_back(history);
704 }
705
706 auto logs = GetRootLogs();
707 if (logs.size() > 0) {
708 logs.insert(logs.begin(), "logs"s);
709 reply.emplace_back(logs);
710 }
711
712 reply.emplace_back(std::vector<std::string>({
713 "drawoptions"s,
717 }));
718
719 std::string msg = "INMSG:";
720 msg.append(TBufferJSON::ToJSON(&reply, TBufferJSON::kNoSpaces).Data());
721
722 fWebWindow->Send(connid, msg);
723}
724
725//////////////////////////////////////////////////////////////////////////////////////////////
726/// Send generic progress message to the web window
727/// Should show progress bar on client side
728
729void RBrowser::SendProgress(unsigned connid, float progr)
730{
731 long long millisec = gSystem->Now();
732
733 // let process window events
734 fWebWindow->Sync();
735
736 if ((!fLastProgressSendTm || millisec > fLastProgressSendTm - 200) && (progr > fLastProgressSend + 0.04) && fWebWindow->CanSend(connid)) {
737 fWebWindow->Send(connid, "PROGRESS:"s + std::to_string(progr));
738
739 fLastProgressSendTm = millisec;
740 fLastProgressSend = progr;
741 }
742}
743
744
745//////////////////////////////////////////////////////////////////////////////////////////////
746/// Return the current directory of ROOT
747
749{
750 return "WORKPATH:"s + TBufferJSON::ToJSON(&fBrowsable.GetWorkingPath()).Data();
751}
752
753//////////////////////////////////////////////////////////////////////////////////////////////
754/// Create message which send to client to create new widget
755
756std::string RBrowser::NewWidgetMsg(std::shared_ptr<RBrowserWidget> &widget)
757{
758 std::vector<std::string> arr = { widget->GetKind(), ".."s + widget->GetUrl(), widget->GetName(), widget->GetTitle(),
759 Browsable::RElement::GetPathAsString(widget->GetPath()) };
760 return "NEWWIDGET:"s + TBufferJSON::ToJSON(&arr, TBufferJSON::kNoSpaces).Data();
761}
762
763//////////////////////////////////////////////////////////////////////////////////////////////
764/// Check if any widget was modified and update if necessary
765
767{
768 std::vector<std::string> del_names;
769
770 for (auto &widget : fWidgets)
771 if (!widget->IsValid())
772 del_names.push_back(widget->GetName());
773
774 if (!del_names.empty())
775 fWebWindow->Send(connid, "CLOSE_WIDGETS:"s + TBufferJSON::ToJSON(&del_names, TBufferJSON::kNoSpaces).Data());
776
777 for (auto name : del_names)
778 CloseTab(name);
779
780 for (auto &widget : fWidgets)
781 widget->CheckModified();
782}
783
784//////////////////////////////////////////////////////////////////////////////////////////////
785/// Process postponed requests - decouple from websocket handling
786/// Only requests which can take longer time should be postponed
787
789{
790 if (fPostponed.empty())
791 return;
792
793 auto arr = fPostponed[0];
794 fPostponed.erase(fPostponed.begin(), fPostponed.begin()+1);
795 if (fPostponed.empty())
796 fTimer->TurnOff();
797
798 std::string reply;
799 unsigned connid = std::stoul(arr.back()); arr.pop_back();
800 std::string kind = arr.back(); arr.pop_back();
801
802 if (kind == "DBLCLK") {
803 reply = ProcessDblClick(connid, arr);
804 if (reply.empty()) reply = "NOPE";
805 }
806
807 if (!reply.empty())
808 fWebWindow->Send(connid, reply);
809}
810
811
812//////////////////////////////////////////////////////////////////////////////////////////////
813/// Process received message from the client
814
815void RBrowser::ProcessMsg(unsigned connid, const std::string &arg0)
816{
817 R__LOG_DEBUG(0, BrowserLog()) << "ProcessMsg len " << arg0.length() << " substr(30) " << arg0.substr(0, 30);
818
819 std::string kind, msg;
820 auto pos = arg0.find(":");
821 if (pos == std::string::npos) {
822 kind = arg0;
823 } else {
824 kind = arg0.substr(0, pos);
825 msg = arg0.substr(pos+1);
826 }
827
828 if (kind == "QUIT_ROOT") {
829
830 fWebWindow->TerminateROOT();
831
832 } else if (kind == "BRREQ") {
833 // central place for processing browser requests
834 auto json = ProcessBrowserRequest(msg);
835 if (!json.empty()) fWebWindow->Send(connid, json);
836
837 } else if (kind == "DBLCLK") {
838
839 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
840 if (arr && (arr->size() > 2)) {
841 arr->push_back(kind);
842 arr->push_back(std::to_string(connid));
843 fPostponed.push_back(*arr);
844 if (fPostponed.size() == 1)
845 fTimer->TurnOn();
846 } else {
847 fWebWindow->Send(connid, "NOPE");
848 }
849
850 } else if (kind == "WIDGET_SELECTED") {
851 fActiveWidgetName = msg;
852 auto widget = GetActiveWidget();
853 if (widget) {
854 auto reply = widget->SendWidgetContent();
855 if (!reply.empty()) fWebWindow->Send(connid, reply);
856 }
857 } else if (kind == "CLOSE_TAB") {
858 CloseTab(msg);
859 } else if (kind == "GETWORKPATH") {
860 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
861 } else if (kind == "CHPATH") {
862 auto path = TBufferJSON::FromJSON<Browsable::RElementPath_t>(msg);
863 if (path) fBrowsable.SetWorkingPath(*path);
864 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
865 } else if (kind == "CMD") {
866 std::string sPrompt = "root []";
867 TApplication *app = gROOT->GetApplication();
868 if (app->InheritsFrom("TRint")) {
869 sPrompt = ((TRint*)gROOT->GetApplication())->GetPrompt();
870 Gl_histadd((char *)msg.c_str());
871 }
872
873 std::ofstream ofs(fPromptFileOutput, std::ofstream::out | std::ofstream::app);
874 ofs << sPrompt << msg << std::endl;
875 ofs.close();
876
878 gROOT->ProcessLine(msg.c_str());
879 gSystem->RedirectOutput(nullptr);
880
881 if (msg == ".g"s) {
882 auto widget = std::dynamic_pointer_cast<RBrowserInfoWidget>(FindWidget(""s, "info"s));
883 if (!widget) {
884 auto new_widget = AddWidget("info"s);
885 fWebWindow->Send(connid, NewWidgetMsg(new_widget));
886 widget = std::dynamic_pointer_cast<RBrowserInfoWidget>(new_widget);
887 } else if (fActiveWidgetName != widget->GetName()) {
888 fWebWindow->Send(connid, "SELECT_WIDGET:"s + widget->GetName());
889 fActiveWidgetName = widget->GetName();
890 }
891
892 if (widget)
893 widget->RefreshFromLogs(sPrompt + msg, GetRootLogs());
894 }
895
896 CheckWidgtesModified(connid);
897 } else if (kind == "GETHISTORY") {
898
899 auto history = GetRootHistory();
900
901 fWebWindow->Send(connid, "HISTORY:"s + TBufferJSON::ToJSON(&history, TBufferJSON::kNoSpaces).Data());
902 } else if (kind == "GETLOGS") {
903
904 auto logs = GetRootLogs();
905 fWebWindow->Send(connid, "LOGS:"s + TBufferJSON::ToJSON(&logs, TBufferJSON::kNoSpaces).Data());
906
907 } else if (RFileDialog::IsMessageToStartDialog(arg0)) {
908
909 RFileDialog::Embed(fWebWindow, connid, arg0);
910
911 } else if (kind == "SYNCEDITOR") {
912 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
913 if (arr && (arr->size() > 4)) {
914 auto editor = std::dynamic_pointer_cast<RBrowserEditorWidget>(FindWidget(arr->at(0)));
915 if (editor) {
916 editor->fFirstSend = true;
917 editor->fTitle = arr->at(1);
918 editor->fFileName = arr->at(2);
919 if (!arr->at(3).empty()) editor->fContent = arr->at(4);
920 if ((arr->size() == 6) && (arr->at(5) == "SAVE"))
921 ProcessSaveFile(editor->fFileName, editor->fContent);
922 if ((arr->size() == 6) && (arr->at(5) == "RUN")) {
923 ProcessSaveFile(editor->fFileName, editor->fContent);
924 ProcessRunMacro(editor->fFileName);
925 CheckWidgtesModified(connid);
926 }
927 }
928 }
929 } else if (kind == "GETINFO") {
930 auto info = std::dynamic_pointer_cast<RBrowserInfoWidget>(FindWidget(msg));
931 if (info) {
932 info->Refresh();
933 fWebWindow->Send(connid, info->SendWidgetContent());
934 }
935 } else if (kind == "NEWWIDGET") {
936 auto widget = AddWidget(msg);
937 if (widget)
938 fWebWindow->Send(connid, NewWidgetMsg(widget));
939 } else if (kind == "CDWORKDIR") {
941 if (fBrowsable.GetWorkingPath() != wrkdir) {
943 } else {
945 }
946 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
947 } else if (kind == "OPTIONS") {
948 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
949 if (arr && (arr->size() == 3)) {
952 Browsable::RProvider::SetClassDrawOption("TProfile", (*arr)[2]);
953 }
954 }
955}
956
957//////////////////////////////////////////////////////////////////////////////////////////////
958/// Set working path in the browser
959
960void RBrowser::SetWorkingPath(const std::string &path)
961{
963 auto elem = fBrowsable.GetSubElement(p);
964 if (elem) {
966 if (fWebWindow && (fWebWindow->NumConnections() > 0))
968 }
969}
970
971//////////////////////////////////////////////////////////////////////////////////////////////
972/// Activate widget in RBrowser
973/// One should specify title and (optionally) kind of widget like "tcanvas" or "geom"
974
975bool RBrowser::ActivateWidget(const std::string &title, const std::string &kind)
976{
977 if (title.empty())
978 return false;
979
980 for (auto &widget : fWidgets) {
981
982 if (widget->GetTitle() != title)
983 continue;
984
985 if (!kind.empty() && (widget->GetKind() != kind))
986 continue;
987
988 if (fWebWindow)
989 fWebWindow->Send(0, "SELECT_WIDGET:"s + widget->GetName());
990 else
991 fActiveWidgetName = widget->GetName();
992 return true;
993 }
994
995 return false;
996}
997
998//////////////////////////////////////////////////////////////////////////////////////////////
999/// Set handle which will be cleared when connection is closed
1000
1001void RBrowser::ClearOnClose(const std::shared_ptr<void> &handle)
1002{
1003 fWebWindow->SetClearOnClose(handle);
1004}
nlohmann::json json
#define R__LOG_ERROR(...)
Definition RLogger.hxx:362
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition RLogger.hxx:365
#define f(i)
Definition RSha256.hxx:104
long Long_t
Definition RtypesCore.h:54
constexpr Bool_t kTRUE
Definition RtypesCore.h:93
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:561
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:251
void Show(const std::string &) override
Definition RBrowser.cxx:241
std::string GetUrl() override
Definition RBrowser.cxx:245
std::string GetKind() const override
Definition RBrowser.cxx:243
std::string GetTitle() override
Definition RBrowser.cxx:247
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:76
std::string fItemPath
! item path in the browser
Definition RBrowser.cxx:68
void ResetConn() override
Definition RBrowser.cxx:73
std::string GetKind() const override
Definition RBrowser.cxx:75
bool fFirstSend
! if editor content was send at least once
Definition RBrowser.cxx:67
RBrowserEditorWidget(const std::string &name, bool is_editor=true)
Definition RBrowser.cxx:70
bool fIsEditor
! either editor or image viewer
Definition RBrowser.cxx:63
std::string GetUrl() override
Definition RBrowser.cxx:77
virtual ~RBrowserEditorWidget()=default
void Show(const std::string &) override
Definition RBrowser.cxx:79
bool DrawElement(std::shared_ptr< Browsable::RElement > &elem, const std::string &="") override
Definition RBrowser.cxx:81
std::string SendWidgetContent() override
Definition RBrowser.cxx:121
void RefreshFromLogs(const std::string &promt, const std::vector< std::string > &logs)
Definition RBrowser.cxx:191
void ResetConn() override
Definition RBrowser.cxx:153
RBrowserInfoWidget(const std::string &name)
Definition RBrowser.cxx:145
std::string GetTitle() override
Definition RBrowser.cxx:156
std::string GetKind() const override
Definition RBrowser.cxx:155
bool fFirstSend
! if editor content was send at least once
Definition RBrowser.cxx:143
std::string SendWidgetContent() override
Definition RBrowser.cxx:218
virtual ~RBrowserInfoWidget()=default
std::string GetUrl() override
Definition RBrowser.cxx:157
void Show(const std::string &) override
Definition RBrowser.cxx:159
bool DrawElement(std::shared_ptr< Browsable::RElement > &, const std::string &="") override
Definition RBrowser.cxx:161
RBrowser & fBrowser
Definition RBrowser.cxx:49
RBrowserTimer(Long_t milliSec, Bool_t mode, RBrowser &br)
!< browser processing postponed requests
Definition RBrowser.cxx:52
void Timeout() override
timeout handler used to process postponed requests in main ROOT thread
Definition RBrowser.cxx:56
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:555
std::vector< std::string > GetRootHistory()
Get content of history file.
Definition RBrowser.cxx:645
void AddInitWidget(const std::string &kind)
Create new widget and send init message to the client.
Definition RBrowser.cxx:606
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
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:412
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:277
void SetWorkingPath(const std::string &path)
Set working path in the browser.
Definition RBrowser.cxx:960
void Hide()
hide Browser
Definition RBrowser.cxx:533
std::string NewWidgetMsg(std::shared_ptr< RBrowserWidget > &widget)
Create message which send to client to create new widget.
Definition RBrowser.cxx:756
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:521
std::string GetCurrentWorkingDirectory()
Return the current directory of ROOT.
Definition RBrowser.cxx:748
void SetUseRCanvas(bool on=true)
Definition RBrowser.hxx:83
std::shared_ptr< RBrowserWidget > FindWidget(const std::string &name, const std::string &kind="") const
Find widget by name or kind.
Definition RBrowser.cxx:616
std::shared_ptr< RBrowserWidget > AddCatchedWidget(RWebWindow *win, const std::string &kind)
Add widget catched from external scripts.
Definition RBrowser.cxx:587
bool GetUseRCanvas() const
Definition RBrowser.hxx:82
std::vector< std::shared_ptr< RBrowserWidget > > fWidgets
! all browser widgets
Definition RBrowser.hxx:39
virtual ~RBrowser()
destructor
Definition RBrowser.cxx:352
void ProcessSaveFile(const std::string &fname, const std::string &content)
Process file save command in the editor.
Definition RBrowser.cxx:387
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:543
std::string ProcessBrowserRequest(const std::string &msg)
Process browser request.
Definition RBrowser.cxx:363
std::vector< std::string > GetRootLogs()
Get content of log file.
Definition RBrowser.cxx:668
void ProcessMsg(unsigned connid, const std::string &arg)
Process received message from the client.
Definition RBrowser.cxx:815
void CheckWidgtesModified(unsigned connid)
Check if any widget was modified and update if necessary.
Definition RBrowser.cxx:766
void CloseTab(const std::string &name)
Close and delete specified widget.
Definition RBrowser.cxx:632
void ProcessPostponedRequests()
Process postponed requests - decouple from websocket handling Only requests which can take longer tim...
Definition RBrowser.cxx:788
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...
Definition RBrowser.cxx:975
void SendInitMsg(unsigned connid)
Process client connect.
Definition RBrowser.cxx:686
void SendProgress(unsigned connid, float progr)
Send generic progress message to the web window Should show progress bar on client side.
Definition RBrowser.cxx:729
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:398
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...
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.
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
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:542
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:1715
virtual int GetPid()
Get process id.
Definition TSystem.cxx:707
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:1063
virtual const char * HomeDirectory(const char *userName=nullptr)
Return the user's home directory.
Definition TSystem.cxx:887
virtual int Unlink(const char *name)
Unlink, i.e.
Definition TSystem.cxx:1381
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
Definition TSystem.cxx:1482
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::Experimental::RLogChannel & BrowserLog()
Log channel for Browser diagnostics.