// Copyright 2010 Jochen Becher
//
// This file is part of MovieSchedule.
//
// MovieSchedule is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// MovieSchedule is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with MovieSchedule. If not, see .
#include "maincontroller.h"
#include "data/settings.h"
#include "data/cinemaschedule.h"
#include "data/movie.h"
#include "data/cinema.h"
#include "control/theatercontroller.h"
#include "control/moviecontroller.h"
#include "control/actioncontroller.h"
#include "control/locationcontroller.h"
#include "control/itemmodelsortclient.h"
#include "control/settingscontroller.h"
#include "ui/aboutdialog.h"
#include "ui/mainwindow.h"
#include "ui/theatermodel.h"
#include "ui/moviemodel.h"
#include "ui/uiutils.h"
#include "searchclients/theatersearchclient.h"
#include "searchclients/moviesearchclient.h"
#include "utils/assertedlocker.h"
#include "utils/asynccall.h"
#include "utils/connectivitymanager.h"
#include
#include
static const char *MSG_NETWORK_ERROR = QT_TRANSLATE_NOOP("MainController", "Unable to access network.");
static const char *MSG_NO_THEATERS_FOUND = QT_TRANSLATE_NOOP("MainController", "No theaters found near %1.");
static const char *MSG_NO_MOVIES_FOUND = QT_TRANSLATE_NOOP("MainController", "No movies found near %1.");
static const char *MSG_THEATERS_ERROR = QT_TRANSLATE_NOOP("MainController", "Error on fetching theaters.");
static const char *MSG_MOVIES_ERROR = QT_TRANSLATE_NOOP("MainController", "Error on fetching movies.");
MainController::MainController(MainWindow *main_window, Settings *settings, CinemaSchedule *cinema_schedule,
TheaterController *theater_controller,
MovieController *movie_controller,
LocationController *location_controller,
ActionController *action_controller,
SettingsController *settings_controller,
ItemModelSortController *sort_controller,
QThread *search_worker)
: _main_window(main_window),
_settings(settings),
_cinema_schedule(cinema_schedule),
_theater_controller(theater_controller),
_movie_controller(movie_controller),
_location_controller(location_controller),
_action_controller(action_controller),
_settings_controller(settings_controller),
_sort_controller(sort_controller),
_search_worker(search_worker),
_connectivity_manager(new ConnectivityManager(this)),
_current_theater_search_task_id(TheaterSearchClient::INVALID_SEARCH_TASK_ID),
_current_movies_search_task_id(MovieSearchClient::INVALID_SEARCH_TASK_ID),
_theater_model(new TheaterModel(cinema_schedule, this)),
_movie_model(new MovieModel(cinema_schedule, this)),
_theater_proxy_model(new QSortFilterProxyModel(this)),
_movie_proxy_model(new QSortFilterProxyModel(this)),
_last_search_settings()
{
connect(_main_window, SIGNAL(SearchTheaters()), this, SLOT(SearchTheaters()));
connect(_main_window, SIGNAL(SearchMovies()), this, SLOT(SearchMovies()));
connect(_main_window, SIGNAL(TheaterSelected(CinemaKey)), _theater_controller, SLOT(ShowTheater(CinemaKey)));
connect(_main_window, SIGNAL(CallTheaterByPhone(CinemaKey)), _action_controller, SLOT(CallTheaterByPhone(CinemaKey)));
connect(_main_window, SIGNAL(FindRouteToTheater(CinemaKey)), _action_controller, SLOT(FindRouteToTheater(CinemaKey)));
connect(_main_window, SIGNAL(SearchTheaterInWeb(CinemaKey)), _action_controller, SLOT(SearchTheaterInWeb(CinemaKey)));
connect(_main_window, SIGNAL(MovieSelected(MovieKey)), _movie_controller, SLOT(ShowMovie(MovieKey)));
connect(_main_window, SIGNAL(SearchMovieInWeb(MovieKey)), _action_controller, SLOT(SearchMovieInWeb(MovieKey)));
connect(_main_window, SIGNAL(OpenLocationDialog()), _location_controller, SLOT(OpenLocationDialog()));
connect(_main_window, SIGNAL(OpenOptionsDialog()), _settings_controller, SLOT(OpenSettingsDialog()));
connect(_main_window, SIGNAL(OpenAboutDialog()), this, SLOT(OpenAboutDialog()));
connect(_settings_controller, SIGNAL(SettingsChanged(Settings)), this, SLOT(SettingsChanged(Settings)));
connect(_location_controller, SIGNAL(Search(Location)), this, SLOT(Search(Location)));
connect(_connectivity_manager, SIGNAL(Connected()), this, SLOT(NetworkConnected()));
connect(_connectivity_manager, SIGNAL(Disconnected()), this, SLOT(NetworkDisconnected()));
connect(_connectivity_manager, SIGNAL(Error()), this, SLOT(NetworkError()));
_main_window->SetOrientation(settings->GetOrientationMode());
}
MainController::~MainController()
{
}
void MainController::Run()
{
_connectivity_manager->Connect();
}
void MainController::Search()
{
_location_controller->CancelAllGpsSearchs();
// TODO add some timer condition? How old are the loaded information
if (_settings->GetLocation() != _last_search_settings.GetLocation()) {
CancelTheaterSearch();
CancelMovieSearch();
_movie_controller->Cancel();
_theater_controller->Cancel();
AssertedWriteLocker locker(_cinema_schedule->GetLock());
_cinema_schedule->Clear();
}
if (_settings->GetLocation().IsValid()) {
// Cancel searchs before _cinema_schedule is locked to avoid dead-locks
switch (_settings->GetSearchObjectsType()) {
case Settings::THEATERS:
CancelTheaterSearch();
break;
case Settings::MOVIES:
CancelMovieSearch();
break;
}
AssertedReadLocker locker(_cinema_schedule->GetLock());
_main_window->SetLocation(_settings->GetLocation());
switch (_settings->GetSearchObjectsType()) {
case Settings::THEATERS:
_main_window->SetTheaterModel(_theater_proxy_model);
if (!_cinema_schedule->AreAllCinemasLoaded()) {
TheaterSearchClient *client = new TheaterSearchClient(_cinema_schedule);
_current_theater_search_task_id = client->GetSearchTaskId();
connect(client, SIGNAL(SearchStarted(int)), this, SLOT(TheatersSearchStarted(int)));
connect(client, SIGNAL(Reply(int, bool)), this, SLOT(TheatersSearchReply(int, bool)));
connect(client, SIGNAL(Error(int)), this, SLOT(TheatersSearchError(int)));
connect(client, SIGNAL(SearchFinished(int, bool)), this, SLOT(TheatersSearchFinished(int, bool)));
client->moveToThread(_search_worker);
CallAsync(client, &TheaterSearchClient::SearchTheater, _settings->GetLocation().GetLocationName());
} else {
TheatersSearchStarted(_current_theater_search_task_id);
TheatersSearchReply(_current_theater_search_task_id, true);
TheatersSearchFinished(_current_theater_search_task_id, true);
}
break;
case Settings::MOVIES:
_main_window->SetMovieModel(_movie_proxy_model);
if (!_cinema_schedule->AreAllMoviesLoaded()) {
MovieSearchClient *client = new MovieSearchClient(_cinema_schedule);
_current_movies_search_task_id = client->GetSearchTaskId();
connect(client, SIGNAL(SearchStarted(int)), this, SLOT(MoviesSearchStarted(int)));
connect(client, SIGNAL(Reply(int, bool)), this, SLOT(MoviesSearchReply(int, bool)));
connect(client, SIGNAL(Error(int)), this, SLOT(MoviesSearchError(int)));
connect(client, SIGNAL(SearchFinished(int, bool)), this, SLOT(MoviesSearchFinished(int, bool)));
client->moveToThread(_search_worker);
CallAsync(client, &MovieSearchClient::SearchMovie, _settings->GetLocation().GetLocationName());
} else {
MoviesSearchStarted(_current_movies_search_task_id);
MoviesSearchReply(_current_movies_search_task_id, true);
MoviesSearchFinished(_current_movies_search_task_id, true);
}
break;
}
_last_search_settings = *_settings;
} else {
_location_controller->OpenLocationDialog();
}
}
void MainController::Search(Location location)
{
_settings->SetLocation(location);
Search();
}
void MainController::NetworkConnected()
{
Search();
}
void MainController::NetworkDisconnected()
{
}
void MainController::NetworkError()
{
_main_window->SetError(tr(MSG_NETWORK_ERROR));
}
void MainController::SettingsChanged(const Settings &settings)
{
_main_window->SetOrientation(settings.GetOrientationMode());
}
void MainController::SearchTheaters()
{
_settings->SetSearchObjectsType(Settings::THEATERS);
Search();
}
void MainController::SearchMovies()
{
_settings->SetSearchObjectsType(Settings::MOVIES);
Search();
}
void MainController::OpenAboutDialog()
{
AboutDialog *about_dialog = new AboutDialog(_main_window);
connect(about_dialog, SIGNAL(ContactAuthor()), _action_controller, SLOT(ContactAuthor()));
about_dialog->show();
// dialog deletes itself
}
void MainController::CancelTheaterSearch()
{
AssertedWriteLocker locker(_cinema_schedule->GetLock());
_current_theater_search_task_id = TheaterSearchClient::INVALID_SEARCH_TASK_ID;
TheaterSearchClient::CancelAllRunningSearchs();
}
void MainController::CancelMovieSearch()
{
AssertedWriteLocker locker(_cinema_schedule->GetLock());
_current_movies_search_task_id = MovieSearchClient::INVALID_SEARCH_TASK_ID;
MovieSearchClient::CancelAllRunningSearchs();
}
void MainController::TheatersSearchStarted(int search_task_id)
{
if (search_task_id != _current_theater_search_task_id) {
return;
}
_main_window->SetBusy(true);
SortTheaters(true, SLOT(TheatersSortFinished(QAbstractItemModel*,int,bool)));
}
void MainController::TheatersSearchReply(int search_task_id, bool intermediate)
{
if (search_task_id != _current_theater_search_task_id) {
return;
}
SortTheaters(intermediate, SLOT(TheatersSortFinished(QAbstractItemModel*,int,bool)));
}
void MainController::TheatersSearchError(int search_task_id)
{
if (search_task_id != _current_theater_search_task_id) {
return;
}
SortTheaters(false, SLOT(TheatersSortErrorFinished(QAbstractItemModel*,int,bool)));
}
void MainController::TheatersSearchFinished(int search_task_id, bool success)
{
Q_UNUSED(success);
if (search_task_id != _current_theater_search_task_id) {
return;
}
_main_window->SetBusy(false);
}
void MainController::SortTheaters(bool intermediate, const char *slot)
{
TheaterModel *theater_model = new TheaterModel(_cinema_schedule, this);
QSortFilterProxyModel *sort_model = new QSortFilterProxyModel(this);
sort_model->setSortCaseSensitivity(Qt::CaseInsensitive);
sort_model->setSortRole(TheaterModel::SortRole);
sort_model->setDynamicSortFilter(false);
sort_model->setSourceModel(theater_model);
ItemModelSortClient *sort_client = new ItemModelSortClient(_sort_controller, this);
connect(sort_client, SIGNAL(SortFinished(QAbstractItemModel*,int,bool)), this, slot);
sort_client->Sort(sort_model, _current_theater_search_task_id, intermediate);
// proxy deletes itself
}
void MainController::TheatersSortFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
{
if (search_task_id != _current_theater_search_task_id) {
return;
}
SetTheaterModel(model);
if (!intermediate) {
AssertedWriteLocker locker(_cinema_schedule->GetLock());
_cinema_schedule->SetAllCinemasLoaded(true);
if (_theater_model->rowCount() == 0) {
_main_window->SetError(tr(MSG_NO_THEATERS_FOUND).arg(_settings->GetLocation().GetLocationName()));
}
}
}
void MainController::TheatersSortErrorFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
{
Q_UNUSED(intermediate);
if (search_task_id != _current_theater_search_task_id) {
return;
}
SetTheaterModel(model);
if (_theater_model->rowCount() == 0) {
_main_window->SetError(tr(MSG_THEATERS_ERROR));
} else {
UiUtils::ShowError(tr(MSG_THEATERS_ERROR));
}
}
void MainController::SetTheaterModel(QAbstractItemModel *model)
{
delete _theater_proxy_model->sourceModel();
_theater_proxy_model->setSourceModel(model);
delete _theater_model;
_theater_model = (TheaterModel *) ((QSortFilterProxyModel *) model)->sourceModel();
}
void MainController::MoviesSearchStarted(int search_task_id)
{
if (search_task_id != _current_movies_search_task_id) {
return;
}
SortMovies(true, SLOT(MoviesSortFinished(QAbstractItemModel*,int,bool)));
}
void MainController::MoviesSearchReply(int search_task_id, bool intermediate)
{
if (search_task_id != _current_movies_search_task_id) {
return;
}
SortMovies(intermediate, SLOT(MoviesSortFinished(QAbstractItemModel*,int,bool)));
}
void MainController::MoviesSearchError(int search_task_id)
{
if (search_task_id != _current_movies_search_task_id) {
return;
}
SortMovies(false, SLOT(MoviesSortErrorFinished(QAbstractItemModel*,int,bool)));
}
void MainController::MoviesSearchFinished(int search_task_id, bool success)
{
Q_UNUSED(success);
if (search_task_id != _current_movies_search_task_id) {
return;
}
_main_window->SetBusy(false);
}
void MainController::SortMovies(bool intermediate, const char *slot)
{
MovieModel *movie_model = new MovieModel(_cinema_schedule, this);
QSortFilterProxyModel *sort_model = new QSortFilterProxyModel(this);
sort_model->setSortCaseSensitivity(Qt::CaseInsensitive);
sort_model->setSortRole(MovieModel::SortRole);
sort_model->setDynamicSortFilter(false);
sort_model->setSourceModel(movie_model);
ItemModelSortClient *sort_client = new ItemModelSortClient(_sort_controller, this);
connect(sort_client, SIGNAL(SortFinished(QAbstractItemModel*,int,bool)), this, slot);
sort_client->Sort(sort_model, _current_movies_search_task_id, intermediate);
// proxy deletes itself
}
void MainController::MoviesSortFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
{
if (search_task_id != _current_movies_search_task_id) {
return;
}
SetMovieModel(model);
if (!intermediate) {
AssertedWriteLocker locker(_cinema_schedule->GetLock());
_cinema_schedule->SetAllMoviesLoaded(true);
if (_movie_model->rowCount() == 0) {
_main_window->SetError(tr(MSG_NO_MOVIES_FOUND).arg(_settings->GetLocation().GetLocationName()));
}
}
}
void MainController::MoviesSortErrorFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
{
Q_UNUSED(intermediate);
if (search_task_id != _current_movies_search_task_id) {
return;
}
SetMovieModel(model);
if (_movie_model->rowCount() == 0) {
_main_window->SetError(tr(MSG_MOVIES_ERROR));
} else {
UiUtils::ShowError(tr(MSG_MOVIES_ERROR));
}
}
void MainController::SetMovieModel(QAbstractItemModel *model)
{
delete _movie_proxy_model->sourceModel();
_movie_proxy_model->setSourceModel(model);
delete _movie_model;
_movie_model = (MovieModel *) ((QSortFilterProxyModel *) model)->sourceModel();
}