Package dtk :: Package ui :: Module iconview

Source Code for Module dtk.ui.iconview

  1  #! /usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  # Copyright (C) 2011 ~ 2012 Deepin, Inc. 
  5  #               2011 ~ 2012 Wang Yong 
  6  #  
  7  # Author:     Wang Yong <lazycat.manatee@gmail.com> 
  8  # Maintainer: Wang Yong <lazycat.manatee@gmail.com> 
  9  #  
 10  # This program is free software: you can redistribute it and/or modify 
 11  # it under the terms of the GNU General Public License as published by 
 12  # the Free Software Foundation, either version 3 of the License, or 
 13  # any later version. 
 14  #  
 15  # This program is distributed in the hope that it will be useful, 
 16  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 17  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 18  # GNU General Public License for more details. 
 19  #  
 20  # You should have received a copy of the GNU General Public License 
 21  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 22   
 23  from draw import draw_pixbuf, draw_vlinear 
 24  from keymap import get_keyevent_name 
 25  from skin_config import skin_config 
 26  from theme import ui_theme 
 27  import gobject 
 28  import gtk 
 29  from utils import (get_match_parent, cairo_state, get_event_coords,  
 30                     is_in_rect, is_left_button, is_double_click,  
 31                     is_single_click, get_window_shadow_size) 
 32   
