1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 from cache_pixbuf import CachePixbuf
24 from constant import DEFAULT_FONT_SIZE, ALIGN_END, ALIGN_START
25 from contextlib import contextmanager
26 from draw import draw_pixbuf, draw_vlinear, draw_text
27 from keymap import get_keyevent_name, has_ctrl_mask, has_shift_mask
28 from skin_config import skin_config
29 from theme import ui_theme
30 import copy
31 import gobject
32 import gtk
33 import os
34 import pango
35 import subprocess
36 import tempfile
37 from utils import (map_value, mix_list_max, get_content_size,
38 unzip, last_index, set_cursor, get_match_parent,
39 remove_file,
40 cairo_state, get_event_coords, is_left_button,
41 is_right_button, is_double_click, is_single_click,
42 is_in_rect, get_disperse_index, get_window_shadow_size)
45 '''
46 Powerful listview widget.
47
48 @undocumented: update_redraw_request_list
49 @undocumented: set_adjust_cursor
50 @undocumented: realize_list_view
51 @undocumented: size_allocate_list_view
52 @undocumented: expose_list_view
53 @undocumented: motion_list_view
54 @undocumented: hover_item
55 @undocumented: button_press_list_view
56 @undocumented: click_item
57 @undocumented: button_release_list_view
58 @undocumented: release_item
59 @undocumented: drag_select_items_at_cursor
60 @undocumented: leave_list_view
61 @undocumented: key_press_list_view
62 @undocumented: key_release_list_view
63
64 '''
65
66 SORT_DESCENDING = False
67 SORT_ASCENDING = True
68 SORT_PADDING_X = 5
69 TITLE_PADDING = 5
70
71 __gsignals__ = {
72 "delete-select-items" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
73 "button-press-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int, int)),
74 "single-click-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int, int)),
75 "double-click-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int, int)),
76 "motion-notify-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int, int)),
77 "right-press-items" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (int, int, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
78 }
79
80 - def __init__(self,
81 sorts=[],
82 drag_data=None,
83 enable_multiple_select=True,
84 enable_drag_drop=True,
85 drag_icon_pixbuf=ui_theme.get_pixbuf("listview/drag_preview.png"),
86 drag_out_offset=50,
87 ):
88 '''
89 Initialize ListView widget.
90
91 @param sorts: Sort function for column of listview.
92 @param drag_data: Drag data for drag data from listview, format: (targets, actions, button_masks)
93 @param enable_multiple_select: Whether allow user select multiple item, default is True.
94 @param enable_drag_drop: Whether allow user drag drop on listview, default is True.
95 @param drag_icon_pixbuf: Drag icon.
96 @param drag_out_offset: Out offset value to trigger drag action on listview, default is 50 pixel, if cursor not drag more than 50 pixel, listview won't think it is B{drag out action}.
97 '''
98
99 gtk.DrawingArea.__init__(self)
100 self.sorts = sorts
101 self.drag_data = drag_data
102 self.add_events(gtk.gdk.ALL_EVENTS_MASK)
103 self.set_can_focus(True)
104 self.items = []
105 self.cell_widths = []
106 self.cell_min_widths = []
107 self.cell_min_heights = []
108 self.left_button_press = False
109 self.hover_row = None
110 self.titles = None
111 self.title_sorts = None
112 self.single_click_row = None
113 self.double_click_row = None
114 self.start_select_item = None
115 self.enable_drag_drop = enable_drag_drop
116 self.start_drag = False
117 self.drag_item = None
118 self.highlight_item = None
119 self.before_drag_items = []
120 self.after_drag_items = []
121 self.title_offset_y = 0
122 self.item_height = 0
123 self.press_ctrl = False
124 self.press_shift = False
125 self.select_rows = []
126 self.start_select_row = None
127 self.press_in_select_rows = None
128 self.expand_column = None
129 self.drag_reference_row = None
130 self.drag_preview_pixbuf = None
131 self.drag_line_pixbuf = CachePixbuf()
132 self.enable_multiple_select = enable_multiple_select
133 self.drag_icon_pixbuf = drag_icon_pixbuf
134 self.drag_out_offset = drag_out_offset
135
136
137 self.connect("realize", self.realize_list_view)
138 self.connect("size-allocate", self.size_allocate_list_view)
139 self.connect("expose-event", self.expose_list_view)
140 self.connect("motion-notify-event", self.motion_list_view)
141 self.connect("button-press-event", self.button_press_list_view)
142 self.connect("button-release-event", self.button_release_list_view)
143 self.connect("leave-notify-event", self.leave_list_view)
144 self.connect("key-press-event", self.key_press_list_view)
145 self.connect("key-release-event", self.key_release_list_view)
146
147
148 if self.drag_data:
149
150 self.drag_source_unset()
151
152
153 self.redraw_request_list = []
154 self.redraw_delay = 100
155 gtk.timeout_add(self.redraw_delay, self.update_redraw_request_list)
156
157
158 self.keymap = {
159 "Home" : self.select_first_item,
160 "End" : self.select_last_item,
161 "Page_Up" : self.scroll_page_up,
162 "Page_Down" : self.scroll_page_down,
163 "Return" : self.double_click_item,
164 "Up" : self.select_prev_item,
165 "Down" : self.select_next_item,
166 "Delete" : self.delete_select_items,
167 "Shift + Up" : self.select_to_prev_item,
168 "Shift + Down" : self.select_to_next_item,
169 "Shift + Home" : self.select_to_first_item,
170 "Shift + End" : self.select_to_last_item,
171 "Ctrl + a" : self.select_all_items,
172 }
173
175 '''
176 Set expand column.
177
178 @param column: Column index to expand space.
179 '''
180 self.expand_column = column
181
183 '''
184 Internal fucntion to update redraw request list.
185 '''
186
187 if len(self.redraw_request_list) > 0:
188
189 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
190
191
192 start_y = offset_y - self.title_offset_y
193 end_y = offset_y + viewport.allocation.height - self.title_offset_y
194 start_index = max(start_y / self.item_height, 0)
195 if (end_y - end_y / self.item_height * self.item_height) == 0:
196 end_index = min(end_y / self.item_height + 1, len(self.items))
197 else:
198 end_index = min(end_y / self.item_height + 2, len(self.items))
199
200
201 viewport_range = range(start_index, end_index)
202 for item in self.redraw_request_list:
203 if item.get_index() in viewport_range:
204 self.queue_draw()
205 break
206
207
208 self.redraw_request_list = []
209
210 return True
211
213 '''
214 Add titles.
215
216 @param titles: A list of title.
217 @param title_height: Height of title.
218 '''
219 self.titles = titles
220 self.title_select_column = None
221 self.title_adjust_column = None
222 self.title_separator_width = 2
223 self.title_clicks = map_value(self.titles, lambda _: False)
224 self.title_sort_column = None
225 self.title_sorts = map_value(self.titles, lambda _: self.SORT_DESCENDING)
226 self.set_title_height(title_height)
227
228 (title_widths, title_heights) = self.get_title_sizes()
229 self.cell_widths = mix_list_max(self.cell_widths, title_widths)
230 self.cell_min_widths = mix_list_max(self.cell_min_widths, title_widths)
231 self.cell_min_heights = mix_list_max(self.cell_min_heights, title_heights)
232
233 self.title_cache_pixbufs = []
234 for title in self.titles:
235 self.title_cache_pixbufs.append(CachePixbuf())
236
238 '''
239 Get title sizes.
240
241 @return: Return title size, as format (title_width, title_height).
242 '''
243 widths = []
244 heights = []
245 if self.titles != None:
246 for title in self.titles:
247 (title_width, title_height) = get_content_size(title, DEFAULT_FONT_SIZE)
248 widths.append(title_width + self.TITLE_PADDING * 2)
249 heights.append(title_height)
250
251 return (widths, heights)
252
253 - def add_items(self, items, insert_pos=None, sort_list=False):
254 '''
255 Add items in listview.
256
257 @param items: A list of item.
258 @param insert_pos: The position to insert, default is None will insert new item at end of list.
259 @param sort_list: Whether sort list after insert, default is False.
260 '''
261
262 with self.keep_select_status():
263 if insert_pos == None:
264 self.items += items
265 else:
266 self.items = self.items[0:insert_pos] + items + self.items[insert_pos::]
267
268
269 (title_widths, title_heights) = self.get_title_sizes()
270 sort_pixbuf = ui_theme.get_pixbuf("listview/sort_descending.png").get_pixbuf()
271 sort_icon_width = sort_pixbuf.get_width() + self.SORT_PADDING_X * 2
272 sort_icon_height = sort_pixbuf.get_height()
273
274 cell_min_sizes = []
275 for item in items:
276
277 item.connect("redraw_request", self.redraw_item)
278
279 sizes = item.get_column_sizes()
280 if cell_min_sizes == []:
281 cell_min_sizes = sizes
282 else:
283 for (index, (width, height)) in enumerate(sizes):
284 if self.titles == None:
285 max_width = max([cell_min_sizes[index][0], width])
286 max_height = max([cell_min_sizes[index][1], sort_icon_height, height])
287 else:
288 max_width = max([cell_min_sizes[index][0], title_widths[index] + sort_icon_width * 2, width])
289 max_height = max([cell_min_sizes[index][1], title_heights[index], sort_icon_height, height])
290
291 cell_min_sizes[index] = (max_width, max_height)
292
293
294 (cell_min_widths, cell_min_heights) = unzip(cell_min_sizes)
295 self.cell_min_widths = mix_list_max(self.cell_min_widths, cell_min_widths)
296 self.cell_min_heights = mix_list_max(self.cell_min_heights, cell_min_heights)
297 self.cell_widths = mix_list_max(self.cell_widths, copy.deepcopy(cell_min_widths))
298
299 self.item_height = max(self.item_height, max(copy.deepcopy(cell_min_heights)))
300
301
302 if sort_list and self.sorts != [] and self.title_sort_column != None:
303 if self.title_sorts == None:
304 reverse_order = False
305 else:
306 reverse_order = self.title_sorts[0]
307
308 with self.keep_select_status():
309 self.items = sorted(self.items,
310 key=self.sorts[self.title_sort_column][0],
311 cmp=self.sorts[self.title_sort_column][1],
312 reverse=reverse_order)
313
314
315 self.update_vadjustment()
316
317
318 self.update_item_index()
319
320 - def sort_items(self, compare_method, sort_reverse=False):
321 '''
322 Sort items with given method.
323
324 @param compare_method: Compare method to sort.
325 @param sort_reverse: Whether sort reverse, default is False.
326 '''
327
328 with self.keep_select_status():
329 self.items = sorted(self.items,
330 cmp=compare_method,
331 reverse=sort_reverse)
332
333
334 self.update_item_index()
335
336
337 self.queue_draw()
338
340 '''
341 Redraw item.
342
343 @param list_item: List item need to redraw.
344 '''
345 self.redraw_request_list.append(list_item)
346
348 '''
349 Update index of items.
350 '''
351 for (index, item) in enumerate(self.items):
352 item.set_index(index)
353
355 '''
356 Set title height.
357 '''
358 self.title_height = title_height
359 if self.titles:
360 self.title_offset_y = self.title_height
361 else:
362 self.title_offset_y = 0
363
365 '''
366 Get sort type with given column index.
367
368 @param column: Column index.
369
370 @return: Return sort type with given column index, return None if haven't found match column index.
371 '''
372 if 0 <= column <= last_index(self.title_sorts):
373 return self.title_sorts[column]
374 else:
375 return None
376
378 '''
379 Set sort type with given value.
380
381 @param column: Column index.
382 @param sort_type: Sort type.
383 '''
384 if 0 <= column <= last_index(self.title_sorts):
385 self.title_sorts[column] = sort_type
386
388 '''
389 Get cell width of columns.
390 '''
391 return self.cell_widths
392
394 '''
395 Set cell width with given value.
396
397 @param column: Column index.
398 @param width: Column width.
399 '''
400 if column <= last_index(self.cell_min_widths) and width >= self.cell_min_widths[column]:
401 self.cell_widths[column] = width
402
404 '''
405 Internal function to set cursor type when adjust size.
406 '''
407 set_cursor(self, gtk.gdk.SB_H_DOUBLE_ARROW)
408 self.adjust_cursor = True
409
411 '''
412 Reset cursor type.
413 '''
414 set_cursor(self, None)
415 self.adjust_cursor = False
416
418 '''
419 Get viewport offset coordinate and viewport.
420
421 @param widget: ListView widget.
422 @return: Return viewport offset and viewport: (offset_x, offset_y, viewport).
423 '''
424
425 rect = widget.allocation
426
427
428 viewport = get_match_parent(widget, ["Viewport"])
429 if viewport:
430 coordinate = widget.translate_coordinates(viewport, rect.x, rect.y)
431 if len(coordinate) == 2:
432 (offset_x, offset_y) = coordinate
433 return (-offset_x, -offset_y, viewport)
434 else:
435 return (0, 0, viewport)
436
437 else:
438 return (0, 0, viewport)
439
441 '''
442 Shadow mask interface for overwrite.
443
444 @param cr: Cairo context.
445 @param x: X coordiante of draw area.
446 @param y: Y coordiante of draw area.
447 @param w: Width of draw area.
448 @param h: Height of draw area.
449 '''
450 pass
451
453 '''
454 Draw mask interface.
455
456 @param cr: Cairo context.
457 @param x: X coordiante of draw area.
458 @param y: Y coordiante of draw area.
459 @param w: Width of draw area.
460 @param h: Height of draw area.
461 '''
462 draw_vlinear(cr, x, y, w, h,
463 ui_theme.get_shadow_color("linear_background").get_color_info()
464 )
465
467 '''
468 Draw item hover interface.
469
470 @param cr: Cairo context.
471 @param x: X coordiante of draw area.
472 @param y: Y coordiante of draw area.
473 @param w: Width of draw area.
474 @param h: Height of draw area.
475 '''
476 draw_vlinear(cr, x, y, w, h, ui_theme.get_shadow_color("listview_hover").get_color_info())
477
479 '''
480 Draw item select interface.
481
482 @param cr: Cairo context.
483 @param x: X coordiante of draw area.
484 @param y: Y coordiante of draw area.
485 @param w: Width of draw area.
486 @param h: Height of draw area.
487 '''
488 draw_vlinear(cr, x, y, w, h, ui_theme.get_shadow_color("listview_select").get_color_info())
489
491 '''
492 Draw item highlight interface.
493
494 @param cr: Cairo context.
495 @param x: X coordiante of draw area.
496 @param y: Y coordiante of draw area.
497 @param w: Width of draw area.
498 @param h: Height of draw area.
499 '''
500 draw_vlinear(cr, x, y, w, h, ui_theme.get_shadow_color("listview_highlight").get_color_info())
501
503 '''
504 Internal fucntion for realize listview.
505
506 @param widget: ListView wiget.
507 '''
508 self.grab_focus()
509
510 rect = widget.allocation
511 if 0 <= self.expand_column < len(self.cell_widths):
512 self.set_cell_width(self.expand_column, rect.width - (sum(self.cell_widths) - self.cell_widths[self.expand_column]))
513
515 '''
516 Internal callback for `size_allocated` signal.
517
518 @param widget: ListView widget.
519 @param allocation: ListView allocation.
520 '''
521 rect = widget.allocation
522 if 0 <= self.expand_column < len(self.cell_widths):
523 self.set_cell_width(self.expand_column, rect.width - (sum(self.cell_widths) - self.cell_widths[self.expand_column]))
524
526 '''
527 Internal callback for `expose-event` signal.
528
529 @param widget: ListView widget.
530 @param event: Expose event.
531 '''
532
533 cr = widget.window.cairo_create()
534 rect = widget.allocation
535 cell_widths = self.get_cell_widths()
536
537
538 (offset_x, offset_y, viewport) = self.get_offset_coordinate(widget)
539
540
541 with cairo_state(cr):
542 scrolled_window = get_match_parent(self, ["ScrolledWindow"])
543 cr.translate(-scrolled_window.allocation.x, -scrolled_window.allocation.y)
544 cr.rectangle(offset_x, offset_y,
545 scrolled_window.allocation.x + scrolled_window.allocation.width,
546 scrolled_window.allocation.y + scrolled_window.allocation.height)
547 cr.clip()
548
549 (shadow_x, shadow_y) = get_window_shadow_size(self.get_toplevel())
550 skin_config.render_background(cr, self, offset_x + shadow_x, offset_y + shadow_y)
551
552
553 self.draw_mask(cr, offset_x, offset_y, viewport.allocation.width, viewport.allocation.height)
554
555 if len(self.items) > 0:
556 with cairo_state(cr):
557
558 cr.rectangle(offset_x, offset_y + self.title_offset_y,
559 viewport.allocation.width, viewport.allocation.height - self.title_offset_y)
560 cr.clip()
561
562
563 highlight_row = None
564 if self.highlight_item:
565 highlight_row = self.highlight_item.get_index()
566
567 if self.hover_row != None and not self.hover_row in self.select_rows and self.hover_row != highlight_row:
568 self.draw_item_hover(
569 cr, offset_x, self.title_offset_y + self.hover_row * self.item_height,
570 viewport.allocation.width, self.item_height)
571
572
573 for select_row in self.select_rows:
574 if select_row != highlight_row:
575 self.draw_item_select(
576 cr, offset_x, self.title_offset_y + select_row * self.item_height,
577 viewport.allocation.width, self.item_height)
578
579
580 if self.highlight_item:
581 self.draw_item_highlight(
582 cr, offset_x, self.title_offset_y + self.highlight_item.get_index() * self.item_height,
583 viewport.allocation.width, self.item_height)
584
585
586 start_y = offset_y - self.title_offset_y
587 end_y = offset_y + viewport.allocation.height - self.title_offset_y
588 start_index = max(start_y / self.item_height, 0)
589 if (end_y - end_y / self.item_height * self.item_height) == 0:
590 end_index = min(end_y / self.item_height + 1, len(self.items))
591 else:
592 end_index = min(end_y / self.item_height + 2, len(self.items))
593
594
595 for (row, item) in enumerate(self.items[start_index:end_index]):
596 renders = item.get_renders()
597 for (column, render) in enumerate(renders):
598 cell_width = cell_widths[column]
599 cell_x = sum(cell_widths[0:column])
600 render_x = rect.x + cell_x
601 render_y = rect.y + (row + start_index) * self.item_height + self.title_offset_y
602 render_width = cell_width
603 render_height = self.item_height
604
605 with cairo_state(cr):
606
607 cr.rectangle(render_x, render_y, render_width, render_height)
608 cr.clip()
609
610
611 render(cr, gtk.gdk.Rectangle(render_x, render_y, render_width, render_height),
612 (start_index + row) in self.select_rows,
613 item == self.highlight_item)
614
615
616
617 if self.titles:
618 for (column, width) in enumerate(cell_widths):
619
620 cell_offset_x = sum(cell_widths[0:column])
621
622
623 if column == last_index(cell_widths):
624 if sum(cell_widths) < rect.width:
625 cell_width = rect.width - cell_offset_x
626 else:
627 cell_width = width
628 else:
629 cell_width = width
630
631
632 if self.title_select_column == column:
633 if self.left_button_press:
634 header_pixbuf = ui_theme.get_pixbuf("listview/header_press.png").get_pixbuf()
635 else:
636 header_pixbuf = ui_theme.get_pixbuf("listview/header_hover.png").get_pixbuf()
637 else:
638 header_pixbuf = ui_theme.get_pixbuf("listview/header_normal.png").get_pixbuf()
639 self.title_cache_pixbufs[column].scale(
640 header_pixbuf, cell_width, self.title_height)
641 draw_pixbuf(cr,
642 self.title_cache_pixbufs[column].get_cache(),
643 cell_offset_x, offset_y)
644
645
646 if cell_offset_x != 0:
647 draw_pixbuf(cr,
648 ui_theme.get_pixbuf("listview/split.png").get_pixbuf(),
649 cell_offset_x - 1, offset_y)
650
651
652 draw_text(cr, self.titles[column],
653 cell_offset_x, offset_y, cell_widths[column], self.title_height,
654 DEFAULT_FONT_SIZE,
655 ui_theme.get_color("list_view_title").get_color(),
656 alignment=pango.ALIGN_CENTER)
657
658
659 if self.title_sort_column == column:
660 sort_type = self.get_column_sort_type(column)
661 if sort_type == self.SORT_DESCENDING:
662 sort_pixbuf = ui_theme.get_pixbuf("listview/sort_descending.png").get_pixbuf()
663 elif sort_type == self.SORT_ASCENDING:
664 sort_pixbuf = ui_theme.get_pixbuf("listview/sort_ascending.png").get_pixbuf()
665
666 draw_pixbuf(cr, sort_pixbuf,
667 cell_offset_x + cell_width - sort_pixbuf.get_width() - self.SORT_PADDING_X,
668 offset_y + (self.title_height - sort_pixbuf.get_height()) / 2)
669
670
671 self.draw_shadow_mask(cr, offset_x, offset_y, viewport.allocation.width, viewport.allocation.height)
672
673
674 if self.drag_reference_row != None:
675 drag_pixbuf = ui_theme.get_pixbuf("listview/drag_line.png").get_pixbuf()
676 self.drag_line_pixbuf.scale(drag_pixbuf, rect.width, drag_pixbuf.get_height())
677 if self.drag_reference_row == 0:
678 drag_line_y = rect.y + self.title_offset_y
679 elif self.drag_reference_row == len(self.items):
680 drag_line_y = rect.y + (self.drag_reference_row) * self.item_height + self.title_offset_y - drag_pixbuf.get_height()
681 else:
682 drag_line_y = rect.y + self.drag_reference_row * self.item_height + self.title_offset_y
683
684 draw_pixbuf(cr, self.drag_line_pixbuf.get_cache(), rect.x, drag_line_y)
685
686 return False
687
689 '''
690 Internal callback for `motion-notify-event` signal.
691
692 @param widget: ListView widget.
693 @param event: Motion event.
694 '''
695 if self.titles:
696
697 (offset_x, offset_y, viewport) = self.get_offset_coordinate(widget)
698
699 if self.title_adjust_column != None:
700
701 cell_min_end_x = sum(self.cell_widths[0:self.title_adjust_column]) + self.cell_min_widths[self.title_adjust_column]
702
703 (ex, ey) = get_event_coords(event)
704 if ex >= cell_min_end_x:
705 self.set_cell_width(self.title_adjust_column, ex - sum(self.cell_widths[0:self.title_adjust_column]))
706 else:
707 if offset_y <= event.y <= offset_y + self.title_height:
708 cell_widths = self.get_cell_widths()
709 for (column, _) in enumerate(cell_widths):
710 if column == last_index(cell_widths):
711 cell_start_x = widget.allocation.width
712 cell_end_x = widget.allocation.width
713 else:
714 cell_start_x = sum(cell_widths[0:column + 1]) - self.title_separator_width
715 cell_end_x = sum(cell_widths[0:column + 1]) + self.title_separator_width
716
717 if event.x < cell_start_x:
718 self.title_select_column = column
719 self.reset_cursor()
720 break
721 elif cell_start_x <= event.x <= cell_end_x:
722 self.title_select_column = None
723 self.set_adjust_cursor()
724 break
725 elif len(self.items) > 0:
726 self.hover_item(event)
727 elif len(self.items) > 0:
728 self.hover_item(event)
729
730
731 self.press_in_select_rows = None
732
733
734 self.queue_draw()
735
737 '''
738 Internal function to handle hover item.
739
740 @param event: Motion notify event.
741 '''
742 if self.left_button_press:
743 if self.start_drag:
744 if self.enable_drag_drop:
745
746 if self.drag_preview_pixbuf == None:
747 temp_filepath = tempfile.mktemp()
748 subprocess.Popen(
749 ["python",
750 os.path.join(os.path.dirname(__file__), "listview_preview_pixbuf.py"),
751 str(len(self.select_rows)),
752 str([(0, ("#40408c", 1)),
753 (1, ("#0093F9", 1))]),
754 "#FFFFFF",
755 temp_filepath]).wait()
756 drag_num_pixbuf = gtk.gdk.pixbuf_new_from_file(temp_filepath)
757 drag_icon_pixbuf = self.drag_icon_pixbuf.get_pixbuf()
758 drag_num_pixbuf.copy_area(
759 0, 0, drag_num_pixbuf.get_width(), drag_num_pixbuf.get_height(),
760 drag_icon_pixbuf,
761 (drag_icon_pixbuf.get_width() - drag_num_pixbuf.get_width()) / 2,
762 drag_icon_pixbuf.get_height() - drag_num_pixbuf.get_height())
763 self.drag_preview_pixbuf = drag_icon_pixbuf
764 remove_file(temp_filepath)
765
766 self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.display_get_default(),
767 self.drag_preview_pixbuf,
768 0, 0))
769
770
771 if self.is_in_visible_area(event):
772
773 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
774 if event.y > vadjust.get_value() + vadjust.get_page_size() - 2 * self.item_height:
775 vadjust.set_value(min(vadjust.get_value() + self.item_height,
776 vadjust.get_upper() - vadjust.get_page_size()))
777 elif event.y < vadjust.get_value() + 2 * self.item_height + self.title_offset_y:
778 vadjust.set_value(max(vadjust.get_value() - self.item_height,
779 vadjust.get_lower()))
780
781
782 self.drag_reference_row = self.get_event_row(event, 1)
783
784 self.queue_draw()
785 else:
786
787 if self.drag_data:
788 (targets, actions, button) = self.drag_data
789 self.drag_begin(targets, actions, button, event)
790
791 self.drag_reference_row = None
792
793 self.queue_draw()
794 else:
795 if self.enable_multiple_select and (not self.press_ctrl and not self.press_shift):
796
797 hover_row = self.get_event_row(event)
798
799
800 if hover_row != None and self.start_select_row != None:
801
802 if hover_row > self.start_select_row:
803 self.select_rows = range(self.start_select_row, hover_row + 1)
804 elif hover_row < self.start_select_row:
805 self.select_rows = range(hover_row, self.start_select_row + 1)
806 else:
807 self.select_rows = [hover_row]
808
809
810 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
811 if event.y > vadjust.get_value() + vadjust.get_page_size() - 2 * self.item_height:
812 vadjust.set_value(min(vadjust.get_value() + self.item_height,
813 vadjust.get_upper() - vadjust.get_page_size()))
814 elif event.y < vadjust.get_value() + 2 * self.item_height + self.title_offset_y:
815 vadjust.set_value(max(vadjust.get_value() - self.item_height,
816 vadjust.get_lower()))
817
818 self.queue_draw()
819 else:
820
821 self.title_select_column = None
822 self.reset_cursor()
823
824
825 self.hover_row = self.get_event_row(event)
826
827
828 self.emit_item_event("motion-notify-item", event)
829
874
876 '''
877 Internal function to handle click item.
878
879 @param event: Button press event.
880 '''
881 click_row = self.get_event_row(event)
882
883 if self.left_button_press:
884 if click_row == None:
885 self.start_select_row = None
886 self.select_rows = []
887 else:
888 if self.press_shift:
889 if self.select_rows == [] or self.start_select_row == None:
890 self.start_select_row = click_row
891 self.select_rows = [click_row]
892 else:
893 if len(self.select_rows) == 1:
894 self.start_select_row = self.select_rows[0]
895
896 if click_row < self.start_select_row:
897 self.select_rows = range(click_row, self.start_select_row + 1)
898 elif click_row > self.start_select_row:
899 self.select_rows = range(self.start_select_row, click_row + 1)
900 else:
901 self.select_rows = [click_row]
902 elif self.press_ctrl:
903 if click_row in self.select_rows:
904 self.select_rows.remove(click_row)
905 else:
906 self.start_select_row = click_row
907 self.select_rows.append(click_row)
908 self.select_rows = sorted(self.select_rows)
909 else:
910 if self.enable_drag_drop and click_row in self.select_rows:
911 self.start_drag = True
912
913 if self.start_select_row:
914 self.start_select_item = self.items[self.start_select_row]
915
916 self.before_drag_items = []
917 self.after_drag_items = []
918
919 for row in self.select_rows:
920 if row == click_row:
921 self.drag_item = self.items[click_row]
922 elif row < click_row:
923 self.before_drag_items.append(self.items[row])
924 elif row > click_row:
925 self.after_drag_items.append(self.items[row])
926
927
928 self.press_in_select_rows = click_row
929 else:
930 self.start_drag = False
931
932 self.start_select_row = click_row
933 self.select_rows = [click_row]
934 self.emit_item_event("button-press-item", event)
935
936 if is_double_click(event):
937 self.double_click_row = copy.deepcopy(click_row)
938 elif is_single_click(event):
939 self.single_click_row = copy.deepcopy(click_row)
940 else:
941 right_press_row = self.get_event_row(event)
942 if right_press_row == None:
943 self.start_select_row = None
944 self.select_rows = []
945
946 self.queue_draw()
947 elif not right_press_row in self.select_rows:
948 self.start_select_row = right_press_row
949 self.select_rows = [right_press_row]
950
951 self.queue_draw()
952
953
954 if self.start_select_row == None:
955 current_item = None
956 else:
957 current_item = self.items[self.start_select_row]
958
959 select_items = []
960 for row in self.select_rows:
961 select_items.append(self.items[row])
962
963 (wx, wy) = self.window.get_root_origin()
964 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
965 self.emit("right-press-items",
966 event.x_root,
967 event.y_root,
968 current_item,
969 select_items)
970
1022
1023 @contextmanager
1025 '''
1026 Handy function that change listview and keep select status not change.
1027 '''
1028
1029 start_select_item = None
1030 if self.start_select_row != None:
1031 start_select_item = self.items[self.start_select_row]
1032
1033 select_items = []
1034 for row in self.select_rows:
1035 select_items.append(self.items[row])
1036
1037 try:
1038 yield
1039 except Exception, e:
1040 print 'with an cairo error %s' % e
1041 else:
1042
1043 if start_select_item != None or select_items != []:
1044
1045 if start_select_item != None:
1046 self.start_select_row = None
1047
1048
1049 if select_items != []:
1050 self.select_rows = []
1051
1052 for (index, item) in enumerate(self.items):
1053
1054 if item in select_items:
1055 self.select_rows.append(index)
1056 select_items.remove(item)
1057
1058
1059 if item == start_select_item:
1060 self.start_select_row = index
1061 start_select_item = None
1062
1063
1064 if select_items == [] and start_select_item == None:
1065 break
1066
1068 '''
1069 Internal function to handle release item.
1070
1071 @param event: Button release event.
1072 '''
1073 if is_left_button(event):
1074 release_row = self.get_event_row(event)
1075
1076 if self.double_click_row == release_row:
1077 self.emit_item_event("double-click-item", event)
1078 elif self.single_click_row == release_row:
1079 self.emit_item_event("single-click-item", event)
1080
1081 if self.start_drag and self.is_in_visible_area(event):
1082 self.drag_select_items_at_cursor(event)
1083
1084 self.reset_cursor()
1085 self.double_click_row = None
1086 self.single_click_row = None
1087 self.start_drag = False
1088
1089
1090 if self.press_in_select_rows:
1091 self.start_select_row = self.press_in_select_rows
1092 self.select_rows = [self.press_in_select_rows]
1093
1094 self.press_in_select_rows = None
1095
1096 self.queue_draw()
1097
1099 '''
1100 Is event coordinate in visible area.
1101
1102 @param event: gtk.gdk.Event.
1103
1104 @return: Return True if event coordiante in visible area.
1105 '''
1106 (event_x, event_y) = get_event_coords(event)
1107 scrolled_window = get_match_parent(self, ["ScrolledWindow"])
1108 vadjust = scrolled_window.get_vadjustment()
1109 return (-self.drag_out_offset <= event_x <= scrolled_window.allocation.width + self.drag_out_offset
1110 and vadjust.get_value() - self.drag_out_offset <= event_y <= vadjust.get_value() + vadjust.get_page_size() + self.drag_out_offset)
1111
1113 '''
1114 Internal function to drag select items at cursor position.
1115 '''
1116 (event_x, event_y) = get_event_coords(event)
1117 hover_row = min(max(int((event_y - self.title_offset_y) / self.item_height), 0),
1118 len(self.items))
1119
1120
1121 filter_items = self.before_drag_items + [self.drag_item] + self.after_drag_items
1122
1123 before_items = []
1124 for item in self.items[0:hover_row]:
1125 if not item in filter_items:
1126 before_items.append(item)
1127
1128 after_items = []
1129 for item in self.items[hover_row::]:
1130 if not item in filter_items:
1131 after_items.append(item)
1132
1133
1134 self.items = before_items + self.before_drag_items + [self.drag_item] + self.after_drag_items + after_items
1135
1136
1137 self.select_rows = range(len(before_items), len(self.items) - len(after_items))
1138
1139
1140 for row in self.select_rows:
1141 if self.items[row] == self.start_select_item:
1142 self.start_select_row = row
1143 break
1144
1145
1146
1147 self.update_item_index()
1148
1149
1150 self.queue_draw()
1151
1153 '''
1154 Internal callback for `leave-notify-event` signal.
1155
1156 @param widget: ListView widget.
1157 @param event: Leave notify event.
1158 '''
1159
1160 self.title_select_column = None
1161 self.title_adjust_column = None
1162 if not self.left_button_press:
1163 self.reset_cursor()
1164
1165
1166 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1167 hadjust = get_match_parent(self, ["ScrolledWindow"]).get_hadjustment()
1168 if not is_in_rect((event.x, event.y),
1169 (hadjust.get_value(), vadjust.get_value(), hadjust.get_page_size(), vadjust.get_page_size())):
1170 self.hover_row = None
1171
1172
1173 self.queue_draw()
1174
1176 '''
1177 Internal callback for `key-press-event` signal.
1178
1179 @param widget: ListView widget.
1180 @param event: Key press event.
1181 '''
1182 if has_ctrl_mask(event):
1183 self.press_ctrl = True
1184
1185 if has_shift_mask(event):
1186 self.press_shift = True
1187
1188 key_name = get_keyevent_name(event)
1189 if self.keymap.has_key(key_name):
1190 self.keymap[key_name]()
1191
1192
1193 if self.hover_row and not has_ctrl_mask(event) and not has_shift_mask(event):
1194 self.hover_row = None
1195 self.queue_draw()
1196
1197 return True
1198
1200 '''
1201 Internal callback for `key-release-event` signal.
1202
1203 @param widget: ListView widget.
1204 @param event: Key release event.
1205 '''
1206 if has_ctrl_mask(event):
1207 self.press_ctrl = False
1208
1209 if has_shift_mask(event):
1210 self.press_shift = False
1211
1213 '''
1214 Wrap method for emit event signal.
1215
1216 @param event_name: Event name.
1217 @param event: Event.
1218 '''
1219 (event_x, event_y) = get_event_coords(event)
1220 event_row = (event_y - self.title_offset_y) / self.item_height
1221 if 0 <= event_row <= last_index(self.items):
1222 offset_y = event_y - event_row * self.item_height - self.title_offset_y
1223 (event_column, offset_x) = get_disperse_index(self.cell_widths, event_x)
1224
1225 self.emit(event_name, self.items[event_row], event_column, offset_x, offset_y)
1226
1228 '''
1229 Get row with given y coordinate.
1230
1231 @param y: Y coordinate.
1232 @return: Return row that match given y coordinate, return None if haven't any row match y coordiante.
1233 '''
1234 row = int((y - self.title_offset_y) / self.item_height)
1235 if 0 <= row <= last_index(self.items):
1236 return row
1237 else:
1238 return None
1239
1241 '''
1242 Get row at event.
1243
1244 @param event: gtk.gdk.Event instance.
1245 @param offset_index: Offset index base on event row.
1246 @return: Return row at event coordinate, return None if haven't any row match event coordiante.
1247 '''
1248 (event_x, event_y) = get_event_coords(event)
1249 row = int((event_y - self.title_offset_y) / self.item_height)
1250 if 0 <= row <= last_index(self.items) + offset_index:
1251 return row
1252 else:
1253 return None
1254
1256 '''
1257 Select first item.
1258 '''
1259 if len(self.items) > 0:
1260
1261 self.start_select_row = 0
1262 self.select_rows = [0]
1263
1264
1265 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1266 vadjust.set_value(vadjust.get_lower())
1267
1268
1269 self.queue_draw()
1270
1272 '''
1273 Select last item.
1274 '''
1275 if len(self.items) > 0:
1276
1277 last_row = last_index(self.items)
1278 self.start_select_row = last_row
1279 self.select_rows = [last_row]
1280
1281
1282 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1283 vadjust.set_value(vadjust.get_upper() - vadjust.get_page_size())
1284
1285
1286 self.queue_draw()
1287
1289 '''
1290 Scroll page up.
1291 '''
1292 if self.select_rows == []:
1293
1294 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1295 select_y = max(vadjust.get_value() - vadjust.get_page_size(), self.title_offset_y)
1296 select_row = int((select_y - self.title_offset_y) / self.item_height)
1297
1298
1299 self.start_select_row = select_row
1300 self.select_rows = [select_row]
1301
1302
1303 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1304 if select_row == 0:
1305 vadjust.set_value(vadjust.get_lower())
1306 elif offset_y > select_row * self.item_height + self.title_offset_y:
1307 vadjust.set_value(max((select_row - 1) * self.item_height + self.title_offset_y, vadjust.get_lower()))
1308
1309
1310 self.queue_draw()
1311 else:
1312 if self.start_select_row != None:
1313
1314 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1315 scroll_offset_y = self.start_select_row * self.item_height + self.title_offset_y - vadjust.get_value()
1316
1317
1318 select_y = max(self.start_select_row * self.item_height - vadjust.get_page_size(), self.title_offset_y)
1319 select_row = int((select_y - self.title_offset_y) / self.item_height)
1320
1321
1322 self.start_select_row = select_row
1323 self.select_rows = [select_row]
1324
1325
1326 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1327 if select_row == 0:
1328 vadjust.set_value(vadjust.get_lower())
1329 elif offset_y > select_row * self.item_height + self.title_offset_y:
1330 vadjust.set_value(max(select_row * self.item_height + self.title_offset_y - scroll_offset_y,
1331 vadjust.get_lower()))
1332
1333
1334 self.queue_draw()
1335 else:
1336 print "scroll_page_up : impossible!"
1337
1339 '''
1340 Scroll page down.
1341 '''
1342 if self.select_rows == []:
1343
1344 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1345 select_y = min(vadjust.get_value() + vadjust.get_page_size(),
1346 vadjust.get_upper() - self.item_height)
1347 select_row = int((select_y - self.title_offset_y) / self.item_height)
1348
1349
1350 self.start_select_row = select_row
1351 self.select_rows = [select_row]
1352
1353
1354 max_y = vadjust.get_upper() - vadjust.get_page_size()
1355 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1356 if offset_y + vadjust.get_page_size() < (select_row + 1) * self.item_height + self.title_offset_y:
1357 vadjust.set_value(min(max_y, (select_row - 1) * self.item_height + self.title_offset_y))
1358
1359
1360 self.queue_draw()
1361 else:
1362 if self.start_select_row != None:
1363
1364 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1365 scroll_offset_y = self.start_select_row * self.item_height + self.title_offset_y - vadjust.get_value()
1366
1367
1368 select_y = min(self.start_select_row * self.item_height + vadjust.get_page_size(),
1369 vadjust.get_upper() - self.item_height)
1370 select_row = int((select_y - self.title_offset_y) / self.item_height)
1371
1372
1373 self.start_select_row = select_row
1374 self.select_rows = [select_row]
1375
1376
1377 max_y = vadjust.get_upper() - vadjust.get_page_size()
1378 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1379 if offset_y + vadjust.get_page_size() < (select_row + 1) * self.item_height + self.title_offset_y:
1380 vadjust.set_value(min(max_y, select_row * self.item_height + self.title_offset_y - scroll_offset_y))
1381
1382
1383 self.queue_draw()
1384 else:
1385 print "scroll_page_down : impossible!"
1386
1388 '''
1389 Select preview item.
1390 '''
1391 if self.select_rows == []:
1392 self.select_first_item()
1393 else:
1394
1395 prev_row = max(0, self.start_select_row - 1)
1396
1397
1398 if prev_row != self.start_select_row:
1399
1400 self.start_select_row = prev_row
1401 self.select_rows = [prev_row]
1402
1403
1404 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1405 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1406 if offset_y > prev_row * self.item_height:
1407 vadjust.set_value(max(vadjust.get_lower(), (prev_row - 1) * self.item_height + self.title_offset_y))
1408 elif offset_y + vadjust.get_page_size() < prev_row * self.item_height + self.title_offset_y:
1409 vadjust.set_value(min(vadjust.get_upper() - vadjust.get_page_size(),
1410 (prev_row - 1) * self.item_height + self.title_offset_y))
1411
1412
1413 self.queue_draw()
1414 elif len(self.select_rows) > 1:
1415
1416 self.start_select_row = prev_row
1417 self.select_rows = [prev_row]
1418
1419
1420 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1421 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1422 if offset_y > prev_row * self.item_height + self.title_offset_y:
1423 vadjust.set_value(max(vadjust.get_lower(), (prev_row - 1) * self.item_height + self.title_offset_y))
1424
1425
1426 self.queue_draw()
1427
1429 '''
1430 Select next item.
1431 '''
1432 if self.select_rows == []:
1433 self.select_first_item()
1434 else:
1435
1436 next_row = min(last_index(self.items), self.start_select_row + 1)
1437
1438
1439 if next_row != self.start_select_row:
1440
1441 self.start_select_row = next_row
1442 self.select_rows = [next_row]
1443
1444
1445 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1446 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1447 if offset_y + vadjust.get_page_size() < (next_row + 1) * self.item_height + self.title_offset_y or offset_y > next_row * self.item_height + self.title_offset_y:
1448 vadjust.set_value(max(vadjust.get_lower(),
1449 (next_row + 1) * self.item_height + self.title_offset_y - vadjust.get_page_size()))
1450
1451
1452 self.queue_draw()
1453 elif len(self.select_rows) > 1:
1454
1455 self.start_select_row = next_row
1456 self.select_rows = [next_row]
1457
1458
1459 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1460 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1461 if offset_y + vadjust.get_page_size() < (next_row + 1) * self.item_height + self.title_offset_y:
1462 vadjust.set_value(max(vadjust.get_lower(),
1463 (next_row + 1) * self.item_height + self.title_offset_y - vadjust.get_page_size()))
1464
1465
1466 self.queue_draw()
1467
1469 '''
1470 Select to preview item.
1471 '''
1472 if self.select_rows == []:
1473 self.select_first_item()
1474 elif self.start_select_row != None:
1475 if self.start_select_row == self.select_rows[-1]:
1476 first_row = self.select_rows[0]
1477 if first_row > 0:
1478 prev_row = first_row - 1
1479 self.select_rows = [prev_row] + self.select_rows
1480
1481 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1482 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1483 if offset_y > prev_row * self.item_height:
1484 vadjust.set_value(max(vadjust.get_lower(), (prev_row - 1) * self.item_height + self.title_offset_y))
1485
1486 self.queue_draw()
1487 elif self.start_select_row == self.select_rows[0]:
1488 last_row = self.select_rows[-1]
1489 self.select_rows.remove(last_row)
1490
1491 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1492 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1493 if offset_y > self.select_rows[-1] * self.item_height:
1494 vadjust.set_value(max(vadjust.get_lower(),
1495 (self.select_rows[-1] - 1) * self.item_height + self.title_offset_y))
1496
1497 self.queue_draw()
1498 else:
1499 print "select_to_prev_item : impossible!"
1500
1502 '''
1503 Select to next item.
1504 '''
1505 if self.select_rows == []:
1506 self.select_first_item()
1507 elif self.start_select_row != None:
1508 if self.start_select_row == self.select_rows[0]:
1509 last_row = self.select_rows[-1]
1510 if last_row < last_index(self.items):
1511 next_row = last_row + 1
1512 self.select_rows.append(next_row)
1513
1514 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1515 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1516 if offset_y + vadjust.get_page_size() < next_row * self.item_height + self.title_offset_y:
1517 vadjust.set_value(max(vadjust.get_lower(),
1518 (next_row + 1) * self.item_height + self.title_offset_y - vadjust.get_page_size()))
1519
1520 self.queue_draw()
1521 elif self.start_select_row == self.select_rows[-1]:
1522 first_row = self.select_rows[0]
1523 self.select_rows.remove(first_row)
1524
1525 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1526 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1527 if offset_y + vadjust.get_page_size() < (self.select_rows[0] + 1) * self.item_height + self.title_offset_y:
1528 vadjust.set_value(max(vadjust.get_lower(),
1529 (self.select_rows[0] + 1) * self.item_height + self.title_offset_y - vadjust.get_page_size()))
1530
1531 self.queue_draw()
1532 else:
1533 print "select_to_next_item : impossible!"
1534
1536 '''
1537 Select to first item.
1538 '''
1539 if self.select_rows == []:
1540 self.select_first_item()
1541 elif self.start_select_row != None:
1542 if self.start_select_row == self.select_rows[-1]:
1543 self.select_rows = range(0, self.select_rows[-1] + 1)
1544 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1545 vadjust.set_value(vadjust.get_lower())
1546 self.queue_draw()
1547 elif self.start_select_row == self.select_rows[0]:
1548 self.select_rows = range(0, self.select_rows[0] + 1)
1549 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1550 vadjust.set_value(vadjust.get_lower())
1551 self.queue_draw()
1552 else:
1553 print "select_to_first_item : impossible!"
1554
1556 '''
1557 Select to last item.
1558 '''
1559 if self.select_rows == []:
1560 self.select_first_item()
1561 elif self.start_select_row != None:
1562 if self.start_select_row == self.select_rows[0]:
1563 self.select_rows = range(self.select_rows[0], len(self.items))
1564 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1565 vadjust.set_value(vadjust.get_upper() - vadjust.get_page_size())
1566 self.queue_draw()
1567 elif self.start_select_row == self.select_rows[-1]:
1568 self.select_rows = range(self.select_rows[-1], len(self.items))
1569 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1570 vadjust.set_value(vadjust.get_upper() - vadjust.get_page_size())
1571 self.queue_draw()
1572 else:
1573 print "select_to_end_item : impossible!"
1574
1576 '''
1577 Select all items.
1578 '''
1579 if self.select_rows == []:
1580 self.start_select_row = 0
1581 self.select_rows = range(0, len(self.items))
1582
1583 self.queue_draw()
1584 else:
1585 self.select_rows = range(0, len(self.items))
1586
1587 self.queue_draw()
1588
1590 '''
1591 Delete select items.
1592 '''
1593
1594 remove_items = []
1595 for row in self.select_rows:
1596 remove_items.append(self.items[row])
1597
1598 if remove_items != []:
1599
1600 self.start_select_row = None
1601 self.select_rows = []
1602 cache_remove_items = []
1603
1604
1605 for remove_item in remove_items:
1606 cache_remove_items.append(remove_item)
1607 self.items.remove(remove_item)
1608
1609
1610 self.emit("delete-select-items", cache_remove_items)
1611
1612
1613 self.update_item_index()
1614
1615
1616 self.update_vadjustment()
1617
1618
1619 self.queue_draw()
1620
1631
1633 '''
1634 Double click item.
1635 '''
1636 if len(self.select_rows) == 1:
1637 self.emit("double-click-item", self.items[self.select_rows[0]], -1, 0, 0)
1638
1640 '''
1641 Clear all list.
1642 '''
1643
1644 self.start_select_row = None
1645 self.select_rows = []
1646 self.items = []
1647
1648
1649 self.update_vadjustment()
1650
1651
1652 self.queue_draw()
1653
1655 '''
1656 Get current item.
1657
1658 @return: Return select row, or return None if not any item selected.
1659 '''
1660 if len(self.select_rows) != 1:
1661 return None
1662 else:
1663 return self.items[self.select_rows[0]]
1664
1666 '''
1667 Set highlight with given item.
1668 '''
1669 self.highlight_item = item
1670
1671 self.visible_highlight()
1672
1673 self.queue_draw()
1674
1676 '''
1677 Clear highlight status.
1678 '''
1679 self.highlight_item = None
1680 self.queue_draw()
1681
1683 '''
1684 Visible highlight item.
1685 '''
1686 if self.highlight_item == None:
1687 print "visible_highlight: highlight item is None."
1688 else:
1689
1690 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self)
1691 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment()
1692 highlight_index = self.highlight_item.get_index()
1693 if offset_y > highlight_index * self.item_height:
1694 vadjust.set_value(highlight_index * self.item_height)
1695 elif offset_y + vadjust.get_page_size() < (highlight_index + 1) * self.item_height:
1696 vadjust.set_value((highlight_index + 1) * self.item_height - vadjust.get_page_size() + self.title_offset_y)
1697
1698 gobject.type_register(ListView)
1701 '''
1702 ListItem template to build your own item for L{ I{ListView} <ListView>}.
1703
1704 @note: This class just template to build list item, you should build new item with same interface.
1705 '''
1706
1707 __gsignals__ = {
1708 "redraw-request" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
1709 }
1710
1711 - def __init__(self, title, artist, length):
1712 '''
1713 Initialize ListItem class.
1714
1715 @param title: Title.
1716 @param artist: Artist.
1717 @param length: Length.
1718 '''
1719 gobject.GObject.__init__(self)
1720 self.update(title, artist, length)
1721 self.index = None
1722
1724 '''
1725 Update index.
1726
1727 This is ListView interface, you should implement it.
1728
1729 @param index: Index.
1730 '''
1731 self.index = index
1732
1734 '''
1735 Get index.
1736
1737 This is ListView interface, you should implement it.
1738 '''
1739 return self.index
1740
1742 '''
1743 Emit redraw-request signal.
1744
1745 This is ListView interface, you should implement it.
1746 '''
1747 self.emit("redraw-request")
1748
1749 - def update(self, title, artist, length):
1750 '''
1751 Update.
1752
1753 This is ListView interface, you should implement it.
1754
1755 @param title: Title.
1756 @param artist: Artist.
1757 @param length: Length.
1758 '''
1759
1760 self.title = title
1761 self.artist = artist
1762 self.length = length
1763
1764
1765 self.title_padding_x = 10
1766 self.title_padding_y = 5
1767 (self.title_width, self.title_height) = get_content_size(self.title, DEFAULT_FONT_SIZE)
1768
1769 self.artist_padding_x = 10
1770 self.artist_padding_y = 5
1771 (self.artist_width, self.artist_height) = get_content_size(self.artist, DEFAULT_FONT_SIZE)
1772
1773 self.length_padding_x = 10
1774 self.length_padding_y = 5
1775 (self.length_width, self.length_height) = get_content_size(self.length, DEFAULT_FONT_SIZE)
1776
1777 - def render_title(self, cr, rect, in_select, in_highlight):
1778 '''
1779 Render title.
1780
1781 @param cr: Cairo context.
1782 @param rect: Redraw rectangle.
1783 @param in_select: Whether current item is selected, this value pass from ListView.
1784 @param in_highlight: Whether current item is highlighted, this value pass from ListView.
1785 '''
1786 rect.x += self.title_padding_x
1787 rect.width -= self.title_padding_x * 2
1788 render_text(cr, rect, self.title, in_select, in_highlight)
1789
1791 '''
1792 Render artist.
1793
1794 @param cr: Cairo context.
1795 @param rect: Redraw rectangle.
1796 @param in_select: Whether current item is selected, this value pass from ListView.
1797 @param in_highlight: Whether current item is highlighted, this value pass from ListView.
1798 '''
1799 rect.x += self.artist_padding_x
1800 rect.width -= self.title_padding_x * 2
1801 render_text(cr, rect, self.artist, in_select, in_highlight)
1802
1804 '''
1805 Render length.
1806
1807 @param cr: Cairo context.
1808 @param rect: Redraw rectangle.
1809 @param in_select: Whether current item is selected, this value pass from ListView.
1810 @param in_highlight: Whether current item is highlighted, this value pass from ListView.
1811 '''
1812 rect.width -= self.length_padding_x * 2
1813 render_text(cr, rect, self.length, in_select, in_highlight, align=ALIGN_END)
1814
1816 '''
1817 Get column sizes.
1818
1819 This is ListView interface, you should implement it.
1820
1821 @return: Return column size tuple.
1822 '''
1823 return [(self.title_width + self.title_padding_x * 2,
1824 self.title_height + self.title_padding_y * 2),
1825 (self.artist_width + self.artist_padding_x * 2,
1826 self.artist_height + self.artist_padding_y * 2),
1827 (self.length_width + self.length_padding_x * 2,
1828 self.length_height + self.length_padding_y * 2),
1829 ]
1830
1832 '''
1833 Get render callbacks.
1834
1835 This is ListView interface, you should implement it.
1836
1837 @return: Return render functions.
1838 '''
1839 return [self.render_title,
1840 self.render_artist,
1841 self.render_length]
1842
1843 -def render_text(cr, rect, content, in_select, in_highlight, align=ALIGN_START, font_size=DEFAULT_FONT_SIZE):
1844 '''
1845 Helper render text function for ListItem, you should implement your own.
1846
1847 @param cr: Cairo context.
1848 @param rect: Draw area.
1849 @param content: Content.
1850 @param in_select: Whether item is selected.
1851 @param in_highlight: Whether item is highlighted.
1852 @param align: Render alignment option, default is ALIGN_START.
1853 @param font_size: Render font size, default is DEFAULT_FONT_SIZE.
1854 '''
1855 if in_select or in_highlight:
1856 color = ui_theme.get_color("list_item_select_text").get_color()
1857 else:
1858 color = ui_theme.get_color("list_item_text").get_color()
1859 draw_text(cr, content,
1860 rect.x, rect.y, rect.width, rect.height,
1861 font_size,
1862 color,
1863 alignment=align)
1864
1866 '''
1867 Helper render image function for ListItem, you should implement your own.
1868
1869 @param cr: Cairo context.
1870 @param rect: Draw area.
1871 @param image_path: Image path.
1872 @param x: X coordiante of draw position.
1873 @param y: Y coordiante of draw position.
1874 '''
1875 draw_pixbuf(cr, ui_theme.get_pixbuf(image_path).get_pixbuf(), x, y)
1876