Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
345 Kevin 1
#include "MacroController.h"
2
 
3
MacroController::MacroController(QWidget *parent) : QWidget(parent)
4
{
349 Kevin 5
    sigmapTransmit = new QSignalMapper();
6
    sigmapKeybind = new QSignalMapper();
345 Kevin 7
 
8
    btnExport = new QPushButton("&Export");
9
    btnImport = new QPushButton("&Import");
348 Kevin 10
    btnAddMacro = new QPushButton("&Add");
11
    btnRemoveMacro = new QPushButton("&Remove");
350 Kevin 12
    btnClear = new QPushButton("&Clear");
349 Kevin 13
    currKeyBindInfo = QPair<QPushButton*,int>(NULL, 0);
345 Kevin 14
 
15
    mainLayout = new QGridLayout();
16
 
17
    macroCount = 0;
18
    connected = false;
346 Kevin 19
    lastKnownFilePath = ".";
345 Kevin 20
 
21
    ioLayout = new QHBoxLayout();
348 Kevin 22
    ioLayout->addWidget(btnAddMacro);
23
    ioLayout->addWidget(btnRemoveMacro);
350 Kevin 24
    ioLayout->addWidget(btnClear);
345 Kevin 25
    ioLayout->addWidget(btnExport);
26
    ioLayout->addWidget(btnImport);
348 Kevin 27
    ioLayout->addStretch();
345 Kevin 28
    mainLayout->addLayout(ioLayout, 0, 0, 1, 2);
29
 
30
    for (int i = 0; i < MACRO_DEFAULT_COUNT; i++) {
31
        Macro_AddEntry();
32
    }
33
 
34
    setLayout(mainLayout);
35
 
348 Kevin 36
    connect(btnAddMacro, SIGNAL(clicked()), this, SLOT(Macro_AddEntry()));
37
    connect(btnRemoveMacro, SIGNAL(clicked()), this, SLOT(Macro_RemoveEntry()));
350 Kevin 38
    connect(btnClear, SIGNAL(clicked()), this, SLOT(Macro_Clear()));
345 Kevin 39
    connect(btnExport, SIGNAL(clicked()), this, SLOT(Macro_WriteToFile()));
40
    connect(btnImport, SIGNAL(clicked()), this, SLOT(Macro_ReadFromFile()));
349 Kevin 41
    connect(sigmapTransmit, SIGNAL(mapped(QWidget*)), this, SLOT(Macro_InitTransmit(QWidget*)));
42
    connect(sigmapKeybind, SIGNAL(mapped(int)), this, SLOT(Macro_KeybindPrompt(int)));
350 Kevin 43
 
44
    // Register global event process for keyboard shortcut handling
45
    qApp->installEventFilter(this);
345 Kevin 46
}
47
 
48
MacroController::~MacroController()
49
{
50
 
51
}
52
 
348 Kevin 53
QSize MacroController::sizeHint() const
54
{
55
    return this->minimumSizeHint();
56
}
57
 
345 Kevin 58
void MacroController::Macro_EnableTransmit()
59
{
60
    connected = true;
349 Kevin 61
    for (int i = 0; i < macroBtnSendList.size(); i++) {
62
        macroBtnSendList[i]->setEnabled(true);
345 Kevin 63
    }
64
}
65
 
66
void MacroController::Macro_DisableTransmit()
67
{
68
    connected = false;
349 Kevin 69
    for (int i = 0; i < macroBtnSendList.size(); i++) {
70
        macroBtnSendList[i]->setEnabled(false);
345 Kevin 71
    }
72
}
73
 
74
void MacroController::Macro_InitTransmit(QWidget *t)
75
{
350 Kevin 76
    if (connected) {
77
        QTextEdit *text = qobject_cast<QTextEdit*>(t);
78
        emit Macro_TransmitText(text->toPlainText());
79
    }
345 Kevin 80
}
81
 
349 Kevin 82
void MacroController::Macro_KeybindPrompt(int id)
83
{
84
    QPushButton *btn = qobject_cast<QPushButton*>(sigmapKeybind->mapping(id));
350 Kevin 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
349 Kevin 94
    btn->setDown(true);
95
    btn->setText("Waiting for Key..");
96
    currKeyBindInfo = QPair<QPushButton*,int>(btn, id);
97
 
350 Kevin 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
349 Kevin 106
    btn->installEventFilter(this);
107
}
108
 
