Subversion Repositories Code-Repo

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
354 Kevin 1
#include "MacroWidget.h"
2
 
3
MacroWidget::MacroWidget(QWidget *parent) : QWidget(parent)
4
{
5
    sigmapTransmit = new QSignalMapper();
6
    sigmapKeybind = new QSignalMapper();
7
 
8
    btnExport = new QPushButton("&Export");
9
    btnImport = new QPushButton("&Import");
10
    btnAddMacro = new QPushButton("&Add");
11
    btnRemoveMacro = new QPushButton("&Remove");
12
    btnClear = new QPushButton("&Clear");
13
    currKeyBindInfo = QPair<QPushButton*,int>(NULL, 0);
14
 
15
    mainLayout = new QGridLayout();
16
 
362 Kevin 17
    count = 0;
354 Kevin 18
    connected = false;
19
    lastKnownFilePath = ".";
20
 
21
    ioLayout = new QHBoxLayout();
22
    ioLayout->addWidget(btnAddMacro);
23
    ioLayout->addWidget(btnRemoveMacro);
24
    ioLayout->addWidget(btnClear);
25
    ioLayout->addWidget(btnExport);
26
    ioLayout->addWidget(btnImport);
27
    ioLayout->addStretch();
28
    mainLayout->addLayout(ioLayout, 0, 0, 1, 2);
29
 
30
    for (int i = 0; i < MACRO_DEFAULT_COUNT; i++) {
362 Kevin 31
        AddEntry();
354 Kevin 32
    }
33
 
34
    setLayout(mainLayout);
35
 
362 Kevin 36
    connect(btnAddMacro, SIGNAL(clicked()), this, SLOT(AddEntry()));
37
    connect(btnRemoveMacro, SIGNAL(clicked()), this, SLOT(RemoveEntry()));
38
    connect(btnClear, SIGNAL(clicked()), this, SLOT(Clear()));
39
    connect(btnExport, SIGNAL(clicked()), this, SLOT(WriteToFile()));
40
    connect(btnImport, SIGNAL(clicked()), this, SLOT(ReadFromFile()));
41
    connect(sigmapTransmit, SIGNAL(mapped(QWidget*)), this, SLOT(InitTransmit(QWidget*)));
42
    connect(sigmapKeybind, SIGNAL(mapped(int)), this, SLOT(KeybindPrompt(int)));
354 Kevin 43
 
44
    // Register global event process for keyboard shortcut handling
45
    qApp->installEventFilter(this);
46
}
47
 
48
MacroWidget::~MacroWidget()
49
{
50
 
51
}
52
 
53
QSize MacroWidget::sizeHint() const
54
{
55
    return this->minimumSizeHint();
56
}
57
 
362 Kevin 58
void MacroWidget::EnableTransmit()
354 Kevin 59
{
60
    connected = true;
362 Kevin 61
    for (int i = 0; i < btnSendList.size(); i++) {
62
        btnSendList[i]->setEnabled(true);
354 Kevin 63
    }
64
}
65
 
362 Kevin 66
void MacroWidget::DisableTransmit()
354 Kevin 67
{
68
    connected = false;
362 Kevin 69
    for (int i = 0; i < btnSendList.size(); i++) {
70
        btnSendList[i]->setEnabled(false);
354 Kevin 71
    }
72
}
73
 
362 Kevin 74
void MacroWidget::InitTransmit(QWidget *t)
354 Kevin 75
{
76
    if (connected) {
77
        QTextEdit *text = qobject_cast<QTextEdit*>(t);
362 Kevin 78
        emit TransmitText(text->toPlainText().toUtf8());
354 Kevin 79
    }
80
}
81
 
362 Kevin 82
void MacroWidget::KeybindPrompt(int id)
354 Kevin 83
{
84
    QPushButton *btn = qobject_cast<QPushButton*>(sigmapKeybind->mapping(id));
85
 
86
    // Check to make sure we arn't processing another key first
87
    if (currKeyBindInfo.first != NULL) {
88
        currKeyBindInfo.first->setText("Hotkey: None");
89
        currKeyBindInfo.first->setDown(false);
90
        currKeyBindInfo.first->removeEventFilter(this);
91
    }
92
 
93
    // Mark and save button as waiting for key sequence
94
    btn->setDown(true);
95
    btn->setText("Waiting for Key..");
96
    currKeyBindInfo = QPair<QPushButton*,int>(btn, id);
97
 
98
    // Remove current keybinding for this macro
99
    int index = registeredKeyMacroIDs.indexOf(id);
100
    if (index >= 0) {
101
        registeredKeySequences.removeAt(index);
102
        registeredKeyMacroIDs.removeAt(index);
103
    }
104
 
105
    // Inspect all following keyboard events
106
    btn->installEventFilter(this);
107
}
108
 
