ScummVM API documentation
cellphonepopup.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef NANCY_UI_CELLPHONEPOPUP_H
23 #define NANCY_UI_CELLPHONEPOPUP_H
24 
25 #include "engines/nancy/commontypes.h"
26 #include "engines/nancy/enginedata.h"
27 #include "engines/nancy/renderobject.h"
28 
29 namespace Nancy {
30 
31 struct NancyInput;
32 
33 namespace UI {
34 
35 // Nancy 10+ cell phone popup, driven by the UICL chunk. Handles dialling and
36 // calls, the contacts directory, the online hub (e-mail / web search / browser,
37 // or the Nancy 13 camera), the help page, and the Nancy 13 photo camera.
38 class CellPhonePopup : public RenderObject {
39 public:
41  ~CellPhonePopup() override = default;
42 
43  void init() override;
44  void registerGraphics() override;
45  void updateGraphics() override;
46  void handleInput(NancyInput &input);
47 
48  void open();
49  void close();
50  void toggle() { if (_isVisible) close(); else open(); }
51 
52  // True once a call has connected and the popup has scene-changed into the
53  // conversation. The phone is just decoration then and the conversation
54  // textbox is the active UI, so the cursor must not be confined to the phone.
55  bool isInCall() const { return _screenState == kConnected; }
56 
57  // Swaps the welcome graphic for the No Signal / No Access / Old Email
58  // Only labels and blocks outgoing calls.
59  void setNoSignal(bool noSignal);
60 
61  // Swaps the battery sprite for the low/dead variant.
62  void setBatteryLow(bool low);
63 
64  // Insert or replace a contact (matched by its 11-byte dial pattern).
65  // Used by AR 130 to add/modify entries at runtime.
66  void upsertContact(const UICL::Contact &c);
67 
68  // Append an entry to the email results list (mode 0) or the web
69  // bookmarks list (mode 1). Driven by AR 131 (AddSearchLink) and the
70  // UICL chunk's initial entries.
71  void addSearchLink(int16 mode, const SearchLink &link);
72 
73  // Phone-call return scene. Set before jumping into a conversation scene
74  // so AR 128 (CellPhonePopCellSceneFromStack) can return there without
75  // trampling the global push slot used by closeups/inventory views.
76  void setReturnScene(const SceneChangeDescription &scene);
77  bool consumeReturnScene(SceneChangeDescription &out);
78 
79  // Start an incoming-call sequence: opens the popup, stores the
80  // destination scene, and joins the kPlaceCall state chain so it
81  // rings, picks up, shows the connecting sprite, and changeScenes
82  // into `scene` (AR 128 returns via the setReturnScene slot).
83  void startIncomingCall(const SceneChangeDescription &scene);
84 
85  // Called by AR 128 when a call's conversation ends. An incoming call takes
86  // the phone down; a player-placed call leaves it open at the welcome screen
87  // (matching the original CCellPhonePopCellSceneFromStack).
88  void endCall();
89 
90 private:
91  enum ScreenState : int {
92  kWelcome = 0,
93  kDialing = 1,
94  kPlaceCall = 2,
95  kWaitOutgoingRing = 3,
96  kLookupContact = 4,
97  kWaitPickup = 5,
98  kConnected = 6,
99  kInvalidNumber = 7,
100  kWaitInvalid = 8,
101  kDirectory = 9,
102  kOnlineHub = 10, // Online heading + Email / Web sub-buttons
103  kWebList = 11, // web search-results list (AR-131 mode 1)
104  kEmailList = 12, // email message list (AR-131 mode 0)
105  kContentView = 13, // full-text view of a single email / page
106 
107  // Nancy 13 camera feature (the web browser was removed; Menu offers
108  // "view pictures" instead).
109  kCamera = 14, // framing the live viewport before a snapshot
110  kPictureView = 15, // reviewing a captured photo (Cam/Del/Send)
111  kDeleteConfirm = 16, // "DELETE? YES OR NO" over a photo
112  kMessageScreen = 17 // a transient message tile (SENT / DELETED / FULL)
113  };
114 
115  void drawChrome();
116  void drawScreenContent();
117  void drawStatusIcons(bool includeSignal = true);
118  void drawWebDirLabels();
119  void drawDialLabel();
120  // Blit one ribbon label sprite (Cam / Menu / Dir / Del / Send / Yes / No)
121  // from the sprite atlas at its chunk dest.
122  void drawRibbonLabel(const UICL::SrcDestRectPair &label);
123  void drawRibbonLabelAt(const Common::Rect &src, const Common::Rect &dest);
124  void drawTypeMessage();
125  void drawConnectedLabel();
126  void drawConnectingSprite();
127  void drawDialedNumber();
128  void drawHelpButton(uint state);
129  void drawCloseButton(bool hovered);
130  void drawStatusLabels();
131  void drawDirectoryList();
132  void drawDirectoryArrows();
133  // Blit one scroll/paging arrow (idle, or its pressed sprite when hovered).
134  void drawScrollArrow(const UICL::ThreeRectWidget &arrow, bool hovered);
135  void drawWelcomeScreen();
136  // Nancy 13: blit one UI_Cell_Xtra atlas tile into the LCD area (the plain
137  // keyboard background or one of the message tiles). These are fixed atlas
138  // positions the original bakes into a pre-rendered per-state LCD surface.
139  void drawLcdTile(const Common::Rect &src);
140  // Show a transient message tile (Picture Sent / Deleted / Camera Full),
141  // dismissed by any click back to returnState.
142  void showMessageScreen(const Common::Rect &tileSrc, ScreenState returnState);
143  // Nancy 13: draw the current captured photo (scaled into the LCD) for the
144  // "view pictures" screen, or the "no pictures" tile when there are none.
145  void drawPictureView();
146  // The captured pictures store, lazily created (Nancy 13 only). May be null.
147  struct CellPhonePictureData *pictureData() const;
148  // Nancy 13 camera: grab the given screen-space region of the live viewport
149  // into a new persisted CapturedPicture and select it. Empty rect = the whole
150  // viewport.
151  void captureViewport(const Common::Rect &screenRegion = Common::Rect());
152 
153  // Nancy 13 camera framing: while kCamera is active the popup covers the
154  // viewport and draws a movable rectangle marking the shot. Entering saves the
155  // phone's rect and grows the draw surface; exiting restores it.
156  void enterCameraFraming();
157  void exitCameraFraming();
158  void drawCameraFraming();
159  // Screen-space rect of the framing box, centred on the mouse and clamped to
160  // the viewport.
161  Common::Rect framingScreenRect() const;
162  // Blit a sub-button's idle sprite at its chunk dest (used for the visible
163  // Back buttons: subButtons[0] on the help / directory / online screens,
164  // subButtons[7] in the zoomed email / browser content view).
165  void drawBackButton(uint subButtonIndex);
166  // Blit an online-hub option button (mail / browser), using its highlighted
167  // sprite when the cursor is over it.
168  void drawHubButton(uint subButtonIndex);
169  // Blit the lit key sprite of the currently held dial-pad slot over its
170  // dest rect, so keypad keys visually depress while pressed.
171  void drawPressedDialKey();
172 
173  // Generic list renderer used by web / email modes.
174  void drawLinkList();
175  // Blit a heading sprite (e.g. emailHeading) at its chunk dest. The
176  // heading sits in the title-bar strip above the LCD content.
177  void drawHeading(const UICL::SrcDestRectPair &heading);
178  // Render the opened entry's body text in the LCD area, word-wrapped.
179  void drawContentView();
180  // Expensive: render the current content page's hypertext into the cache
181  // surface (+ text height, image/link hotspots). Called by drawContentView
182  // only when the page key changes.
183  void renderContentPage(int surfaceWidth);
184  // The LCD area the content view renders into: the small screen for the help
185  // page, the taller zoomed-in one for browser / email articles.
186  const Common::Rect &contentViewScreenRect() const;
187  // Per-click scroll amount (pixels) for the article/help content view.
188  uint contentScrollStep() const;
189  // Enter the content view for a list entry whose AUTOTEXT key is `key`.
190  void openContentView(const Common::String &key, const UICL::SrcDestRectPair &heading);
191  // Web button: open the first url entry as the browser home page (page 0).
192  void openBrowserHome();
193 
194  // Up/down scroll buttons differ by mode: subButtons[1]/[2] for the
195  // directory's narrower list area, [5]/[6] for the taller search /
196  // email / browser-content LCD area.
197  const UICL::ThreeRectWidget &scrollUpButton() const;
198  const UICL::ThreeRectWidget &scrollDownButton() const;
199 
200  // True when the current screen uses the zoomed-in (no-keypad)
201  // chrome variant: search list, email list, and content view.
202  bool isZoomedChromeState() const {
203  return _screenState == kWebList ||
204  _screenState == kEmailList ||
205  _screenState == kContentView;
206  }
207 
208  // The help "?" page reuses the content-view state, but unlike browser /
209  // email articles the original renders it in the regular (keypad-visible)
210  // chrome and the small LCD, not the zoomed full-screen variant.
211  bool isHelpContentView() const {
212  return _screenState == kContentView && _uiclData &&
213  _contentHeading == &_uiclData->helpHeading;
214  }
215 
216  // True for screens that hide the status icons and "?" button so the
217  // top bar shows only the section heading and the up arrow.
218  bool isSubScreenState() const {
219  return _screenState == kDirectory ||
220  _screenState == kOnlineHub ||
221  isZoomedChromeState();
222  }
223 
224  void resetDialPad();
225  void enterScreenState(ScreenState newState);
226  // With no signal the phone locks to "Old Email Only": on the welcome,
227  // dialing and online-hub screens every keypad key is dead except Menu
228  // (slot 13), which still reaches the e-mail list. Digits, *, #, Talk, Dir
229  // and the Help "?" go inert. The directory keeps its keys so the reachable
230  // e-mail path still works.
231  bool isDialKeyActive(uint slot) const;
232  // True while a player-placed call is ringing / waiting for pickup, so the
233  // connecting strip shows a Back button (subButtons[0]) that cancels it.
234  // Incoming calls have no Back button.
235  bool isCallBackButtonActive() const {
236  return (_screenState == kPlaceCall || _screenState == kWaitOutgoingRing ||
237  _screenState == kLookupContact || _screenState == kWaitPickup) &&
238  !_hasPendingCallScene && _uiclData &&
239  !_uiclData->subButtons[0].destRect.isEmpty();
240  }
241  // Cancel a ringing / waiting call and return to the welcome screen.
242  void cancelCall();
243  void appendDigit(byte slotIndex);
244  // Play a dial-pad key's DTMF tone. The name is a raw sound filename, so it
245  // is played through the phone's call-sound channel rather than the common
246  // (boot-registered) sound table.
247  void playDialPadSound(const Common::String &name);
248  // Play a popup button's click sound (the close X), like the inventory
249  // popup. Falls back to the shared button-click slot in the popup header.
250  void playButtonClickSound(const UIButtonRecord &button);
251  bool playSoundIfPresent(const Common::Path &soundName);
252  bool callSoundIsStillPlaying() const;
253  void triggerContactCallSceneChange(uint contactIndex);
254  int findContactByDialBuffer() const;
255 
256  uint maxDirectoryRows() const;
257  uint directoryRowAt(const Common::Point &chunkMouse) const;
258  Common::Rect directoryRowRect(uint visibleIndex) const;
259  // Row pitch and first-row Y (screen coords) from the layout data.
260  int rowPitch() const;
261  int rowTopScreen() const;
262  bool isLinkListMode() const { return _screenState == kWebList || _screenState == kEmailList; }
263  bool isOnlineMode() const { return _screenState == kOnlineHub || isLinkListMode(); }
264 
265  // Section headings live in the phone's title-bar strip above the LCD,
266  // so they don't consume a list row. Kept as a hook in case a game
267  // needs an in-LCD title row.
268  uint listTitleRows() const { return 0; }
269 
270  // Layout for the two clickable labels on the Online hub.
271  Common::Rect hubEmailRect() const;
272  Common::Rect hubWebRect() const;
273  void startCallToContact(uint contactIndex);
274  // Visible (deduplicated) row -> raw contact index, or -1.
275  int contactIndexForVisibleRow(uint visibleRow) const;
276  uint deduplicatedContactCount() const;
277  // Entry count for whichever list the popup is currently showing.
278  uint currentListEntryCount() const;
279  // Absolute indices into the current list's backing array that pass
280  // the active filter. Email applies the "Old Email Only" filter
281  // (no-signal -> read messages only); web shows everything.
282  Common::Array<uint> listVisibleIndices() const;
283  // True when the contact's visibility flag is currently unlocked.
284  bool isContactVisible(const UICL::Contact &c) const;
285  // Popup-local rect of the Back hotspot in directory mode.
286  Common::Rect backLabelHitRect() const;
287  // Popup-local rect of a visible Back sub-button (subButtons[index]).
288  Common::Rect backButtonHitRect(uint subButtonIndex) const;
289  // subButtons index of the Back / HOME button visible in the current state,
290  // or -1 when none is shown. Used to drive its hover highlight.
291  int currentBackButtonIndex() const;
292  // subButtons index of the bottom button on a content view.
293  uint contentViewBottomButton() const;
294  // A browser content view other than the main page (i.e. an actual web page).
295  bool isBrowserArticle() const;
296  // Move the directory selection by delta, scrolling as needed.
297  void moveDirectorySelection(int delta);
298 
299  Common::Point mouseToChunkCoords(const Common::Point &mouse) const;
300 
301  const UICL *_uiclData;
302 
303  // Runtime contact list, seeded from _uiclData->contacts and then
304  // mutable (AR 130 inserts/replaces entries).
306 
307  // Chrome (header.imageName) and sprite atlas (overlayImageName).
308  Graphics::ManagedSurface _overlayImage;
309  Graphics::ManagedSurface _spritesImage;
310 
311  bool _closeButtonHovered = false;
312  bool _scrollUpHovered = false;
313  bool _scrollDownHovered = false;
314  // Green-arrow highlight state for the captioned "> HELP" (welcome / dialing)
315  // and "< BACK" / HOME sub-buttons: each swaps to its pressed sprite on hover.
316  bool _helpButtonHovered = false;
317  bool _backButtonHovered = false;
318 
319  ScreenState _screenState = kWelcome;
320 
321  // Dialed digits as '0'..'9' chars; convert with `c - '0'` to get
322  // the slot index that matches a contact's dial prefix.
323  Common::String _dialedNumber;
324 
325  SoundDescription _callSound;
326 
327  // Resolved during kLookupContact, valid through kWaitPickup; -1 = miss.
328  int _resolvedContact = -1;
329 
330  int _hoveredSlot = -1;
331 
332  // Dial-pad slot currently held down (shows the lit / depressed key), or -1.
333  int _pressedSlot = -1;
334 
335  // Online-hub option button under the cursor (subButtons index 3 = mail,
336  // 4 = browser), drawn with its highlighted sprite; -1 = none.
337  int _hoveredHubButton = -1;
338 
339  // A call queued by auto-dial / Talk, waiting for the key's DTMF tone to
340  // finish before entering kPlaceCall (see updateGraphics).
341  bool _autoDialPending = false;
342 
343  // First visible deduplicated contact, and the active row within the page.
344  uint _directoryScroll = 0;
345  uint _directorySelection = 0;
346 
347  // Nancy 13 "view pictures": index of the currently displayed captured photo.
348  int _pictureIndex = 0;
349 
350  // Nancy 13 camera framing state.
351  bool _inCameraFraming = false;
352  Common::Rect _savedPhoneRect; // phone rect to restore when framing ends
353  Common::Point _framingMouse; // last mouse pos (screen coords)
354  static const int kFramingWidth = 220;
355  static const int kFramingHeight = 176;
356 
357  // The original caps the persisted camera roll at 50 pictures.
358  static const uint kMaxPictures = 50;
359 
360  // True while the directory is open to pick a recipient for the current photo
361  // (reached from the picture-review "Send" button).
362  bool _sendingPicture = false;
363 
364  // Active tile + return target for the transient kMessageScreen state.
365  const Common::Rect *_messageTileSrc = nullptr;
366  ScreenState _messageReturnState = kWelcome;
367 
368  // Content-view (single email / page) state.
369  ScreenState _contentReturnState = kOnlineHub;
370  const UICL::SrcDestRectPair *_contentHeading = nullptr;
371  Common::String _contentKey;
372  // Uppercased key of the main browser page (empty until the browser is opened).
373  Common::String _browserHomeKey;
374  uint _contentScroll = 0;
375 
376  // Email "opening" transition: the visible row whose closed envelope briefly
377  // flashes open before the body is shown (-1 = none), the deadline for that
378  // transition, and the body CVTX key to open when it fires.
379  int _openingEmailRow = -1;
380  uint32 _openingEmailTime = 0;
381  Common::String _openingEmailKey;
382 
383  // In-page hyperlinks: rects (popup-local, recomputed every draw) and
384  // the target CVTX key parsed from each <H>...<L> region of the body.
385  Common::Array<Common::Rect> _contentHotspots;
386  Common::Array<Common::String> _contentHotspotTargets;
387 
388  // Cached render of the current content page. Rendering the hypertext is
389  // expensive, so it's only rebuilt when the page key changes — scrolling
390  // and hover redraws just re-blit a window of the cached surface (fixes the
391  // cursor stutter while hovering the scroll arrows).
392  Graphics::ManagedSurface _contentCacheSurface;
393  Common::String _contentCacheKey;
394  uint16 _contentCacheTextHeight = 0;
395  Common::Array<Common::Rect> _contentCacheHotspots;
396  Common::Array<Common::String> _contentCacheTargets;
397 
398  bool _noSignal = false;
399  bool _batteryLow = false;
400 
401  // Incoming-call destination (set by startIncomingCall, consumed by the
402  // kConnected handler once the player has answered).
403  SceneChangeDescription _pendingCallScene;
404  bool _hasPendingCallScene = false;
405 
406  // True while the in-progress / just-finished call was incoming (auto-rung),
407  // so AR 128 knows to close the phone afterwards; player-placed calls leave
408  // it open. Persists past _hasPendingCallScene, which is consumed on connect.
409  bool _callWasIncoming = false;
410 
411  SceneChangeDescription _returnScene;
412  bool _hasReturnScene = false;
413 };
414 
415 } // End of namespace UI
416 } // End of namespace Nancy
417 
418 #endif // NANCY_UI_CELLPHONEPOPUP_H
Definition: managed_surface.h:51
Definition: str.h:59
Definition: commontypes.h:165
Definition: enginedata.h:588
Definition: enginedata.h:601
Definition: rect.h:536
Definition: path.h:52
Definition: enginedata.h:595
Definition: input.h:41
Definition: soundequalizerpuzzle.h:27
Definition: commontypes.h:400
Definition: renderobject.h:36
Definition: puzzledata.h:267
bool isEmpty() const
Definition: rect.h:385
Definition: rect.h:144
Definition: cellphonepopup.h:38
Definition: enginedata.h:615
Definition: commontypes.h:282
Definition: actionmanager.h:32