1 // Copyright 2010 Jochen Becher
3 // This file is part of MovieSchedule.
5 // MovieSchedule 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.
10 // MovieSchedule 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.
15 // You should have received a copy of the GNU General Public License
16 // along with MovieSchedule. If not, see <http://www.gnu.org/licenses/>.
18 #include "maincontroller.h"
20 #include "data/settings.h"
21 #include "data/cinemaschedule.h"
22 #include "data/movie.h"
23 #include "data/cinema.h"
24 #include "control/theatercontroller.h"
25 #include "control/moviecontroller.h"
26 #include "control/actioncontroller.h"
27 #include "control/locationcontroller.h"
28 #include "control/itemmodelsortclient.h"
29 #include "ui/aboutdialog.h"
30 #include "ui/mainwindow.h"
31 #include "ui/theatermodel.h"
32 #include "ui/moviemodel.h"
33 #include "ui/uiutils.h"
34 #include "searchclients/theatersearchclient.h"
35 #include "searchclients/moviesearchclient.h"
36 #include "utils/assertedlocker.h"
37 #include "utils/asynccall.h"
38 #include "utils/connectivitymanager.h"
40 #include <QSortFilterProxyModel>
43 static const char *MSG_NETWORK_ERROR = QT_TRANSLATE_NOOP("MainController", "Unable to access network.");
44 static const char *MSG_NO_THEATERS_FOUND = QT_TRANSLATE_NOOP("MainController", "No theaters found near %1.");
45 static const char *MSG_NO_MOVIES_FOUND = QT_TRANSLATE_NOOP("MainController", "No movies found near %1.");
46 static const char *MSG_THEATERS_ERROR = QT_TRANSLATE_NOOP("MainController", "Error on fetching theaters.");
47 static const char *MSG_MOVIES_ERROR = QT_TRANSLATE_NOOP("MainController", "Error on fetching movies.");
49 MainController::MainController(MainWindow *main_window, Settings *settings, CinemaSchedule *cinema_schedule,
50 TheaterController *theater_controller,
51 MovieController *movie_controller,
52 LocationController *location_controller,
53 ActionController *action_controller,
54 ItemModelSortController *sort_controller,
55 QThread *search_worker)
56 : _main_window(main_window),
58 _cinema_schedule(cinema_schedule),
59 _theater_controller(theater_controller),
60 _movie_controller(movie_controller),
61 _location_controller(location_controller),
62 _action_controller(action_controller),
63 _sort_controller(sort_controller),
64 _search_worker(search_worker),
65 _connectivity_manager(new ConnectivityManager(this)),
66 _current_theater_search_task_id(TheaterSearchClient::INVALID_SEARCH_TASK_ID),
67 _current_movies_search_task_id(MovieSearchClient::INVALID_SEARCH_TASK_ID),
68 _theater_model(new TheaterModel(cinema_schedule, this)),
69 _movie_model(new MovieModel(cinema_schedule, this)),
70 _theater_proxy_model(new QSortFilterProxyModel(this)),
71 _movie_proxy_model(new QSortFilterProxyModel(this)),
72 _last_search_settings()
74 connect(_main_window, SIGNAL(SearchTheaters()), this, SLOT(SearchTheaters()));
75 connect(_main_window, SIGNAL(SearchMovies()), this, SLOT(SearchMovies()));
76 connect(_main_window, SIGNAL(TheaterSelected(CinemaKey)), _theater_controller, SLOT(ShowTheater(CinemaKey)));
77 connect(_main_window, SIGNAL(CallTheaterByPhone(CinemaKey)), _action_controller, SLOT(CallTheaterByPhone(CinemaKey)));
78 connect(_main_window, SIGNAL(FindRouteToTheater(CinemaKey)), _action_controller, SLOT(FindRouteToTheater(CinemaKey)));
79 connect(_main_window, SIGNAL(SearchTheaterInWeb(CinemaKey)), _action_controller, SLOT(SearchTheaterInWeb(CinemaKey)));
80 connect(_main_window, SIGNAL(MovieSelected(MovieKey)), _movie_controller, SLOT(ShowMovie(MovieKey)));
81 connect(_main_window, SIGNAL(SearchMovieInWeb(MovieKey)), _action_controller, SLOT(SearchMovieInWeb(MovieKey)));
82 connect(_main_window, SIGNAL(OpenLocationDialog()), _location_controller, SLOT(OpenLocationDialog()));
83 connect(_main_window, SIGNAL(OpenAboutDialog()), this, SLOT(OpenAboutDialog()));
84 connect(_location_controller, SIGNAL(Search(Location)), this, SLOT(Search(Location)));
85 connect(_connectivity_manager, SIGNAL(Connected()), this, SLOT(NetworkConnected()));
86 connect(_connectivity_manager, SIGNAL(Disconnected()), this, SLOT(NetworkDisconnected()));
87 connect(_connectivity_manager, SIGNAL(Error()), this, SLOT(NetworkError()));
90 MainController::~MainController()
94 void MainController::Run()
96 _connectivity_manager->Connect();
99 void MainController::Search()
101 _location_controller->CancelAllGpsSearchs();
102 // TODO add some timer condition? How old are the loaded information
103 if (_settings->GetLocation() != _last_search_settings.GetLocation()) {
104 CancelTheaterSearch();
106 _movie_controller->Cancel();
107 _theater_controller->Cancel();
108 AssertedWriteLocker locker(_cinema_schedule->GetLock());
109 _cinema_schedule->Clear();
111 if (_settings->GetLocation().IsValid()) {
112 // Cancel searchs before _cinema_schedule is locked to avoid dead-locks
113 switch (_settings->GetSearchObjectsType()) {
114 case Settings::THEATERS:
115 CancelTheaterSearch();
117 case Settings::MOVIES:
121 AssertedReadLocker locker(_cinema_schedule->GetLock());
122 _main_window->SetLocation(_settings->GetLocation());
123 switch (_settings->GetSearchObjectsType()) {
124 case Settings::THEATERS:
125 _main_window->SetTheaterModel(_theater_proxy_model);
126 if (!_cinema_schedule->AreAllCinemasLoaded()) {
127 TheaterSearchClient *client = new TheaterSearchClient(_cinema_schedule);
128 _current_theater_search_task_id = client->GetSearchTaskId();
129 connect(client, SIGNAL(SearchStarted(int)), this, SLOT(TheatersSearchStarted(int)));
130 connect(client, SIGNAL(Reply(int, bool)), this, SLOT(TheatersSearchReply(int, bool)));
131 connect(client, SIGNAL(Error(int)), this, SLOT(TheatersSearchError(int)));
132 connect(client, SIGNAL(SearchFinished(int, bool)), this, SLOT(TheatersSearchFinished(int, bool)));
133 client->moveToThread(_search_worker);
134 CallAsync(client, &TheaterSearchClient::SearchTheater, _settings->GetLocation().GetLocationName());
136 TheatersSearchStarted(_current_theater_search_task_id);
137 TheatersSearchReply(_current_theater_search_task_id, true);
138 TheatersSearchFinished(_current_theater_search_task_id, true);
141 case Settings::MOVIES:
142 _main_window->SetMovieModel(_movie_proxy_model);
143 if (!_cinema_schedule->AreAllMoviesLoaded()) {
144 MovieSearchClient *client = new MovieSearchClient(_cinema_schedule);
145 _current_movies_search_task_id = client->GetSearchTaskId();
146 connect(client, SIGNAL(SearchStarted(int)), this, SLOT(MoviesSearchStarted(int)));
147 connect(client, SIGNAL(Reply(int, bool)), this, SLOT(MoviesSearchReply(int, bool)));
148 connect(client, SIGNAL(Error(int)), this, SLOT(MoviesSearchError(int)));
149 connect(client, SIGNAL(SearchFinished(int, bool)), this, SLOT(MoviesSearchFinished(int, bool)));
150 client->moveToThread(_search_worker);
151 CallAsync(client, &MovieSearchClient::SearchMovie, _settings->GetLocation().GetLocationName());
153 MoviesSearchStarted(_current_movies_search_task_id);
154 MoviesSearchReply(_current_movies_search_task_id, true);
155 MoviesSearchFinished(_current_movies_search_task_id, true);
159 _last_search_settings = *_settings;
161 _location_controller->OpenLocationDialog();
165 void MainController::Search(Location location)
167 _settings->SetLocation(location);
171 void MainController::NetworkConnected()
176 void MainController::NetworkDisconnected()
180 void MainController::NetworkError()
182 _main_window->SetError(tr(MSG_NETWORK_ERROR));
185 void MainController::SearchTheaters()
187 _settings->SetSearchObjectsType(Settings::THEATERS);
191 void MainController::SearchMovies()
193 _settings->SetSearchObjectsType(Settings::MOVIES);
197 void MainController::OpenAboutDialog()
199 AboutDialog *about_dialog = new AboutDialog(_main_window);
200 connect(about_dialog, SIGNAL(ContactAuthor()), _action_controller, SLOT(ContactAuthor()));
201 about_dialog->show();
202 // dialog deletes itself
205 void MainController::CancelTheaterSearch()
207 AssertedWriteLocker locker(_cinema_schedule->GetLock());
208 _current_theater_search_task_id = TheaterSearchClient::INVALID_SEARCH_TASK_ID;
209 TheaterSearchClient::CancelAllRunningSearchs();
212 void MainController::CancelMovieSearch()
214 AssertedWriteLocker locker(_cinema_schedule->GetLock());
215 _current_movies_search_task_id = MovieSearchClient::INVALID_SEARCH_TASK_ID;
216 MovieSearchClient::CancelAllRunningSearchs();
219 void MainController::TheatersSearchStarted(int search_task_id)
221 if (search_task_id != _current_theater_search_task_id) {
224 _main_window->SetBusy(true);
225 SortTheaters(true, SLOT(TheatersSortFinished(QAbstractItemModel*,int,bool)));
228 void MainController::TheatersSearchReply(int search_task_id, bool intermediate)
230 if (search_task_id != _current_theater_search_task_id) {
233 SortTheaters(intermediate, SLOT(TheatersSortFinished(QAbstractItemModel*,int,bool)));
236 void MainController::TheatersSearchError(int search_task_id)
238 if (search_task_id != _current_theater_search_task_id) {
241 SortTheaters(false, SLOT(TheatersSortErrorFinished(QAbstractItemModel*,int,bool)));
244 void MainController::TheatersSearchFinished(int search_task_id, bool success)
247 if (search_task_id != _current_theater_search_task_id) {
250 _main_window->SetBusy(false);
253 void MainController::SortTheaters(bool intermediate, const char *slot)
255 TheaterModel *theater_model = new TheaterModel(_cinema_schedule, this);
256 QSortFilterProxyModel *sort_model = new QSortFilterProxyModel(this);
257 sort_model->setSortCaseSensitivity(Qt::CaseInsensitive);
258 sort_model->setSortRole(TheaterModel::SortRole);
259 sort_model->setDynamicSortFilter(false);
260 sort_model->setSourceModel(theater_model);
261 ItemModelSortClient *sort_client = new ItemModelSortClient(_sort_controller, this);
262 connect(sort_client, SIGNAL(SortFinished(QAbstractItemModel*,int,bool)), this, slot);
263 sort_client->Sort(sort_model, _current_theater_search_task_id, intermediate);
264 // proxy deletes itself
267 void MainController::TheatersSortFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
269 if (search_task_id != _current_theater_search_task_id) {
272 SetTheaterModel(model);
274 AssertedWriteLocker locker(_cinema_schedule->GetLock());
275 _cinema_schedule->SetAllCinemasLoaded(true);
276 if (_theater_model->rowCount() == 0) {
277 _main_window->SetError(tr(MSG_NO_THEATERS_FOUND).arg(_settings->GetLocation().GetLocationName()));
282 void MainController::TheatersSortErrorFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
284 Q_UNUSED(intermediate);
285 if (search_task_id != _current_theater_search_task_id) {
288 SetTheaterModel(model);
289 if (_theater_model->rowCount() == 0) {
290 _main_window->SetError(tr(MSG_THEATERS_ERROR));
292 UiUtils::ShowError(tr(MSG_THEATERS_ERROR));
296 void MainController::SetTheaterModel(QAbstractItemModel *model)
298 delete _theater_proxy_model->sourceModel();
299 _theater_proxy_model->setSourceModel(model);
300 delete _theater_model;
301 _theater_model = (TheaterModel *) ((QSortFilterProxyModel *) model)->sourceModel();
304 void MainController::MoviesSearchStarted(int search_task_id)
306 if (search_task_id != _current_movies_search_task_id) {
309 SortMovies(true, SLOT(MoviesSortFinished(QAbstractItemModel*,int,bool)));
312 void MainController::MoviesSearchReply(int search_task_id, bool intermediate)
314 if (search_task_id != _current_movies_search_task_id) {
317 SortMovies(intermediate, SLOT(MoviesSortFinished(QAbstractItemModel*,int,bool)));
320 void MainController::MoviesSearchError(int search_task_id)
322 if (search_task_id != _current_movies_search_task_id) {
325 SortMovies(false, SLOT(MoviesSortErrorFinished(QAbstractItemModel*,int,bool)));
328 void MainController::MoviesSearchFinished(int search_task_id, bool success)
331 if (search_task_id != _current_movies_search_task_id) {
334 _main_window->SetBusy(false);
337 void MainController::SortMovies(bool intermediate, const char *slot)
339 MovieModel *movie_model = new MovieModel(_cinema_schedule, this);
340 QSortFilterProxyModel *sort_model = new QSortFilterProxyModel(this);
341 sort_model->setSortCaseSensitivity(Qt::CaseInsensitive);
342 sort_model->setSortRole(MovieModel::SortRole);
343 sort_model->setDynamicSortFilter(false);
344 sort_model->setSourceModel(movie_model);
345 ItemModelSortClient *sort_client = new ItemModelSortClient(_sort_controller, this);
346 connect(sort_client, SIGNAL(SortFinished(QAbstractItemModel*,int,bool)), this, slot);
347 sort_client->Sort(sort_model, _current_movies_search_task_id, intermediate);
348 // proxy deletes itself
351 void MainController::MoviesSortFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
353 if (search_task_id != _current_movies_search_task_id) {
356 SetMovieModel(model);
358 AssertedWriteLocker locker(_cinema_schedule->GetLock());
359 _cinema_schedule->SetAllMoviesLoaded(true);
360 if (_movie_model->rowCount() == 0) {
361 _main_window->SetError(tr(MSG_NO_MOVIES_FOUND).arg(_settings->GetLocation().GetLocationName()));
366 void MainController::MoviesSortErrorFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
368 Q_UNUSED(intermediate);
369 if (search_task_id != _current_movies_search_task_id) {
372 SetMovieModel(model);
373 if (_movie_model->rowCount() == 0) {
374 _main_window->SetError(tr(MSG_MOVIES_ERROR));
376 UiUtils::ShowError(tr(MSG_MOVIES_ERROR));
380 void MainController::SetMovieModel(QAbstractItemModel *model)
382 delete _movie_proxy_model->sourceModel();
383 _movie_proxy_model->setSourceModel(model);
385 _movie_model = (MovieModel *) ((QSortFilterProxyModel *) model)->sourceModel();