362 Kevin 109
void MacroWidget::AddEntry()
354 Kevin 110
{
362 Kevin 111
    count++;
354 Kevin 112
 
113
    // Create new layout/widgets
362 Kevin 114
    QLineEdit *lineEdit = new QLineEdit(QString("Macro %1").arg(count));
354 Kevin 115
    lineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
116
    lineEdit->setMinimumWidth(100);
117
    lineEdit->setAlignment(Qt::AlignCenter);
362 Kevin 118
    nameList.append(lineEdit);
354 Kevin 119
 
120
    QTextEdit *textEdit = new QTextEdit();
121
    textEdit->setMinimumHeight(50);
122
    textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
123
    textEdit->setWordWrapMode(QTextOption::NoWrap);
124
    textEdit->setFont(QFont("Consolas", 8));
125
    textEdit->setAcceptRichText(false);
126
    textEdit->setTabChangesFocus(true);
362 Kevin 127
    valueList.append(textEdit);
354 Kevin 128
 
129
    QPushButton *keyButton = new QPushButton("Hotkey: None");
130
    keyButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
362 Kevin 131
    btnKeyList.append(keyButton);
354 Kevin 132
 
133
    QPushButton *pushButton = new QPushButton("Send Macro");
134
    pushButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
135
    pushButton->setEnabled(connected);
362 Kevin 136
    btnSendList.append(pushButton);
354 Kevin 137
 
138
    QVBoxLayout *tmpLayout = new QVBoxLayout();
139
    tmpLayout->addWidget(lineEdit);
140
    tmpLayout->addWidget(keyButton);
141
    tmpLayout->addWidget(pushButton);
142
 
143
    // Add layout/widgets to main layout
362 Kevin 144
    mainLayout->addLayout(tmpLayout, count, 0);
145
    mainLayout->addWidget(textEdit, count, 1);
354 Kevin 146
    mainLayout->setColumnStretch(1, 1);
147
 
148
    // Associate KeyButton with its corresponding ID
362 Kevin 149
    sigmapKeybind->setMapping(keyButton, count-1);
354 Kevin 150
    connect(keyButton, SIGNAL(clicked()), sigmapKeybind, SLOT(map()));
151
 
152
    // Associate PushButton with its corresponding TextEdit
153
    sigmapTransmit->setMapping(pushButton, textEdit);
154
    connect(pushButton, SIGNAL(clicked()), sigmapTransmit, SLOT(map()));
155
 
156
    QLayoutItem *item = mainLayout->itemAtPosition(1, 1);
157
    int height = item->widget()->height() + mainLayout->verticalSpacing();
158
 
159
    QDockWidget *parent = qobject_cast<QDockWidget*>(this->parent());
160
    if (parent->isFloating())
161
        parent->resize(parent->width(), parent->height() + height);
162
 
163
    btnRemoveMacro->setEnabled(true);
164
}
165
 
362 Kevin 166
void MacroWidget::RemoveEntry()
354 Kevin 167
{
168
 
169
    // Remove and delete layout/widgets at last macro slot
362 Kevin 170
    QLayoutItem *item = mainLayout->itemAtPosition(count, 0);
354 Kevin 171
    while(!item->isEmpty())
172
        delete item->layout()->takeAt(0)->widget();
173
    delete item;
174
 
362 Kevin 175
    item = mainLayout->itemAtPosition(count, 1);
354 Kevin 176
    delete item->widget();
177
 
178
    item = mainLayout->itemAtPosition(1, 1);
179
    int height = item->widget()->height() + mainLayout->verticalSpacing();
180
 
181
    // Unmap and remove widgets from lists
362 Kevin 182
    QPushButton *pushButton = btnSendList.back();
354 Kevin 183
    sigmapTransmit->removeMappings(pushButton);
184
 
362 Kevin 185
    QPushButton *keyButton = btnKeyList.back();
354 Kevin 186
    sigmapKeybind->removeMappings(keyButton);
187
 
362 Kevin 188
    nameList.pop_back();
189
    valueList.pop_back();
190
    btnSendList.pop_back();
191
    btnKeyList.pop_back();
354 Kevin 192
 
362 Kevin 193
    int index = registeredKeyMacroIDs.indexOf(count-1);
354 Kevin 194
    if (index >= 0) {
195
        registeredKeySequences.removeAt(index);
196
        registeredKeyMacroIDs.removeAt(index);
197
    }
198
 
362 Kevin 199
    count--;
354 Kevin 200
 
201
    QDockWidget *parent = qobject_cast<QDockWidget*>(this->parent());
202
    if (parent->isFloating())
203
        parent->resize(parent->width(), parent->height() - height);
204
 
362 Kevin 205
    if (count == 1)
354 Kevin 206
        btnRemoveMacro->setEnabled(false);
207
}
208
 
