Index: trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.cpp
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.cpp	(revision 44661)
+++ trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.cpp	(revision 44662)
@@ -7,5 +7,5 @@
 
 /*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -18,9 +18,16 @@
  */
 
-/* Global includes */
+/* Qt includes: */
 #include <QShortcut>
-
-/* Local includes */
+#include <QHeaderView>
+#include <QAbstractItemDelegate>
+#include <QStyledItemDelegate>
+#include <QItemEditorFactory>
+
+/* GUI includes: */
 #include "UIGlobalSettingsInput.h"
+#include "UIShortcutPool.h"
+#include "UIHotKeyEditor.h"
+#include "UIHostComboEditor.h"
 #include "VBoxGlobalSettings.h"
 
@@ -103,2 +110,331 @@
 }
 
+
+UIHotKeyTableModel::UIHotKeyTableModel(QObject *pParent, UIActionPoolType type)
+    : QAbstractTableModel(pParent)
+    , m_type(type)
+{
+}
+
+void UIHotKeyTableModel::load(const UIShortcutCache &shortcuts)
+{
+    /* Load shortcuts: */
+    foreach (const UIShortcutCacheItem &item, shortcuts)
+    {
+        /* Filter out unnecessary shortcuts: */
+        if ((m_type == UIActionPoolType_Selector && item.key.startsWith(GUI_Input_MachineShortcuts)) ||
+            (m_type == UIActionPoolType_Runtime && item.key.startsWith(GUI_Input_SelectorShortcuts)))
+            continue;
+        /* Load shortcut cache item into model: */
+        m_shortcuts << item;
+    }
+    /* Apply filter: */
+    applyFilter();
+    /* Notify table: */
+    emit sigShortcutsLoaded();
+}
+
+void UIHotKeyTableModel::save(UIShortcutCache &shortcuts)
+{
+    /* Save shortcuts: */
+    foreach (const UIShortcutCacheItem &item, m_shortcuts)
+    {
+        /* Search for corresponding cache item pointer: */
+        UIShortcutCache::iterator cacheItemPointer = qFind(shortcuts.begin(), shortcuts.end(), item);
+        /* Make sure the pointer is valid: */
+        if (cacheItemPointer == shortcuts.end())
+            continue;
+        /* Save shortcut cache item into cache: */
+        *cacheItemPointer = item;
+    }
+}
+
+void UIHotKeyTableModel::setFilter(const QString &strFilter)
+{
+    m_strFilter = strFilter;
+    applyFilter();
+}
+
+bool UIHotKeyTableModel::isAllShortcutsUnique()
+{
+    /* Enumerate all the sequences: */
+    QMap<QString, QString> usedSequences;
+    foreach (const UIShortcutCacheItem &item, m_shortcuts)
+        if (!item.currentSequence.isEmpty())
+            usedSequences.insertMulti(item.currentSequence, item.key);
+    /* Enumerate all the duplicated sequences: */
+    QSet<QString> duplicatedSequences;
+    foreach (const QString &strKey, usedSequences.keys())
+        if (usedSequences.count(strKey) > 1)
+            duplicatedSequences.unite(usedSequences.values(strKey).toSet());
+    /* Is there something changed? */
+    if (m_duplicatedSequences != duplicatedSequences)
+    {
+        m_duplicatedSequences = duplicatedSequences;
+        emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+    }
+    /* Are there duplicated shortcuts? */
+    if (!m_duplicatedSequences.isEmpty())
+        return false;
+    /* True by default: */
+    return true;
+}
+
+int UIHotKeyTableModel::rowCount(const QModelIndex& /*parent = QModelIndex()*/) const
+{
+    return m_filteredShortcuts.size();
+}
+
+int UIHotKeyTableModel::columnCount(const QModelIndex& /*parent = QModelIndex()*/) const
+{
+    return 2;
+}
+
+Qt::ItemFlags UIHotKeyTableModel::flags(const QModelIndex &index) const
+{
+    /* No flags for invalid index: */
+    if (!index.isValid()) return Qt::NoItemFlags;
+    /* Switch for different columns: */
+    switch (index.column())
+    {
+        case UIHotKeyTableSection_Name: return Qt::ItemIsEnabled;
+        case UIHotKeyTableSection_Value: return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
+        default: break;
+    }
+    /* No flags by default: */
+    return Qt::NoItemFlags;
+}
+
+QVariant UIHotKeyTableModel::headerData(int iSection, Qt::Orientation orientation, int iRole /*= Qt::DisplayRole*/) const
+{
+    /* Switch for different roles: */
+    switch (iRole)
+    {
+        case Qt::DisplayRole:
+        {
+            /* Invalid for vertical header: */
+            if (orientation == Qt::Vertical) return QString();
+            /* Switch for different columns: */
+            switch (iSection)
+            {
+                case UIHotKeyTableSection_Name: return tr("Name");
+                case UIHotKeyTableSection_Value: return tr("Shortcut");
+                default: break;
+            }
+            /* Invalid for other cases: */
+            return QString();
+        }
+        default: break;
+    }
+    /* Invalid by default: */
+    return QVariant();
+}
+
+QVariant UIHotKeyTableModel::data(const QModelIndex &index, int iRole /*= Qt::DisplayRole*/) const
+{
+    /* No data for invalid index: */
+    if (!index.isValid()) return QVariant();
+    int iIndex = index.row();
+    /* Switch for different roles: */
+    switch (iRole)
+    {
+        case Qt::DisplayRole:
+        {
+            /* Switch for different columns: */
+            switch (index.column())
+            {
+                case UIHotKeyTableSection_Name: return m_filteredShortcuts[iIndex].description;
+                case UIHotKeyTableSection_Value: return m_filteredShortcuts[iIndex].key == UIHostCombo::hostComboCacheKey() ?
+                                                        UIHostCombo::toReadableString(m_filteredShortcuts[iIndex].currentSequence) :
+                                                        m_filteredShortcuts[iIndex].currentSequence;
+                default: break;
+            }
+            /* Invalid for other cases: */
+            return QString();
+        }
+        case Qt::EditRole:
+        {
+            /* Switch for different columns: */
+            switch (index.column())
+            {
+                case UIHotKeyTableSection_Value: return m_filteredShortcuts[iIndex].key == UIHostCombo::hostComboCacheKey() ?
+                                                        QVariant::fromValue(UIHostComboWrapper(m_filteredShortcuts[iIndex].currentSequence)) :
+                                                        QVariant::fromValue(UIHotKey(m_filteredShortcuts[iIndex].currentSequence,
+                                                                                     m_filteredShortcuts[iIndex].defaultSequence));
+                default: break;
+            }
+            /* Invalid for other cases: */
+            return QString();
+        }
+        case Qt::FontRole:
+        {
+            /* Do we have a default font? */
+            QFont font(QApplication::font());
+            /* Switch for different columns: */
+            switch (index.column())
+            {
+                case UIHotKeyTableSection_Value:
+                {
+                    if (m_filteredShortcuts[iIndex].key != UIHostCombo::hostComboCacheKey() &&
+                        m_filteredShortcuts[iIndex].currentSequence != m_filteredShortcuts[iIndex].defaultSequence)
+                        font.setBold(true);
+                    break;
+                }
+                default: break;
+            }
+            /* Return resulting font: */
+            return font;
+        }
+        case Qt::ForegroundRole:
+        {
+            /* Switch for different columns: */
+            switch (index.column())
+            {
+                case UIHotKeyTableSection_Value:
+                {
+                    if (m_duplicatedSequences.contains(m_filteredShortcuts[iIndex].key))
+                        return QBrush(Qt::red);
+                    break;
+                }
+                default: break;
+            }
+            /* Default for other cases: */
+            return QString();
+        }
+        default: break;
+    }
+    /* Invalid by default: */
+    return QVariant();
+}
+
+bool UIHotKeyTableModel::setData(const QModelIndex &index, const QVariant &value, int iRole /*= Qt::EditRole*/)
+{
+    /* Nothing to set for invalid index: */
+    if (!index.isValid()) return false;
+    /* Switch for different roles: */
+    switch (iRole)
+    {
+        case Qt::EditRole:
+        {
+            /* Switch for different columns: */
+            switch (index.column())
+            {
+                case UIHotKeyTableSection_Value:
+                {
+                    /* Get index: */
+                    int iIndex = index.row();
+                    /* Set sequence to shortcut: */
+                    UIShortcutCacheItem &filteredShortcut = m_filteredShortcuts[iIndex];
+                    UIShortcutCache::iterator shortcutPointer = qFind(m_shortcuts.begin(), m_shortcuts.end(), filteredShortcut);
+                    if (shortcutPointer != m_shortcuts.end())
+                    {
+                        filteredShortcut.currentSequence = filteredShortcut.key == UIHostCombo::hostComboCacheKey() ?
+                                                           value.value<UIHostComboWrapper>().toString() :
+                                                           value.value<UIHotKey>().sequence();
+                        *shortcutPointer = filteredShortcut;
+                        emit sigRevalidationRequired();
+                        return true;
+                    }
+                    break;
+                }
+                default: break;
+            }
+            break;
+        }
+        default: break;
+    }
+    /* Nothing to set by default: */
+    return false;
+}
+
+void UIHotKeyTableModel::sort(int iColumn, Qt::SortOrder order /*= Qt::AscendingOrder*/)
+{
+    qStableSort(m_shortcuts.begin(), m_shortcuts.end(), UIShortcutCacheItemFunctor(iColumn, order));
+    applyFilter();
+    emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+}
+
+void UIHotKeyTableModel::applyFilter()
+{
+    /* Erase items first: */
+    beginRemoveRows(QModelIndex(), 0, m_filteredShortcuts.size() - 1);
+    m_filteredShortcuts.clear();
+    endRemoveRows();
+
+    /* If filter is empty: */
+    if (m_strFilter.isEmpty())
+    {
+        /* Just add all the items: */
+        m_filteredShortcuts = m_shortcuts;
+    }
+    else
+    {
+        /* Check if the description matches the filter: */
+        foreach (const UIShortcutCacheItem &item, m_shortcuts)
+        {
+            /* If neither description nor sequence matches the filter, skip item: */
+            if (!item.description.contains(m_strFilter, Qt::CaseInsensitive) &&
+                !item.currentSequence.contains(m_strFilter, Qt::CaseInsensitive))
+                continue;
+            /* Add that item: */
+            m_filteredShortcuts << item;
+        }
+    }
+    beginInsertRows(QModelIndex(), 0, m_filteredShortcuts.size() - 1);
+    endInsertRows();
+}
+
+
+UIHotKeyTable::UIHotKeyTable(UIHotKeyTableModel *pModel)
+{
+    /* Connect model: */
+    setModel(pModel);
+    connect(pModel, SIGNAL(sigShortcutsLoaded()), this, SLOT(sltHandleShortcutsLoaded()));
+
+    /* Configure self: */
+    setTabKeyNavigation(false);
+    setContextMenuPolicy(Qt::CustomContextMenu);
+    setSelectionMode(QAbstractItemView::SingleSelection);
+    setEditTriggers(QAbstractItemView::CurrentChanged | QAbstractItemView::SelectedClicked);
+
+    /* Configure headers: */
+    verticalHeader()->hide();
+    verticalHeader()->setDefaultSectionSize((int)(verticalHeader()->minimumSectionSize() * 1.33));
+    horizontalHeader()->setStretchLastSection(false);
+    horizontalHeader()->setResizeMode(UIHotKeyTableSection_Name, QHeaderView::Interactive);
+    horizontalHeader()->setResizeMode(UIHotKeyTableSection_Value, QHeaderView::Stretch);
+
+    /* Register delegate editor: */
+    if (QAbstractItemDelegate *pAbstractItemDelegate = itemDelegate())
+    {
+        if (QStyledItemDelegate *pStyledItemDelegate = qobject_cast<QStyledItemDelegate*>(pAbstractItemDelegate))
+        {
+            /* Create new item editor factory: */
+            QItemEditorFactory *pNewItemEditorFactory = new QItemEditorFactory;
+
+            /* Register UIHotKeyEditor as the UIHotKey editor: */
+            int iHotKeyTypeId = qRegisterMetaType<UIHotKey>();
+            QStandardItemEditorCreator<UIHotKeyEditor> *pHotKeyItemEditorCreator = new QStandardItemEditorCreator<UIHotKeyEditor>();
+            pNewItemEditorFactory->registerEditor((QVariant::Type)iHotKeyTypeId, pHotKeyItemEditorCreator);
+
+            /* Register UIHostComboEditor as the UIHostComboWrapper: */
+            int iHostComboTypeId = qRegisterMetaType<UIHostComboWrapper>();
+            QStandardItemEditorCreator<UIHostComboEditor> *pHostComboItemEditorCreator = new QStandardItemEditorCreator<UIHostComboEditor>();
+            pNewItemEditorFactory->registerEditor((QVariant::Type)iHostComboTypeId, pHostComboItemEditorCreator);
+
+            /* Set configured item editor factory for table delegate: */
+            pStyledItemDelegate->setItemEditorFactory(pNewItemEditorFactory);
+        }
+    }
+}
+
+void UIHotKeyTable::sltHandleShortcutsLoaded()
+{
+    /* Resize columns to feat contents: */
+    resizeColumnsToContents();
+
+    /* Configure sorting: */
+    sortByColumn(UIHotKeyTableSection_Name, Qt::AscendingOrder);
+    setSortingEnabled(true);
+}
+
Index: trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.h
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.h	(revision 44661)
+++ trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.h	(revision 44662)
@@ -20,7 +20,12 @@
 #define __UIGlobalSettingsInput_h__
 
