Skip to content

Commit 64f7345

Browse files
committed
initial commit
0 parents  commit 64f7345

22 files changed

Lines changed: 6745 additions & 0 deletions

.gitattributes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Set default behaviour, in case users don't have core.autocrlf set.
2+
* text=auto
3+
4+
# Try to ensure that po files in the repo does not include
5+
# source code line numbers.
6+
# Every person expected to commit po files should change their personal config file as described here:
7+
# https://mail.gnome.org/archives/kupfer-list/2010-June/msg00002.html
8+
*.po filter=cleanpo

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
addon/doc/*.css
2+
addon/doc/en/
3+
*_docHandler.py
4+
*.html
5+
*.ini
6+
*.mo
7+
*.pot
8+
*.pyc
9+
*.nvda-addon
10+
.sconsign.dblite

COPYING.txt

Lines changed: 340 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# VolumeManager NVDA addon
4+
# Authors: Danstiv, Beqa gozalishvili
5+
# Copyright 2019, released under GPL.
6+
7+
import addonHandler
8+
from comtypes import CLSCTX_ALL
9+
from ctypes import POINTER, cast
10+
import globalPluginHandler
11+
import gui
12+
from gui.guiHelper import BoxSizerHelper
13+
import os
14+
from speech import cancelSpeech
15+
import sys
16+
import tones
17+
import ui
18+
import wx
19+
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
20+
from . import pycaw
21+
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
22+
del sys.path[-1]
23+
24+
addonHandler.initTranslation()
25+
26+
class Change_volume_dialog(wx.Dialog):
27+
def __init__(self, parent=None, value=100):
28+
wx.Dialog.__init__(self, parent, title=_('Set volume'))
29+
self.main_sizer=BoxSizerHelper(self, wx.VERTICAL)
30+
self.volume_field=self.main_sizer.addLabeledControl(_('Volume'), wx.SpinCtrl, min=0, max=100, initial=value, style=wx.SP_ARROW_KEYS|wx.TE_PROCESS_ENTER)
31+
self.Bind(wx.EVT_TEXT_ENTER, self.on_enter, self.volume_field)
32+
self.btn_sizer=BoxSizerHelper(self, wx.HORIZONTAL)
33+
self.ok_btn=wx.Button(self, wx.ID_OK, label=_('OK'))
34+
self.btn_sizer.addItem(self.ok_btn)
35+
self.Bind(wx.EVT_BUTTON, self.on_ok, self.ok_btn)
36+
self.cancel_btn=wx.Button(self, wx.ID_CANCEL, label=_('Cancel'))
37+
self.btn_sizer.addItem(self.cancel_btn)
38+
self.Bind(wx.EVT_BUTTON, self.on_cancel, self.cancel_btn)
39+
self.main_sizer.addItem(self.btn_sizer)
40+
self.SetSizer(self.main_sizer.sizer)
41+
self.Bind(wx.EVT_SHOW, self.on_show)
42+
self.Bind(wx.EVT_CLOSE, self.on_close)
43+
def on_show(self, event):
44+
self.volume_field.SetFocus()
45+
self.volume_field.SetSelection(0, -1)
46+
event.Skip()
47+
def on_enter(self, event):
48+
self.set()
49+
self.Close()
50+
def on_ok(self, event):
51+
self.set()
52+
event.Skip()
53+
def on_cancel(self, event):
54+
self.set_all_gestures()
55+
event.Skip()
56+
def on_close(self, event):
57+
self.set_all_gestures()
58+
event.Skip()
59+
def set(self):
60+
self.callback(self.volume_field.GetValue())
61+
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
62+
def __init__(self, *args, **kwargs):
63+
globalPluginHandler.GlobalPlugin.__init__(self, *args, **kwargs)
64+
self.enabled=False
65+
self.app_index=0
66+
devices = AudioUtilities.GetSpeakers()
67+
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
68+
self.master_volume=cast(interface, POINTER(IAudioEndpointVolume))
69+
self.master_volume.SetMasterVolume=self.master_volume.SetMasterVolumeLevelScalar
70+
self.master_volume.GetMasterVolume=self.master_volume.GetMasterVolumeLevelScalar
71+
self.master_volume.name=_('Master volume')
72+
self.current_app=self.master_volume
73+
Change_volume_dialog.callback=self.set_volume
74+
Change_volume_dialog.set_all_gestures=self.set_all_gestures
75+
self.standard_gestures={'kb:nvda+shift+v': 'turn', 'kb:volumeDown': 'volume_changed', 'kb:volumeUp': 'volume_changed'}
76+
self.gestures={'kb:leftArrow': 'move_to_app', 'kb:rightArrow': 'move_to_app', 'kb:upArrow': 'change_volume', 'kb:downArrow': 'change_volume', 'kb:space': 'set_volume', 'kb:m': 'mute_app'}
77+
self.set_standard_gestures()
78+
def script_change_volume(self, gesture):
79+
direction=1 if gesture.logIdentifier.split(':')[-1]=='upArrow' else -1
80+
volume=round(self.current_app.GetMasterVolume(), 2)
81+
if direction == 1:
82+
if volume>=1.0:
83+
tones.beep(500, 300)
84+
return
85+
volume+=0.01
86+
self.current_app.SetMasterVolume(volume, None)
87+
else:
88+
if volume<=0.0:
89+
tones.beep(200, 300)
90+
return
91+
volume-=0.01
92+
self.current_app.SetMasterVolume(volume, None)
93+
ui.message(str(int(round(volume*100, 0)))+'%')
94+
def script_volume_changed(self, gesture):
95+
gesture.send()
96+
cancelSpeech()
97+
ui.message(str(int(round(round(self.master_volume.GetMasterVolume(), 2)*100, 0)))+'%')
98+
def script_move_to_app(self, gesture):
99+
direction=1 if gesture.logIdentifier.split(':')[-1]=='rightArrow' else -1
100+
l=len(self.apps)
101+
i=self.app_index
102+
i=i+1 if direction==1 else i-1
103+
if i<0:
104+
i=l-1
105+
if i>=l:
106+
i=0
107+
self.app_index=i
108+
self.current_app=self.apps[self.app_index]
109+
ui.message(self.current_app.name + ' ' + str(int(round(round(self.current_app.GetMasterVolume(), 2)*100, 0))) + ' %')
110+
def script_set_volume(self, gesture):
111+
self.clearGestureBindings()
112+
gui.mainFrame._popupSettingsDialog(Change_volume_dialog, int(round(round(self.current_app.GetMasterVolume(), 2)*100, 0)))
113+
def set_volume(self, volume):
114+
self.current_app.SetMasterVolume(volume/100.0, None)
115+
self.set_all_gestures()
116+
def script_mute_app(self, gesture):
117+
muteState = self.current_app.GetMute()
118+
if muteState == 0:
119+
self.current_app.SetMute(1, None)
120+
ui.message(_('muted'))
121+
elif muteState == 1:
122+
self.current_app.SetMute(0, None)
123+
ui.message(_('unmuted'))
124+
def script_turn(self, gesture):
125+
self.enabled=not self.enabled
126+
if not self.enabled:
127+
tones.beep(440, 100)
128+
self.set_standard_gestures()
129+
return
130+
all_sessions=AudioUtilities.GetAllSessions()
131+
self.apps=[]
132+
del self.app_index
133+
self.apps.append(self.master_volume)
134+
for session in all_sessions:
135+
if session.Process:
136+
s=session.SimpleAudioVolume
137+
s.name=session.Process.name()
138+
self.apps.append(s)
139+
if s.name==self.current_app.name:
140+
self.app_index=len(self.apps)-1
141+
if not hasattr(self, 'app_index'):
142+
self.app_index=0
143+
self.current_app=self.apps[self.app_index]
144+
tones.beep(660, 100)
145+
self.set_all_gestures()
146+
def set_standard_gestures(self):
147+
self.clearGestureBindings()
148+
self.bindGestures(self.standard_gestures)
149+
def set_all_gestures(self):
150+
self.clearGestureBindings()
151+
self.bindGestures(self.standard_gestures)
152+
self.bindGestures(self.gestures)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
Copyright (c) 2013, Ethan Furman.
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions
6+
are met:
7+
8+
Redistributions of source code must retain the above
9+
copyright notice, this list of conditions and the
10+
following disclaimer.
11+
12+
Redistributions in binary form must reproduce the above
13+
copyright notice, this list of conditions and the following
14+
disclaimer in the documentation and/or other materials
15+
provided with the distribution.
16+
17+
Neither the name Ethan Furman nor the names of any
18+
contributors may be used to endorse or promote products
19+
derived from this software without specific prior written
20+
permission.
21+
22+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32+
POSSIBILITY OF SUCH DAMAGE.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
enum34 is the new Python stdlib enum module available in Python 3.4
2+
backported for previous versions of Python from 2.4 to 3.3.
3+
tested on 2.6, 2.7, and 3.3+

0 commit comments

Comments
 (0)