# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: mhameed@src.gnome.org-20110504192104-ctvtkbunjfr9wkbe
# target_branch: http://bzr.nvaccess.org/nvda/main/
# testament_sha1: f1eccb6adc6803b0822734aaa506aec0db0d1e09
# timestamp: 2011-05-04 20:21:49 +0100
# base_revision_id: mick@kulgan.net-20110504114646-9rlkcpcxl75iremd
# 
# Begin patch
=== modified file 'source/config/__init__.py'
--- source/config/__init__.py	2011-04-23 01:33:21 +0000
+++ source/config/__init__.py	2011-05-04 19:21:04 +0000
@@ -140,6 +140,7 @@
 	reportSpellingErrors = boolean(default=true)
 	reportPage = boolean(default=true)
 	reportLineNumber = boolean(default=False)
+	reportLineIndentation = boolean(default=False)
 	reportTables = boolean(default=true)
 	includeLayoutTables = boolean(default=False)
 	reportTableHeaders = boolean(default=True)

=== modified file 'source/gui/settingsDialogs.py'
--- source/gui/settingsDialogs.py	2011-04-14 00:20:39 +0000
+++ source/gui/settingsDialogs.py	2011-05-04 19:21:04 +0000
@@ -733,6 +733,12 @@
 		self.lineNumberCheckBox=wx.CheckBox(self,wx.NewId(),label=_("Report &line numbers"))
 		self.lineNumberCheckBox.SetValue(config.conf["documentFormatting"]["reportLineNumber"])
 		settingsSizer.Add(self.lineNumberCheckBox,border=10,flag=wx.BOTTOM)
+		# Translators: This message is presented in the document formatting settings dialogue
+		# If this option is selected, NVDA will cound the leading spaces and tabs of a line and speak it.
+		#
+		self.lineIndentationCheckBox=wx.CheckBox(self,wx.NewId(),label=_("Report l&ine indentation"))
+		self.lineIndentationCheckBox.SetValue(config.conf["documentFormatting"]["reportLineIndentation"])
+		settingsSizer.Add(self.lineIndentationCheckBox,border=10,flag=wx.BOTTOM)
 		self.tablesCheckBox=wx.CheckBox(self,wx.NewId(),label=_("Report &tables"))
 		self.tablesCheckBox.SetValue(config.conf["documentFormatting"]["reportTables"])
 		settingsSizer.Add(self.tablesCheckBox,border=10,flag=wx.BOTTOM)
@@ -769,6 +775,7 @@
 		config.conf["documentFormatting"]["reportSpellingErrors"]=self.spellingErrorsCheckBox.IsChecked()
 		config.conf["documentFormatting"]["reportPage"]=self.pageCheckBox.IsChecked()
 		config.conf["documentFormatting"]["reportLineNumber"]=self.lineNumberCheckBox.IsChecked()
+		config.conf["documentFormatting"]["reportLineIndentation"]=self.lineIndentationCheckBox.IsChecked()
 		config.conf["documentFormatting"]["reportTables"]=self.tablesCheckBox.IsChecked()
 		config.conf["documentFormatting"]["reportTableHeaders"]=self.tableHeadersCheckBox.IsChecked()
 		config.conf["documentFormatting"]["reportLinks"]=self.linksCheckBox.IsChecked()

=== modified file 'source/speech.py'
--- source/speech.py	2011-05-03 15:48:49 +0000
+++ source/speech.py	2011-05-04 19:21:04 +0000
@@ -295,6 +295,32 @@
 		speechSequence.append(text)
 	speak(speechSequence,symbolLevel=symbolLevel)
 
+indentationReg1 = re.compile(r'^(\s+)')
+indentationReg2 = re.compile("(\t+|\ +)")
+def calculateTextIndentation(line):
+	""" Calculates number of leading tabs and spaces in the order that they are entered in the given line.
+
+	@param line: the line to be examined.
+	@type line: string
+	"""
+
+	lineSegments = indentationReg1.split(line)
+	# We expect to have 3 segments, 
+	# the first is the empty string, second is the leading whitespace, and the third is the remaining text on the line.
+
+	# Translators: no indentation is spoken when the user moves from a line that has indentation, to one that 
+	# does not.
+	#
+	if not indentationReg2.match(lineSegments[1]): return (_("No indentation"), line)
+	whitespaceList = indentationReg2.split(lineSegments[1])
+	res = []
+	locale=languageHandler.getLanguage()
+	for type in whitespaceList:
+		if type in ('', '\r'): continue
+		symbol = characterProcessing.processSpeechSymbol(locale, type[0])
+		res.append(u"{count} {char}".format(count=len(type), char=symbol))
+	return (" ".join(res), lineSegments[2])
+
 def speak(speechSequence,symbolLevel=None):
 	"""Speaks a sequence of text and speech commands
 	@param speechSequence: the sequence of text and L{SpeechCommand} objects to speak
@@ -621,6 +647,14 @@
 				lastTextOkToMerge=False
 
 	text=" ".join(relativeTextList)
+	if unit == textInfos.UNIT_LINE and formatConfig["reportLineIndentation"]:
+		(lineIndentation, text) = calculateTextIndentation(text)
+		oldLineIndentation = getattr(info.obj,'_speakTextInfo_lineIndentationCache', '')
+		if lineIndentation != oldLineIndentation:
+			textList.append(lineIndentation)
+			info.obj._speakTextInfo_lineIndentationCache = lineIndentation
+			textListBlankLen += 1 # need to add one so that we continue to say blank for blank lines.
+
 	# Don't add this text if it is blank.
 	if text and not isBlank(text):
 		textList.append(text)

=== modified file 'user_docs/en/changes.t2t'
--- user_docs/en/changes.t2t	2011-05-04 02:43:49 +0000
+++ user_docs/en/changes.t2t	2011-05-04 19:21:04 +0000
@@ -12,6 +12,7 @@
 - When punctuation or other symbols are repeated more than four times, the number of repetitions is now announced instead of speaking the repeated symbols. (#43)
 - New braille translation table: Norwegian 8 dot computer braille. (#1456)
 - Speech no longer unnaturally pauses at the end of each line when using the say all command. (#149)
+- By selecting report line indentation in the document formatting dialogue, NVDA will announce the number of leading tabs and spaces of the current line in the order that they are entered. (#373)
 
 
 == Changes ==

# Begin bundle
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
