Package dtk :: Package ui :: Module treeview

Source Code for Module dtk.ui.treeview

  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:     Hailong Qiu <qiuhailong@linuxdeepin.com> 
  8  # Maintainer: Hailong Qiu <qiuhailong@linuxdeepin.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 dtk.ui.skin_config import skin_config 
 24  # from dtk.ui.theme import Theme, ui_theme 
 25   
 26  from collections import OrderedDict 
 27  from draw import draw_pixbuf, draw_vlinear, draw_text 
 28  from skin_config import skin_config 
 29  from theme import ui_theme 
 30  import gobject 
 31  import gtk 
 32  import pango 
 33  from utils import (get_content_size, is_single_click, is_double_click, is_right_button, get_window_shadow_size, 
 34                     cairo_state, get_match_parent) 
 35   
 36  # (cr, text, font_size, font_color, x, y, width, height, font_align 
37 -class TreeView(gtk.DrawingArea):
38 39 __gsignals__ = { 40 "single-click-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 41 "double-click-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), 42 "right-press-item" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_INT)), 43 } 44
45 - def __init__(self, 46 width=20, 47 height = 30, 48 font_size = 10, 49 font_x_padding=5, 50 font_width=120, 51 font_height = 0, 52 font_align=pango.ALIGN_LEFT, 53 arrow_x_padding = 10, 54 normal_pixbuf = ui_theme.get_pixbuf("treeview/arrow_right.png"), 55 press_pixbuf = ui_theme.get_pixbuf("treeview/arrow_down.png"), 56 normal_hover_pixbuf = ui_theme.get_pixbuf("treeview/arrow_right_hover.png"), 57 press_hover_pixbuf = ui_theme.get_pixbuf("treeview/arrow_down_hover.png")):
58 gtk.DrawingArea.__init__(self) 59 self.root = Tree() 60 self.tree_list = [] 61 self.tree_all_node_list = [] # Save all node. 62 63 self.tree_id_list = [] 64 self.tree_id_num = 0 65 self.scan_save_item = None 66 67 self.set_can_focus(True) 68 # Init DrawingArea event. 69 self.add_events(gtk.gdk.ALL_EVENTS_MASK) 70 self.connect("button-press-event", self.tree_view_press_event) 71 self.connect("motion-notify-event", self.tree_view_motion_event) 72 self.connect("expose-event", self.tree_view_expose_event) 73 self.connect("key-press-event", self.tree_view_key_press_event) 74 self.connect("leave-notify-event", self.tree_view_leave_notify_event) 75 self.connect("realize", lambda w: self.grab_focus()) # focus key after realize 76 77 self.width = width 78 self.height = height 79 # Draw icon. 80 self.normal_pixbuf = normal_pixbuf 81 self.press_pixbuf = press_pixbuf 82 self.normal_hover_pixbuf = normal_hover_pixbuf 83 self.press_hover_pixbuf = press_hover_pixbuf 84 self.arrow_x_padding = arrow_x_padding 85 # Draw move background. 86 self.move_height = -1 87 self.move_draw_bool = False 88 self.move_index_num = None 89 # Draw press background. 90 self.press_height = -1 91 self.press_draw_bool = False 92 # Draw font. 93 self.font_x_padding = font_x_padding 94 self.font_width = font_width 95 self.font_height = font_height 96 self.font_align = font_align 97 self.font_size = font_size 98 99 self.highlight_index = None 100 101 if not self.font_size: 102 self.font_size = self.height/2 - 4 103 104 if self.font_size > self.height - 15: 105 self.font_size = self.height - 15
106 107 # DrawingArea event function.
108 - def tree_view_press_event(self, widget, event):
109 self.press_notify_function(event)
110 111
112 - def press_notify_function(self, event):
113 temp_press_height = self.press_height 114 self.press_height = event.y 115 index_len = len(self.tree_list) 116 index = int(self.press_height / self.height) 117 118 if index_len > index: 119 if is_single_click(event) and event.button == 1: 120 self.highlight_index = index 121 self.press_draw_bool = True 122 123 if self.tree_list[index].child_items: 124 self.tree_list[index].show_child_items_bool = not self.tree_list[index].show_child_items_bool 125 self.sort() 126 self.queue_draw() 127 128 if is_single_click(event) and event.button == 1: 129 self.emit("single-click-item", self.tree_list[index].tree_view_item) 130 elif is_double_click(event): 131 self.emit("double-click-item", self.tree_list[index].tree_view_item) 132 elif is_right_button(event): 133 self.press_height = self.highlight_index * self.height 134 self.emit("right-press-item", self.tree_list[index].tree_view_item, int(event.x_root), int(event.y_root), index) 135 136 else: 137 self.press_height = temp_press_height 138 if is_right_button(event): 139 self.press_height = self.highlight_index * self.height 140 self.emit("right-press-item", event, int(event.x_root), int(event.y_root), -1) 141
142 - def set_highlight_index(self, index):
143 index_len = len(self.tree_list) 144 self.highlight_index = index 145 if index_len > index: 146 self.press_height = self.height * index 147 self.press_draw_bool = True 148 self.queue_draw() 149
150 - def get_highlight_index(self):
151 return self.highlight_index 152
153 - def get_highlight_item(self):
154 return self.tree_list[self.highlight_index].tree_view_item
155
156 - def tree_view_motion_event(self, widget, event):
157 temp_move_height = self.move_height # Save move_height. 158 self.move_height = event.y 159 index_len = len(self.tree_list) 160 index_num = int(self.move_height) / self.height 161 162 if index_len > index_num: 163 self.move_index_num = index_num 164 self.move_draw_bool = True 165 self.queue_draw() 166 else: 167 self.move_height = temp_move_height
168
169 - def get_offset_coordinate(self, widget):
170 '''Get offset coordinate.''' 171 # Init. 172 rect = widget.allocation 173 174 # Get coordinate. 175 viewport = get_match_parent(widget, ["Viewport"]) 176 if viewport: 177 coordinate = widget.translate_coordinates(viewport, rect.x, rect.y) 178 if len(coordinate) == 2: 179 (offset_x, offset_y) = coordinate 180 return (-offset_x, -offset_y, viewport) 181 else: 182 return (0, 0, viewport) 183 else: 184 return (0, 0, viewport)
185
186 - def draw_mask(self, cr, x, y, w, h):
187 pass
188
189 - def tree_view_expose_event(self, widget, event):
190 cr = widget.window.cairo_create() 191 rect = widget.allocation 192 x, y, w, h = rect.x, rect.y, rect.width, rect.height 193 194 # Get offset. 195 (offset_x, offset_y, viewport) = self.get_offset_coordinate(widget) 196 197 198 # Draw background. 199 with cairo_state(cr): 200 scrolled_window = get_match_parent(self, ["ScrolledWindow"]) 201 cr.translate(-scrolled_window.allocation.x, -scrolled_window.allocation.y) 202 cr.rectangle(offset_x, offset_y, 203 scrolled_window.allocation.x + scrolled_window.allocation.width, 204 scrolled_window.allocation.y + scrolled_window.allocation.height) 205 cr.clip() 206 207 (shadow_x, shadow_y) = get_window_shadow_size(self.get_toplevel()) 208 skin_config.render_background(cr, self, offset_x + shadow_x, offset_y + shadow_y) 209 210 # Draw mask. 211 self.draw_mask(cr, offset_x, offset_y, viewport.allocation.width, viewport.allocation.height) 212 213 if self.press_draw_bool: 214 self.draw_y_padding = int(self.press_height) / self.height * self.height 215 draw_vlinear( 216 cr, 217 x, y + self.draw_y_padding, w, self.height, 218 ui_theme.get_shadow_color("tree_item_select").get_color_info()) 219 220 if self.move_draw_bool: 221 if int(self.press_height) / self.height * self.height != int(self.move_height) / self.height * self.height: 222 self.draw_y_padding = int(self.move_height) / self.height * self.height 223 draw_vlinear( 224 cr, 225 x, y + self.draw_y_padding, w, self.height, 226 ui_theme.get_shadow_color("tree_item_hover").get_color_info()) 227 228 if self.tree_list: 229 temp_height = 0 230 # (cr, text, font_size, font_color, x, y, width, height, font_align 231 for (widget_index, draw_widget) in enumerate(self.tree_list): 232 if draw_widget.text: 233 index = int(self.press_height) / self.height 234 if widget_index == index: 235 font_color = ui_theme.get_color("tree_item_select_font").get_color() 236 else: 237 font_color = ui_theme.get_color("tree_item_normal_font").get_color() 238 draw_text(cr, draw_widget.text, 239 self.font_x_padding + draw_widget.width, 240 temp_height + self.height/2, 241 self.font_width, 242 self.font_height, 243 self.font_size, 244 font_color, 245 alignment=self.font_align 246 ) 247 248 font_w, font_h = get_content_size(draw_widget.text, self.font_size) 249 index = int(self.press_height) / self.height 250 if draw_widget.tree_view_item.get_has_arrow(): 251 if not draw_widget.show_child_items_bool: 252 if widget_index == index: 253 pixbuf = self.normal_hover_pixbuf.get_pixbuf() 254 else: 255 pixbuf = self.normal_pixbuf.get_pixbuf() 256 draw_pixbuf(cr, pixbuf, 257 widget.allocation.width - self.arrow_x_padding - self.font_x_padding, 258 # draw_widget.width - self.arrow_x_padding + self.font_x_padding, 259 # font_w + self.font_x_padding + draw_widget.width + self.arrow_x_padding, 260 temp_height + (self.height - self.normal_pixbuf.get_pixbuf().get_height()) / 2) 261 else: 262 if widget_index == index: 263 pixbuf = self.press_hover_pixbuf.get_pixbuf() 264 else: 265 pixbuf = self.press_pixbuf.get_pixbuf() 266 draw_pixbuf(cr, pixbuf, 267 widget.allocation.width - self.arrow_x_padding - self.font_x_padding, 268 # draw_widget.width - self.arrow_x_padding + self.font_x_padding, 269 # font_w + self.font_x_padding + draw_widget.width + self.arrow_x_padding, 270 temp_height + (self.height - self.normal_pixbuf.get_pixbuf().get_height()) / 2) 271 272 temp_height += self.height
273 274
275 - def tree_view_key_press_event(self, widget, event):
276 pass
277
278 - def tree_view_leave_notify_event(self, widget, event):
279 self.move_draw_bool = False 280 self.move_index_num = None 281 self.queue_draw()
282
283 - def add_items(self, parent_id, child_items):
284 if not isinstance(child_items, (tuple, list, set)): 285 child_items = [ child_items ] 286 for child_item in child_items: 287 self.add_item(parent_id, child_item) 288 289 self.queue_draw()
290
291 - def add_item(self, parent_id, child_item):
292 temp_tree = self.create_tree(child_item) 293 294 temp_child_id = self.tree_id_num 295 296 if None == parent_id: 297 self.tree_id_list.append(self.tree_id_num) 298 self.tree_list.append(temp_tree) 299 300 self.root.add_node(parent_id, temp_child_id, temp_tree) 301 302 self.tree_id_num += 1 303 child_item.set_item_id(temp_child_id) 304 return temp_child_id 305
306 - def create_tree(self, child_item):
307 temp_tree = Tree() 308 temp_tree.id = self.tree_id_num 309 temp_tree.tree_view_item = child_item 310 temp_tree.child_items = {} 311 temp_tree.text = child_item.get_title() 312 return temp_tree
313 314
315 - def scan_item(self, item_id, node):
316 317 for key in node.keys(): 318 if node[key].id == item_id: 319 self.scan_save_item = node[key] 320 break 321 322 child_items = node[key].child_items 323 if child_items: 324 self.scan_item(item_id, child_items) 325 return self.scan_save_item
326
327 - def clear_scan_save_item(self):
328 self.scan_save_item = None
329
330 - def set_index_text(self, index, text):
331 self.tree_list[index].text = text 332 self.queue_draw() 333
334 - def del_item_index(self):
335 if self.move_index_num is not None: 336 self.del_item(self.tree_list[self.move_index_num].id) 337 index = int(self.press_height / self.height) 338 if self.move_index_num == index: 339 self.press_draw_bool = False 340 self.queue_draw()
341
342 - def del_item_from_index(self, index):
343 item_id = self.tree_list[index].id 344 self.del_item(item_id) 345
346 - def del_item(self, item_id):
347 if item_id is not None: 348 item = self.scan_item(item_id, self.root.child_items) 349 if not item: 350 return 351 352 item.child_items = {} 353 if item.parent_item: 354 del item.parent_item.child_items[item_id] 355 else: 356 del self.root.child_items[item_id] 357 else: 358 del self.root.child_items 359 del self.tree_list[:] 360 361 self.sort() 362 self.move_draw_bool = False 363 self.queue_draw() 364 self.clear_scan_save_item()
365
366 - def get_other_item(self, index):
367 other_item = [] 368 large_index = len(self.tree_list) 369 if large_index <= index: 370 return other_item 371 else: 372 for each_index, item in enumerate(self.tree_list): 373 if each_index != index: 374 other_item.append(item.tree_view_item) 375 return other_item 376
377 - def get_item_from_index(self, index):
378 return self.tree_list[index].tree_view_item 379
380 - def get_items(self, parent_id):
381 if parent_id is not None: 382 scan_results = self.scan_item(parent_id, self.root.child_items) 383 self.clear_scan_save_item() 384 return [item.tree_view_item for item in scan_results.child_items.values()] 385 else: 386 return [item.tree_view_item for item in self.root.child_items.values()] 387
388 - def set_text(self, item):
389 pass 390
391 - def get_text(self, item):
392 pass
393
394 - def clear(self):
395 del self.root.child_items 396 del self.tree_list[:] 397 398 self.move_draw_bool = False 399 self.queue_draw()
400
401 - def sort_all_nodes(self, nodes):
402 for key in nodes.keys(): 403 self.tree_all_node_list.append(nodes[key]) 404 if nodes[key].child_items: 405 self.sort_all_nodes(nodes[key].child_items) 406
407 - def get_all_items(self):
408 self.tree_all_node_list = [] 409 self.sort_all_nodes(self.root.child_items) 410 return self.tree_all_node_list 411
412 - def sort(self):
413 self.tree_list = [] 414 for key in self.root.child_items.keys(): 415 self.tree_list.append(self.root.child_items[key]) 416 if self.root.child_items[key].child_items: 417 self.sort2(self.root.child_items[key], self.width) 418
419 - def sort2(self, node, width):
420 for key in node.child_items.keys(): 421 if node.child_items[key].parent_item.show_child_items_bool: 422 node.child_items[key].width = width 423 self.tree_list.append(node.child_items[key]) 424 if node.child_items[key].child_items: 425 self.sort2(node.child_items[key], width + self.width)
426
427 -class Tree(object):
428 - def __init__(self):
429 self.id = None 430 431 self.tree_view_item = None 432 self.parent_item = None 433 434 self.child_items = OrderedDict() 435 self.child_items = {} 436 self.text = "" 437 self.show_child_items_bool = False 438 439 self.has_arrow = True 440 self.item_left_image = None 441 442 self.width = 0
443 444
445 - def add_node(self, root_id, node_id, node_item):
446 # Root node add child widget. 447 if None == root_id: 448 self.child_items[node_id] = node_item 449 else: 450 for key in self.child_items.keys(): 451 if key == root_id: 452 node_item.parent_item = self.child_items[key] # Save parent Node. 453 self.child_items[key].child_items[node_id] = node_item 454 return True 455 456 if self.scan_node(self.child_items[key], root_id, node_id, node_item): 457 return True
458
459 - def scan_node(self, root_node, root_id, node_id, node_item):
460 if root_node.child_items: 461 for key in root_node.child_items.keys(): 462 if key == root_id: 463 node_item.parent_item = root_node.child_items[key] # Save parent Node. 464 root_node.child_items[key].child_items[node_id] = node_item 465 return True 466 else: 467 self.scan_node(root_node.child_items[key], root_id, node_id, node_item)
468 469 gobject.type_register(TreeView) 470
471 -class TreeViewItem(object):
472 - def __init__(self, 473 item_title, 474 has_arrow=True):
475 self.item_title = item_title 476 self.has_arrow = has_arrow 477 self.item_id = None
478
479 - def get_title(self):
480 return self.item_title 481
482 - def get_has_arrow(self):
483 return self.has_arrow
484
485 - def set_item_id(self, new_id):
486 self.item_id = new_id
487
488 - def get_item_id(self):
489 return self.item_id 490