Create the invisible mouse cursor in a simpler way.
[joel/kofoto.git] / src / packages / kofoto / gkofoto / fullscreenwindow.py
1 """This module contains the FullScreenWindow class."""
2
3 __all__ = ["FullScreenWindow"]
4
5 import gtk
6 import gobject
7 from kofoto.gkofoto.imageview import ImageView
8 from kofoto.gkofoto.environment import env
9
10 class FullScreenWindow(gtk.Window):
11     """A fullscreen window widget."""
12
13     def __init__(self, image_versions, current_index=0):
14         """Constructor.
15
16         Arguments:
17
18         image_versions -- A list of ImageVersion instance to display.
19         current_index  -- Where to start in image_versions.
20         """
21
22         self.__gobject_init__() # TODO: Use gtk.Window.__init__ in PyGTK 2.8.
23
24         self._last_allocated_size = None
25         self._image_versions = image_versions
26         self._current_index = current_index
27         self._latest_handle = None
28         self._latest_size = (0, 0)
29
30         bg_color = gtk.gdk.color_parse("#000000")
31         fg_color = gtk.gdk.color_parse("#999999")
32
33         eventbox = gtk.EventBox()
34         vbox = gtk.VBox()
35         eventbox.add(vbox)
36         self._image_view = ImageView()
37         self._image_view.set_error_pixbuf(
38             gtk.gdk.pixbuf_new_from_file(env.unknownImageIconFileName))
39         vbox.pack_start(self._image_view)
40         self._info_label = gtk.Label()
41         self._info_label.set_text(
42             "No more images.\nPress escape to get back to GKofoto.")
43         self._info_label.set_justify(gtk.JUSTIFY_CENTER)
44         self._info_label.hide()
45         vbox.pack_start(self._info_label)
46
47         self.modify_bg(gtk.STATE_NORMAL, bg_color)
48         eventbox.modify_bg(gtk.STATE_NORMAL, bg_color)
49         self._image_view.modify_bg(gtk.STATE_NORMAL, bg_color)
50         self._info_label.modify_fg(gtk.STATE_NORMAL, fg_color)
51         self.add(eventbox)
52         self.set_modal(True)
53         self.set_default_size(400, 400)
54         self.fullscreen()
55         self.connect_after("map-event", self._after_map_event_cb)
56         self.connect_after("size-allocate", self._after_size_allocate_cb)
57         eventbox.connect("button-press-event", self._button_press_event_cb)
58         self.connect("key-press-event", self._key_press_event_cb)
59
60     def destroy(self):
61         """Destroy the widget."""
62
63         self._maybe_cancel_load()
64         gtk.Window.destroy(self)
65
66     # ----------------------------------------
67
68     def _after_map_event_cb(self, *unused):
69         self._hide_cursor()
70
71     def _after_size_allocate_cb(self, widget, rect):
72         allocated_size = (rect.width, rect.height)
73         if allocated_size == self._last_allocated_size:
74             return
75         self._last_allocated_size = allocated_size
76         self._goto(self._current_index)
77
78     def _button_press_event_cb(self, widget, event):
79         if event.button == 1:
80             self._goto(self._current_index + 1)
81
82     def _display_end_screen(self):
83         self._maybe_cancel_load()
84         self._image_view.hide()
85         self._info_label.show()
86
87     def _get_image_async_cb(self, size):
88         path = self._image_versions[self._current_index].getLocation()
89         self._maybe_cancel_load()
90         self._latest_handle = env.pixbufLoader.load(
91             path,
92             size,
93             self._image_view.set_from_pixbuf,
94             self._image_view.set_error)
95         if size != self._latest_size:
96             self._unload(self._latest_size)
97         self._preload(size)
98         self._latest_size = size
99
100     def _goto(self, new_index):
101         if new_index < 0:
102             self._current_index = -1
103             self._display_end_screen()
104         elif new_index >= len(self._image_versions):
105             self._current_index = len(self._image_versions)
106             self._display_end_screen()
107         else:
108             self._current_index = new_index
109             self._image_view.show()
110             self._info_label.hide()
111             self._image_view.set_image(self._get_image_async_cb)
112
113     def _hide_cursor(self):
114         pixmap = gtk.gdk.Pixmap(None, 1, 1, 1)
115         color = gtk.gdk.Color()
116         invisible_cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0)
117         self.window.set_cursor(invisible_cursor)
118
119     def _is_valid_index(self, index):
120         return 0 <= index < len(self._image_versions)
121
122     def _key_press_event_cb(self, unused, event):
123         k = gtk.keysyms
124         if event.keyval in [k.space, k.Right, k.Down, k.Page_Down]:
125             self._goto(self._current_index + 1)
126             return True
127         if event.keyval in [k.BackSpace, k.Left, k.Up, k.Page_Up]:
128             self._goto(self._current_index - 1)
129             return True
130         if event.keyval == k.Home:
131             self._goto(0)
132             return True
133         if event.keyval == k.End:
134             self._goto(len(self._image_versions) - 1)
135             return True
136         if event.keyval == k.Escape:
137             self.destroy()
138             return True
139         return False
140
141     def _maybe_cancel_load(self):
142         if self._latest_handle is None:
143             # Nothing to cancel.
144             return
145         env.pixbufLoader.cancel_load(self._latest_handle)
146         self._latest_handle = None
147
148     def _preload(self, size):
149         self._preload_or_unload(size, True)
150
151     def _preload_or_unload(self, size, preload):
152         index = self._current_index
153         for x in [index + 2, index - 1, index + 1]:
154             if self._is_valid_index(x):
155                 location = self._image_versions[x].getLocation()
156                 if preload:
157                     env.pixbufLoader.preload(location, size)
158                 else:
159                     env.pixbufLoader.unload(location, size)
160
161     def _unload(self, size):
162         self._preload_or_unload(size, False)
163
164 gobject.type_register(FullScreenWindow) # TODO: Not needed in PyGTK 2.8.