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