Exemplo n.º 1
0
	def _on_symbol_viewer_clicked(self, btn):
		"""
		Show a window with an information viewer in it.
		"""
		if self.symbol_viewer is not None:
			# Raise the window if it is already open
			self.symbol_viewer.get_parent().present()
		else:
			self.symbol_viewer = SymbolViewer(self.system)
			
			window = gtk.Window()
			# XXX: Getting a refrence to the top-level main window I can't see any
			# other way of doing it than knowing how many levels of container we're
			# in...
			window.set_transient_for(self.get_parent().get_parent())
			window.add(self.symbol_viewer)
			window.set_title("Symbols")
			window.set_default_size(450, 550)
			
			def on_dismiss(widget, self, window):
				self.symbol_viewer = None
			window.connect("destroy", on_dismiss, self, window)
			
			window.show_all()
			window.present()
Exemplo n.º 2
0
class ControlBar(gtk.VBox):
	
	__gsignals__ = {
		# Emitted when something which might change the global state occurs
		# indicating a global refresh would be a good idea.
		"device-state-changed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()),
		
		# Actions which are to be carried out by the MainWindow
		"auto-refresh-toggled": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()),
		"select-target-clicked": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()),
		"refresh-clicked": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()),
		"quit-clicked": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()),
		"new-memory-viewer-clicked": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()),
		"new-register-viewer-clicked": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, tuple()),
	}
	
	def __init__(self, system):
		"""
		A control bar for a system.
		"""
		gtk.VBox.__init__(self)
		
		self.system = system
		
		# A widget displaying device information
		self.device_info_viewer = None
		
		# A widget showing memory symbols
		self.symbol_viewer = None
		
		# A list of widgets which are disabled when the processor is busy
		self.disabled_on_busy = []
		
		# A list of widgets which are disabled when no assemblers are available
		self.disabled_on_no_assembler = []
		
		# A list of widgets which are disabled when no loaders are available
		self.disabled_on_no_loader = []
		
		# Maps a peripheral widget to a tuple (tool_item, menu_item)
		self.periphs = {}
		
		# The keyboard shortcuts defined by this widget
		self.accelerators = gtk.AccelGroup()
		
		# Add the menubar
		self.menubar = gtk.MenuBar()
		self._init_menubar()
		self.pack_start(self.menubar, fill = True, expand = False)
		self.menubar.show()
		
		# Add the toolbar
		self.toolbar = gtk.Toolbar()
		self._init_toolbar()
		self.pack_start(self.toolbar, fill = True, expand = False)
		self.toolbar.show()
		
		# Initialise enabled values
		self.architecture_changed()
	
	
	def _init_menubar(self):
		"""
		Add the menu buttons to the bar
		"""
		self.auto_refresh_button = self._make_menu_item("_Auto Refresh",
		                                                check = True,
		                                                accelerator = "<Shift>F1")
		self.auto_refresh_button.connect("toggled", self._on_auto_refresh_toggled)
		device_submenu = self._make_menu((
			("Select _New Target", gtk.STOCK_DISCONNECT, self._on_select_target_clicked, "<Control>n"),
			None,
			("Device _Info", gtk.STOCK_INFO, self._on_info_clicked, "<Control>i"),
			None,
			("_Refresh Now", gtk.STOCK_REFRESH, self._on_refresh_clicked, "F1"),
			self.auto_refresh_button,
			None,
			("_Quit", gtk.STOCK_QUIT, self._on_quit_clicked, "<Control>w"),
		))
		
		device_menu = self._make_menu_item("_Device")
		device_menu.set_submenu(device_submenu)
		self.menubar.append(device_menu)
		
		self.assemble_menu_btn   = self._make_menu_item("Assemble",  gtk.STOCK_CONVERT)
		self.reassemble_menu_btn = self._make_menu_item("Re_assemble", gtk.STOCK_REFRESH, accelerator = "F3")
		self.load_menu_btn   = self._make_menu_item("Load Image",    gtk.STOCK_GO_DOWN)
		self.reload_menu_btn = self._make_menu_item("Re_load Image", gtk.STOCK_REFRESH, accelerator = "F4")
		self.assemble_menu_btn.connect("activate",   self._on_select_source_file_clicked)
		self.reassemble_menu_btn.connect("activate", self._on_assemble_clicked)
		self.load_menu_btn.connect("activate",       self._on_select_image_file_clicked)
		self.reload_menu_btn.connect("activate",     self._on_load_clicked)
		
		self.symbol_viewer_btn = self._make_menu_item("_Symbol Viewer", gtk.STOCK_GO_DOWN, accelerator = "<Control>s")
		self.symbol_viewer_btn.connect("activate", self._on_symbol_viewer_clicked)
		program_submenu = self._make_menu((
			self.assemble_menu_btn,
			self.reassemble_menu_btn,
			None,
			self.load_menu_btn,
			self.reload_menu_btn,
			None,
			self.symbol_viewer_btn
		))
		program_menu = self._make_menu_item("P_rogram")
		program_menu.set_submenu(program_submenu)
		self.menubar.append(program_menu)
		
		self.disabled_on_no_assembler.append(self.assemble_menu_btn)
		self.disabled_on_no_assembler.append(self.reassemble_menu_btn)
		
		self.disabled_on_no_loader.append(self.load_menu_btn)
		self.disabled_on_no_loader.append(self.reload_menu_btn)
		
		self.reset_menu_btn = self._make_menu_item("R_eset",
			gtk.STOCK_REFRESH, accelerator = "F2")
		self.run_menu_btn = self._make_menu_item("_Run",
			gtk.STOCK_MEDIA_PLAY, accelerator = "F5")
		self.stop_menu_btn = self._make_menu_item("_Stop",
			gtk.STOCK_MEDIA_STOP, accelerator =  "F6")
		self.step_menu_btn = self._make_menu_item("S_tep",
			gtk.STOCK_MEDIA_NEXT, accelerator =  "F7")
		self.multi_step_menu_btn = self._make_menu_item("_Multi-Step",
			gtk.STOCK_MEDIA_FORWARD, accelerator =  "F8")
		self.pause_menu_btn = self._make_menu_item("_Pause",
			check = True, accelerator="F9")
		
		self.reset_menu_btn.connect("activate", self._on_reset_clicked)
		self.run_menu_btn.connect("activate", self._on_run_clicked)
		self.stop_menu_btn.connect("activate", self._on_stop_clicked)
		self.step_menu_btn.connect("activate", self._on_step_clicked)
		self.multi_step_menu_btn.connect("activate", self._on_multi_step_clicked)
		self.pause_menu_btn.connect("toggled", self._on_pause_clicked)
		
		self.disabled_on_busy.append(self.reset_menu_btn)
		self.disabled_on_busy.append(self.run_menu_btn)
		self.disabled_on_busy.append(self.stop_menu_btn)
		self.disabled_on_busy.append(self.step_menu_btn)
		self.disabled_on_busy.append(self.multi_step_menu_btn)
		self.disabled_on_busy.append(self.pause_menu_btn)
		
		control_submenu = self._make_menu((
			self.reset_menu_btn,
			None,
			self.run_menu_btn,
			self.stop_menu_btn,
			None,
			self.step_menu_btn,
			self.multi_step_menu_btn,
			self.pause_menu_btn,
		))
		control_menu = self._make_menu_item("_Control")
		control_menu.set_submenu(control_submenu)
		self.menubar.append(control_menu)
		
		self.base_bin_btn = self._make_menu_item("_Binary", radio = True,
		                                         accelerator = "<Control>b")
		self.base_oct_btn = self._make_menu_item("_Octal", radio = True,
		                                         group = self.base_bin_btn,
		                                         accelerator = "<Control>o")
		self.base_dec_btn = self._make_menu_item("_Decimal", radio = True,
		                                         group = self.base_bin_btn,
		                                         accelerator = "<Control>d")
		self.base_hex_btn = self._make_menu_item("_Hexadecimal", radio = True,
		                                         group = self.base_bin_btn,
		                                         accelerator = "<Control>h")
		
		self.base_bin_btn.connect("toggled", self._on_base_changed, 2)
		self.base_oct_btn.connect("toggled", self._on_base_changed, 8)
		self.base_dec_btn.connect("toggled", self._on_base_changed, 10)
		self.base_hex_btn.connect("toggled", self._on_base_changed, 16)
		
		if format.format_base   == 2:  self.base_bin_btn.set_active(True)
		elif format.format_base == 8:  self.base_oct_btn.set_active(True)
		elif format.format_base == 10: self.base_dec_btn.set_active(True)
		elif format.format_base == 16: self.base_hex_btn.set_active(True)
		
		
		self.base_prefix_btn = self._make_menu_item("Show Base _Prefix",
		                                            check = True,
		                                            accelerator = "<Control>p")
		self.base_prefix_btn.connect("toggled", self._on_base_prefix_btn_toggled)
		self.base_prefix_btn.set_active(format.format_show_prefix)
		window_submenu = self._make_menu((
			("New _Memory Viewer",   gtk.STOCK_NEW, self._on_new_memory_viewer_clicked, "<Control>m"),
			("New _Register Viewer", gtk.STOCK_NEW, self._on_new_register_viewer_clicked, "<Control>r"),
			None,
			self.base_bin_btn,
			self.base_oct_btn,
			self.base_dec_btn,
			self.base_hex_btn,
			None,
			self.base_prefix_btn,
		))
		window_menu = self._make_menu_item("_Window")
		window_menu.set_submenu(window_submenu)
		self.menubar.append(window_menu)
		
		self.periph_submenu = gtk.Menu()
		self.periph_menu = self._make_menu_item("_Peripherals")
		self.periph_menu.set_submenu(self.periph_submenu)
		self.periph_menu.set_no_show_all(True)
		self.periph_menu.hide()
		self.menubar.append(self.periph_menu)
		
		help_submenu = self._make_menu((
			("About", gtk.STOCK_ABOUT, self._on_about_clicked),
		))
		help_menu = self._make_menu_item("_Help")
		help_menu.set_submenu(help_submenu)
		self.menubar.append(help_menu)
	
	
	def _init_toolbar(self):
		"""
		Add the main buttons to the toolbar
		"""
		b = self._add_button("Reset", gtk.STOCK_REFRESH, self._on_reset_clicked,
		                     "Reset the board (F2)")
		self.disabled_on_busy.append(b)
		assemble_submenu = self._make_menu((
			("Reassemble (F3)", gtk.STOCK_CONVERT, self._on_reassemble_clicked),
			("Select Source File", gtk.STOCK_OPEN, self._on_select_source_file_clicked),
		))
		self.assemble_btn = self._add_menu_button("Assemble", gtk.STOCK_CONVERT, assemble_submenu,
		                                          self._on_assemble_clicked,
		                                          "Reassemble source file (F3)")
		load_submenu = self._make_menu((
			("Reload (F4)", gtk.STOCK_GO_DOWN, self._on_reload_clicked),
			("Select Image File", gtk.STOCK_OPEN, self._on_select_image_file_clicked),
		))
		self.load_btn = self._add_menu_button("Load", gtk.STOCK_GO_DOWN, load_submenu,
		                                      self._on_load_clicked,
		                                      "Load memory image onto device (F4)")
		
		self.disabled_on_no_assembler.append(self.assemble_btn)
		self.disabled_on_no_loader.append(self.load_btn)
		
		self._add_separator()
		
		b = self._add_button("Run", gtk.STOCK_MEDIA_PLAY, self._on_run_clicked,
		                     "Run indefinitely (F5)")
		self.disabled_on_busy.append(b)
		b = self._add_button("Stop", gtk.STOCK_MEDIA_STOP, self._on_stop_clicked,
		                     "Stop and ignore remaining steps (F6)")
		self.disabled_on_busy.append(b)
		
		self._add_separator()
		
		b = self._add_button("Step", gtk.STOCK_MEDIA_NEXT, self._on_step_clicked,
		                     "Execute a single step (F7)")
		self.disabled_on_busy.append(b)
		b = self._add_button("Multi-Step", gtk.STOCK_MEDIA_FORWARD, self._on_multi_step_clicked,
		                     "Run for the specified number of steps (F8)")
		self.disabled_on_busy.append(b)
		self.pause_btn = self._add_toggle_button(
		                      "Pause", gtk.STOCK_MEDIA_PAUSE, self._on_pause_clicked,
		                      "Toggle Pause Multi-Stepping (F9)")
		self.disabled_on_busy.append(self.pause_btn)
		self.multi_step_spin = self._add_spin_box(4, 1, 1<<32, self._on_multi_step_changed,
		                   "Number of steps to execute for Multi-Step")
		self.disabled_on_busy.append(self.multi_step_spin)
		
		# Show text and icons
		self.toolbar.set_style(gtk.TOOLBAR_BOTH)
		
		# The separator before the peripherals
		self.periph_sep = self._add_separator()
		self.periph_sep.set_no_show_all(True)
		self.periph_sep.hide()
		
		# Make all buttons homogeneous
		for tool in self.toolbar:
			tool.set_homogeneous(True)
	
	
	def _add_button(self, text, icon_stock_id, callback, tooltip, icon = None):
		# Load the stock icon
		if icon is None:
			icon = gtk.Image()
			icon.set_from_stock(icon_stock_id, gtk.ICON_SIZE_LARGE_TOOLBAR)
			icon.show()
		
		# Create the button
		btn = gtk.ToolButton(icon, text)
		btn.connect("clicked", callback)
		btn.set_tooltip_text(tooltip)
		
		# Add it to the toolbar
		self.toolbar.insert(btn, -1)
		btn.show()
		
		return btn
	
	
	def _add_menu_button(self, text, icon_stock_id, menu, callback, tooltip):
		# Load the stock icon
		icon = gtk.Image()
		icon.set_from_stock(icon_stock_id, gtk.ICON_SIZE_LARGE_TOOLBAR)
		icon.show()
		
		# Create the button
		btn = gtk.MenuToolButton(icon, text)
		btn.set_menu(menu)
		btn.connect("clicked", callback)
		btn.set_tooltip_text(tooltip)
		
		# Add it to the toolbar
		self.toolbar.insert(btn, -1)
		btn.show()
		
		return btn
	
	
	
	def _make_menu_item(self, item, icon_stock_id = None, icon = None,
	                    check = False, radio = False, group = None,
	                    accelerator = None):
		"""
		Create a MenuItem (or Radio/CheckMenuItem) with the given text and icon_stock_id.
		"""
		if accelerator is not None:
			key,mod = gtk.accelerator_parse(accelerator)
		
		# Create the menu item
		if check:
			menu_item = gtk.CheckMenuItem()
		elif radio:
			menu_item = gtk.RadioMenuItem(group)
		else:
			menu_item = gtk.ImageMenuItem()
			menu_item.set_always_show_image(True)
		
		if accelerator is not None:
			menu_item.add_accelerator("activate", self.accelerators, key,mod, gtk.ACCEL_VISIBLE)
		
		menu_item.set_use_underline(True)
		menu_item.set_label(item)
		
		if icon_stock_id is not None and not check:
			icon = gtk.Image()
			icon.set_from_stock(icon_stock_id, gtk.ICON_SIZE_MENU)
			icon.show()
		
		if icon is not None:
			menu_item.set_image(icon)
		
		return menu_item
	
	
	def _make_menu(self, items):
		"""
		Make a GTK Menu with (item, icon_stock_id, callback) pairs.
		"""
		menu = gtk.Menu()
		
		for item in items:
			if item is None:
				menu.append(gtk.SeparatorMenuItem())
			elif type(item) is tuple:
				if len(item) == 3:
					item, icon_stock_id, callback = item
					accelerator = None
				elif len(item) == 4:
					item, icon_stock_id, callback, accelerator = item
				
				menu_item = self._make_menu_item(item, icon_stock_id,
				                                 accelerator = accelerator)
				
				# Add callback and add to the menu
				menu_item.connect("activate", callback)
				menu_item.show()
				menu.append(menu_item)
			else:
				item.show()
				menu.append(item)
		
		return menu
	
	
	def _add_toggle_button(self, text, icon_stock_id, callback, tooltip):
		# Load the icon
		icon = gtk.Image()
		icon.set_from_stock(icon_stock_id, gtk.ICON_SIZE_LARGE_TOOLBAR)
		icon.show()
		
		# Create the button
		btn = gtk.ToggleToolButton()
		btn.set_label(text)
		btn.set_icon_widget(icon)
		btn.set_tooltip_text(tooltip)
		btn.connect("clicked", callback)
		
		# Add to the toolbar
		self.toolbar.insert(btn, -1)
		btn.show()
		
		return btn
	
	
	def _add_spin_box(self, value, min_value, max_value, callback, tooltip):
		# Create a container for the item
		tool = gtk.ToolItem()
		
		# Create the model of the value chosen
		adj = gtk.Adjustment(value, min_value, max_value, 1, 1)
		
		# Create the spinbox
		spin = gtk.SpinButton(adj)
		spin.set_tooltip_text(tooltip)
		spin.connect("activate", callback)
		
		# Put the spinbox in the container
		tool.add(spin)
		spin.show()
		
		# Add the container to the toolbar
		self.toolbar.insert(tool, -1)
		tool.show()
		
		return spin
	
	
	def _add_separator(self):
		sep = gtk.SeparatorToolItem()
		self.toolbar.insert(sep, -1)
		sep.show()
		return sep
	
	
	def add_periph(self, periph_widget, callback):
		"""
		Add a button for the given peripheral widget with the click event connected
		to the provided callback.
		"""
		self.periph_sep.show()
		self.periph_menu.show()
		
		# Add to toolbar
		icon = gtk.Image()
		icon.set_from_pixbuf(periph_widget.get_icon(gtk.ICON_SIZE_LARGE_TOOLBAR))
		icon.show()
		tool_item = self._add_button(periph_widget.get_short_name(),
		                             None, callback,
		                             periph_widget.get_name(),
		                             icon = icon)
		
		# Add to menubar
		icon = gtk.Image()
		icon.set_from_pixbuf(periph_widget.get_icon(gtk.ICON_SIZE_MENU))
		icon.show()
		menu_item = self._make_menu_item(periph_widget.get_name(),
		                                 icon = icon)
		menu_item.connect("activate", callback)
		menu_item.show()
		self.periph_submenu.append(menu_item)
		
		# Store a refrence to the toolbar and menu items
		assert(periph_widget not in self.periphs)
		self.periphs[periph_widget] = (tool_item, menu_item)
	
	
	def remove_periph(self, periph_widget):
		"""
		Remove the given periph_widget's menu/toolbar entries
		"""
		tool_item, menu_item = self.periphs[periph_widget]
		del self.periphs[periph_widget]
		
		# Remove the toolbar item
		self.toolbar.remove(tool_item)
		tool_item.destroy()
		
		# Remove the menu item
		self.periph_submenu.remove(menu_item)
		menu_item.destroy()
		
		# If that was the last periph, hide the seperators/menus
		if not self.periphs:
			self.periph_sep.hide()
			self.periph_menu.hide()
	
	
	@RunInBackground()
	def _on_reset_clicked(self, btn):
		self.system.reset()
		
		# Return to GTK thread
		yield
		
		self.emit("device-state-changed")
	
	
	def _on_assemble_clicked(self, btn):
		"""
		When the assemble button is clicked, assemble if a file is already set,
		otherwise chose a new one.
		"""
		if self.system.get_source_filename() is None:
			self._on_select_source_file_clicked(btn)
		else:
			self._on_reassemble_clicked(btn)
	
	assembler_background_decorator = RunInBackground()
	@assembler_background_decorator
	def _on_reassemble_clicked(self, btn):
		"""
		Reassemble the current source file
		"""
		if self.system.get_source_filename() is None:
			return
		
		# Set the progress to 0% and then assemble in the background
		yield (0,100)
		self.system.assemble()
		
		# Return to the GTK thread to emit the event.
		yield
		
		self.emit("device-state-changed")
	
	
	def _on_select_source_file_clicked(self, btn):
		selection = gtk.FileChooserDialog("Select source file", None,
		                                  gtk.FILE_CHOOSER_ACTION_OPEN,
		                                  (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
		                                   gtk.STOCK_OPEN, gtk.RESPONSE_OK)
		)
		
		file_filter = gtk.FileFilter()
		file_filter.set_name("Assembly File (*.s)")
		file_filter.add_pattern("*.s")
		
		all_filter = gtk.FileFilter()
		all_filter.set_name("All Files")
		all_filter.add_pattern("*.*")
		
		selection.add_filter(file_filter)
		selection.add_filter(all_filter)
		
		if selection.run() == gtk.RESPONSE_OK:
			self.system.set_source_filename(selection.get_filename())
			self._on_reassemble_clicked(btn)
		
		selection.destroy()
	
	
	def _on_load_clicked(self, btn):
		"""
		When the load button is clicked, load if a file is already set,
		otherwise chose a new one.
		"""
		if self.system.get_image_filename() is None:
			self._on_select_image_file_clicked(btn)
		else:
			self._on_reload_clicked(btn)
	
	
	loader_background_decorator = RunInBackground()
	@loader_background_decorator
	def _on_reload_clicked(self, btn):
		"""
		Reload the current image file
		"""
		# Load the memory in a background thread
		if self.system.get_image_filename() is None:
			return
		
		# Start displaying progress
		yield (0,1)
		
		for progress in self.system.load_image_():
			from time import sleep
			yield progress
		
		# Return to the GTK thread
		yield
		
		# Update the symbol viewer
		if self.symbol_viewer is not None:
			self.symbol_viewer.refresh_symbols()
		
		self.emit("device-state-changed")
	
	
	def _on_select_image_file_clicked(self, btn):
		selection = gtk.FileChooserDialog("Select image file", None,
		                                  gtk.FILE_CHOOSER_ACTION_OPEN,
		                                  (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
		                                   gtk.STOCK_OPEN, gtk.RESPONSE_OK)
		)
		
		formats = ["*%s"%f for f in self.system.get_loader_formats()]
		
		file_filter = gtk.FileFilter()
		file_filter.set_name("Image File (%s)"%(", ".join(formats)))
		for f in formats:
			file_filter.add_pattern(f)
		
		all_filter = gtk.FileFilter()
		all_filter.set_name("All Files")
		all_filter.add_pattern("*.*")
		
		selection.add_filter(file_filter)
		selection.add_filter(all_filter)
		
		if selection.run() == gtk.RESPONSE_OK:
			self.system.set_image_filename(selection.get_filename())
			self._on_reload_clicked(btn)
		
		selection.destroy()
	
	
	@RunInBackground()
	def _on_run_clicked(self, btn):
		"""
		Run button clicked: Start executing indefinitely
		"""
		self.system.run()
		
		# Return to GTK thread
		yield
		
		self.emit("device-state-changed")
	
	
	@RunInBackground()
	def _on_stop_clicked(self, btn):
		"""
		Stop button clicked: Stop the device
		"""
		self.system.stop()
		
		# Return to GTK thread
		yield
		
		self.emit("device-state-changed")
	
	
	@RunInBackground()
	def _on_step_clicked(self, btn):
		"""
		Step button clicked: Execute a single step.
		"""
		self.system.run(1, break_on_first_instruction = False)
		
		# Return to GTK thread
		yield
		
		self.emit("device-state-changed")
	
	
	def _on_multi_step_clicked(self, btn):
		"""
		Multi-Step button clicked: Execute a number of steps in the spin box
		"""
		self.multi_step_spin.update()
		self.multi_step_spin.activate()
	
	
	@RunInBackground()
	def _on_pause_clicked(self, btn):
		"""
		Pause/unpause execution
		"""
		status, steps_remaining, steps_since_reset = self.system.get_status()
		running = status in (self.system.STATUS_RUNNING,
		                     self.system.STATUS_RUNNING_SWI)
		
		if running:
			self.system.pause_execution()
		else:
			self.system.continue_execution()
		
		# Return to GTK thread
		yield
		
		self.emit("device-state-changed")
	
	
	@RunInBackground(start_in_gtk = True)
	def _on_multi_step_changed(self, btn):
		num_steps = int(self.multi_step_spin.get_value_as_int())
		
		# Run in background
		yield
		
		self.system.run(num_steps, break_on_first_instruction = True)
		
		# Return to GTK thread
		yield
		
		self.emit("device-state-changed")
	
	def _on_auto_refresh_toggled(self, btn):
		self.emit("auto-refresh-toggled")
	
	
	def _on_select_target_clicked(self, btn):
		self.emit("select-target-clicked")
	
	
	def _on_symbol_viewer_clicked(self, btn):
		"""
		Show a window with an information viewer in it.
		"""
		if self.symbol_viewer is not None:
			# Raise the window if it is already open
			self.symbol_viewer.get_parent().present()
		else:
			self.symbol_viewer = SymbolViewer(self.system)
			
			window = gtk.Window()
			# XXX: Getting a refrence to the top-level main window I can't see any
			# other way of doing it than knowing how many levels of container we're
			# in...
			window.set_transient_for(self.get_parent().get_parent())
			window.add(self.symbol_viewer)
			window.set_title("Symbols")
			window.set_default_size(450, 550)
			
			def on_dismiss(widget, self, window):
				self.symbol_viewer = None
			window.connect("destroy", on_dismiss, self, window)
			
			window.show_all()
			window.present()
	
	
	def _on_info_clicked(self, btn):
		"""
		Show a window with an information viewer in it.
		"""
		if self.device_info_viewer is not None:
			# Raise the window if it is already open
			self.device_info_viewer.get_parent().present()
		else:
			self.device_info_viewer = DeviceInfoViewer(self.system)
			
			window = gtk.Window()
			# XXX: Getting a refrence to the top-level main window I can't see any
			# other way of doing it than knowing how many levels of container we're
			# in...
			window.set_transient_for(self.get_parent().get_parent())
			window.add(self.device_info_viewer)
			window.set_title("Device Information")
			window.set_default_size(450, 550)
			
			def on_dismiss(widget):
				window.destroy()
				self.device_info_viewer = None
			self.device_info_viewer.connect("dismissed", on_dismiss)
			window.connect("destroy", on_dismiss)
			
			window.show_all()
			window.present()
	
	
	def _on_about_clicked(self, btn):
		about_dialog = AboutDialog()
		# XXX: Getting a refrence to the top-level main window I can't see any
		# other way of doing it than knowing how many levels of container we're
		# in...
		about_dialog.set_transient_for(self.get_parent().get_parent())
		
		def close_dialog(*args):
			about_dialog.destroy()
		about_dialog.connect("response", close_dialog)
		about_dialog.connect("close", close_dialog)
		
		about_dialog.show_all()
	
	
	def _on_refresh_clicked(self, btn):
		self.emit("refresh-clicked")
	
	
	def _on_quit_clicked(self, btn):
		self.emit("quit-clicked")
	
	def _on_new_memory_viewer_clicked(self, btn):
		self.emit("new-memory-viewer-clicked")
	
	
	def _on_new_register_viewer_clicked(self, btn):
		self.emit("new-register-viewer-clicked")
	
	
	def _on_base_changed(self, btn, base):
		"""
		Change the base of the formatted fields
		"""
		if btn.get_active():
			format.set_base(base)
		
		self.emit("device-state-changed")
	
	def _on_base_prefix_btn_toggled(self, btn):
		"""
		Enable/disable the prefix in formatted values.
		"""
		format.set_show_prefix(btn.get_active())
		
		self.emit("device-state-changed")
	
	
	@RunInBackground()
	def _refresh_pause_btn(self):
		"""
		Update the state/tooltip of the pause button
		"""
		status, steps_remaining, steps_since_reset = self.system.get_status()
		
		stopped = status in (self.system.STATUS_STOPPED,
		                     self.system.STATUS_STOPPED_BREAKPOINT,
		                     self.system.STATUS_STOPPED_WATCHPOINT,
		                     self.system.STATUS_STOPPED_MEM_FAULT,
		                     self.system.STATUS_STOPPED_PROG_REQ)
		
		# If we're stopped and there are steps remaining, we're paused
		paused = stopped and steps_remaining > 0
		
		# Run in GTK thread
		yield
		
		# Update the pause button
		self.pause_btn.handler_block_by_func(self._on_pause_clicked)
		self.pause_btn.set_active(paused)
		self.pause_btn.handler_unblock_by_func(self._on_pause_clicked)
		
		self.pause_menu_btn.handler_block_by_func(self._on_pause_clicked)
		self.pause_menu_btn.set_active(paused)
		self.pause_menu_btn.handler_unblock_by_func(self._on_pause_clicked)
	
	
	@RunInBackground()
	def _refresh_busy(self):
		"""
		If the processor is busy, disable the control buttons.
		"""
		status, _, _ = self.system.get_status()
		
		is_busy = status == self.system.STATUS_BUSY
		
		# Run in GTK thread
		yield
		
		# Update the pause button
		for widget in self.disabled_on_busy:
			widget.set_sensitive(not is_busy)
	
	
	def refresh(self):
		self._refresh_pause_btn()
		self._refresh_busy()
		
		if self.device_info_viewer is not None:
			self.device_info_viewer.refresh()
		
		if self.symbol_viewer is not None:
			self.symbol_viewer.refresh()
	
	
	def architecture_changed(self):
		"""
		Called when the architecture changes, deals with all the
		architecture-specific changes which need to be made to the GUI.
		"""
		# Enable/disable assembly/loading buttons as appropriate
		can_assemble = False
		can_load     = False
		
		if self.system.architecture is not None:
			memories = self.system.architecture.memories
			can_load = len(memories) > 0
			can_assemble = len(sum((m.assemblers for m in memories), [])) > 0
		
		for widget in self.disabled_on_no_assembler:
			widget.set_sensitive(can_assemble)
		
		for widget in self.disabled_on_no_loader:
			widget.set_sensitive(can_load)
		
		if self.device_info_viewer is not None:
			self.device_info_viewer.architecture_changed()
		
		if self.symbol_viewer is not None:
			self.symbol_viewer.architecture_changed()
		
		self.refresh()