1 /* cdrdao - write audio CD-Rs in disc-at-once mode
2 *
3 * Copyright (C) 1998-2002 Andreas Mueller <andreas@daneb.de>
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 Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <stdio.h>
21 #include <limits.h>
22 #include <math.h>
23 #include <assert.h>
24 #include <ctype.h>
25
26 #include <gtkmm.h>
27 #include <gnome.h>
28
29 #include "DeviceConfDialog.h"
30
31 #include "CdDevice.h"
32 #include "guiUpdate.h"
33
34 #include "util.h"
35
36 #define MAX_DEVICE_TYPE_ID 2
37
38 static CdDevice::DeviceType ID2DEVICE_TYPE[MAX_DEVICE_TYPE_ID + 1] = {
39 CdDevice::CD_ROM,
40 CdDevice::CD_R,
41 CdDevice::CD_RW
42 };
43
44
DeviceConfDialog()45 DeviceConfDialog::DeviceConfDialog()
46 {
47 int i;
48 Gtk::Label *label;
49 Gtk::Table *table;
50 Gtk::HBox *hbox;
51 Gtk::VBox *vbox;
52 Gtk::Button *button;
53
54 active_ = false;
55
56 set_title(_("Configure Devices"));
57
58 // TreeView initialization
59 listModel_ = Gtk::ListStore::create(listColumns_);
60 list_.set_model(listModel_);
61 list_.append_column(_("Dev"), listColumns_.dev);
62 list_.append_column(_("Vendor"), listColumns_.vendor);
63 list_.append_column(_("Model"), listColumns_.model);
64 list_.append_column(_("Status"), listColumns_.status);
65
66 selectedRow_ = list_.get_selection()->get_selected();
67 list_.get_selection()->signal_changed().
68 connect(mem_fun(*this, &DeviceConfDialog::selectionChanged));
69
70 Gtk::Menu *dmenu = manage(new Gtk::Menu);
71 Gtk::MenuItem *mi;
72
73 for (i = 0; i <= CdDevice::maxDriverId(); i++) {
74 mi = manage(new Gtk::MenuItem(CdDevice::driverName(i)));
75 mi->signal_activate().connect(bind(mem_fun(*this,
76 &DeviceConfDialog::setDriverId),
77 i));
78 mi->show();
79 dmenu->append(*mi);
80 }
81
82 driverMenu_ = manage(new Gtk::OptionMenu);
83 driverMenu_->set_menu(*dmenu);
84
85 Gtk::Menu *tmenu = manage(new Gtk::Menu);
86
87 for (i = 0; i <= MAX_DEVICE_TYPE_ID; i++) {
88 mi = manage(new
89 Gtk::MenuItem(CdDevice::deviceType2string(ID2DEVICE_TYPE[i])));
90 mi->signal_activate().connect(bind(mem_fun(*this,
91 &DeviceConfDialog::setDeviceType),
92 i));
93 mi->show();
94 tmenu->append(*mi);
95 }
96
97 devtypeMenu_ = manage(new Gtk::OptionMenu);
98 devtypeMenu_->set_menu(*tmenu);
99
100 devEntry_.set_max_length(32);
101 vendorEntry_.set_max_length(8);
102 productEntry_.set_max_length(16);
103
104 Gtk::VBox *contents = manage(new Gtk::VBox);
105 contents->set_spacing(5);
106 contents->set_border_width(7);
107
108 // ---------------------------- Device list
109 Gtk::VBox *listBox = manage(new Gtk::VBox);
110 listBox->set_spacing(5);
111 listBox->set_border_width(5);
112
113 hbox = manage(new Gtk::HBox);
114
115 hbox->pack_start(list_, Gtk::PACK_EXPAND_WIDGET);
116
117 Gtk::Adjustment *adjust = manage(new Gtk::Adjustment(0.0, 0.0, 0.0));
118 Gtk::VScrollbar *scrollBar = manage(new Gtk::VScrollbar(*adjust));
119 hbox->pack_start(*scrollBar, Gtk::PACK_SHRINK);
120
121 list_.set_vadjustment(*adjust);
122
123 listBox->pack_start(*hbox, Gtk::PACK_EXPAND_WIDGET);
124
125 Gtk::ButtonBox *bbox = manage(new Gtk::HButtonBox(Gtk::BUTTONBOX_SPREAD));
126
127 button = manage(new Gtk::Button(_("Rescan")));
128 bbox->pack_start(*button);
129 button->signal_clicked().
130 connect(sigc::mem_fun(*this,&DeviceConfDialog::rescanAction));
131
132 button = manage(new Gtk::Button(Gtk::StockID(Gtk::Stock::DELETE)));
133 bbox->pack_start(*button);
134 button->signal_clicked().
135 connect(sigc::mem_fun(*this,&DeviceConfDialog::deleteDeviceAction));
136
137 listBox->pack_start(*bbox, Gtk::PACK_SHRINK);
138
139 listFrame_.set_label(_(" Device List "));
140 listFrame_.add(*listBox);
141 contents->pack_start(listFrame_, Gtk::PACK_EXPAND_WIDGET);
142
143 // ---------------------------- Device settings
144
145 settingFrame_.set_label(_(" Device Settings "));
146 table = manage(new Gtk::Table(2, 4, FALSE));
147 table->set_row_spacings(5);
148 table->set_col_spacings(5);
149 table->set_border_width(10);
150 settingFrame_.add(*table);
151
152 label = manage(new Gtk::Label(_("Device Type:")));
153 table->attach(*label, 0, 1, 0, 1);
154 table->attach(*devtypeMenu_, 1, 2, 0, 1);
155
156 label = manage(new Gtk::Label(_("Driver:")));
157 table->attach(*label, 0, 1, 1, 2);
158 table->attach(*driverMenu_, 1, 2, 1, 2);
159
160 label = manage(new Gtk::Label(_("Driver Options:")));
161 table->attach(*label, 0, 1, 2, 3);
162 table->attach(driverOptionsEntry_, 1, 2, 2, 3);
163
164 contents->pack_start(settingFrame_, Gtk::PACK_SHRINK);
165
166 // -------------- Add device
167
168 addDeviceFrame_.set_label(_(" Add Device "));
169 Gtk::VBox *addDeviceBox = manage(new Gtk::VBox);
170 addDeviceBox->set_spacing(5);
171 addDeviceBox->set_border_width(5);
172
173 table = manage(new Gtk::Table(3, 2, FALSE));
174 table->set_row_spacings(5);
175 table->set_col_spacings(5);
176 addDeviceBox->pack_start(*table, Gtk::PACK_EXPAND_WIDGET);
177
178 label = manage(new Gtk::Label(_("Device:")));
179 table->attach(*label, 0, 1, 0, 1);
180 table->attach(devEntry_, 1, 2, 0, 1);
181
182 label = manage(new Gtk::Label(_("Vendor:")));
183 table->attach(*label, 0, 1, 1, 2);
184 table->attach(vendorEntry_, 1, 2, 1, 2);
185
186 label = manage(new Gtk::Label(_("Product:")));
187 table->attach(*label, 0, 1, 2, 3);
188 table->attach(productEntry_, 1, 2, 2, 3);
189
190 bbox = manage(new Gtk::HButtonBox(Gtk::BUTTONBOX_SPREAD));
191 bbox->set_spacing(5);
192 Gtk::Button* addButton =
193 manage(new Gtk::Button(Gtk::StockID(Gtk::Stock::ADD)));
194 bbox->pack_start(*addButton);
195 addButton->signal_clicked().
196 connect(mem_fun(*this, &DeviceConfDialog::addDeviceAction));
197 addDeviceBox->pack_start(*bbox);
198
199 addDeviceFrame_.add(*addDeviceBox);
200 contents->pack_start(addDeviceFrame_, Gtk::PACK_SHRINK);
201
202 // 3 buttons at bottom of window.
203
204 bbox = manage(new Gtk::HButtonBox(Gtk::BUTTONBOX_SPREAD));
205 bbox->set_spacing(5);
206 hbox->set_border_width(10);
207 Gtk::Button* applyButton =
208 manage(new Gtk::Button(Gtk::StockID(Gtk::Stock::APPLY)));
209 bbox->pack_start(*applyButton);
210 applyButton->signal_clicked().connect(mem_fun(*this,
211 &DeviceConfDialog::applyAction));
212
213 Gtk::Button *resetButton = manage(new Gtk::Button(_("Reset")));
214 bbox->pack_start(*resetButton);
215 resetButton->signal_clicked().connect(mem_fun(*this,
216 &DeviceConfDialog::resetAction));
217
218 Gtk::Button *cancelButton =
219 manage(new Gtk::Button(Gtk::StockID(Gtk::Stock::CLOSE)));
220 bbox->pack_start(*cancelButton);
221 cancelButton->signal_clicked().connect(mem_fun(*this,
222 &DeviceConfDialog::closeAction));
223
224 contents->pack_start(*bbox, Gtk::PACK_SHRINK);
225
226 add(*contents);
227 }
228
~DeviceConfDialog()229 DeviceConfDialog::~DeviceConfDialog()
230 {
231 }
232
233
start()234 void DeviceConfDialog::start()
235 {
236 if (active_) {
237 present();
238 return;
239 }
240
241 active_ = true;
242 update(UPD_CD_DEVICES);
243 show_all();
244 }
245
stop()246 void DeviceConfDialog::stop()
247 {
248 hide();
249 active_ = false;
250 }
251
update(unsigned long level)252 void DeviceConfDialog::update(unsigned long level)
253 {
254 if (!active_)
255 return;
256
257 if (level & UPD_CD_DEVICES)
258 import();
259 else if (level & UPD_CD_DEVICE_STATUS)
260 importStatus();
261 }
262
closeAction()263 void DeviceConfDialog::closeAction()
264 {
265 stop();
266 }
267
resetAction()268 void DeviceConfDialog::resetAction()
269 {
270 import();
271 }
272
273
applyAction()274 void DeviceConfDialog::applyAction()
275 {
276 if (selectedRow_)
277 exportConfiguration(selectedRow_);
278 exportData();
279 guiUpdate(UPD_CD_DEVICES);
280 }
281
addDeviceAction()282 void DeviceConfDialog::addDeviceAction()
283 {
284 const char *s;
285
286 std::string dev;
287 std::string vendor;
288 std::string product;
289 CdDevice *cddev;
290
291 if ((s = checkString(devEntry_.get_text())) == NULL)
292 return;
293 dev = s;
294
295 if ((s = checkString(vendorEntry_.get_text())) == NULL)
296 return;
297 vendor = s;
298
299 if ((s = checkString(productEntry_.get_text())) == NULL)
300 return;
301 product = s;
302
303 if (CdDevice::find(dev.c_str()) != NULL)
304 return;
305
306 cddev = CdDevice::add(dev.c_str(), vendor.c_str(), product.c_str());
307
308 if (cddev) {
309 cddev->manuallyConfigured(true);
310 Gtk::TreeIter new_entry = appendTableEntry(cddev);
311 list_.get_selection()->select(new_entry);
312 }
313
314 guiUpdate(UPD_CD_DEVICES);
315 }
316
deleteDeviceAction()317 void DeviceConfDialog::deleteDeviceAction()
318 {
319 DeviceData *data;
320 CdDevice *dev;
321
322 if (selectedRow_) {
323
324 data = (*selectedRow_)[listColumns_.data];
325
326 dev = CdDevice::find(data->dev.c_str());
327 if (dev == NULL || dev->status() == CdDevice::DEV_RECORDING ||
328 dev->status() == CdDevice::DEV_BLANKING) {
329 // don't remove device that is currently busy
330 return;
331 }
332
333 CdDevice::remove(data->dev.c_str());
334 listModel_->erase(selectedRow_);
335 list_.get_selection()->unselect_all();
336 selectedRow_ = list_.get_selection()->get_selected();
337 delete data;
338
339 guiUpdate(UPD_CD_DEVICES);
340 }
341 }
342
rescanAction()343 void DeviceConfDialog::rescanAction()
344 {
345 CdDevice::scan();
346 guiUpdate(UPD_CD_DEVICES);
347 }
348
appendTableEntry(CdDevice * dev)349 Gtk::TreeIter DeviceConfDialog::appendTableEntry(CdDevice *dev)
350 {
351 DeviceData *data;
352 const gchar *rowStr[6];
353
354 data = new DeviceData;
355 data->dev = dev->dev();
356 data->driverId = dev->driverId();
357 data->options = dev->driverOptions();
358
359 switch (dev->deviceType()) {
360 case CdDevice::CD_ROM:
361 data->deviceType = 0;
362 break;
363 case CdDevice::CD_R:
364 data->deviceType = 1;
365 break;
366 case CdDevice::CD_RW:
367 data->deviceType = 2;
368 break;
369 }
370
371 Gtk::TreeIter newiter = listModel_->append();
372 Gtk::TreeModel::Row row = *newiter;
373 row[listColumns_.dev] = data->dev;
374 row[listColumns_.vendor] = dev->vendor();
375 row[listColumns_.model] = dev->product();
376 row[listColumns_.status] = CdDevice::status2string(dev->status());
377 row[listColumns_.data] = data;
378
379 return newiter;
380 }
381
import()382 void DeviceConfDialog::import()
383 {
384 CdDevice *drun;
385 DeviceData *data;
386
387 list_.get_selection()->unselect_all();
388 selectedRow_ = list_.get_selection()->get_selected();
389
390 listModel_->clear();
391
392 for (drun = CdDevice::first(); drun != NULL; drun = CdDevice::next(drun)) {
393 appendTableEntry(drun);
394 }
395
396 if (listModel_->children().size() > 0) {
397 list_.columns_autosize();
398 list_.get_selection()->select(Gtk::TreeModel::Path((unsigned)1));
399 }
400 }
401
importConfiguration(Gtk::TreeIter row)402 void DeviceConfDialog::importConfiguration(Gtk::TreeIter row)
403 {
404 char buf[50];
405 DeviceData *data;
406
407 if (selectedRow_) {
408
409 data = (*selectedRow_)[listColumns_.data];
410 driverMenu_->set_sensitive(true);
411 driverMenu_->set_history(data->driverId);
412 devtypeMenu_->set_sensitive(true);
413 devtypeMenu_->set_history(data->deviceType);
414 driverOptionsEntry_.set_sensitive(true);
415 sprintf(buf, "0x%lx", data->options);
416 driverOptionsEntry_.set_text(buf);
417
418 } else {
419
420 driverMenu_->set_history(0);
421 driverMenu_->set_sensitive(false);
422 devtypeMenu_->set_history(0);
423 devtypeMenu_->set_sensitive(false);
424 driverOptionsEntry_.set_text("");
425 driverOptionsEntry_.set_sensitive(false);
426 }
427 }
428
importStatus()429 void DeviceConfDialog::importStatus()
430 {
431 DeviceData *data;
432 CdDevice *dev;
433
434 Gtk::TreeNodeChildren ch = listModel_->children();
435 for (unsigned i = 0; i < ch.size(); i++) {
436 Gtk::TreeRow row = ch[i];
437 data = row[listColumns_.data];
438 if (data && (dev = CdDevice::find(data->dev.c_str()))) {
439 row[listColumns_.status] = CdDevice::status2string(dev->status());
440 }
441 }
442
443 list_.columns_autosize();
444 }
445
exportConfiguration(Gtk::TreeIter row)446 void DeviceConfDialog::exportConfiguration(Gtk::TreeIter row)
447 {
448 DeviceData *data;
449
450 if (row) {
451 data = (*row)[listColumns_.data];
452
453 if (data) {
454 data->options = strtoul(driverOptionsEntry_.get_text().c_str(), NULL, 0);
455 }
456 }
457 }
458
exportData()459 void DeviceConfDialog::exportData()
460 {
461 DeviceData *data;
462 CdDevice *dev;
463 std::string s;
464
465 Gtk::TreeNodeChildren ch = listModel_->children();
466 for (unsigned i = 0; i < ch.size(); i++) {
467 Gtk::TreeRow row = ch[i];
468 data = row[listColumns_.data];
469 if (data && (dev = CdDevice::find(data->dev.c_str()))) {
470
471 if (dev->driverId() != data->driverId) {
472 dev->driverId(data->driverId);
473 dev->manuallyConfigured(true);
474 }
475
476 if (dev->deviceType() != ID2DEVICE_TYPE[data->deviceType]) {
477 dev->deviceType(ID2DEVICE_TYPE[data->deviceType]);
478 dev->manuallyConfigured(true);
479 }
480
481 if (dev->driverOptions() != data->options) {
482 dev->driverOptions(data->options);
483 dev->manuallyConfigured(true);
484 }
485 }
486 }
487 }
488
489
490
setDriverId(int id)491 void DeviceConfDialog::setDriverId(int id)
492 {
493 DeviceData *data;
494
495 if (selectedRow_ && id >= 0 && id <= CdDevice::maxDriverId()) {
496 data = (*selectedRow_)[listColumns_.data];
497 if (data)
498 data->driverId = id;
499 }
500 }
501
setDeviceType(int id)502 void DeviceConfDialog::setDeviceType(int id)
503 {
504 DeviceData *data;
505
506 if (selectedRow_ && id >= 0 && id <= CdDevice::maxDriverId()) {
507 data = (*selectedRow_)[listColumns_.data];
508 if (data)
509 data->deviceType = id;
510 }
511 }
512
selectionChanged()513 void DeviceConfDialog::selectionChanged()
514 {
515 Gtk::TreeIter new_sel = list_.get_selection()->get_selected();
516
517 if ((bool)selectedRow_ != (bool)new_sel || selectedRow_ != new_sel) {
518
519 if (selectedRow_)
520 exportConfiguration(selectedRow_);
521
522 selectedRow_ = new_sel;
523 importConfiguration(selectedRow_);
524 }
525 }
526
checkString(const std::string & str)527 const char *DeviceConfDialog::checkString(const std::string &str)
528 {
529 static char *buf = NULL;
530 static long bufLen = 0;
531 char *p, *s;
532 long len = strlen(str.c_str());
533
534 if (len == 0)
535 return NULL;
536
537 if (buf == NULL || len + 1 > bufLen) {
538 delete[] buf;
539 bufLen = len + 1;
540 buf = new char[bufLen];
541 }
542
543 strcpy(buf, str.c_str());
544
545 s = buf;
546 p = buf + len - 1;
547
548 while (*s != 0 && isspace(*s))
549 s++;
550
551 if (*s == 0)
552 return NULL;
553
554 while (p > s && isspace(*p)) {
555 *p = 0;
556 p--;
557 }
558
559 return s;
560 }
561