summaryrefslogtreecommitdiff
path: root/grc/gui/Notebook.py
diff options
context:
space:
mode:
authorMarcus Müller <marcus@hostalia.de>2018-09-21 00:00:57 +0200
committerMarcus Müller <marcus@hostalia.de>2018-09-21 00:00:57 +0200
commit267d669eb21c514c18a6ee979f5cf247d251f1ad (patch)
treec6120f5993f82daf13894e8bf905c4169493152e /grc/gui/Notebook.py
parent896d1c9da31963ecf5b0d90942c2af51ca998a69 (diff)
parent7b20b28a9e5aa4e32ee37e89e7f80d74485344e8 (diff)
Merge branch 'merge_next' (which merges next)
This has been in the making far too long. We finally merge the next branch into master, in preparation of releasing GNU Radio 3.8. There will be breakage. There will be awesomeness. There will be progress in the greatest SDR framework to ever grace the surface of the earth. Hold tight for now.
Diffstat (limited to 'grc/gui/Notebook.py')
-rw-r--r--grc/gui/Notebook.py187
1 files changed, 187 insertions, 0 deletions
diff --git a/grc/gui/Notebook.py b/grc/gui/Notebook.py
new file mode 100644
index 0000000000..9f63190b31
--- /dev/null
+++ b/grc/gui/Notebook.py
@@ -0,0 +1,187 @@
+"""
+Copyright 2008, 2009, 2011 Free Software Foundation, Inc.
+This file is part of GNU Radio
+
+GNU Radio Companion is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+GNU Radio Companion 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. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+"""
+
+from __future__ import absolute_import
+import os
+import logging
+
+from gi.repository import Gtk, Gdk, GObject
+
+from . import Actions
+from .StateCache import StateCache
+from .Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT
+from .DrawingArea import DrawingArea
+
+
+log = logging.getLogger(__name__)
+
+
+class Notebook(Gtk.Notebook):
+ def __init__(self):
+ Gtk.Notebook.__init__(self)
+ log.debug("notebook()")
+ self.app = Gtk.Application.get_default()
+ self.current_page = None
+
+ self.set_show_border(False)
+ self.set_scrollable(True)
+ self.connect('switch-page', self._handle_page_change)
+
+ self.add_events(Gdk.EventMask.SCROLL_MASK)
+ self.connect('scroll-event', self._handle_scroll)
+ self._ignore_consecutive_scrolls = 0
+
+ def _handle_page_change(self, notebook, page, page_num):
+ """
+ Handle a page change. When the user clicks on a new tab,
+ reload the flow graph to update the vars window and
+ call handle states (select nothing) to update the buttons.
+
+ Args:
+ notebook: the notebook
+ page: new page
+ page_num: new page number
+ """
+ self.current_page = self.get_nth_page(page_num)
+ Actions.PAGE_CHANGE()
+
+ def _handle_scroll(self, widget, event):
+ # Not sure how to handle this at the moment.
+ natural = True
+ # Slow it down
+ if self._ignore_consecutive_scrolls == 0:
+ if event.direction in (Gdk.ScrollDirection.UP, Gdk.ScrollDirection.LEFT):
+ if natural:
+ self.prev_page()
+ else:
+ self.next_page()
+ elif event.direction in (Gdk.ScrollDirection.DOWN, Gdk.ScrollDirection.RIGHT):
+ if natural:
+ self.next_page()
+ else:
+ self.prev_page()
+ self._ignore_consecutive_scrolls = 3
+ else:
+ self._ignore_consecutive_scrolls -= 1
+ return False
+
+
+class Page(Gtk.HBox):
+ """A page in the notebook."""
+
+ def __init__(self, main_window, flow_graph, file_path=''):
+ """
+ Page constructor.
+
+ Args:
+ main_window: main window
+ file_path: path to a flow graph file
+ """
+ Gtk.HBox.__init__(self)
+
+ self.main_window = main_window
+ self.flow_graph = flow_graph
+ self.file_path = file_path
+
+ self.process = None
+ self.saved = True
+
+ # import the file
+ initial_state = flow_graph.parent_platform.parse_flow_graph(file_path)
+ flow_graph.import_data(initial_state)
+ self.state_cache = StateCache(initial_state)
+
+ # tab box to hold label and close button
+ self.label = Gtk.Label()
+ image = Gtk.Image.new_from_icon_name('window-close', Gtk.IconSize.MENU)
+ image_box = Gtk.HBox(homogeneous=False, spacing=0)
+ image_box.pack_start(image, True, False, 0)
+ button = Gtk.Button()
+ button.connect("clicked", self._handle_button)
+ button.set_relief(Gtk.ReliefStyle.NONE)
+ button.add(image_box)
+
+ tab = self.tab = Gtk.HBox(homogeneous=False, spacing=0)
+ tab.pack_start(self.label, False, False, 0)
+ tab.pack_start(button, False, False, 0)
+ tab.show_all()
+
+ # setup scroll window and drawing area
+ self.drawing_area = DrawingArea(flow_graph)
+ flow_graph.drawing_area = self.drawing_area
+
+ self.scrolled_window = Gtk.ScrolledWindow()
+ self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT)
+ self.scrolled_window.set_policy(Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS)
+ self.scrolled_window.connect('key-press-event', self._handle_scroll_window_key_press)
+
+ self.scrolled_window.add(self.drawing_area)
+ self.pack_start(self.scrolled_window, True, True, 0)
+ self.show_all()
+
+ def _handle_scroll_window_key_press(self, widget, event):
+ is_ctrl_pg = (
+ event.state & Gdk.ModifierType.CONTROL_MASK and
+ event.keyval in (Gdk.KEY_Page_Up, Gdk.KEY_Page_Down)
+ )
+ if is_ctrl_pg:
+ return self.get_parent().event(event)
+
+ def get_generator(self):
+ """
+ Get the generator object for this flow graph.
+
+ Returns:
+ generator
+ """
+ platform = self.flow_graph.parent_platform
+ return platform.Generator(self.flow_graph, os.path.dirname(self.file_path))
+
+ def _handle_button(self, button):
+ """
+ The button was clicked.
+ Make the current page selected, then close.
+
+ Args:
+ the: button
+ """
+ self.main_window.page_to_be_closed = self
+ Actions.FLOW_GRAPH_CLOSE()
+
+ def set_markup(self, markup):
+ """
+ Set the markup in this label.
+
+ Args:
+ markup: the new markup text
+ """
+ self.label.set_markup(markup)
+
+ def get_read_only(self):
+ """
+ Get the read-only state of the file.
+ Always false for empty path.
+
+ Returns:
+ true for read-only
+ """
+ if not self.file_path:
+ return False
+ return (os.path.exists(self.file_path) and
+ not os.access(self.file_path, os.W_OK))