Subversion Repositories Code-Repo

Rev

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