Changeset 71105 in vbox for trunk/src/VBox/Frontends/VirtualBox
- Timestamp:
- Feb 22, 2018 3:12:14 PM (7 years ago)
- svn:sync-xref-src-repo-rev:
- 120970
- Location:
- trunk/src/VBox/Frontends/VirtualBox
- Files:
-
- 2 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk
r71101 r71105 377 377 src/runtime/information/UIVMInformationDialog.h \ 378 378 src/runtime/information/guestctrl/UIGuestControlConsole.h \ 379 src/runtime/information/guestctrl/UIGuestControlFileManager.h \ 379 380 src/runtime/information/guestctrl/UIGuestControlInterface.h \ 380 381 src/runtime/information/guestctrl/UIGuestControlTreeItem.h \ … … 565 566 src/runtime/UIIndicatorsPool.cpp \ 566 567 src/runtime/UIStatusBarEditorWindow.cpp \ 568 src/runtime/information/guestctrl/UIGuestControlFileManager.cpp \ 567 569 src/runtime/information/guestctrl/UIGuestControlWidget.cpp \ 568 570 src/selector/UIActionPoolSelector.cpp \ … … 707 709 src/runtime/information/UIVMInformationDialog.cpp \ 708 710 src/runtime/information/guestctrl/UIGuestControlConsole.cpp \ 711 src/runtime/information/guestctrl/UIGuestControlFileManager.cpp \ 709 712 src/runtime/information/guestctrl/UIGuestControlInterface.cpp \ 710 713 src/runtime/information/guestctrl/UIGuestControlTreeItem.cpp \ -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/information/UIVMInformationDialog.cpp
r71101 r71105 38 38 # include "VBoxUtils.h" 39 39 # include "UIInformationConfiguration.h" 40 # include "UIGuestControlFileManager.h" 40 41 # include "UIGuestControlWidget.h" 41 42 # include "UIInformationRuntime.h" … … 119 120 m_pTabWidget->setTabText(1, tr("&Runtime Information")); 120 121 m_pTabWidget->setTabText(2, tr("&Guest Session")); 122 m_pTabWidget->setTabText(3, tr("&Guest File Manager")); 121 123 } 122 124 … … 253 255 } 254 256 } 257 if(m_pMachineWindow->console().isOk()) 258 { 259 UIGuestControlFileManager *pGuestControlFileManager = 260 new UIGuestControlFileManager(this, m_pMachineWindow->console().GetGuest()); 261 if (pGuestControlFileManager) 262 { 263 m_tabs.insert(3, pGuestControlFileManager); 264 m_pTabWidget->addTab(m_tabs.value(3), QString()); 265 } 266 } 267 268 255 269 /* Set Runtime Information tab as default: */ 256 270 m_pTabWidget->setCurrentIndex(1); -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/information/guestctrl/UIGuestControlFileManager.cpp
r71101 r71105 1 1 /* $Id$ */ 2 2 /** @file 3 * VBox Qt GUI - UIGuestControl Widgetclass implementation.3 * VBox Qt GUI - UIGuestControlFileManager class implementation. 4 4 */ 5 5 … … 21 21 22 22 /* Qt includes: */ 23 # include <QApplication>24 23 # include <QSplitter> 24 # include <QHBoxLayout> 25 25 # include <QVBoxLayout> 26 26 27 28 27 /* GUI includes: */ 29 # include "QITreeWidget.h" 30 # include "UIExtraDataManager.h" 31 # include "UIGuestControlConsole.h" 32 # include "UIGuestControlInterface.h" 33 # include "UIGuestControlTreeItem.h" 34 # include "UIGuestControlWidget.h" 28 # include "QILabel.h" 29 # include "QILineEdit.h" 30 # include "QIToolButton.h" 31 # include "QIWithRetranslateUI.h" 32 # include "UIGuestControlFileManager.h" 35 33 # include "UIVMInformationDialog.h" 36 34 # include "VBoxGlobal.h" … … 38 36 /* COM includes: */ 39 37 # include "CGuest.h" 40 # include "CEventSource.h"41 38 42 39 #endif /* !VBOX_WITH_PRECOMPILED_HEADERS */ 43 40 44 class UIGuest ControlTreeWidget : public QITreeWidget41 class UIGuestSessionCreateWidget : public QIWithRetranslateUI<QWidget> 45 42 { 46 47 43 Q_OBJECT; 48 49 signals:50 51 void sigCloseSessionOrProcess();52 44 53 45 public: 54 46 55 UIGuestControlTreeWidget(QWidget *pParent = 0) 56 :QITreeWidget(pParent) 57 { 58 setSelectionMode(QAbstractItemView::SingleSelection); 59 } 60 61 UIGuestControlTreeItem *selectedItem() 62 { 63 QList<QTreeWidgetItem*> selectedList = selectedItems(); 64 if (selectedList.isEmpty()) 65 return 0; 66 UIGuestControlTreeItem *item = 67 dynamic_cast<UIGuestControlTreeItem*>(selectedList[0]); 68 /* Return the firstof the selected items */ 69 return item; 70 } 47 UIGuestSessionCreateWidget(QWidget *pParent = 0); 71 48 72 49 protected: 73 50 74 void contextMenuEvent(QContextMenuEvent *pEvent) /* override */ 75 { 76 QMenu *menu = new QMenu(this); 77 QList<QTreeWidgetItem *> selectedList = selectedItems(); 51 void retranslateUi(); 78 52 79 UIGuestSessionTreeItem *sessionTreeItem = 0; 80 if (!selectedList.isEmpty())81 sessionTreeItem = dynamic_cast<UIGuestSessionTreeItem*>(selectedList[0]);82 QAction *pSessionCloseAction = 0;53 private: 54 void prepareWidgets(); 55 QILineEdit *m_pUserNameEdit; 56 QILineEdit *m_pPasswordEdit; 83 57 84 /* Create a guest session related context menu */ 85 if (sessionTreeItem) 86 { 87 pSessionCloseAction = menu->addAction(UIVMInformationDialog::tr("Close Session")); 88 if (pSessionCloseAction) 89 connect(pSessionCloseAction, &QAction::triggered, 90 this, &UIGuestControlTreeWidget::sigCloseSessionOrProcess); 91 } 92 UIGuestProcessTreeItem *processTreeItem = 0; 93 if (!selectedList.isEmpty()) 94 processTreeItem = dynamic_cast<UIGuestProcessTreeItem*>(selectedList[0]); 95 QAction *pProcessTerminateAction = 0; 96 if (processTreeItem) 97 { 98 pProcessTerminateAction = menu->addAction(UIVMInformationDialog::tr("Terminate Process")); 99 if (pProcessTerminateAction) 100 connect(pProcessTerminateAction, &QAction::triggered, 101 this, &UIGuestControlTreeWidget::sigCloseSessionOrProcess); 102 } 103 if (pProcessTerminateAction || pSessionCloseAction) 104 menu->addSeparator(); 105 // Add actions to expand/collapse all tree items 106 QAction *pExpandAllAction = menu->addAction(UIVMInformationDialog::tr("Expand All")); 107 if (pExpandAllAction) 108 connect(pExpandAllAction, &QAction::triggered, 109 this, &UIGuestControlTreeWidget::sltExpandAll); 110 QAction *pCollapseAllAction = menu->addAction(UIVMInformationDialog::tr("Collapse All")); 111 if (pCollapseAllAction) 112 connect(pCollapseAllAction, &QAction::triggered, 113 this, &UIGuestControlTreeWidget::sltCollapseAll); 58 QILabel *m_pUserNameLabel; 59 QILabel *m_pPasswordLabel; 60 QIToolButton *m_pCreateButton; 114 61 115 menu->exec(pEvent->globalPos()); 116 117 if (pSessionCloseAction) 118 disconnect(pSessionCloseAction, &QAction::triggered, 119 this, &UIGuestControlTreeWidget::sigCloseSessionOrProcess); 120 if (pProcessTerminateAction) 121 disconnect(pProcessTerminateAction, &QAction::triggered, 122 this, &UIGuestControlTreeWidget::sigCloseSessionOrProcess); 123 124 if (pExpandAllAction) 125 disconnect(pExpandAllAction, &QAction::triggered, 126 this, &UIGuestControlTreeWidget::sltExpandAll); 127 128 if (pCollapseAllAction) 129 disconnect(pCollapseAllAction, &QAction::triggered, 130 this, &UIGuestControlTreeWidget::sltCollapseAll); 131 132 delete menu; 133 } 134 135 private slots: 136 137 void sltExpandAll() 138 { 139 expandCollapseAll(true); 140 } 141 142 void sltCollapseAll() 143 { 144 expandCollapseAll(false); 145 } 146 147 void expandCollapseAll(bool bFlag) 148 { 149 for(int i = 0; i < topLevelItemCount(); ++i) 150 { 151 if (!topLevelItem(i)) 152 break; 153 topLevelItem(i)->setExpanded(bFlag); 154 for (int j = 0; j < topLevelItem(i)->childCount(); ++j) 155 { 156 if (topLevelItem(i)->child(j)) 157 { 158 topLevelItem(i)->child(j)->setExpanded(bFlag); 159 } 160 } 161 } 162 } 62 QHBoxLayout *m_pMainLayout; 163 63 164 64 }; 165 65 166 UIGuestControlWidget::UIGuestControlWidget(QWidget *pParent, const CGuest &comGuest) 66 UIGuestSessionCreateWidget::UIGuestSessionCreateWidget(QWidget *pParent /* = 0 */) 67 : QIWithRetranslateUI<QWidget>(pParent) 68 , m_pUserNameEdit(0) 69 , m_pPasswordEdit(0) 70 , m_pUserNameLabel(0) 71 , m_pPasswordLabel(0) 72 , m_pCreateButton(0) 73 , m_pMainLayout(0) 74 { 75 prepareWidgets(); 76 } 77 78 void UIGuestSessionCreateWidget::prepareWidgets() 79 { 80 m_pMainLayout = new QHBoxLayout(this); 81 if (!m_pMainLayout) 82 return; 83 m_pUserNameEdit = new QILineEdit; 84 if (m_pUserNameEdit) 85 { 86 m_pMainLayout->addWidget(m_pUserNameEdit); 87 } 88 m_pUserNameLabel = new QILabel; 89 if (m_pUserNameLabel) 90 { 91 m_pMainLayout->addWidget(m_pUserNameLabel); 92 } 93 m_pPasswordEdit = new QILineEdit; 94 if (m_pPasswordEdit) 95 { 96 m_pMainLayout->addWidget(m_pPasswordEdit); 97 } 98 99 m_pPasswordLabel = new QILabel; 100 if (m_pPasswordLabel) 101 { 102 m_pMainLayout->addWidget(m_pPasswordLabel); 103 } 104 105 m_pCreateButton = new QIToolButton; 106 if (m_pCreateButton) 107 { 108 m_pMainLayout->addWidget(m_pCreateButton); 109 } 110 retranslateUi(); 111 } 112 113 void UIGuestSessionCreateWidget::retranslateUi() 114 { 115 if (m_pUserNameEdit) 116 { 117 m_pUserNameEdit->setToolTip(UIVMInformationDialog::tr("User name to authenticate session creation")); 118 } 119 if (m_pPasswordEdit) 120 { 121 m_pPasswordEdit->setToolTip(UIVMInformationDialog::tr("Password to authenticate session creation")); 122 } 123 if (m_pUserNameLabel) 124 { 125 m_pUserNameLabel->setToolTip(UIVMInformationDialog::tr("User name to authenticate session creation")); 126 m_pUserNameLabel->setText(UIVMInformationDialog::tr("User name")); 127 } 128 if (m_pPasswordLabel) 129 { 130 m_pPasswordLabel->setToolTip(UIVMInformationDialog::tr("Password to authenticate session creation")); 131 m_pPasswordLabel->setText(UIVMInformationDialog::tr("Password")); 132 } 133 if(m_pCreateButton) 134 { 135 m_pCreateButton->setText(UIVMInformationDialog::tr("Create Session")); 136 } 137 } 138 139 UIGuestControlFileManager::UIGuestControlFileManager(QWidget *pParent, const CGuest &comGuest) 167 140 : QWidget(pParent) 168 141 , m_comGuest(comGuest) 169 142 , m_pMainLayout(0) 170 143 , m_pSplitter(0) 171 , m_pTreeWidget(0) 172 , m_pConsole(0) 173 , m_pControlInterface(0) 174 , m_pQtListener(0) 144 , m_pSessionCreateWidget(0) 175 145 { 176 prepareListener();177 146 prepareObjects(); 178 147 prepareConnections(); 179 initGuestSessionTree();180 148 } 181 149 182 void UIGuestControl Widget::prepareObjects()150 void UIGuestControlFileManager::prepareObjects() 183 151 { 184 m_pControlInterface = new UIGuestControlInterface(this, m_comGuest);185 186 152 /* Create layout: */ 187 153 m_pMainLayout = new QVBoxLayout(this); … … 197 163 return; 198 164 165 m_pSessionCreateWidget = new UIGuestSessionCreateWidget(); 166 if (m_pSessionCreateWidget) 167 { 168 m_pSplitter->addWidget(m_pSessionCreateWidget); 169 } 170 199 171 m_pSplitter->setOrientation(Qt::Vertical); 200 201 172 m_pMainLayout->addWidget(m_pSplitter); 202 173 203 174 204 m_pTreeWidget = new UIGuestControlTreeWidget; 205 206 if (m_pTreeWidget) 207 { 208 m_pSplitter->addWidget(m_pTreeWidget); 209 m_pTreeWidget->setColumnCount(3); 210 QStringList labels; 211 labels << "" << "" << ""; 212 213 m_pTreeWidget->setHeaderLabels(labels); 214 } 215 m_pConsole = new UIGuestControlConsole; 216 if (m_pConsole) 217 { 218 m_pSplitter->addWidget(m_pConsole); 219 setFocusProxy(m_pConsole); 220 } 221 222 m_pSplitter->setStretchFactor(0, 9); 223 m_pSplitter->setStretchFactor(1, 4); 224 225 updateTreeWidget(); 175 // m_pSplitter->setStretchFactor(0, 9); 176 // m_pSplitter->setStretchFactor(1, 4); 226 177 } 227 178 228 void UIGuestControl Widget::updateTreeWidget()179 void UIGuestControlFileManager::prepareConnections() 229 180 { 230 if (!m_pTreeWidget)231 return;232 181 233 m_pTreeWidget->clear();234 QVector<QITreeWidgetItem> treeItemVector;235 update();236 182 } 237 183 238 void UIGuestControlWidget::prepareConnections() 239 { 240 qRegisterMetaType<QVector<int> >(); 241 connect(m_pControlInterface, &UIGuestControlInterface::sigOutputString, 242 this, &UIGuestControlWidget::sltConsoleOutputReceived); 243 connect(m_pConsole, &UIGuestControlConsole::commandEntered, 244 this, &UIGuestControlWidget::sltConsoleCommandEntered); 245 246 if (m_pTreeWidget) 247 connect(m_pTreeWidget, &UIGuestControlTreeWidget::sigCloseSessionOrProcess, 248 this, &UIGuestControlWidget::sltCloseSessionOrProcess); 249 250 if (m_pQtListener) 251 { 252 connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestSessionRegistered, 253 this, &UIGuestControlWidget::sltGuestSessionRegistered, Qt::DirectConnection); 254 connect(m_pQtListener->getWrapped(), &UIMainEventListener::sigGuestSessionUnregistered, 255 this, &UIGuestControlWidget::sltGuestSessionUnregistered, Qt::DirectConnection); 256 } 257 } 258 259 void UIGuestControlWidget::sltGuestSessionsUpdated() 260 { 261 updateTreeWidget(); 262 } 263 264 void UIGuestControlWidget::sltConsoleCommandEntered(const QString &strCommand) 265 { 266 if (m_pControlInterface) 267 { 268 m_pControlInterface->putCommand(strCommand); 269 } 270 } 271 272 void UIGuestControlWidget::sltConsoleOutputReceived(const QString &strOutput) 273 { 274 if (m_pConsole) 275 { 276 m_pConsole->putOutput(strOutput); 277 } 278 } 279 280 void UIGuestControlWidget::sltCloseSessionOrProcess() 281 { 282 if (!m_pTreeWidget) 283 return; 284 UIGuestControlTreeItem *selectedTreeItem = 285 m_pTreeWidget->selectedItem(); 286 if (!selectedTreeItem) 287 return; 288 UIGuestProcessTreeItem *processTreeItem = 289 dynamic_cast<UIGuestProcessTreeItem*>(selectedTreeItem); 290 if (processTreeItem) 291 { 292 CGuestProcess guestProcess = processTreeItem->guestProcess(); 293 if (guestProcess.isOk()) 294 { 295 guestProcess.Terminate(); 296 } 297 return; 298 } 299 UIGuestSessionTreeItem *sessionTreeItem = 300 dynamic_cast<UIGuestSessionTreeItem*>(selectedTreeItem); 301 if (!sessionTreeItem) 302 return; 303 CGuestSession guestSession = sessionTreeItem->guestSession(); 304 if (!guestSession.isOk()) 305 return; 306 guestSession.Close(); 307 } 308 309 void UIGuestControlWidget::prepareListener() 310 { 311 /* Create event listener instance: */ 312 m_pQtListener.createObject(); 313 m_pQtListener->init(new UIMainEventListener, this); 314 m_comEventListener = CEventListener(m_pQtListener); 315 316 /* Get CProgress event source: */ 317 CEventSource comEventSource = m_comGuest.GetEventSource(); 318 AssertWrapperOk(comEventSource); 319 320 /* Enumerate all the required event-types: */ 321 QVector<KVBoxEventType> eventTypes; 322 eventTypes << KVBoxEventType_OnGuestSessionRegistered; 323 324 325 /* Register event listener for CProgress event source: */ 326 comEventSource.RegisterListener(m_comEventListener, eventTypes, 327 gEDataManager->eventHandlingType() == EventHandlingType_Active ? TRUE : FALSE); 328 AssertWrapperOk(comEventSource); 329 330 /* If event listener registered as passive one: */ 331 if (gEDataManager->eventHandlingType() == EventHandlingType_Passive) 332 { 333 /* Register event sources in their listeners as well: */ 334 m_pQtListener->getWrapped()->registerSource(comEventSource, m_comEventListener); 335 } 336 } 337 338 void UIGuestControlWidget::initGuestSessionTree() 339 { 340 if (!m_comGuest.isOk()) 341 return; 342 343 QVector<CGuestSession> sessions = m_comGuest.GetSessions(); 344 for (int i = 0; i < sessions.size(); ++i) 345 { 346 addGuestSession(sessions.at(i)); 347 } 348 } 349 350 void UIGuestControlWidget::cleanupListener() 351 { 352 /* If event listener registered as passive one: */ 353 if (gEDataManager->eventHandlingType() == EventHandlingType_Passive) 354 { 355 /* Unregister everything: */ 356 m_pQtListener->getWrapped()->unregisterSources(); 357 } 358 359 /* Make sure VBoxSVC is available: */ 360 if (!vboxGlobal().isVBoxSVCAvailable()) 361 return; 362 363 /* Get CProgress event source: */ 364 CEventSource comEventSource = m_comGuest.GetEventSource(); 365 AssertWrapperOk(comEventSource); 366 367 /* Unregister event listener for CProgress event source: */ 368 comEventSource.UnregisterListener(m_comEventListener); 369 } 370 371 372 void UIGuestControlWidget::sltGuestSessionRegistered(CGuestSession guestSession) 373 { 374 if (!guestSession.isOk()) 375 return; 376 addGuestSession(guestSession); 377 } 378 379 void UIGuestControlWidget::addGuestSession(CGuestSession guestSession) 380 { 381 UIGuestSessionTreeItem* sessionTreeItem = new UIGuestSessionTreeItem(m_pTreeWidget, guestSession); 382 connect(sessionTreeItem, &UIGuestSessionTreeItem::sigGuessSessionUpdated, 383 this, &UIGuestControlWidget::sltTreeItemUpdated); 384 connect(sessionTreeItem, &UIGuestSessionTreeItem::sigGuestSessionErrorText, 385 this, &UIGuestControlWidget::sltGuestControlErrorText); 386 } 387 388 void UIGuestControlWidget::sltGuestControlErrorText(QString strError) 389 { 390 if (m_pConsole) 391 { 392 m_pConsole->putOutput(strError); 393 } 394 } 395 396 void UIGuestControlWidget::sltTreeItemUpdated() 397 { 398 if (m_pTreeWidget) 399 m_pTreeWidget->update(); 400 } 401 402 void UIGuestControlWidget::sltGuestSessionUnregistered(CGuestSession guestSession) 403 { 404 if (!guestSession.isOk()) 405 return; 406 if (!m_pTreeWidget) 407 return; 408 409 UIGuestSessionTreeItem *selectedItem = NULL; 410 411 412 for (int i = 0; i < m_pTreeWidget->topLevelItemCount(); ++i) 413 { 414 QTreeWidgetItem *item = m_pTreeWidget->topLevelItem( i ); 415 416 UIGuestSessionTreeItem *treeItem = dynamic_cast<UIGuestSessionTreeItem*>(item); 417 if (treeItem && treeItem->guestSession() == guestSession) 418 { 419 selectedItem = treeItem; 420 break; 421 } 422 } 423 delete selectedItem; 424 } 425 426 #include "UIGuestControlWidget.moc" 184 #include "UIGuestControlFileManager.moc" -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/information/guestctrl/UIGuestControlFileManager.h
r71101 r71105 1 1 /* $Id$ */ 2 2 /** @file 3 * VBox Qt GUI - UIGuestControl Widgetclass declaration.3 * VBox Qt GUI - UIGuestControlFileManager class declaration. 4 4 */ 5 5 … … 16 16 */ 17 17 18 #ifndef ___UIGuestControl Widget_h___19 #define ___UIGuestControl Widget_h___18 #ifndef ___UIGuestControlFileManager_h___ 19 #define ___UIGuestControlFileManager_h___ 20 20 21 21 /* Qt includes: */ … … 25 25 #include "COMEnums.h" 26 26 #include "CGuest.h" 27 #include "CEventListener.h"28 29 /* GUI includes: */30 #include "UIMainEventListener.h"31 27 32 28 /* Forward declarations: */ 33 class QITreeWidget;34 29 class QVBoxLayout; 35 30 class QSplitter; 36 class UIGuestControlConsole; 37 class UIGuestControlInterface; 38 class UIGuestSessionsEventHandler; 39 class UIGuestControlTreeWidget; 31 40 32 41 33 /** QWidget extension 42 34 * providing GUI with guest session information and control tab in session-information window. */ 43 class UIGuestControl Widget: public QWidget35 class UIGuestControlFileManager : public QWidget 44 36 { 45 37 Q_OBJECT; … … 47 39 public: 48 40 49 UIGuestControlWidget(QWidget *pParent, const CGuest &comGuest); 50 51 private slots: 52 53 void sltGuestSessionsUpdated(); 54 void sltConsoleCommandEntered(const QString &strCommand); 55 void sltConsoleOutputReceived(const QString &strOutput); 56 57 void sltGuestSessionRegistered(CGuestSession guestSession); 58 void sltGuestSessionUnregistered(CGuestSession guestSession); 59 void sltGuestControlErrorText(QString strError); 60 61 void sltTreeItemUpdated(); 62 void sltCloseSessionOrProcess(); 41 UIGuestControlFileManager(QWidget *pParent, const CGuest &comGuest); 63 42 64 43 private: … … 66 45 void prepareObjects(); 67 46 void prepareConnections(); 68 void prepareListener();69 void initGuestSessionTree();70 void updateTreeWidget();71 void cleanupListener();72 void addGuestSession(CGuestSession guestSession);73 47 74 CGuest m_comGuest; 75 QVBoxLayout *m_pMainLayout; 76 QSplitter *m_pSplitter; 77 UIGuestControlTreeWidget *m_pTreeWidget; 78 UIGuestControlConsole *m_pConsole; 79 UIGuestControlInterface *m_pControlInterface; 80 81 /** Holds the Qt event listener instance. */ 82 ComObjPtr<UIMainEventListenerImpl> m_pQtListener; 83 /** Holds the COM event listener instance. */ 84 CEventListener m_comEventListener; 48 CGuest m_comGuest; 49 QVBoxLayout *m_pMainLayout; 50 QSplitter *m_pSplitter; 51 QWidget *m_pSessionCreateWidget; 85 52 }; 86 53 87 #endif /* !___UIGuestControl Widget_h___ */54 #endif /* !___UIGuestControlFileManager_h___ */
Note:
See TracChangeset
for help on using the changeset viewer.