e409fdcd1448cbb517e4baea9c0f4d2808acdd17
[movie-schedule] / maincontroller.cpp
1 // Copyright 2010 Jochen Becher
2 //
3 // This file is part of MovieSchedule.
4 //
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.
9 //
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.
14 //
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/>.
17
18 #include "maincontroller.h"
19
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 "control/settingscontroller.h"
30 #include "ui/aboutdialog.h"
31 #include "ui/mainwindow.h"
32 #include "ui/theatermodel.h"
33 #include "ui/moviemodel.h"
34 #include "ui/uiutils.h"
35 #include "searchclients/theatersearchclient.h"
36 #include "searchclients/moviesearchclient.h"
37 #include "utils/assertedlocker.h"
38 #include "utils/asynccall.h"
39 #include "utils/connectivitymanager.h"
40
41 #include <QSortFilterProxyModel>
42 #include <iostream>
43
44 static const char *MSG_NETWORK_ERROR = QT_TRANSLATE_NOOP("MainController", "Unable to access network.");
45 static const char *MSG_NO_THEATERS_FOUND = QT_TRANSLATE_NOOP("MainController", "No theaters found near %1.");
46 static const char *MSG_NO_MOVIES_FOUND = QT_TRANSLATE_NOOP("MainController", "No movies found near %1.");
47 static const char *MSG_THEATERS_ERROR = QT_TRANSLATE_NOOP("MainController", "Error on fetching theaters.");
48 static const char *MSG_MOVIES_ERROR = QT_TRANSLATE_NOOP("MainController", "Error on fetching movies.");
49
50 MainController::MainController(MainWindow *main_window, Settings *settings, CinemaSchedule *cinema_schedule,
51                                TheaterController *theater_controller,
52                                MovieController *movie_controller,
53                                LocationController *location_controller,
54                                ActionController *action_controller,
55                                SettingsController *settings_controller,
56                                ItemModelSortController *sort_controller,
57                                QThread *search_worker)
58                                    : _main_window(main_window),
59                                    _settings(settings),
60                                    _cinema_schedule(cinema_schedule),
61                                    _theater_controller(theater_controller),
62                                    _movie_controller(movie_controller),
63                                    _location_controller(location_controller),
64                                    _action_controller(action_controller),
65                                    _settings_controller(settings_controller),
66                                    _sort_controller(sort_controller),
67                                    _search_worker(search_worker),
68                                    _connectivity_manager(new ConnectivityManager(this)),
69                                    _current_theater_search_task_id(TheaterSearchClient::INVALID_SEARCH_TASK_ID),
70                                    _current_movies_search_task_id(MovieSearchClient::INVALID_SEARCH_TASK_ID),
71                                    _theater_model(new TheaterModel(cinema_schedule, this)),
72                                    _movie_model(new MovieModel(cinema_schedule, this)),
73                                    _theater_proxy_model(new QSortFilterProxyModel(this)),
74                                    _movie_proxy_model(new QSortFilterProxyModel(this)),
75                                    _last_search_settings()
76 {
77     connect(_main_window, SIGNAL(SearchTheaters()), this, SLOT(SearchTheaters()));
78     connect(_main_window, SIGNAL(SearchMovies()), this, SLOT(SearchMovies()));
79     connect(_main_window, SIGNAL(TheaterSelected(CinemaKey)), _theater_controller, SLOT(ShowTheater(CinemaKey)));
80     connect(_main_window, SIGNAL(CallTheaterByPhone(CinemaKey)), _action_controller, SLOT(CallTheaterByPhone(CinemaKey)));
81     connect(_main_window, SIGNAL(FindRouteToTheater(CinemaKey)), _action_controller, SLOT(FindRouteToTheater(CinemaKey)));
82     connect(_main_window, SIGNAL(SearchTheaterInWeb(CinemaKey)), _action_controller, SLOT(SearchTheaterInWeb(CinemaKey)));
83     connect(_main_window, SIGNAL(MovieSelected(MovieKey)), _movie_controller, SLOT(ShowMovie(MovieKey)));
84     connect(_main_window, SIGNAL(SearchMovieInWeb(MovieKey)), _action_controller, SLOT(SearchMovieInWeb(MovieKey)));
85     connect(_main_window, SIGNAL(OpenLocationDialog()), _location_controller, SLOT(OpenLocationDialog()));
86     connect(_main_window, SIGNAL(OpenOptionsDialog()), _settings_controller, SLOT(OpenSettingsDialog()));
87     connect(_main_window, SIGNAL(OpenAboutDialog()), this, SLOT(OpenAboutDialog()));
88     connect(_settings_controller, SIGNAL(SettingsChanged(Settings)), this, SLOT(SettingsChanged(Settings)));
89     connect(_location_controller, SIGNAL(Search(Location)), this, SLOT(Search(Location)));
90     connect(_connectivity_manager, SIGNAL(Connected()), this, SLOT(NetworkConnected()));
91     connect(_connectivity_manager, SIGNAL(Disconnected()), this, SLOT(NetworkDisconnected()));
92     connect(_connectivity_manager, SIGNAL(Error()), this, SLOT(NetworkError()));
93     _main_window->SetOrientation(settings->GetOrientationMode());
94 }
95
96 MainController::~MainController()
97 {
98 }
99
100 void MainController::Run()
101 {
102     _connectivity_manager->Connect();
103 }
104
105 void MainController::Search()
106 {
107     _location_controller->CancelAllGpsSearchs();
108     // TODO add some timer condition? How old are the loaded information
109     if (_settings->GetLocation() != _last_search_settings.GetLocation()) {
110         CancelTheaterSearch();
111         CancelMovieSearch();
112         _movie_controller->Cancel();
113         _theater_controller->Cancel();
114         AssertedWriteLocker locker(_cinema_schedule->GetLock());
115         _cinema_schedule->Clear();
116     }
117     if (_settings->GetLocation().IsValid()) {
118         // Cancel searchs before _cinema_schedule is locked to avoid dead-locks
119         switch (_settings->GetSearchObjectsType()) {
120         case Settings::THEATERS:
121             CancelTheaterSearch();
122             break;
123         case Settings::MOVIES:
124             CancelMovieSearch();
125             break;
126         }
127         AssertedReadLocker locker(_cinema_schedule->GetLock());
128         _main_window->SetLocation(_settings->GetLocation());
129         switch (_settings->GetSearchObjectsType()) {
130         case Settings::THEATERS:
131             _main_window->SetTheaterModel(_theater_proxy_model);
132             if (!_cinema_schedule->AreAllCinemasLoaded()) {
133                 TheaterSearchClient *client = new TheaterSearchClient(_cinema_schedule);
134                 _current_theater_search_task_id = client->GetSearchTaskId();
135                 connect(client, SIGNAL(SearchStarted(int)), this, SLOT(TheatersSearchStarted(int)));
136                 connect(client, SIGNAL(Reply(int, bool)), this, SLOT(TheatersSearchReply(int, bool)));
137                 connect(client, SIGNAL(Error(int)), this, SLOT(TheatersSearchError(int)));
138                 connect(client, SIGNAL(SearchFinished(int, bool)), this, SLOT(TheatersSearchFinished(int, bool)));
139                 client->moveToThread(_search_worker);
140                 CallAsync(client, &TheaterSearchClient::SearchTheater, _settings->GetLocation().GetLocationName());
141             } else {
142                 TheatersSearchStarted(_current_theater_search_task_id);
143                 TheatersSearchReply(_current_theater_search_task_id, true);
144                 TheatersSearchFinished(_current_theater_search_task_id, true);
145             }
146             break;
147         case Settings::MOVIES:
148             _main_window->SetMovieModel(_movie_proxy_model);
149             if (!_cinema_schedule->AreAllMoviesLoaded()) {
150                 MovieSearchClient *client = new MovieSearchClient(_cinema_schedule);
151                 _current_movies_search_task_id = client->GetSearchTaskId();
152                 connect(client, SIGNAL(SearchStarted(int)), this, SLOT(MoviesSearchStarted(int)));
153                 connect(client, SIGNAL(Reply(int, bool)), this, SLOT(MoviesSearchReply(int, bool)));
154                 connect(client, SIGNAL(Error(int)), this, SLOT(MoviesSearchError(int)));
155                 connect(client, SIGNAL(SearchFinished(int, bool)), this, SLOT(MoviesSearchFinished(int, bool)));
156                 client->moveToThread(_search_worker);
157                 CallAsync(client, &MovieSearchClient::SearchMovie, _settings->GetLocation().GetLocationName());
158             } else {
159                 MoviesSearchStarted(_current_movies_search_task_id);
160                 MoviesSearchReply(_current_movies_search_task_id, true);
161                 MoviesSearchFinished(_current_movies_search_task_id, true);
162             }
163             break;
164         }
165         _last_search_settings = *_settings;
166     } else {
167         _location_controller->OpenLocationDialog();
168     }
169 }
170
171 void MainController::Search(Location location)
172 {
173     _settings->SetLocation(location);
174     Search();
175 }
176
177 void MainController::NetworkConnected()
178 {
179     Search();
180 }
181
182 void MainController::NetworkDisconnected()
183 {
184 }
185
186 void MainController::NetworkError()
187 {
188     _main_window->SetError(tr(MSG_NETWORK_ERROR));
189 }
190
191 void MainController::SettingsChanged(const Settings &settings)
192 {
193     _main_window->SetOrientation(settings.GetOrientationMode());
194 }
195
196 void MainController::SearchTheaters()
197 {
198     _settings->SetSearchObjectsType(Settings::THEATERS);
199     Search();
200 }
201
202 void MainController::SearchMovies()
203 {
204     _settings->SetSearchObjectsType(Settings::MOVIES);
205     Search();
206 }
207
208 void MainController::OpenAboutDialog()
209 {
210     AboutDialog *about_dialog = new AboutDialog(_main_window);
211     connect(about_dialog, SIGNAL(ContactAuthor()), _action_controller, SLOT(ContactAuthor()));
212     about_dialog->show();
213     // dialog deletes itself
214 }
215
216 void MainController::CancelTheaterSearch()
217 {
218     AssertedWriteLocker locker(_cinema_schedule->GetLock());
219     _current_theater_search_task_id = TheaterSearchClient::INVALID_SEARCH_TASK_ID;
220     TheaterSearchClient::CancelAllRunningSearchs();
221 }
222
223 void MainController::CancelMovieSearch()
224 {
225     AssertedWriteLocker locker(_cinema_schedule->GetLock());
226     _current_movies_search_task_id = MovieSearchClient::INVALID_SEARCH_TASK_ID;
227     MovieSearchClient::CancelAllRunningSearchs();
228 }
229
230 void MainController::TheatersSearchStarted(int search_task_id)
231 {
232     if (search_task_id != _current_theater_search_task_id) {
233         return;
234     }
235     _main_window->SetBusy(true);
236     SortTheaters(true, SLOT(TheatersSortFinished(QAbstractItemModel*,int,bool)));
237 }
238
239 void MainController::TheatersSearchReply(int search_task_id, bool intermediate)
240 {
241     if (search_task_id != _current_theater_search_task_id) {
242         return;
243     }
244     SortTheaters(intermediate, SLOT(TheatersSortFinished(QAbstractItemModel*,int,bool)));
245 }
246
247 void MainController::TheatersSearchError(int search_task_id)
248 {
249     if (search_task_id != _current_theater_search_task_id) {
250         return;
251     }
252     SortTheaters(false, SLOT(TheatersSortErrorFinished(QAbstractItemModel*,int,bool)));
253 }
254
255 void MainController::TheatersSearchFinished(int search_task_id, bool success)
256 {
257     Q_UNUSED(success);
258     if (search_task_id != _current_theater_search_task_id) {
259         return;
260     }
261     _main_window->SetBusy(false);
262 }
263
264 void MainController::SortTheaters(bool intermediate, const char *slot)
265 {
266     TheaterModel *theater_model = new TheaterModel(_cinema_schedule, this);
267     QSortFilterProxyModel *sort_model = new QSortFilterProxyModel(this);
268     sort_model->setSortCaseSensitivity(Qt::CaseInsensitive);
269     sort_model->setSortRole(TheaterModel::SortRole);
270     sort_model->setDynamicSortFilter(false);
271     sort_model->setSourceModel(theater_model);
272     ItemModelSortClient *sort_client = new ItemModelSortClient(_sort_controller, this);
273     connect(sort_client, SIGNAL(SortFinished(QAbstractItemModel*,int,bool)), this, slot);
274     sort_client->Sort(sort_model, _current_theater_search_task_id, intermediate);
275     // proxy deletes itself
276 }
277
278 void MainController::TheatersSortFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
279 {
280     if (search_task_id != _current_theater_search_task_id) {
281         return;
282     }
283     SetTheaterModel(model);
284     if (!intermediate) {
285         AssertedWriteLocker locker(_cinema_schedule->GetLock());
286         _cinema_schedule->SetAllCinemasLoaded(true);
287         if (_theater_model->rowCount() == 0) {
288             _main_window->SetError(tr(MSG_NO_THEATERS_FOUND).arg(_settings->GetLocation().GetLocationName()));
289         }
290     }
291 }
292
293 void MainController::TheatersSortErrorFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
294 {
295     Q_UNUSED(intermediate);
296     if (search_task_id != _current_theater_search_task_id) {
297         return;
298     }
299     SetTheaterModel(model);
300     if (_theater_model->rowCount() == 0) {
301         _main_window->SetError(tr(MSG_THEATERS_ERROR));
302     } else {
303         UiUtils::ShowError(tr(MSG_THEATERS_ERROR));
304     }
305 }
306
307 void MainController::SetTheaterModel(QAbstractItemModel *model)
308 {
309     delete _theater_proxy_model->sourceModel();
310     _theater_proxy_model->setSourceModel(model);
311     delete _theater_model;
312     _theater_model = (TheaterModel *) ((QSortFilterProxyModel *) model)->sourceModel();
313 }
314
315 void MainController::MoviesSearchStarted(int search_task_id)
316 {
317     if (search_task_id != _current_movies_search_task_id) {
318         return;
319     }
320     SortMovies(true, SLOT(MoviesSortFinished(QAbstractItemModel*,int,bool)));
321 }
322
323 void MainController::MoviesSearchReply(int search_task_id, bool intermediate)
324 {
325     if (search_task_id != _current_movies_search_task_id) {
326         return;
327     }
328     SortMovies(intermediate, SLOT(MoviesSortFinished(QAbstractItemModel*,int,bool)));
329 }
330
331 void MainController::MoviesSearchError(int search_task_id)
332 {
333     if (search_task_id != _current_movies_search_task_id) {
334         return;
335     }
336     SortMovies(false, SLOT(MoviesSortErrorFinished(QAbstractItemModel*,int,bool)));
337 }
338
339 void MainController::MoviesSearchFinished(int search_task_id, bool success)
340 {
341     Q_UNUSED(success);
342     if (search_task_id != _current_movies_search_task_id) {
343         return;
344     }
345     _main_window->SetBusy(false);
346 }
347
348 void MainController::SortMovies(bool intermediate, const char *slot)
349 {
350     MovieModel *movie_model = new MovieModel(_cinema_schedule, this);
351     QSortFilterProxyModel *sort_model = new QSortFilterProxyModel(this);
352     sort_model->setSortCaseSensitivity(Qt::CaseInsensitive);
353     sort_model->setSortRole(MovieModel::SortRole);
354     sort_model->setDynamicSortFilter(false);
355     sort_model->setSourceModel(movie_model);
356     ItemModelSortClient *sort_client = new ItemModelSortClient(_sort_controller, this);
357     connect(sort_client, SIGNAL(SortFinished(QAbstractItemModel*,int,bool)), this, slot);
358     sort_client->Sort(sort_model, _current_movies_search_task_id, intermediate);
359     // proxy deletes itself
360 }
361
362 void MainController::MoviesSortFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
363 {
364     if (search_task_id != _current_movies_search_task_id) {
365         return;
366     }
367     SetMovieModel(model);
368     if (!intermediate) {
369         AssertedWriteLocker locker(_cinema_schedule->GetLock());
370         _cinema_schedule->SetAllMoviesLoaded(true);
371         if (_movie_model->rowCount() == 0) {
372             _main_window->SetError(tr(MSG_NO_MOVIES_FOUND).arg(_settings->GetLocation().GetLocationName()));
373         }
374     }
375 }
376
377 void MainController::MoviesSortErrorFinished(QAbstractItemModel *model, int search_task_id, bool intermediate)
378 {
379     Q_UNUSED(intermediate);
380     if (search_task_id != _current_movies_search_task_id) {
381         return;
382     }
383     SetMovieModel(model);
384     if (_movie_model->rowCount() == 0) {
385         _main_window->SetError(tr(MSG_MOVIES_ERROR));
386     } else {
387         UiUtils::ShowError(tr(MSG_MOVIES_ERROR));
388     }
389 }
390
391 void MainController::SetMovieModel(QAbstractItemModel *model)
392 {
393     delete _movie_proxy_model->sourceModel();
394     _movie_proxy_model->setSourceModel(model);
395     delete _movie_model;
396     _movie_model = (MovieModel *) ((QSortFilterProxyModel *) model)->sourceModel();
397 }