diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:42:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:42:04 +0000 |
commit | 0d47952611198ef6b1163f366dc03922d20b1475 (patch) | |
tree | 3d840a3b8c0daef0754707bfb9f5e873b6b1ac13 /zenmap/radialnet/gui/ControlWidget.py | |
parent | Initial commit. (diff) | |
download | nmap-upstream.tar.xz nmap-upstream.zip |
Adding upstream version 7.94+git20230807.3be01efb1+dfsg.upstream/7.94+git20230807.3be01efb1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'zenmap/radialnet/gui/ControlWidget.py')
-rw-r--r-- | zenmap/radialnet/gui/ControlWidget.py | 1270 |
1 files changed, 1270 insertions, 0 deletions
diff --git a/zenmap/radialnet/gui/ControlWidget.py b/zenmap/radialnet/gui/ControlWidget.py new file mode 100644 index 0000000..99be773 --- /dev/null +++ b/zenmap/radialnet/gui/ControlWidget.py @@ -0,0 +1,1270 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GLib, Gdk + +import math + +import radialnet.util.geometry as geometry + +from radialnet.bestwidgets.boxes import * +from radialnet.core.Coordinate import PolarCoordinate +import radialnet.gui.RadialNet as RadialNet +from radialnet.bestwidgets.expanders import BWExpander + + +OPTIONS = ['address', + 'hostname', + 'icon', + 'latency', + 'ring', + 'region', + 'slow in/out'] + +REFRESH_RATE = 500 + + +class ControlWidget(BWVBox): + """ + """ + def __init__(self, radialnet): + """ + """ + BWVBox.__init__(self) + self.set_border_width(6) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__action = ControlAction(self.radialnet) + self.__interpolation = ControlInterpolation(self.radialnet) + self.__layout = ControlLayout(self.radialnet) + self.__view = ControlView(self.radialnet) + + self.bw_pack_start_noexpand_nofill(self.__action) + self.bw_pack_start_noexpand_nofill(self.__interpolation) + self.bw_pack_start_noexpand_nofill(self.__layout) + self.bw_pack_start_noexpand_nofill(self.__view) + + +class ControlAction(BWExpander): + """ + """ + def __init__(self, radialnet): + """ + """ + BWExpander.__init__(self, _('Action')) + self.set_expanded(True) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__tbox = BWTable(1, 4) + self.__tbox.bw_set_spacing(0) + self.__vbox = BWVBox() + + self.__jump_to = Gtk.RadioToolButton(group=None, + stock_id=Gtk.STOCK_JUMP_TO) + self.__jump_to.set_tooltip_text('Change focus') + self.__jump_to.connect('toggled', + self.__change_pointer, + RadialNet.POINTER_JUMP_TO) + + self.__info = Gtk.RadioToolButton(group=self.__jump_to, + stock_id=Gtk.STOCK_INFO) + self.__info.set_tooltip_text('Show information') + self.__info.connect('toggled', + self.__change_pointer, + RadialNet.POINTER_INFO) + + self.__group = Gtk.RadioToolButton(group=self.__jump_to, + stock_id=Gtk.STOCK_ADD) + self.__group.set_tooltip_text('Group children') + self.__group.connect('toggled', + self.__change_pointer, + RadialNet.POINTER_GROUP) + + self.__region = Gtk.RadioToolButton(group=self.__jump_to, + stock_id=Gtk.STOCK_SELECT_COLOR) + self.__region.set_tooltip_text('Fill region') + self.__region.connect('toggled', + self.__change_pointer, + RadialNet.POINTER_FILL) + + self.__region_color = Gtk.ComboBoxText.new() + self.__region_color.append_text(_('Red')) + self.__region_color.append_text(_('Yellow')) + self.__region_color.append_text(_('Green')) + self.__region_color.connect('changed', self.__change_region) + self.__region_color.set_active(self.radialnet.get_region_color()) + + self.__tbox.bw_attach_next(self.__jump_to) + self.__tbox.bw_attach_next(self.__info) + self.__tbox.bw_attach_next(self.__group) + self.__tbox.bw_attach_next(self.__region) + + self.__vbox.bw_pack_start_noexpand_nofill(self.__tbox) + self.__vbox.bw_pack_start_noexpand_nofill(self.__region_color) + + self.bw_add(self.__vbox) + + self.__jump_to.set_active(True) + self.__region_color.set_no_show_all(True) + self.__region_color.hide() + + def __change_pointer(self, widget, pointer): + """ + """ + if pointer != self.radialnet.get_pointer_status(): + self.radialnet.set_pointer_status(pointer) + + if pointer == RadialNet.POINTER_FILL: + self.__region_color.show() + else: + self.__region_color.hide() + + def __change_region(self, widget): + """ + """ + self.radialnet.set_region_color(self.__region_color.get_active()) + + +class ControlVariableWidget(Gtk.DrawingArea): + """ + """ + def __init__(self, name, value, update, increment=1): + """ + """ + Gtk.DrawingArea.__init__(self) + + self.__variable_name = name + self.__value = value + self.__update = update + self.__increment_pass = increment + + self.__radius = 6 + self.__increment_time = 100 + + self.__pointer_position = 0 + self.__active_increment = False + + self.__last_value = self.__value() + + self.connect('draw', self.draw) + self.connect('button_press_event', self.button_press) + self.connect('button_release_event', self.button_release) + self.connect('motion_notify_event', self.motion_notify) + + self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.POINTER_MOTION_HINT_MASK | + Gdk.EventMask.POINTER_MOTION_MASK) + + GLib.timeout_add(REFRESH_RATE, self.verify_value) + + def verify_value(self): + """ + """ + if self.__value() != self.__last_value: + self.__last_value = self.__value() + + self.queue_draw() + + return True + + def button_press(self, widget, event): + """ + """ + self.__active_increment = False + pointer = self.get_pointer() + + if self.__button_is_clicked(pointer) and event.button == 1: + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) + self.__active_increment = True + self.__increment_value() + + def button_release(self, widget, event): + """ + """ + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR)) + + self.__active_increment = False + self.__pointer_position = 0 + + self.queue_draw() + + def motion_notify(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + if self.__active_increment: + + xc, yc = self.__center_of_widget + x, _ = self.get_pointer() + + if x - self.__radius > 0 and x + self.__radius < 2 * xc: + self.__pointer_position = x - xc + + self.queue_draw() + + def draw(self, widget, context): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type context: cairo.Context + @param context: cairo context class + @rtype: boolean + @return: Indicator of the event propagation + """ + self.set_size_request(100, 30) + + self.__draw(context) + + return True + + def __draw(self, context): + """ + """ + allocation = self.get_allocation() + + self.__center_of_widget = (allocation.width // 2, + allocation.height // 2) + + xc, yc = self.__center_of_widget + + # draw line + context.set_line_width(1) + context.set_dash([1, 2]) + context.move_to(self.__radius, + yc + self.__radius) + context.line_to(2 * xc - 5, + yc + self.__radius) + context.stroke() + + # draw text + context.set_dash([1, 0]) + context.set_font_size(10) + + context.move_to(5, yc - self.__radius) + context.show_text(self.__variable_name) + + width = context.text_extents(str(self.__value()))[2] + context.move_to(2 * xc - width - 5, yc - self.__radius) + context.show_text(str(self.__value())) + + context.set_line_width(1) + context.stroke() + + # draw node + context.arc(xc + self.__pointer_position, + yc + self.__radius, + self.__radius, 0, 2 * math.pi) + if self.__active_increment: + context.set_source_rgb(0.0, 0.0, 0.0) + else: + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill_preserve() + context.set_source_rgb(0.0, 0.0, 0.0) + context.stroke() + + def __button_is_clicked(self, pointer): + """ + """ + xc, yc = self.__center_of_widget + center = (xc, yc + self.__radius) + + return geometry.is_in_circle(pointer, 6, center) + + def __increment_value(self): + """ + """ + self.__update(self.__value() + self.__pointer_position / 4) + + self.queue_draw() + + if self.__active_increment: + + GLib.timeout_add(self.__increment_time, + self.__increment_value) + + def set_value_function(self, value): + """ + """ + self.__value = value + + def set_update_function(self, update): + """ + """ + self.__update = update + + +class ControlVariable(BWHBox): + """ + """ + def __init__(self, name, get_function, set_function, increment=1): + """ + """ + BWHBox.__init__(self, spacing=0) + + self.__increment_pass = increment + self.__increment_time = 200 + self.__increment = False + + self.__name = name + self.__get_function = get_function + self.__set_function = set_function + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__control = ControlVariableWidget(self.__name, + self.__get_function, + self.__set_function, + self.__increment_pass) + + self.__left_button = Gtk.Button() + self.__left_button.set_size_request(20, 20) + self.__left_arrow = Gtk.Image.new_from_icon_name("pan-start-symbolic", + Gtk.IconSize.BUTTON); + self.__left_button.add(self.__left_arrow) + self.__left_button.connect('pressed', + self.__pressed, + -self.__increment_pass) + self.__left_button.connect('released', self.__released) + + self.__right_button = Gtk.Button() + self.__right_button.set_size_request(20, 20) + self.__right_arrow = Gtk.Image.new_from_icon_name("pan-end-symbolic", + Gtk.IconSize.BUTTON); + self.__right_button.add(self.__right_arrow) + self.__right_button.connect('pressed', + self.__pressed, + self.__increment_pass) + self.__right_button.connect('released', self.__released) + + self.bw_pack_start_noexpand_nofill(self.__left_button) + self.bw_pack_start_expand_fill(self.__control) + self.bw_pack_start_noexpand_nofill(self.__right_button) + + def __pressed(self, widget, increment): + """ + """ + self.__increment = True + self.__increment_function(increment) + + def __increment_function(self, increment): + """ + """ + if self.__increment: + + self.__set_function(self.__get_function() + increment) + self.__control.verify_value() + + GLib.timeout_add(self.__increment_time, + self.__increment_function, + increment) + + def __released(self, widget): + """ + """ + self.__increment = False + + +class ControlFisheye(BWVBox): + """ + """ + def __init__(self, radialnet): + """ + """ + BWVBox.__init__(self) + self.set_border_width(6) + + self.radialnet = radialnet + self.__ring_max_value = self.radialnet.get_number_of_rings() + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__params = BWHBox() + + self.__fisheye_label = Gtk.Label.new(_('<b>Fisheye</b> on ring')) + self.__fisheye_label.set_use_markup(True) + + self.__ring = Gtk.Adjustment.new(0, 0, self.__ring_max_value, 0.01, 0.01, 0) + + self.__ring_spin = Gtk.SpinButton(adjustment=self.__ring) + self.__ring_spin.set_digits(2) + + self.__ring_scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=self.__ring) + self.__ring_scale.set_size_request(100, -1) + self.__ring_scale.set_digits(2) + self.__ring_scale.set_value_pos(Gtk.PositionType.LEFT) + self.__ring_scale.set_draw_value(False) + + self.__interest_label = Gtk.Label.new(_('with interest factor')) + self.__interest = Gtk.Adjustment.new(0, 0, 10, 0.01, 0, 0) + self.__interest_spin = Gtk.SpinButton(adjustment=self.__interest) + self.__interest_spin.set_digits(2) + + self.__spread_label = Gtk.Label.new(_('and spread factor')) + self.__spread = Gtk.Adjustment.new(0, -1.0, 1.0, 0.01, 0.01, 0) + self.__spread_spin = Gtk.SpinButton(adjustment=self.__spread) + self.__spread_spin.set_digits(2) + + self.__params.bw_pack_start_noexpand_nofill(self.__fisheye_label) + self.__params.bw_pack_start_noexpand_nofill(self.__ring_spin) + self.__params.bw_pack_start_expand_fill(self.__ring_scale) + self.__params.bw_pack_start_noexpand_nofill(self.__interest_label) + self.__params.bw_pack_start_noexpand_nofill(self.__interest_spin) + self.__params.bw_pack_start_noexpand_nofill(self.__spread_label) + self.__params.bw_pack_start_noexpand_nofill(self.__spread_spin) + + self.bw_pack_start_noexpand_nofill(self.__params) + + self.__ring.connect('value_changed', self.__change_ring) + self.__interest.connect('value_changed', self.__change_interest) + self.__spread.connect('value_changed', self.__change_spread) + + GLib.timeout_add(REFRESH_RATE, self.__update_fisheye) + + def __update_fisheye(self): + """ + """ + # adjust ring scale to radialnet number of nodes + ring_max_value = self.radialnet.get_number_of_rings() - 1 + + if ring_max_value != self.__ring_max_value: + + value = self.__ring.get_value() + + if value == 0 and ring_max_value != 0: + value = 1 + + elif value > ring_max_value: + value = ring_max_value + + self.__ring.configure(value, 1, ring_max_value, 0.01, 0.01, 0) + self.__ring_max_value = ring_max_value + + self.__ring_scale.queue_draw() + + # check ring value + ring_value = self.radialnet.get_fisheye_ring() + + if self.__ring.get_value() != ring_value: + self.__ring.set_value(ring_value) + + # check interest value + interest_value = self.radialnet.get_fisheye_interest() + + if self.__interest.get_value() != interest_value: + self.__interest.set_value(interest_value) + + # check spread value + spread_value = self.radialnet.get_fisheye_spread() + + if self.__spread.get_value() != spread_value: + self.__spread.set_value(spread_value) + + return True + + def active_fisheye(self): + """ + """ + self.radialnet.set_fisheye(True) + self.__change_ring() + self.__change_interest() + + def deactive_fisheye(self): + """ + """ + self.radialnet.set_fisheye(False) + + def __change_ring(self, widget=None): + """ + """ + if not self.radialnet.is_in_animation(): + self.radialnet.set_fisheye_ring(self.__ring.get_value()) + else: + self.__ring.set_value(self.radialnet.get_fisheye_ring()) + + def __change_interest(self, widget=None): + """ + """ + if not self.radialnet.is_in_animation(): + self.radialnet.set_fisheye_interest(self.__interest.get_value()) + else: + self.__interest.set_value(self.radialnet.get_fisheye_interest()) + + def __change_spread(self, widget=None): + """ + """ + if not self.radialnet.is_in_animation(): + self.radialnet.set_fisheye_spread(self.__spread.get_value()) + else: + self.__spread.set_value(self.radialnet.get_fisheye_spread()) + + +class ControlInterpolation(BWExpander): + """ + """ + def __init__(self, radialnet): + """ + """ + BWExpander.__init__(self, _('Interpolation')) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__vbox = BWVBox() + + self.__cartesian_radio = Gtk.RadioButton(group=None, + label=_('Cartesian')) + self.__polar_radio = Gtk.RadioButton(group=self.__cartesian_radio, + label=_('Polar')) + self.__cartesian_radio.connect('toggled', + self.__change_system, + RadialNet.INTERPOLATION_CARTESIAN) + self.__polar_radio.connect('toggled', + self.__change_system, + RadialNet.INTERPOLATION_POLAR) + + self.__system_box = BWHBox() + self.__system_box.bw_pack_start_noexpand_nofill(self.__polar_radio) + self.__system_box.bw_pack_start_noexpand_nofill(self.__cartesian_radio) + + self.__frames_box = BWHBox() + self.__frames_label = Gtk.Label.new(_('Frames')) + self.__frames_label.set_alignment(0.0, 0.5) + self.__frames = Gtk.Adjustment.new( + self.radialnet.get_number_of_frames(), 1, 1000, 1, 0, 0) + self.__frames.connect('value_changed', self.__change_frames) + self.__frames_spin = Gtk.SpinButton(adjustment=self.__frames) + self.__frames_box.bw_pack_start_expand_fill(self.__frames_label) + self.__frames_box.bw_pack_start_noexpand_nofill(self.__frames_spin) + + self.__vbox.bw_pack_start_noexpand_nofill(self.__frames_box) + self.__vbox.bw_pack_start_noexpand_nofill(self.__system_box) + + self.bw_add(self.__vbox) + + GLib.timeout_add(REFRESH_RATE, self.__update_animation) + + def __update_animation(self): + """ + """ + active = self.radialnet.get_interpolation() + + if active == RadialNet.INTERPOLATION_CARTESIAN: + self.__cartesian_radio.set_active(True) + + else: + self.__polar_radio.set_active(True) + + return True + + def __change_system(self, widget, value): + """ + """ + if not self.radialnet.set_interpolation(value): + + active = self.radialnet.get_interpolation() + + if active == RadialNet.INTERPOLATION_CARTESIAN: + self.__cartesian_radio.set_active(True) + + else: + self.__polar_radio.set_active(True) + + def __change_frames(self, widget): + """ + """ + if not self.radialnet.set_number_of_frames(self.__frames.get_value()): + self.__frames.set_value(self.radialnet.get_number_of_frames()) + + +class ControlLayout(BWExpander): + """ + """ + def __init__(self, radialnet): + """ + """ + BWExpander.__init__(self, _('Layout')) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__hbox = BWHBox() + + self.__layout = Gtk.ComboBoxText() + self.__layout.append_text(_('Symmetric')) + self.__layout.append_text(_('Weighted')) + self.__layout.set_active(self.radialnet.get_layout()) + self.__layout.connect('changed', self.__change_layout) + self.__force = Gtk.ToolButton(stock_id=Gtk.STOCK_REFRESH) + self.__force.connect('clicked', self.__force_update) + + self.__hbox.bw_pack_start_expand_fill(self.__layout) + self.__hbox.bw_pack_start_noexpand_nofill(self.__force) + + self.bw_add(self.__hbox) + + self.__check_layout() + + def __check_layout(self): + """ + """ + if self.__layout.get_active() == RadialNet.LAYOUT_WEIGHTED: + self.__force.set_sensitive(True) + + else: + self.__force.set_sensitive(False) + + return True + + def __force_update(self, widget): + """ + """ + self.__fisheye_ring = self.radialnet.get_fisheye_ring() + self.radialnet.update_layout() + + def __change_layout(self, widget): + """ + """ + if not self.radialnet.set_layout(self.__layout.get_active()): + self.__layout.set_active(self.radialnet.get_layout()) + + else: + self.__check_layout() + + +class ControlRingGap(BWVBox): + """ + """ + def __init__(self, radialnet): + """ + """ + BWVBox.__init__(self) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__radius = ControlVariable(_('Ring gap'), + self.radialnet.get_ring_gap, + self.radialnet.set_ring_gap) + + self.__label = Gtk.Label.new(_('Lower ring gap')) + self.__label.set_alignment(0.0, 0.5) + self.__adjustment = Gtk.Adjustment.new( + self.radialnet.get_min_ring_gap(), 0, 50, 1, 0, 0) + self.__spin = Gtk.SpinButton(adjustment=self.__adjustment) + self.__spin.connect('value_changed', self.__change_lower) + + self.__lower_hbox = BWHBox() + self.__lower_hbox.bw_pack_start_expand_fill(self.__label) + self.__lower_hbox.bw_pack_start_noexpand_nofill(self.__spin) + + self.bw_pack_start_noexpand_nofill(self.__radius) + self.bw_pack_start_noexpand_nofill(self.__lower_hbox) + + def __change_lower(self, widget): + """ + """ + if not self.radialnet.set_min_ring_gap(self.__adjustment.get_value()): + self.__adjustment.set_value(self.radialnet.get_min_ring_gap()) + + +class ControlOptions(BWScrolledWindow): + """ + """ + def __init__(self, radialnet): + """ + """ + BWScrolledWindow.__init__(self) + + self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS) + self.set_shadow_type(Gtk.ShadowType.NONE) + + self.radialnet = radialnet + + self.enable_labels = True + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__liststore = Gtk.ListStore.new([bool, str]) + + self.__liststore.append([None, OPTIONS[0]]) + self.__liststore.append([None, OPTIONS[1]]) + self.__liststore.append([None, OPTIONS[2]]) + self.__liststore.append([None, OPTIONS[3]]) + self.__liststore.append([None, OPTIONS[4]]) + self.__liststore.append([None, OPTIONS[5]]) + self.__liststore.append([None, OPTIONS[6]]) + + self.__cell_toggle = Gtk.CellRendererToggle() + self.__cell_toggle.set_property('activatable', True) + self.__cell_toggle.connect('toggled', + self.__change_option, + self.__liststore) + + self.__column_toggle = Gtk.TreeViewColumn(cell_renderer=self.__cell_toggle) + self.__column_toggle.add_attribute(self.__cell_toggle, 'active', 0) + self.__column_toggle.set_cell_data_func(self.__cell_toggle, self.__cell_toggle_data_method) + + self.__cell_text = Gtk.CellRendererText() + + self.__column_text = Gtk.TreeViewColumn(title=None, + cell_renderer=self.__cell_text, + text=1) + self.__column_text.set_cell_data_func(self.__cell_text, self.__cell_text_data_method) + + self.__treeview = Gtk.TreeView.new_with_model(self.__liststore) + self.__treeview.set_enable_search(True) + self.__treeview.set_search_column(1) + self.__treeview.set_headers_visible(False) + self.__treeview.append_column(self.__column_toggle) + self.__treeview.append_column(self.__column_text) + + self.add_with_viewport(self.__treeview) + + GLib.timeout_add(REFRESH_RATE, self.__update_options) + + def __cell_toggle_data_method(self, column, cell, model, it, data): + if not self.enable_labels and model.get_value(it, 1) == 'hostname': + cell.set_property('activatable', False) + else: + cell.set_property('activatable', True) + + def __cell_text_data_method(self, column, cell, model, it, data): + if not self.enable_labels and model.get_value(it, 1) == 'hostname': + cell.set_property('strikethrough', True) + else: + cell.set_property('strikethrough', False) + + def __update_options(self): + """ + """ + model = self.__liststore + + model[OPTIONS.index('address')][0] = self.radialnet.get_show_address() + model[OPTIONS.index('hostname')][0] = self.enable_labels and \ + self.radialnet.get_show_hostname() + model[OPTIONS.index('icon')][0] = self.radialnet.get_show_icon() + model[OPTIONS.index('latency')][0] = self.radialnet.get_show_latency() + model[OPTIONS.index('ring')][0] = self.radialnet.get_show_ring() + model[OPTIONS.index('region')][0] = self.radialnet.get_show_region() + model[OPTIONS.index('slow in/out')][0] = \ + self.radialnet.get_slow_inout() + + return True + + def __change_option(self, cell, option, model): + """ + """ + option = int(option) + model[option][0] = not model[option][0] + + if OPTIONS[option] == 'address': + self.radialnet.set_show_address(model[option][0]) + if model[option][0]: + model[OPTIONS.index('hostname')][0] = self.radialnet.get_show_hostname() + else: + model[OPTIONS.index('hostname')][0] = False + self.enable_labels = model[option][0] + + elif OPTIONS[option] == 'hostname': + self.radialnet.set_show_hostname(model[option][0]) + + elif OPTIONS[option] == 'icon': + self.radialnet.set_show_icon(model[option][0]) + + elif OPTIONS[option] == 'latency': + self.radialnet.set_show_latency(model[option][0]) + + elif OPTIONS[option] == 'ring': + self.radialnet.set_show_ring(model[option][0]) + + elif OPTIONS[option] == 'region': + self.radialnet.set_show_region(model[option][0]) + + elif OPTIONS[option] == 'slow in/out': + self.radialnet.set_slow_inout(model[option][0]) + + +class ControlView(BWExpander): + """ + """ + def __init__(self, radialnet): + """ + """ + BWExpander.__init__(self, _('View')) + self.set_expanded(True) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__vbox = BWVBox(spacing=0) + + self.__zoom = ControlVariable(_('Zoom'), + self.radialnet.get_zoom, + self.radialnet.set_zoom) + + self.__ring_gap = ControlRingGap(self.radialnet) + self.__navigation = ControlNavigation(self.radialnet) + + self.__options = ControlOptions(self.radialnet) + self.__options.set_border_width(0) + + self.__vbox.bw_pack_start_expand_nofill(self.__options) + self.__vbox.bw_pack_start_noexpand_nofill(self.__navigation) + self.__vbox.bw_pack_start_noexpand_nofill(self.__zoom) + self.__vbox.bw_pack_start_noexpand_nofill(self.__ring_gap) + + self.bw_add(self.__vbox) + + +class ControlNavigation(Gtk.DrawingArea): + """ + """ + def __init__(self, radialnet): + """ + """ + Gtk.DrawingArea.__init__(self) + + self.radialnet = radialnet + + self.__rotate_node = PolarCoordinate() + self.__rotate_node.set_coordinate(40, 90) + self.__center_of_widget = (50, 50) + self.__moving = None + self.__centering = False + self.__rotating = False + self.__move_pass = 100 + + self.__move_position = (0, 0) + self.__move_addition = [(-1, 0), + (-1, -1), + (0, -1), + (1, -1), + (1, 0), + (1, 1), + (0, 1), + (-1, 1)] + + self.__move_factor = 1 + self.__move_factor_limit = 20 + + self.__rotate_radius = 6 + self.__move_radius = 6 + + self.__rotate_clicked = False + self.__move_clicked = None + + self.connect('draw', self.draw) + self.connect('button_press_event', self.button_press) + self.connect('button_release_event', self.button_release) + self.connect('motion_notify_event', self.motion_notify) + self.connect('enter_notify_event', self.enter_notify) + self.connect('leave_notify_event', self.leave_notify) + self.connect('key_press_event', self.key_press) + self.connect('key_release_event', self.key_release) + + self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.ENTER_NOTIFY_MASK | + Gdk.EventMask.LEAVE_NOTIFY_MASK | + Gdk.EventMask.KEY_PRESS_MASK | + Gdk.EventMask.KEY_RELEASE_MASK | + Gdk.EventMask.POINTER_MOTION_HINT_MASK | + Gdk.EventMask.POINTER_MOTION_MASK) + + self.__rotate_node.set_coordinate(40, self.radialnet.get_rotation()) + + def key_press(self, widget, event): + """ + """ + # key = Gdk.keyval_name(event.keyval) + + self.queue_draw() + + return True + + def key_release(self, widget, event): + """ + """ + # key = Gdk.keyval_name(event.keyval) + + self.queue_draw() + + return True + + def enter_notify(self, widget, event): + """ + """ + return False + + def leave_notify(self, widget, event): + """ + """ + self.queue_draw() + + return False + + def button_press(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + pointer = self.get_pointer() + + direction = False + + if self.__rotate_is_clicked(pointer): + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) + self.__rotating = True + + direction = self.__move_is_clicked(pointer) + + if direction is not None and self.__moving is None: + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) + self.__moving = direction + self.__move_in_direction(direction) + + if self.__center_is_clicked(pointer): + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) + self.__centering = True + self.__move_position = (0, 0) + self.radialnet.set_translation(self.__move_position) + + self.queue_draw() + + return False + + def button_release(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + self.__moving = None # stop moving + self.__centering = False + self.__rotating = False # stop rotate + self.__move_factor = 1 + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR)) + + self.queue_draw() + + return False + + def motion_notify(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + xc, yc = self.__center_of_widget + x, y = self.get_pointer() + + status = not self.radialnet.is_in_animation() + status = status and not self.radialnet.is_empty() + + if self.__rotating and status: + + r, t = self.__rotate_node.get_coordinate() + t = math.degrees(math.atan2(yc - y, x - xc)) + + if t < 0: + t = 360 + t + + self.radialnet.set_rotation(t) + self.__rotate_node.set_coordinate(r, t) + + self.queue_draw() + + return False + + def draw(self, widget, context): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type context: cairo.Context + @param context: cairo context class + @rtype: boolean + @return: Indicator of the event propagation + """ + self.set_size_request(120, 130) + + self.__draw(context) + + return False + + def __draw_rotate_control(self, context): + """ + """ + xc, yc = self.__center_of_widget + r, t = self.__rotate_node.get_coordinate() + x, y = self.__rotate_node.to_cartesian() + + # draw text + context.set_font_size(10) + context.move_to(xc - 49, yc - 48) + context.show_text(_("Navigation")) + + width = context.text_extents(str(int(t)))[2] + context.move_to(xc + 49 - width - 2, yc - 48) + context.show_text(str(round(t, 1))) + context.set_line_width(1) + context.stroke() + + # draw arc + context.set_dash([1, 2]) + context.arc(xc, yc, 40, 0, 2 * math.pi) + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + # draw node + context.set_dash([1, 0]) + context.arc(xc + x, yc - y, self.__rotate_radius, 0, 2 * math.pi) + + if self.__rotating: + context.set_source_rgb(0.0, 0.0, 0.0) + else: + context.set_source_rgb(1.0, 1.0, 1.0) + + context.fill_preserve() + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + return False + + def __draw_move_control(self, context): + """ + """ + xc, yc = self.__center_of_widget + pc = PolarCoordinate() + + context.set_dash([1, 1]) + context.arc(xc, yc, 23, 0, 2 * math.pi) + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + for i in range(8): + + pc.set_coordinate(23, 45 * i) + x, y = pc.to_cartesian() + + context.set_dash([1, 1]) + context.move_to(xc, yc) + context.line_to(xc + x, yc - y) + context.stroke() + + context.set_dash([1, 0]) + context.arc( + xc + x, yc - y, self.__move_radius, 0, 2 * math.pi) + + if i == self.__moving: + context.set_source_rgb(0.0, 0.0, 0.0) + else: + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill_preserve() + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + context.arc(xc, yc, 6, 0, 2 * math.pi) + + if self.__centering: + context.set_source_rgb(0.0, 0.0, 0.0) + else: + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill_preserve() + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + return False + + def __draw(self, context): + """ + Drawing method + """ + # Getting allocation reference + allocation = self.get_allocation() + + self.__center_of_widget = (allocation.width // 2, + allocation.height // 2) + + self.__draw_rotate_control(context) + self.__draw_move_control(context) + + return False + + def __move_in_direction(self, direction): + """ + """ + if self.__moving is not None: + + bx, by = self.__move_position + ax, ay = self.__move_addition[direction] + + self.__move_position = (bx + self.__move_factor * ax, + by + self.__move_factor * ay) + self.radialnet.set_translation(self.__move_position) + + if self.__move_factor < self.__move_factor_limit: + self.__move_factor += 1 + + GLib.timeout_add(self.__move_pass, + self.__move_in_direction, + direction) + + return False + + def __rotate_is_clicked(self, pointer): + """ + """ + xn, yn = self.__rotate_node.to_cartesian() + xc, yc = self.__center_of_widget + + center = (xc + xn, yc - yn) + return geometry.is_in_circle(pointer, self.__rotate_radius, center) + + def __center_is_clicked(self, pointer): + """ + """ + return geometry.is_in_circle(pointer, self.__move_radius, + self.__center_of_widget) + + def __move_is_clicked(self, pointer): + """ + """ + xc, yc = self.__center_of_widget + pc = PolarCoordinate() + + for i in range(8): + + pc.set_coordinate(23, 45 * i) + x, y = pc.to_cartesian() + + center = (xc + x, yc - y) + if geometry.is_in_circle(pointer, self.__move_radius, center): + return i + + return None |