2
* Copyright (C) 2009 Michael Lamothe
4
* This file is part of Me TV
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU Library General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
21
#include "scan_window.h"
22
#include "dvb_scanner.h"
24
#include "application.h"
25
#include "channels_conf_line.h"
28
ScanWindow* ScanWindow::create(Glib::RefPtr<Gnome::Glade::Xml> glade)
30
ScanWindow* scan_window = NULL;
31
glade->get_widget_derived("window_scan_wizard", scan_window);
35
Glib::ustring ScanWindow::get_initial_tuning_dir()
38
gboolean done = false;
41
StringSplitter splitter(SCAN_DIRECTORIES, ":", 100);
45
if (i >= splitter.get_count())
51
Glib::ustring scan_directory = splitter.get_value(i);
53
g_debug("Checking '%s'", scan_directory.c_str());
55
if (Gio::File::create_for_path(scan_directory)->query_exists())
58
result = scan_directory;
60
switch(frontend.get_frontend_info().type)
62
case FE_OFDM: result += "/dvb-t"; break;
63
case FE_QAM: result += "/dvb-c"; break;
64
case FE_QPSK: result += "/dvb-s"; break;
65
case FE_ATSC: result += "/atsc"; break;
66
default: throw Exception(_("Unknown frontend type"));
69
g_debug("Found '%s'", result.c_str());
79
bool compare_countries (const Country& a, const Country& b)
81
return a.name < b.name;
84
ScanWindow::ScanWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml) :
85
Gtk::Window(cobject), glade(glade_xml), frontend(get_application().get_device_manager().get_frontend())
89
notebook_scan_wizard = dynamic_cast<Gtk::Notebook*>(glade->get_widget("notebook_scan_wizard"));
90
label_scan_information = dynamic_cast<Gtk::Label*>(glade->get_widget("label_scan_information"));
91
glade->connect_clicked("button_scan_wizard_add", sigc::mem_fun(*this, &ScanWindow::on_button_scan_wizard_add_clicked));
92
glade->connect_clicked("button_scan_wizard_next", sigc::mem_fun(*this, &ScanWindow::on_button_scan_wizard_next_clicked));
93
glade->connect_clicked("button_scan_wizard_cancel", sigc::mem_fun(*this, &ScanWindow::on_button_scan_wizard_cancel_clicked));
95
notebook_scan_wizard->set_show_tabs(false);
97
Glib::ustring device_name = frontend.get_frontend_info().name;
98
dynamic_cast<Gtk::Label*>(glade->get_widget("label_scan_device"))->set_label(device_name);
100
progress_bar_scan = dynamic_cast<Gtk::ProgressBar*>(glade->get_widget("progress_bar_scan"));
101
tree_view_scanned_channels = dynamic_cast<Gtk::TreeView*>(glade->get_widget("tree_view_scanned_channels"));
103
list_store = Gtk::ListStore::create(columns);
104
tree_view_scanned_channels->set_model(list_store);
105
tree_view_scanned_channels->append_column(_("Service Name"), columns.column_name);
107
Glib::RefPtr<Gtk::TreeSelection> selection = tree_view_scanned_channels->get_selection();
108
selection->set_mode(Gtk::SELECTION_MULTIPLE);
110
combo_box_select_country = NULL;
111
glade->get_widget_derived("combo_box_select_country", combo_box_select_country);
113
combo_box_select_region = NULL;
114
glade->get_widget_derived("combo_box_select_region", combo_box_select_region);
116
scan_directory_path = get_initial_tuning_dir();
118
if (scan_directory_path.size() > 0)
120
Glib::RefPtr<Gio::File> scan_directory = Gio::File::create_for_path(scan_directory_path);
121
g_debug("Scanning directory: %s", scan_directory_path.c_str());
123
// This is a hack because I can't get scan_directory->enumerate_children() to work
124
GFileEnumerator* children = g_file_enumerate_children(scan_directory->gobj(),
125
"*", G_FILE_QUERY_INFO_NONE, NULL, NULL);
126
if (children != NULL)
128
GFileInfo* file_info = g_file_enumerator_next_file(children, NULL, NULL);
129
while (file_info != NULL)
131
Glib::ustring name = g_file_info_get_name(file_info);
132
if (name.substr(2,1) == "-")
134
Glib::ustring country_name = name.substr(0,2);
135
Glib::ustring region_name = name.substr(3);
136
Country& country = get_country(country_name);
137
country.regions.push_back(region_name);
139
file_info = g_file_enumerator_next_file(children, NULL, NULL);
143
countries.sort(compare_countries);
144
CountryList::iterator country_iterator = countries.begin();
145
while (country_iterator != countries.end())
147
Country& country = *country_iterator;
148
country.regions.sort();
149
combo_box_select_country->append_text(country.name);
152
combo_box_select_country->signal_changed().connect(sigc::mem_fun(*this, &ScanWindow::on_combo_box_select_country_changed));
153
combo_box_select_country->set_active(0);
158
ScanWindow::~ScanWindow()
163
void ScanWindow::on_show()
166
update_channel_count();
167
progress_bar_scan->set_fraction(0);
168
glade->get_widget("button_scan_wizard_add")->hide();
169
glade->get_widget("button_scan_wizard_next")->show();
170
notebook_scan_wizard->set_current_page(0);
174
void ScanWindow::on_hide()
180
void ScanWindow::on_combo_box_select_country_changed()
182
combo_box_select_region->clear_items();
183
Country& country = get_country(combo_box_select_country->get_active_text());
184
StringList::iterator region_iterator = country.regions.begin();
185
while (region_iterator != country.regions.end())
187
combo_box_select_region->append_text(*region_iterator);
190
combo_box_select_region->set_active(0);
193
void ScanWindow::stop_scan()
195
if (scan_thread != NULL)
197
g_debug("Stopping scan");
199
scan_thread->join(true);
205
void ScanWindow::on_button_scan_wizard_cancel_clicked()
210
void ScanWindow::import_channels_conf(const Glib::ustring& channels_conf_path)
212
Profile& current_profile = get_application().get_profile_manager().get_current_profile();
213
Glib::RefPtr<Glib::IOChannel> file = Glib::IOChannel::create_from_file(channels_conf_path, "r");
215
guint line_count = 0;
217
while (file->read_line(line) == Glib::IO_STATUS_NORMAL)
219
ChannelsConfLine channels_conf_line(line);
220
guint parameter_count = channels_conf_line.get_parameter_count();
224
g_debug("Line %d (%d parameters): '%s'", line_count, parameter_count, line.c_str());
226
if (parameter_count > 1)
228
switch(frontend.get_frontend_info().type)
231
if (parameter_count != 13)
233
Glib::ustring message = Glib::ustring::compose(_("Invalid parameter count on line %1"), line_count);
234
throw Exception(message);
240
channel.name = channels_conf_line.get_name(0);
241
channel.sort_order = line_count;
242
channel.flags = CHANNEL_FLAG_DVB_T;
244
channel.frontend_parameters.frequency = channels_conf_line.get_frequency(1);
245
channel.frontend_parameters.inversion = channels_conf_line.get_inversion(2);
246
channel.frontend_parameters.u.ofdm.bandwidth = channels_conf_line.get_bandwidth(3);
247
channel.frontend_parameters.u.ofdm.code_rate_HP = channels_conf_line.get_fec(4);
248
channel.frontend_parameters.u.ofdm.code_rate_LP = channels_conf_line.get_fec(5);
249
channel.frontend_parameters.u.ofdm.constellation = channels_conf_line.get_modulation(6);
250
channel.frontend_parameters.u.ofdm.transmission_mode = channels_conf_line.get_transmit_mode(7);
251
channel.frontend_parameters.u.ofdm.guard_interval = channels_conf_line.get_guard_interval(8);
252
channel.frontend_parameters.u.ofdm.hierarchy_information = channels_conf_line.get_hierarchy(9);
253
channel.service_id = channels_conf_line.get_service_id(12);
255
current_profile.add_channel(channel);
260
if (parameter_count != 9)
262
Glib::ustring message = Glib::ustring::compose(_("Invalid parameter count on line %1"), line_count);
263
throw Exception(message);
269
channel.name = channels_conf_line.get_name(0);
270
channel.sort_order = line_count;
271
channel.flags = CHANNEL_FLAG_DVB_C;
273
channel.frontend_parameters.frequency = channels_conf_line.get_frequency(1);
274
channel.frontend_parameters.inversion = channels_conf_line.get_inversion(2);
275
channel.frontend_parameters.u.qam.symbol_rate = channels_conf_line.get_symbol_rate(3);
276
channel.frontend_parameters.u.qam.fec_inner = channels_conf_line.get_fec(4);
277
channel.frontend_parameters.u.qam.modulation = channels_conf_line.get_modulation(5);
278
channel.service_id = channels_conf_line.get_service_id(8);
280
current_profile.add_channel(channel);
285
if (parameter_count != 6)
287
Glib::ustring message = Glib::ustring::compose(_("Invalid parameter count on line %1"), line_count);
288
throw Exception(message);
294
channel.name = channels_conf_line.get_name(0);
295
channel.sort_order = line_count;
296
channel.flags = CHANNEL_FLAG_ATSC;
298
channel.frontend_parameters.frequency = channels_conf_line.get_frequency(1);
299
channel.frontend_parameters.inversion = INVERSION_AUTO;
300
channel.frontend_parameters.u.vsb.modulation = channels_conf_line.get_modulation(2);
301
channel.service_id = channels_conf_line.get_service_id(5);
303
current_profile.add_channel(channel);
308
throw Exception(_("Failed to import: importing a channels.conf is only supported with DVB-T, DVB-C and ATSC"));
313
g_debug("Finished importing channels");
316
data.replace_profile(current_profile);
320
void ScanWindow::on_button_scan_wizard_next_clicked()
323
gboolean do_scan = true;
329
Glib::ustring initial_tuning_file;
331
Gtk::RadioButton* radio_button_scan_by_location = dynamic_cast<Gtk::RadioButton*>(glade->get_widget("radio_button_scan_by_location"));
332
Gtk::RadioButton* radio_button_scan_by_file = dynamic_cast<Gtk::RadioButton*>(glade->get_widget("radio_button_scan_by_file"));
333
Gtk::RadioButton* radio_button_scan_import_channels_conf = dynamic_cast<Gtk::RadioButton*>(glade->get_widget("radio_button_scan_import_channels_conf"));
335
if (radio_button_scan_by_location->get_active())
337
Glib::ustring country_name = combo_box_select_country->get_active_text();
338
Glib::ustring region_name = combo_box_select_region->get_active_text();
339
initial_tuning_file = scan_directory_path + "/" + country_name + "-" + region_name;
341
else if (radio_button_scan_by_file->get_active())
343
Gtk::FileChooserButton* file_chooser = dynamic_cast<Gtk::FileChooserButton*>(glade->get_widget("file_chooser_button_select_file_to_scan"));
344
initial_tuning_file = file_chooser->get_filename();
346
else if (radio_button_scan_import_channels_conf->get_active())
348
Gtk::FileChooserButton* file_chooser = dynamic_cast<Gtk::FileChooserButton*>(glade->get_widget("file_chooser_button_channels_conf"));
349
Glib::ustring channels_conf_path = file_chooser->get_filename();
350
import_channels_conf(channels_conf_path);
355
throw Exception(_("No scan/import option specified"));
360
switch(frontend.get_frontend_info().type)
366
throw Exception(_("Failed to scan: scanning is only supported for DVB-T and DVB-C devices"));
369
if (initial_tuning_file.empty())
371
Gtk::MessageDialog dialog(*this, _("No tuning file has been selected"));
376
glade->get_widget("button_scan_wizard_next")->hide();
377
notebook_scan_wizard->next_page();
379
g_debug("Initial tuning file: '%s'", initial_tuning_file.c_str());
380
scan_thread = new ScanThread(frontend, initial_tuning_file);
381
Dvb::Scanner& scanner = scan_thread->get_scanner();
382
scanner.signal_service.connect(sigc::mem_fun(*this, &ScanWindow::on_signal_service));
383
scanner.signal_progress.connect(sigc::mem_fun(*this, &ScanWindow::on_signal_progress));
384
get_application().stop_stream_thread();
385
scan_thread->start();
391
void ScanWindow::on_button_scan_wizard_add_clicked()
393
Profile& current_profile = get_application().get_profile_manager().get_current_profile();
395
std::list<Gtk::TreeModel::Path> selected_services = tree_view_scanned_channels->get_selection()->get_selected_rows();
396
std::list<Gtk::TreeModel::Path>::iterator iterator = selected_services.begin();
397
while (iterator != selected_services.end())
399
Gtk::TreeModel::Row row(*list_store->get_iter(*iterator));
403
channel.service_id = row.get_value(columns.column_id);
404
channel.name = row.get_value(columns.column_name);
405
channel.frontend_parameters = row.get_value(columns.column_frontend_parameters);
407
switch(frontend.get_frontend_info().type)
410
channel.flags = CHANNEL_FLAG_DVB_T;
414
channel.flags = CHANNEL_FLAG_DVB_C;
418
channel.flags = CHANNEL_FLAG_ATSC;
422
throw Exception(_("Invalid frontend type"));
426
current_profile.add_channel(channel);
432
data.replace_profile(current_profile);
436
void ScanWindow::on_signal_service(struct dvb_frontend_parameters& frontend_parameters, guint id, const Glib::ustring& name)
439
Gtk::TreeModel::iterator iterator = list_store->append();
440
Gtk::TreeModel::Row row = *iterator;
441
row[columns.column_id] = id;
442
row[columns.column_name] = name;
443
row[columns.column_frontend_parameters] = frontend_parameters;
444
tree_view_scanned_channels->get_selection()->select(row);
447
update_channel_count();
450
void ScanWindow::update_channel_count()
452
label_scan_information->set_text(Glib::ustring::compose(ngettext("Found 1 channel", "Found %1 channels", channel_count), channel_count));
455
void ScanWindow::on_signal_progress(guint step, gsize total)
458
gdouble fraction = total == 0 ? 0 : step/(gdouble)total;
459
progress_bar_scan->set_fraction(fraction);
462
glade->get_widget("button_scan_wizard_add")->show();
463
notebook_scan_wizard->next_page();
467
Country& ScanWindow::get_country(const Glib::ustring& country_name)
469
Country* result = find_country(country_name);
471
// If we don't have the country already then create one
475
country.name = country_name;
476
countries.push_back(country);
477
result = find_country(country_name);
483
Country* ScanWindow::find_country(const Glib::ustring& country_name)
485
Country* result = NULL;
487
CountryList::iterator country_iterator = countries.begin();
488
while (country_iterator != countries.end() && result == NULL)
490
Country& country = *country_iterator;
491
if (country.name == country_name)
493
result = &(*country_iterator);