aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/EmWindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/EmWindow.cpp')
-rw-r--r--SrcShared/EmWindow.cpp1388
1 files changed, 1388 insertions, 0 deletions
diff --git a/SrcShared/EmWindow.cpp b/SrcShared/EmWindow.cpp
new file mode 100644
index 0000000..7ab366e
--- /dev/null
+++ b/SrcShared/EmWindow.cpp
@@ -0,0 +1,1388 @@
+/* -*- mode: C++; tab-width: 4 -*- */
+/* ===================================================================== *\
+ Copyright (c) 2001 Palm, Inc. or its subsidiaries.
+ All rights reserved.
+
+ This file is part of the Palm OS Emulator.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+\* ===================================================================== */
+
+#include "EmCommon.h"
+#include "EmWindow.h"
+
+#include "EmHAL.h" // EmHAL::GetVibrateOn
+#include "EmJPEG.h" // JPEGToPixMap
+#include "EmPixMap.h" // EmPixMap
+#include "EmQuantizer.h" // EmQuantizer
+#include "EmRegion.h" // EmRegion
+#include "EmScreen.h" // EmScreenUpdateInfo
+#include "EmSession.h" // PostPenEvent, PostButtonEvent, etc.
+#include "EmStream.h" // delete imageStream
+#include "Platform.h" // Platform::PinToScreen
+
+EmWindow* gWindow;
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::EmWindow
+// ---------------------------------------------------------------------------
+// Constructor. Perform simple data member initialization. The heavy stuff
+// occurs in WindowCreate.
+
+EmWindow::EmWindow (void) :
+ fSkinBase (),
+ fSkinCurrent (),
+ fSkinColors (),
+ fSkinRegion (),
+ fPrevLCDColors (),
+ fCurrentButton (kElement_None),
+ fNeedWindowReset (false),
+ fNeedWindowInvalidate (false),
+ fOldLCDOn (false),
+ fOldBacklightOn (false),
+ fOldLEDState (0),
+ fWiggled (false),
+ fActive (true),
+ fDebugMode (false),
+ fGremlinMode (false)
+{
+ EmAssert (gWindow == NULL);
+ gWindow = this;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::~EmWindow
+// ---------------------------------------------------------------------------
+// Dispose of the window and all its data. Unregister for notification, save
+// the window location, and destroy the host window object.
+
+EmWindow::~EmWindow (void)
+{
+ EmAssert (gWindow == this);
+ gWindow = NULL;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PreDestroy
+// ---------------------------------------------------------------------------
+// Carry out actions to be performed just before the window is closed. These
+// actions would normally be performed in EmWindow's destructor, except that
+// the host window is already deleted by then. Therefore, each sub-class of
+// EmWindow needs to call this in *its* destructor before destroying the host
+// window.
+
+void EmWindow::PreDestroy (void)
+{
+ gPrefs->RemoveNotification (EmWindow::PrefsChangedCB);
+
+ // Save the window location.
+
+ EmRect rect (this->HostWindowBoundsGet ());
+
+ if (!rect.IsEmpty ())
+ {
+ Preference <EmPoint> pref (kPrefKeyWindowLocation);
+ pref = rect.TopLeft ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::WindowInit
+// ---------------------------------------------------------------------------
+// Create the LCD window, along with any host data and host window objects.
+// Set the window's skin, register for any notification regarding the window's
+// appearance, reposition the window to where it should live, ensure that
+// the window is still on the screen, and then finally make the window
+// visible.
+//
+// This function should be called immediately after the EmWindow object is
+// created.
+
+void EmWindow::WindowInit (void)
+{
+ // Establish the window's skin.
+
+ this->WindowReset ();
+
+ // Install notification callbacks for when the skin changes.
+
+ gPrefs->AddNotification (&EmWindow::PrefsChangedCB, kPrefKeySkins, this);
+ gPrefs->AddNotification (&EmWindow::PrefsChangedCB, kPrefKeyScale, this);
+ gPrefs->AddNotification (&EmWindow::PrefsChangedCB, kPrefKeyDimWhenInactive, this);
+ gPrefs->AddNotification (&EmWindow::PrefsChangedCB, kPrefKeyShowDebugMode, this);
+ gPrefs->AddNotification (&EmWindow::PrefsChangedCB, kPrefKeyShowGremlinMode, this);
+ gPrefs->AddNotification (&EmWindow::PrefsChangedCB, kPrefKeyBackgroundColor, this);
+ gPrefs->AddNotification (&EmWindow::PrefsChangedCB, kPrefKeyStayOnTop, this);
+
+ // Restore the window location.
+
+ Preference<EmPoint> pref (kPrefKeyWindowLocation);
+
+ if (pref.Loaded ())
+ {
+ this->HostWindowMoveTo (*pref);
+ }
+
+ // No previously saved position, so center it.
+
+ else
+ {
+ this->HostWindowCenter ();
+ }
+
+ // Ensure the window is on the screen.
+
+ EmRect rect (this->HostWindowBoundsGet ());
+
+ if (Platform::PinToScreen (rect))
+ {
+ this->HostWindowMoveTo (rect.TopLeft ());
+ }
+
+ // Show the window.
+
+ this->HostWindowShow ();
+
+ // Make the window stay on top if it is set as such.
+
+ this->HostWindowStayOnTop ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::WindowReset
+// ---------------------------------------------------------------------------
+
+void EmWindow::WindowReset (void)
+{
+ // Can only set the skin if we have a session running, so that we can
+ // query it for its configuration.
+
+ if (!gSession)
+ return;
+
+ // Configure our Skin engine.
+
+ ::SkinSetSkin ();
+
+ // Get the specified skin, or the default skin if the specified
+ // one cannot be found.
+
+ if (!this->GetSkin (fSkinBase))
+ {
+ this->GetDefaultSkin (fSkinBase);
+ }
+
+ // Create a one-bpp mask of the skin.
+
+ EmPixMap mask;
+ fSkinBase.CreateMask (mask);
+
+ // Convert it to a region.
+
+ fSkinRegion = mask.CreateRegion ();
+
+ // Clear our color caches. They'll get filled again on demand.
+
+ for (int ii = 0; ii < (long) countof (fSkinColors); ++ii)
+ {
+ fSkinColors[ii].clear ();
+ }
+
+ // Clear the image of the skin, altered for its mode. This image
+ // will get regenerated on demand.
+
+ fSkinCurrent = EmPixMap ();
+
+ // Clear out the previously saved LCD colors. This will force the
+ // any host palettes to be regenerated, because they'll think the
+ // Palm OS LCD palette has changed.
+
+ fPrevLCDColors.clear ();
+
+ // Now convert this all to platform-specific data structures.
+ // This will also resize the window.
+
+ this->HostWindowReset ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HandlePenEvent
+// ---------------------------------------------------------------------------
+
+void EmWindow::HandlePenEvent (const EmPoint& where, Bool down)
+{
+ // Find out what part of the case we clicked on.
+
+ SkinElementType what;
+
+ // If the mouse button is down and we aren't currently tracking
+ // anything, then find out what the user clicked on.
+
+ if (down && (fCurrentButton == kElement_None))
+ {
+ what = ::SkinTestPoint (where);
+
+ if ((what != kElement_Frame) && (what != kElement_None))
+ {
+ this->HostMouseCapture ();
+ }
+ }
+
+ // If the pen is up, or if we were already in the progress of tracking
+ // something, then stick with it.
+
+ else
+ {
+ what = fCurrentButton;
+ }
+
+ // If we clicked in the touchscreen area (which includes the LCD area
+ // and the silkscreen area), start the mechanism for tracking the mouse.
+
+ if (what == kElement_Touchscreen)
+ {
+ EmPoint whereLCD = ::SkinWindowToTouchscreen (where);
+ EmPenEvent event (whereLCD, down);
+
+ EmAssert (gSession);
+ gSession->PostPenEvent (event);
+ }
+
+ // If we clicked in the frame, start dragging the window.
+
+ else if (what == kElement_Frame)
+ {
+ if (down)
+ {
+ this->HostWindowDrag ();
+
+ // We normally record the mouse button going up in
+ // EventMouseUp. However, that method is not called
+ // after ClickInDrag, so say the mouse button is up here.
+
+ down = false;
+ }
+ }
+
+ // Otherwise, if we clicked on a button, start tracking it.
+
+ else if (what != kElement_None)
+ {
+ EmButtonEvent event (what, down);
+
+ EmAssert (gSession);
+ gSession->PostButtonEvent (event);
+ }
+
+ // Set or clear what we're tracking.
+
+ if (down)
+ fCurrentButton = what;
+ else
+ fCurrentButton = kElement_None;
+
+ if (fCurrentButton == kElement_None)
+ {
+ this->HostMouseRelease ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HandleUpdate
+// ---------------------------------------------------------------------------
+// Called to handle update events.
+
+void EmWindow::HandleUpdate (void)
+{
+ this->HostUpdateBegin ();
+
+ this->PaintScreen (true, true);
+
+ this->HostUpdateEnd ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HandlePalette
+// ---------------------------------------------------------------------------
+// Called when we need to tell the OS what our preferred palette is. Usually
+// called when window orders change.
+
+void EmWindow::HandlePalette (void)
+{
+ this->HostPalette ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HandleDisplayChange
+// ---------------------------------------------------------------------------
+// Called when the display depth changes.
+
+void EmWindow::HandleDisplayChange (void)
+{
+ this->HostDisplayChange ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HandleActivate
+// ---------------------------------------------------------------------------
+// Called when the display depth changes.
+
+void EmWindow::HandleActivate (Bool active)
+{
+ if (fActive != active)
+ {
+ fActive = active;
+ this->HostWindowReset ();
+
+ fSkinCurrent = EmPixMap ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HandleDebugMode
+// ---------------------------------------------------------------------------
+// Called when the display depth changes.
+
+void EmWindow::HandleDebugMode (Bool debugMode)
+{
+ if (fDebugMode != debugMode)
+ {
+ fDebugMode = debugMode;
+ this->HostWindowReset ();
+
+ fSkinCurrent = EmPixMap ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HandleGremlinMode
+// ---------------------------------------------------------------------------
+// Called when the display depth changes.
+
+void EmWindow::HandleGremlinMode (Bool gremlinMode)
+{
+ if (fGremlinMode != gremlinMode)
+ {
+ fGremlinMode = gremlinMode;
+ this->HostWindowReset ();
+
+ fSkinCurrent = EmPixMap ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HandleIdle
+// ---------------------------------------------------------------------------
+// Called several times a second to perform idle time events such as
+// refreshing the LCD area and vibrating the window.
+
+void EmWindow::HandleIdle (void)
+{
+ // Get the current mouse position.
+
+ EmPoint where = this->HostGetCurrentMouse ();
+
+ // Track the pen if it's down.
+
+ if (fCurrentButton == kElement_Touchscreen)
+ {
+ this->HandlePenEvent (where, true);
+ }
+
+ // Refresh the LCD area.
+
+ this->HostDrawingBegin ();
+
+ if (fNeedWindowReset)
+ {
+ this->WindowReset ();
+ }
+ else if (fNeedWindowInvalidate)
+ {
+ this->PaintScreen (false, true);
+ }
+ else
+ {
+ this->PaintScreen (false, false);
+ }
+
+ fNeedWindowReset = false;
+ fNeedWindowInvalidate = false;
+
+ this->HostDrawingEnd ();
+
+
+ // Do the Wiggle Walk.
+
+ {
+ const int kWiggleOffset = 2;
+
+ EmSessionStopper stopper (gSession, kStopNow);
+
+ if (stopper.Stopped ())
+ {
+ if (EmHAL::GetVibrateOn ())
+ {
+ if (!fWiggled)
+ {
+ fWiggled = true;
+ this->HostWindowMoveBy (EmPoint (kWiggleOffset, 0));
+ }
+ else
+ {
+ fWiggled = false;
+ this->HostWindowMoveBy (EmPoint (-kWiggleOffset, 0));
+ }
+ }
+
+ // If the vibrator just went off, then put the window
+ // back to where it was.
+
+ else if (fWiggled)
+ {
+ fWiggled = false;
+ this->HostWindowMoveBy (EmPoint (-kWiggleOffset, 0));
+ }
+ }
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetLCDContents
+// ---------------------------------------------------------------------------
+// Return the full contents of the LCD area. Used for screenshots.
+
+void EmWindow::GetLCDContents (EmScreenUpdateInfo& info)
+{
+ EmSessionStopper stopper (gSession, kStopNow);
+
+ if (stopper.Stopped ())
+ {
+ EmScreen::InvalidateAll ();
+ EmScreen::GetBits (info);
+ EmScreen::InvalidateAll ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PaintScreen
+// ---------------------------------------------------------------------------
+// Paint the entire window. Could be called in response to an update event
+// or as part of our regular idle time handling.
+
+void EmWindow::PaintScreen (Bool drawCase, Bool wholeLCD)
+{
+ EmScreenUpdateInfo info;
+ Bool bufferDirty;
+ Bool drawFrame = false;
+ Bool drawLED = false;
+ Bool drawLCD = false;
+ Bool lcdOn;
+ Bool backlightOn;
+ uint16 ledState;
+
+ {
+ // Pause the CPU so that we can get the current hardware state.
+
+ EmSessionStopper stopper (gSession, kStopNow);
+
+ if (!stopper.Stopped ())
+ return;
+
+ // Determine if we have to force the redrawing of the background.
+ // We have to do that if the LCD or LEDs have turned off.
+
+ lcdOn = EmHAL::GetLCDScreenOn ();
+ backlightOn = EmHAL::GetLCDBacklightOn ();
+ ledState = EmHAL::GetLEDState ();
+
+ if (!lcdOn && fOldLCDOn)
+ {
+ drawCase = true;
+ }
+
+ if (!ledState && fOldLEDState)
+ {
+ drawCase = true;
+ }
+
+ // Determine if we have to draw the LCD or LED. We'd have to do that
+ // if their state has changed or if we had to draw the background.
+
+ if (drawCase || (lcdOn != fOldLCDOn) || (backlightOn != fOldBacklightOn))
+ {
+ drawLCD = lcdOn;
+ }
+
+ if (drawCase || (lcdOn != fOldLCDOn))
+ {
+ drawFrame = lcdOn && EmHAL::GetLCDHasFrame ();
+ }
+
+ if (drawCase || (ledState != fOldLEDState))
+ {
+ drawLED = ledState != 0;
+ }
+
+ fOldLCDOn = lcdOn;
+ fOldBacklightOn = backlightOn;
+ fOldLEDState = ledState;
+
+ // If we're going to be drawing the whole LCD, then invalidate
+ // the entire screen.
+
+ if (wholeLCD || drawLCD)
+ {
+ EmScreen::InvalidateAll ();
+ }
+
+ // Get the current LCD state.
+
+ bufferDirty = EmScreen::GetBits (info);
+
+ // If the buffer was dirty, that's another reason to draw the LCD.
+
+ if (bufferDirty)
+ {
+ drawLCD = lcdOn;
+ }
+
+ // If there is no width or height to the buffer, then that's a
+ // great reason to NOT draw the LCD.
+
+ EmPoint size = info.fImage.GetSize ();
+ if (size.fX == 0 || size.fY == 0)
+ {
+ drawLCD = false;
+ }
+ }
+
+ // Set up the graphics palette.
+
+ Bool drawAnything = drawCase || drawFrame || drawLED || drawLCD;
+
+ if (drawAnything)
+ {
+ this->HostPaletteSet (info);
+ }
+
+
+ // Draw the case.
+
+ if (drawCase)
+ {
+ this->PaintCase (info);
+ }
+
+ // Draw any LCD frame.
+
+ if (drawFrame)
+ {
+ this->PaintLCDFrame (info);
+ }
+
+ // Draw any LEDs.
+
+ if (drawLED)
+ {
+ this->PaintLED (ledState);
+ }
+
+ // Draw the LCD area.
+
+ if (drawLCD)
+ {
+ this->PaintLCD (info);
+ }
+
+#if 0
+ // Draw frames around the buttons. This is handy when determining their
+ // locations initially.
+
+ if (drawCase)
+ {
+ this->PaintButtonFrames ();
+ }
+#endif
+
+ // Restore the palette that was changed in HostSetPalette.
+
+ if (drawAnything)
+ {
+ this->HostPaletteRestore ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PaintCase
+// ---------------------------------------------------------------------------
+// Draw the skin.
+
+void EmWindow::PaintCase (const EmScreenUpdateInfo& info)
+{
+ this->HostPaintCase (info);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PaintLCDFrame
+// ---------------------------------------------------------------------------
+// Draw any frame around the LCD area required in order to faithfully
+// emulate the appearance of the real LCD.
+
+void EmWindow::PaintLCDFrame (const EmScreenUpdateInfo&)
+{
+ RGBType backlightColor (0xff, 0xff, 0xff);
+ EmRect lcdRect (this->GetLCDBounds ());
+ EmPoint penSize (2, 2);
+
+ penSize = ::SkinScaleUp (penSize);
+
+ lcdRect.Inset (-penSize.fX, -penSize.fY);
+
+ this->HostRectFrame (lcdRect, penSize, backlightColor);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PaintLCD
+// ---------------------------------------------------------------------------
+// Draw the LCD portion of the window.
+
+void EmWindow::PaintLCD (const EmScreenUpdateInfo& info)
+{
+ // Get the bounds of the LCD area.
+
+ EmRect lcdRect = this->GetLCDBounds ();
+
+ // Limit the vertical range to the lines that have changed.
+
+ EmRect destRect = ::SkinScaleDown (lcdRect);
+
+ destRect.fBottom = destRect.fTop + info.fLastLine;
+ destRect.fTop = destRect.fTop + info.fFirstLine;
+
+ destRect = ::SkinScaleUp (destRect);
+
+ // Get the bounds of the area we'll be blitting from.
+
+ EmRect srcRect (destRect);
+ srcRect -= lcdRect.TopLeft ();
+
+ // Determine if the image needs to be scaled.
+
+ EmPoint before (1, 1);
+ EmPoint after = ::SkinScaleUp (before);
+
+ // Setup is done. Let the host-specific routines handle the rest.
+
+ this->HostPaintLCD (info, srcRect, destRect, before.fX != after.fX);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PaintLED
+// ---------------------------------------------------------------------------
+
+void EmWindow::PaintLED (uint16 ledState)
+{
+ // Get the color for the LED.
+
+ RGBType ledColor;
+
+ if ((ledState & (kLEDRed | kLEDGreen)) == (kLEDRed | kLEDGreen))
+ {
+ ledColor = RGBType (255, 128, 0); // Orange
+ }
+ else if ((ledState & kLEDRed) == kLEDRed)
+ {
+ ledColor = RGBType (255, 0, 0); // Red
+ }
+ else /* green */
+ {
+ ledColor = RGBType (0, 255, 0); // Green
+ }
+
+ EmRect bounds (this->GetLEDBounds ());
+
+ this->HostOvalPaint (bounds, ledColor);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PaintButtonFrames
+// ---------------------------------------------------------------------------
+// Draw frames around the buttons. This is handy when determining their
+// locations initially.
+
+void EmWindow::PaintButtonFrames (void)
+{
+ EmPoint penSize (1, 1);
+ RGBType color (0, 0, 0);
+
+ int index = 0;
+ SkinElementType type;
+ EmRect bounds;
+
+ while (::SkinGetElementInfo (index, type, bounds))
+ {
+ this->HostRectFrame (bounds, penSize, color);
+
+ ++index;
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PrefsChangedCB
+// ---------------------------------------------------------------------------
+// Respond to a preference change. This version of the function retrieves
+// the EmWindow object pointer and calls a non-static member function of the
+// object.
+
+void EmWindow::PrefsChangedCB (PrefKeyType key, void* data)
+{
+ EmAssert (data);
+
+ EmWindow* lcd = static_cast<EmWindow*>(data);
+ lcd->PrefsChanged (key);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PrefsChanged
+// ---------------------------------------------------------------------------
+// Respond to a preference change. If the sking or display size has changed,
+// we need to rebuild our skin image. If the background color preference
+// has changed, then just flag that the LCD area needs to be redrawn.
+
+void EmWindow::PrefsChanged (PrefKeyType key)
+{
+ if (::PrefKeysEqual (key, kPrefKeyScale) ||
+ ::PrefKeysEqual (key, kPrefKeySkins) ||
+ ::PrefKeysEqual (key, kPrefKeyShowDebugMode) ||
+ ::PrefKeysEqual (key, kPrefKeyShowGremlinMode) ||
+ ::PrefKeysEqual (key, kPrefKeyBackgroundColor))
+ {
+ fNeedWindowReset = true;
+ }
+ else if (::PrefKeysEqual (key, kPrefKeyBackgroundColor))
+ {
+ fNeedWindowInvalidate = true;
+ }
+ else if (::PrefKeysEqual (key, kPrefKeyStayOnTop))
+ {
+ this->HostWindowStayOnTop ();
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetLCDBounds
+// ---------------------------------------------------------------------------
+// Get the bounds for the LCD.
+
+EmRect EmWindow::GetLCDBounds (void)
+{
+ int ii = 0;
+ SkinElementType type = kElement_None;
+ EmRect bounds (10, 10, 170, 170);
+
+ while (::SkinGetElementInfo (ii, type, bounds))
+ {
+ if (type == kElement_LCD)
+ {
+ break;
+ }
+
+ ++ii;
+ }
+
+ return bounds;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetLEDBounds
+// ---------------------------------------------------------------------------
+// Get the bounds for the LED. Put it in the power button.
+
+EmRect EmWindow::GetLEDBounds (void)
+{
+ int ii = 0;
+ SkinElementType type = kElement_None;
+ Bool found = false;
+ EmRect bounds (10, 10, 20, 20);
+
+ while (::SkinGetElementInfo (ii++, type, bounds))
+ {
+ if (type == kElement_LED)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ ii = 0;
+ while (::SkinGetElementInfo (ii++, type, bounds))
+ {
+ if (type == kElement_PowerButton)
+ {
+ // Create a square centered in the power button.
+
+ if (bounds.Width () > bounds.Height ())
+ {
+ bounds.fLeft = bounds.fLeft + (bounds.Width () - bounds.Height ()) / 2;
+ bounds.fRight = bounds.fLeft + bounds.Height ();
+ }
+ else
+ {
+ bounds.fTop = bounds.fTop + (bounds.Height () - bounds.Width ()) / 2;
+ bounds.fBottom = bounds.fTop + bounds.Width ();
+ }
+
+ // Inset it a little -- looks better this way.
+
+ bounds.Inset (2, 2);
+
+ break;
+ }
+ }
+ }
+
+ return bounds;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetSkin
+// ---------------------------------------------------------------------------
+// Get the currently selected skin as a PixMap.
+
+Bool EmWindow::GetSkin (EmPixMap& pixMap)
+{
+ EmStream* imageStream = ::SkinGetSkinStream ();
+
+ if (!imageStream)
+ return false;
+
+ // Turn the JPEG image into BMP format.
+
+ ::JPEGToPixMap (*imageStream, pixMap);
+
+ // Free up the resource info.
+
+ delete imageStream;
+ imageStream = NULL;
+
+ return true;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetDefaultSkin
+// ---------------------------------------------------------------------------
+// Get the default, built-in skin as a PixMap.
+
+void EmWindow::GetDefaultSkin (EmPixMap& pixMap)
+{
+ Preference<ScaleType> scalePref (kPrefKeyScale);
+
+ EmAssert ((*scalePref == 1) || (*scalePref == 2));
+
+ this->HostGetDefaultSkin (pixMap, *scalePref);
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::PrevLCDColorsChanged
+// ---------------------------------------------------------------------------
+
+Bool EmWindow::PrevLCDColorsChanged (const RGBList& newLCDColors)
+{
+ // If the incoming image does not have a color table, then it's a
+ // direct-pixel image. It can have any set of colors in it, so
+ // we'll have to create an 8-bit palette for it no matter what.
+ //
+ // If the incoming image's color table has a different size than
+ // the previously saved color table, then we'll need to regenerate
+ // our cached palette.
+
+ if (newLCDColors.size () == 0 ||
+ newLCDColors.size () != fPrevLCDColors.size ())
+ {
+ return true;
+ }
+
+ // The incoming color table size is the same as the previously saved
+ // color table size, and neither of them are zero. Check the contents
+ // of the two tables to see if they've changed.
+
+ for (size_t ii = 0; ii < newLCDColors.size (); ++ii)
+ {
+ if (newLCDColors[ii] != fPrevLCDColors[ii])
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::SaveLCDColors
+// ---------------------------------------------------------------------------
+
+void EmWindow::SaveLCDColors (const RGBList& newLCDColors)
+{
+ fPrevLCDColors = newLCDColors;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetSystemColors
+// ---------------------------------------------------------------------------
+
+void EmWindow::GetSystemColors (const EmScreenUpdateInfo& info, RGBList& colors)
+{
+ // If this is a 1, 2, or 4 bit LCD, then we can use the whole
+ // palette from it along with our beefy skin color table.
+
+ if (info.fImage.GetDepth () <= 4)
+ {
+ colors = info.fImage.GetColorTable ();
+ colors.insert (colors.end (),
+ this->GetCurrentSkinColors (false).begin (),
+ this->GetCurrentSkinColors (false).end ());
+ }
+
+ // If this is an 8-bit image, then we have an interesting problem. We
+ // want to generate a palette that we can cache until the Palm OS
+ // palette changes. The palette we generate shouldn't be based on the
+ // current image, as that can change and use different colors without
+ // the Palm OS palette changing. So if we were to take that route,
+ // the palette we generated would be based on the handful of colors on
+ // the screen and not what's in the palette. It would be nice to use
+ // the Palm OS palette itself, but there are 256 colors in there, and
+ // we need to cut that down a little so that we can share time with
+ // the skin. Therefore, we run the Palm OS palette itself through the
+ // color quantizer itself to generate a nice subset of the palette.
+ // Since we have to merge 84 or so colors together, and since all
+ // colors in the Palm OS palette are weighted evenly, we don't want
+ // those 84 merged colors to appear at one end of the spectrum or the
+ // other. Therefore, we'll randomize the Palm OS palette before
+ // offering its colors to the quantizer.
+
+ else if (info.fImage.GetDepth () == 8)
+ {
+ srand (1); // Randomize it consistantly... :-)
+
+ colors = info.fImage.GetColorTable ();
+
+ for (size_t ii = 0; ii < colors.size (); ++ii)
+ {
+ size_t jj = rand () % colors.size ();
+
+ swap (colors[ii], colors[jj]);
+ }
+
+ EmQuantizer q (172, 6);
+ q.ProcessColorTable (colors);
+ q.GetColorTable (colors);
+
+ colors.insert (colors.end (),
+ this->GetCurrentSkinColors (true).begin (),
+ this->GetCurrentSkinColors (true).end ());
+ }
+
+ // Otherwise, if this is a 16 or 24 bit LCD, then we have to
+ // get the best 172 colors and combine that with our 64-entry
+ // skin color table.
+
+ else
+ {
+ EmQuantizer q (172, 6);
+ q.ProcessImage (info.fImage);
+ q.GetColorTable (colors);
+
+ colors.insert (colors.end (),
+ this->GetCurrentSkinColors (true).begin (),
+ this->GetCurrentSkinColors (true).end ());
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::QuantizeSkinColors
+// ---------------------------------------------------------------------------
+
+void EmWindow::QuantizeSkinColors (const EmPixMap& skin, RGBList& colors, Bool polite)
+{
+ if (polite)
+ {
+ EmQuantizer q (64, 6); // Leaves 256 - 64 - 20 = 172 for LCD
+ q.ProcessImage (skin);
+ q.GetColorTable (colors);
+ }
+ else
+ {
+ EmQuantizer q (220, 6); // Leaves 256 - 220 - 20 = 16 for LCD
+ q.ProcessImage (skin);
+ q.GetColorTable (colors);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetCurrentSkin
+// ---------------------------------------------------------------------------
+
+const EmPixMap& EmWindow::GetCurrentSkin (void)
+{
+ if (fSkinCurrent.GetSize () == EmPoint (0, 0))
+ {
+ fSkinCurrent = fSkinBase;
+
+ Preference<bool> prefDimWhenInactive (kPrefKeyDimWhenInactive);
+ Preference<bool> prefShowDebugMode (kPrefKeyShowDebugMode);
+ Preference<bool> prefShowGremlinMode (kPrefKeyShowGremlinMode);
+
+ if (!fActive && *prefDimWhenInactive)
+ fSkinCurrent.ChangeTone (40);
+
+ if (fDebugMode && *prefShowDebugMode)
+ fSkinCurrent.ConvertToColor (1);
+
+ else if (fGremlinMode && *prefShowGremlinMode)
+ fSkinCurrent.ConvertToColor (2);
+ }
+
+ return fSkinCurrent;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetCurrentSkinColors
+// ---------------------------------------------------------------------------
+
+const RGBList& EmWindow::GetCurrentSkinColors (Bool polite)
+{
+ int index = 0;
+
+ if (fDebugMode)
+ index |= 0x0001;
+
+ else if (fGremlinMode)
+ index |= 0x0002;
+
+ if (fActive)
+ index |= 0x0004;
+
+ if (polite)
+ index |= 0x0008;
+
+ RGBList& result = fSkinColors[index];
+
+ if (result.empty ())
+ {
+ const EmPixMap& skin = this->GetCurrentSkin ();
+
+ this->QuantizeSkinColors (skin, result, polite);
+ }
+
+ return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::GetCurrentSkinRegion
+// ---------------------------------------------------------------------------
+
+const EmRegion& EmWindow::GetCurrentSkinRegion (void)
+{
+ return fSkinRegion;
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostWindowReset
+// ---------------------------------------------------------------------------
+// Update the window's appearance due to a skin change.
+
+void EmWindow::HostWindowReset (void)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostMouseCapture
+// ---------------------------------------------------------------------------
+// Capture the mouse so that all mouse events get sent to this window.
+
+void EmWindow::HostMouseCapture (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostMouseRelease
+// ---------------------------------------------------------------------------
+// Release the mouse so that mouse events get sent to the window the
+// cursor is over.
+
+void EmWindow::HostMouseRelease (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostUpdateBegin
+// ---------------------------------------------------------------------------
+// Prepare the host window object for updating.
+
+void EmWindow::HostUpdateBegin (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostUpdateEnd
+// ---------------------------------------------------------------------------
+// Finalize the host window object after it's been updated.
+
+void EmWindow::HostUpdateEnd (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostDrawingBegin
+// ---------------------------------------------------------------------------
+// Prepare the host window object for drawing outside of an update event.
+
+void EmWindow::HostDrawingBegin (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostDrawingEnd
+// ---------------------------------------------------------------------------
+// Finalize the host window object after drawing outside of an update event.
+
+void EmWindow::HostDrawingEnd (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostPaletteSet
+// ---------------------------------------------------------------------------
+// Establish the palette to be used for drawing.
+
+void EmWindow::HostPaletteSet (const EmScreenUpdateInfo&)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostPaletteRestore
+// ---------------------------------------------------------------------------
+// Clean up after HostPaletteSet.
+
+void EmWindow::HostPaletteRestore (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostWindowMoveBy
+// ---------------------------------------------------------------------------
+// Move the host window object by the given offset.
+
+void EmWindow::HostWindowMoveBy (const EmPoint&)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostWindowMoveTo
+// ---------------------------------------------------------------------------
+// Move the host window object to the given location.
+
+void EmWindow::HostWindowMoveTo (const EmPoint&)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostWindowBoundsGet
+// ---------------------------------------------------------------------------
+// Get the global bounds of the host window object.
+
+EmRect EmWindow::HostWindowBoundsGet (void)
+{
+ EmAssert (false);
+ return EmRect (0, 0, 0, 0);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostWindowCenter
+// ---------------------------------------------------------------------------
+// Center the window to the main display.
+
+void EmWindow::HostWindowCenter (void)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostWindowShow
+// ---------------------------------------------------------------------------
+// Make the host window object visible.
+
+void EmWindow::HostWindowShow (void)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostWindowStayOnTop
+// ---------------------------------------------------------------------------
+// Make the host window stay on top.
+
+void EmWindow::HostWindowStayOnTop (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostWindowDrag
+// ---------------------------------------------------------------------------
+// The user has clicked in a region of the host window object that causes
+// the window to be dragged. Drag the window around.
+
+void EmWindow::HostWindowDrag (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostRectFrame
+// ---------------------------------------------------------------------------
+// Draw a rectangle frame with the given width in the given color.
+
+void EmWindow::HostRectFrame (const EmRect&, const EmPoint&, const RGBType&)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostOvalPaint
+// ---------------------------------------------------------------------------
+// Fill an oval with the given color.
+
+void EmWindow::HostOvalPaint (const EmRect&, const RGBType&)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostPaintCase
+// ---------------------------------------------------------------------------
+// Draw the skin.
+
+void EmWindow::HostPaintCase (const EmScreenUpdateInfo&)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostPaintLCD
+// ---------------------------------------------------------------------------
+// Draw the LCD area.
+
+void EmWindow::HostPaintLCD (const EmScreenUpdateInfo&, const EmRect&,
+ const EmRect&, Bool)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostPalette
+// ---------------------------------------------------------------------------
+// Tell the system about the palette we want to use. Called when Windows
+// is updating its system palette.
+
+void EmWindow::HostPalette (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostDisplayChange
+// ---------------------------------------------------------------------------
+// Respond to the display's bit depth changing. All we do here is flush our
+// caches of the skin information. It will get regenerated when it's needed.
+
+void EmWindow::HostDisplayChange (void)
+{
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostGetDefaultSkin
+// ---------------------------------------------------------------------------
+// Get the default (built-in) skin image.
+
+void EmWindow::HostGetDefaultSkin (EmPixMap&, int)
+{
+ EmAssert (false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmWindow::HostGetCurrentMouse
+// ---------------------------------------------------------------------------
+// Get the current mouse location.
+
+EmPoint EmWindow::HostGetCurrentMouse (void)
+{
+ EmAssert (false);
+ return EmPoint (0, 0);
+}