Use list.sort's keyword parameters
[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         iv = self._image_view
80         if event.button == 1 and iv.get_zoom_mode() == iv.ZoomMode.BestFit:
81             self._goto(self._current_index + 1)
82
83     def _display_end_screen(self):
84         self._maybe_cancel_load()
85         self._image_view.hide()
86         self._info_label.show()
87
88     def _get_image_async_cb(self, size):
89         path = self._image_versions[self._current_index].getLocation()
90         self._maybe_cancel_load()
91         self._latest_handle = env.pixbufLoader.load(
92             path,
93             size,
94             self._image_view.set_from_pixbuf,
95             self._image_view.set_error)
96         if size != self._latest_size:
97             self._unload(self._latest_size)
98         self._preload(size)
99         self._latest_size = size
100
101     def _goto(self, new_index):
102         if new_index < 0:
103             self._current_index = -1
104             self._display_end_screen()
105         elif new_index >= len(self._image_versions):
106             self._current_index = len(self._image_versions)
107             self._display_end_screen()
108         else:
109             self._current_index = new_index
110             self._image_view.show()
111             self._info_label.hide()
112             self._image_view.set_image(self._get_image_async_cb)
113
114     def _hide_cursor(self):
115         pixmap = gtk.gdk.Pixmap(None, 1, 1, 1)
116         color = gtk.gdk.Color()
117         invisible_cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0)
118         self.window.set_cursor(invisible_cursor)
119
120     def _is_valid_index(self, index):
121         return 0 <= index < len(self._image_versions)
122
123     def _key_press_event_cb(self, unused, event):
124         k = gtk.keysyms
125         if event.state in [0, gtk.gdk.SHIFT_MASK]:
126             if event.keyval in [k.space, k.Right, k.Down, k.Page_Down]:
127                 self._goto(self._current_index + 1)
128                 return True
129             if event.keyval in [k.BackSpace, k.Left, k.Up, k.Page_Up]:
130                 self._goto(self._current_index - 1)
131                 return True
132             if event.keyval == k.Home:
133                 self._goto(0)
134                 return True
135             if event.keyval == k.End:
136                 self._goto(len(self._image_versions) - 1)
137                 return True
138             if event.keyval == k.Escape:
139                 self.destroy()
140                 return True
141             if event.keyval == k.plus:
142                 self._image_view.zoom_in()
143                 return True
144             if event.keyval == k.minus:
145                 self._image_view.zoom_out()
146                 return True
147             if event.keyval == k._0:
148                 self._image_view.zoom_to_actual()
149                 return True
150             if event.keyval == k.equal:
151                 self._image_view.zoom_to_fit()
152                 return True
153         return False
154
155     def _maybe_cancel_load(self):
156         if self._latest_handle is None:
157             # Nothing to cancel.
158             return
159         env.pixbufLoader.cancel_load(self._latest_handle)
160         self._latest_handle = None
161
162     def _preload(self, size):
163         self._preload_or_unload(size, True)
164
165     def _preload_or_unload(self, size, preload):
166         index = self._current_index
167         for x in [index + 2, index - 1, index + 1]:
168             if self._is_valid_index(x):
169                 location = self._image_versions[x].getLocation()
170                 if preload:
171                     env.pixbufLoader.preload(location, size)
172                 else:
173                     env.pixbufLoader.unload(location, size)
174
175     def _unload(self, size):
176         self._preload_or_unload(size, False)
177
178 gobject.type_register(FullScreenWindow) # TODO: Not needed in PyGTK 2.8.