[kupfer: 1/53] Allow to manually configure default actions for objects



commit fdd2e101a8b2c36eb01b7ee5d6a64512517013b0
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date:   Thu Mar 24 17:22:31 2011 +0100

    Allow to manually configure default actions for objects

 kupfer/core/data.py   |   42 +++++++++++++++++++++++++++++++++++++++---
 kupfer/core/learn.py  |   36 +++++++++++++++++++++++++++++++++++-
 kupfer/core/search.py |    3 ++-
 kupfer/ui/browser.py  |   23 +++++++++++++++++++++++
 4 files changed, 99 insertions(+), 5 deletions(-)
---
diff --git a/kupfer/core/data.py b/kupfer/core/data.py
index f9f3bf9..08d3a8d 100644
--- a/kupfer/core/data.py
+++ b/kupfer/core/data.py
@@ -145,11 +145,13 @@ class Searcher (object):
 		match, match_iter = peekfirst(decorator(valid_check(unique_matches)))
 		return match, match_iter
 
-	def rank_actions(self, objects, key, item_check=None, decorator=None):
+	def rank_actions(self, objects, key, leaf, item_check=None, decorator=None):
 		"""
 		rank @objects, which should be a sequence of KupferObjects,
 		for @key, with the action ranker algorithm.
 
+		@leaf is the Leaf the action is going to be invoked on
+
 		Filters and return value like .score().
 		"""
 		if not item_check: item_check = identity
@@ -160,7 +162,7 @@ class Searcher (object):
 			rankables = search.score_objects(rankables, key)
 			matches = search.bonus_objects(rankables, key)
 		else:
-			matches = search.score_actions(rankables)
+			matches = search.score_actions(rankables, leaf)
 		matches = sorted(matches, key=operator.attrgetter("rank"), reverse=True)
 
 		match, match_iter = peekfirst(decorator(matches))
@@ -348,7 +350,7 @@ class PrimaryActionPane (Pane):
 				if is_valid_cached(obj.object):
 					yield obj
 
