Merge branch 'master' of https://git.maemo.org/projects/feedingit
[feedingit] / src / 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 import gconf
27
28
29 class FremantleRotation(object):
30     """thp's screen rotation for Maemo 5
31
32     Simply instantiate an object of this class and let it auto-rotate
33     your StackableWindows depending on the device orientation.
34
35     If you need to relayout a window, connect to its "configure-event"
36     signal and measure the ratio of width/height and relayout for that.
37
38     You can set the mode for rotation to AUTOMATIC (default), NEVER or
39     ALWAYS with the set_mode() method.
40     """
41     AUTOMATIC, NEVER, ALWAYS = range(3)
42
43     # Human-readable captions for the above constants
44     MODE_CAPTIONS = ("Automatic", "Landscape", "Portrait")
45
46     # Privately-used constants
47     _PORTRAIT, _LANDSCAPE = ('portrait', 'landscape')
48     _ENABLE_ACCEL = 'req_accelerometer_enable'
49     _DISABLE_ACCEL = 'req_accelerometer_disable'
50
51     # Defined in mce/dbus-names.h
52     _MCE_SERVICE = 'com.nokia.mce'
53     _MCE_REQUEST_PATH = '/com/nokia/mce/request'
54     _MCE_REQUEST_IF = 'com.nokia.mce.request'
55
56     def __init__(self, app_name, main_window=None, version='1.0', mode=0, app=None):
57         """Create a new rotation manager
58
59         app_name    ... The name of your application (for osso.Context)
60         main_window ... The root window (optional, hildon.StackableWindow)
61         version     ... The version of your application (optional, string)
62         mode        ... Initial mode for this manager (default: AUTOMATIC)
63         """
64         self._orientation = None
65         self._main_window = main_window
66         self._stack = hildon.WindowStack.get_default()
67         self._mode = -1
68         self.app = app
69         app_id = '-'.join((app_name, self.__class__.__name__))
70         self._osso_context = osso.Context(app_id, version, False)
71         self._last_dbus_orientation = self._get_current_orientation()
72         program = hildon.Program.get_instance()
73         program.connect('notify::is-topmost', self._on_topmost_changed)
74         system_bus = dbus.Bus.get_system()
75         system_bus.add_signal_receiver(self._on_orientation_signal, \
76                 signal_name='sig_device_orientation_ind', \
77                 dbus_interface='com.nokia.mce.signal', \
78                 path='/com/nokia/mce/signal')
79         self.set_mode(mode)
80
81     def _get_current_orientation(self):
82         """Return the current orientation
83         
84         Returns portrait if in portrait mode for sure, landscape if
85         in landscape mode or unknown.
86         """
87         if self._send_mce_request('get_device_orientation', True) \
88                 == self._PORTRAIT:
89             return self._PORTRAIT
90         else:
91             return self._LANDSCAPE
92
93     def get_mode(self):
94         """Get the currently-set rotation mode
95
96         This will return one of three values: AUTOMATIC, ALWAYS or NEVER.
97         """
98         return self._mode
99
100     def set_mode(self, new_mode):
101         """Set the rotation mode
102
103         You can set the rotation mode to AUTOMATIC (use hardware rotation
104         info), ALWAYS (force portrait) and NEVER (force landscape).
105         """
106         if new_mode not in (self.AUTOMATIC, self.ALWAYS, self.NEVER):
107             raise ValueError('Unknown rotation mode')
108
109         if self._mode != new_mode:
110             if self._mode == self.AUTOMATIC:
111                 # Remember the current "automatic" orientation for later
112                 self._last_dbus_orientation = self._orientation
113                 # Tell MCE that we don't need the accelerometer anymore
114                 self._send_mce_request(self._DISABLE_ACCEL)
115
116             if new_mode == self.NEVER:
117                 self._orientation_changed(self._LANDSCAPE)
118             elif new_mode == self.ALWAYS:
119                 self._orientation_changed(self._PORTRAIT)
120             elif new_mode == self.AUTOMATIC:
121                 # Restore the last-known "automatic" orientation
122                 self._orientation_changed(self._last_dbus_orientation)
123                 # Tell MCE that we need the accelerometer again
124                 self._send_mce_request(self._ENABLE_ACCEL)
125
126             self._mode = new_mode
127
128     def _send_mce_request(self, request, wait_reply=False):
129         rpc = osso.Rpc(self._osso_context)
130         return rpc.rpc_run(self._MCE_SERVICE,
131                     self._MCE_REQUEST_PATH,
132                     self._MCE_REQUEST_IF,
133                     request,
134                     wait_reply=wait_reply,
135                     use_system_bus=True)
136
137     def _on_topmost_changed(self, program, property_spec):
138         # XXX: This seems to never get called on Fremantle(?)
139         if self._mode == self.AUTOMATIC:
140             if program.get_is_topmost():
141                 self._send_mce_request(self._ENABLE_ACCEL)
142             else:
143                 self._send_mce_request(self._DISABLE_ACCEL)
144
145     def _get_main_window(self):
146         if self._main_window:
147             # If we have gotten the main window as parameter, return it and
148             # don't try "harder" to find another window using the stack
149             return self._main_window
150         else:
151             # The main window is at the "bottom" of the window stack, and as
152             # the list we get with get_windows() is sorted "topmost first", we
153             # simply take the last item of the list to get our main window
154             windows = self._stack.get_windows()
155             if windows:
156                 return windows[-1]
157             else:
158                 return None
159
160     def _orientation_changed(self, orientation):
161         if self._orientation == orientation:
162             # Ignore repeated requests
163             return
164
165         flags = 0
166
167         if orientation != self._LANDSCAPE:
168             flags |= hildon.PORTRAIT_MODE_SUPPORT
169
170         if orientation == self._PORTRAIT:
171             flags |= hildon.PORTRAIT_MODE_REQUEST
172
173         window = self._get_main_window()
174         if window is not None:
175             hildon.hildon_gtk_window_set_portrait_flags(window, flags)
176
177         self._orientation = orientation
178         #if orientation == self._PORTRAIT:
179         #    try:
180         #        self.app.disp.clear()
181         #        self.app.disp.displayFeed()
182         #    except:
183         #        pass
184
185     def _on_orientation_signal(self, orientation, stand, face, x, y, z):            
186         if orientation in (self._PORTRAIT, self._LANDSCAPE):
187             if (self._mode == self.AUTOMATIC) and (not gconf.client_get_default().get_bool('/system/osso/af/slide-open')):
188                 # Automatically set the rotation based on hardware orientation, if the keyboard is not open
189                 self._orientation_changed(orientation)
190             else:
191                 # Ignore orientation changes for non-automatic modes, but save
192                 # the current orientation for "automatic" mode later on
193                 self._last_dbus_orientation = orientation