import pygtk
pygtk.require("2.0")

from tuna import tuna, gui
import gobject, gtk, procfs, re, schedutils
try:
	import perf
except:
	pass

def N_(s):
	"""gettext_noop"""
	return s

class process_druid:

	( PROCESS_COL_PID, PROCESS_COL_NAME ) = list(range(2))

	def __init__(self, ps, pid, pid_info, nr_cpus, gladefile):
		self.ps = ps
		self.pid = pid
		self.pid_info = pid_info
		self.nr_cpus = nr_cpus
		self.window = gtk.glade.XML(gladefile, "set_process_attributes", "tuna")
		self.dialog = self.window.get_widget("set_process_attributes")
		pixbuf = self.dialog.render_icon(gtk.STOCK_PREFERENCES,
						 gtk.ICON_SIZE_SMALL_TOOLBAR)
		self.dialog.set_icon(pixbuf)
		event_handlers = { "on_cmdline_regex_changed" : self.on_cmdline_regex_changed,
				   "on_affinity_text_changed" : self.on_affinity_text_changed,
				   "on_sched_policy_combo_changed" : self.on_sched_policy_combo_changed,
				   "on_command_regex_clicked" : self.on_command_regex_clicked,
				   "on_all_these_threads_clicked" : self.on_all_these_threads_clicked,
				   "on_just_this_thread_clicked" : self.on_just_this_thread_clicked }
		self.window.signal_autoconnect(event_handlers)

		self.sched_pri = self.window.get_widget("sched_pri_spin")
		self.sched_policy = self.window.get_widget("sched_policy_combo")
		self.regex_edit = self.window.get_widget("cmdline_regex")
		self.affinity = self.window.get_widget("affinity_text")
		self.just_this_thread = self.window.get_widget("just_this_thread")
		self.all_these_threads = self.window.get_widget("all_these_threads")
		processes = self.window.get_widget("matching_process_list")

		self.sched_pri.set_value(int(pid_info["stat"]["rt_priority"]))
		cmdline_regex = procfs.process_cmdline(pid_info)
		self.affinity_text = tuna.list_to_cpustring(schedutils.get_affinity(pid))
		self.affinity.set_text(self.affinity_text)
		self.create_matching_process_model(processes)
		self.create_policy_model(self.sched_policy)
		self.sched_policy.set_active(schedutils.get_scheduler(pid))
		self.regex_edit.set_text(cmdline_regex)
		self.just_this_thread.set_active(True)
		self.regex_edit.set_sensitive(False)
		if pid not in ps or "threads" not in ps[pid]:
			self.all_these_threads.hide()
		self.on_just_this_thread_clicked(None)

	def refresh_match_pids(self, cmdline_regex):
		self.process_list_store.clear()
		for match_pid in self.ps.find_by_cmdline_regex(cmdline_regex):
			info = self.process_list_store.append()
			pid_info = self.ps[match_pid]
			cmdline = procfs.process_cmdline(pid_info)
			self.process_list_store.set(info, self.PROCESS_COL_PID, match_pid,
						    self.PROCESS_COL_NAME,
						    cmdline)

	def create_matching_process_model(self, processes):
		labels = [ "PID", "Name" ]

		self.process_list_store = gtk.ListStore(gobject.TYPE_UINT,
							gobject.TYPE_STRING)
		renderer = gtk.CellRendererText()

		for col in range(len(labels)):
			column = gtk.TreeViewColumn(labels[col], renderer, text = col)
			column.set_sort_column_id(col)
			processes.append_column(column)

		processes.set_model(self.process_list_store)

	def create_policy_model(self, policy):
		( COL_TEXT, COL_SCHED ) = list(range(2))
		list_store = gtk.ListStore(gobject.TYPE_STRING,
					   gobject.TYPE_UINT)
		renderer = gtk.CellRendererText()
		policy.pack_start(renderer, True)
		policy.add_attribute(renderer, "text", COL_TEXT)
		for pol in range(4):
			row = list_store.append()
			list_store.set(row, COL_TEXT, schedutils.schedstr(pol),
					    COL_SCHED, pol)
		policy.set_model(list_store)

	def on_cmdline_regex_changed(self, entry):
		process_regex_text = entry.get_text()
		try:
			cmdline_regex = re.compile(process_regex_text)
		except:
			self.process_list_store.clear()
			return
		self.refresh_match_pids(cmdline_regex)

	def on_just_this_thread_clicked(self, button):
		self.regex_edit.set_sensitive(False)
		self.process_list_store.clear()
		info = self.process_list_store.append()
		cmdline = procfs.process_cmdline(self.pid_info)
		self.process_list_store.set(info,
					    self.PROCESS_COL_PID, self.pid,
					    self.PROCESS_COL_NAME, cmdline)

	def on_command_regex_clicked(self, button):
		self.regex_edit.set_sensitive(True)
		self.on_cmdline_regex_changed(self.regex_edit)

	def on_all_these_threads_clicked(self, button):
		self.regex_edit.set_sensitive(False)
		self.process_list_store.clear()
		info = self.process_list_store.append()
		cmdline = procfs.process_cmdline(self.ps[self.pid])
		self.process_list_store.set(info,
					    self.PROCESS_COL_PID, self.pid,
					    self.PROCESS_COL_NAME, cmdline)
		for tid in list(self.ps[self.pid]["threads"].keys()):
			child = self.process_list_store.append()
			self.process_list_store.set(child,
						    self.PROCESS_COL_PID, tid,
						    self.PROCESS_COL_NAME, cmdline)


	def on_sched_policy_combo_changed(self, button):
		new_policy = self.sched_policy.get_active()
		if new_policy in ( schedutils.SCHED_FIFO, schedutils.SCHED_RR ):
			can_change_pri = True
		else:
			can_change_pri = False
		self.sched_pri.set_sensitive(can_change_pri)

	def on_affinity_text_changed(self, button):
		gui.on_affinity_text_changed(self)

	def set_attributes_for_regex(self, regex, new_policy, new_prio, new_affinity):
		changed = False
		cmdline_regex = re.compile(regex)
		for match_pid in self.ps.find_by_cmdline_regex(cmdline_regex):
			if gui.thread_set_attributes(self.ps[match_pid],
						     new_policy, new_prio,
						     new_affinity,
						     self.nr_cpus):
				changed = True

		return changed

	def set_attributes_for_threads(self, pid, new_policy, new_prio, new_affinity):
		changed = False
		threads = self.ps[pid]["threads"]
		for tid in list(threads.keys()):
			if gui.thread_set_attributes(threads[tid], new_policy,
						     new_prio, new_affinity,
						     self.nr_cpus):
				changed = True

		return changed

	def run(self):
		changed = False
		if self.dialog.run() == gtk.RESPONSE_OK:
			new_policy = int(self.sched_policy.get_active())
			new_prio = int(self.sched_pri.get_value())
			new_affinity = self.affinity.get_text()
			if self.just_this_thread.get_active():
				changed = gui.thread_set_attributes(self.pid_info,
								    new_policy,
								    new_prio,
								    new_affinity,
								    self.nr_cpus)
			elif self.all_these_threads.get_active():
				if gui.thread_set_attributes(self.pid_info,
							     new_policy, new_prio,
							     new_affinity,
							     self.nr_cpus):
					changed = True
				if self.set_attributes_for_threads(self.pid,
								   new_policy,
								   new_prio,
								   new_affinity):
					changed = True
			else:
				changed = self.set_attributes_for_regex(self.regex_edit.get_text(),
									new_policy,
									new_prio,
									new_affinity)

		self.dialog.destroy()
		return changed

