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_TASKBAR_CELLPHONEPOPUP_H
23 #define NANCY_UI_TASKBAR_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  Common::Rect zoomedChromeSrc() const;
116  void drawChrome();
117  void drawScreenContent();
118  void drawStatusIcons(bool includeSignal = true);
119  void drawWebDirLabels();
120  void drawDialLabel();
121  // Blit one ribbon label sprite (Cam / Menu / Dir / Del / Send / Yes / No)
122  // from the sprite atlas at its chunk dest.
123  void drawRibbonLabel(const UICL::SrcDestRectPair &label);
124  void drawRibbonLabelAt(const Common::Rect &src, const Common::Rect &dest);
125  void drawTypeMessage();
126  void drawConnectedLabel();
127  void drawConnectingSprite();
128  void drawDialedNumber();
129  void drawHelpButton(uint state);
130  void drawCloseButton(bool hovered);
131  void drawStatusLabels();
132  void drawDirectoryList();
133  void drawDirectoryArrows();
134  // Blit one scroll/paging arrow (idle, or its pressed sprite when hovered).
135  void drawScrollArrow(const UICL::ThreeRectWidget &arrow, bool hovered);
136  void drawWelcomeScreen();
137  // Nancy 13: blit one UI_Cell_Xtra atlas tile into the LCD area (the plain
138  // keyboard background or one of the message tiles). These are fixed atlas
139  // positions the original bakes into a pre-rendered per-state LCD surface.
140  void drawLcdTile(const Common::Rect &src);
141  // Show a transient message tile (Picture Sent / Deleted / Camera Full),
142  // dismissed by any click back to returnState.
143  void showMessageScreen(const Common::Rect &tileSrc, ScreenState returnState);
144  // Nancy 13: draw the current captured photo (scaled into the LCD) for the
145  // "view pictures" screen, or the "no pictures" tile when there are none.
146  void drawPictureView();
147  // The captured pictures store, lazily created (Nancy 13 only). May be null.
148  struct CellPhonePictureData *pictureData() const;
149  // Nancy 13 camera: grab the given screen-space region of the live viewport
150  // into a new persisted CapturedPicture and select it. Empty rect = the whole
151  // viewport.
152  void captureViewport(const Common::Rect &screenRegion = Common::Rect());
153 
154  // Nancy 13 camera framing: while kCamera is active the popup covers the
155  // viewport and draws a movable rectangle marking the shot. Entering saves the
156  // phone's rect and grows the draw surface; exiting restores it.
157  void enterCameraFraming();
158  void exitCameraFraming();
159  void drawCameraFraming();
160  // Screen-space rect of the framing box, centred on the mouse and clamped to
161  // the viewport.
162  // Each paging arrow is only drawn and live while its side has a photo.
163  bool canPageToPreviousPicture() const;
164  bool canPageToNextPicture() const;
165  Common::Rect framingScreenRect() const;
166  // Marks the shown snapshot sent and fires the flags of every subject in it
167  // addressed to the contact on the given directory row.
168  void sendCurrentPicture(uint listRow);
169  // Blit a sub-button's idle sprite at its chunk dest (used for the visible
170  // Back buttons: subButtons[0] on the help / directory / online screens,
171  // subButtons[7] in the zoomed email / browser content view).
172  void drawBackButton(uint subButtonIndex);
173  // Blit an online-hub option button (mail / browser), using its highlighted
174  // sprite when the cursor is over it.
175  void drawHubButton(uint subButtonIndex);
176  // Blit the lit key sprite of the currently held dial-pad slot over its
177  // dest rect, so keypad keys visually depress while pressed.
178  void drawPressedDialKey();
179 
180  // Generic list renderer used by web / email modes.
181  void drawLinkList();
182  // Blit a heading sprite (e.g. emailHeading) at its chunk dest. The
183  // heading sits in the title-bar strip above the LCD content.
184  void drawHeading(const UICL::SrcDestRectPair &heading);
185  // Render the opened entry's body text in the LCD area, word-wrapped.
186  void drawContentView();
187  // Expensive: render the current content page's hypertext into the cache
188  // surface (+ text height, image/link hotspots). Called by drawContentView
189  // only when the page key changes.
190  void renderContentPage(int surfaceWidth);
191  // The LCD area the content view renders into: the small screen for the help
192  // page, the taller zoomed-in one for browser / email articles.
193  const Common::Rect &contentViewScreenRect() const;
194  // Per-click scroll amount (pixels) for the article/help content view.
195  uint contentScrollStep() const;
196  // Enter the content view for a list entry whose AUTOTEXT key is `key`.
197  void openContentView(const Common::String &key, const UICL::SrcDestRectPair &heading);
198  // Web button: open the first url entry as the browser home page (page 0).
199  void openBrowserHome();
200 
201  // Up/down scroll buttons differ by mode: subButtons[1]/[2] for the
202  // directory's narrower list area, [5]/[6] for the taller search /
203  // email / browser-content LCD area.
204  const UICL::ThreeRectWidget &scrollUpButton() const;
205  const UICL::ThreeRectWidget &scrollDownButton() const;
206 
207  // True when the current screen uses the zoomed-in (no-keypad)
208  // chrome variant: search list, email list, and content view.
209  bool isZoomedChromeState() const {
210  return _screenState == kWebList ||
211  _screenState == kEmailList ||
212  _screenState == kContentView;
213  }
214 
215  // The help "?" page reuses the content-view state, but unlike browser /
216  // email articles the original renders it in the regular (keypad-visible)
217  // chrome and the small LCD, not the zoomed full-screen variant.
218  bool isHelpContentView() const {
219  return _screenState == kContentView && _uiclData &&
220  _contentHeading == &_uiclData->helpHeading;
221  }
222 
223  // True for screens that hide the status icons and "?" button so the
224  // top bar shows only the section heading and the up arrow.
225  bool isSubScreenState() const {
226  return _screenState == kDirectory ||
227  _screenState == kOnlineHub ||
228  isZoomedChromeState();
229  }
230 
231  void resetDialPad();
232  void enterScreenState(ScreenState newState);
233  // With no signal the phone locks to "Old Email Only": on the welcome,
234  // dialing and online-hub screens every keypad key is dead except Menu
235  // (the Menu key), which still reaches the e-mail list. Digits, *, #, Talk, Dir
236  // and the Help "?" go inert. The directory keeps its keys so the reachable
237  // e-mail path still works.
238  bool isDialKeyActive(uint slot) const;
239  // True while a player-placed call is ringing / waiting for pickup, so the
240  // connecting strip shows a Back button (subButtons[0]) that cancels it.
241  // Incoming calls have no Back button.
242  bool isCallBackButtonActive() const {
243  return (_screenState == kPlaceCall || _screenState == kWaitOutgoingRing ||
244  _screenState == kLookupContact || _screenState == kWaitPickup) &&
245  !_hasPendingCallScene && _uiclData &&
246  !_uiclData->subButtons[0].destRect.isEmpty();
247  }
248  // Cancel a ringing / waiting call and return to the welcome screen.
249  void cancelCall();
250  void appendDigit(byte slotIndex);
251  // Play a dial-pad key's DTMF tone. The name is a raw sound filename, so it
252  // is played through the phone's call-sound channel rather than the common
253  // (boot-registered) sound table.
254  void playDialPadSound(const Common::String &name);
255  // Play a popup button's click sound (the close X), like the inventory
256  // popup. Falls back to the shared button-click slot in the popup header.
257  void playButtonClickSound(const UIButtonRecord &button);
258  bool playSoundIfPresent(const Common::Path &soundName);
259  bool callSoundIsStillPlaying() const;
260  void triggerContactCallSceneChange(uint contactIndex);
261  int findContactByDialBuffer() const;
262 
263  // LCD content bounds the directory list is laid out against.
264  const Common::Rect &lcdListBounds() const;
265  uint maxDirectoryRows() const;
266  uint directoryRowAt(const Common::Point &chunkMouse) const;
267  Common::Rect directoryRowRect(uint visibleIndex) const;
268  // Row pitch and first-row Y (screen coords) from the layout data.
269  int rowPitch() const;
270  int rowTopScreen() const;
271  bool isLinkListMode() const { return _screenState == kWebList || _screenState == kEmailList; }
272  bool isOnlineMode() const { return _screenState == kOnlineHub || isLinkListMode(); }
273 
274  // Section headings live in the phone's title-bar strip above the LCD,
275  // so they don't consume a list row. Kept as a hook in case a game
276  // needs an in-LCD title row.
277  uint listTitleRows() const { return 0; }
278 
279  // Whether the phone itself takes and shows pictures, i.e. whether the Menu
280  // screen offers "View Pictures" and the top row carries the Cam label.
281  bool hasCameraFeature() const;
282 
283  // Layout for the two clickable labels on the Online hub.
284  Common::Rect hubEmailRect() const;
285  Common::Rect hubWebRect() const;
286  void startCallToContact(uint contactIndex);
287  // Directory row -> raw contact index, or -1.
288  int contactIndexForVisibleRow(uint visibleRow) const;
289  uint visibleContactCount() const;
290  // Entry count for whichever list the popup is currently showing.
291  uint currentListEntryCount() const;
292  // Absolute indices into the current list's backing array that pass
293  // the active filter. Email applies the "Old Email Only" filter
294  // (no-signal -> read messages only); web shows everything.
295  Common::Array<uint> listVisibleIndices() const;
296  // True when the contact's visibility flag is currently unlocked.
297  bool isContactVisible(const UICL::Contact &c) const;
298  // Popup-local rect of the Back hotspot in directory mode.
299  Common::Rect backLabelHitRect() const;
300  // Popup-local rect of a visible Back sub-button (subButtons[index]).
301  Common::Rect backButtonHitRect(uint subButtonIndex) const;
302  // subButtons index of the Back / HOME button visible in the current state,
303  // or -1 when none is shown. Used to drive its hover highlight.
304  int currentBackButtonIndex() const;
305  // subButtons index of the bottom button on a content view.
306  uint contentViewBottomButton() const;
307  // A browser content view other than the main page (i.e. an actual web page).
308  bool isBrowserArticle() const;
309  // Move the directory selection by delta, scrolling as needed.
310  void moveDirectorySelection(int delta);
311  // Scroll the e-mail / search list by one row. These lists have no
312  // selection, so the arrows move the list itself.
313  void scrollLinkList(int delta);
314 
315  Common::Point mouseToChunkCoords(const Common::Point &mouse) const;
316 
317  const UICL *_uiclData;
318 
319  // Runtime contact list, seeded from _uiclData->contacts and then
320  // mutable (AR 130 inserts/replaces entries).
322 
323  // Chrome (header.imageName) and sprite atlas (overlayImageName).
324  Graphics::ManagedSurface _overlayImage;
325  Graphics::ManagedSurface _spritesImage;
326  // Nancy 13 camera viewfinder, drawn over the scene while aiming.
327  Graphics::ManagedSurface _cameraViewImage;
328 
329  bool _closeButtonHovered = false;
330  bool _scrollUpHovered = false;
331  bool _scrollDownHovered = false;
332  // Green-arrow highlight state for the captioned "> HELP" (welcome / dialing)
333  // and "< BACK" / HOME sub-buttons: each swaps to its pressed sprite on hover.
334  bool _helpButtonHovered = false;
335  bool _backButtonHovered = false;
336 
337  ScreenState _screenState = kWelcome;
338 
339  // Dialed digits as '0'..'9' chars; convert with `c - '0'` to get
340  // the slot index that matches a contact's dial prefix.
341  Common::String _dialedNumber;
342 
343  SoundDescription _callSound;
344 
345  // Resolved during kLookupContact, valid through kWaitPickup; -1 = miss.
346  int _resolvedContact = -1;
347 
348  int _hoveredSlot = -1;
349 
350  // Dial-pad slot currently held down (shows the lit / depressed key), or -1.
351  int _pressedSlot = -1;
352 
353  // Online-hub option button under the cursor (subButtons index 3 = mail,
354  // 4 = browser), drawn with its highlighted sprite; -1 = none.
355  int _hoveredHubButton = -1;
356 
357  // A call queued by auto-dial / Talk, waiting for the key's DTMF tone to
358  // finish before entering kPlaceCall (see updateGraphics).
359  bool _autoDialPending = false;
360 
361  // First visible deduplicated contact, and the active row within the page.
362  uint _directoryScroll = 0;
363  uint _directorySelection = 0;
364 
365  // Nancy 13 "view pictures": index of the currently displayed captured photo.
366  int _pictureIndex = 0;
367 
368  // Nancy 13 camera framing state.
369  bool _inCameraFraming = false;
370  Common::Rect _savedPhoneRect; // phone rect to restore when framing ends
371  Common::Point _framingMouse; // last mouse pos (screen coords)
372 
373  // The original caps the persisted camera roll at 50 pictures.
374  static const uint kMaxPictures = 50;
375 
376  // True while the directory is open to pick a recipient for the current photo
377  // (reached from the picture-review "Send" button).
378  bool _sendingPicture = false;
379 
380  // Active tile + return target for the transient kMessageScreen state.
381  const Common::Rect *_messageTileSrc = nullptr;
382  ScreenState _messageReturnState = kWelcome;
383 
384  // Content-view (single email / page) state.
385  ScreenState _contentReturnState = kOnlineHub;
386  const UICL::SrcDestRectPair *_contentHeading = nullptr;
387  Common::String _contentKey;
388  // Uppercased key of the main browser page (empty until the browser is opened).
389  Common::String _browserHomeKey;
390  uint _contentScroll = 0;
391 
392  // Email "opening" transition: the visible row whose closed envelope briefly
393  // flashes open before the body is shown (-1 = none), the deadline for that
394  // transition, and the body CVTX key to open when it fires.
395  int _openingEmailRow = -1;
396  uint32 _openingEmailTime = 0;
397  Common::String _openingEmailKey;
398 
399  // In-page hyperlinks: rects (popup-local, recomputed every draw) and
400  // the target CVTX key parsed from each <H>...<L> region of the body.
401  Common::Array<Common::Rect> _contentHotspots;
402  Common::Array<Common::String> _contentHotspotTargets;
403 
404  // Cached render of the current content page. Rendering the hypertext is
405  // expensive, so it's only rebuilt when the page key changes — scrolling
406  // and hover redraws just re-blit a window of the cached surface (fixes the
407  // cursor stutter while hovering the scroll arrows).
408  Graphics::ManagedSurface _contentCacheSurface;
409  Common::String _contentCacheKey;
410  uint16 _contentCacheTextHeight = 0;
411  Common::Array<Common::Rect> _contentCacheHotspots;
412  Common::Array<Common::String> _contentCacheTargets;
413 
414  bool _noSignal = false;
415  bool _batteryLow = false;
416 
417  // Incoming-call destination (set by startIncomingCall, consumed by the
418  // kConnected handler once the player has answered).
419  SceneChangeDescription _pendingCallScene;
420  bool _hasPendingCallScene = false;
421 
422  // True while the in-progress / just-finished call was incoming (auto-rung),
423  // so AR 128 knows to close the phone afterwards; player-placed calls leave
424  // it open. Persists past _hasPendingCallScene, which is consumed on connect.
425  bool _callWasIncoming = false;
426 
427  SceneChangeDescription _returnScene;
428  bool _hasReturnScene = false;
429 };
430 
431 } // End of namespace UI
432 } // End of namespace Nancy
433 
434 #endif // NANCY_UI_TASKBAR_CELLPHONEPOPUP_H
Definition: managed_surface.h:51
Definition: str.h:59
Definition: commontypes.h:186
Definition: enginedata.h:630
Definition: enginedata.h:645
Definition: rect.h:536
Definition: path.h:52
Definition: enginedata.h:637
Definition: input.h:41
Definition: soundequalizerpuzzle.h:27
Definition: commontypes.h:429
Definition: renderobject.h:36
Definition: puzzledata.h:353
bool isEmpty() const
Definition: rect.h:385
Definition: rect.h:144
Definition: cellphonepopup.h:38
Definition: enginedata.h:664
Definition: commontypes.h:306
Definition: actionmanager.h:32