Completely redesigned the indentation plugin. Now adds a tab to preferences with complete customization for how indentation is presented.
This commit is contained in:
+670
-21
@@ -21,6 +21,55 @@
|
||||
<property name="step_increment">10</property>
|
||||
<property name="page_increment">50</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="indentationSpacesPerLevelAdjustment">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">16</property>
|
||||
<property name="value">4</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="indentationTabWidthAdjustment">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">16</property>
|
||||
<property name="value">4</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="indentationAudioBaseFrequencyAdjustment">
|
||||
<property name="lower">50</property>
|
||||
<property name="upper">2000</property>
|
||||
<property name="value">200</property>
|
||||
<property name="step_increment">10</property>
|
||||
<property name="page_increment">50</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="indentationAudioStepFrequencyAdjustment">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">500</property>
|
||||
<property name="value">80</property>
|
||||
<property name="step_increment">5</property>
|
||||
<property name="page_increment">20</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="indentationAudioMaxFrequencyAdjustment">
|
||||
<property name="lower">200</property>
|
||||
<property name="upper">5000</property>
|
||||
<property name="value">1200</property>
|
||||
<property name="step_increment">50</property>
|
||||
<property name="page_increment">100</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="indentationAudioDurationAdjustment">
|
||||
<property name="lower">0.01</property>
|
||||
<property name="upper">2</property>
|
||||
<property name="value">0.15</property>
|
||||
<property name="step_increment">0.01</property>
|
||||
<property name="page_increment">0.1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="indentationAudioVolumeAdjustment">
|
||||
<property name="lower">0</property>
|
||||
<property name="upper">1</property>
|
||||
<property name="value">0.7</property>
|
||||
<property name="step_increment">0.05</property>
|
||||
<property name="page_increment">0.1</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
@@ -1830,21 +1879,6 @@
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="enableSpeechIndentationCheckButton">
|
||||
<property name="label" translatable="yes">Speak _indentation and justification</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="checkButtonToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="enableMnemonicSpeakingCheckButton">
|
||||
<property name="label" translatable="yes">Spea_k object mnemonics</property>
|
||||
@@ -3331,8 +3365,8 @@
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="textAttributesConfigGrid">
|
||||
<child>
|
||||
<object class="GtkGrid" id="textAttributesConfigGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
@@ -3553,6 +3587,621 @@
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="indentationGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="row_spacing">10</property>
|
||||
<property name="column_spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="indentationPresentationFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="indentationPresentationAlignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="indentationPresentationGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationPresentationOffButton">
|
||||
<property name="label" translatable="yes">_Off</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="indentationPresentationModeToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationPresentationSpeechButton">
|
||||
<property name="label" translatable="yes">_Speech</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">indentationPresentationOffButton</property>
|
||||
<signal name="toggled" handler="indentationPresentationModeToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationPresentationBeepsButton">
|
||||
<property name="label" translatable="yes">_Beeps</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">indentationPresentationOffButton</property>
|
||||
<signal name="toggled" handler="indentationPresentationModeToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationPresentationSpeechAndBeepsButton">
|
||||
<property name="label" translatable="yes">Speech _and beeps</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">indentationPresentationOffButton</property>
|
||||
<signal name="toggled" handler="indentationPresentationModeToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="indentationPresentationLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Presentation Mode</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="indentationChangeFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="indentationChangeAlignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="indentationChangeGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationChangeAlwaysButton">
|
||||
<property name="label" translatable="yes">Always present indentation</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="indentationChangeModeToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationChangeAnyButton">
|
||||
<property name="label" translatable="yes">Present any indentation changes</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">indentationChangeAlwaysButton</property>
|
||||
<signal name="toggled" handler="indentationChangeModeToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationChangeLevelButton">
|
||||
<property name="label" translatable="yes">Present only when indentation level changes</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">indentationChangeAlwaysButton</property>
|
||||
<signal name="toggled" handler="indentationChangeModeToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="indentationChangeLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">When to Present</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="indentationSpeechFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="indentationSpeechAlignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="indentationSpeechGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationSpeechSpacesTabsButton">
|
||||
<property name="label" translatable="yes">Spaces and tabs</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="indentationSpeechStyleToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationSpeechLevelsButton">
|
||||
<property name="label" translatable="yes">Indentation level</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">indentationSpeechSpacesTabsButton</property>
|
||||
<signal name="toggled" handler="indentationSpeechStyleToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationSpeechColumnsButton">
|
||||
<property name="label" translatable="yes">Indentation columns</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">indentationSpeechSpacesTabsButton</property>
|
||||
<signal name="toggled" handler="indentationSpeechStyleToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="indentationSpeechLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Speech Format</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="indentationUnitsFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="indentationUnitsAlignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="indentationUnitsGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="indentationSpacesPerLevelLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Spaces per level:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">indentationSpacesPerLevelSpinButton</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="indentationSpacesPerLevelSpinButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">indentationSpacesPerLevelAdjustment</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="value-changed" handler="indentationSpinValueChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="indentationTabWidthLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Tab width:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">indentationTabWidthSpinButton</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="indentationTabWidthSpinButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">indentationTabWidthAdjustment</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="value-changed" handler="indentationSpinValueChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="indentationAudioUnitLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Audio unit:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="indentationAudioUnitGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationAudioUnitLevelsButton">
|
||||
<property name="label" translatable="yes">Levels</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="indentationAudioUnitToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="indentationAudioUnitColumnsButton">
|
||||
<property name="label" translatable="yes">Columns</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">indentationAudioUnitLevelsButton</property>
|
||||
<signal name="toggled" handler="indentationAudioUnitToggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="indentationUnitsLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Units</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="indentationAudioFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="indentationAudioAlignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="indentationAudioGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="indentationAudioBaseFrequencyLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Base frequency (Hz):</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">indentationAudioBaseFrequencySpinButton</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="indentationAudioBaseFrequencySpinButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">indentationAudioBaseFrequencyAdjustment</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="value-changed" handler="indentationSpinValueChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="indentationAudioStepFrequencyLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Step per unit (Hz):</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">indentationAudioStepFrequencySpinButton</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="indentationAudioStepFrequencySpinButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">indentationAudioStepFrequencyAdjustment</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="value-changed" handler="indentationSpinValueChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="indentationAudioMaxFrequencyLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Maximum frequency (Hz):</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">indentationAudioMaxFrequencySpinButton</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="indentationAudioMaxFrequencySpinButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">indentationAudioMaxFrequencyAdjustment</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="value-changed" handler="indentationSpinValueChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="indentationAudioDurationLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Tone duration (sec):</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">indentationAudioDurationSpinButton</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="indentationAudioDurationSpinButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">indentationAudioDurationAdjustment</property>
|
||||
<property name="climb_rate">0.01</property>
|
||||
<property name="digits">2</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="value-changed" handler="indentationSpinValueChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="indentationAudioVolumeLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Volume multiplier:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">indentationAudioVolumeSpinButton</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="indentationAudioVolumeSpinButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">indentationAudioVolumeAdjustment</property>
|
||||
<property name="climb_rate">0.05</property>
|
||||
<property name="digits">2</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="value-changed" handler="indentationSpinValueChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="indentationAudioLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Audio</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="indentationTabLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Indentation</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">8</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="aiPage">
|
||||
<property name="visible">True</property>
|
||||
@@ -3773,7 +4422,7 @@
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">8</property>
|
||||
<property name="position">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
@@ -3783,7 +4432,7 @@
|
||||
<property name="label" translatable="yes">AI Assistant</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">8</property>
|
||||
<property name="position">9</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -3959,7 +4608,7 @@
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">9</property>
|
||||
<property name="position">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
@@ -3969,7 +4618,7 @@
|
||||
<property name="label" translatable="yes">OCR</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">9</property>
|
||||
<property name="position">10</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
@@ -1482,8 +1482,6 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
||||
self.get_widget("onlySpeakDisplayedTextCheckButton").set_active(
|
||||
prefs["onlySpeakDisplayedText"])
|
||||
|
||||
self.get_widget("enableSpeechIndentationCheckButton").set_active(\
|
||||
prefs["enableSpeechIndentation"])
|
||||
|
||||
self.get_widget("speakBlankLinesCheckButton").set_active(\
|
||||
prefs["speakBlankLines"])
|
||||
@@ -1825,6 +1823,10 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
||||
# AI Assistant settings
|
||||
#
|
||||
self._initAIState()
|
||||
|
||||
# Indentation settings
|
||||
#
|
||||
self._initIndentationState()
|
||||
|
||||
# OCR Plugin settings
|
||||
#
|
||||
@@ -1929,6 +1931,119 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
||||
if enabled:
|
||||
current_provider = self.prefsDict.get("aiProvider", settings.aiProvider)
|
||||
self._updateProviderControls(current_provider)
|
||||
|
||||
def _initIndentationState(self):
|
||||
"""Initialize Indentation tab widgets with current settings."""
|
||||
prefs = self.prefsDict
|
||||
|
||||
self.indentationPresentationOffButton = self.get_widget("indentationPresentationOffButton")
|
||||
self.indentationPresentationSpeechButton = self.get_widget("indentationPresentationSpeechButton")
|
||||
self.indentationPresentationBeepsButton = self.get_widget("indentationPresentationBeepsButton")
|
||||
self.indentationPresentationSpeechAndBeepsButton = self.get_widget("indentationPresentationSpeechAndBeepsButton")
|
||||
|
||||
self.indentationChangeAlwaysButton = self.get_widget("indentationChangeAlwaysButton")
|
||||
self.indentationChangeAnyButton = self.get_widget("indentationChangeAnyButton")
|
||||
self.indentationChangeLevelButton = self.get_widget("indentationChangeLevelButton")
|
||||
|
||||
self.indentationSpeechSpacesTabsButton = self.get_widget("indentationSpeechSpacesTabsButton")
|
||||
self.indentationSpeechLevelsButton = self.get_widget("indentationSpeechLevelsButton")
|
||||
self.indentationSpeechColumnsButton = self.get_widget("indentationSpeechColumnsButton")
|
||||
|
||||
self.indentationAudioUnitLevelsButton = self.get_widget("indentationAudioUnitLevelsButton")
|
||||
self.indentationAudioUnitColumnsButton = self.get_widget("indentationAudioUnitColumnsButton")
|
||||
|
||||
self.indentationSpacesPerLevelSpinButton = self.get_widget("indentationSpacesPerLevelSpinButton")
|
||||
self.indentationTabWidthSpinButton = self.get_widget("indentationTabWidthSpinButton")
|
||||
self.indentationAudioBaseFrequencySpinButton = self.get_widget("indentationAudioBaseFrequencySpinButton")
|
||||
self.indentationAudioStepFrequencySpinButton = self.get_widget("indentationAudioStepFrequencySpinButton")
|
||||
self.indentationAudioMaxFrequencySpinButton = self.get_widget("indentationAudioMaxFrequencySpinButton")
|
||||
self.indentationAudioDurationSpinButton = self.get_widget("indentationAudioDurationSpinButton")
|
||||
self.indentationAudioVolumeSpinButton = self.get_widget("indentationAudioVolumeSpinButton")
|
||||
|
||||
mode = prefs.get("indentationPresentationMode", settings.indentationPresentationMode)
|
||||
if mode == settings.INDENTATION_PRESENTATION_SPEECH:
|
||||
self.indentationPresentationSpeechButton.set_active(True)
|
||||
elif mode == settings.INDENTATION_PRESENTATION_BEEPS:
|
||||
self.indentationPresentationBeepsButton.set_active(True)
|
||||
elif mode == settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS:
|
||||
self.indentationPresentationSpeechAndBeepsButton.set_active(True)
|
||||
else:
|
||||
self.indentationPresentationOffButton.set_active(True)
|
||||
|
||||
enableSpeechIndentation = mode in (
|
||||
settings.INDENTATION_PRESENTATION_SPEECH,
|
||||
settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS,
|
||||
)
|
||||
self.prefsDict["enableSpeechIndentation"] = enableSpeechIndentation
|
||||
|
||||
only_if_changed = prefs.get("speakIndentationOnlyIfChanged", settings.speakIndentationOnlyIfChanged)
|
||||
change_mode = prefs.get("indentationChangeMode", settings.indentationChangeMode)
|
||||
if not only_if_changed or change_mode == settings.INDENTATION_CHANGE_ALWAYS:
|
||||
self.indentationChangeAlwaysButton.set_active(True)
|
||||
elif change_mode == settings.INDENTATION_CHANGE_ANY:
|
||||
self.indentationChangeAnyButton.set_active(True)
|
||||
else:
|
||||
self.indentationChangeLevelButton.set_active(True)
|
||||
|
||||
speech_style = prefs.get("indentationSpeechStyle", settings.indentationSpeechStyle)
|
||||
if speech_style == settings.INDENTATION_SPEECH_STYLE_LEVELS:
|
||||
self.indentationSpeechLevelsButton.set_active(True)
|
||||
elif speech_style == settings.INDENTATION_SPEECH_STYLE_COLUMNS:
|
||||
self.indentationSpeechColumnsButton.set_active(True)
|
||||
else:
|
||||
self.indentationSpeechSpacesTabsButton.set_active(True)
|
||||
|
||||
audio_unit = prefs.get("indentationAudioUnit", settings.indentationAudioUnit)
|
||||
if audio_unit == settings.INDENTATION_UNIT_COLUMNS:
|
||||
self.indentationAudioUnitColumnsButton.set_active(True)
|
||||
else:
|
||||
self.indentationAudioUnitLevelsButton.set_active(True)
|
||||
|
||||
self.indentationSpacesPerLevelSpinButton.set_value(
|
||||
prefs.get("indentationSpacesPerLevel", settings.indentationSpacesPerLevel))
|
||||
self.indentationTabWidthSpinButton.set_value(
|
||||
prefs.get("indentationTabWidth", settings.indentationTabWidth))
|
||||
self.indentationAudioBaseFrequencySpinButton.set_value(
|
||||
prefs.get("indentationAudioBaseFrequency", settings.indentationAudioBaseFrequency))
|
||||
self.indentationAudioStepFrequencySpinButton.set_value(
|
||||
prefs.get("indentationAudioStepFrequency", settings.indentationAudioStepFrequency))
|
||||
self.indentationAudioMaxFrequencySpinButton.set_value(
|
||||
prefs.get("indentationAudioMaxFrequency", settings.indentationAudioMaxFrequency))
|
||||
self.indentationAudioDurationSpinButton.set_value(
|
||||
prefs.get("indentationAudioDuration", settings.indentationAudioDuration))
|
||||
self.indentationAudioVolumeSpinButton.set_value(
|
||||
prefs.get("indentationAudioVolume", settings.indentationAudioVolume))
|
||||
|
||||
self._updateIndentationControlsState(mode)
|
||||
|
||||
def _updateIndentationControlsState(self, mode):
|
||||
"""Enable or disable indentation controls based on presentation mode."""
|
||||
speech_enabled = mode in (
|
||||
settings.INDENTATION_PRESENTATION_SPEECH,
|
||||
settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS,
|
||||
)
|
||||
beeps_enabled = mode in (
|
||||
settings.INDENTATION_PRESENTATION_BEEPS,
|
||||
settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS,
|
||||
)
|
||||
|
||||
for widget in (
|
||||
self.indentationSpeechSpacesTabsButton,
|
||||
self.indentationSpeechLevelsButton,
|
||||
self.indentationSpeechColumnsButton,
|
||||
):
|
||||
widget.set_sensitive(speech_enabled)
|
||||
|
||||
for widget in (
|
||||
self.indentationAudioUnitLevelsButton,
|
||||
self.indentationAudioUnitColumnsButton,
|
||||
self.indentationAudioBaseFrequencySpinButton,
|
||||
self.indentationAudioStepFrequencySpinButton,
|
||||
self.indentationAudioMaxFrequencySpinButton,
|
||||
self.indentationAudioDurationSpinButton,
|
||||
self.indentationAudioVolumeSpinButton,
|
||||
):
|
||||
widget.set_sensitive(beeps_enabled)
|
||||
|
||||
def _updateProviderControls(self, provider):
|
||||
"""Update visibility/sensitivity of provider-specific controls."""
|
||||
@@ -3839,6 +3954,92 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
||||
|
||||
self.__initProfileCombo()
|
||||
|
||||
# Indentation signal handlers
|
||||
|
||||
def indentationPresentationModeToggled(self, widget):
|
||||
"""Indentation presentation mode radio toggled handler."""
|
||||
if not widget.get_active():
|
||||
return
|
||||
|
||||
mapping = {
|
||||
"indentationPresentationOffButton": settings.INDENTATION_PRESENTATION_OFF,
|
||||
"indentationPresentationSpeechButton": settings.INDENTATION_PRESENTATION_SPEECH,
|
||||
"indentationPresentationBeepsButton": settings.INDENTATION_PRESENTATION_BEEPS,
|
||||
"indentationPresentationSpeechAndBeepsButton": settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS,
|
||||
}
|
||||
widget_name = Gtk.Buildable.get_name(widget)
|
||||
mode = mapping.get(widget_name)
|
||||
if mode is None:
|
||||
return
|
||||
|
||||
self.prefsDict["indentationPresentationMode"] = mode
|
||||
self.prefsDict["enableSpeechIndentation"] = mode in (
|
||||
settings.INDENTATION_PRESENTATION_SPEECH,
|
||||
settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS,
|
||||
)
|
||||
self._updateIndentationControlsState(mode)
|
||||
|
||||
def indentationChangeModeToggled(self, widget):
|
||||
"""Indentation change mode radio toggled handler."""
|
||||
if not widget.get_active():
|
||||
return
|
||||
|
||||
widget_name = Gtk.Buildable.get_name(widget)
|
||||
if widget_name == "indentationChangeAlwaysButton":
|
||||
self.prefsDict["speakIndentationOnlyIfChanged"] = False
|
||||
self.prefsDict["indentationChangeMode"] = settings.INDENTATION_CHANGE_ALWAYS
|
||||
elif widget_name == "indentationChangeAnyButton":
|
||||
self.prefsDict["speakIndentationOnlyIfChanged"] = True
|
||||
self.prefsDict["indentationChangeMode"] = settings.INDENTATION_CHANGE_ANY
|
||||
else:
|
||||
self.prefsDict["speakIndentationOnlyIfChanged"] = True
|
||||
self.prefsDict["indentationChangeMode"] = settings.INDENTATION_CHANGE_LEVEL
|
||||
|
||||
def indentationSpeechStyleToggled(self, widget):
|
||||
"""Indentation speech style radio toggled handler."""
|
||||
if not widget.get_active():
|
||||
return
|
||||
|
||||
mapping = {
|
||||
"indentationSpeechSpacesTabsButton": settings.INDENTATION_SPEECH_STYLE_SPACES_TABS,
|
||||
"indentationSpeechLevelsButton": settings.INDENTATION_SPEECH_STYLE_LEVELS,
|
||||
"indentationSpeechColumnsButton": settings.INDENTATION_SPEECH_STYLE_COLUMNS,
|
||||
}
|
||||
widget_name = Gtk.Buildable.get_name(widget)
|
||||
style = mapping.get(widget_name)
|
||||
if style is None:
|
||||
return
|
||||
|
||||
self.prefsDict["indentationSpeechStyle"] = style
|
||||
|
||||
def indentationAudioUnitToggled(self, widget):
|
||||
"""Indentation audio unit radio toggled handler."""
|
||||
if not widget.get_active():
|
||||
return
|
||||
|
||||
if Gtk.Buildable.get_name(widget) == "indentationAudioUnitColumnsButton":
|
||||
self.prefsDict["indentationAudioUnit"] = settings.INDENTATION_UNIT_COLUMNS
|
||||
else:
|
||||
self.prefsDict["indentationAudioUnit"] = settings.INDENTATION_UNIT_LEVELS
|
||||
|
||||
def indentationSpinValueChanged(self, widget):
|
||||
"""Indentation spin button value changed handler."""
|
||||
settingName = Gtk.Buildable.get_name(widget)
|
||||
if settingName.endswith("SpinButton"):
|
||||
settingName = settingName[:-10]
|
||||
|
||||
int_settings = {
|
||||
"indentationSpacesPerLevel",
|
||||
"indentationTabWidth",
|
||||
"indentationAudioBaseFrequency",
|
||||
"indentationAudioStepFrequency",
|
||||
"indentationAudioMaxFrequency",
|
||||
}
|
||||
if settingName in int_settings:
|
||||
self.prefsDict[settingName] = int(widget.get_value())
|
||||
else:
|
||||
self.prefsDict[settingName] = float(widget.get_value())
|
||||
|
||||
# AI Assistant signal handlers
|
||||
|
||||
def enableAIToggled(self, widget):
|
||||
|
||||
@@ -44,6 +44,12 @@ from typing import TYPE_CHECKING, Optional
|
||||
import gi
|
||||
gi.require_version("Atspi", "2.0")
|
||||
from gi.repository import Atspi
|
||||
try:
|
||||
gi.require_version("Wnck", "3.0")
|
||||
from gi.repository import Wnck
|
||||
_wnck_available = True
|
||||
except Exception:
|
||||
_wnck_available = False
|
||||
|
||||
from . import debug
|
||||
from . import focus_manager
|
||||
@@ -53,6 +59,7 @@ from . import settings
|
||||
from . import cthulhu_state
|
||||
from .ax_object import AXObject
|
||||
from .ax_utilities import AXUtilities
|
||||
from .ax_utilities_application import AXUtilitiesApplication
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import keybindings
|
||||
@@ -69,6 +76,35 @@ class InputEventManager:
|
||||
self._grabbed_bindings: dict[int, keybindings.KeyBinding] = {}
|
||||
self._paused: bool = False
|
||||
|
||||
def _active_window_has_accessible_app(self) -> Optional[bool]:
|
||||
"""Returns True if the WM active window maps to an AT-SPI app."""
|
||||
|
||||
if not _wnck_available:
|
||||
return None
|
||||
|
||||
screen = Wnck.Screen.get_default()
|
||||
if not screen:
|
||||
return None
|
||||
|
||||
try:
|
||||
screen.force_update()
|
||||
active_window = screen.get_active_window()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if not active_window:
|
||||
return None
|
||||
|
||||
try:
|
||||
pid = active_window.get_pid()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if pid <= 0:
|
||||
return None
|
||||
|
||||
return AXUtilitiesApplication.get_application_with_pid(pid) is not None
|
||||
|
||||
def start_key_watcher(self) -> None:
|
||||
"""Starts the watcher for keyboard input events."""
|
||||
|
||||
@@ -281,6 +317,12 @@ class InputEventManager:
|
||||
|
||||
manager = focus_manager.get_manager()
|
||||
if pressed:
|
||||
has_accessible_app = self._active_window_has_accessible_app()
|
||||
if has_accessible_app is False and manager.get_active_window() is not None:
|
||||
msg = "INPUT EVENT MANAGER: Active window has no AT-SPI app. Clearing active window."
|
||||
debug.print_message(debug.LEVEL_INFO, msg, True)
|
||||
manager.set_active_window(None, notify_script=True)
|
||||
|
||||
window = manager.get_active_window()
|
||||
if not AXUtilities.can_be_active_window(window):
|
||||
new_window = AXUtilities.find_active_window()
|
||||
|
||||
@@ -2812,6 +2812,16 @@ def tabsCount(count):
|
||||
# tab characters in a string.
|
||||
return ngettext("%d tab", "%d tabs", count) % count
|
||||
|
||||
def indentationLevelCount(count):
|
||||
# Translators: This message is presented to inform the user of the
|
||||
# indentation level for a line of text.
|
||||
return ngettext("indentation level %d", "indentation level %d", count) % count
|
||||
|
||||
def indentationColumnCount(count):
|
||||
# Translators: This message is presented to inform the user of the number
|
||||
# of indentation columns for a line of text.
|
||||
return ngettext("indentation %d column", "indentation %d columns", count) % count
|
||||
|
||||
def tableCount(count, onlyIfFound=True):
|
||||
if not count and onlyIfFound:
|
||||
return ""
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
"""IndentationAudio plugin for Cthulhu - Provides audio feedback for indentation level changes."""
|
||||
|
||||
import logging
|
||||
import math
|
||||
import re
|
||||
from gi.repository import GLib
|
||||
|
||||
from cthulhu.plugin import Plugin, cthulhu_hookimpl
|
||||
from cthulhu import debug
|
||||
from cthulhu import settings
|
||||
from cthulhu import settings_manager
|
||||
from cthulhu.ax_object import AXObject
|
||||
from cthulhu.ax_text import AXText
|
||||
|
||||
@@ -28,6 +31,7 @@ except ImportError as e:
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Sound import failed: {e}", True)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
_settingsManager = settings_manager.getManager()
|
||||
|
||||
|
||||
class IndentationAudio(Plugin):
|
||||
@@ -37,14 +41,18 @@ class IndentationAudio(Plugin):
|
||||
"""Initialize the IndentationAudio plugin."""
|
||||
super().__init__(*args, **kwargs)
|
||||
self._enabled = True # Start enabled by default
|
||||
self._last_indentation_level = {} # Track per-object indentation
|
||||
self._last_indentation_data = {} # Track per-object indentation
|
||||
self._event_listener_id = None
|
||||
self._kb_binding = None
|
||||
# Audio settings
|
||||
self._base_frequency = 200 # Base frequency in Hz
|
||||
self._frequency_step = 80 # Hz per indentation level
|
||||
self._tone_duration = 0.15 # Seconds
|
||||
self._max_frequency = 1200 # Cap frequency to avoid harsh sounds
|
||||
self._base_frequency = settings.indentationAudioBaseFrequency
|
||||
self._frequency_step = settings.indentationAudioStepFrequency
|
||||
self._tone_duration = settings.indentationAudioDuration
|
||||
self._max_frequency = settings.indentationAudioMaxFrequency
|
||||
self._volume_multiplier = settings.indentationAudioVolume
|
||||
self._alerts_suspended = False
|
||||
self._saved_presentation_mode = None
|
||||
self._saved_speech_indentation = None
|
||||
|
||||
self._activated = False
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin initialized", True)
|
||||
@@ -98,7 +106,7 @@ class IndentationAudio(Plugin):
|
||||
self._disconnect_from_events()
|
||||
|
||||
# Clear tracking data
|
||||
self._last_indentation_level.clear()
|
||||
self._last_indentation_data.clear()
|
||||
|
||||
self._activated = False
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin deactivated successfully", True)
|
||||
@@ -117,7 +125,7 @@ class IndentationAudio(Plugin):
|
||||
|
||||
# Register Cthulhu+I keybinding using the plugin's registerGestureByString method
|
||||
gesture_string = "kb:cthulhu+i"
|
||||
description = "Toggle indentation audio feedback"
|
||||
description = "Toggle indentation alerts"
|
||||
|
||||
self._kb_binding = self.registerGestureByString(
|
||||
self._toggle_indentation_audio,
|
||||
@@ -169,7 +177,7 @@ class IndentationAudio(Plugin):
|
||||
def _on_caret_moved(self, event):
|
||||
"""Handle caret movement events."""
|
||||
try:
|
||||
if not self._enabled:
|
||||
if not self._beeps_enabled():
|
||||
return
|
||||
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Caret moved in {event.source}", True)
|
||||
@@ -292,18 +300,41 @@ class IndentationAudio(Plugin):
|
||||
def _toggle_indentation_audio(self, script, inputEvent=None):
|
||||
"""Toggle the indentation audio feedback on/off."""
|
||||
try:
|
||||
self._enabled = not self._enabled
|
||||
state = "enabled" if self._enabled else "disabled"
|
||||
|
||||
current_mode = _settingsManager.getSetting('indentationPresentationMode') \
|
||||
or settings.indentationPresentationMode
|
||||
current_speech = _settingsManager.getSetting('enableSpeechIndentation')
|
||||
|
||||
if self._alerts_suspended:
|
||||
new_mode = self._saved_presentation_mode
|
||||
if new_mode is None:
|
||||
new_mode = current_mode
|
||||
_settingsManager.setSetting('indentationPresentationMode', new_mode)
|
||||
if self._saved_speech_indentation is None:
|
||||
self._sync_speech_setting(new_mode)
|
||||
else:
|
||||
_settingsManager.setSetting('enableSpeechIndentation', self._saved_speech_indentation)
|
||||
self._alerts_suspended = False
|
||||
state = "enabled"
|
||||
else:
|
||||
self._saved_presentation_mode = current_mode
|
||||
self._saved_speech_indentation = current_speech
|
||||
_settingsManager.setSetting(
|
||||
'indentationPresentationMode',
|
||||
settings.INDENTATION_PRESENTATION_OFF,
|
||||
)
|
||||
_settingsManager.setSetting('enableSpeechIndentation', False)
|
||||
self._alerts_suspended = True
|
||||
state = "disabled"
|
||||
|
||||
# Announce the state change
|
||||
message = f"Indentation audio {state}"
|
||||
message = f"Indentation alerts {state}"
|
||||
if hasattr(script, 'speakMessage'):
|
||||
script.speakMessage(message)
|
||||
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Toggled to {state}", True)
|
||||
|
||||
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Alerts toggled to {state}", True)
|
||||
|
||||
# Test the indentation detection on current line when enabled
|
||||
if self._enabled and script:
|
||||
if state == "enabled" and script:
|
||||
try:
|
||||
# Try to get current focus object and line text
|
||||
from cthulhu import cthulhu_state
|
||||
@@ -324,62 +355,107 @@ class IndentationAudio(Plugin):
|
||||
logger.error(f"IndentationAudio: Error toggling state: {e}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Error toggling: {e}", True)
|
||||
return False
|
||||
|
||||
def _calculate_indentation_level(self, line_text):
|
||||
"""Calculate the indentation level of a line."""
|
||||
|
||||
def _sync_speech_setting(self, presentation_mode):
|
||||
enable_speech = presentation_mode in (
|
||||
settings.INDENTATION_PRESENTATION_SPEECH,
|
||||
settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS,
|
||||
)
|
||||
_settingsManager.setSetting('enableSpeechIndentation', enable_speech)
|
||||
|
||||
def _beeps_enabled(self):
|
||||
if not self._enabled:
|
||||
return False
|
||||
presentation_mode = _settingsManager.getSetting('indentationPresentationMode') \
|
||||
or settings.indentationPresentationMode
|
||||
return presentation_mode in (
|
||||
settings.INDENTATION_PRESENTATION_BEEPS,
|
||||
settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _extract_indentation(line_text):
|
||||
if not line_text:
|
||||
return 0
|
||||
|
||||
# Remove non-breaking spaces and convert to regular spaces
|
||||
return ""
|
||||
|
||||
line = line_text.replace("\u00a0", " ")
|
||||
|
||||
# Find the first non-whitespace character
|
||||
match = re.search(r"[^ \t]", line)
|
||||
if not match:
|
||||
return 0 # Empty or whitespace-only line
|
||||
|
||||
indent_text = line[:match.start()]
|
||||
|
||||
# Calculate indentation level (4 spaces = 1 level, 1 tab = 1 level)
|
||||
level = 0
|
||||
for char in indent_text:
|
||||
if char == '\t':
|
||||
level += 1
|
||||
elif char == ' ':
|
||||
level += 0.25 # 4 spaces = 1 level
|
||||
|
||||
return int(level)
|
||||
return line
|
||||
|
||||
return line[:match.start()]
|
||||
|
||||
@staticmethod
|
||||
def _count_columns(indentation, tab_width):
|
||||
columns = 0
|
||||
tab_width = max(1, tab_width)
|
||||
for char in indentation:
|
||||
if char == "\t":
|
||||
columns += tab_width - (columns % tab_width)
|
||||
else:
|
||||
columns += 1
|
||||
|
||||
return columns
|
||||
|
||||
@staticmethod
|
||||
def _count_levels(columns, spaces_per_level):
|
||||
spaces_per_level = max(1, spaces_per_level)
|
||||
if columns <= 0:
|
||||
return 0
|
||||
|
||||
return int(math.ceil(columns / spaces_per_level))
|
||||
|
||||
def _get_indentation_data(self, line_text):
|
||||
indentation = self._extract_indentation(line_text)
|
||||
tab_width = _settingsManager.getSetting('indentationTabWidth') \
|
||||
or settings.indentationTabWidth
|
||||
spaces_per_level = _settingsManager.getSetting('indentationSpacesPerLevel') \
|
||||
or settings.indentationSpacesPerLevel
|
||||
columns = self._count_columns(indentation, tab_width)
|
||||
levels = self._count_levels(columns, spaces_per_level)
|
||||
return indentation, columns, levels
|
||||
|
||||
def _generate_indentation_tone(self, new_level, old_level):
|
||||
"""Generate an audio tone for indentation level change."""
|
||||
if not self._enabled:
|
||||
def _generate_indentation_tone(self, new_units, old_units):
|
||||
"""Generate an audio tone for indentation change."""
|
||||
if not self._beeps_enabled():
|
||||
return
|
||||
|
||||
base_frequency = _settingsManager.getSetting('indentationAudioBaseFrequency') \
|
||||
or self._base_frequency
|
||||
frequency_step = _settingsManager.getSetting('indentationAudioStepFrequency') \
|
||||
or self._frequency_step
|
||||
max_frequency = _settingsManager.getSetting('indentationAudioMaxFrequency') \
|
||||
or self._max_frequency
|
||||
tone_duration = _settingsManager.getSetting('indentationAudioDuration') \
|
||||
or self._tone_duration
|
||||
volume_multiplier = _settingsManager.getSetting('indentationAudioVolume') \
|
||||
or self._volume_multiplier
|
||||
|
||||
# Calculate frequency based on new indentation level
|
||||
# Calculate frequency based on new indentation units
|
||||
base_frequency = min(
|
||||
self._base_frequency + (new_level * self._frequency_step),
|
||||
self._max_frequency
|
||||
base_frequency + (new_units * frequency_step),
|
||||
max_frequency
|
||||
)
|
||||
|
||||
# Add directional audio cues
|
||||
if new_level > old_level:
|
||||
if new_units > old_units:
|
||||
# Indentation increased - higher pitch
|
||||
frequency = base_frequency + 50
|
||||
elif new_level < old_level:
|
||||
# Indentation decreased - lower pitch
|
||||
elif new_units < old_units:
|
||||
# Indentation decreased - lower pitch
|
||||
frequency = max(base_frequency - 50, 100)
|
||||
else:
|
||||
# Same level (shouldn't happen but just in case)
|
||||
frequency = base_frequency
|
||||
|
||||
try:
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: About to generate tone for level {new_level} (freq: {frequency}Hz)", True)
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: About to generate tone for units {new_units} (freq: {frequency}Hz)", True)
|
||||
|
||||
if not SOUND_AVAILABLE or not self._player:
|
||||
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Sound player not available, using fallback", True)
|
||||
# Fallback to ASCII bell
|
||||
if new_level > 0:
|
||||
beeps = min(new_level, 5)
|
||||
if new_units > 0:
|
||||
beeps = min(new_units, 5)
|
||||
for i in range(beeps):
|
||||
print("\a", end="", flush=True)
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Sent {beeps} ASCII bell beeps", True)
|
||||
@@ -388,8 +464,7 @@ class IndentationAudio(Plugin):
|
||||
# Use Cthulhu's proper sound system
|
||||
try:
|
||||
# Create a tone based on indentation level
|
||||
duration = self._tone_duration
|
||||
volume_multiplier = 0.7
|
||||
duration = tone_duration
|
||||
|
||||
tone = Tone(
|
||||
duration=duration,
|
||||
@@ -400,13 +475,13 @@ class IndentationAudio(Plugin):
|
||||
|
||||
# Play the tone
|
||||
self._player.play(tone, interrupt=False)
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Played Cthulhu tone - Level: {new_level}, Freq: {frequency}Hz", True)
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Played Cthulhu tone - Units: {new_units}, Freq: {frequency}Hz", True)
|
||||
|
||||
except Exception as sound_e:
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Cthulhu sound failed: {sound_e}", True)
|
||||
# Fallback to ASCII bell
|
||||
if new_level > 0:
|
||||
beeps = min(new_level, 5)
|
||||
if new_units > 0:
|
||||
beeps = min(new_units, 5)
|
||||
for i in range(beeps):
|
||||
print("\a", end="", flush=True)
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Used fallback ASCII bell ({beeps} beeps)", True)
|
||||
@@ -420,31 +495,57 @@ class IndentationAudio(Plugin):
|
||||
|
||||
This method is intended to be called by scripts during line navigation.
|
||||
"""
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: check_indentation_change called: enabled={self._enabled}, line='{line_text}'", True)
|
||||
|
||||
if not self._enabled or not line_text:
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: check_indentation_change called: line='{line_text}'", True)
|
||||
|
||||
if not line_text or not self._beeps_enabled():
|
||||
return
|
||||
|
||||
try:
|
||||
# Get object identifier for tracking
|
||||
obj_id = str(obj) if obj else "unknown"
|
||||
|
||||
# Calculate current indentation level
|
||||
current_level = self._calculate_indentation_level(line_text)
|
||||
|
||||
# Get previous level for this object
|
||||
previous_level = self._last_indentation_level.get(obj_id, current_level)
|
||||
|
||||
# Update tracking
|
||||
self._last_indentation_level[obj_id] = current_level
|
||||
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: Levels - previous: {previous_level}, current: {current_level}", True)
|
||||
|
||||
# Calculate current indentation data
|
||||
indentation, columns, levels = self._get_indentation_data(line_text)
|
||||
audio_unit = _settingsManager.getSetting('indentationAudioUnit') \
|
||||
or settings.indentationAudioUnit
|
||||
current_units = columns if audio_unit == settings.INDENTATION_UNIT_COLUMNS else levels
|
||||
|
||||
previous_data = self._last_indentation_data.get(obj_id)
|
||||
previous_units = previous_data.get("units") if previous_data else current_units
|
||||
|
||||
self._last_indentation_data[obj_id] = {
|
||||
"signature": indentation,
|
||||
"levels": levels,
|
||||
"columns": columns,
|
||||
"units": current_units,
|
||||
}
|
||||
|
||||
change_mode = _settingsManager.getSetting('indentationChangeMode') \
|
||||
or settings.indentationChangeMode
|
||||
only_if_changed = _settingsManager.getSetting('speakIndentationOnlyIfChanged')
|
||||
|
||||
if not only_if_changed:
|
||||
changed = True
|
||||
elif previous_data is None:
|
||||
changed = True
|
||||
elif change_mode == settings.INDENTATION_CHANGE_ANY:
|
||||
changed = previous_data.get("signature") != indentation
|
||||
elif change_mode == settings.INDENTATION_CHANGE_LEVEL:
|
||||
changed = previous_data.get("levels") != levels
|
||||
else:
|
||||
changed = True
|
||||
|
||||
debug.printMessage(
|
||||
debug.LEVEL_INFO,
|
||||
f"IndentationAudio: Units - previous: {previous_units}, current: {current_units}, changed={changed}",
|
||||
True,
|
||||
)
|
||||
|
||||
# Play audio cue if indentation changed
|
||||
if current_level != previous_level:
|
||||
self._generate_indentation_tone(current_level, previous_level)
|
||||
if changed:
|
||||
self._generate_indentation_tone(current_units, previous_units)
|
||||
|
||||
debug_msg = f"IndentationAudio: Indentation changed from {previous_level} to {current_level}"
|
||||
debug_msg = f"IndentationAudio: Indentation units changed from {previous_units} to {current_units}"
|
||||
debug.printMessage(debug.LEVEL_INFO, debug_msg, True)
|
||||
|
||||
except Exception as e:
|
||||
@@ -469,7 +570,7 @@ class IndentationAudio(Plugin):
|
||||
self._monkey_patch_script_methods()
|
||||
|
||||
# Clear tracking data for new context
|
||||
self._last_indentation_level.clear()
|
||||
self._last_indentation_data.clear()
|
||||
|
||||
logger.info("IndentationAudio: Handled script change")
|
||||
|
||||
|
||||
+177
-10
@@ -118,6 +118,7 @@ class Utilities:
|
||||
|
||||
self._script = script
|
||||
self._clipboardHandlerId = None
|
||||
self._lastIndentationData = {}
|
||||
self._selectedMenuBarMenu = {}
|
||||
|
||||
#########################################################################
|
||||
@@ -3126,27 +3127,193 @@ class Utilities:
|
||||
|
||||
return string
|
||||
|
||||
def indentationDescription(self, line):
|
||||
if _settingsManager.getSetting('onlySpeakDisplayedText') \
|
||||
or not _settingsManager.getSetting('enableSpeechIndentation'):
|
||||
def _get_indentation_key(self, obj):
|
||||
if obj is None:
|
||||
return "global"
|
||||
|
||||
try:
|
||||
return id(obj)
|
||||
except Exception:
|
||||
return str(obj)
|
||||
|
||||
@staticmethod
|
||||
def _extract_indentation(line):
|
||||
if not line:
|
||||
return ""
|
||||
|
||||
line = line.replace("\u00a0", " ")
|
||||
end = re.search("[^ \t]", line)
|
||||
if end:
|
||||
line = line[:end.start()]
|
||||
return line[:end.start()]
|
||||
|
||||
result = ""
|
||||
spaces = [m.span() for m in re.finditer(" +", line)]
|
||||
tabs = [m.span() for m in re.finditer("\t+", line)]
|
||||
return line
|
||||
|
||||
@staticmethod
|
||||
def _get_indentation_segments(indentation):
|
||||
if not indentation:
|
||||
return []
|
||||
|
||||
spaces = [m.span() for m in re.finditer(" +", indentation)]
|
||||
tabs = [m.span() for m in re.finditer("\t+", indentation)]
|
||||
spans = sorted(spaces + tabs)
|
||||
segments = []
|
||||
for (start, end) in spans:
|
||||
if (start, end) in spaces:
|
||||
result += f"{messages.spacesCount(end - start)} "
|
||||
segments.append(("spaces", end - start))
|
||||
else:
|
||||
result += f"{messages.tabsCount(end - start)} "
|
||||
segments.append(("tabs", end - start))
|
||||
|
||||
return result
|
||||
return segments
|
||||
|
||||
@staticmethod
|
||||
def _get_indentation_columns(indentation, tab_width):
|
||||
columns = 0
|
||||
tab_width = max(1, tab_width)
|
||||
for char in indentation:
|
||||
if char == "\t":
|
||||
columns += tab_width - (columns % tab_width)
|
||||
else:
|
||||
columns += 1
|
||||
|
||||
return columns
|
||||
|
||||
@staticmethod
|
||||
def _get_indentation_levels(columns, spaces_per_level):
|
||||
spaces_per_level = max(1, spaces_per_level)
|
||||
if columns <= 0:
|
||||
return 0
|
||||
|
||||
return int(math.ceil(columns / spaces_per_level))
|
||||
|
||||
def _get_indentation_data(self, line):
|
||||
indentation = self._extract_indentation(line)
|
||||
tab_width = _settingsManager.getSetting('indentationTabWidth') \
|
||||
or settings.indentationTabWidth
|
||||
spaces_per_level = _settingsManager.getSetting('indentationSpacesPerLevel') \
|
||||
or settings.indentationSpacesPerLevel
|
||||
columns = self._get_indentation_columns(indentation, tab_width)
|
||||
levels = self._get_indentation_levels(columns, spaces_per_level)
|
||||
segments = self._get_indentation_segments(indentation)
|
||||
return {
|
||||
"indentation": indentation,
|
||||
"segments": segments,
|
||||
"columns": columns,
|
||||
"levels": levels,
|
||||
}
|
||||
|
||||
def _remember_indentation(self, obj, data):
|
||||
key = self._get_indentation_key(obj)
|
||||
self._lastIndentationData[key] = {
|
||||
"signature": data["indentation"],
|
||||
"levels": data["levels"],
|
||||
"columns": data["columns"],
|
||||
}
|
||||
|
||||
def _indentation_has_changed(self, obj, data):
|
||||
key = self._get_indentation_key(obj)
|
||||
previous = self._lastIndentationData.get(key)
|
||||
self._remember_indentation(obj, data)
|
||||
if previous is None:
|
||||
return True
|
||||
|
||||
change_mode = _settingsManager.getSetting('indentationChangeMode') \
|
||||
or settings.indentationChangeMode
|
||||
if change_mode == settings.INDENTATION_CHANGE_ANY:
|
||||
return previous.get("signature") != data["indentation"]
|
||||
if change_mode == settings.INDENTATION_CHANGE_LEVEL:
|
||||
return previous.get("levels") != data["levels"]
|
||||
|
||||
return True
|
||||
|
||||
def _indentation_speech_enabled(self):
|
||||
if _settingsManager.getSetting('onlySpeakDisplayedText'):
|
||||
return False
|
||||
if not _settingsManager.getSetting('enableSpeechIndentation'):
|
||||
return False
|
||||
|
||||
presentation_mode = _settingsManager.getSetting('indentationPresentationMode') \
|
||||
or settings.indentationPresentationMode
|
||||
return presentation_mode in (
|
||||
settings.INDENTATION_PRESENTATION_SPEECH,
|
||||
settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS,
|
||||
)
|
||||
|
||||
def _format_indentation_description(self, data):
|
||||
style = _settingsManager.getSetting('indentationSpeechStyle') \
|
||||
or settings.indentationSpeechStyle
|
||||
if style == settings.INDENTATION_SPEECH_STYLE_LEVELS:
|
||||
return messages.indentationLevelCount(data["levels"])
|
||||
if style == settings.INDENTATION_SPEECH_STYLE_COLUMNS:
|
||||
return messages.indentationColumnCount(data["columns"])
|
||||
|
||||
result = ""
|
||||
for kind, count in data["segments"]:
|
||||
if kind == "spaces":
|
||||
result += f"{messages.spacesCount(count)} "
|
||||
else:
|
||||
result += f"{messages.tabsCount(count)} "
|
||||
|
||||
if not result:
|
||||
return messages.spacesCount(0)
|
||||
|
||||
return result.strip()
|
||||
|
||||
def get_indentation_presentation(self, line, obj=None):
|
||||
data = self._get_indentation_data(line)
|
||||
has_indentation = bool(data["indentation"])
|
||||
presentation_mode = _settingsManager.getSetting('indentationPresentationMode') \
|
||||
or settings.indentationPresentationMode
|
||||
only_if_changed = _settingsManager.getSetting('speakIndentationOnlyIfChanged')
|
||||
change_mode = _settingsManager.getSetting('indentationChangeMode') \
|
||||
or settings.indentationChangeMode
|
||||
indent_debug = data["indentation"].replace("\t", "\\t").replace(" ", ".")
|
||||
|
||||
if not self._indentation_speech_enabled():
|
||||
self._remember_indentation(obj, data)
|
||||
msg = (
|
||||
f"INDENTATION: speech disabled mode={presentation_mode} "
|
||||
f"onlyIfChanged={only_if_changed} changeMode={change_mode} "
|
||||
f"levels={data['levels']} columns={data['columns']} indent='{indent_debug}'"
|
||||
)
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
return "", has_indentation
|
||||
|
||||
if only_if_changed:
|
||||
changed = self._indentation_has_changed(obj, data)
|
||||
else:
|
||||
changed = True
|
||||
self._remember_indentation(obj, data)
|
||||
|
||||
if not changed:
|
||||
msg = (
|
||||
f"INDENTATION: unchanged mode={presentation_mode} "
|
||||
f"onlyIfChanged={only_if_changed} changeMode={change_mode} "
|
||||
f"levels={data['levels']} columns={data['columns']} indent='{indent_debug}'"
|
||||
)
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
return "", has_indentation
|
||||
|
||||
description = self._format_indentation_description(data)
|
||||
msg = (
|
||||
f"INDENTATION: speaking '{description}' mode={presentation_mode} "
|
||||
f"onlyIfChanged={only_if_changed} changeMode={change_mode} "
|
||||
f"levels={data['levels']} columns={data['columns']} indent='{indent_debug}'"
|
||||
)
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
return description, has_indentation
|
||||
|
||||
def should_strip_indentation(self, line):
|
||||
presentation_mode = _settingsManager.getSetting('indentationPresentationMode') \
|
||||
or settings.indentationPresentationMode
|
||||
if presentation_mode == settings.INDENTATION_PRESENTATION_OFF:
|
||||
return False
|
||||
|
||||
data = self._get_indentation_data(line)
|
||||
return bool(data["indentation"])
|
||||
|
||||
def indentationDescription(self, line, obj=None):
|
||||
description, _has_indentation = self.get_indentation_presentation(line, obj=obj)
|
||||
return description
|
||||
|
||||
@staticmethod
|
||||
def absoluteMouseCoordinates():
|
||||
|
||||
@@ -2354,9 +2354,11 @@ class Script(script.Script):
|
||||
|
||||
[line, caretOffset, startOffset] = self.getTextLineAtCaret(obj)
|
||||
if len(line) and line != "\n":
|
||||
indentationDescription = self.utilities.indentationDescription(line)
|
||||
indentationDescription, hasIndentation = \
|
||||
self.utilities.get_indentation_presentation(line, obj=obj)
|
||||
if indentationDescription:
|
||||
self.speakMessage(indentationDescription)
|
||||
stripIndentation = hasIndentation and self.utilities.should_strip_indentation(line)
|
||||
|
||||
endOffset = startOffset + len(line)
|
||||
cthulhu.emitRegionChanged(obj, startOffset, endOffset, cthulhu.CARET_TRACKING)
|
||||
@@ -2376,7 +2378,7 @@ class Script(script.Script):
|
||||
|
||||
# Some synthesizers will verbalize the whitespace, so if we've already
|
||||
# described it, prevent double-presentation by stripping it off.
|
||||
if not utterance and indentationDescription:
|
||||
if not utterance and stripIndentation:
|
||||
string = string.lstrip()
|
||||
|
||||
result = [string]
|
||||
@@ -2406,13 +2408,16 @@ class Script(script.Script):
|
||||
return
|
||||
|
||||
if len(phrase) > 1 or phrase.isalnum():
|
||||
result = self.utilities.indentationDescription(phrase)
|
||||
if result:
|
||||
self.speakMessage(result)
|
||||
indentationDescription, hasIndentation = \
|
||||
self.utilities.get_indentation_presentation(phrase, obj=obj)
|
||||
if indentationDescription:
|
||||
self.speakMessage(indentationDescription)
|
||||
|
||||
cthulhu.emitRegionChanged(obj, startOffset, endOffset, cthulhu.CARET_TRACKING)
|
||||
|
||||
voice = self.speechGenerator.voice(obj=obj, string=phrase)
|
||||
if hasIndentation and self.utilities.should_strip_indentation(phrase):
|
||||
phrase = phrase.lstrip()
|
||||
phrase = self.utilities.adjustForRepeats(phrase)
|
||||
if self.utilities.shouldVerbalizeAllPunctuation(obj):
|
||||
phrase = self.utilities.verbalizeAllPunctuation(phrase)
|
||||
|
||||
+42
-2
@@ -48,6 +48,18 @@ userCustomizableSettings = [
|
||||
"readFullRowInDocumentTable",
|
||||
"readFullRowInSpreadSheet",
|
||||
"enableSpeechIndentation",
|
||||
"indentationPresentationMode",
|
||||
"indentationChangeMode",
|
||||
"speakIndentationOnlyIfChanged",
|
||||
"indentationSpeechStyle",
|
||||
"indentationAudioUnit",
|
||||
"indentationSpacesPerLevel",
|
||||
"indentationTabWidth",
|
||||
"indentationAudioBaseFrequency",
|
||||
"indentationAudioStepFrequency",
|
||||
"indentationAudioMaxFrequency",
|
||||
"indentationAudioDuration",
|
||||
"indentationAudioVolume",
|
||||
"enableEchoByCharacter",
|
||||
"enableEchoByWord",
|
||||
"enableEchoBySentence",
|
||||
@@ -218,6 +230,22 @@ AI_SCREENSHOT_QUALITY_LOW = "low"
|
||||
AI_SCREENSHOT_QUALITY_MEDIUM = "medium"
|
||||
AI_SCREENSHOT_QUALITY_HIGH = "high"
|
||||
|
||||
INDENTATION_PRESENTATION_OFF = "off"
|
||||
INDENTATION_PRESENTATION_SPEECH = "speech"
|
||||
INDENTATION_PRESENTATION_BEEPS = "beeps"
|
||||
INDENTATION_PRESENTATION_SPEECH_AND_BEEPS = "speech_and_beeps"
|
||||
|
||||
INDENTATION_CHANGE_ALWAYS = "always"
|
||||
INDENTATION_CHANGE_ANY = "any"
|
||||
INDENTATION_CHANGE_LEVEL = "level"
|
||||
|
||||
INDENTATION_SPEECH_STYLE_SPACES_TABS = "spaces_tabs"
|
||||
INDENTATION_SPEECH_STYLE_LEVELS = "levels"
|
||||
INDENTATION_SPEECH_STYLE_COLUMNS = "columns"
|
||||
|
||||
INDENTATION_UNIT_LEVELS = "levels"
|
||||
INDENTATION_UNIT_COLUMNS = "columns"
|
||||
|
||||
DEFAULT_VOICE = "default"
|
||||
UPPERCASE_VOICE = "uppercase"
|
||||
HYPERLINK_VOICE = "hyperlink"
|
||||
@@ -252,7 +280,19 @@ silenceSpeech = False
|
||||
enableTutorialMessages = False
|
||||
enableMnemonicSpeaking = False
|
||||
enablePositionSpeaking = False
|
||||
enableSpeechIndentation = False
|
||||
enableSpeechIndentation = True
|
||||
indentationPresentationMode = INDENTATION_PRESENTATION_SPEECH_AND_BEEPS
|
||||
indentationChangeMode = INDENTATION_CHANGE_ANY
|
||||
speakIndentationOnlyIfChanged = True
|
||||
indentationSpeechStyle = INDENTATION_SPEECH_STYLE_COLUMNS
|
||||
indentationAudioUnit = INDENTATION_UNIT_COLUMNS
|
||||
indentationSpacesPerLevel = 4
|
||||
indentationTabWidth = 4
|
||||
indentationAudioBaseFrequency = 200
|
||||
indentationAudioStepFrequency = 30
|
||||
indentationAudioMaxFrequency = 2700
|
||||
indentationAudioDuration = 0.15
|
||||
indentationAudioVolume = 0.7
|
||||
onlySpeakDisplayedText = False
|
||||
presentToolTips = False
|
||||
speakBlankLines = True
|
||||
@@ -449,7 +489,7 @@ presentChatRoomLast = False
|
||||
presentLiveRegionFromInactiveTab = False
|
||||
|
||||
# Plugins
|
||||
activePlugins = ['AIAssistant', 'DisplayVersion', 'OCR', 'PluginManager', 'HelloCthulhu', 'ByeCthulhu']
|
||||
activePlugins = ['AIAssistant', 'DisplayVersion', 'OCR', 'PluginManager', 'HelloCthulhu', 'ByeCthulhu', 'IndentationAudio']
|
||||
|
||||
# AI Assistant settings (disabled by default for opt-in behavior)
|
||||
aiAssistantEnabled = True
|
||||
|
||||
@@ -967,11 +967,29 @@ class SpeechAndVerbosityManager:
|
||||
"""Returns whether speaking of indentation and justification is enabled."""
|
||||
return _settings_manager.getSetting('enableSpeechIndentation')
|
||||
|
||||
@staticmethod
|
||||
def _sync_indentation_presentation_mode(enable_speech):
|
||||
mode = _settings_manager.getSetting('indentationPresentationMode') \
|
||||
or settings.indentationPresentationMode
|
||||
if enable_speech:
|
||||
if mode == settings.INDENTATION_PRESENTATION_OFF:
|
||||
mode = settings.INDENTATION_PRESENTATION_SPEECH
|
||||
elif mode == settings.INDENTATION_PRESENTATION_BEEPS:
|
||||
mode = settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS
|
||||
else:
|
||||
if mode == settings.INDENTATION_PRESENTATION_SPEECH:
|
||||
mode = settings.INDENTATION_PRESENTATION_OFF
|
||||
elif mode == settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS:
|
||||
mode = settings.INDENTATION_PRESENTATION_BEEPS
|
||||
|
||||
_settings_manager.setSetting('indentationPresentationMode', mode)
|
||||
|
||||
@dbus_service.setter
|
||||
def set_speak_indentation_and_justification(self, value: bool) -> bool:
|
||||
"""Sets whether speaking of indentation and justification is enabled."""
|
||||
try:
|
||||
_settings_manager.setSetting('enableSpeechIndentation', value)
|
||||
self._sync_indentation_presentation_mode(value)
|
||||
return True
|
||||
except Exception as e:
|
||||
debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak indentation: {e}", True)
|
||||
@@ -1804,6 +1822,7 @@ class SpeechAndVerbosityManager:
|
||||
|
||||
value = _settings_manager.getSetting('enableSpeechIndentation')
|
||||
_settings_manager.setSetting('enableSpeechIndentation', not value)
|
||||
self._sync_indentation_presentation_mode(not value)
|
||||
if _settings_manager.getSetting('enableSpeechIndentation'):
|
||||
full = messages.INDENTATION_JUSTIFICATION_ON_FULL
|
||||
brief = messages.INDENTATION_JUSTIFICATION_ON_BRIEF
|
||||
|
||||
@@ -193,6 +193,22 @@ class SpeechDBusManager:
|
||||
|
||||
return self._settings_manager.getSetting("enableSpeechIndentation")
|
||||
|
||||
def _sync_indentation_presentation_mode(self, enable_speech):
|
||||
mode = self._settings_manager.getSetting("indentationPresentationMode") \
|
||||
or settings.indentationPresentationMode
|
||||
if enable_speech:
|
||||
if mode == settings.INDENTATION_PRESENTATION_OFF:
|
||||
mode = settings.INDENTATION_PRESENTATION_SPEECH
|
||||
elif mode == settings.INDENTATION_PRESENTATION_BEEPS:
|
||||
mode = settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS
|
||||
else:
|
||||
if mode == settings.INDENTATION_PRESENTATION_SPEECH:
|
||||
mode = settings.INDENTATION_PRESENTATION_OFF
|
||||
elif mode == settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS:
|
||||
mode = settings.INDENTATION_PRESENTATION_BEEPS
|
||||
|
||||
self._settings_manager.setSetting("indentationPresentationMode", mode)
|
||||
|
||||
@dbus_service.setter
|
||||
def set_speak_indentation_and_justification(self, value: bool) -> bool:
|
||||
"""Sets whether speaking of indentation and justification is enabled."""
|
||||
@@ -200,6 +216,7 @@ class SpeechDBusManager:
|
||||
msg = f"SPEECH DBUS MANAGER: Setting speak indentation and justification to {value}."
|
||||
debug.printMessage(debug.LEVEL_INFO, msg, True)
|
||||
self._settings_manager.setSetting("enableSpeechIndentation", value)
|
||||
self._sync_indentation_presentation_mode(value)
|
||||
return True
|
||||
|
||||
@dbus_service.command
|
||||
@@ -512,4 +529,4 @@ class SpeechDBusManager:
|
||||
self._settings_manager.setSetting("enableEchoBySentence", new_sentence)
|
||||
|
||||
if script is not None:
|
||||
script.presentMessage(full, brief)
|
||||
script.presentMessage(full, brief)
|
||||
|
||||
@@ -1685,7 +1685,8 @@ class SpeechGenerator(generator.Generator):
|
||||
return []
|
||||
|
||||
line, caretOffset, startOffset = self._script.getTextLineAtCaret(obj)
|
||||
description = self._script.utilities.indentationDescription(line)
|
||||
description, _hasIndentation = \
|
||||
self._script.utilities.get_indentation_presentation(line, obj=obj)
|
||||
if not description:
|
||||
return []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user