class procview:

	nr_columns = 8
	( COL_PID, COL_POL, COL_PRI, COL_AFF, COL_VOLCTXT, COL_NONVOLCTXT, COL_CGROUP, COL_CMDLINE ) = list(range(nr_columns))
	columns = (gui.list_store_column(_("PID")),
		   gui.list_store_column(_("Policy"), gobject.TYPE_STRING),
		   gui.list_store_column(_("Priority")),
		   gui.list_store_column(_("Affinity"), gobject.TYPE_STRING),
		   gui.list_store_column(_("VolCtxtSwitch"), gobject.TYPE_UINT),
		   gui.list_store_column(_("NonVolCtxtSwitch"), gobject.TYPE_UINT),
		   gui.list_store_column(_("CGroup"), gobject.TYPE_STRING),
		   gui.list_store_column(_("Command Line"), gobject.TYPE_STRING))

	def __init__(self, treeview, ps,
		     show_kthreads, show_uthreads,
		     cpus_filtered, gladefile):
		self.ps = ps
		self.treeview = treeview
		self.nr_cpus = procfs.cpuinfo().nr_cpus
		self.gladefile = gladefile
	
		self.evlist = None
		try:
			self.perf_init() 
		except: # No perf, poll /proc baby, poll
			pass

		if "voluntary_ctxt_switches" not in ps[1]["status"]:
			self.nr_columns = 5
		else:
			self.nr_columns = 7
		try:
			if ps[1]["cgroups"]:
				self.nr_columns = self.nr_columns + 1
		except:
			pass

		self.columns = (gui.list_store_column(_("PID")),
						gui.list_store_column(_("Policy"), gobject.TYPE_STRING),
						gui.list_store_column(_("Priority")),
						gui.list_store_column(_("Affinity"), gobject.TYPE_STRING))

		if self.nr_columns==5:
			( self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_CMDLINE ) = list(range(self.nr_columns))
			self.columns = self.columns + (gui.list_store_column(_("Command Line"), gobject.TYPE_STRING))

		elif self.nr_columns==6:
			( self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_CGROUP, self.COL_CMDLINE ) = list(range(self.nr_columns))
			self.columns = self.columns + (gui.list_store_column(_("CGroup"), gobject.TYPE_STRING),
										   gui.list_store_column(_("Command Line"), gobject.TYPE_STRING))

		elif self.nr_columns==7:
			( self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_VOLCTXT,
				self.NONVOLCTXT, self.COL_CMDLINE ) = list(range(self.nr_columns))
			self.columns = self.columns + (gui.list_store_column(_("VolCtxtSwitch"), gobject.TYPE_UINT),
										   gui.list_store_column(_("NonVolCtxtSwitch"), gobject.TYPE_UINT),
										   gui.list_store_column(_("Command Line"), gobject.TYPE_STRING))

		elif self.nr_columns==8:
			( self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_VOLCTXT,
				self.COL_NONVOLCTXT, self.COL_CGROUP, self.COL_CMDLINE ) = list(range(self.nr_columns))
			self.columns = self.columns + (gui.list_store_column(_("VolCtxtSwitch"), gobject.TYPE_UINT),
										   gui.list_store_column(_("NonVolCtxtSwitch"), gobject.TYPE_UINT),
										   gui.list_store_column(_("CGroup"), gobject.TYPE_STRING),
										   gui.list_store_column(_("Command Line"), gobject.TYPE_STRING))

		self.tree_store = gtk.TreeStore(*gui.generate_list_store_columns_with_attr(self.columns))
		self.treeview.set_model(self.tree_store)

		# Allow selecting multiple rows
		selection = treeview.get_selection()
		selection.set_mode(gtk.SELECTION_MULTIPLE)

		# Allow enable drag and drop of rows
		self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
						       gui.DND_TARGETS,
						       gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE)
		self.treeview.connect("drag_data_get", self.on_drag_data_get_data)
		try:
			self.treeview.connect("query-tooltip", self.on_query_tooltip)
		except:
			# old versions of pygtk2+ doesn't have this signal
			pass

		self.renderer = gtk.CellRendererText()
		for col in range(self.nr_columns):
			column = gtk.TreeViewColumn(self.columns[col].name,
						    self.renderer, text = col)
			column.add_attribute(self.renderer, "weight",
					     col + self.nr_columns)
			column.set_sort_column_id(col)
			if(col == self.COL_CGROUP):
				column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
				column.set_fixed_width(130)
			try:
				self.treeview.set_tooltip_column(col)
			except:
				# old versions of pygtk2+ doesn't have this signal
				pass
			column.set_resizable(True)
			self.treeview.append_column(column)

		self.show_kthreads = show_kthreads
		self.show_uthreads = show_uthreads
		self.cpus_filtered = cpus_filtered
		self.refreshing = True

	def perf_process_events(self, source, condition):
		had_events = True
		while had_events:
			had_events = False
			for cpu in self.cpu_map:
				event = self.evlist.read_on_cpu(cpu)
				if event:
					had_events = True
					if event.type == perf.RECORD_FORK:
						if event.pid == event.tid:
							try:
								self.ps.processes[event.pid] = procfs.process(event.pid)
							except: # short lived thread
								pass
						else:
							try:
								self.ps.processes[event.pid].threads.processes[event.tid] = procfs.process(event.tid)
							except AttributeError:
								self.ps.processes[event.pid].threads = procfs.pidstats("/proc/%d/task/" % event.pid)
					elif event.type == perf.RECORD_EXIT:
						del self.ps[int(event.tid)]
					elif event.type == perf.RECORD_SAMPLE:
						tid = event.sample_tid
						if tid in self.perf_counter:
							self.perf_counter[tid] += event.sample_period
						else:
							self.perf_counter[tid] = event.sample_period
			
		self.show()
		return True

	def perf_init(self):
		self.cpu_map = perf.cpu_map()
		self.thread_map = perf.thread_map()
		self.evsel_cycles = perf.evsel(task = 1, comm = 1,
					       wakeup_events = 1,
					       watermark = 1,
					       sample_type = perf.SAMPLE_CPU |
							     perf.SAMPLE_TID)
		self.evsel_cycles.open(cpus = self.cpu_map, threads = self.thread_map);
		self.evlist = perf.evlist(self.cpu_map, self.thread_map)
		self.evlist.add(self.evsel_cycles)
		self.evlist.mmap()
		self.pollfd = self.evlist.get_pollfd()
		for f in self.pollfd:
			gobject.io_add_watch(f, gtk.gdk.INPUT_READ, self.perf_process_events)
		self.perf_counter = {}

	def on_query_tooltip(self, treeview, x, y, keyboard_mode, tooltip):
		x, y = treeview.convert_widget_to_bin_window_coords(x, y)
		ret = treeview.get_path_at_pos(x, y)
		tooltip.set_text(None)
		if not ret:
			return True
		path, col, xpos, ypos = ret
		if not path:
			return True
		col_id = col.get_sort_column_id()
		if col_id != self.COL_CMDLINE:
			return True
		row = self.tree_store.get_iter(path)
		if not row:
			return True
		pid = int(self.tree_store.get_value(row, self.COL_PID))
		if not tuna.iskthread(pid):
			return True
		cmdline = self.tree_store.get_value(row, self.COL_CMDLINE).split(' ')[0]
		help = tuna.kthread_help(cmdline)
		tooltip.set_markup("<b>%s %d(%s)</b>\n%s" % \
			(_("Kernel Thread"), pid, cmdline, _(help)))
		return True

	def foreach_selected_cb(self, model, path, iter, pid_list):
		pid = model.get_value(iter, self.COL_PID)
		pid_list.append(str(pid))

	def on_drag_data_get_data(self, treeview, context,
			          selection, target_id, etime):
		treeselection = treeview.get_selection()
		pid_list = []
		treeselection.selected_foreach(self.foreach_selected_cb, pid_list)
		selection.set(selection.target, 8, "pid:" + ",".join(pid_list))

	def set_thread_columns(self, iter, tid, thread_info):
		new_value = [ None ] * self.nr_columns

		new_value[self.COL_PRI] = int(thread_info["stat"]["rt_priority"])
		new_value[self.COL_POL] = schedutils.schedstr(schedutils.get_scheduler(tid))[6:]
		thread_affinity_list = schedutils.get_affinity(tid)

		new_value[self.COL_PID] = tid
		new_value[self.COL_AFF] = tuna.list_to_cpustring(thread_affinity_list)
		try:
			new_value[self.COL_VOLCTXT] = int(thread_info["status"]["voluntary_ctxt_switches"])
			new_value[self.COL_NONVOLCTXT] = int(thread_info["status"]["nonvoluntary_ctxt_switches"])
			new_value[self.COL_CGROUP] = thread_info["cgroups"]
		except:
			pass

		new_value[self.COL_CMDLINE] = procfs.process_cmdline(thread_info)

		gui.set_store_columns(self.tree_store, iter, new_value)

	def show(self, force_refresh = False):
		# Start with the first row, if there is one, on the
		# process list. If the first time update_rows will just
		# have everything in new_tids and append_new_tids will
		# create the rows.
		if not self.refreshing and not force_refresh:
			return
		row = self.tree_store.get_iter_first()
		self.update_rows(self.ps, row, None)
		self.treeview.show_all()

	def update_rows(self, threads, row, parent_row):
		new_tids = list(threads.keys())
		previous_row = None
		while row:
			tid = self.tree_store.get_value(row, self.COL_PID)
			if previous_row:
				previous_tid = self.tree_store.get_value(previous_row, self.COL_PID)
				if previous_tid == tid:
					# print "WARNING: tree_store dup %d, fixing..." % tid
					self.tree_store.remove(previous_row)
			if tid not in threads:
				if self.tree_store.remove(row):
					# removed and now row is the next one
					continue
				# removed and its the last one
				break
			else:
				try:
					new_tids.remove(tid)
				except:
					# FIXME: understand in what situation this
					# can happen, seems harmless from visual
					# inspection.
					pass
				if tuna.thread_filtered(tid, self.cpus_filtered,
						        self.show_kthreads,
							self.show_uthreads):
					if self.tree_store.remove(row):
						# removed and now row is the next one
						continue
					# removed and its the last one
					break
				else:
					try:
						self.set_thread_columns(row, tid, threads[tid])

						if "threads" in threads[tid]:
							children = threads[tid]["threads"]
						else:
							children = {}

						child_row = self.tree_store.iter_children(row)
						self.update_rows(children, child_row, row)
					except: # thread doesn't exists anymore
						if self.tree_store.remove(row):
							# removed and now row is the next one
							continue
						# removed and its the last one
						break

			previous_row = row
			row = self.tree_store.iter_next(row)

		new_tids.sort()
		self.append_new_tids(parent_row, threads, new_tids)

	def append_new_tids(self, parent_row, threads, tid_list):
		for tid in tid_list:
			if tuna.thread_filtered(tid, self.cpus_filtered,
						self.show_kthreads,
						self.show_uthreads):
				continue

			row = self.tree_store.append(parent_row)

			try:
				self.set_thread_columns(row, tid, threads[tid])
			except: # Thread doesn't exists anymore
				self.tree_store.remove(row)
				continue

			if "threads" in threads[tid]:
				children = threads[tid]["threads"]
				children_list = list(children.keys())
				children_list.sort()
				for child in children_list:
					child_row = self.tree_store.append(row)
					try:
						self.set_thread_columns(child_row,
									child,
									children[child])
					except: # Thread doesn't exists anymore
						self.tree_store.remove(child_row)

	def refresh(self):
		self.ps.reload()
		self.ps.reload_threads()

		self.show(True)

	def edit_attributes(self, a):
		ret = self.treeview.get_path_at_pos(self.last_x, self.last_y)
		if not ret:
			return
		path, col, xpos, ypos = ret
		if not path:
			return
		row = self.tree_store.get_iter(path)
		pid = self.tree_store.get_value(row, self.COL_PID)
		if pid in self.ps:
			pid_info = self.ps[pid]
		else:
			parent = self.tree_store.iter_parent(row)
			ppid = self.tree_store.get_value(parent, self.COL_PID)
			pid_info = self.ps[ppid].threads[pid]

		dialog = process_druid(self.ps, pid, pid_info, self.nr_cpus,
				       self.gladefile)
		if dialog.run():
			self.refresh()

	def kthreads_view_toggled(self, a):
		self.show_kthreads = not self.show_kthreads
		self.show(True)

	def uthreads_view_toggled(self, a):
		self.show_uthreads = not self.show_uthreads
		self.show(True)

	def help_dialog(self, a):
		ret = self.treeview.get_path_at_pos(self.last_x, self.last_y)
		if not ret:
			return
		path, col, xpos, ypos = ret
		if not path:
			return
		row = self.tree_store.get_iter(path)
		pid = self.tree_store.get_value(row, self.COL_PID)
		if pid not in self.ps:
			return

		cmdline = self.tree_store.get_value(row, self.COL_CMDLINE)
		help, title = tuna.kthread_help_plain_text(pid, cmdline)

		dialog = gtk.MessageDialog(None,
					   gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
					   gtk.MESSAGE_INFO,
					   gtk.BUTTONS_OK, _(help))
		dialog.set_title(title)
		ret = dialog.run()
		dialog.destroy()

	def refresh_toggle(self, a):
		self.refreshing = not self.refreshing

	def save_kthreads_tunings(self, a):
		dialog = gtk.FileChooserDialog(_("Save As"),
					       None,
					       gtk.FILE_CHOOSER_ACTION_SAVE,
					       (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
						gtk.STOCK_OK, gtk.RESPONSE_OK))
		dialog.set_default_response(gtk.RESPONSE_OK)

		try:
			dialog.set_do_overwrite_confirmation(True)
		except:
			pass

		filter = gtk.FileFilter()
		filter.set_name("rtctl config files")
		filter.add_pattern("*.rtctl")
		filter.add_pattern("*.tuna")
		filter.add_pattern("*rtgroup*")
		dialog.add_filter(filter)

		filter = gtk.FileFilter()
		filter.set_name("All files")
		filter.add_pattern("*")
		dialog.add_filter(filter)

		response = dialog.run()

		filename = dialog.get_filename()
		dialog.destroy()

		if response != gtk.RESPONSE_OK:
			return

		self.refresh()
		kthreads = tuna.get_kthread_sched_tunings(self.ps)
		tuna.generate_rtgroups(filename, kthreads, self.nr_cpus)

		if filename != "/etc/rtgroups":
			dialog = gtk.MessageDialog(None,
						   gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
						   gtk.MESSAGE_INFO,
						   gtk.BUTTONS_YES_NO,
						   "Kernel thread tunings saved!\n\n"
						   "Now you can use it with rtctl:\n\n"
						   "rtctl --file %s reset\n\n"
						   "If you want the changes to be in "
						   "effect every time you boot the system "
						   "please move %s to /etc/rtgroups\n\n"
						   "Do you want to do that now?" % (filename, filename))
			response = dialog.run()
			dialog.destroy()
			if response == gtk.RESPONSE_YES:
				filename = "/etc/rtgroups"
				tuna.generate_rtgroups(filename, kthreads, self.nr_cpus)

		dialog = gtk.MessageDialog(None,
					   gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
					   gtk.MESSAGE_INFO,
					   gtk.BUTTONS_OK,
					   _("Kernel thread tunings saved to %s!") % filename)
		dialog.run()
		dialog.destroy()

	def on_processlist_button_press_event(self, treeview, event):
		if event.type != gtk.gdk.BUTTON_PRESS or event.button != 3:
			return

		self.last_x = int(event.x)
		self.last_y = int(event.y)

		menu = gtk.Menu()

		setattr = gtk.MenuItem(_("_Set process attributes"))
		if self.refreshing:
			refresh = gtk.MenuItem(_("Sto_p refreshing the process list"))
		else:
			refresh = gtk.MenuItem(_("_Refresh the process list"))

		if self.show_kthreads:
			kthreads = gtk.MenuItem(_("_Hide kernel threads"))
		else:
			kthreads = gtk.MenuItem(_("_Show kernel threads"))

		if self.show_uthreads:
			uthreads = gtk.MenuItem(_("_Hide user threads"))
		else:
			uthreads = gtk.MenuItem(_("_Show user threads"))

		help = gtk.MenuItem(_("_What is this?"))

		save_kthreads_tunings = gtk.MenuItem(_("_Save kthreads tunings"))

		menu.add(save_kthreads_tunings)
		menu.add(setattr)
		menu.add(refresh)
		menu.add(kthreads)
		menu.add(uthreads)
		menu.add(help)

		save_kthreads_tunings.connect_object('activate',
						     self.save_kthreads_tunings, event)
		setattr.connect_object('activate', self.edit_attributes, event)
		refresh.connect_object('activate', self.refresh_toggle, event)
		kthreads.connect_object('activate', self.kthreads_view_toggled, event)
		uthreads.connect_object('activate', self.uthreads_view_toggled, event)
		help.connect_object('activate', self.help_dialog, event)

		save_kthreads_tunings.show()
		setattr.show()
		refresh.show()
		kthreads.show()
		uthreads.show()
		help.show()

		menu.popup(None, None, None, event.button, event.time)

	def toggle_mask_cpu(self, cpu, enabled):
		if not enabled:
			if cpu not in self.cpus_filtered:
				self.cpus_filtered.append(cpu)
				self.show(True)
		else:
			if cpu in self.cpus_filtered:
				self.cpus_filtered.remove(cpu)
				self.show(True)