+/* Qt includes: */
+#include <QAbstractTableModel>
+#include <QTableView>
+
 /* GUI includes: */
 #include "UISettingsPage.h"
 #include "UIGlobalSettingsInput.gen.h"
+#include "UIActionPool.h"
 
 /* Global settings / Input page / Cache / Shortcut cache item: */
@@ -64,4 +69,32 @@
 };
 
+/* Global settings / Input page / Cache / Shortcut cache item sort functor: */
+class UIShortcutCacheItemFunctor
+{
+public:
+
+    UIShortcutCacheItemFunctor(int iColumn, Qt::SortOrder order)
+        : m_iColumn(iColumn)
+        , m_order(order)
+    {
+    }
+
+    bool operator()(const UIShortcutCacheItem &item1, const UIShortcutCacheItem &item2)
+    {
+        switch (m_iColumn)
+        {
+            case 0: return m_order == Qt::AscendingOrder ? item1.description < item2.description : item1.description > item2.description;
+            case 1: return m_order == Qt::AscendingOrder ? item1.currentSequence < item2.currentSequence : item1.currentSequence > item2.currentSequence;
+            default: break;
+        }
+        return m_order == Qt::AscendingOrder ? item1.key < item2.key : item1.key > item2.key;
+    }
+
+private:
+
+    int m_iColumn;
+    Qt::SortOrder m_order;
+};
+
 /* Global settings / Input page / Cache / Shortcut cache: */
 typedef QList<UIShortcutCacheItem> UIShortcutCache;