345 Kevin 109
void MacroController::Macro_AddEntry()
110
{
111
    macroCount++;
112
 
113
    // Create new layout/widgets
114
    QLineEdit *lineEdit = new QLineEdit(QString("Macro %1").arg(macroCount));
350 Kevin 115
    lineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
116
    lineEdit->setMinimumWidth(100);
117
    lineEdit->setAlignment(Qt::AlignCenter);
345 Kevin 118
    macroNameList.append(lineEdit);
119
 
120
    QTextEdit *textEdit = new QTextEdit();
121
    textEdit->setMinimumHeight(50);
346 Kevin 122
    textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
345 Kevin 123
    textEdit->setWordWrapMode(QTextOption::NoWrap);
124
    textEdit->setFont(QFont("Consolas", 8));
125
    textEdit->setAcceptRichText(false);
346 Kevin 126
    textEdit->setTabChangesFocus(true);
345 Kevin 127
    macroValueList.append(textEdit);
128
 
350 Kevin 129
    QPushButton *keyButton = new QPushButton("Hotkey: None");
349 Kevin 130
    keyButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
131
    macroBtnKeyList.append(keyButton);
132
 
345 Kevin 133
    QPushButton *pushButton = new QPushButton("Send Macro");
134
    pushButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
135
    pushButton->setEnabled(connected);
349 Kevin 136
    macroBtnSendList.append(pushButton);
345 Kevin 137
 
138
    QVBoxLayout *tmpLayout = new QVBoxLayout();
139
    tmpLayout->addWidget(lineEdit);
349 Kevin 140
    tmpLayout->addWidget(keyButton);
345 Kevin 141
    tmpLayout->addWidget(pushButton);
142
 
143
    // Add layout/widgets to main layout
144
    mainLayout->addLayout(tmpLayout, macroCount, 0);
145
    mainLayout->addWidget(textEdit, macroCount, 1);
146
    mainLayout->setColumnStretch(1, 1);
147
 
349 Kevin 148
    // Associate KeyButton with its corresponding ID
149
    sigmapKeybind->setMapping(keyButton, macroCount-1);
150
    connect(keyButton, SIGNAL(clicked()), sigmapKeybind, SLOT(map()));
151
 
345 Kevin 152
    // Associate PushButton with its corresponding TextEdit
349 Kevin 153
    sigmapTransmit->setMapping(pushButton, textEdit);
154
    connect(pushButton, SIGNAL(clicked()), sigmapTransmit, SLOT(map()));
345 Kevin 155
 
156
    QLayoutItem *item = mainLayout->itemAtPosition(1, 1);
346 Kevin 157
    int height = item->widget()->height() + mainLayout->verticalSpacing();
345 Kevin 158
 
348 Kevin 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);
345 Kevin 164
}
165
 
166
void MacroController::Macro_RemoveEntry()
167
{
168
 
169
    // Remove and delete layout/widgets at last macro slot
170
    QLayoutItem *item = mainLayout->itemAtPosition(macroCount, 0);
171
    while(!item->isEmpty())
172
        delete item->layout()->takeAt(0)->widget();
173
    delete item;
174
 
175
    item = mainLayout->itemAtPosition(macroCount, 1);
176
    delete item->widget();
177
 
346 Kevin 178
    item = mainLayout->itemAtPosition(1, 1);
179
    int height = item->widget()->height() + mainLayout->verticalSpacing();
180
 
345 Kevin 181
    // Unmap and remove widgets from lists
349 Kevin 182
    QPushButton *pushButton = macroBtnSendList.back();
183
    sigmapTransmit->removeMappings(pushButton);
345 Kevin 184
 
349 Kevin 185
    QPushButton *keyButton = macroBtnKeyList.back();
186
    sigmapKeybind->removeMappings(keyButton);
187
 
345 Kevin 188
    macroNameList.pop_back();
189
    macroValueList.pop_back();
349 Kevin 190
    macroBtnSendList.pop_back();
191
    macroBtnKeyList.pop_back();
345 Kevin 192
 
350 Kevin 193
    int index = registeredKeyMacroIDs.indexOf(macroCount-1);
194
    if (index >= 0) {
195
        registeredKeySequences.removeAt(index);
196
        registeredKeyMacroIDs.removeAt(index);
197
    }
198
 
345 Kevin 199
    macroCount--;
200
 
348 Kevin 201
    QDockWidget *parent = qobject_cast<QDockWidget*>(this->parent());
202
    if (parent->isFloating())
203
        parent->resize(parent->width(), parent->height() - height);
345 Kevin 204
 
205
    if (macroCount == 1)
348 Kevin 206
        btnRemoveMacro->setEnabled(false);
345 Kevin 207
}
208
 
