Got the stacked windows working!
[woller] / woller.cpp
1 // Copyright 2010 Ilkka Tengvall
2 //
3 // This file is part of Woller.
4 //
5 // Woller 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 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Woller 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 Woller.  If not, see <http://www.gnu.org/licenses/>.
17
18
19 #ifdef Q_WS_MAEMO_5
20   #include <QtMaemo5>
21 #endif
22 #include "woller.h"
23
24 MainWindow::MainWindow()
25 {
26 #ifdef Q_WS_MAEMO_5
27     this->setAttribute(Qt::WA_Maemo5StackedWindow);
28 #endif
29     woller = new Woller(this);
30     this->setCentralWidget(woller);
31 }
32
33 MainWindow::~MainWindow()
34 {
35     delete woller;
36 }
37
38
39 Woller::Woller(QWidget *parent)
40         : QWidget(parent)
41 {
42     QList<QString> targets;
43     status_orig = new QString(
44             tr("Choose the target or configure and fire it up!"));
45     status_timer = new QTimer(this);
46     hosts_list = new QList<host_s>;
47     settings = new QSettings("woller", "woller");
48     // build the main widget
49     this->setWindowIcon(QIcon::fromTheme("woller", QIcon(":/woller.png")));
50     status_lbl = new QLabel(*status_orig , this);
51     fire_btn = new QPushButton(tr("Wake UP"), this);
52     fire_btn->setToolTip(
53             tr("Press the button to wake up selected target (or config)"));
54     fire_btn->setIcon(QIcon::fromTheme("woller", QIcon(":/woller.png")));
55     targets_cb = new QComboBox(this);
56     targets_cb->setToolTip(
57             tr("Choose host to wake up, or configure hosts option"));
58
59     vlayout = new QVBoxLayout();
60     hlayout = new QHBoxLayout();
61     hlayout->addWidget(targets_cb);
62     hlayout->addWidget(fire_btn);
63     vlayout->addWidget(status_lbl);
64     vlayout->addLayout(hlayout);
65     this->setLayout(vlayout);
66     this->setWindowTitle(tr("Woller-program"));
67 #ifdef Q_WS_MAEMO_5
68     this->setAttribute(Qt::WA_Maemo5StackedWindow);
69 #else
70     this->setWindowModality(Qt::ApplicationModal);
71 #endif
72
73     //populate CB
74     load_config();
75
76     target = new wol_target;
77
78     connect(fire_btn, SIGNAL(clicked()), this, SLOT(send_pkt()));
79     connect(targets_cb, SIGNAL(activated(int)), this, SLOT(targets_act(int)));
80 }
81
82 Woller::~Woller()
83 {
84     int i = targets_cb->currentIndex();
85     qDebug() << "~Woller" << endl;
86     qDebug() << "~Woller last" << i << endl;
87     settings->setValue("last selected", i);
88     settings->setValue("position", pos());
89     delete settings;
90     delete hosts_list;
91     delete status_lbl;
92     delete fire_btn;
93     delete targets_cb;
94     //delete vlayout;
95     //delete hlayout;
96     delete target;
97     delete status_orig;
98     delete status_timer;
99 }
100
101 void Woller::send_pkt()
102 {
103     int t = targets_cb->currentIndex();
104
105     if (t == 0)
106     {
107         targets_act(0);
108     }
109     else
110     {
111         if (!hosts_list->isEmpty() && t-1 <= hosts_list->count())
112         {
113             QString status = "Sent wakeup packet to ";
114             status.append(hosts_list->at(t-1).hostname);
115             target->set_mac(hosts_list->at(t-1).mac);
116             target->set_ip(hosts_list->at(t-1).ip);
117             target->wake_me();
118             status_lbl->setText(status);
119             status_timer->singleShot(5000, this, SLOT(reset_status()));
120         }
121     }
122 }
123
124 void Woller::reset_status()
125 {
126     status_lbl->setText(*status_orig);
127 }
128
129 void Woller::targets_act(int i)
130 {
131     if (i == 0)
132     {
133         // build the config widget
134         qDebug() << "building the config" << endl;
135         config_win = new ConfigWidget(this, hosts_list);
136         connect(config_win, SIGNAL(hosts_changed()), this, SLOT(hosts_changed()));
137         config_win->show();
138     }
139     else
140     {
141         qDebug() << "targets: " << targets_cb->currentText() << endl;
142     }
143 }
144
145 void Woller::add_new(host_s *host)
146 {
147     qDebug() << "add_new:" << host->hostname << host->ip << host->mac << endl;
148 }
149
150 void Woller::do_list()
151 {
152     qDebug() << "do_list" << endl;
153     //populate CB
154     targets_cb->clear();
155     targets_cb->addItem(tr("Configure Targets"));
156     for (int i = 0; i < hosts_list->size(); ++i) {
157         targets_cb->addItem(hosts_list->at(i).hostname);
158      }
159 }
160
161 void Woller::hosts_changed()
162 {
163     qDebug() << "hosts_changed" << endl;
164     do_list();
165     save_config();
166 }
167
168 void Woller::save_config()
169 {
170     qDebug() << "save config" << endl;
171     settings->beginWriteArray("targets");
172     for (int i = 0; i < hosts_list->count(); ++i) {
173         settings->setArrayIndex(i);
174         settings->setValue("hostname", hosts_list->at(i).hostname);
175         settings->setValue("ip", hosts_list->at(i).ip.toString().toAscii());
176         settings->setValue("mac", hosts_list->at(i).mac.toAscii());
177      }
178     settings->endArray();
179 }
180
181 void Woller::load_config()
182 {
183     host_s host;
184     qDebug() << "loading config" << endl;
185     int size = settings->beginReadArray("targets");
186     for (int i = 0; i < size; ++i) {
187         settings->setArrayIndex(i);
188         host.hostname = settings->value("hostname").toString();
189         host.ip.setAddress(settings->value("ip").toString());
190         host.mac = settings->value("mac").toString();
191         hosts_list->append(host);
192     }
193     settings->endArray();
194
195     move(settings->value("position", QPoint(200, 200)).toPoint());
196
197     do_list();
198
199     targets_cb->setCurrentIndex(settings->value("last selected", 0).toInt());
200 }
201
202
203 ConfigWidget::ConfigWidget(QWidget *parent, QList<host_s> *hosts)
204         : QWidget(parent)
205 {
206     qDebug() << "ConfigWidget" << endl;
207     hosts_list = hosts;
208
209     //add buttons and the list
210     this->setWindowTitle(tr("Woller Config"));
211 #ifdef Q_WS_MAEMO_5
212     this->setAttribute(Qt::WA_Maemo5StackedWindow);
213     this->setWindowFlags(this->windowFlags() | Qt::Window);
214 #else
215     this->setWindowModality(Qt::ApplicationModal);
216 #endif
217     add_host = new QPushButton(tr("&Add Host"), this);
218     edit_host = new QPushButton(tr("&Edit Host"), this);
219     del_host = new QPushButton(tr("&Delete Host"), this);
220     save_btn = new QPushButton(tr("&Save"), this);
221     close_btn = new QPushButton(tr("&Close"), this);
222     list = new QListWidget(this);
223     button_layout = new QVBoxLayout();
224     layout = new QHBoxLayout();
225     button_layout->addWidget(add_host);
226     button_layout->addWidget(edit_host);
227     button_layout->addWidget(del_host);
228     button_layout->addWidget(save_btn);
229     button_layout->addWidget(close_btn);
230     layout->addWidget(list);
231     layout->addLayout(button_layout);
232     this->setLayout(layout);
233     close_btn->setDefault(true);
234     edit_host->setDisabled(true);
235     del_host->setDisabled(true);
236     save_btn->setDisabled(true);
237
238     connect(add_host, SIGNAL(clicked()), this, SLOT(add_sig()));
239     connect(edit_host, SIGNAL(clicked()), this, SLOT(edit_sig()));
240     connect(del_host, SIGNAL(clicked()), this, SLOT(del_sig()));
241     connect(save_btn, SIGNAL(clicked()), this, SLOT(save_sig()));
242     connect(close_btn, SIGNAL(clicked()), this, SLOT(close_sig()));
243     connect(list, SIGNAL(itemClicked(QListWidgetItem *)), this,
244             SLOT(select_sig()));
245     connect(list, SIGNAL(itemActivated(QListWidgetItem *)), this,
246             SLOT(edit_sig()));
247
248     for (int i = 0; i < hosts_list->size(); ++i) {
249         list->addItem(hosts_list->at(i).hostname);
250      }
251 }
252
253 ConfigWidget::~ConfigWidget()
254 {
255     delete add_host;
256     delete edit_host;
257     delete del_host;
258     delete save_btn;
259     delete close_btn;
260     delete list;
261     delete button_layout;
262     delete layout;
263 }
264
265
266 void ConfigWidget::add_sig()
267 {
268     qDebug() << "conf add" << endl;
269     host_tmp.hostname.clear();
270     host_tmp.mac.clear();
271     host_tmp.ip.clear();
272     host_widget = new HostWidget(this, &host_tmp);
273
274     connect(host_widget, SIGNAL(change_host()), this, SLOT(host_added()));
275     host_widget->show();
276     qDebug() << "conf add - exit" << endl;
277
278 }
279
280 void ConfigWidget::edit_sig()
281 {
282     qDebug() << "conf edit" << endl;
283     host_tmp.hostname = hosts_list->at(list->currentIndex().row()).hostname;
284     host_tmp.mac = hosts_list->at(list->currentIndex().row()).mac;
285     host_tmp.ip = hosts_list->at(list->currentIndex().row()).ip;
286     host_widget = new HostWidget(this, &host_tmp);
287     connect(host_widget, SIGNAL(change_host()), this, SLOT(host_edited()));
288
289     //hostWidget->hostname->setPlaceholderText("my_host_name");
290     //hostWidget->hostname->setText("edit1");
291     //hostWidget->mac->setPlaceholderText("001133aabbcc");
292     //hostWidget->mac->setText("001133aabbcc");
293     //hostWidget->ip->setPlaceholderText("10.10.11.255");
294     host_widget->show();
295 }
296
297 void ConfigWidget::del_sig()
298 {
299     QMessageBox msg_box;
300     int ret, i = list->currentIndex().row();
301     qDebug() << "conf del: " << i << endl;
302
303     msg_box.setText("Host is to be deleted.");
304     msg_box.setInformativeText("Do you want to save your changes?");
305     msg_box.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
306     msg_box.setDefaultButton(QMessageBox::Save);
307     ret = msg_box.exec();
308
309     if ( ret ==  QMessageBox::Save ) {
310         hosts_list->removeAt(i);
311         list->takeItem(i);
312         emit hosts_changed();
313     }
314 }
315
316 void ConfigWidget::save_sig()
317 {
318     qDebug() << "conf save" << endl;
319     emit hosts_changed();
320     save_btn->setDisabled(true);
321 }
322
323 void ConfigWidget::close_sig()
324 {
325     qDebug() << "conf close" << endl;
326     this->close();
327 }
328
329 void ConfigWidget::select_sig()
330 {
331     qDebug() << "conf sel: " << endl;
332     edit_host->setDisabled(false);
333     del_host->setDisabled(false);
334 }
335
336 void ConfigWidget::host_added()
337 {
338     qDebug() << "host changed" << endl;
339     list->addItem(host_tmp.hostname);
340     qDebug() << "hostsList size:" << hosts_list->size()
341         << "count:" << hosts_list->size() << endl;
342     hosts_list->append(host_tmp);
343     qDebug() << "hostsList size:" << hosts_list->size()
344         << "count:" << hosts_list->size() << endl;
345     save_btn->setDisabled(false);
346     save_btn->setDefault(true);
347 }
348
349 void ConfigWidget::host_edited()
350 {
351     int i = list->currentIndex().row();
352     qDebug() << "host edited: " << i << endl;
353
354     hosts_list->replace(i, host_tmp);
355     list->takeItem(i);
356     list->insertItem(i, hosts_list->at(i).hostname);
357     save_btn->setDisabled(false);
358     save_btn->setDefault(true);
359 }
360
361 HostWidget::HostWidget(QWidget *parent, host_s *host)
362         : QWidget(parent)
363 {
364     //TODO: add setValidator and use QRegExpValidator
365
366     qDebug() << "host widget" << endl;
367     new_host = host;
368     this->setWindowTitle(tr("WOL target details"));
369 #ifdef Q_WS_MAEMO_5
370     this->setAttribute(Qt::WA_Maemo5StackedWindow);
371     this->setWindowFlags(this->windowFlags() | Qt::Window);
372 #else
373     this->setWindowModality(Qt::ApplicationModal);
374 #endif
375     vlayout = new QVBoxLayout(this);
376
377     host_row = new QHBoxLayout;
378     host_lbl = new QLabel(tr("Hostname:"), this);
379     hostname = new QLineEdit(host->hostname, this);
380     hostname->setToolTip(tr("Give target computer's hostname"));
381     host_row->addWidget(host_lbl);
382     host_row->addWidget(hostname);
383     vlayout->addLayout(host_row);
384
385     mac_row = new QHBoxLayout;
386     mac_lbl = new QLabel(tr("Mac:"), this);
387     mac = new QLineEdit(host->mac, this);
388     mac->setToolTip(
389             tr("HW address of target, e.g. 00:01:02:AA:BB:CC. "
390                "Hint, in Linux/Unix: \"ifconfig\","
391                "in Windows \"ipconfig /all\""));
392     mac->setInputMask("HH:HH:HH:HH:HH:HH;0");
393     mac_row->addWidget(mac_lbl);
394     mac_row->addWidget(mac);
395     vlayout->addLayout(mac_row);
396
397     ip_row = new QHBoxLayout;
398     ip_lbl = new QLabel(tr("Bcast ip:"), this);
399     ip = new QLineEdit(host->ip.toString(), this);
400     ip->setToolTip(
401             tr("Use ip broadcast address if you want to wake up target outside"
402                "of local network, or if you have several network interfaces."
403                "E.g. 192.168.1.255. You may leave this empty if unsure."));
404     ip->setMaxLength(MAX_IP_LEN);
405     ip_row->addWidget(ip_lbl);
406     ip_row->addWidget(ip);
407     vlayout->addLayout(ip_row);
408
409     button_row = new QHBoxLayout;
410     cancel = new QPushButton(tr("&Cancel"), this);
411     button_row->addWidget(cancel);
412     ok = new QPushButton(tr("&Ok"), this);
413     ok->setDefault(true);
414     button_row->addWidget(ok);
415     vlayout->addLayout(button_row);
416
417     this->setLayout(vlayout);
418
419     ok->setDefault(true);
420
421     connect(ok, SIGNAL(clicked()), this, SLOT(ok_sig()));
422     connect(cancel, SIGNAL(clicked()), this, SLOT(cancel_sig()));
423
424 }
425
426 void HostWidget::cancel_sig()
427 {
428     qDebug() << "host cancel" << endl;
429     this->close();
430 }
431
432 void HostWidget::ok_sig()
433 {
434     qDebug() << "host ok" << this->hostname->text() << this->mac->text()
435             << this->ip->text() << endl;
436
437     new_host->hostname = this->hostname->text();
438     if (new_host->hostname.isEmpty())
439     {
440         QMessageBox msgBox;
441         msgBox.setText("You must set hostname!");
442         msgBox.exec();
443         return;
444     }
445     new_host->mac= this->mac->text();
446     if (new_host->mac.isEmpty())
447     {
448         QMessageBox msgBox;
449         msgBox.setText("You must set mac!");
450         msgBox.exec();
451         return;
452     }
453     new_host->ip = this->ip->text();
454     emit change_host();
455
456     qDebug() << "host ok" << new_host->hostname << new_host->mac
457             << new_host->ip << endl;
458
459     this->close();
460 }
461
462 HostWidget::~HostWidget()
463 {
464     qDebug() << "host ~widget" << endl;
465     delete vlayout;
466     delete host_row;
467     delete host_lbl;
468     delete hostname;
469     delete mac_row;
470     delete mac_lbl;
471     delete mac;
472     delete ip_row;
473     delete ip_lbl;
474     delete ip;
475     delete button_row;
476     delete cancel;
477     delete ok;
478     //delete new_host;
479 }
480
481
482 int wol_target::get_mac( QString *gmac)
483 {
484     if (!gmac)
485         return -1;
486     *gmac = *mac;
487     return 0;
488 }
489
490 int wol_target::set_mac( const QString smac )
491 {
492     if (smac.size() != MAX_MAC_LEN) {
493         qDebug() << "set mac mac size:" << smac.size() << smac << endl;
494         //return -1;
495     }
496     *mac = smac;
497     qDebug() << "set mac mac:" << mac;
498     return 0;
499 }
500
501 int wol_target::get_ip( QHostAddress *gip)
502 {
503     if (!gip)
504         return -1;
505     *gip = *ip;
506     return 0;
507 }
508
509 int wol_target::set_ip( const QHostAddress sip )
510 {
511     if (sip.isNull())
512         return -1;
513     *ip = sip;
514     return 0;
515 }
516
517
518 wol_target::wol_target()
519 {
520     mac = new QString;
521     ip = new QHostAddress;
522     magic_pkt = new QByteArray;
523 }
524
525 wol_target::~wol_target()
526 {
527     delete mac;
528     delete ip;
529     delete magic_pkt;
530 }
531
532 int wol_target::create_magic_pkt(const QString *mac)
533 {
534     const char magic_header[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
535     qDebug() << "create magic pkt" << endl;
536     if (mac->size() != MAX_MAC_LEN)
537     {
538         qDebug() << "create magic pkt size:" << mac->size() << endl;
539         //return -1;
540     }
541     *magic_pkt = QByteArray::fromRawData(magic_header, sizeof(magic_header));
542     for (int i=0; i < WOL_MAGIC_MAC_CNT; i++)
543         magic_pkt->append(QByteArray::fromHex(mac->toAscii()));
544     return 0;
545 }
546
547 int wol_target::wake_me()
548 {
549     QUdpSocket udpSocket;
550
551     if (mac->isEmpty()) {
552         qDebug() << "wakeme: no mac set" << endl;
553         return -1;
554     }
555     create_magic_pkt(mac);
556     qDebug() << "magic:" << endl;
557     qDebug() << magic_pkt->toHex() << endl ;
558
559     if (ip->isNull()) {
560         qDebug() << "sending broadcast to mac" << *mac << endl;
561         udpSocket.writeDatagram(magic_pkt->data(), magic_pkt->size(),
562                                 QHostAddress::Broadcast,
563                                 WOL_MAGIC_UDP_PORT);
564     }
565     else {
566         qDebug() << "sending with ip" << *ip << endl;
567         udpSocket.writeDatagram(magic_pkt->data(), magic_pkt->size(),
568                                 *ip,
569                                 WOL_MAGIC_UDP_PORT);
570     }
571     return 0;
572 }
573