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