Fixed searchclients to handle new Google URLs correctly; added GUI
[movie-schedule] / src / control / actioncontroller.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 "actioncontroller.h"
19
20 #include "data/cinemaschedule.h"
21 #include "data/scheduleentry.h"
22 #include "data/cinema.h"
23 #include "data/movie.h"
24 #include "ui/uiutils.h"
25 #include "utils/calendar.h"
26 #include "utils/timeutils.h"
27 #include "utils/assertedlocker.h"
28
29 #include <QtDBus>
30 #include <QUrl>
31 #include <QDesktopServices>
32 #include <iostream>
33
34 ActionController::ActionController(const CinemaSchedule *cinema_schedule) :
35         QObject(0),
36         _cinema_schedule(cinema_schedule)
37 {
38 }
39
40 void ActionController::AddToCalendar(ScheduleEntryKey schedule_entry_key)
41 {
42 #ifdef MAEMO_SDK
43     ScheduleEntry schedule_entry;
44     Movie movie;
45     Cinema cinema;
46     {
47         AssertedReadLocker locker(_cinema_schedule->GetLock());
48         schedule_entry = _cinema_schedule->FindScheduleEntry(schedule_entry_key);
49         if (schedule_entry.IsValid()) {
50             movie = *schedule_entry.GetMovie();
51             cinema = *schedule_entry.GetCinema();
52         }
53     }
54     if (schedule_entry.IsValid()) {
55         Calendar calendar(this);
56         QDateTime start_time;
57         start_time.setDate(schedule_entry.GetDate());
58         start_time.setTime(schedule_entry.GetStartTime());
59         int minutes = movie.GetDuration().hour() * 60
60                       + movie.GetDuration().minute();
61         minutes = ((minutes + 14) / 15) * 15;
62         QDateTime end_time = start_time.addSecs(minutes * 60);
63         QString location = cinema.GetName();
64         QString description;
65         if (!cinema.GetAddress().isEmpty()) {
66             if (!location.isEmpty()) {
67                 location += ", ";
68             }
69             location += cinema.GetAddress();
70         }
71         if (!cinema.GetTelephone().isEmpty()) {
72             if (!description.isEmpty()) {
73                 description += "\n";
74             }
75             description += cinema.GetTelephone();
76         }
77         if (!calendar.AddEvent(movie.GetName(), start_time, end_time,
78                                location, description)) {
79             ShowError(tr("Adding calendar event failed."));
80             return;
81         }
82         UiUtils::ShowInformation(
83                 tr("%1 (%2 %3) added to your calendar.")
84                 .arg(movie.GetName())
85                 .arg(TimeUtils::ToDateString(schedule_entry.GetDate()))
86                 .arg(TimeUtils::ToTimeString(schedule_entry.GetStartTime())));
87     } else {
88         ShowError(tr("Adding calendar event failed."));
89     }
90 #else
91     Q_UNUSED(schedule_entry_key);
92     ShowError(tr("Adding calendar event failed."));
93 #endif
94 }
95
96 void ActionController::CallTheaterByPhone(CinemaKey cinema_key)
97 {
98     Cinema cinema;
99     {
100         AssertedReadLocker locker(_cinema_schedule->GetLock());
101         const Cinema *cinema_p = _cinema_schedule->FindCinema(cinema_key);
102         if (cinema_p != 0) {
103             cinema = *cinema_p;
104         }
105     }
106     if (cinema.IsValid()) {
107         if (!cinema.GetTelephone().isEmpty()) {
108             if (!QDBusConnection::systemBus().isConnected()) {
109                 ShowError(tr("Unable to call theater by phone: communication bus not available."));
110                 return;
111             }
112             QDBusInterface iface("com.nokia.csd.Call", "/com/nokia/csd/call", "com.nokia.csd.Call", QDBusConnection::systemBus());
113             if (!iface.isValid()) {
114                 ShowError(tr("Unable to call theater by phone: phone service not available."));
115                 return;
116             }
117             QString phone_number = TrimPhoneNumber(cinema.GetTelephone());
118             QDBusReply<QDBusObjectPath> reply = iface.call("CreateWith", phone_number, 0);
119             if (!reply.isValid()) {
120                 std::cout << qPrintable(reply.error().message()) << ": <" << qPrintable(phone_number) << ">" << std::endl;
121                 ShowError(tr("Unable to call theater by phone: call failed."));
122                 return;
123             }
124         }
125     }
126 }
127
128 void ActionController::FindRouteToTheater(CinemaKey cinema_key)
129 {
130     Cinema cinema;
131     {
132         AssertedReadLocker locker(_cinema_schedule->GetLock());
133         const Cinema *cinema_p = _cinema_schedule->FindCinema(cinema_key);
134         if (cinema_p != 0) {
135             cinema = *cinema_p;
136         }
137     }
138     if (cinema.IsValid()) {
139         // TODO implement find route to theater
140     }
141 }
142
143 void ActionController::SearchTheaterInWeb(CinemaKey cinema_key)
144 {
145     QString key;
146     {
147         AssertedReadLocker locker(_cinema_schedule->GetLock());
148         const Cinema *cinema = _cinema_schedule->FindCinema(cinema_key);
149         if (cinema != 0) {
150             key = cinema->GetName() + " " + cinema->GetAddress();
151         }
152     }
153     if (!key.isEmpty()) {
154         SearchInWeb(key);
155     }
156 }
157
158 void ActionController::SearchMovieInWeb(MovieKey movie_key)
159 {
160     QString key;
161     {
162         AssertedReadLocker locker(_cinema_schedule->GetLock());
163         const Movie *movie = _cinema_schedule->FindMovie(movie_key);
164         if (movie != 0) {
165             key = movie->GetName();
166         }
167     }
168     if (!key.isEmpty()) {
169         SearchInWeb(key);
170     }
171 }
172
173 void ActionController::ContactAuthor()
174 {
175     QUrl url("mailto::Jochen Becher <j.becher@ovi.com>", QUrl::TolerantMode);
176     url.addQueryItem("subject", "Application 'MovieSchedule' for Nokia N900");
177     QDesktopServices::openUrl(url);
178 }
179
180 QString ActionController::TrimPhoneNumber(const QString &phone_number)
181 {
182     QString trimmed;
183     bool plus_is_valid = true;
184     foreach (QChar ch, phone_number) {
185         switch (ch.toAscii()) {
186         case ',':
187         case '.':
188         case '(':
189         case ')':
190         case '-':
191         case ' ':
192         case '\t':
193         case '/':
194             // trim character
195             break;
196         case 'p':
197         case 'w':
198         case 'x':
199             trimmed += ch.toUpper();
200             break;
201         case '+':
202             if (plus_is_valid) {
203                 trimmed += ch;
204             }
205             break;
206         default:
207             trimmed += ch;
208         }
209         // TODO plus_is_valid stays true as long as we are parsing a number suppression prefix (e.g. *31#)
210         plus_is_valid = false;
211     }
212     return trimmed;
213 }
214
215 void ActionController::SearchInWeb(const QString &key)
216 {
217     QUrl url("http://www.google.com/search");
218     url.addQueryItem("q", key);
219     if (!QDesktopServices::openUrl(url)) {
220         ShowError(tr("Unable to search in web: browser service not available."));
221         return;
222     }
223 }
224
225 void ActionController::ShowError(const QString &msg)
226 {
227     UiUtils::ShowError(msg);
228 }