Changeset 108555 in vbox for trunk/src/VBox/Frontends
- Timestamp:
- Mar 13, 2025 5:48:30 PM (5 weeks ago)
- svn:sync-xref-src-repo-rev:
- 167952
- Location:
- trunk/src/VBox/Frontends/VirtualBox
- Files:
-
- 1 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk
r108532 r108555 581 581 src/manager/UIHomePane.h \ 582 582 src/manager/UIMachineToolsWidget.h \ 583 src/manager/UIManagementToolsWidget.h \ 583 584 src/manager/UIProgressTaskReadCloudMachineList.h \ 584 585 src/manager/UITaskCloudGetSettingsForm.h \ … … 1103 1104 src/manager/UIHomePane.cpp \ 1104 1105 src/manager/UIMachineToolsWidget.cpp \ 1106 src/manager/UIManagementToolsWidget.cpp \ 1105 1107 src/manager/UIProgressTaskReadCloudMachineList.cpp \ 1106 1108 src/manager/UITaskCloudGetSettingsForm.cpp \ -
trunk/src/VBox/Frontends/VirtualBox/src/manager/UIManagementToolsWidget.cpp
r108553 r108555 1 1 /* $Id$ */ 2 2 /** @file 3 * VBox Qt GUI - UI GlobalToolsWidget class implementation.3 * VBox Qt GUI - UIManagementToolsWidget class implementation. 4 4 */ 5 5 … … 27 27 28 28 /* Qt includes: */ 29 #include <Q GridLayout>29 #include <QVBoxLayout> 30 30 31 31 /* GUI includes: */ 32 #include "QIToolBar.h" 33 #include "UIChooser.h" 34 #include "UICommon.h" 35 #include "UIExtraDataManager.h" 36 #include "UIGlobalToolsWidget.h" 37 #include "UIMachineToolsWidget.h" 32 #include "UIManagementToolsWidget.h" 38 33 #include "UIToolPane.h" 39 #include "UITools.h"40 #include "UIVirtualBoxEventHandler.h"41 #include "UIVirtualMachineItem.h"42 34 43 35 /* Other VBox includes: */ … … 45 37 46 38 47 UI GlobalToolsWidget::UIGlobalToolsWidget(QWidget *pParent, UIActionPool *pActionPool)39 UIManagementToolsWidget::UIManagementToolsWidget(QWidget *pParent, UIActionPool *pActionPool) 48 40 : QWidget(pParent) 49 41 , m_pActionPool(pActionPool) 50 , m_pLayout(0)51 , m_pMenu(0)52 42 , m_pPane(0) 53 43 { … … 55 45 } 56 46 57 void UIGlobalToolsWidget::addToolBar(QIToolBar *pToolBar) 58 { 59 AssertPtrReturnVoid(m_pLayout); 60 m_pLayout->addWidget(pToolBar, 0, 1); 61 } 62 63 UIToolPane *UIGlobalToolsWidget::toolPane() const 47 UIToolPane *UIManagementToolsWidget::toolPane() const 64 48 { 65 49 return m_pPane; 66 50 } 67 51 68 UIMachineToolsWidget *UIGlobalToolsWidget::machineToolsWidget() const 69 { 70 return toolPane()->machineToolsWidget(); 71 } 72 73 UIToolType UIGlobalToolsWidget::menuToolType(UIToolClass enmClass) const 74 { 75 AssertPtrReturn(toolMenu(), UIToolType_Invalid); 76 return toolMenu()->toolsType(enmClass); 77 } 78 79 void UIGlobalToolsWidget::setMenuToolType(UIToolType enmType) 80 { 81 /* Sanity check: */ 82 AssertReturnVoid(enmType != UIToolType_Invalid); 83 84 AssertPtrReturnVoid(toolMenu()); 85 toolMenu()->setToolsType(enmType); 86 } 87 88 UIToolType UIGlobalToolsWidget::toolType() const 52 UIToolType UIManagementToolsWidget::toolType() const 89 53 { 90 54 AssertPtrReturn(toolPane(), UIToolType_Invalid); … … 92 56 } 93 57 94 bool UI GlobalToolsWidget::isToolOpened(UIToolType enmType) const58 bool UIManagementToolsWidget::isToolOpened(UIToolType enmType) const 95 59 { 96 60 /* Sanity check: */ 97 61 AssertReturn(enmType != UIToolType_Invalid, false); 98 /* Make sure new tool type is of Globalclass: */99 AssertReturn(UIToolStuff::isTypeOfClass(enmType, UIToolClass_ Global), false);62 /* Make sure new tool type is of Management class: */ 63 AssertReturn(UIToolStuff::isTypeOfClass(enmType, UIToolClass_Management), false); 100 64 101 65 AssertPtrReturn(toolPane(), false); … … 103 67 } 104 68 105 void UI GlobalToolsWidget::switchToolTo(UIToolType enmType)69 void UIManagementToolsWidget::switchToolTo(UIToolType enmType) 106 70 { 107 71 /* Sanity check: */ 108 72 AssertReturnVoid(enmType != UIToolType_Invalid); 109 /* Make sure new tool type is of Globalclass: */110 AssertReturnVoid(UIToolStuff::isTypeOfClass(enmType, UIToolClass_ Global));73 /* Make sure new tool type is of Management class: */ 74 AssertReturnVoid(UIToolStuff::isTypeOfClass(enmType, UIToolClass_Management)); 111 75 112 76 /* Open corresponding tool: */ … … 114 78 toolPane()->openTool(enmType); 115 79 116 /* Notify corresponding tool-pane it's active: */117 toolPane()->setActive(enmType != UIToolType_Machines && enmType != UIToolType_Managers);118 toolPaneMachine()->setActive(enmType == UIToolType_Machines);119 120 /* Special handling for Activities Global tool,121 * start unconditionally updating all cloud VMs: */122 if (enmType == UIToolType_Activities)123 {124 chooser()->setKeepCloudNodesUpdated(true);125 toolPane()->setCloudMachineItems(chooser()->cloudMachineItems());126 }127 /* Otherwise, stop unconditionally updating all cloud VMs,128 * (tho they will still be updated if selected) */129 else130 chooser()->setKeepCloudNodesUpdated(false);131 132 80 /* Let the parent know: */ 133 81 emit sigToolTypeChange(); 134 82 } 135 83 136 void UI GlobalToolsWidget::closeTool(UIToolType enmType)84 void UIManagementToolsWidget::closeTool(UIToolType enmType) 137 85 { 138 86 /* Sanity check: */ 139 87 AssertReturnVoid(enmType != UIToolType_Invalid); 140 /* Make sure new tool type is of Globalclass: */141 AssertReturnVoid(UIToolStuff::isTypeOfClass(enmType, UIToolClass_ Global));88 /* Make sure new tool type is of Management class: */ 89 AssertReturnVoid(UIToolStuff::isTypeOfClass(enmType, UIToolClass_Management)); 142 90 143 91 AssertPtrReturnVoid(toolPane()); … … 145 93 } 146 94 147 QString UI GlobalToolsWidget::currentHelpKeyword() const95 QString UIManagementToolsWidget::currentHelpKeyword() const 148 96 { 149 if (toolType() == UIToolType_Machines)150 {151 AssertPtrReturn(machineToolsWidget(), QString());152 return machineToolsWidget()->currentHelpKeyword();153 }154 155 97 AssertPtrReturn(toolPane(), QString()); 156 98 return toolPane()->currentHelpKeyword(); 157 99 } 158 100 159 void UIGlobalToolsWidget::sltHandleCommitData() 160 { 161 cleanupConnections(); 162 } 163 164 void UIGlobalToolsWidget::sltHandleMachineRegistrationChanged(const QUuid &, const bool fRegistered) 165 { 166 /* On any VM registered switch from Home to Machines: */ 167 AssertPtrReturnVoid(toolMenu()); 168 if (fRegistered && toolMenu()->toolsType(UIToolClass_Global) == UIToolType_Home) 169 setMenuToolType(UIToolType_Machines); 170 } 171 172 void UIGlobalToolsWidget::sltHandleSettingsExpertModeChange() 173 { 174 /* Update tools restrictions: */ 175 emit sigToolMenuUpdate(); 176 } 177 178 void UIGlobalToolsWidget::sltHandleChooserPaneSelectionChange() 179 { 180 /* Update tools restrictions: */ 181 emit sigToolMenuUpdate(); 182 } 183 184 void UIGlobalToolsWidget::sltHandleCloudProfileStateChange(const QString &, const QString &) 185 { 186 /* If Global Activities tool is currently chosen: */ 187 AssertPtrReturnVoid(toolPane()); 188 if (toolType() == UIToolType_Activities) 189 { 190 /* Propagate a set of cloud machine items to Global tool-pane: */ 191 toolPane()->setCloudMachineItems(chooser()->cloudMachineItems()); 192 } 193 } 194 195 void UIGlobalToolsWidget::sltHandleGlobalToolMenuUpdate() 196 { 197 /* Prepare tool restrictions: */ 198 QSet<UIToolType> restrictedTypes; 199 200 /* Restrict some types for Basic mode: */ 201 const bool fExpertMode = gEDataManager->isSettingsInExpertMode(); 202 if (!fExpertMode) 203 restrictedTypes << UIToolType_Media 204 << UIToolType_Network; 205 206 /* Make sure Machines tool is hidden for empty Chooser-pane: */ 207 if (!chooser()->currentItem()) 208 restrictedTypes << UIToolType_Machines; 209 210 /* Make sure no restricted tool is selected: */ 211 if (restrictedTypes.contains(toolMenu()->toolsType(UIToolClass_Global))) 212 setMenuToolType(UIToolType_Home); 213 214 /* Hide restricted tools in the menu: */ 215 const QList restrictions(restrictedTypes.begin(), restrictedTypes.end()); 216 toolMenu()->setRestrictedToolTypes(UIToolClass_Global, restrictions); 217 218 /* Close all restricted tools (besides the Machines and Management): */ 219 foreach (const UIToolType &enmRestrictedType, restrictedTypes) 220 if ( enmRestrictedType != UIToolType_Machines 221 && enmRestrictedType != UIToolType_Managers) 222 toolPane()->closeTool(enmRestrictedType); 223 } 224 225 void UIGlobalToolsWidget::sltHandleMachineToolMenuUpdate(UIVirtualMachineItem *pItem) 226 { 227 /* Prepare tool restrictions: */ 228 QSet<UIToolType> restrictedTypes; 229 230 /* Restrict some types for Basic mode: */ 231 const bool fExpertMode = gEDataManager->isSettingsInExpertMode(); 232 if (!fExpertMode) 233 restrictedTypes << UIToolType_FileManager; 234 235 /* Make sure local VM tools are hidden for cloud VMs: */ 236 if (pItem && pItem->itemType() != UIVirtualMachineItemType_Local) 237 restrictedTypes << UIToolType_Snapshots 238 << UIToolType_Logs 239 << UIToolType_FileManager; 240 241 /* Make sure no restricted tool is selected: */ 242 if (restrictedTypes.contains(toolMenu()->toolsType(UIToolClass_Machine))) 243 setMenuToolType(UIToolType_Details); 244 245 /* Hide restricted tools in the menu: */ 246 const QList restrictions(restrictedTypes.begin(), restrictedTypes.end()); 247 toolMenu()->setRestrictedToolTypes(UIToolClass_Machine, restrictions); 248 249 /// @todo finish that 250 // /* Disable even unrestricted tools for inacccessible VMs: */ 251 // const bool fCurrentItemIsOk = isItemAccessible(pItem); 252 // toolMenu()->setItemsEnabled(fCurrentItemIsOk); 253 254 /* Close all restricted tools: */ 255 foreach (const UIToolType &enmRestrictedType, restrictedTypes) 256 toolPaneMachine()->closeTool(enmRestrictedType); 257 } 258 259 void UIGlobalToolsWidget::sltHandleToolsMenuIndexChange(UIToolType enmType) 260 { 261 /* Determine tool class of passed tool type: */ 262 const UIToolClass enmClass = UIToolStuff::castTypeToClass(enmType); 263 264 /* For Global tool class: */ 265 if (enmClass == UIToolClass_Global) 266 { 267 /* Mark Machine & Management tools [un]suitable depending on Global tool selected: */ 268 toolMenu()->setUnsuitableToolClass(UIToolClass_Machine, enmType != UIToolType_Machines); 269 toolMenu()->setUnsuitableToolClass(UIToolClass_Management, enmType != UIToolType_Managers); 270 271 /* Switch tool-pane accordingly: */ 272 switchToolTo(enmType); 273 } 274 /* For Machine tool class => switch tool-pane accordingly: */ 275 else if (enmClass == UIToolClass_Machine) 276 machineToolsWidget()->switchToolTo(enmType); 277 /* For Management tool class => switch tool-pane accordingly: */ 278 else if (enmClass == UIToolClass_Management) 279 switchToolTo(enmType); 280 } 281 282 void UIGlobalToolsWidget::sltSwitchToActivitiesTool() 283 { 284 setMenuToolType(UIToolType_Activities); 285 } 286 287 void UIGlobalToolsWidget::prepare() 101 void UIManagementToolsWidget::prepare() 288 102 { 289 103 /* Prepare everything: */ 290 104 prepareWidgets(); 291 prepareConnections();292 293 /* Load settings: */294 loadSettings();295 105 } 296 106 297 void UI GlobalToolsWidget::prepareWidgets()107 void UIManagementToolsWidget::prepareWidgets() 298 108 { 299 109 /* Create layout: */ 300 m_pLayout = new QGridLayout(this);301 if ( m_pLayout)110 QVBoxLayout *pLayout = new QVBoxLayout(this); 111 if (pLayout) 302 112 { 303 113 /* Configure layout: */ 304 m_pLayout->setContentsMargins(0, 0, 0, 0); 305 m_pLayout->setSpacing(0); 306 307 /* Create tool-menu: */ 308 m_pMenu = new UITools(this, UIToolClass_Global, actionPool(), Qt::Widget); 309 if (toolMenu()) 310 { 311 /* Add into layout: */ 312 m_pLayout->addWidget(toolMenu(), 0, 0, 2, 1); 313 } 114 pLayout->setContentsMargins(0, 0, 0, 0); 115 pLayout->setSpacing(0); 314 116 315 117 /* Create tool-pane: */ 316 m_pPane = new UIToolPane(this, UIToolClass_ Global, actionPool());118 m_pPane = new UIToolPane(this, UIToolClass_Management, actionPool()); 317 119 if (toolPane()) 318 120 { … … 321 123 322 124 /* Add into layout: */ 323 m_pLayout->addWidget(toolPane(), 1, 1);125 pLayout->addWidget(toolPane()); 324 126 } 325 127 } 326 128 } 327 328 void UIGlobalToolsWidget::prepareConnections()329 {330 /* UICommon connections: */331 connect(&uiCommon(), &UICommon::sigAskToCommitData,332 this, &UIGlobalToolsWidget::sltHandleCommitData);333 334 /* Global COM event handlers: */335 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,336 this, &UIGlobalToolsWidget::sltHandleMachineRegistrationChanged);337 connect(gEDataManager, &UIExtraDataManager::sigSettingsExpertModeChange,338 this, &UIGlobalToolsWidget::sltHandleSettingsExpertModeChange);339 340 /* Chooser-pane connections: */341 connect(chooser(), &UIChooser::sigSelectionChanged,342 this, &UIGlobalToolsWidget::sltHandleChooserPaneSelectionChange);343 connect(chooser(), &UIChooser::sigCloudProfileStateChange,344 this, &UIGlobalToolsWidget::sltHandleCloudProfileStateChange);345 346 /* Tools-menu connections: */347 connect(toolMenu(), &UITools::sigSelectionChanged,348 this, &UIGlobalToolsWidget::sltHandleToolsMenuIndexChange);349 350 /* Tools-pane connections: */351 connect(this, &UIGlobalToolsWidget::sigToolMenuUpdate,352 this, &UIGlobalToolsWidget::sltHandleGlobalToolMenuUpdate);353 connect(machineToolsWidget(), &UIMachineToolsWidget::sigToolMenuUpdate,354 this, &UIGlobalToolsWidget::sltHandleMachineToolMenuUpdate);355 connect(toolPaneMachine(), &UIToolPane::sigSwitchToActivityOverviewPane,356 this, &UIGlobalToolsWidget::sltSwitchToActivitiesTool);357 }358 359 void UIGlobalToolsWidget::loadSettings()360 {361 /* Acquire & select tools currently chosen in the menu: */362 sltHandleToolsMenuIndexChange(toolMenu()->toolsType(UIToolClass_Global));363 sltHandleToolsMenuIndexChange(toolMenu()->toolsType(UIToolClass_Machine));364 365 /* Update Global tools restrictions: */366 sltHandleGlobalToolMenuUpdate();367 /* Update Machine tools restrictions for currently selected item: */368 UIVirtualMachineItem *pItem = machineToolsWidget()->currentItem();369 if (pItem)370 sltHandleMachineToolMenuUpdate(pItem);371 }372 373 void UIGlobalToolsWidget::cleanupConnections()374 {375 /* Global COM event handlers: */376 disconnect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,377 this, &UIGlobalToolsWidget::sltHandleMachineRegistrationChanged);378 disconnect(gEDataManager, &UIExtraDataManager::sigSettingsExpertModeChange,379 this, &UIGlobalToolsWidget::sltHandleSettingsExpertModeChange);380 381 /* Chooser-pane connections: */382 disconnect(chooser(), &UIChooser::sigSelectionChanged,383 this, &UIGlobalToolsWidget::sltHandleChooserPaneSelectionChange);384 disconnect(chooser(), &UIChooser::sigCloudProfileStateChange,385 this, &UIGlobalToolsWidget::sltHandleCloudProfileStateChange);386 387 /* Tools-menu connections: */388 disconnect(toolMenu(), &UITools::sigSelectionChanged,389 this, &UIGlobalToolsWidget::sltHandleToolsMenuIndexChange);390 391 /* Tools-pane connections: */392 disconnect(this, &UIGlobalToolsWidget::sigToolMenuUpdate,393 this, &UIGlobalToolsWidget::sltHandleGlobalToolMenuUpdate);394 disconnect(machineToolsWidget(), &UIMachineToolsWidget::sigToolMenuUpdate,395 this, &UIGlobalToolsWidget::sltHandleMachineToolMenuUpdate);396 disconnect(toolPaneMachine(), &UIToolPane::sigSwitchToActivityOverviewPane,397 this, &UIGlobalToolsWidget::sltSwitchToActivitiesTool);398 }399 400 UITools *UIGlobalToolsWidget::toolMenu() const401 {402 return m_pMenu;403 }404 405 UIChooser *UIGlobalToolsWidget::chooser() const406 {407 return machineToolsWidget()->chooser();408 }409 410 UIToolPane *UIGlobalToolsWidget::toolPaneMachine() const411 {412 return machineToolsWidget()->toolPane();413 } -
trunk/src/VBox/Frontends/VirtualBox/src/manager/UIManagementToolsWidget.h
r108553 r108555 1 1 /* $Id$ */ 2 2 /** @file 3 * VBox Qt GUI - UI GlobalToolsWidget class declaration.3 * VBox Qt GUI - UIManagementToolsWidget class declaration. 4 4 */ 5 5 … … 26 26 */ 27 27 28 #ifndef FEQT_INCLUDED_SRC_manager_UI GlobalToolsWidget_h29 #define FEQT_INCLUDED_SRC_manager_UI GlobalToolsWidget_h28 #ifndef FEQT_INCLUDED_SRC_manager_UIManagementToolsWidget_h 29 #define FEQT_INCLUDED_SRC_manager_UIManagementToolsWidget_h 30 30 #ifndef RT_WITHOUT_PRAGMA_ONCE 31 31 # pragma once … … 39 39 40 40 /* Forward declarations: */ 41 class QGridLayout;42 class QIToolBar;43 41 class UIActionPool; 44 class UIChooser;45 class UIMachineToolsWidget;46 42 class UIToolPane; 47 class UITools;48 class UIVirtualMachineItem;49 43 50 /** QWidget extension used as GlobalTools Widget instance. */51 class UI GlobalToolsWidget : public QWidget44 /** QWidget extension used as Management Tools Widget instance. */ 45 class UIManagementToolsWidget : public QWidget 52 46 { 53 47 Q_OBJECT; … … 57 51 /** @name Tools pane stuff. 58 52 * @{ */ 59 /** Notifies about required tool menu update. */60 void sigToolMenuUpdate();61 62 53 /** Notifies about Tool type change. */ 63 54 void sigToolTypeChange(); … … 66 57 public: 67 58 68 /** Constructs Global Tools Widget passing @a pParent to the base-class. 69 * @param pActionPool Brings the action-pool reference. */ 70 UIGlobalToolsWidget(QWidget *pParent, UIActionPool *pActionPool); 71 72 /** @name Tool-bar stuff. 73 * @{ */ 74 /** Adds tool-bar into grid layout. */ 75 void addToolBar(QIToolBar *pToolBar); 76 /** @} */ 59 /** Constructs Management Tools Widget passing @a pParent to the base-class. 60 * @param pActionPool Brings the action-pool reference. */ 61 UIManagementToolsWidget(QWidget *pParent, UIActionPool *pActionPool); 77 62 78 63 /** @name Tools pane stuff. … … 80 65 /** Returns tool-pane instance. */ 81 66 UIToolPane *toolPane() const; 82 /** Returns Machine Tools Widget reference. */83 UIMachineToolsWidget *machineToolsWidget() const;84 85 /** Returns menu tool type for the @a enmClass specified. */86 UIToolType menuToolType(UIToolClass enmClass) const;87 /** Defines menu tool @a enmType. */88 void setMenuToolType(UIToolType enmType);89 67 90 68 /** Returns pane tool type. */ … … 104 82 /** @} */ 105 83 106 private slots:107 108 /** @name General stuff.109 * @{ */110 /** Handles request to commit data. */111 void sltHandleCommitData();112 /** @} */113 114 /** @name COM event handling stuff.115 * @{ */116 /** Handles signal about machine registration change.117 * @param uId Brings [un]registered machine id.118 * @param fRegistered Brings whether machine was registered, unregistered otherwise. */119 void sltHandleMachineRegistrationChanged(const QUuid &uId, const bool fRegistered);120 121 /** Handles signal about settings expert mode change. */122 void sltHandleSettingsExpertModeChange();123 /** @} */124 125 /** @name Chooser pane stuff.126 * @{ */127 /** Handles Chooser-pane selection change. */128 void sltHandleChooserPaneSelectionChange();129 130 /** Handles state change for cloud profile with certain @a strProviderShortName and @a strProfileName. */131 void sltHandleCloudProfileStateChange(const QString &strProviderShortName,132 const QString &strProfileName);133 /** @} */134 135 /** @name Tools pane stuff.136 * @{ */137 /** Handles request for Global tools menu update. */138 void sltHandleGlobalToolMenuUpdate();139 /** Handles request for Machine tools menu update for the @a pItem specified. */140 void sltHandleMachineToolMenuUpdate(UIVirtualMachineItem *pItem);141 142 /** Handles signal about Tools-menu index change.143 * @param enmType Brings current tool type. */144 void sltHandleToolsMenuIndexChange(UIToolType enmType);145 146 /** Handles signal requesting switch to Activities tool. */147 void sltSwitchToActivitiesTool();148 /** @} */149 150 84 private: 151 85 … … 156 90 /** Prepares widgets. */ 157 91 void prepareWidgets(); 158 /** Prepares connections. */159 void prepareConnections();160 /** Loads settings. */161 void loadSettings();162 163 /** Cleanups connections. */164 void cleanupConnections();165 92 /** @} */ 166 93 … … 171 98 /** @} */ 172 99 173 /** @name Tools stuff.174 * @{ */175 /** Returns tool-menu instance. */176 UITools *toolMenu() const;177 /** Returns Machine Tools Widget's chooser-pane reference. */178 UIChooser *chooser() const;179 /** Returns Machine Tools Widget's tool-pane instance. */180 UIToolPane *toolPaneMachine() const;181 /** @} */182 183 100 /** Holds the action-pool reference. */ 184 101 UIActionPool *m_pActionPool; 185 102 186 /** Holds the grid-layout instance. */187 QGridLayout *m_pLayout;188 189 /** Holds the tools-menu instance. */190 UITools *m_pMenu;191 103 /** Holds the tool-pane instance. */ 192 104 UIToolPane *m_pPane; 193 105 }; 194 106 195 #endif /* !FEQT_INCLUDED_SRC_manager_UI GlobalToolsWidget_h */107 #endif /* !FEQT_INCLUDED_SRC_manager_UIManagementToolsWidget_h */
Note:
See TracChangeset
for help on using the changeset viewer.