351 Kevin 209
void MacroController::Macro_Clear()
210
{
211
    for (int i = 0; i < macroCount; i++) {
212
        macroNameList[i]->setText(QString("Macro %1").arg(i+1));
213
        macroValueList[i]->clear();
214
        macroBtnKeyList[i]->setText("Hotkey: None");
215
    }
216
    registeredKeyMacroIDs.clear();
217
    registeredKeySequences.clear();
218
}
219
 
345 Kevin 220
void MacroController::Macro_WriteToFile()
221
{
346 Kevin 222
    QString file = QFileDialog::getSaveFileName(this, "Settings XML File", lastKnownFilePath, "XML File (*.xml)");
223
    QCoreApplication::processEvents(); // Wait for dialog to close
345 Kevin 224
 
346 Kevin 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
 
350 Kevin 231
    if (file.size() != 0)
232
        QFile::remove(file);
233
 
346 Kevin 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
 
244
    for (int i = 0; i < macroCount; i++) {
245
        stream.writeStartElement("Macro_Entry");
246
 
247
        stream.writeAttribute("Name", macroNameList[i]->text());
248
        stream.writeTextElement("Value", macroValueList[i]->toPlainText());
249
 
350 Kevin 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
 
346 Kevin 257
        stream.writeEndElement(); // Macro Entry
258
    }
259
 
260
    stream.writeEndElement(); // Macro
261
    stream.writeEndElement(); // Settings
262
    stream.writeEndDocument();
263
 
264
    inputFile.close();
345 Kevin 265
}
266
 
267
void MacroController::Macro_ReadFromFile()
268
{
346 Kevin 269
    int counter = 0;
345 Kevin 270
 
346 Kevin 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
 
350 Kevin 276
    Macro_Clear();
277
 
346 Kevin 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") {
350 Kevin 303
                QString name, value, keybinding;
346 Kevin 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
                        }
350 Kevin 321
                        if (stream.name() == "Keybinding") {
322
                            stream.readNext();
323
                            keybinding = stream.text().toString();
324
                        }
346 Kevin 325
                    }
326
                    stream.readNext();
327
                }
328
 
329
                // Write values to GUI
330
                if (counter == macroCount)
331
                    Macro_AddEntry();
332
 
333
                macroNameList[counter]->setText(name);
334
                macroValueList[counter]->setText(value);
350 Kevin 335
                if (keybinding != "") {
336
                    registeredKeySequences.append(QKeySequence(keybinding));
337
                    registeredKeyMacroIDs.append(counter);
338
                    macroBtnKeyList[counter]->setText("Hotkey: " + keybinding);
339
                }
346 Kevin 340
                counter++;
341
            }
342
        }
343
    }
345 Kevin 344
}
349 Kevin 345
 
346
bool MacroController::eventFilter(QObject *obj, QEvent *event)
347
{
350 Kevin 348
    // Only process keyboard events
349 Kevin 349
    if (event->type() == QEvent::KeyPress) {
350
        QKeyEvent *keyevent = static_cast<QKeyEvent*>(event);
350 Kevin 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
        }
349 Kevin 357
 
350 Kevin 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) {
363
                    Macro_InitTransmit(macroValueList[registeredKeyMacroIDs[index]]);
364
                    return true;
365
                }
366
            }
367
        }
368
 
369
        // Then save key sequence if needed
349 Kevin 370
        if (currKeyBindInfo.first != NULL) {
350 Kevin 371
            // Ignore modifier keys and locks
349 Kevin 372
            if (keyevent->key() == Qt::Key_Shift || keyevent->key() == Qt::Key_Control ||
350 Kevin 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)
349 Kevin 376
                return true;
377
 
378
            // Reset on ESC key
379
            if (keyevent->key() == Qt::Key_Escape) {
350 Kevin 380
                currKeyBindInfo.first->setText("Hotkey: None");
349 Kevin 381
                currKeyBindInfo.first->setDown(false);
382
                currKeyBindInfo.first->removeEventFilter(this);
383
                currKeyBindInfo = QPair<QPushButton*, int>(NULL, 0);
384
                return true;
385
            }
386
 
350 Kevin 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
            }
349 Kevin 397
        }
398
    }
399
 
400
    return QWidget::eventFilter(obj, event);
401
}