almost ready to merge into master
[avrdudequi:avrdudequi.git] / programmer.cpp
1 /***************************************************************************
2  *   Copyright (C) 2011 by Maurilio                                        *
3  *   mau_ri_tec@yahoo.it                                                   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21
22 #include <QDebug>
23 #include "programmersettings.h"
24 #include "programmer.h"
25 #include <QDir>
26 #include <QStringList>
27 #include <QMessageBox>
28 #include "global.h"
29
30 Programmer *Programmer::m_instance = 0;
31
32 Programmer::Programmer() :
33     QObject(0), programmerIsValid(false)
34 {
35     sysDevice = SysDevice::instance();
36     sysDevice->setParent(this);
37     _currentDevice = UdevQt::Device();
38
39     connect(sysDevice, SIGNAL(deviceRemoved(UdevQt::Device)), this, SLOT(deviceRemoved(UdevQt::Device)));
40
41 }
42
43 Programmer::~Programmer()
44 {
45
46 }
47
48 void Programmer::fillProgrammer(QComboBox *cBoxProg)
49 {
50     _cBoxProg = cBoxProg;
51     QDir programmer_dir(AvrDudeQuiPath::programmers());
52     QStringList xmlFileNames = programmer_dir.entryList(QStringList("*.xml"),
53                                                        QDir::Files);
54     streamParser(xmlFileNames);
55
56     connect(_cBoxProg, SIGNAL(currentIndexChanged(int)), this, SLOT(change(int)));
57
58 }
59
60 QMap<QString, QString> *Programmer::dataMap()
61 {
62     return &m_prog;
63 }
64
65 void Programmer::parseDeviceNode(QDomElement &child)
66 {
67     QDomElement saveChild = child;
68     child = child.firstChildElement();
69     for (; !child.isNull(); child = child.nextSiblingElement())
70         m_prog.insert(saveChild.nodeName() + "." + child.nodeName(), child.text());
71
72     child = saveChild;
73 }
74
75 void Programmer::deviceRemoved(const UdevQt::Device &dev)
76 {
77     /*
78     Tutto questo controllo dipende dal fatto che:
79     Device ha delle proprietà differenti se c'è attivato il monitor. Es "dev" viene
80     passato a questo slot se il device viene rimosso, dev contiene una proprietà ACTION
81     che può assumere valore stringa remove o added.
82     Device non ha la proprietà ACTION sempre e comunque, ma solo a seguito di un evento.
83     Infatti i device ritornati da UdevQt::Client::allDevices() non hanno questa proprietà.
84
85     Device currentDevice = theProgrammer->currentDevice();
86     currentDevice è il device salvato prima, questo non ha la proprietà ACTION.
87
88     Device non implementa l'operatore "==" quindi non è possibile comparare due
89     device, anche perchè la comparazione avrebbe dato esito negativo anche i due
90     device comparati si riferiscono allo stesso device, perchè uno contiene una proprietà
91     in più.
92
93     */
94     //theProgrammer->currentDevice() Action" << theProgrammer->currentDevice().deviceProperties();
95     //Device currentDevice = theProgrammer->currentDevice();
96     QString currentUdi;
97     QString removedUdi;
98     //QMap<QString,QString> * dataP = theProgrammer->dataMap();
99     if (_currentDevice.subsystem() == "usb") {
100         removedUdi = dev.subsystem() + "/"
101                 + dev.devType() + "/"
102                 + dev.deviceProperty("ID_VENDOR_ID") + "/"
103                 + dev.deviceProperty("ID_MODEL_ID") + "/"
104                 + dev.deviceProperty("ID_SERIAL_SHORT") + "/"
105                 + dev.deviceProperty("DEVNAME");
106         currentUdi = _currentDevice.subsystem() + "/"
107                 + _currentDevice.devType() + "/"
108                 + _currentDevice.deviceProperty("ID_VENDOR_ID") + "/"
109                 + _currentDevice.deviceProperty("ID_MODEL_ID") + "/"
110                 + _currentDevice.deviceProperty("ID_SERIAL_SHORT") + "/"
111                 + _currentDevice.deviceProperty("DEVNAME");
112
113
114     }
115     else
116     if (_currentDevice.subsystem() == "tty") {
117         removedUdi = dev.subsystem() + "/"
118                 + dev.deviceProperty("ID_TYPE") + "/"
119                 + dev.deviceProperty("ID_VENDOR_ID") + "/"
120                 + dev.deviceProperty("ID_MODEL_ID") + "/"
121                 + dev.deviceProperty("ID_USB_DRIVER") + "/"
122                 + dev.deviceProperty("ID_SERIAL_SHORT") + "/"
123                 + dev.deviceProperty("DEVNAME");
124         currentUdi = _currentDevice.subsystem() + "/"
125                 + _currentDevice.deviceProperty("ID_TYPE") + "/"
126                 + _currentDevice.deviceProperty("ID_VENDOR_ID") + "/"
127                 + _currentDevice.deviceProperty("ID_MODEL_ID") + "/"
128                 + _currentDevice.deviceProperty("ID_USB_DRIVER") + "/"
129                 + _currentDevice.deviceProperty("ID_SERIAL_SHORT") + "/"
130                 + _currentDevice.deviceProperty("DEVNAME");
131     }
132
133
134     // change(-1) rende invalido il device e il programmatore scelto
135     if (currentUdi == removedUdi) {
136         change(-1);
137     }
138     // !*!*!*!*! DEVE ESSERE PIÙ SEMPLICE !*!*!*!*!*!*
139 }
140
141 void Programmer::settingDialog()
142 {
143
144     ProgrammerSettings _settingDialog(qobject_cast<QWidget *>(parent()));
145     //connect(theProgrammer, SIGNAL(programmerChanged(QString)), &settingDialog, SLOT(abort(QString)));
146     int result = _settingDialog.exec();
147
148     if (result) { // pressed ok
149         _currentDevice = _settingDialog.currentDevice();
150         programmerIsValid = true;
151     } else {
152         programmerIsValid = false;
153     }
154 }
155
156 void Programmer::change(int idx)
157 {
158
159     programmerIsValid = false;
160     m_prog.clear();
161     if ((_cBoxProg->currentIndex() < 1) || (idx < 0) ) {
162         _cBoxProg->setCurrentIndex(0);
163         emit programmerChanged("");
164         return;
165     }
166
167     QStringList file_id = _cBoxProg->itemData(idx).toString().split("/");
168     currentFile = file_id[0];       // es arduino.xml
169     idProgrammer = file_id[1];      // es 2009
170
171
172     QFile file(AvrDudeQuiPath::programmers() + "/" + currentFile);
173     if (!file.open(QFile::ReadOnly | QFile::Text)) {
174         QMessageBox::warning(_cBoxProg, tr("AvrDudeQui error dialog"),
175                              tr("Cannot read file %1:\n%2.")
176                              .arg(currentFile)
177                              .arg(file.errorString()));
178         return;
179     }
180     currentDoc.clear();
181     currentDoc.setContent(&file);
182     int n_id = currentDoc.elementsByTagName("id").count();
183
184     QDomElement root = currentDoc.documentElement();
185     QDomElement child = root.firstChildElement();
186
187     // parse and mach idProgrammer
188     for(int i=0; i<n_id; i++) {
189         if (child.nodeName() == "id") {
190             if (child.attribute("term") == idProgrammer) {
191                 currentProgrammer = child;
192                 break;
193             } else {
194                 child = child.nextSibling().toElement();
195             }
196         }
197     }
198     if (currentProgrammer.isNull())
199         return;
200
201
202     QString node_parent;
203     // parse device and opt element
204     child = currentProgrammer.firstChildElement();
205     for (; !child.isNull(); child = child.nextSibling().toElement()) {
206         if (child.hasAttribute("term")) {
207             m_prog.insert(child.nodeName(), child.attribute("term"));
208         } else {
209             if (child.nodeName() == "device") { // entra nel tag <device>
210                 parseDeviceNode(child);
211             } else {
212                 m_prog.insert(child.parentNode().nodeName()
213                               + "." + child.nodeName(), child.text());
214             }
215             if (child.nodeName() == "opt") { // entra nel tag <opt>
216                 parseDeviceNode(child);
217             }
218         }
219     }
220     file.close();
221     QString udi;
222     // assembling device udi
223     udi = m_prog.value("device.SUBSYSTEM") + "/";
224     if (m_prog.value("device.SUBSYSTEM") == "usb")
225         udi += m_prog.value("device.DEVTYPE")
226                 + "/" + m_prog.value("device.ID_VENDOR_ID")
227                 + "/" + m_prog.value("device.ID_MODEL_ID");
228     else
229         udi += m_prog.value("device.ID_TYPE")
230                 + "/" + m_prog.value("device.ID_VENDOR_ID")
231                 + "/" + m_prog.value("device.ID_MODEL_ID")
232                 + "/" + m_prog.value("device.ID_USB_DRIVER");
233
234     m_prog.insert("device.UDI", udi);
235
236     devListMatch = sysDevice->getMatchListFromUDI(udi);
237
238     // found more candidate device, there's need run dialog to choice device file
239     if (devListMatch.count() > 1)
240         settingDialog();
241     else {
242         if (!devListMatch.isEmpty()) {
243             _currentDevice = devListMatch.at(0);
244             programmerIsValid = true;
245
246         } else {
247             _currentDevice = UdevQt::Device();
248             programmerIsValid = false;
249         }
250     }
251     if (programmerIsValid) {
252         m_prog.insert("id.term", idProgrammer);
253
254     }
255
256
257     emit programmerChanged(idProgrammer);
258 }
259
260 UdevQt::DeviceList Programmer::getDevices()
261 {
262     return devListMatch;
263 }
264
265 bool Programmer::isValid()
266 {
267
268     return programmerIsValid;
269
270 }
271
272 void Programmer::streamParser(const QStringList &xmlFileNames)
273 {
274
275     foreach(QString xmlFileName, xmlFileNames)  {
276         QFile file(AvrDudeQuiPath::programmers() + "/" + xmlFileName);
277         if (!file.open(QFile::ReadOnly | QFile::Text)) {
278             qDebug() << "Errore nel leggere il file " + xmlFileName;
279         } else {
280             currentFile = xmlFileName;
281             reader.setDevice(&file);
282
283             reader.readNext();
284             while (!reader.atEnd()) {
285                 if (reader.isStartElement()) {
286                     if (reader.name() == "avrdude") {
287                            readVersion();
288                     } else {
289                         reader.raiseError(QObject::tr("version tag missing"));
290                     }
291                 } else {
292                     reader.readNext();
293                 }
294             }
295             file.close();
296         }
297     }
298 }
299 void Programmer::readVersion()
300 {
301
302     version = reader.attributes().value("version").toString();
303
304     reader.readNext();
305     while (!reader.atEnd()) {
306         if (reader.isEndElement()) {
307             reader.readNext();
308             break;
309         }
310
311         if (reader.isStartElement()) {
312             if (reader.name() == "id") {
313                 readId();
314             } else {
315                 skipUnknownElement();
316             }
317         } else {
318             reader.readNext();
319         }
320     }
321 }
322
323 void Programmer::readId()
324 {
325     idProgrammer = reader.attributes().value("term").toString();
326     reader.readNext();
327     while (!reader.atEnd()) {
328         if (reader.isEndElement()) {
329             reader.readNext();
330             break;
331         }
332
333         if (reader.isStartElement()) {
334             if (reader.name() == "name") {
335                 readName();
336             } else {
337                 skipUnknownElement();
338             }
339         } else {
340             reader.readNext();
341         }
342     }
343 }
344
345 void Programmer::readName()
346 {
347     _cBoxProg->addItem(reader.readElementText().simplified(),
348                        QVariant(currentFile + "/"+ idProgrammer));
349     if (reader.isEndElement())
350         reader.readNext();
351 }
352
353 void Programmer::skipUnknownElement()
354 {
355     reader.readNext();
356     while (!reader.atEnd()) {
357         if (reader.isEndElement()) {
358             reader.readNext();
359             break;
360         }
361
362         if (reader.isStartElement()) {
363             skipUnknownElement();
364         } else {
365             reader.readNext();
366         }
367     }
368 }