1# vim: set fileencoding=utf-8 : 2 3# ***********************IMPORTANT NMAP LICENSE TERMS************************ 4# * * 5# * The Nmap Security Scanner is (C) 1996-2020 Insecure.Com LLC ("The Nmap * 6# * Project"). Nmap is also a registered trademark of the Nmap Project. * 7# * * 8# * This program is distributed under the terms of the Nmap Public Source * 9# * License (NPSL). The exact license text applying to a particular Nmap * 10# * release or source code control revision is contained in the LICENSE * 11# * file distributed with that version of Nmap or source code control * 12# * revision. More Nmap copyright/legal information is available from * 13# * https://nmap.org/book/man-legal.html, and further information on the * 14# * NPSL license itself can be found at https://nmap.org/npsl. This header * 15# * summarizes some key points from the Nmap license, but is no substitute * 16# * for the actual license text. * 17# * * 18# * Nmap is generally free for end users to download and use themselves, * 19# * including commercial use. It is available from https://nmap.org. * 20# * * 21# * The Nmap license generally prohibits companies from using and * 22# * redistributing Nmap in commercial products, but we sell a special Nmap * 23# * OEM Edition with a more permissive license and special features for * 24# * this purpose. See https://nmap.org/oem * 25# * * 26# * If you have received a written Nmap license agreement or contract * 27# * stating terms other than these (such as an Nmap OEM license), you may * 28# * choose to use and redistribute Nmap under those terms instead. * 29# * * 30# * The official Nmap Windows builds include the Npcap software * 31# * (https://npcap.org) for packet capture and transmission. It is under * 32# * separate license terms which forbid redistribution without special * 33# * permission. So the official Nmap Windows builds may not be * 34# * redistributed without special permission (such as an Nmap OEM * 35# * license). * 36# * * 37# * Source is provided to this software because we believe users have a * 38# * right to know exactly what a program is going to do before they run it. * 39# * This also allows you to audit the software for security holes. * 40# * * 41# * Source code also allows you to port Nmap to new platforms, fix bugs, * 42# * and add new features. You are highly encouraged to submit your * 43# * changes as a Github PR or by email to the dev@nmap.org mailing list * 44# * for possible incorporation into the main distribution. Unless you * 45# * specify otherwise, it is understood that you are offering us very * 46# * broad rights to use your submissions as described in the Nmap Public * 47# * Source License Contributor Agreement. This is important because we * 48# * fund the project by selling licenses with various terms, and also * 49# * because the inability to relicense code has caused devastating * 50# * problems for other Free Software projects (such as KDE and NASM). * 51# * * 52# * The free version of Nmap is distributed in the hope that it will be * 53# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * 54# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, * 55# * indemnification and commercial support are all available through the * 56# * Npcap OEM program--see https://nmap.org/oem. * 57# * * 58# ***************************************************************************/ 59 60import gtk 61import math 62import gobject 63 64import radialnet.util.geometry as geometry 65 66from radialnet.bestwidgets.boxes import * 67from radialnet.core.Coordinate import PolarCoordinate 68import radialnet.gui.RadialNet as RadialNet 69from radialnet.bestwidgets.expanders import BWExpander 70 71 72OPTIONS = ['address', 73 'hostname', 74 'icon', 75 'latency', 76 'ring', 77 'region', 78 'slow in/out'] 79 80REFRESH_RATE = 500 81 82 83class ControlWidget(BWVBox): 84 """ 85 """ 86 def __init__(self, radialnet): 87 """ 88 """ 89 BWVBox.__init__(self) 90 self.set_border_width(6) 91 92 self.radialnet = radialnet 93 94 self.__create_widgets() 95 96 def __create_widgets(self): 97 """ 98 """ 99 self.__action = ControlAction(self.radialnet) 100 self.__interpolation = ControlInterpolation(self.radialnet) 101 self.__layout = ControlLayout(self.radialnet) 102 self.__view = ControlView(self.radialnet) 103 104 self.bw_pack_start_noexpand_nofill(self.__action) 105 self.bw_pack_start_noexpand_nofill(self.__interpolation) 106 self.bw_pack_start_noexpand_nofill(self.__layout) 107 self.bw_pack_start_noexpand_nofill(self.__view) 108 109 110def try_set_tooltip_text(widget, text): 111 try: 112 widget.set_tooltip_text(text) 113 except AttributeError: 114 # The set_tooltip_text method was introduced in PyGTK 2.12. 115 pass 116 117 118class ControlAction(BWExpander): 119 """ 120 """ 121 def __init__(self, radialnet): 122 """ 123 """ 124 BWExpander.__init__(self, _('Action')) 125 self.set_expanded(True) 126 127 self.radialnet = radialnet 128 129 self.__create_widgets() 130 131 def __create_widgets(self): 132 """ 133 """ 134 self.__tbox = BWTable(1, 4) 135 self.__tbox.bw_set_spacing(0) 136 self.__vbox = BWVBox() 137 138 self.__jump_to = gtk.RadioToolButton(None, gtk.STOCK_JUMP_TO) 139 try_set_tooltip_text(self.__jump_to, 'Change focus') 140 self.__jump_to.connect('toggled', 141 self.__change_pointer, 142 RadialNet.POINTER_JUMP_TO) 143 144 try: 145 # gtk.STOCK_INFO is available only in PyGTK 2.8 and later. 146 info_icon = gtk.STOCK_INFO 147 except AttributeError: 148 self.__info = gtk.RadioToolButton(self.__jump_to, None) 149 self.__info.set_label(_("Info")) 150 else: 151 self.__info = gtk.RadioToolButton(self.__jump_to, info_icon) 152 try_set_tooltip_text(self.__info, 'Show information') 153 self.__info.connect('toggled', 154 self.__change_pointer, 155 RadialNet.POINTER_INFO) 156 157 self.__group = gtk.RadioToolButton(self.__jump_to, gtk.STOCK_ADD) 158 try_set_tooltip_text(self.__group, 'Group children') 159 self.__group.connect('toggled', 160 self.__change_pointer, 161 RadialNet.POINTER_GROUP) 162 163 self.__region = gtk.RadioToolButton(self.__jump_to, 164 gtk.STOCK_SELECT_COLOR) 165 try_set_tooltip_text(self.__region, 'Fill region') 166 self.__region.connect('toggled', 167 self.__change_pointer, 168 RadialNet.POINTER_FILL) 169 170 self.__region_color = gtk.combo_box_new_text() 171 self.__region_color.append_text(_('Red')) 172 self.__region_color.append_text(_('Yellow')) 173 self.__region_color.append_text(_('Green')) 174 self.__region_color.connect('changed', self.__change_region) 175 self.__region_color.set_active(self.radialnet.get_region_color()) 176 177 self.__tbox.bw_attach_next(self.__jump_to) 178 self.__tbox.bw_attach_next(self.__info) 179 self.__tbox.bw_attach_next(self.__group) 180 self.__tbox.bw_attach_next(self.__region) 181 182 self.__vbox.bw_pack_start_noexpand_nofill(self.__tbox) 183 self.__vbox.bw_pack_start_noexpand_nofill(self.__region_color) 184 185 self.bw_add(self.__vbox) 186 187 self.__jump_to.set_active(True) 188 self.__region_color.set_no_show_all(True) 189 self.__region_color.hide() 190 191 def __change_pointer(self, widget, pointer): 192 """ 193 """ 194 if pointer != self.radialnet.get_pointer_status(): 195 self.radialnet.set_pointer_status(pointer) 196 197 if pointer == RadialNet.POINTER_FILL: 198 self.__region_color.show() 199 else: 200 self.__region_color.hide() 201 202 def __change_region(self, widget): 203 """ 204 """ 205 self.radialnet.set_region_color(self.__region_color.get_active()) 206 207 208class ControlVariableWidget(gtk.DrawingArea): 209 """ 210 """ 211 def __init__(self, name, value, update, increment=1): 212 """ 213 """ 214 gtk.DrawingArea.__init__(self) 215 216 self.__variable_name = name 217 self.__value = value 218 self.__update = update 219 self.__increment_pass = increment 220 221 self.__radius = 6 222 self.__increment_time = 100 223 224 self.__pointer_position = 0 225 self.__active_increment = False 226 227 self.__last_value = self.__value() 228 229 self.connect('expose_event', self.expose) 230 self.connect('button_press_event', self.button_press) 231 self.connect('button_release_event', self.button_release) 232 self.connect('motion_notify_event', self.motion_notify) 233 234 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | 235 gtk.gdk.BUTTON_RELEASE_MASK | 236 gtk.gdk.MOTION_NOTIFY | 237 gtk.gdk.POINTER_MOTION_HINT_MASK | 238 gtk.gdk.POINTER_MOTION_MASK) 239 240 gobject.timeout_add(REFRESH_RATE, self.verify_value) 241 242 def verify_value(self): 243 """ 244 """ 245 if self.__value() != self.__last_value: 246 self.__last_value = self.__value() 247 248 self.queue_draw() 249 250 return True 251 252 def button_press(self, widget, event): 253 """ 254 """ 255 self.__active_increment = False 256 pointer = self.get_pointer() 257 258 if self.__button_is_clicked(pointer) and event.button == 1: 259 260 event.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) 261 self.__active_increment = True 262 self.__increment_value() 263 264 def button_release(self, widget, event): 265 """ 266 """ 267 event.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) 268 269 self.__active_increment = False 270 self.__pointer_position = 0 271 272 self.queue_draw() 273 274 def motion_notify(self, widget, event): 275 """ 276 Drawing callback 277 @type widget: GtkWidget 278 @param widget: Gtk widget superclass 279 @type event: GtkEvent 280 @param event: Gtk event of widget 281 @rtype: boolean 282 @return: Indicator of the event propagation 283 """ 284 if self.__active_increment: 285 286 xc, yc = self.__center_of_widget 287 x, _ = self.get_pointer() 288 289 if x - self.__radius > 0 and x + self.__radius < 2 * xc: 290 self.__pointer_position = x - xc 291 292 self.queue_draw() 293 294 def expose(self, widget, event): 295 """ 296 Drawing callback 297 @type widget: GtkWidget 298 @param widget: Gtk widget superclass 299 @type event: GtkEvent 300 @param event: Gtk event of widget 301 @rtype: boolean 302 @return: Indicator of the event propagation 303 """ 304 self.set_size_request(100, 30) 305 306 self.context = widget.window.cairo_create() 307 self.__draw() 308 309 return True 310 311 def __draw(self): 312 """ 313 """ 314 allocation = self.get_allocation() 315 316 self.__center_of_widget = (allocation.width / 2, 317 allocation.height / 2) 318 319 xc, yc = self.__center_of_widget 320 321 # draw line 322 self.context.set_line_width(1) 323 self.context.set_dash([1, 2]) 324 self.context.move_to(self.__radius, 325 yc + self.__radius) 326 self.context.line_to(2 * xc - 5, 327 yc + self.__radius) 328 self.context.stroke() 329 330 # draw text 331 self.context.set_dash([1, 0]) 332 self.context.set_font_size(10) 333 334 self.context.move_to(5, yc - self.__radius) 335 self.context.show_text(self.__variable_name) 336 337 width = self.context.text_extents(str(self.__value()))[2] 338 self.context.move_to(2 * xc - width - 5, yc - self.__radius) 339 self.context.show_text(str(self.__value())) 340 341 self.context.set_line_width(1) 342 self.context.stroke() 343 344 # draw node 345 self.context.arc(xc + self.__pointer_position, 346 yc + self.__radius, 347 self.__radius, 0, 2 * math.pi) 348 if self.__active_increment: 349 self.context.set_source_rgb(0.0, 0.0, 0.0) 350 else: 351 self.context.set_source_rgb(1.0, 1.0, 1.0) 352 self.context.fill_preserve() 353 self.context.set_source_rgb(0.0, 0.0, 0.0) 354 self.context.stroke() 355 356 def __button_is_clicked(self, pointer): 357 """ 358 """ 359 xc, yc = self.__center_of_widget 360 center = (xc, yc + self.__radius) 361 362 return geometry.is_in_circle(pointer, 6, center) 363 364 def __increment_value(self): 365 """ 366 """ 367 self.__update(self.__value() + self.__pointer_position / 4) 368 369 self.queue_draw() 370 371 if self.__active_increment: 372 373 gobject.timeout_add(self.__increment_time, 374 self.__increment_value) 375 376 def set_value_function(self, value): 377 """ 378 """ 379 self.__value = value 380 381 def set_update_function(self, update): 382 """ 383 """ 384 self.__update = update 385 386 387class ControlVariable(BWHBox): 388 """ 389 """ 390 def __init__(self, name, get_function, set_function, increment=1): 391 """ 392 """ 393 BWHBox.__init__(self, spacing=0) 394 395 self.__increment_pass = increment 396 self.__increment_time = 200 397 self.__increment = False 398 399 self.__name = name 400 self.__get_function = get_function 401 self.__set_function = set_function 402 403 self.__create_widgets() 404 405 def __create_widgets(self): 406 """ 407 """ 408 self.__control = ControlVariableWidget(self.__name, 409 self.__get_function, 410 self.__set_function, 411 self.__increment_pass) 412 413 self.__left_button = gtk.Button() 414 self.__left_button.set_size_request(20, 20) 415 self.__left_arrow = gtk.Arrow(gtk.ARROW_LEFT, gtk.SHADOW_NONE) 416 self.__left_button.add(self.__left_arrow) 417 self.__left_button.connect('pressed', 418 self.__pressed, 419 -self.__increment_pass) 420 self.__left_button.connect('released', self.__released) 421 422 self.__right_button = gtk.Button() 423 self.__right_button.set_size_request(20, 20) 424 self.__right_arrow = gtk.Arrow(gtk.ARROW_RIGHT, gtk.SHADOW_NONE) 425 self.__right_button.add(self.__right_arrow) 426 self.__right_button.connect('pressed', 427 self.__pressed, 428 self.__increment_pass) 429 self.__right_button.connect('released', self.__released) 430 431 self.bw_pack_start_noexpand_nofill(self.__left_button) 432 self.bw_pack_start_expand_fill(self.__control) 433 self.bw_pack_start_noexpand_nofill(self.__right_button) 434 435 def __pressed(self, widget, increment): 436 """ 437 """ 438 self.__increment = True 439 self.__increment_function(increment) 440 441 def __increment_function(self, increment): 442 """ 443 """ 444 if self.__increment: 445 446 self.__set_function(self.__get_function() + increment) 447 self.__control.verify_value() 448 449 gobject.timeout_add(self.__increment_time, 450 self.__increment_function, 451 increment) 452 453 def __released(self, widget): 454 """ 455 """ 456 self.__increment = False 457 458 459class ControlFisheye(BWVBox): 460 """ 461 """ 462 def __init__(self, radialnet): 463 """ 464 """ 465 BWVBox.__init__(self) 466 self.set_border_width(6) 467 468 self.radialnet = radialnet 469 self.__ring_max_value = self.radialnet.get_number_of_rings() 470 471 self.__create_widgets() 472 473 def __create_widgets(self): 474 """ 475 """ 476 self.__params = BWHBox() 477 478 self.__fisheye_label = gtk.Label(_('<b>Fisheye</b> on ring')) 479 self.__fisheye_label.set_use_markup(True) 480 481 self.__ring = gtk.Adjustment(0, 0, self.__ring_max_value, 0.01, 0.01) 482 483 self.__ring_spin = gtk.SpinButton(self.__ring) 484 self.__ring_spin.set_digits(2) 485 486 self.__ring_scale = gtk.HScale(self.__ring) 487 self.__ring_scale.set_size_request(100, -1) 488 self.__ring_scale.set_digits(2) 489 self.__ring_scale.set_value_pos(gtk.POS_LEFT) 490 self.__ring_scale.set_draw_value(False) 491 self.__ring_scale.set_update_policy(gtk.UPDATE_CONTINUOUS) 492 493 self.__interest_label = gtk.Label(_('with interest factor')) 494 self.__interest = gtk.Adjustment(0, 0, 10, 0.01) 495 self.__interest_spin = gtk.SpinButton(self.__interest) 496 self.__interest_spin.set_digits(2) 497 498 self.__spread_label = gtk.Label(_('and spread factor')) 499 self.__spread = gtk.Adjustment(0, -1.0, 1.0, 0.01, 0.01) 500 self.__spread_spin = gtk.SpinButton(self.__spread) 501 self.__spread_spin.set_digits(2) 502 503 self.__params.bw_pack_start_noexpand_nofill(self.__fisheye_label) 504 self.__params.bw_pack_start_noexpand_nofill(self.__ring_spin) 505 self.__params.bw_pack_start_expand_fill(self.__ring_scale) 506 self.__params.bw_pack_start_noexpand_nofill(self.__interest_label) 507 self.__params.bw_pack_start_noexpand_nofill(self.__interest_spin) 508 self.__params.bw_pack_start_noexpand_nofill(self.__spread_label) 509 self.__params.bw_pack_start_noexpand_nofill(self.__spread_spin) 510 511 self.bw_pack_start_noexpand_nofill(self.__params) 512 513 self.__ring.connect('value_changed', self.__change_ring) 514 self.__interest.connect('value_changed', self.__change_interest) 515 self.__spread.connect('value_changed', self.__change_spread) 516 517 gobject.timeout_add(REFRESH_RATE, self.__update_fisheye) 518 519 def __update_fisheye(self): 520 """ 521 """ 522 # adjust ring scale to radialnet number of nodes 523 ring_max_value = self.radialnet.get_number_of_rings() - 1 524 525 if ring_max_value != self.__ring_max_value: 526 527 value = self.__ring.get_value() 528 529 if value == 0 and ring_max_value != 0: 530 value = 1 531 532 elif value > ring_max_value: 533 value = ring_max_value 534 535 self.__ring.set_all(value, 1, ring_max_value, 0.01, 0.01, 0) 536 self.__ring_max_value = ring_max_value 537 538 self.__ring_scale.queue_draw() 539 540 # check ring value 541 ring_value = self.radialnet.get_fisheye_ring() 542 543 if self.__ring.get_value() != ring_value: 544 self.__ring.set_value(ring_value) 545 546 # check interest value 547 interest_value = self.radialnet.get_fisheye_interest() 548 549 if self.__interest.get_value() != interest_value: 550 self.__interest.set_value(interest_value) 551 552 # check spread value 553 spread_value = self.radialnet.get_fisheye_spread() 554 555 if self.__spread.get_value() != spread_value: 556 self.__spread.set_value(spread_value) 557 558 return True 559 560 def active_fisheye(self): 561 """ 562 """ 563 self.radialnet.set_fisheye(True) 564 self.__change_ring() 565 self.__change_interest() 566 567 def deactive_fisheye(self): 568 """ 569 """ 570 self.radialnet.set_fisheye(False) 571 572 def __change_ring(self, widget=None): 573 """ 574 """ 575 if not self.radialnet.is_in_animation(): 576 self.radialnet.set_fisheye_ring(self.__ring.get_value()) 577 else: 578 self.__ring.set_value(self.radialnet.get_fisheye_ring()) 579 580 def __change_interest(self, widget=None): 581 """ 582 """ 583 if not self.radialnet.is_in_animation(): 584 self.radialnet.set_fisheye_interest(self.__interest.get_value()) 585 else: 586 self.__interest.set_value(self.radialnet.get_fisheye_interest()) 587 588 def __change_spread(self, widget=None): 589 """ 590 """ 591 if not self.radialnet.is_in_animation(): 592 self.radialnet.set_fisheye_spread(self.__spread.get_value()) 593 else: 594 self.__spread.set_value(self.radialnet.get_fisheye_spread()) 595 596 597class ControlInterpolation(BWExpander): 598 """ 599 """ 600 def __init__(self, radialnet): 601 """ 602 """ 603 BWExpander.__init__(self, _('Interpolation')) 604 605 self.radialnet = radialnet 606 607 self.__create_widgets() 608 609 def __create_widgets(self): 610 """ 611 """ 612 self.__vbox = BWVBox() 613 614 self.__cartesian_radio = gtk.RadioButton(None, _('Cartesian')) 615 self.__polar_radio = gtk.RadioButton( 616 self.__cartesian_radio, _('Polar')) 617 self.__cartesian_radio.connect('toggled', 618 self.__change_system, 619 RadialNet.INTERPOLATION_CARTESIAN) 620 self.__polar_radio.connect('toggled', 621 self.__change_system, 622 RadialNet.INTERPOLATION_POLAR) 623 624 self.__system_box = BWHBox() 625 self.__system_box.bw_pack_start_noexpand_nofill(self.__polar_radio) 626 self.__system_box.bw_pack_start_noexpand_nofill(self.__cartesian_radio) 627 628 self.__frames_box = BWHBox() 629 self.__frames_label = gtk.Label(_('Frames')) 630 self.__frames_label.set_alignment(0.0, 0.5) 631 self.__frames = gtk.Adjustment(self.radialnet.get_number_of_frames(), 632 1, 633 1000, 634 1) 635 self.__frames.connect('value_changed', self.__change_frames) 636 self.__frames_spin = gtk.SpinButton(self.__frames) 637 self.__frames_box.bw_pack_start_expand_fill(self.__frames_label) 638 self.__frames_box.bw_pack_start_noexpand_nofill(self.__frames_spin) 639 640 self.__vbox.bw_pack_start_noexpand_nofill(self.__frames_box) 641 self.__vbox.bw_pack_start_noexpand_nofill(self.__system_box) 642 643 self.bw_add(self.__vbox) 644 645 gobject.timeout_add(REFRESH_RATE, self.__update_animation) 646 647 def __update_animation(self): 648 """ 649 """ 650 active = self.radialnet.get_interpolation() 651 652 if active == RadialNet.INTERPOLATION_CARTESIAN: 653 self.__cartesian_radio.set_active(True) 654 655 else: 656 self.__polar_radio.set_active(True) 657 658 return True 659 660 def __change_system(self, widget, value): 661 """ 662 """ 663 if not self.radialnet.set_interpolation(value): 664 665 active = self.radialnet.get_interpolation() 666 667 if active == RadialNet.INTERPOLATION_CARTESIAN: 668 self.__cartesian_radio.set_active(True) 669 670 else: 671 self.__polar_radio.set_active(True) 672 673 def __change_frames(self, widget): 674 """ 675 """ 676 if not self.radialnet.set_number_of_frames(self.__frames.get_value()): 677 self.__frames.set_value(self.radialnet.get_number_of_frames()) 678 679 680class ControlLayout(BWExpander): 681 """ 682 """ 683 def __init__(self, radialnet): 684 """ 685 """ 686 BWExpander.__init__(self, _('Layout')) 687 688 self.radialnet = radialnet 689 690 self.__create_widgets() 691 692 def __create_widgets(self): 693 """ 694 """ 695 self.__hbox = BWHBox() 696 697 self.__layout = gtk.combo_box_new_text() 698 self.__layout.append_text(_('Symmetric')) 699 self.__layout.append_text(_('Weighted')) 700 self.__layout.set_active(self.radialnet.get_layout()) 701 self.__layout.connect('changed', self.__change_layout) 702 self.__force = gtk.ToolButton(gtk.STOCK_REFRESH) 703 self.__force.connect('clicked', self.__force_update) 704 705 self.__hbox.bw_pack_start_expand_fill(self.__layout) 706 self.__hbox.bw_pack_start_noexpand_nofill(self.__force) 707 708 self.bw_add(self.__hbox) 709 710 self.__check_layout() 711 712 def __check_layout(self): 713 """ 714 """ 715 if self.__layout.get_active() == RadialNet.LAYOUT_WEIGHTED: 716 self.__force.set_sensitive(True) 717 718 else: 719 self.__force.set_sensitive(False) 720 721 return True 722 723 def __force_update(self, widget): 724 """ 725 """ 726 self.__fisheye_ring = self.radialnet.get_fisheye_ring() 727 self.radialnet.update_layout() 728 729 def __change_layout(self, widget): 730 """ 731 """ 732 if not self.radialnet.set_layout(self.__layout.get_active()): 733 self.__layout.set_active(self.radialnet.get_layout()) 734 735 else: 736 self.__check_layout() 737 738 739class ControlRingGap(BWVBox): 740 """ 741 """ 742 def __init__(self, radialnet): 743 """ 744 """ 745 BWVBox.__init__(self) 746 747 self.radialnet = radialnet 748 749 self.__create_widgets() 750 751 def __create_widgets(self): 752 """ 753 """ 754 self.__radius = ControlVariable(_('Ring gap'), 755 self.radialnet.get_ring_gap, 756 self.radialnet.set_ring_gap) 757 758 self.__label = gtk.Label(_('Lower ring gap')) 759 self.__label.set_alignment(0.0, 0.5) 760 self.__adjustment = gtk.Adjustment(self.radialnet.get_min_ring_gap(), 761 0, 762 50, 763 1) 764 self.__spin = gtk.SpinButton(self.__adjustment) 765 self.__spin.connect('value_changed', self.__change_lower) 766 767 self.__lower_hbox = BWHBox() 768 self.__lower_hbox.bw_pack_start_expand_fill(self.__label) 769 self.__lower_hbox.bw_pack_start_noexpand_nofill(self.__spin) 770 771 self.bw_pack_start_noexpand_nofill(self.__radius) 772 self.bw_pack_start_noexpand_nofill(self.__lower_hbox) 773 774 def __change_lower(self, widget): 775 """ 776 """ 777 if not self.radialnet.set_min_ring_gap(self.__adjustment.get_value()): 778 self.__adjustment.set_value(self.radialnet.get_min_ring_gap()) 779 780 781class ControlOptions(BWScrolledWindow): 782 """ 783 """ 784 def __init__(self, radialnet): 785 """ 786 """ 787 BWScrolledWindow.__init__(self) 788 789 self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) 790 self.set_shadow_type(gtk.SHADOW_NONE) 791 792 self.radialnet = radialnet 793 794 self.enable_labels = True 795 796 self.__create_widgets() 797 798 def __create_widgets(self): 799 """ 800 """ 801 self.__liststore = gtk.ListStore(gobject.TYPE_BOOLEAN, 802 gobject.TYPE_STRING) 803 804 self.__liststore.append([None, OPTIONS[0]]) 805 self.__liststore.append([None, OPTIONS[1]]) 806 self.__liststore.append([None, OPTIONS[2]]) 807 self.__liststore.append([None, OPTIONS[3]]) 808 self.__liststore.append([None, OPTIONS[4]]) 809 self.__liststore.append([None, OPTIONS[5]]) 810 self.__liststore.append([None, OPTIONS[6]]) 811 812 self.__cell_toggle = gtk.CellRendererToggle() 813 self.__cell_toggle.set_property('activatable', True) 814 self.__cell_toggle.connect('toggled', 815 self.__change_option, 816 self.__liststore) 817 818 self.__column_toggle = gtk.TreeViewColumn('', self.__cell_toggle) 819 self.__column_toggle.add_attribute(self.__cell_toggle, 'active', 0) 820 self.__column_toggle.set_cell_data_func(self.__cell_toggle, self.__cell_toggle_data_method) 821 822 self.__cell_text = gtk.CellRendererText() 823 824 self.__column_text = gtk.TreeViewColumn(None, 825 self.__cell_text, 826 text=1) 827 self.__column_text.set_cell_data_func(self.__cell_text, self.__cell_text_data_method) 828 829 self.__treeview = gtk.TreeView(self.__liststore) 830 self.__treeview.set_enable_search(True) 831 self.__treeview.set_search_column(1) 832 self.__treeview.set_headers_visible(False) 833 self.__treeview.append_column(self.__column_toggle) 834 self.__treeview.append_column(self.__column_text) 835 836 self.add_with_viewport(self.__treeview) 837 838 gobject.timeout_add(REFRESH_RATE, self.__update_options) 839 840 def __cell_toggle_data_method(self, column, cell, model, it): 841 if not self.enable_labels and model.get_value(it, 1) == 'hostname': 842 cell.set_property('activatable', False) 843 else: 844 cell.set_property('activatable', True) 845 846 def __cell_text_data_method(self, column, cell, model, it): 847 if not self.enable_labels and model.get_value(it, 1) == 'hostname': 848 cell.set_property('strikethrough', True) 849 else: 850 cell.set_property('strikethrough', False) 851 852 def __update_options(self): 853 """ 854 """ 855 model = self.__liststore 856 857 model[OPTIONS.index('address')][0] = self.radialnet.get_show_address() 858 model[OPTIONS.index('hostname')][0] = self.enable_labels and \ 859 self.radialnet.get_show_hostname() 860 model[OPTIONS.index('icon')][0] = self.radialnet.get_show_icon() 861 model[OPTIONS.index('latency')][0] = self.radialnet.get_show_latency() 862 model[OPTIONS.index('ring')][0] = self.radialnet.get_show_ring() 863 model[OPTIONS.index('region')][0] = self.radialnet.get_show_region() 864 model[OPTIONS.index('slow in/out')][0] = \ 865 self.radialnet.get_slow_inout() 866 867 return True 868 869 def __change_option(self, cell, option, model): 870 """ 871 """ 872 option = int(option) 873 model[option][0] = not model[option][0] 874 875 if OPTIONS[option] == 'address': 876 self.radialnet.set_show_address(model[option][0]) 877 if model[option][0]: 878 model[OPTIONS.index('hostname')][0] = self.radialnet.get_show_hostname() 879 else: 880 model[OPTIONS.index('hostname')][0] = False 881 self.enable_labels = model[option][0] 882 883 elif OPTIONS[option] == 'hostname': 884 self.radialnet.set_show_hostname(model[option][0]) 885 886 elif OPTIONS[option] == 'icon': 887 self.radialnet.set_show_icon(model[option][0]) 888 889 elif OPTIONS[option] == 'latency': 890 self.radialnet.set_show_latency(model[option][0]) 891 892 elif OPTIONS[option] == 'ring': 893 self.radialnet.set_show_ring(model[option][0]) 894 895 elif OPTIONS[option] == 'region': 896 self.radialnet.set_show_region(model[option][0]) 897 898 elif OPTIONS[option] == 'slow in/out': 899 self.radialnet.set_slow_inout(model[option][0]) 900 901 902class ControlView(BWExpander): 903 """ 904 """ 905 def __init__(self, radialnet): 906 """ 907 """ 908 BWExpander.__init__(self, _('View')) 909 self.set_expanded(True) 910 911 self.radialnet = radialnet 912 913 self.__create_widgets() 914 915 def __create_widgets(self): 916 """ 917 """ 918 self.__vbox = BWVBox(spacing=0) 919 920 self.__zoom = ControlVariable(_('Zoom'), 921 self.radialnet.get_zoom, 922 self.radialnet.set_zoom) 923 924 self.__ring_gap = ControlRingGap(self.radialnet) 925 self.__navigation = ControlNavigation(self.radialnet) 926 927 self.__options = ControlOptions(self.radialnet) 928 self.__options.set_border_width(0) 929 930 self.__vbox.bw_pack_start_expand_nofill(self.__options) 931 self.__vbox.bw_pack_start_noexpand_nofill(self.__navigation) 932 self.__vbox.bw_pack_start_noexpand_nofill(self.__zoom) 933 self.__vbox.bw_pack_start_noexpand_nofill(self.__ring_gap) 934 935 self.bw_add(self.__vbox) 936 937 938class ControlNavigation(gtk.DrawingArea): 939 """ 940 """ 941 def __init__(self, radialnet): 942 """ 943 """ 944 gtk.DrawingArea.__init__(self) 945 946 self.radialnet = radialnet 947 948 self.__rotate_node = PolarCoordinate() 949 self.__rotate_node.set_coordinate(40, 90) 950 self.__center_of_widget = (50, 50) 951 self.__moving = None 952 self.__centering = False 953 self.__rotating = False 954 self.__move_pass = 100 955 956 self.__move_position = (0, 0) 957 self.__move_addition = [(-1, 0), 958 (-1, -1), 959 (0, -1), 960 (1, -1), 961 (1, 0), 962 (1, 1), 963 (0, 1), 964 (-1, 1)] 965 966 self.__move_factor = 1 967 self.__move_factor_limit = 20 968 969 self.__rotate_radius = 6 970 self.__move_radius = 6 971 972 self.__rotate_clicked = False 973 self.__move_clicked = None 974 975 self.connect('expose_event', self.expose) 976 self.connect('button_press_event', self.button_press) 977 self.connect('button_release_event', self.button_release) 978 self.connect('motion_notify_event', self.motion_notify) 979 self.connect('enter_notify_event', self.enter_notify) 980 self.connect('leave_notify_event', self.leave_notify) 981 self.connect('key_press_event', self.key_press) 982 self.connect('key_release_event', self.key_release) 983 984 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | 985 gtk.gdk.BUTTON_RELEASE_MASK | 986 gtk.gdk.ENTER_NOTIFY | 987 gtk.gdk.LEAVE_NOTIFY | 988 gtk.gdk.MOTION_NOTIFY | 989 gtk.gdk.NOTHING | 990 gtk.gdk.KEY_PRESS_MASK | 991 gtk.gdk.KEY_RELEASE_MASK | 992 gtk.gdk.POINTER_MOTION_HINT_MASK | 993 gtk.gdk.POINTER_MOTION_MASK) 994 995 self.__rotate_node.set_coordinate(40, self.radialnet.get_rotation()) 996 997 def key_press(self, widget, event): 998 """ 999 """ 1000 # key = gtk.gdk.keyval_name(event.keyval) 1001 1002 self.queue_draw() 1003 1004 return True 1005 1006 def key_release(self, widget, event): 1007 """ 1008 """ 1009 # key = gtk.gdk.keyval_name(event.keyval) 1010 1011 self.queue_draw() 1012 1013 return True 1014 1015 def enter_notify(self, widget, event): 1016 """ 1017 """ 1018 return False 1019 1020 def leave_notify(self, widget, event): 1021 """ 1022 """ 1023 self.queue_draw() 1024 1025 return False 1026 1027 def button_press(self, widget, event): 1028 """ 1029 Drawing callback 1030 @type widget: GtkWidget 1031 @param widget: Gtk widget superclass 1032 @type event: GtkEvent 1033 @param event: Gtk event of widget 1034 @rtype: boolean 1035 @return: Indicator of the event propagation 1036 """ 1037 pointer = self.get_pointer() 1038 1039 direction = False 1040 1041 if self.__rotate_is_clicked(pointer): 1042 1043 event.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) 1044 self.__rotating = True 1045 1046 direction = self.__move_is_clicked(pointer) 1047 1048 if direction is not None and self.__moving is None: 1049 1050 event.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) 1051 self.__moving = direction 1052 self.__move_in_direction(direction) 1053 1054 if self.__center_is_clicked(pointer): 1055 1056 event.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) 1057 self.__centering = True 1058 self.__move_position = (0, 0) 1059 self.radialnet.set_translation(self.__move_position) 1060 1061 self.queue_draw() 1062 1063 return False 1064 1065 def button_release(self, widget, event): 1066 """ 1067 Drawing callback 1068 @type widget: GtkWidget 1069 @param widget: Gtk widget superclass 1070 @type event: GtkEvent 1071 @param event: Gtk event of widget 1072 @rtype: boolean 1073 @return: Indicator of the event propagation 1074 """ 1075 self.__moving = None # stop moving 1076 self.__centering = False 1077 self.__rotating = False # stop rotate 1078 self.__move_factor = 1 1079 1080 event.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) 1081 1082 self.queue_draw() 1083 1084 return False 1085 1086 def motion_notify(self, widget, event): 1087 """ 1088 Drawing callback 1089 @type widget: GtkWidget 1090 @param widget: Gtk widget superclass 1091 @type event: GtkEvent 1092 @param event: Gtk event of widget 1093 @rtype: boolean 1094 @return: Indicator of the event propagation 1095 """ 1096 xc, yc = self.__center_of_widget 1097 x, y = self.get_pointer() 1098 1099 status = not self.radialnet.is_in_animation() 1100 status = status and not self.radialnet.is_empty() 1101 1102 if self.__rotating and status: 1103 1104 r, t = self.__rotate_node.get_coordinate() 1105 t = math.degrees(math.atan2(yc - y, x - xc)) 1106 1107 if t < 0: 1108 t = 360 + t 1109 1110 self.radialnet.set_rotation(t) 1111 self.__rotate_node.set_coordinate(r, t) 1112 1113 self.queue_draw() 1114 1115 return False 1116 1117 def expose(self, widget, event): 1118 """ 1119 Drawing callback 1120 @type widget: GtkWidget 1121 @param widget: Gtk widget superclass 1122 @type event: GtkEvent 1123 @param event: Gtk event of widget 1124 @rtype: boolean 1125 @return: Indicator of the event propagation 1126 """ 1127 self.set_size_request(120, 130) 1128 1129 self.context = widget.window.cairo_create() 1130 self.__draw() 1131 1132 return False 1133 1134 def __draw_rotate_control(self): 1135 """ 1136 """ 1137 xc, yc = self.__center_of_widget 1138 r, t = self.__rotate_node.get_coordinate() 1139 x, y = self.__rotate_node.to_cartesian() 1140 1141 # draw text 1142 self.context.set_font_size(10) 1143 self.context.move_to(xc - 49, yc - 48) 1144 self.context.show_text(_("Navigation")) 1145 1146 width = self.context.text_extents(str(int(t)))[2] 1147 self.context.move_to(xc + 49 - width - 2, yc - 48) 1148 self.context.show_text(str(round(t, 1))) 1149 self.context.set_line_width(1) 1150 self.context.stroke() 1151 1152 # draw arc 1153 self.context.set_dash([1, 2]) 1154 self.context.arc(xc, yc, 40, 0, 2 * math.pi) 1155 self.context.set_source_rgb(0.0, 0.0, 0.0) 1156 self.context.set_line_width(1) 1157 self.context.stroke() 1158 1159 # draw node 1160 self.context.set_dash([1, 0]) 1161 self.context.arc(xc + x, yc - y, self.__rotate_radius, 0, 2 * math.pi) 1162 1163 if self.__rotating: 1164 self.context.set_source_rgb(0.0, 0.0, 0.0) 1165 1166 else: 1167 self.context.set_source_rgb(1.0, 1.0, 1.0) 1168 1169 self.context.fill_preserve() 1170 self.context.set_source_rgb(0.0, 0.0, 0.0) 1171 self.context.set_line_width(1) 1172 self.context.stroke() 1173 1174 return False 1175 1176 def __draw_move_control(self): 1177 """ 1178 """ 1179 xc, yc = self.__center_of_widget 1180 pc = PolarCoordinate() 1181 1182 self.context.set_dash([1, 1]) 1183 self.context.arc(xc, yc, 23, 0, 2 * math.pi) 1184 self.context.set_source_rgb(0.0, 0.0, 0.0) 1185 self.context.set_line_width(1) 1186 self.context.stroke() 1187 1188 for i in range(8): 1189 1190 pc.set_coordinate(23, 45 * i) 1191 x, y = pc.to_cartesian() 1192 1193 self.context.set_dash([1, 1]) 1194 self.context.move_to(xc, yc) 1195 self.context.line_to(xc + x, yc - y) 1196 self.context.stroke() 1197 1198 self.context.set_dash([1, 0]) 1199 self.context.arc( 1200 xc + x, yc - y, self.__move_radius, 0, 2 * math.pi) 1201 1202 if i == self.__moving: 1203 self.context.set_source_rgb(0.0, 0.0, 0.0) 1204 else: 1205 self.context.set_source_rgb(1.0, 1.0, 1.0) 1206 self.context.fill_preserve() 1207 self.context.set_source_rgb(0.0, 0.0, 0.0) 1208 self.context.set_line_width(1) 1209 self.context.stroke() 1210 1211 self.context.arc(xc, yc, 6, 0, 2 * math.pi) 1212 1213 if self.__centering: 1214 self.context.set_source_rgb(0.0, 0.0, 0.0) 1215 else: 1216 self.context.set_source_rgb(1.0, 1.0, 1.0) 1217 self.context.fill_preserve() 1218 self.context.set_source_rgb(0.0, 0.0, 0.0) 1219 self.context.set_line_width(1) 1220 self.context.stroke() 1221 1222 return False 1223 1224 def __draw(self): 1225 """ 1226 Drawing method 1227 """ 1228 # Getting allocation reference 1229 allocation = self.get_allocation() 1230 1231 self.__center_of_widget = (allocation.width / 2, 1232 allocation.height / 2) 1233 1234 self.__draw_rotate_control() 1235 self.__draw_move_control() 1236 1237 return False 1238 1239 def __move_in_direction(self, direction): 1240 """ 1241 """ 1242 if self.__moving is not None: 1243 1244 bx, by = self.__move_position 1245 ax, ay = self.__move_addition[direction] 1246 1247 self.__move_position = (bx + self.__move_factor * ax, 1248 by + self.__move_factor * ay) 1249 self.radialnet.set_translation(self.__move_position) 1250 1251 if self.__move_factor < self.__move_factor_limit: 1252 self.__move_factor += 1 1253 1254 gobject.timeout_add(self.__move_pass, 1255 self.__move_in_direction, 1256 direction) 1257 1258 return False 1259 1260 def __rotate_is_clicked(self, pointer): 1261 """ 1262 """ 1263 xn, yn = self.__rotate_node.to_cartesian() 1264 xc, yc = self.__center_of_widget 1265 1266 center = (xc + xn, yc - yn) 1267 return geometry.is_in_circle(pointer, self.__rotate_radius, center) 1268 1269 def __center_is_clicked(self, pointer): 1270 """ 1271 """ 1272 return geometry.is_in_circle(pointer, self.__move_radius, 1273 self.__center_of_widget) 1274 1275 def __move_is_clicked(self, pointer): 1276 """ 1277 """ 1278 xc, yc = self.__center_of_widget 1279 pc = PolarCoordinate() 1280 1281 for i in range(8): 1282 1283 pc.set_coordinate(23, 45 * i) 1284 x, y = pc.to_cartesian() 1285 1286 center = (xc + x, yc - y) 1287 if geometry.is_in_circle(pointer, self.__move_radius, center): 1288 return i 1289 1290 return None 1291