Rev 364 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "MacroWidget.h"
MacroWidget::MacroWidget(QWidget *parent) : QWidget(parent)
{
sigmapTransmit = new QSignalMapper();
sigmapKeybind = new QSignalMapper();
btnExport = new QPushButton("Export");
btnImport = new QPushButton("Import");
btnAddMacro = new QPushButton("&Add");
btnRemoveMacro = new QPushButton("&Remove");
btnClear = new QPushButton("&Clear");
currKeyBindInfo = QPair<QPushButton*,int>(NULL, 0);
mainLayout = new QGridLayout();
count = 0;
connected = false;
lastKnownFilePath = ".";
ioLayout = new QHBoxLayout();
ioLayout->addWidget(btnAddMacro);
ioLayout->addWidget(btnRemoveMacro);
ioLayout->addWidget(btnClear);
ioLayout->addWidget(btnExport);
ioLayout->addWidget(btnImport);
ioLayout->addStretch();
mainLayout->addLayout(ioLayout, 0, 0, 1, 2);
for (int i = 0; i < MACRO_DEFAULT_COUNT; i++) {
AddEntry();
}
setLayout(mainLayout);
connect(btnAddMacro, SIGNAL(clicked()), this, SLOT(AddEntry()));
connect(btnRemoveMacro, SIGNAL(clicked()), this, SLOT(RemoveEntry()));
connect(btnClear, SIGNAL(clicked()), this, SLOT(Clear()));
connect(btnExport, SIGNAL(clicked()), this, SLOT(WriteToFile()));
connect(btnImport, SIGNAL(clicked()), this, SLOT(ReadFromFile()));
connect(sigmapTransmit, SIGNAL(mapped(QWidget*)), this, SLOT(InitTransmit(QWidget*)));
connect(sigmapKeybind, SIGNAL(mapped(int)), this, SLOT(KeybindPrompt(int)));
// Register global event process for keyboard shortcut handling
qApp->installEventFilter(this);
}
MacroWidget::~MacroWidget()
{
}
QSize MacroWidget::sizeHint() const
{
return this->minimumSizeHint();
}
void MacroWidget::EnableTransmit(bool enable)
{
if (enable) {
connected = true;
for (int i = 0; i < btnSendList.size(); i++) {
btnSendList[i]->setEnabled(true);
}
} else {
connected = false;
for (int i = 0; i < btnSendList.size(); i++) {
btnSendList[i]->setEnabled(false);
}
}
}
void MacroWidget::InitTransmit(QWidget *t)
{
if (connected) {
QTextEdit *text = qobject_cast<QTextEdit*>(t);
emit TransmitText(text->toPlainText().toUtf8());
}
}
void MacroWidget::KeybindPrompt(int id)
{
QPushButton *btn = qobject_cast<QPushButton*>(sigmapKeybind->mapping(id));
// Check to make sure we arn't processing another key first
if (currKeyBindInfo.first != NULL) {
currKeyBindInfo.first->setText("Hotkey: None");
currKeyBindInfo.first->setDown(false);
currKeyBindInfo.first->removeEventFilter(this);
}
// Mark and save button as waiting for key sequence
btn->setDown(true);
btn->setText("Waiting for Key..");
currKeyBindInfo = QPair<QPushButton*,int>(btn, id);
// Remove current keybinding for this macro
int index = registeredKeyMacroIDs.indexOf(id);
if (index >= 0) {
registeredKeySequences.removeAt(index);
registeredKeyMacroIDs.removeAt(index);
}
// Inspect all following keyboard events
btn->installEventFilter(this);
}
void MacroWidget::AddEntry()
{
count++;
// Create new layout/widgets
QLineEdit *lineEdit = new QLineEdit(QString("Macro %1").arg(count));
lineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
lineEdit->setMinimumWidth(100);
lineEdit->setAlignment(Qt::AlignCenter);
nameList.append(lineEdit);
QTextEdit *textEdit = new QTextEdit();
textEdit->setMinimumHeight(50);
textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
textEdit->setWordWrapMode(QTextOption::NoWrap);
textEdit->setFont(QFont("Consolas", 8));
textEdit->setAcceptRichText(false);
textEdit->setTabChangesFocus(true);
valueList.append(textEdit);
QPushButton *keyButton = new QPushButton("Hotkey: None");
keyButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
btnKeyList.append(keyButton);
QPushButton *pushButton = new QPushButton("Send Macro");
pushButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
pushButton->setEnabled(connected);
btnSendList.append(pushButton);
QVBoxLayout *tmpLayout = new QVBoxLayout();
tmpLayout->addWidget(lineEdit);
tmpLayout->addWidget(keyButton);
tmpLayout->addWidget(pushButton);
// Add layout/widgets to main layout
mainLayout->addLayout(tmpLayout, count, 0);
mainLayout->addWidget(textEdit, count, 1);
mainLayout->setColumnStretch(1, 1);
// Associate KeyButton with its corresponding ID
sigmapKeybind->setMapping(keyButton, count-1);
connect(keyButton, SIGNAL(clicked()), sigmapKeybind, SLOT(map()));
// Associate PushButton with its corresponding TextEdit
sigmapTransmit->setMapping(pushButton, textEdit);
connect(pushButton, SIGNAL(clicked()), sigmapTransmit, SLOT(map()));
QLayoutItem *item = mainLayout->itemAtPosition(1, 1);
int height = item->widget()->height() + mainLayout->verticalSpacing();
QDockWidget *parent = qobject_cast<QDockWidget*>(this->parent());
if (parent->isFloating())
parent->resize(parent->width(), parent->height() + height);
btnRemoveMacro->setEnabled(true);
}
void MacroWidget::RemoveEntry()
{
// Remove and delete layout/widgets at last macro slot
QLayoutItem *item = mainLayout->itemAtPosition(count, 0);
while(!item->isEmpty())
delete item->layout()->takeAt(0)->widget();
delete item;
item = mainLayout->itemAtPosition(count, 1);
delete item->widget();
item = mainLayout->itemAtPosition(1, 1);
int height = item->widget()->height() + mainLayout->verticalSpacing();
// Unmap and remove widgets from lists
QPushButton *pushButton = btnSendList.back();
sigmapTransmit->removeMappings(pushButton);
QPushButton *keyButton = btnKeyList.back();
sigmapKeybind->removeMappings(keyButton);
nameList.pop_back();
valueList.pop_back();
btnSendList.pop_back();
btnKeyList.pop_back();
int index = registeredKeyMacroIDs.indexOf(count-1);
if (index >= 0) {
registeredKeySequences.removeAt(index);
registeredKeyMacroIDs.removeAt(index);
}
count--;
QDockWidget *parent = qobject_cast<QDockWidget*>(this->parent());
if (parent->isFloating())
parent->resize(parent->width(), parent->height() - height);
if (count == 1)
btnRemoveMacro->setEnabled(false);
}
void MacroWidget::Clear()
{
for (int i = 0; i < count; i++) {
nameList[i]->setText(QString("Macro %1").arg(i+1));
valueList[i]->clear();
btnKeyList[i]->setText("Hotkey: None");
}
registeredKeyMacroIDs.clear();
registeredKeySequences.clear();
}
void MacroWidget::WriteToFile()
{
QString file = QFileDialog::getSaveFileName(this, "Settings XML File", lastKnownFilePath, "XML File (*.xml)");
QCoreApplication::processEvents(); // Wait for dialog to close
if (file.size() == 0) return;
// If file was selected, save directory for next time
QFileInfo fileInfo = QFileInfo(file);
lastKnownFilePath = fileInfo.absolutePath();
if (file.size() != 0)
QFile::remove(file);
QFile inputFile(file);
if (!inputFile.open(QIODevice::ReadWrite | QIODevice::Text)) return;
QXmlStreamWriter stream(&inputFile);
stream.setAutoFormatting(true);
stream.writeStartDocument();
stream.writeStartElement("Settings");
stream.writeStartElement("Macro");
for (int i = 0; i < count; i++) {
stream.writeStartElement("Macro_Entry");
stream.writeAttribute("Name", nameList[i]->text());
stream.writeTextElement("Value", valueList[i]->toPlainText());
int index = registeredKeyMacroIDs.indexOf(i);
if (index >= 0) {
stream.writeTextElement("Keybinding", registeredKeySequences[index].toString());
} else {
stream.writeTextElement("Keybinding", "");
}
stream.writeEndElement(); // Macro Entry
}
stream.writeEndElement(); // Macro
stream.writeEndElement(); // Settings
stream.writeEndDocument();
inputFile.close();
}
void MacroWidget::ReadFromFile()
{
int counter = 0;
QString file = QFileDialog::getOpenFileName(this, "Settings XML File", lastKnownFilePath, "XML File (*.xml)");
QCoreApplication::processEvents(); // Wait for dialog to close
if (file.size() == 0) return;
Clear();
// If file was selected, save directory for next time
QFileInfo fileInfo = QFileInfo(file);
lastKnownFilePath = fileInfo.absolutePath();
QFile inputFile(file);
if (!inputFile.open(QIODevice::ReadWrite | QIODevice::Text)) return;
QXmlStreamReader stream(&inputFile);
// Parse the XML file till we reach the end (or errors)
while (!stream.atEnd() && !stream.hasError()) {
QXmlStreamReader::TokenType token = stream.readNext();
// Ignore StartDocument
if (token == QXmlStreamReader::StartDocument) continue;
// Parse StartElements
if (token == QXmlStreamReader::StartElement) {
// Ignore elements <Settings> and <Macro>
if (stream.name() == "Settings") continue;
if (stream.name() == "Macro") continue;
// Parse element <Macro_Entry>
if (stream.name() == "Macro_Entry") {
QString name, value, keybinding;
// Read and save attribute value
QXmlStreamAttributes attr = stream.attributes();
if (attr.hasAttribute("Name"))
name = attr.value("Name").toString();
stream.readNext();
// Loop in this element to find all sub-elements
while (!(stream.tokenType() == QXmlStreamReader::EndElement && stream.name() == "Macro_Entry")) {
// Parse and save element value
if (stream.tokenType() == QXmlStreamReader::StartElement) {
if (stream.name() == "Value") {
stream.readNext();
value = stream.text().toString();
}
if (stream.name() == "Keybinding") {
stream.readNext();
keybinding = stream.text().toString();
}
}
stream.readNext();
}
// Write values to GUI
if (counter == count)
AddEntry();
nameList[counter]->setText(name);
valueList[counter]->setText(value);
if (keybinding != "") {
registeredKeySequences.append(QKeySequence(keybinding));
registeredKeyMacroIDs.append(counter);
btnKeyList[counter]->setText("Hotkey: " + keybinding);
}
counter++;
}
}
}
}
bool MacroWidget::eventFilter(QObject *obj, QEvent *event)
{
// Only process keyboard events
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyevent = static_cast<QKeyEvent*>(event);
QKeySequence seq = keyevent->modifiers() + keyevent->key();
if ((keyevent->key() >= 0x21 && keyevent->key() <= 0x2F) ||
(keyevent->key() >= 0x3A && keyevent->key() <= 0x40) ||
(keyevent->key() >= 0x5E && keyevent->key() <= 0x7E) ) {
seq = keyevent->key();
}
if (connected) {
// First check if key sequence matches any saved ones
if (!registeredKeySequences.isEmpty()) {
int index = registeredKeySequences.indexOf(seq);
if (index >= 0) {
InitTransmit(valueList[registeredKeyMacroIDs[index]]);
return true;
}
}
}
// Then save key sequence if needed
if (currKeyBindInfo.first != NULL) {
// Ignore modifier keys and locks
if (keyevent->key() == Qt::Key_Shift || keyevent->key() == Qt::Key_Control ||
keyevent->key() == Qt::Key_Meta || keyevent->key() == Qt::Key_Alt ||
keyevent->key() == Qt::Key_AltGr || keyevent->key() == Qt::Key_CapsLock ||
keyevent->key() == Qt::Key_NumLock || keyevent->key() == Qt::Key_ScrollLock)
return true;
// Reset on ESC key
if (keyevent->key() == Qt::Key_Escape) {
currKeyBindInfo.first->setText("Hotkey: None");
currKeyBindInfo.first->setDown(false);
currKeyBindInfo.first->removeEventFilter(this);
currKeyBindInfo = QPair<QPushButton*, int>(NULL, 0);
return true;
}
// Otherwise save key sequence if it doesnt already exist
if (!registeredKeySequences.contains(seq)) {
registeredKeySequences.append(seq);
registeredKeyMacroIDs.append(currKeyBindInfo.second);
currKeyBindInfo.first->setText("Hotkey: " + seq.toString());
currKeyBindInfo.first->setDown(false);
currKeyBindInfo.first->removeEventFilter(this);
currKeyBindInfo = QPair<QPushButton*, int>(NULL, 0);
return true;
}
}
}
return QWidget::eventFilter(obj, event);
}