362 Kevin 209
void MacroWidget::Clear()
354 Kevin 210
{
362 Kevin 211
    for (int i = 0; i < count; i++) {
212
        nameList[i]->setText(QString("Macro %1").arg(i+1));
213
        valueList[i]->clear();
214
        btnKeyList[i]->setText("Hotkey: None");
354 Kevin 215
    }
216
    registeredKeyMacroIDs.clear();
217
    registeredKeySequences.clear();
218
}
219
 
362 Kevin 220
void MacroWidget::WriteToFile()
354 Kevin 221
{
222
    QString file = QFileDialog::getSaveFileName(this, "Settings XML File", lastKnownFilePath, "XML File (*.xml)");
223
    QCoreApplication::processEvents(); // Wait for dialog to close
224
 
225
    if (file.size() == 0) return;
226
 
227
    // If file was selected, save directory for next time
228
    QFileInfo fileInfo = QFileInfo(file);
229
    lastKnownFilePath = fileInfo.absolutePath();
230
 
231
    if (file.size() != 0)
232
        QFile::remove(file);
233
 
234
    QFile inputFile(file);
235
    if (!inputFile.open(QIODevice::ReadWrite | QIODevice::Text)) return;
236
 
237
    QXmlStreamWriter stream(&inputFile);
238
    stream.setAutoFormatting(true);
239
 
240
    stream.writeStartDocument();
241
    stream.writeStartElement("Settings");
242
    stream.writeStartElement("Macro");
243
 
362 Kevin 244
    for (int i = 0; i < count; i++) {
354 Kevin 245
        stream.writeStartElement("Macro_Entry");
246
 
362 Kevin 247
        stream.writeAttribute("Name", nameList[i]->text());
248
        stream.writeTextElement("Value", valueList[i]->toPlainText());
354 Kevin 249
 
250
        int index = registeredKeyMacroIDs.indexOf(i);
251
        if (index >= 0) {
252
            stream.writeTextElement("Keybinding", registeredKeySequences[index].toString());
253
        } else {
254
            stream.writeTextElement("Keybinding", "");
255
        }
256
 
257
        stream.writeEndElement(); // Macro Entry
258
    }
259
 
260
    stream.writeEndElement(); // Macro
261
    stream.writeEndElement(); // Settings
262
    stream.writeEndDocument();
263
 
264
    inputFile.close();
265
}
266
 