-		match, match_iter = self.searcher.rank_actions(actions, key,
+		match, match_iter = self.searcher.rank_actions(actions, key, leaf,
 				decorator=valid_decorator)
 		self.emit_search_result(match, match_iter, context)
 
@@ -839,6 +841,40 @@ class DataController (gobject.GObject, pretty.OutputMixin):
 		if found and not found == self.source_pane.get_selection():
 			self._insert_object(SourcePane, found)
 
+	def mark_as_default(self, pane):
+		"""
+		Make the object selected on @pane as default
+		for the selection in previous pane.
+		"""
+		if pane is SourcePane or pane is ObjectPane:
+			raise RuntimeError("Setting default on pane 1 or 3 not supported")
+		obj = self.source_pane.get_selection()
+		act = self.action_pane.get_selection()
+		assert obj and act
+		learn.set_correlation(act, obj)
+
+	def get_object_has_affinity(self, pane):
+		"""
+		Return ``True`` if we have any recorded affinity
+		for the object selected in @pane
+		"""
+		panectl = self._panectl_table[pane]
+		selection = panectl.get_selection()
+		if not selection:
+			return None
+		return learn.get_object_has_affinity(selection)
+
+	def erase_object_affinity(self, pane):
+		"""
+		Erase all learned and configured affinity for
+		the selection of @pane
+		"""
+		panectl = self._panectl_table[pane]
+		selection = panectl.get_selection()
+		if not selection:
+			return None
+		return learn.erase_object_affinity(selection)
+
 	def compose_selection(self):
 		leaf, action, iobj = self._get_current_command_objects()
 		if leaf is None:
diff --git a/kupfer/core/learn.py b/kupfer/core/learn.py
index fe31139..4369f40 100644
--- a/kupfer/core/learn.py
+++ b/kupfer/core/learn.py
@@ -6,6 +6,7 @@ from kupfer import conspickle
 from kupfer import pretty
 
 mnemonics_filename = "mnemonics.pickle"
+CORRELATION_KEY = 'kupfer.bonus.correlation'
 
 class Mnemonics (object):
 	"""
@@ -102,6 +103,39 @@ def get_record_score(obj, key=u""):
 	mnscore += 50 * (1 - 1.0/(exact + 1))
 	return fav + mnscore
 
+
+def get_correlation_bonus(obj, for_leaf):
+	"""
+	Get the bonus rank for @obj when used with @for_leaf
+	"""
+	if _register.setdefault(CORRELATION_KEY, {}).get(repr(for_leaf)) == repr(obj):
+		return 50
+	else:
+		return 0
+
+def set_correlation(obj, for_leaf):
+	"""
+	Register @obj to get a bonus when used with @for_leaf
+	"""
+	_register.setdefault(CORRELATION_KEY, {})[repr(for_leaf)] = repr(obj)
+
+def _get_mnemonic_items(in_register):
+	return [(k,v) for k,v in in_register.items() if k != CORRELATION_KEY]
+
+def get_object_has_affinity(obj):
+	"""
+	Return if @obj has any positive score in the register
+	"""
+	return bool(_register.get(repr(obj)) or
+	            _register.get(CORRELATION_KEY, {}).get(repr(obj)))
+
+def erase_object_affinity(obj):
+	"""
+	Remove all track of affinity for @obj
+	"""
+	_register.pop(repr(obj), None)
+	_register.get(CORRELATION_KEY, {}).pop(repr(obj), None)
+
 def _prune_register():
 	"""
 	Remove items with chance (len/25000)
@@ -121,7 +155,7 @@ def _prune_register():
 	alpha = flux/goalitems**2
 
 	chance = min(0.1, len(_register)*alpha)
-	for leaf, mn in _register.items():
+	for leaf, mn in _get_mnemonic_items(_register):
 		if rand() > chance:
 			continue
 		mn.decrement()
diff --git a/kupfer/core/search.py b/kupfer/core/search.py
index 26a2278..d7240fa 100644
--- a/kupfer/core/search.py
+++ b/kupfer/core/search.py
@@ -75,13 +75,14 @@ def score_objects(rankables, key):
 			yield rb
 
 
-def score_actions(rankables):
+def score_actions(rankables, for_leaf):
 	"""Alternative (rigid) scoring mechanism for objects,
 	putting much more weight in rank_adjust
 	"""
 	get_record_score = learn.get_record_score
 	for obj in rankables:
 		ra = obj.object.rank_adjust
+		ra += learn.get_correlation_bonus(obj.object, for_leaf)
 		if ra > 0:
 			obj.rank = 50 + ra + get_record_score(obj.object)//2
 		elif ra == 0:
diff --git a/kupfer/ui/browser.py b/kupfer/ui/browser.py
index 3c5a153..0d269ba 100644
--- a/kupfer/ui/browser.py
+++ b/kupfer/ui/browser.py
@@ -1344,6 +1344,17 @@ class Interface (gobject.GObject):
 	def compose_action(self):
 		self.data_controller.compose_selection()
 
+	def mark_as_default(self):
+		if self.action.get_match_state() != State.Match:
+			return False
+		self.data_controller.mark_as_default(data.ActionPane)
+		return True
+
+	def erase_affinity_for_first_pane(self):
+		self.data_controller.erase_object_affinity(data.SourcePane)
+		return True
+
+
 	def comma_trick(self):
 		if self.current.get_match_state() != State.Match:
 			return False
@@ -1367,6 +1378,18 @@ class Interface (gobject.GObject):
 		yield (_("Select Selected Text"), self.select_selected_text)
 		if self.get_can_enter_text_mode():
 			yield (_("Toggle Text Mode"), self.toggle_text_mode_quick)
+		if self.action.get_match_state() == State.Match:
+			match = self.action.get_current()
+			# TRANS: Remember = Make the action '%s' default
+			yield (_('Remember "%s" for this Object') % unicode(match),
+			       self.mark_as_default)
+		if self.search.get_match_state() == State.Match:
+			if self.data_controller.get_object_has_affinity(data.SourcePane):
+				match = self.search.get_current()
+				# TRANS: Affinity= learned and/or configured bonus rank
+				# TRANS: when matching it in search
+				yield (_('Forget Affinity for "%s"') % unicode(match),
+				       self.erase_affinity_for_first_pane)
 
 	def _pane_reset(self, controller, pane, item):
 		wid = self._widget_for_pane(pane)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]