@@ -112,4 +145,81 @@
 };
 
+/* Hot-key table field indexes: */
+enum UIHotKeyTableSection
+{
+    UIHotKeyTableSection_Name = 0,
+    UIHotKeyTableSection_Value = 1
+};
+
+/* A model representing hot-key combination table: */
+class UIHotKeyTableModel : public QAbstractTableModel
+{
+    Q_OBJECT;
+
+signals:
+
+    /* Notifier: Readiness stuff: */
+    void sigShortcutsLoaded();
+
+    /* Notifier: Validation stuff: */
+    void sigRevalidationRequired();
+
+public:
+
+    /* Constructor: */
+    UIHotKeyTableModel(QObject *pParent, UIActionPoolType type);
+
+    /* API: Loading/saving stuff: */
+    void load(const UIShortcutCache &shortcuts);
+    void save(UIShortcutCache &shortcuts);
+
+    /* API: Filtering stuff: */
+    void setFilter(const QString &strFilter);
+
+    /* API: Validation stuff: */
+    bool isAllShortcutsUnique();
+
+private:
+
+    /* Internal API: Size stuff: */
+    int rowCount(const QModelIndex &parent = QModelIndex()) const;
+    int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+    /* Internal API: Data stuff: */
+    Qt::ItemFlags flags(const QModelIndex &index) const;
+    QVariant headerData(int iSection, Qt::Orientation orientation, int iRole = Qt::DisplayRole) const;
+    QVariant data(const QModelIndex &index, int iRole = Qt::DisplayRole) const;
+    bool setData(const QModelIndex &index, const QVariant &value, int iRole = Qt::EditRole);
+
+    /* Internal API: Sorting stuff: */
+    void sort(int iColumn, Qt::SortOrder order = Qt::AscendingOrder);
+
+    /* Helper: Filtering stuff: */
+    void applyFilter();
+
+    /* Variables: */
+    UIActionPoolType m_type;
+    QString m_strFilter;
+    UIShortcutCache m_shortcuts;
+    UIShortcutCache m_filteredShortcuts;
+    QSet<QString> m_duplicatedSequences;
+};
+
+/* A table reflecting hot-key combinations: */
+class UIHotKeyTable : public QTableView
+{
+    Q_OBJECT;
+
+public:
+
+    /* Constructor: */
+    UIHotKeyTable(UIHotKeyTableModel *pModel);
+
+private slots:
+
+    /* Handler: Readiness stuff: */
+    void sltHandleShortcutsLoaded();
+};
+
 #endif // __UIGlobalSettingsInput_h__
 