362 Kevin 267
void MacroWidget::ReadFromFile()
354 Kevin 268
{
269
    int counter = 0;
270
 
271
    QString file = QFileDialog::getOpenFileName(this, "Settings XML File", lastKnownFilePath, "XML File (*.xml)");
272
    QCoreApplication::processEvents(); // Wait for dialog to close
273
 
274
    if (file.size() == 0) return;
275
 
362 Kevin 276
    Clear();
354 Kevin 277
 
278
    // If file was selected, save directory for next time
279
    QFileInfo fileInfo = QFileInfo(file);
280
    lastKnownFilePath = fileInfo.absolutePath();
281
 
282
    QFile inputFile(file);
283
    if (!inputFile.open(QIODevice::ReadWrite | QIODevice::Text)) return;
284
 
285
    QXmlStreamReader stream(&inputFile);
286
 
287
    // Parse the XML file till we reach the end  (or errors)
288
    while (!stream.atEnd() && !stream.hasError()) {
289
        QXmlStreamReader::TokenType token = stream.readNext();
290
 
291
        // Ignore StartDocument
292
        if (token == QXmlStreamReader::StartDocument) continue;
293
 
294
        // Parse StartElements
295
        if (token == QXmlStreamReader::StartElement) {
296
 
297
            // Ignore elements <Settings> and <Macro>
298
            if (stream.name() == "Settings") continue;
299
            if (stream.name() == "Macro") continue;
300
 
301
            // Parse element <Macro_Entry>
302
            if (stream.name() == "Macro_Entry") {
303
                QString name, value, keybinding;
304
 
305
                // Read and save attribute value
306
                QXmlStreamAttributes attr = stream.attributes();
307
                if (attr.hasAttribute("Name"))
308
                    name = attr.value("Name").toString();
309
 
310
                stream.readNext();
311
 
312
                // Loop in this element to find all sub-elements
313
                while (!(stream.tokenType() == QXmlStreamReader::EndElement && stream.name() == "Macro_Entry")) {
314
 
315
                    // Parse and save element value
316
                    if (stream.tokenType() == QXmlStreamReader::StartElement) {
317
                        if (stream.name() == "Value") {
318
                            stream.readNext();
319
                            value = stream.text().toString();
320
                        }
321
                        if (stream.name() == "Keybinding") {
322
                            stream.readNext();
323
                            keybinding = stream.text().toString();
324
                        }
325
                    }
326
                    stream.readNext();
327
                }
328
 
329
                // Write values to GUI
362 Kevin 330
                if (counter == count)
331
                    AddEntry();
354 Kevin 332
 
362 Kevin 333
                nameList[counter]->setText(name);
334
                valueList[counter]->setText(value);
354 Kevin 335
                if (keybinding != "") {
336
                    registeredKeySequences.append(QKeySequence(keybinding));
337
                    registeredKeyMacroIDs.append(counter);
362 Kevin 338
                    btnKeyList[counter]->setText("Hotkey: " + keybinding);
354 Kevin 339
                }
340
                counter++;
341
            }
342
        }
343
    }
344
}
345
 
346
bool MacroWidget::eventFilter(QObject *obj, QEvent *event)
347
{
348
    // Only process keyboard events
349
    if (event->type() == QEvent::KeyPress) {
350
        QKeyEvent *keyevent = static_cast<QKeyEvent*>(event);
351
        QKeySequence seq = keyevent->modifiers() + keyevent->key();
352
        if ((keyevent->key() >= 0x21 && keyevent->key() <= 0x2F) ||
353
            (keyevent->key() >= 0x3A && keyevent->key() <= 0x40) ||
354
            (keyevent->key() >= 0x5E && keyevent->key() <= 0x7E) ) {
355
            seq = keyevent->key();
356
        }
357
 
358
        if (connected) {
359
            // First check if key sequence matches any saved ones
360
            if (!registeredKeySequences.isEmpty()) {
361
                int index = registeredKeySequences.indexOf(seq);
362
                if (index >= 0) {
362 Kevin 363
                    InitTransmit(valueList[registeredKeyMacroIDs[index]]);
354 Kevin 364
                    return true;
365
                }
366
            }
367
        }
368
 
369
        // Then save key sequence if needed
370
        if (currKeyBindInfo.first != NULL) {
371
            // Ignore modifier keys and locks
372
            if (keyevent->key() == Qt::Key_Shift || keyevent->key() == Qt::Key_Control ||
373
                keyevent->key() == Qt::Key_Meta || keyevent->key() == Qt::Key_Alt ||
374
                keyevent->key() == Qt::Key_AltGr || keyevent->key() == Qt::Key_CapsLock ||
375
                keyevent->key() == Qt::Key_NumLock || keyevent->key() == Qt::Key_ScrollLock)
376
                return true;
377
 
378
            // Reset on ESC key
379
            if (keyevent->key() == Qt::Key_Escape) {
380
                currKeyBindInfo.first->setText("Hotkey: None");
381
                currKeyBindInfo.first->setDown(false);
382
                currKeyBindInfo.first->removeEventFilter(this);
383
                currKeyBindInfo = QPair<QPushButton*, int>(NULL, 0);
384
                return true;
385
            }
386
 
387
            // Otherwise save key sequence if it doesnt already exist
388
            if (!registeredKeySequences.contains(seq)) {
389
                registeredKeySequences.append(seq);
390
                registeredKeyMacroIDs.append(currKeyBindInfo.second);
391
                currKeyBindInfo.first->setText("Hotkey: " + seq.toString());
392
                currKeyBindInfo.first->setDown(false);
393
                currKeyBindInfo.first->removeEventFilter(this);
394
                currKeyBindInfo = QPair<QPushButton*, int>(NULL, 0);
395
                return true;
396
            }
397
        }
398
    }
399
 
400
    return QWidget::eventFilter(obj, event);
401
}