33 -class IconView(gtk.DrawingArea):
34 ''' 35 Icon view. 36 37 @undocumented: expose_icon_view 38 @undocumented: motion_icon_view 39 @undocumented: icon_view_get_event_index 40 @undocumented: button_press_icon_view 41 @undocumented: button_release_icon_view 42 @undocumented: leave_icon_view 43 @undocumented: key_press_icon_view 44 @undocumented: key_release_icon_view 45 @undocumented: update_redraw_request_list 46 @undocumented: redraw_item 47 @undocumented: get_offset_coordinate 48 ''' 49 50 __gsignals__ = { 51 "lost-focus-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 52 "motion-notify-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int)), 53 "highlight-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 54 "normal-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 55 "button-press-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int)), 56 "button-release-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int)), 57 "single-click-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int)), 58 "double-click-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, int, int)), 59 } 60
61 - def __init__(self, padding_x=0, padding_y=0):
62 ''' 63 Initialize IconView class. 64 65 @param padding_x: Horizontal padding value. 66 @param padding_y: Vertical padding value. 67 ''' 68 # Init. 69 gtk.DrawingArea.__init__(self) 70 self.padding_x = padding_x 71 self.padding_y = padding_y 72 self.add_events(gtk.gdk.ALL_EVENTS_MASK) 73 self.set_can_focus(True) # can focus to response key-press signal 74 self.items = [] 75 self.focus_index = None 76 self.highlight_item = None 77 self.double_click_item = None 78 self.single_click_item = None 79 80 # Signal. 81 self.connect("realize", lambda w: self.grab_focus()) # focus key after realize 82 self.connect("expose-event", self.expose_icon_view) 83 self.connect("motion-notify-event", self.motion_icon_view) 84 self.connect("button-press-event", self.button_press_icon_view) 85 self.connect("button-release-event", self.button_release_icon_view) 86 self.connect("leave-notify-event", self.leave_icon_view) 87 self.connect("key-press-event", self.key_press_icon_view) 88 89 # Add item singal. 90 self.connect("lost-focus-item", lambda view, item: item.icon_item_lost_focus()) 91 self.connect("motion-notify-item", lambda view, item, x, y: item.icon_item_motion_notify(x, y)) 92 self.connect("highlight-item", lambda view, item: item.icon_item_highlight()) 93 self.connect("normal-item", lambda view, item: item.icon_item_normal()) 94 self.connect("button-press-item", lambda view, item, x, y: item.icon_item_button_press(x, y)) 95 self.connect("button-release-item", lambda view, item, x, y: item.icon_item_button_release(x, y)) 96 self.connect("single-click-item", lambda view, item, x, y: item.icon_item_single_click(x, y)) 97 self.connect("double-click-item", lambda view, item, x, y: item.icon_item_double_click(x, y)) 98 99 # Redraw. 100 self.redraw_request_list = [] 101 self.redraw_delay = 100 # 100 milliseconds should be enough for redraw 102 gtk.timeout_add(self.redraw_delay, self.update_redraw_request_list) 103 104 self.keymap = { 105 "Home" : self.select_first_item, 106 "End" : self.select_last_item, 107 "Return" : self.return_item, 108 "Up" : self.select_up_item, 109 "Down" : self.select_down_item, 110 "Left" : self.select_left_item, 111 "Right" : self.select_right_item, 112 "Page_Up" : self.scroll_page_up, 113 "Page_Down" : self.scroll_page_down, 114 }
115
116 - def select_first_item(self):
117 ''' 118 Select first item. 119 ''' 120 if len(self.items) > 0: 121 self.clear_focus_item() 122 self.focus_index = 0 123 124 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 125 126 # Scroll to top. 127 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 128 vadjust.set_value(vadjust.get_lower())
129
130 - def select_last_item(self):
131 ''' 132 Select last item. 133 ''' 134 if len(self.items) > 0: 135 self.clear_focus_item() 136 self.focus_index = len(self.items) - 1 137 138 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 139 140 # Scroll to bottom. 141 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 142 vadjust.set_value(vadjust.get_upper() - vadjust.get_page_size())
143
144 - def return_item(self):
145 ''' 146 Do return action. 147 148 This function will emit `double-click-item` signal. 149 ''' 150 if self.focus_index != None: 151 self.emit("double-click-item", self.items[self.focus_index], 0, 0)
152
153 - def select_up_item(self):
154 ''' 155 Select up row item. 156 ''' 157 if len(self.items) > 0: 158 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 159 160 if self.focus_index == None: 161 self.focus_index = 0 162 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 163 164 # Scroll to top. 165 vadjust.set_value(vadjust.get_lower()) 166 else: 167 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 168 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 169 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 170 171 if self.focus_index - columns >= 0: 172 self.emit("lost-focus-item", self.items[self.focus_index]) 173 self.focus_index -= columns 174 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 175 176 # Scroll to item. 177 row = int(self.focus_index / columns) 178 if vadjust.get_value() - self.padding_y > row * item_height: 179 vadjust.set_value(vadjust.get_lower() + row * item_height + self.padding_y) 180 elif vadjust.get_value() - self.padding_y == row * item_height: 181 vadjust.set_value(vadjust.get_lower())
182
183 - def select_down_item(self):
184 ''' 185 Select next row item. 186 ''' 187 if len(self.items) > 0: 188 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 189 if self.focus_index == None: 190 self.focus_index = 0 191 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 192 193 # Scroll to top. 194 vadjust.set_value(vadjust.get_lower()) 195 else: 196 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 197 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 198 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 199 200 if self.focus_index + columns <= len(self.items) - 1: 201 self.emit("lost-focus-item", self.items[self.focus_index]) 202 self.focus_index += columns 203 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 204 205 # Scroll to item. 206 row = int(self.focus_index / columns) 207 if vadjust.get_value() + vadjust.get_page_size() - self.padding_y < (row + 1) * item_height: 208 vadjust.set_value(vadjust.get_lower() + (row + 1) * item_height - vadjust.get_page_size() + self.padding_y) 209 elif vadjust.get_value() + vadjust.get_page_size() - self.padding_y == (row + 1) * item_height: 210 vadjust.set_value(vadjust.get_upper() - vadjust.get_page_size())
211
212 - def select_left_item(self):
213 ''' 214 Select left item. 215 ''' 216 if len(self.items) > 0: 217 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 218 if self.focus_index == None: 219 self.focus_index = 0 220 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 221 222 # Scroll to top. 223 vadjust.set_value(vadjust.get_lower()) 224 else: 225 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 226 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 227 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 228 row = int(self.focus_index / columns) 229 min_index = row * columns 230 231 if self.focus_index - 1 >= min_index: 232 self.emit("lost-focus-item", self.items[self.focus_index]) 233 self.focus_index -= 1 234 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0)
235
236 - def select_right_item(self):
237 ''' 238 Select right item. 239 ''' 240 if len(self.items) > 0: 241 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 242 if self.focus_index == None: 243 self.focus_index = 0 244 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 245 246 # Scroll to top. 247 vadjust.set_value(vadjust.get_lower()) 248 else: 249 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 250 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 251 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 252 row = int(self.focus_index / columns) 253 max_index = min((row + 1) * columns - 1, len(self.items) - 1) 254 255 if self.focus_index + 1 <= max_index: 256 self.emit("lost-focus-item", self.items[self.focus_index]) 257 self.focus_index += 1 258 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0)
259
260 - def scroll_page_up(self):
261 ''' 262 Scroll page up of iconview. 263 ''' 264 if len(self.items) > 0: 265 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 266 if self.focus_index == None: 267 self.focus_index = 0 268 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 269 270 # Scroll to top. 271 vadjust.set_value(vadjust.get_lower()) 272 else: 273 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 274 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 275 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 276 column = int(self.focus_index % columns) 277 if (vadjust.get_value() - vadjust.get_lower()) % item_height == 0: 278 row = int((vadjust.get_value() - vadjust.get_lower() - self.padding_y) / item_height) - 1 279 else: 280 row = int((vadjust.get_value() - vadjust.get_lower() - self.padding_y) / item_height) 281 if row * columns + column >= 0: 282 self.emit("lost-focus-item", self.items[self.focus_index]) 283 self.focus_index = row * columns + column 284 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 285 else: 286 self.emit("lost-focus-item", self.items[self.focus_index]) 287 self.focus_index = column 288 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 289 290 vadjust.set_value(max(0, vadjust.get_value() - vadjust.get_page_size() + self.padding_y))
291
292 - def scroll_page_down(self):
293 ''' 294 Scroll page down of iconview. 295 ''' 296 if len(self.items): 297 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 298 if self.focus_index == None: 299 self.focus_index = len(self.items) - 1 300 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 301 302 # Scroll to top. 303 vadjust.set_value(vadjust.get_upper() - vadjust.get_page_size()) 304 else: 305 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 306 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 307 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 308 column = int(self.focus_index % columns) 309 if (vadjust.get_value() - vadjust.get_lower() + vadjust.get_page_size()) % item_height == 0: 310 row = int((vadjust.get_value() - vadjust.get_lower() + vadjust.get_page_size() - self.padding_y) / item_height) - 1 311 else: 312 row = int((vadjust.get_value() - vadjust.get_lower() + vadjust.get_page_size() - self.padding_y) / item_height) 313 if row * columns + column <= len(self.items) - 1: 314 self.emit("lost-focus-item", self.items[self.focus_index]) 315 self.focus_index = row * columns + column 316 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 317 else: 318 self.emit("lost-focus-item", self.items[self.focus_index]) 319 self.focus_index = (row - 1) * columns + column 320 self.emit("motion-notify-item", self.items[self.focus_index], 0, 0) 321 322 vadjust.set_value(min(vadjust.get_upper() - vadjust.get_page_size(), 323 vadjust.get_value() + vadjust.get_page_size() - self.padding_y))
324
325 - def add_items(self, items, insert_pos=None):
326 ''' 327 Add items to iconview. 328 329 @param items: A list of item that follow the rule of IconItem. 330 @param insert_pos: Insert position, default is None to insert new item at B{end} position. 331 ''' 332 if insert_pos == None: 333 self.items += items 334 else: 335 self.items = self.items[0:insert_pos] + items + self.items[insert_pos::] 336 337 for item in items: 338 item.connect("redraw_request", self.redraw_item) 339 340 self.queue_draw()
341
342 - def delete_items(self, items):
343 ''' 344 Delete items. 345 346 @param items: Items need to remove. 347 ''' 348 match_item = False 349 for item in items: 350 if item in self.items: 351 self.items.remove(item) 352 match_item = True 353 354 if match_item: 355 self.queue_draw()
356
357 - def clear(self):
358 ''' 359 Clear all items. 360 ''' 361 self.items = [] 362 self.queue_draw()
363
364 - def draw_mask(self, cr, x, y, w, h):
365 ''' 366 Draw mask interface. 367 368 @param cr: Cairo context. 369 @param x: X coordiante of draw area. 370 @param y: Y coordiante of draw area. 371 @param w: Width of draw area. 372 @param h: Height of draw area. 373 ''' 374 draw_vlinear(cr, x, y, w, h, 375 ui_theme.get_shadow_color("linear_background").get_color_info())
376
377 - def expose_icon_view(self, widget, event):
378 ''' 379 Internal callback for `expose-event` signal. 380 ''' 381 # Update vadjustment. 382 self.update_vadjustment() 383 384 # Init. 385 cr = widget.window.cairo_create() 386 rect = widget.allocation 387 388 # Get offset. 389 (offset_x, offset_y, viewport) = self.get_offset_coordinate(widget) 390 391 # Draw background. 392 with cairo_state(cr): 393 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 394 cr.translate(-scrolled_window.allocation.x, -scrolled_window.allocation.y) 395 cr.rectangle(offset_x, offset_y, 396 scrolled_window.allocation.x + scrolled_window.allocation.width, 397 scrolled_window.allocation.y + scrolled_window.allocation.height) 398 cr.clip() 399 400 (shadow_x, shadow_y) = get_window_shadow_size(self.get_toplevel()) 401 skin_config.render_background(cr, self, offset_x + shadow_x, offset_y + shadow_y) 402 403 # Draw mask. 404 self.draw_mask(cr, offset_x, offset_y, viewport.allocation.width, viewport.allocation.height) 405 406 # Draw item. 407 if len(self.items) > 0: 408 with cairo_state(cr): 409 # Don't draw any item out of viewport area. 410 cr.rectangle(offset_x, offset_y, 411 viewport.allocation.width, 412 viewport.allocation.height) 413 cr.clip() 414 415 # Draw item. 416 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 417 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 418 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 419 420 # Get viewport index. 421 start_y = offset_y - self.padding_y 422 start_row = max(int(start_y / item_height), 0) 423 start_index = start_row * columns 424 425 end_y = offset_y - self.padding_y + viewport.allocation.height 426 if end_y % item_height == 0: 427 end_row = end_y / item_height - 1 428 else: 429 end_row = end_y / item_height 430 end_index = min((end_row + 1) * columns, len(self.items)) 431 432 for (index, item) in enumerate(self.items[start_index:end_index]): 433 row = int((start_index + index) / columns) 434 column = (start_index + index) % columns 435 render_x = rect.x + self.padding_x + column * item_width 436 render_y = rect.y + self.padding_y + row * item_height 437 438 with cairo_state(cr): 439 # Don't allow draw out of item area. 440 cr.rectangle(render_x, render_y, item_width, item_height) 441 cr.clip() 442 443 item.render(cr, gtk.gdk.Rectangle(render_x, render_y, item_width, item_height))
444
445 - def clear_focus_item(self):
446 ''' 447 Clear item's focus status. 448 ''' 449 if self.focus_index != None: 450 if 0 <= self.focus_index < len(self.items): 451 self.emit("lost-focus-item", self.items[self.focus_index]) 452 self.focus_index = None
453
454 - def motion_icon_view(self, widget, event):
455 ''' 456 Internal callback for `motion-notify-event` signal. 457 ''' 458 if len(self.items) > 0: 459 index_info = self.icon_view_get_event_index(event) 460 if index_info == None: 461 self.clear_focus_item() 462 else: 463 (row_index, column_index, item_index, offset_x, offset_y) = index_info 464 465 # Don't clear focus item when motion index is current one. 466 if self.focus_index != item_index: 467 self.clear_focus_item() 468 469 self.focus_index = item_index 470 471 self.emit("motion-notify-item", self.items[self.focus_index], offset_x - self.padding_x, offset_y - self.padding_y)
472
473 - def icon_view_get_event_index(self, event):
474 ''' 475 Internal function to get item index at event coordinate.. 476 ''' 477 if len(self.items) > 0: 478 (event_x, event_y) = get_event_coords(event) 479 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 480 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 481 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 482 if len(self.items) % columns == 0: 483 rows = int(len(self.items) / columns) 484 else: 485 rows = int(len(self.items) / columns) + 1 486 487 if event_x > columns * item_width: 488 return None 489 elif event_y > rows * item_height: 490 return None 491 else: 492 493 if event_x % item_width == 0: 494 column_index = max(event_x / item_width - 1, 0) 495 else: 496 column_index = min(event_x / item_width, columns - 1) 497 498 if event_y % item_height == 0: 499 row_index = max(event_y / item_height - 1, 0) 500 else: 501 row_index = min(event_y / item_height, rows - 1) 502 503 item_index = row_index * columns + column_index 504 if item_index > len(self.items) - 1: 505 return None 506 else: 507 return (row_index, column_index, item_index, 508 event_x - column_index * item_width, 509 event_y - row_index * item_height)
510
511 - def button_press_icon_view(self, widget, event):
512 ''' 513 Internal callback for `button-press-event` signal. 514 ''' 515 # Grab focus when button press, otherwise key-press signal can't response. 516 self.grab_focus() 517 518 if len(self.items) > 0 and is_left_button(event): 519 index_info = self.icon_view_get_event_index(event) 520 521 if index_info: 522 (row_index, column_index, item_index, offset_x, offset_y) = index_info 523 self.emit("button-press-item", self.items[item_index], offset_x - self.padding_x, offset_y - self.padding_y) 524 525 if is_double_click(event): 526 if index_info: 527 self.double_click_item = index_info[2] 528 else: 529 self.double_click_item = None 530 elif is_single_click(event): 531 if index_info: 532 self.single_click_item = index_info[2] 533 else: 534 self.single_click_item = None 535 536 # Set highlight. 537 if index_info: 538 self.clear_highlight() 539 540 self.set_highlight(self.items[index_info[2]])
541
542 - def set_highlight(self, item):
543 ''' 544 Set highlight status with given item. 545 546 @param item: Item need highlight. 547 ''' 548 self.highlight_item = item 549 self.emit("highlight-item", self.highlight_item)
550
551 - def clear_highlight(self):
552 ''' 553 Clear all highlight status. 554 ''' 555 if self.highlight_item != None: 556 self.emit("normal-item", self.highlight_item) 557 self.highlight_item = None
558
559 - def button_release_icon_view(self, widget, event):
560 ''' 561 Internal callback for `button-release-event` signal. 562 ''' 563 if len(self.items) > 0 and is_left_button(event): 564 index_info = self.icon_view_get_event_index(event) 565 if index_info: 566 (row_index, column_index, item_index, offset_x, offset_y) = index_info 567 568 self.emit("button-release-item", self.items[item_index], offset_x - self.padding_x, offset_y - self.padding_y) 569 570 if self.double_click_item == item_index: 571 self.emit("double-click-item", self.items[self.double_click_item], offset_x - self.padding_x, offset_y - self.padding_y) 572 elif self.single_click_item == item_index: 573 self.emit("single-click-item", self.items[self.single_click_item], offset_x - self.padding_x, offset_y - self.padding_y) 574 575 self.double_click_item = None 576 self.single_click_item = None
577
578 - def leave_icon_view(self, widget, event):
579 ''' 580 Internal callback for `leave-notify` signal. 581 ''' 582 # Hide hover row when cursor out of viewport area. 583 vadjust = get_match_parent(self, ["ScrolledWindow"]).get_vadjustment() 584 hadjust = get_match_parent(self, ["ScrolledWindow"]).get_hadjustment() 585 if not is_in_rect((event.x, event.y), 586 (hadjust.get_value(), vadjust.get_value(), hadjust.get_page_size(), vadjust.get_page_size())): 587 self.clear_focus_item()
588
589 - def key_press_icon_view(self, widget, event):
590 ''' 591 Internal callback for `key-press-event` signal. 592 ''' 593 key_name = get_keyevent_name(event) 594 if self.keymap.has_key(key_name): 595 self.keymap[key_name]() 596 597 return True
598
599 - def key_release_icon_view(self, widget, event):
600 ''' 601 Internal callback for `key-release-event` signal. 602 ''' 603 pass
604
606 ''' 607 Internal function to update redraw request list. 608 ''' 609 # Redraw when request list is not empty. 610 if len(self.redraw_request_list) > 0: 611 # Get offset. 612 (offset_x, offset_y, viewport) = self.get_offset_coordinate(self) 613 614 # Get viewport index. 615 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 616 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 617 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 618 619 start_y = offset_y - self.padding_y 620 start_row = max(int(start_y / item_height), 0) 621 start_index = start_row * columns 622 623 end_y = offset_y - self.padding_y + viewport.allocation.height 624 if end_y % item_height == 0: 625 end_row = end_y / item_height - 1 626 else: 627 end_row = end_y / item_height 628 end_index = min((end_row + 1) * columns, len(self.items)) 629 630 # Redraw whole viewport area once found any request item in viewport. 631 for item in self.redraw_request_list: 632 if item in self.items[start_index:end_index]: 633 self.queue_draw() 634 break 635 636 # Clear redraw request list. 637 self.redraw_request_list = [] 638 639 return True
640
641 - def redraw_item(self, list_item):
642 ''' 643 Internal function to redraw item. 644 ''' 645 self.redraw_request_list.append(list_item)
646
647 - def get_offset_coordinate(self, widget):
648 ''' 649 Internal function to get offset coordinate. 650 ''' 651 # Init. 652 rect = widget.allocation 653 654 # Get coordinate. 655 viewport = get_match_parent(widget, "Viewport") 656 if viewport: 657 coordinate = widget.translate_coordinates(viewport, rect.x, rect.y) 658 if len(coordinate) == 2: 659 (offset_x, offset_y) = coordinate 660 return (-offset_x, -offset_y, viewport) 661 else: 662 return (0, 0, viewport) 663 else: 664 return (0, 0, viewport)
665
666 - def update_vadjustment(self):
667 ''' 668 Update vertical adjustment. 669 ''' 670 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 671 672 if len(self.items) > 0: 673 item_width, item_height = self.items[0].get_width(), self.items[0].get_height() 674 columns = int((scrolled_window.allocation.width - self.padding_x * 2) / item_width) 675 if columns > 0: 676 if len(self.items) % columns == 0: 677 view_height = int(len(self.items) / columns) * item_height 678 else: 679 view_height = (int(len(self.items) / columns) + 1) * item_height 680 681 self.set_size_request(columns * item_width + self.padding_x * 2, 682 view_height + self.padding_y * 2) 683 if scrolled_window != None: 684 vadjust = scrolled_window.get_vadjustment() 685 vadjust.set_upper(max(view_height + self.padding_y * 2, 686 scrolled_window.allocation.height)) 687 else: 688 self.set_size_request(scrolled_window.allocation.width, 689 scrolled_window.allocation.height) 690 vadjust = scrolled_window.get_vadjustment() 691 vadjust.set_upper(scrolled_window.allocation.height)
692 693 gobject.type_register(IconView) 694
695 -class IconItem(gobject.GObject):
696 ''' 697 Icon item. 698 ''' 699 700 __gsignals__ = { 701 "redraw-request" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), 702 } 703
704 - def __init__(self, pixbuf):
705 ''' 706 Initialize ItemIcon class. 707 708 @param pixbuf: Icon pixbuf. 709 ''' 710 gobject.GObject.__init__(self) 711 self.pixbuf = pixbuf 712 self.padding_x = 21 713 self.padding_y = 21 714 self.hover_flag = False 715 self.highlight_flag = False
716
717 - def emit_redraw_request(self):
718 ''' 719 Emit `redraw-request` signal. 720 721 This is IconView interface, you should implement it. 722 ''' 723 self.emit("redraw-request")
724
725 - def get_width(self):
726 ''' 727 Get item width. 728 729 This is IconView interface, you should implement it. 730 ''' 731 return self.pixbuf.get_width() + self.padding_x * 2
732
733 - def get_height(self):
734 ''' 735 Get item height. 736 737 This is IconView interface, you should implement it. 738 ''' 739 return self.pixbuf.get_height() + self.padding_y * 2
740
741 - def render(self, cr, rect):
742 ''' 743 Render item. 744 745 This is IconView interface, you should implement it. 746 ''' 747 # Draw cover border. 748 border_size = 4 749 750 if self.hover_flag: 751 cr.set_source_rgb(1, 0, 0) 752 elif self.highlight_flag: 753 cr.set_source_rgb(0, 1, 0) 754 else: 755 cr.set_source_rgb(1, 1, 1) 756 cr.rectangle( 757 rect.x + (rect.width - self.pixbuf.get_width()) / 2 - border_size, 758 rect.y + (rect.height - self.pixbuf.get_height()) / 2 - border_size, 759 self.pixbuf.get_width() + border_size * 2, 760 self.pixbuf.get_height() + border_size * 2) 761 cr.fill() 762 763 # Draw cover. 764 draw_pixbuf( 765 cr, 766 self.pixbuf, 767 rect.x + self.padding_x, 768 rect.y + self.padding_y)
769
770 - def icon_item_motion_notify(self, x, y):
771 ''' 772 Handle `motion-notify-event` signal. 773 774 This is IconView interface, you should implement it. 775 ''' 776 self.hover_flag = True 777 778 self.emit_redraw_request()
779
780 - def icon_item_lost_focus(self):
781 ''' 782 Lost focus. 783 784 This is IconView interface, you should implement it. 785 ''' 786 self.hover_flag = False 787 788 self.emit_redraw_request()
789
790 - def icon_item_highlight(self):
791 ''' 792 Highlight item. 793 794 This is IconView interface, you should implement it. 795 ''' 796 self.highlight_flag = True 797 798 self.emit_redraw_request()
799
800 - def icon_item_normal(self):
801 ''' 802 Set item with normal status. 803 804 This is IconView interface, you should implement it. 805 ''' 806 self.highlight_flag = False 807 808 self.emit_redraw_request()
809
810 - def icon_item_button_press(self, x, y):
811 ''' 812 Handle button-press event. 813 814 This is IconView interface, you should implement it. 815 ''' 816 pass
817
818 - def icon_item_button_release(self, x, y):
819 ''' 820 Handle button-release event. 821 822 This is IconView interface, you should implement it. 823 ''' 824 pass
825
826 - def icon_item_single_click(self, x, y):
827 ''' 828 Handle single click event. 829 830 This is IconView interface, you should implement it. 831 ''' 832 pass
833
834 - def icon_item_double_click(self, x, y):
835 ''' 836 Handle double click event. 837 838 This is IconView interface, you should implement it. 839 ''' 840 pass
841 842 gobject.type_register(IconItem) 843