new portfolio features, metal graphs, preparing for 0.4 release
[stockthis] / portrait.py
1 # -*- coding: utf-8 -*-
2 #
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2009 Thomas Perl and the gPodder Team
5 #
6 # gPodder is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # gPodder is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 #
19
20 import dbus
21 import dbus.glib
22
23 import hildon
24 import osso
25
26
27
28 class FremantleRotation(object):
29     """thp's screen rotation for Maemo 5
30
31     Simply instantiate an object of this class and let it auto-rotate
32     your StackableWindows depending on the device orientation.
33
34     If you need to relayout a window, connect to its "configure-event"
35     signal and measure the ratio of width/height and relayout for that.
36
37     You can set the mode for rotation to AUTOMATIC (default), NEVER or
38     ALWAYS with the set_mode() method.
39     """
40     AUTOMATIC, NEVER, ALWAYS = range(3)
41
42     # Human-readable captions for the above constants
43     MODE_CAPTIONS = ('Automatic', 'Landscape', 'Portrait')
44
45     # Privately-used constants
46     _PORTRAIT, _LANDSCAPE = ('portrait', 'landscape')
47     _ENABLE_ACCEL = 'req_accelerometer_enable'
48     _DISABLE_ACCEL = 'req_accelerometer_disable'
49
50     # Defined in mce/dbus-names.h
51     _MCE_SERVICE = 'com.nokia.mce'
52     _MCE_REQUEST_PATH = '/com/nokia/mce/request'
53     _MCE_REQUEST_IF = 'com.nokia.mce.request'
54
55     def __init__(self, app_name, main_window=None, version='1.0', mode=0):
56         """Create a new rotation manager
57
58         app_name    ... The name of your application (for osso.Context)
59         main_window ... The root window (optional, hildon.StackableWindow)
60         version     ... The version of your application (optional, string)
61         mode        ... Initial mode for this manager (default: AUTOMATIC)
62         """
63         self._orientation = None
64         self._main_window = main_window
65         self._stack = hildon.WindowStack.get_default()
66         self._mode = -1
67         self._last_dbus_orientation = None
68         app_id = '-'.join((app_name, self.__class__.__name__))
69         self._osso_context = osso.Context(app_id, version, False)
70         program = hildon.Program.get_instance()
71         program.connect('notify::is-topmost', self._on_topmost_changed)
72         system_bus = dbus.Bus.get_system()
73         system_bus.add_signal_receiver(self._on_orientation_signal, \
74                 signal_name='sig_device_orientation_ind', \
75                 dbus_interface='com.nokia.mce.signal', \
76                 path='/com/nokia/mce/signal')
77         self.set_mode(mode)
78
79     def get_mode(self):
80         """Get the currently-set rotation mode
81
82         This will return one of three values: AUTOMATIC, ALWAYS or NEVER.
83         """
84         return self._mode
85
86     def set_mode(self, new_mode):
87         """Set the rotation mode
88
89         You can set the rotation mode to AUTOMATIC (use hardware rotation
90         info), ALWAYS (force portrait) and NEVER (force landscape).
91         """
92         if new_mode not in (self.AUTOMATIC, self.ALWAYS, self.NEVER):
93             raise ValueError('Unknown rotation mode')
94
95         if self._mode != new_mode:
96             if self._mode == self.AUTOMATIC:
97                 # Remember the current "automatic" orientation for later
98                 self._last_dbus_orientation = self._orientation
99                 # Tell MCE that we don't need the accelerometer anymore
100                 self._send_mce_request(self._DISABLE_ACCEL)
101
102             if new_mode == self.NEVER:
103                 self._orientation_changed(self._LANDSCAPE)
104             elif new_mode == self.ALWAYS:
105                 self._orientation_changed(self._PORTRAIT)
106             elif new_mode == self.AUTOMATIC:
107                 # Restore the last-known "automatic" orientation
108                 self._orientation_changed(self._last_dbus_orientation)
109                 # Tell MCE that we need the accelerometer again
110                 self._send_mce_request(self._ENABLE_ACCEL)
111
112             self._mode = new_mode
113
114     def _send_mce_request(self, request):
115         rpc = osso.Rpc(self._osso_context)
116         rpc.rpc_run(self._MCE_SERVICE, \
117                     self._MCE_REQUEST_PATH, \
118                     self._MCE_REQUEST_IF, \
119                     request, \
120                     use_system_bus=True)
121
122     def _on_topmost_changed(self, program, property_spec):
123         # XXX: This seems to never get called on Fremantle(?)
124         if self._mode == self.AUTOMATIC:
125             if program.get_is_topmost():
126                 self._send_mce_request(self._ENABLE_ACCEL)
127             else:
128                 self._send_mce_request(self._DISABLE_ACCEL)
129
130     def _get_main_window(self):
131         if self._main_window:
132             # If we have gotten the main window as parameter, return it and
133             # don't try "harder" to find another window using the stack
134             return self._main_window
135         else:
136             # The main window is at the "bottom" of the window stack, and as
137             # the list we get with get_windows() is sorted "topmost first", we
138             # simply take the last item of the list to get our main window
139             windows = self._stack.get_windows()
140             if windows:
141                 return windows[-1]
142             else:
143                 return None
144
145     def _orientation_changed(self, orientation):
146         if self._orientation == orientation:
147             # Ignore repeated requests
148             return
149
150         flags = hildon.PORTRAIT_MODE_SUPPORT
151         if orientation == self._PORTRAIT:
152             flags |= hildon.PORTRAIT_MODE_REQUEST
153
154         window = self._get_main_window()
155         if window is not None:
156             hildon.hildon_gtk_window_set_portrait_flags(window, flags)
157
158         self._orientation = orientation
159
160     def _on_orientation_signal(self, orientation, stand, face, x, y, z):
161         if orientation in (self._PORTRAIT, self._LANDSCAPE):
162             if self._mode == self.AUTOMATIC:
163                 # Automatically set the rotation based on hardware orientation
164                 self._orientation_changed(orientation)
165             else:
166                 # Ignore orientation changes for non-automatic modes, but save
167                 # the current orientation for "automatic" mode later on
168                 self._last_dbus_orientation = orientation