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
4/*************************************************************************
5 * Copyright (C) 1995-2021, 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
12#include <ROOT/RBrowser.hxx>
13
17
18#include <ROOT/RLogger.hxx>
19#include <ROOT/RFileDialog.hxx>
21
22#include "RBrowserWidget.hxx"
23
24#include "TVirtualPad.h"
25#include "TString.h"
26#include "TSystem.h"
27#include "TError.h"
28#include "TTimer.h"
29#include "TEnv.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
265For normal interactive mode, any modern web browser should be able to display it.
266Chrome or Firefox browsers are though required when running ROOT in batch mode.
267
268Most configuration options for RBrowser, such as default web browser, server mode are not specific to this class,
269but are rather applied for all web widgets: canvases, geometry viewer, eve7, browser, fit panel, etc.
270
271Following `.rootrc` parameters can be configured for the browser:
272
273 * WebGui.Browser.SortBy: sort by "name", "size", "none" (default "name")
274 * WebGui.Browser.Reverse: reverse item order (default off)
275 * WebGui.Browser.ShowHidden: show hidden files (default off)
276 * WebGui.Browser.LastCycle: show only last key cycle (default off)
277 * WebGui.Browser.Expand: expand browsable area (default off)
278
279\note See major settings in RWebWindowWindowsManager::CreateServer and RWebWindowsManager::ShowWindow
280*/
281
282//////////////////////////////////////////////////////////////////////////////////////////////
283/// constructor
284
286{
287 if (gROOT->IsWebDisplayBatch()) {
288 ::Warning("RBrowser::RBrowser", "The RBrowser cannot run in web batch mode");
289 return;
290 }
291
292 std::ostringstream pathtmp;
293 pathtmp << gSystem->TempDirectory() << "/command." << gSystem->GetPid() << ".log";
295
297
299
300 fTimer = std::make_unique<RBrowserTimer>(10, kTRUE, *this);
301
303 if (!fWebWindow)
304 return;
305
306 fWebWindow->SetDefaultPage("file:rootui5sys/browser/browser.html");
307
308 std::string sortby = gEnv->GetValue("WebGui.Browser.SortBy", "name"),
309 reverse = gEnv->GetValue("WebGui.Browser.Reverse", "no"),
310 hidden = gEnv->GetValue("WebGui.Browser.ShowHidden", "no"),
311 lastcycle = gEnv->GetValue("WebGui.Browser.LastCycle", "");
312
313 if (sortby != "name" && sortby != "size" && sortby != "none")
314 sortby = "name";
315
316 reverse = (reverse == "on" || reverse == "yes" || reverse == "1") ? "true" : "false";
317 hidden = (hidden == "on" || hidden == "yes" || hidden == "1") ? "true" : "false";
318 if (lastcycle == "on" || lastcycle == "yes" || lastcycle == "1")
320 else if (lastcycle == "off" || lastcycle == "no" || lastcycle == "0")
322
323 fWebWindow->SetUserArgs(TString::Format("{ sort: \"%s\", reverse: %s, hidden: %s }", sortby.c_str(), reverse.c_str(), hidden.c_str()).Data());
324
325 // this is call-back, invoked when message received via websocket
326 fWebWindow->SetCallBacks([this](unsigned connid) { fConnId = connid; SendInitMsg(connid); },
327 [this](unsigned connid, const std::string &arg) { ProcessMsg(connid, arg); });
328 fWebWindow->SetGeometry(1200, 700); // configure predefined window geometry
329 fWebWindow->SetConnLimit(1); // the only connection is allowed
330 fWebWindow->SetMaxQueueLength(30); // number of allowed entries in the window queue
331
332 fWebWindow->GetManager()->SetShowCallback([this](RWebWindow &win, const RWebDisplayArgs &args) -> bool {
333
334 std::string kind;
335
336 if (args.GetWidgetKind() == "RCanvas")
337 kind = "rcanvas";
338 else if (args.GetWidgetKind() == "TCanvas")
339 kind = "tcanvas";
340 else if (args.GetWidgetKind() == "RGeomViewer")
341 kind = "geom";
342 else if (args.GetWidgetKind() == "RTreeViewer")
343 kind = "tree";
344
345 if (!fWebWindow || !fCatchWindowShow || kind.empty())
346 return false;
347
348 // before create new widget check if other disappear
350
352 if (widget) {
353 widget->fBrowser = this;
354 fWidgets.emplace_back(widget);
355 fActiveWidgetName = widget->GetName();
356 } else {
357 widget = AddCatchedWidget(&win, kind);
358 }
359
360 if (widget && fWebWindow && (fWebWindow->NumConnections() > 0))
361 fWebWindow->Send(0, NewWidgetMsg(widget));
362
363 return widget ? true : false;
364 });
365
366 fWebWindow->GetManager()->SetDeleteCallback([this](RWebWindow &win) -> void {
367 for (auto &widget : fWidgets) {
368 auto catched = dynamic_cast<RBrowserCatchedWidget *>(widget.get());
369 if (catched && (catched->fWindow == &win))
370 catched->fWindow = nullptr;
371 }
372
373 if (fWebWindow)
375 });
376
377 Show();
378}
379
380//////////////////////////////////////////////////////////////////////////////////////////////
381/// destructor
382
384{
385 if (fWebWindow) {
386 fWebWindow->GetManager()->SetShowCallback(nullptr);
387 fWebWindow->GetManager()->SetDeleteCallback(nullptr);
388 fWebWindow->Reset();
389 }
390}
391
392//////////////////////////////////////////////////////////////////////////////////////////////
393/// Process browser request
394
395std::string RBrowser::ProcessBrowserRequest(const std::string &msg)
396{
397 std::unique_ptr<RBrowserRequest> request;
398
399 if (msg.empty()) {
400 request = std::make_unique<RBrowserRequest>();
401 request->first = 0;
402 request->number = 100;
403 } else {
404 request = TBufferJSON::FromJSON<RBrowserRequest>(msg);
405 }
406
407 if (!request)
408 return ""s;
409
410 if (request->path.empty() && fWidgets.empty() && fBrowsable.GetWorkingPath().empty())
412
413 return "BREPL:"s + fBrowsable.ProcessRequest(*request.get());
414}
415
416/////////////////////////////////////////////////////////////////////////////////
417/// Process file save command in the editor
418
419void RBrowser::ProcessSaveFile(const std::string &fname, const std::string &content)
420{
421 if (fname.empty()) return;
422 R__LOG_DEBUG(0, BrowserLog()) << "SaveFile " << fname << " content length " << content.length();
423 std::ofstream f(fname);
424 f << content;
425}
426
427/////////////////////////////////////////////////////////////////////////////////
428/// Process run macro command in the editor
429
430void RBrowser::ProcessRunMacro(const std::string &file_path)
431{
432 if (file_path.rfind(".py") == file_path.length() - 3) {
433 TString exec;
434 exec.Form("TPython::ExecScript(\"%s\");", file_path.c_str());
435 gROOT->ProcessLine(exec.Data());
436 } else {
437 gInterpreter->ExecuteMacro(file_path.c_str());
438 }
439}
440
441/////////////////////////////////////////////////////////////////////////////////
442/// Process dbl click on browser item
443
444std::string RBrowser::ProcessDblClick(unsigned connid, std::vector<std::string> &args)
445{
446 args.pop_back(); // remove exec string, not used now
447
448 std::string opt = args.back();
449 args.pop_back(); // remove option
450
451 auto path = fBrowsable.GetWorkingPath();
452 path.insert(path.end(), args.begin(), args.end());
453
454 R__LOG_DEBUG(0, BrowserLog()) << "DoubleClick " << Browsable::RElement::GetPathAsString(path);
455
456 auto elem = fBrowsable.GetSubElement(path);
457 if (!elem) return ""s;
458
459 auto dflt_action = elem->GetDefaultAction();
460
461 // special case when canvas is clicked - always start new widget
463 std::string widget_kind;
464
465 if (elem->IsCapable(Browsable::RElement::kActDraw7))
466 widget_kind = "rcanvas";
467 else
468 widget_kind = "tcanvas";
469
470 std::string name = widget_kind + std::to_string(++fWidgetCnt);
471
473
474 if (!new_widget)
475 return ""s;
476
477 // assign back pointer
478 new_widget->fBrowser = this;
479 fWidgets.emplace_back(new_widget);
480 fActiveWidgetName = new_widget->GetName();
481
482 return NewWidgetMsg(new_widget);
483 }
484
485 // before display tree or geometry ensure that they read and cached inside element
487 elem->GetChildsIter();
488 }
489
491 Browsable::RProvider::ProgressHandle handle(elem.get(), [this, connid](float progress, void *) {
492 SendProgress(connid, progress);
493 });
494
495 auto widget = GetActiveWidget();
496 if (widget && widget->DrawElement(elem, opt)) {
497 widget->SetPath(path);
498 return widget->SendWidgetContent();
499 }
500
501 // check if element was drawn in other widget and just activate that widget
502 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(),
503 [path](const std::shared_ptr<RBrowserWidget> &wg) { return path == wg->GetPath(); });
504
505 if (iter != fWidgets.end())
506 return "SELECT_WIDGET:"s + (*iter)->GetName();
507
508 // check if object can be drawn in RCanvas even when default action is drawing in TCanvas
511
512 std::string widget_kind;
513 switch(dflt_action) {
514 case Browsable::RElement::kActDraw6: widget_kind = "tcanvas"; break;
515 case Browsable::RElement::kActDraw7: widget_kind = "rcanvas"; break;
516 case Browsable::RElement::kActEdit: widget_kind = "editor"; break;
517 case Browsable::RElement::kActImage: widget_kind = "image"; break;
518 case Browsable::RElement::kActTree: widget_kind = "tree"; break;
519 case Browsable::RElement::kActGeom: widget_kind = "geom"; break;
520 default: widget_kind.clear();
521 }
522
523 if (!widget_kind.empty()) {
525 if (new_widget) {
526 // draw object before client side is created - should not be a problem
527 // after widget add in browser, connection will be established and data provided
528 if (new_widget->DrawElement(elem, opt))
529 new_widget->SetPath(path);
530 return NewWidgetMsg(new_widget);
531 }
532 }
533
534 if (elem->IsCapable(Browsable::RElement::kActBrowse) && (elem->GetNumChilds() > 0)) {
535 // remove extra index in subitems name
536 for (auto &pathelem : path)
540 }
541
542 return ""s;
543}
544
545/////////////////////////////////////////////////////////////////////////////////
546/// Process drop of item in the current tab
547
548std::string RBrowser::ProcessDrop(unsigned connid, std::vector<std::string> &args)
549{
550 auto path = fBrowsable.GetWorkingPath();
551 path.insert(path.end(), args.begin(), args.end());
552
553 R__LOG_DEBUG(0, BrowserLog()) << "DoubleClick " << Browsable::RElement::GetPathAsString(path);
554
555 auto elem = fBrowsable.GetSubElement(path);
556 if (!elem) return ""s;
557
559 Browsable::RProvider::ProgressHandle handle(elem.get(), [this, connid](float progress, void *) {
560 SendProgress(connid, progress);
561 });
562
563 auto widget = GetActiveWidget();
564 if (widget && widget->DrawElement(elem, "<append>")) {
565 widget->SetPath(path);
566 return widget->SendWidgetContent();
567 }
568
569 return ""s;
570}
571
572
573/////////////////////////////////////////////////////////////////////////////////
574/// Show or update RBrowser in web window
575/// If web window already started - just refresh it like "reload" button does
576/// If no web window exists or \param always_start_new_browser configured, starts new window
577/// \param args display arguments
578
580{
581 if (!fWebWindow->NumConnections() || always_start_new_browser) {
582 fWebWindow->Show(args);
583 }
584}
585
586///////////////////////////////////////////////////////////////////////////////////////////////////////
587/// Hide ROOT Browser
588
590{
591 if (fWebWindow)
592 fWebWindow->CloseConnections();
593}
594
595///////////////////////////////////////////////////////////////////////////////////////////////////////
596/// Run widget Sync method - processing pending actions
597
599{
600 if (fWebWindow)
601 fWebWindow->Sync();
602}
603
604///////////////////////////////////////////////////////////////////////////////////////////////////////
605/// Return URL parameter for the window showing ROOT Browser
606/// See \ref ROOT::RWebWindow::GetUrl docu for more details
607
609{
610 if (fWebWindow)
611 return fWebWindow->GetUrl(remote);
612
613 return ""s;
614}
615
616
617//////////////////////////////////////////////////////////////////////////////////////////////
618/// Creates new widget
619
620std::shared_ptr<RBrowserWidget> RBrowser::AddWidget(const std::string &kind)
621{
622 std::string name = kind + std::to_string(++fWidgetCnt);
623
624 std::shared_ptr<RBrowserWidget> widget;
625
626 if (kind == "editor"s)
627 widget = std::make_shared<RBrowserEditorWidget>(name, true);
628 else if (kind == "image"s)
629 widget = std::make_shared<RBrowserEditorWidget>(name, false);
630 else if (kind == "info"s)
631 widget = std::make_shared<RBrowserInfoWidget>(name);
632 else
634
635 if (!widget) {
636 R__LOG_ERROR(BrowserLog()) << "Fail to create widget of kind " << kind;
637 return nullptr;
638 }
639
640 widget->fBrowser = this;
641 fWidgets.emplace_back(widget);
643
644 return widget;
645}
646
647//////////////////////////////////////////////////////////////////////////////////////////////
648/// Add widget catched from external scripts
649
650std::shared_ptr<RBrowserWidget> RBrowser::AddCatchedWidget(RWebWindow *win, const std::string &kind)
651{
652 if (!win || kind.empty())
653 return nullptr;
654
655 std::string name = "catched"s + std::to_string(++fWidgetCnt);
656
657 auto widget = std::make_shared<RBrowserCatchedWidget>(name, win, kind);
658
659 fWidgets.emplace_back(widget);
660
662
663 return widget;
664}
665
666
667//////////////////////////////////////////////////////////////////////////////////////////////
668/// Create new widget and send init message to the client
669
670void RBrowser::AddInitWidget(const std::string &kind)
671{
672 auto widget = AddWidget(kind);
673 if (widget && fWebWindow && (fWebWindow->NumConnections() > 0))
674 fWebWindow->Send(0, NewWidgetMsg(widget));
675}
676
677//////////////////////////////////////////////////////////////////////////////////////////////
678/// Find widget by name or kind
679
680std::shared_ptr<RBrowserWidget> RBrowser::FindWidget(const std::string &name, const std::string &kind) const
681{
682 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(),
683 [name, kind](const std::shared_ptr<RBrowserWidget> &widget) {
684 return kind.empty() ? name == widget->GetName() : kind == widget->GetKind();
685 });
686
687 if (iter != fWidgets.end())
688 return *iter;
689
690 return nullptr;
691}
692
693//////////////////////////////////////////////////////////////////////////////////////////////
694/// Close and delete specified widget
695
696void RBrowser::CloseTab(const std::string &name)
697{
698 auto iter = std::find_if(fWidgets.begin(), fWidgets.end(), [name](std::shared_ptr<RBrowserWidget> &widget) { return name == widget->GetName(); });
699 if (iter != fWidgets.end())
700 fWidgets.erase(iter);
701
702 if (fActiveWidgetName == name)
703 fActiveWidgetName.clear();
704}
705
706//////////////////////////////////////////////////////////////////////////////////////////////
707/// Get content of history file
708
709std::vector<std::string> RBrowser::GetRootHistory()
710{
711 std::vector<std::string> arr;
712
713 std::string path = gSystem->UnixPathName(gSystem->HomeDirectory());
714 path += "/.root_hist" ;
715 std::ifstream infile(path);
716
717 if (infile) {
718 std::string line;
719 while (std::getline(infile, line) && (arr.size() < 1000)) {
720 if(!(std::find(arr.begin(), arr.end(), line) != arr.end())) {
721 arr.emplace_back(line);
722 }
723 }
724 }
725
726 return arr;
727}
728
729//////////////////////////////////////////////////////////////////////////////////////////////
730/// Get content of log file
731
732std::vector<std::string> RBrowser::GetRootLogs()
733{
734 std::vector<std::string> arr;
735
736 std::ifstream infile(fPromptFileOutput);
737 if (infile) {
738 std::string line;
739 while (std::getline(infile, line) && (arr.size() < 10000)) {
740 arr.emplace_back(line);
741 }
742 }
743
744 return arr;
745}
746
747//////////////////////////////////////////////////////////////////////////////////////////////
748/// Process client connect
749
750void RBrowser::SendInitMsg(unsigned connid)
751{
752 std::vector<std::vector<std::string>> reply;
753
754 reply.emplace_back(fBrowsable.GetWorkingPath()); // first element is current path
755
756 for (auto &widget : fWidgets) {
757 widget->ResetConn();
758 reply.emplace_back(std::vector<std::string>({ widget->GetKind(), widget->GetUrl(), widget->GetName(), widget->GetTitle() }));
759 }
760
761 if (!fActiveWidgetName.empty())
762 reply.emplace_back(std::vector<std::string>({ "active"s, fActiveWidgetName }));
763
764 auto history = GetRootHistory();
765 if (history.size() > 0) {
766 history.insert(history.begin(), "history"s);
767 reply.emplace_back(history);
768 }
769
770 auto logs = GetRootLogs();
771 if (logs.size() > 0) {
772 logs.insert(logs.begin(), "logs"s);
773 reply.emplace_back(logs);
774 }
775
776 reply.emplace_back(std::vector<std::string>({
777 "drawoptions"s,
781 }));
782
783 reply.emplace_back(std::vector<std::string>({
784 "settings"s,
785 gEnv->GetValue("WebGui.Browser.Expand", "no"),
787 }));
788
789 std::string msg = "INMSG:";
791
792 fWebWindow->Send(connid, msg);
793}
794
795//////////////////////////////////////////////////////////////////////////////////////////////
796/// Send generic progress message to the web window
797/// Should show progress bar on client side
798
799void RBrowser::SendProgress(unsigned connid, float progr)
800{
801 long long millisec = gSystem->Now();
802
803 // let process window events
804 fWebWindow->Sync();
805
806 if ((!fLastProgressSendTm || millisec > fLastProgressSendTm - 200) && (progr > fLastProgressSend + 0.04) && fWebWindow->CanSend(connid)) {
807 fWebWindow->Send(connid, "PROGRESS:"s + std::to_string(progr));
808
811 }
812}
813
814
815//////////////////////////////////////////////////////////////////////////////////////////////
816/// Return the current directory of ROOT
817
819{
820 return "WORKPATH:"s + TBufferJSON::ToJSON(&fBrowsable.GetWorkingPath()).Data();
821}
822
823//////////////////////////////////////////////////////////////////////////////////////////////
824/// Create message which send to client to create new widget
825
826std::string RBrowser::NewWidgetMsg(std::shared_ptr<RBrowserWidget> &widget)
827{
828 std::vector<std::string> arr = { widget->GetKind(), widget->GetUrl(), widget->GetName(), widget->GetTitle(),
830 return "NEWWIDGET:"s + TBufferJSON::ToJSON(&arr, TBufferJSON::kNoSpaces).Data();
831}
832
833//////////////////////////////////////////////////////////////////////////////////////////////
834/// Check if any widget was modified and update if necessary
835
837{
838 std::vector<std::string> del_names;
839
840 for (auto &widget : fWidgets)
841 if (!widget->IsValid())
842 del_names.push_back(widget->GetName());
843
844 if (!del_names.empty())
845 fWebWindow->Send(connid, "CLOSE_WIDGETS:"s + TBufferJSON::ToJSON(&del_names, TBufferJSON::kNoSpaces).Data());
846
847 for (auto name : del_names)
848 CloseTab(name);
849
850 for (auto &widget : fWidgets)
851 widget->CheckModified();
852}
853
854//////////////////////////////////////////////////////////////////////////////////////////////
855/// Process postponed requests - decouple from websocket handling
856/// Only requests which can take longer time should be postponed
857
859{
860 if (fPostponed.empty())
861 return;
862
863 auto arr = fPostponed[0];
864 fPostponed.erase(fPostponed.begin(), fPostponed.begin()+1);
865 if (fPostponed.empty())
866 fTimer->TurnOff();
867
868 std::string reply;
869 unsigned connid = std::stoul(arr.back()); arr.pop_back();
870 std::string kind = arr.back(); arr.pop_back();
871
872 if (kind == "DBLCLK") {
873 reply = ProcessDblClick(connid, arr);
874 if (reply.empty()) reply = "NOPE";
875 } else if (kind == "DROP") {
876 reply = ProcessDrop(connid, arr);
877 if (reply.empty()) reply = "NOPE";
878 }
879
880 if (!reply.empty())
881 fWebWindow->Send(connid, reply);
882}
883
884
885//////////////////////////////////////////////////////////////////////////////////////////////
886/// Process received message from the client
887
888void RBrowser::ProcessMsg(unsigned connid, const std::string &arg0)
889{
890 R__LOG_DEBUG(0, BrowserLog()) << "ProcessMsg len " << arg0.length() << " substr(30) " << arg0.substr(0, 30);
891
892 std::string kind, msg;
893 auto pos = arg0.find(":");
894 if (pos == std::string::npos) {
895 kind = arg0;
896 } else {
897 kind = arg0.substr(0, pos);
898 msg = arg0.substr(pos+1);
899 }
900
901 if (kind == "QUIT_ROOT") {
902
903 fWebWindow->TerminateROOT();
904
905 } else if (kind == "BRREQ") {
906 // central place for processing browser requests
908 if (!json.empty()) fWebWindow->Send(connid, json);
909
910 } else if (kind == "LASTCYCLE") {
911 // when changed on clients side
913
914 } else if (kind == "DBLCLK") {
915
916 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
917 if (arr && (arr->size() > 2)) {
918 arr->push_back(kind);
919 arr->push_back(std::to_string(connid));
920 fPostponed.push_back(*arr);
921 if (fPostponed.size() == 1)
922 fTimer->TurnOn();
923 } else {
924 fWebWindow->Send(connid, "NOPE");
925 }
926
927 } else if (kind == "DROP") {
928
929 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
930 if (arr && arr->size()) {
931 arr->push_back(kind);
932 arr->push_back(std::to_string(connid));
933 fPostponed.push_back(*arr);
934 if (fPostponed.size() == 1)
935 fTimer->TurnOn();
936 } else {
937 fWebWindow->Send(connid, "NOPE");
938 }
939
940 } else if (kind == "WIDGET_SELECTED") {
942 auto widget = GetActiveWidget();
943 if (widget) {
944 auto reply = widget->SendWidgetContent();
945 if (!reply.empty()) fWebWindow->Send(connid, reply);
946 }
947 } else if (kind == "CLOSE_TAB") {
948 CloseTab(msg);
949 } else if (kind == "GETWORKPATH") {
950 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
951 } else if (kind == "CHPATH") {
952 auto path = TBufferJSON::FromJSON<Browsable::RElementPath_t>(msg);
953 if (path) fBrowsable.SetWorkingPath(*path);
954 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
955 } else if (kind == "CMD") {
956 std::string sPrompt = "root []";
957 TApplication *app = gROOT->GetApplication();
958 if (app->InheritsFrom("TRint")) {
959 sPrompt = ((TRint*)gROOT->GetApplication())->GetPrompt();
960 Gl_histadd((char *)msg.c_str());
961 }
962
963 std::ofstream ofs(fPromptFileOutput, std::ofstream::out | std::ofstream::app);
964 ofs << sPrompt << msg << std::endl;
965 ofs.close();
966
968 gROOT->ProcessLine(msg.c_str());
969 gSystem->RedirectOutput(nullptr);
970
971 if (msg == ".g"s) {
972 auto widget = std::dynamic_pointer_cast<RBrowserInfoWidget>(FindWidget(""s, "info"s));
973 if (!widget) {
974 auto new_widget = AddWidget("info"s);
975 fWebWindow->Send(connid, NewWidgetMsg(new_widget));
976 widget = std::dynamic_pointer_cast<RBrowserInfoWidget>(new_widget);
977 } else if (fActiveWidgetName != widget->GetName()) {
978 fWebWindow->Send(connid, "SELECT_WIDGET:"s + widget->GetName());
979 fActiveWidgetName = widget->GetName();
980 }
981
982 if (widget)
983 widget->RefreshFromLogs(sPrompt + msg, GetRootLogs());
984 }
985
986 CheckWidgtesModified(connid);
987 } else if (kind == "GETHISTORY") {
988
989 auto history = GetRootHistory();
990
991 fWebWindow->Send(connid, "HISTORY:"s + TBufferJSON::ToJSON(&history, TBufferJSON::kNoSpaces).Data());
992 } else if (kind == "GETLOGS") {
993
994 auto logs = GetRootLogs();
995 fWebWindow->Send(connid, "LOGS:"s + TBufferJSON::ToJSON(&logs, TBufferJSON::kNoSpaces).Data());
996
998
1000
1001 } else if (kind == "SYNCEDITOR") {
1002 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
1003 if (arr && (arr->size() > 4)) {
1004 auto editor = std::dynamic_pointer_cast<RBrowserEditorWidget>(FindWidget(arr->at(0)));
1005 if (editor) {
1006 editor->fFirstSend = true;
1007 editor->fTitle = arr->at(1);
1008 editor->fFileName = arr->at(2);
1009 if (!arr->at(3).empty()) editor->fContent = arr->at(4);
1010 if ((arr->size() == 6) && (arr->at(5) == "SAVE"))
1011 ProcessSaveFile(editor->fFileName, editor->fContent);
1012 if ((arr->size() == 6) && (arr->at(5) == "RUN")) {
1013 ProcessSaveFile(editor->fFileName, editor->fContent);
1014 ProcessRunMacro(editor->fFileName);
1015 CheckWidgtesModified(connid);
1016 }
1017 }
1018 }
1019 } else if (kind == "GETINFO") {
1020 auto info = std::dynamic_pointer_cast<RBrowserInfoWidget>(FindWidget(msg));
1021 if (info) {
1022 info->Refresh();
1023 fWebWindow->Send(connid, info->SendWidgetContent());
1024 }
1025 } else if (kind == "NEWWIDGET") {
1026 auto widget = AddWidget(msg);
1027 if (widget)
1028 fWebWindow->Send(connid, NewWidgetMsg(widget));
1029 } else if (kind == "NEWCHANNEL") {
1030 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
1031 if (arr && (arr->size() == 2)) {
1032 auto widget = FindWidget((*arr)[0]);
1033 if (widget)
1034 RWebWindow::ShowWindow(widget->GetWindow(), { fWebWindow, connid, std::stoi((*arr)[1]) });
1035 }
1036 } else if (kind == "CDWORKDIR") {
1038 if (fBrowsable.GetWorkingPath() != wrkdir) {
1040 } else {
1042 }
1043 fWebWindow->Send(connid, GetCurrentWorkingDirectory());
1044 } else if (kind == "OPTIONS") {
1045 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(msg);
1046 if (arr && (arr->size() == 3)) {
1050 }
1051 }
1052}
1053
1054//////////////////////////////////////////////////////////////////////////////////////////////
1055/// Set working path in the browser
1056
1057void RBrowser::SetWorkingPath(const std::string &path)
1058{
1059 auto p = Browsable::RElement::ParsePath(path);
1061 if (elem) {
1063 if (fWebWindow && (fWebWindow->NumConnections() > 0))
1065 }
1066}
1067
1068//////////////////////////////////////////////////////////////////////////////////////////////
1069/// Activate widget in RBrowser
1070/// One should specify title and (optionally) kind of widget like "tcanvas" or "geom"
1071
1072bool RBrowser::ActivateWidget(const std::string &title, const std::string &kind)
1073{
1074 if (title.empty())
1075 return false;
1076
1077 for (auto &widget : fWidgets) {
1078
1079 if (widget->GetTitle() != title)
1080 continue;
1081
1082 if (!kind.empty() && (widget->GetKind() != kind))
1083 continue;
1084
1085 if (fWebWindow)
1086 fWebWindow->Send(0, "SELECT_WIDGET:"s + widget->GetName());
1087 else
1088 fActiveWidgetName = widget->GetName();
1089 return true;
1090 }
1091
1092 return false;
1093}
1094
1095//////////////////////////////////////////////////////////////////////////////////////////////
1096/// Set handle which will be cleared when connection is closed
1097
1098void RBrowser::ClearOnClose(const std::shared_ptr<void> &handle)
1099{
1100 fWebWindow->SetClearOnClose(handle);
1101}
nlohmann::json json
#define R__LOG_ERROR(...)
Definition RLogger.hxx:356
#define R__LOG_DEBUG(DEBUGLEVEL,...)
Definition RLogger.hxx:359
#define f(i)
Definition RSha256.hxx:104
long Long_t
Signed long integer 4 bytes (long). Size depends on architecture.
Definition RtypesCore.h:69
constexpr Bool_t kTRUE
Definition RtypesCore.h:108
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
R__EXTERN TEnv * gEnv
Definition TEnv.h:126
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:252
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:148
#define gInterpreter
#define gROOT
Definition TROOT.h:417
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
static bool IsLastKeyCycle()
Is only last cycle from the list of keys is shown.
Definition RElement.cxx:195
static void SetLastKeyCycle(bool on=true)
Set flag to show only last cycle from the list of keys.
Definition RElement.cxx:203
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:180
@ kActImage
can be shown in image viewer, can provide image
Definition RElement.hxx:53
@ kActDraw6
can be drawn inside ROOT6 canvas
Definition RElement.hxx:54
@ kActCanvas
indicate that it is canvas and should be drawn directly
Definition RElement.hxx:56
@ kActTree
can be shown in tree viewer
Definition RElement.hxx:57
@ kActGeom
can be shown in geometry viewer
Definition RElement.hxx:58
@ kActBrowse
just browse (expand) item
Definition RElement.hxx:51
@ kActEdit
can provide data for text editor
Definition RElement.hxx:52
@ kActDraw7
can be drawn inside ROOT7 canvas
Definition RElement.hxx:55
static std::string GetPathAsString(const RElementPath_t &path)
Converts element path back to string.
Definition RElement.cxx:162
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:118
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:545
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:26
std::unique_ptr< RBrowserTimer > fTimer
! timer to handle postponed requests
Definition RBrowser.hxx:47
RBrowserData fBrowsable
! central browsing element
Definition RBrowser.hxx:46
std::shared_ptr< RBrowserWidget > AddWidget(const std::string &kind)
Creates new widget.
Definition RBrowser.cxx:620
std::vector< std::string > GetRootHistory()
Get content of history file.
Definition RBrowser.cxx:709
void AddInitWidget(const std::string &kind)
Create new widget and send init message to the client.
Definition RBrowser.cxx:670
std::vector< std::vector< std::string > > fPostponed
! postponed messages, handled in timer
Definition RBrowser.hxx:48
std::shared_ptr< RWebWindow > fWebWindow
! web window to browser
Definition RBrowser.hxx:44
std::string ProcessDrop(unsigned connid, std::vector< std::string > &args)
Process drop of item in the current tab.
Definition RBrowser.cxx:548
int fWidgetCnt
! counter for created widgets
Definition RBrowser.hxx:39
std::shared_ptr< RBrowserWidget > GetActiveWidget() const
Definition RBrowser.hxx:53
std::string ProcessDblClick(unsigned connid, std::vector< std::string > &args)
Process dbl click on browser item.
Definition RBrowser.cxx:444
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:37
RBrowser(bool use_rcanvas=false)
constructor
Definition RBrowser.cxx:285
void SetWorkingPath(const std::string &path)
Set working path in the browser.
void Hide()
Hide ROOT Browser.
Definition RBrowser.cxx:589
std::string NewWidgetMsg(std::shared_ptr< RBrowserWidget > &widget)
Create message which send to client to create new widget.
Definition RBrowser.cxx:826
bool fCatchWindowShow
! if arbitrary RWebWindow::Show calls should be catched by browser
Definition RBrowser.hxx:36
std::string fPromptFileOutput
! file name for prompt output
Definition RBrowser.hxx:40
void Show(const RWebDisplayArgs &args="", bool always_start_new_browser=false)
Show or update RBrowser in web window If web window already started - just refresh it like "reload" b...
Definition RBrowser.cxx:579
std::string GetCurrentWorkingDirectory()
Return the current directory of ROOT.
Definition RBrowser.cxx:818
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:680
std::shared_ptr< RBrowserWidget > AddCatchedWidget(RWebWindow *win, const std::string &kind)
Add widget catched from external scripts.
Definition RBrowser.cxx:650
bool GetUseRCanvas() const
Definition RBrowser.hxx:82
std::vector< std::shared_ptr< RBrowserWidget > > fWidgets
! all browser widgets
Definition RBrowser.hxx:38
virtual ~RBrowser()
destructor
Definition RBrowser.cxx:383
void ProcessSaveFile(const std::string &fname, const std::string &content)
Process file save command in the editor.
Definition RBrowser.cxx:419
float fLastProgressSend
! last value of send progress
Definition RBrowser.hxx:41
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:608
std::string ProcessBrowserRequest(const std::string &msg)
Process browser request.
Definition RBrowser.cxx:395
std::vector< std::string > GetRootLogs()
Get content of log file.
Definition RBrowser.cxx:732
void ProcessMsg(unsigned connid, const std::string &arg)
Process received message from the client.
Definition RBrowser.cxx:888
void CheckWidgtesModified(unsigned connid)
Check if any widget was modified and update if necessary.
Definition RBrowser.cxx:836
void CloseTab(const std::string &name)
Close and delete specified widget.
Definition RBrowser.cxx:696
void ProcessPostponedRequests()
Process postponed requests - decouple from websocket handling Only requests which can take longer tim...
Definition RBrowser.cxx:858
unsigned fConnId
! default connection id
Definition RBrowser.hxx:33
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:750
void SendProgress(unsigned connid, float progr)
Send generic progress message to the web window Should show progress bar on client side.
Definition RBrowser.cxx:799
long long fLastProgressSendTm
! time when last progress message was send
Definition RBrowser.hxx:42
void ProcessRunMacro(const std::string &file_path)
Process run macro command in the editor.
Definition RBrowser.cxx:430
void Sync()
Run widget Sync method - processing pending actions.
Definition RBrowser.cxx:598
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:77
@ kNoSpaces
no new lines plus remove all spaces around "," and ":" symbols
Definition TBufferJSON.h:39
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:511
Definition TRint.h:31
Basic string class.
Definition TString.h:138
const char * Data() const
Definition TString.h:386
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2459
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2437
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:1730
virtual int GetPid()
Get process id.
Definition TSystem.cxx:720
virtual TTime Now()
Get current time in milliseconds since 0:00 Jan 1 1995.
Definition TSystem.cxx:465
virtual const char * UnixPathName(const char *unixpathname)
Convert from a local pathname to a Unix pathname.
Definition TSystem.cxx:1077
virtual const char * HomeDirectory(const char *userName=nullptr)
Return the user's home directory.
Definition TSystem.cxx:901
virtual int Unlink(const char *name)
Unlink, i.e.
Definition TSystem.cxx:1396
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
Definition TSystem.cxx:1497
Handles synchronous and a-synchronous timer events.
Definition TTimer.h:51
TLine * line
ROOT::RLogChannel & BrowserLog()
Log channel for Browser diagnostics.