Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial import to Git

  • Loading branch information...
commit eda3190d6d0632249790829567fedf407baedf26 1 parent 3a3a77b
Charalampos Emmanouilidis authored
Showing with 2,165 additions and 0 deletions.
  1. +3 −0  AUTHORS
  2. +33 −0 bin/naturalscrolling
  3. BIN  data/media/background.png
  4. BIN  data/media/natural-scrolling-status-activated.png
  5. BIN  data/media/natural-scrolling-status-not-activated.png
  6. +139 −0 data/media/naturalscrolling.svg
  7. BIN  data/media/screenshot.png
  8. +40 −0 data/ui/AboutNaturalscrollingDialog.ui
  9. +299 −0 data/ui/NaturalscrollingWindow.ui
  10. +93 −0 data/ui/PreferencesNaturalscrollingDialog.ui
  11. +9 −0 data/ui/about_naturalscrolling_dialog.xml
  12. +8 −0 data/ui/naturalscrolling_window.xml
  13. +9 −0 data/ui/preferences_naturalscrolling_dialog.xml
  14. +5 −0 debian/changelog
  15. +1 −0  debian/compat
  16. +25 −0 debian/control
  17. +10 −0 debian/copyright
  18. +8 −0 debian/rules
  19. BIN  help/C/figures/icon.png
  20. +44 −0 help/C/index.page
  21. +18 −0 help/C/preferences.page
  22. +18 −0 help/C/topic1.page
  23. +8 −0 naturalscrolling.desktop.in
  24. +24 −0 naturalscrolling/AboutNaturalscrollingDialog.py
  25. +128 −0 naturalscrolling/NaturalscrollingIndicator.py
  26. +30 −0 naturalscrolling/NaturalscrollingWindow.py
  27. +51 −0 naturalscrolling/PreferencesNaturalscrollingDialog.py
  28. +50 −0 naturalscrolling/__init__.py
  29. +37 −0 naturalscrolling_lib/AboutDialog.py
  30. +296 −0 naturalscrolling_lib/Builder.py
  31. +132 −0 naturalscrolling_lib/PreferencesDialog.py
  32. +129 −0 naturalscrolling_lib/Window.py
  33. +13 −0 naturalscrolling_lib/__init__.py
  34. +101 −0 naturalscrolling_lib/helpers.py
  35. +63 −0 naturalscrolling_lib/naturalscrollingconfig.py
  36. +100 −0 naturalscrolling_lib/preferences.py
  37. +97 −0 po/naturalscrolling.pot
  38. +88 −0 setup.py
  39. +26 −0 tests/test_example.py
  40. +30 −0 tests/test_lint.py
View
3  AUTHORS
@@ -0,0 +1,3 @@
+Copyright (C) Eumorphed UG 2011 Charalampos Emmanouilidis <charalampos.emmanouilidis@eumorphed.com>
+
+
View
33 bin/naturalscrolling
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+import sys
+import os
+
+import gettext
+from gettext import gettext as _
+gettext.textdomain('naturalscrolling')
+
+# Add project root directory (enable symlink and trunk execution)
+PROJECT_ROOT_DIRECTORY = os.path.abspath(
+ os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))))
+
+python_path = []
+if os.path.abspath(__file__).startswith('/opt'):
+ syspath = sys.path[:] # copy to avoid infinite loop in pending objects
+ for path in syspath:
+ opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/naturalscrolling')
+ python_path.insert(0, opt_path)
+ sys.path.insert(0, opt_path)
+if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'naturalscrolling'))
+ and PROJECT_ROOT_DIRECTORY not in sys.path):
+ python_path.insert(0, PROJECT_ROOT_DIRECTORY)
+ sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
+if python_path:
+ os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) # for subprocesses
+
+import naturalscrolling
+naturalscrolling.main()
View
BIN  data/media/background.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  data/media/natural-scrolling-status-activated.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  data/media/natural-scrolling-status-not-activated.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
139 data/media/naturalscrolling.svg
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48"
+ height="48"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="naturalscrolling.svg"
+ inkscape:export-filename="/home/charemma/bin/Scroll Reverser/resources/scroll-reverser-active.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3796">
+ <stop
+ style="stop-color:#353035;stop-opacity:1;"
+ offset="0"
+ id="stop3798" />
+ <stop
+ style="stop-color:#796e72;stop-opacity:1;"
+ offset="1"
+ id="stop3800" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3796"
+ id="linearGradient3802"
+ x1="23.313826"
+ y1="0.12099174"
+ x2="23.313826"
+ y2="47.467667"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7.0773453"
+ inkscape:cx="22.506663"
+ inkscape:cy="17.350728"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer3"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1533"
+ inkscape:window-height="1061"
+ inkscape:window-x="92"
+ inkscape:window-y="37"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="inactive"
+ style="display:none">
+ <path
+ transform="translate(0,-1004.3622)"
+ style="fill:#3c3c3c;fill-opacity:1;stroke:#3c3c3c;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ d="m 19.094206,1023.6054 3.989317,-0.027 0,9.5641 -3.989317,-0.027 4.751113,5.465 5.060475,-5.3525 -4.256997,-0.027 0,-9.6767 4.256997,-0.027 -5.060475,-5.3534 c -1.575822,1.8051 -3.170713,3.6436 -4.751113,5.4615 z"
+ id="rect2989"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccc" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer4"
+ inkscape:label="active"
+ style="display:none">
+ <path
+ transform="translate(0,-1004.3622)"
+ sodipodi:nodetypes="ccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path3859"
+ d="m 19.094206,1023.6054 3.989317,-0.027 0,9.5641 -3.989317,-0.027 4.751113,5.465 5.060475,-5.3525 -4.256997,-0.027 0,-9.6767 4.256997,-0.027 -5.060475,-5.3534 c -1.575822,1.8051 -3.170713,3.6436 -4.751113,5.4615 z"
+ style="fill:#a23800;fill-opacity:1;stroke:#a23800;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="icon"
+ id="g2989"
+ inkscape:groupmode="layer">
+ <rect
+ style="fill:url(#linearGradient3802);fill-opacity:1;stroke:#726f9b;stroke-width:1.11526561;stroke-miterlimit:4;stroke-opacity:0.44047618;stroke-dasharray:none;stroke-dashoffset:0.6;display:inline"
+ id="rect3793"
+ width="46.884735"
+ height="46.884735"
+ x="0.5576328"
+ y="0.55763459"
+ ry="7.9856253" />
+ <path
+ style="fill:#f7f0d0;fill-opacity:1;stroke:#f7f0d0;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ d="m 19.094206,1023.6054 3.989317,-0.027 0,9.5641 -3.989317,-0.027 4.751113,5.465 5.060475,-5.3525 -4.256997,-0.027 0,-9.6767 4.256997,-0.027 -5.060475,-5.3534 c -1.575822,1.8051 -3.170713,3.6436 -4.751113,5.4615 z"
+ id="path2991"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccc"
+ transform="translate(0,-1004.3622)" />
+ </g>
+ <g
+ inkscape:label="mouse"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1004.3622)"
+ style="display:inline">
+ <rect
+ style="fill:none;stroke:#d6d4dc;stroke-width:3.26242971;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0.6"
+ id="rect3790-9"
+ width="21.60466"
+ height="34.502579"
+ x="13.19767"
+ y="1011.1109"
+ ry="10.201539" />
+ </g>
+</svg>
View
BIN  data/media/screenshot.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
40 data/ui/AboutNaturalscrollingDialog.ui
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-requires about_naturalscrolling_dialog 1.0 -->
+ <!-- interface-naming-policy project-wide -->
+ <object class="AboutNaturalscrollingDialog" id="about_naturalscrolling_dialog">
+ <property name="border_width">5</property>
+ <property name="icon">../media/naturalscrolling.svg</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <property name="program_name">Naturalscrolling</property>
+ <property name="version"></property>
+ <property name="copyright"></property>
+ <property name="website"></property>
+ <property name="license"></property>
+ <property name="authors"></property>
+ <property name="logo">../media/naturalscrolling.svg</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
View
299 data/ui/NaturalscrollingWindow.ui
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-requires naturalscrolling_window 1.0 -->
+ <!-- interface-naming-policy project-wide -->
+ <!-- interface-local-resource-path ../media -->
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-help</property>
+ </object>
+ <object class="NaturalscrollingWindow" id="naturalscrolling_window">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">Naturalscrolling</property>
+ <property name="icon">../media/naturalscrolling.svg</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="mnu_file">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_new">
+ <property name="label">gtk-new</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="n" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_open">
+ <property name="label">gtk-open</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="o" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_save">
+ <property name="label">gtk-save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="s" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_save_as">
+ <property name="label">gtk-save-as</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="s" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="w" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="mnu_edit">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_cut">
+ <property name="label">gtk-cut</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="x" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_copy">
+ <property name="label">gtk-copy</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="c" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_paste">
+ <property name="label">gtk-paste</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="v" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_delete">
+ <property name="label">gtk-delete</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="Delete" signal="activate"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_preferences">
+ <property name="label">gtk-preferences</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="mnu_view">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="mnu_help">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="helpMenu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_contents">
+ <property name="label" translatable="yes">Contents</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="image">image2</property>
+ <property name="use_stock">False</property>
+ <accelerator key="F1" signal="activate"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="mnu_about">
+ <property name="label">gtk-about</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">30</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">Your application has been created!
+
+To start changing this user interface, run 'quickly design', which will open Glade so you can edit the default windows and dialogs.
+
+To change the behavior and edit the python code, run 'quickly edit', which will bring up a text editor.</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">5</property>
+ <property name="ypad">5</property>
+ <property name="pixbuf">../media/background.png</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">15</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkStatusbar" id="statusbar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">5</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">Status Area</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
View
93 data/ui/PreferencesNaturalscrollingDialog.ui
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-requires preferences_naturalscrolling_dialog 1.0 -->
+ <!-- interface-naming-policy project-wide -->
+ <object class="PreferencesNaturalscrollingDialog" id="preferences_naturalscrolling_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Naturalscrolling Preferences</property>
+ <property name="icon">../media/naturalscrolling.svg</property>
+ <property name="type_hint">normal</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="example_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">•</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="example_entry_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Example entry:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">example_entry</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="btn_help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="btn_close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">btn_help</action-widget>
+ <action-widget response="-7">btn_close</action-widget>
+ </action-widgets>
+ </object>
+</interface>
View
9 data/ui/about_naturalscrolling_dialog.xml
@@ -0,0 +1,9 @@
+<glade-catalog name="about_naturalscrolling_dialog" domain="glade-3"
+ depends="gtk+" version="1.0">
+ <glade-widget-classes>
+ <glade-widget-class title="About Naturalscrolling Dialog" name="AboutNaturalscrollingDialog"
+ generic-name="AboutNaturalscrollingDialog" parent="GtkAboutDialog"
+ icon-name="widget-gtk-about-dialog"/>
+ </glade-widget-classes>
+
+</glade-catalog>
View
8 data/ui/naturalscrolling_window.xml
@@ -0,0 +1,8 @@
+<glade-catalog name="naturalscrolling_window" domain="glade-3"
+ depends="gtk+" version="1.0">
+ <glade-widget-classes>
+ <glade-widget-class title="Naturalscrolling Window" name="NaturalscrollingWindow"
+ generic-name="NaturalscrollingWindow" parent="GtkWindow"
+ icon-name="widget-gtk-window"/>
+ </glade-widget-classes>
+</glade-catalog>
View
9 data/ui/preferences_naturalscrolling_dialog.xml
@@ -0,0 +1,9 @@
+<glade-catalog name="preferences_naturalscrolling_dialog" domain="glade-3"
+ depends="gtk+" version="1.0">
+ <glade-widget-classes>
+ <glade-widget-class title="Naturalscrolling Preferences Dialog" name="PreferencesNaturalscrollingDialog"
+ generic-name="PreferenceNaturalscrollingDialog" parent="GtkDialog"
+ icon-name="widget-gtk-dialog"/>
+ </glade-widget-classes>
+
+</glade-catalog>
View
5 debian/changelog
@@ -0,0 +1,5 @@
+naturalscrolling (0.3) natty; urgency=low
+
+ * Initial release.
+
+ -- Charalampos Emmanouilidis <charemma@bbl1lrce.bbl.ms.philips.com> Tue, 26 Jul 2011 08:54:49 +0200
View
1  debian/compat
@@ -0,0 +1 @@
+6
View
25 debian/control
@@ -0,0 +1,25 @@
+Source: naturalscrolling
+Section: python
+Priority: extra
+Build-Depends: cdbs (>= 0.4.43),
+ debhelper (>= 6),
+ python,
+ python-support (>= 0.6.4),
+ python-distutils-extra (>= 2.10)
+Maintainer: Charalampos Emmanouilidis <charalampos.emmanouilidis@eumorphed.com>
+Standards-Version: 3.8.3
+XS-Python-Version: current
+
+Package: naturalscrolling
+Architecture: all
+XB-Python-Version: ${python:Versions}
+Depends: ${misc:Depends},
+ ${python:Depends},
+ python-appindicator,
+ python-launchpad-integration,
+ python-gtk2,
+ python-gobject,
+ python-desktopcouch-records,
+ yelp
+Description: Natural Scrolling for Linux
+ UNKNOWN
View
10 debian/copyright
@@ -0,0 +1,10 @@
+Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
+Name: naturalscrolling
+Maintainer: Charalampos Emmanouilidis <charalampos.emmanouilidis@eumorphed.com>
+Source: http://www.eumorphed.com/naturalscrolling
+
+Files: *
+Copyright: (C) Eumorphed UG 2011 Charalampos Emmanouilidis <charalampos.emmanouilidis@eumorphed.com>
+License: GPL-3
+ The full text of the GPL is distributed in
+ /usr/share/common-licenses/GPL-3 on Debian systems.
View
8 debian/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+DEB_PYTHON_SYSTEM := pysupport
+
+
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/python-distutils.mk
+# langpack.mk is relevant on Ubuntu only, not Debian; it does not matter if it's missing
+-include /usr/share/cdbs/1/rules/langpack.mk
View
BIN  help/C/figures/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
44 help/C/index.page
@@ -0,0 +1,44 @@
+<page xmlns="http://projectmallard.org/1.0/"
+ type="guide"
+ id="index">
+
+<info>
+ <!-- This shows in the title bar so does not show [icon] -->
+ <title type="text">Naturalscrolling</title>
+ <desc>The <app>Naturalscrolling</app> help.</desc>
+ <credit type="author">
+ <name>Your Name</name>
+ <email>Your E-mail</email>
+ <years>2010</years>
+ </credit>
+ <license href="http://creativecommons.org/licenses/by-sa/3.0/">
+ <p>Creative Commons Attribution-Share Alike 3.0 Unported License</p>
+ </license>
+</info>
+
+<title>
+<!-- This shows on the page in title font -->
+<!-- the icon only shows when installed -->
+<media type="image" mime="image/png" src="figures/icon.png">[icon]</media>
+<app>Naturalscrolling</app> Help
+</title>
+
+<p>This is an example guide page. It's main function is to link together the help topics.</p>
+
+<!-- This is the visible index -->
+<section id="contents" style="2column">
+<!-- other pages needs to have a link to index#contents for this to work -->
+<title>Contents</title>
+</section>
+
+<note>
+ <!-- delete this note -->
+ <p>
+ Your script or application looks better if it has help files similar to other applications in ubuntu.
+ </p>
+ <p>
+ Some people think that help files are only for apps that are difficult to use.
+ </p>
+</note>
+
+</page>
View
18 help/C/preferences.page
@@ -0,0 +1,18 @@
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic"
+ id="preferences">
+
+<info>
+ <link type="guide" xref="index#contents"/>
+ <credit type="author">
+ <name>Your Name</name>
+ <email>Your E-mail</email>
+ <years>2010</years>
+ </credit>
+ <desc>Optional short description of Preferences for contents page</desc>
+</info>
+
+<title>Preferences</title>
+<p>This is the preferences page.</p>
+
+</page>
View
18 help/C/topic1.page
@@ -0,0 +1,18 @@
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic"
+ id="topic1">
+
+<info>
+ <link type="guide" xref="index#contents"/>
+ <credit type="author">
+ <name>Your Name</name>
+ <email>Your E-mail</email>
+ <years>2010</years>
+ </credit>
+ <desc>Optional short description of Topic 1 for contents page</desc>
+</info>
+
+<title>Topic 1</title>
+<p>This is an example topic page. It's main function is to describe one topic.</p>
+
+</page>
View
8 naturalscrolling.desktop.in
@@ -0,0 +1,8 @@
+[Desktop Entry]
+_Name=Naturalscrolling
+_Comment=Naturalscrolling application
+Categories=GNOME;Utility;
+Exec=naturalscrolling
+Icon=/usr/share/naturalscrolling/media/naturalscrolling.svg
+Terminal=false
+Type=Application
View
24 naturalscrolling/AboutNaturalscrollingDialog.py
@@ -0,0 +1,24 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+import gettext
+from gettext import gettext as _
+gettext.textdomain('naturalscrolling')
+
+import logging
+logger = logging.getLogger('naturalscrolling')
+
+from naturalscrolling_lib.AboutDialog import AboutDialog
+
+# See naturalscrolling_lib.AboutDialog.py for more details about how this class works.
+class AboutNaturalscrollingDialog(AboutDialog):
+ __gtype_name__ = "AboutNaturalscrollingDialog"
+
+ def finish_initializing(self, builder): # pylint: disable=E1002
+ """Set up the about dialog"""
+ super(AboutNaturalscrollingDialog, self).finish_initializing(builder)
+
+ # Code for other initialization actions should be added here.
+
View
128 naturalscrolling/NaturalscrollingIndicator.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+import sys
+import os
+import gtk
+import appindicator
+
+from naturalscrolling_lib import naturalscrollingconfig
+from naturalscrolling.AboutNaturalscrollingDialog import AboutNaturalscrollingDialog
+
+import gettext
+from gettext import gettext as _
+gettext.textdomain('naturalscrolling')
+
+class NaturalscrollingIndicator:
+ def __init__(self):
+ self.AboutDialog = AboutNaturalscrollingDialog
+ self.mouseid = self.get_slave_pointer()
+ self.pingfrequency = 1 # in seconds
+
+ self.ind = appindicator.Indicator("natural-scrolling-indicator", 'natural-scrolling-status-not-activated', appindicator.CATEGORY_APPLICATION_STATUS)
+
+ media_path = "%s/media/" % naturalscrollingconfig.get_data_path()
+ print media_path
+ self.ind.set_icon_theme_path (media_path)
+ self.ind.set_attention_icon ("natural-scrolling-status-activated")
+
+ self.menu_setup()
+ self.ind.set_menu (self.menu)
+
+ def get_slave_pointer (self):
+ cmd = "xinput list | grep pointer | grep slave | grep -v XTEST | gawk -F'id=' '{print $2}' | gawk '{print $1}'"
+ slavepointer = os.popen (cmd).read().split()
+ return slavepointer
+
+ def menu_setup(self):
+ self.menu = gtk.Menu()
+
+ #natural scrolling
+ self.menu_item_natural_scrolling = gtk.CheckMenuItem(_('Natural Scrolling'))
+ if self.isreversed():
+ self.menu_item_natural_scrolling.set_active (True)
+ self.menu_item_natural_scrolling.connect ('activate', self.on_natural_scrolling_toggled)
+ self.menu_item_natural_scrolling.show()
+
+ #seperator 1
+ self.menu_item_seperator1 = gtk.SeparatorMenuItem()
+ self.menu_item_seperator1.show()
+
+ #preferences
+ self.menu_sub = gtk.Menu()
+ self.menu_item_preferences = gtk.MenuItem (_('Preferences'))
+ self.menu_item_start_at_login = gtk.CheckMenuItem (_('Start at login'))
+ self.menu_sub.append (self.menu_item_start_at_login)
+ self.menu_item_preferences.set_submenu (self.menu_sub)
+
+ self.menu_item_start_at_login.show()
+ self.menu_item_preferences.show()
+
+ #about
+ self.menu_item_about = gtk.MenuItem (_('About...'))
+ self.menu_item_about.connect ('activate', self.on_about_clicked)
+ self.menu_item_about.show()
+
+ #seperator 2
+ self.menu_item_seperator2 = gtk.SeparatorMenuItem()
+ self.menu_item_seperator2.show()
+
+ #quit
+ self.menu_item_quit = gtk.MenuItem(_('Quit Natural Srcolling'))
+ self.menu_item_quit.connect("activate", self.quit)
+ self.menu_item_quit.show()
+
+ #add items to menu
+ self.menu.append(self.menu_item_natural_scrolling)
+ self.menu.append(self.menu_item_seperator1)
+ self.menu.append(self.menu_item_preferences)
+ self.menu.append(self.menu_item_about)
+ self.menu.append(self.menu_item_seperator2)
+ self.menu.append(self.menu_item_quit)
+
+ def main(self):
+ self.check_scrolling()
+ gtk.timeout_add(self.pingfrequency * 1000, self.check_scrolling)
+ gtk.main()
+
+ def quit(self, widget):
+ sys.exit(0)
+
+ def isreversed (self):
+ inreverseorder = False
+
+ for id in self.mouseid:
+ map = os.popen('xinput get-button-map %s' % id).read().strip()
+
+ if '3 5 4' in map:
+ inreverseorder = True
+ break
+
+ return inreverseorder
+
+
+ def on_natural_scrolling_toggled (self, widget, data=None):
+ map = ''
+
+ for id in self.mouseid:
+ map = os.popen ('xinput get-button-map %s' % id).read().strip()
+
+ if self.isreversed():
+ map = map.replace ('3 5 4', '3 4 5')
+ else:
+ map = map.replace ('3 4 5', '3 5 4')
+
+ os.system ('xinput set-button-map %s %s' % (id, map))
+
+ def on_about_clicked (self, widget, data=None):
+ about = self.AboutDialog() # pylint: disable=E1102
+ response = about.run()
+ about.destroy()
+
+
+ def check_scrolling (self):
+ if self.isreversed():
+ self.ind.set_status(appindicator.STATUS_ATTENTION)
+ else:
+ self.ind.set_status(appindicator.STATUS_ACTIVE)
+
+ return True
+
View
30 naturalscrolling/NaturalscrollingWindow.py
@@ -0,0 +1,30 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+import gettext
+from gettext import gettext as _
+gettext.textdomain('naturalscrolling')
+
+import gtk
+import logging
+logger = logging.getLogger('naturalscrolling')
+
+from naturalscrolling_lib import Window
+from naturalscrolling.AboutNaturalscrollingDialog import AboutNaturalscrollingDialog
+from naturalscrolling.PreferencesNaturalscrollingDialog import PreferencesNaturalscrollingDialog
+
+# See naturalscrolling_lib.Window.py for more details about how this class works
+class NaturalscrollingWindow(Window):
+ __gtype_name__ = "NaturalscrollingWindow"
+
+ def finish_initializing(self, builder): # pylint: disable=E1002
+ """Set up the main window"""
+ super(NaturalscrollingWindow, self).finish_initializing(builder)
+
+ self.AboutDialog = AboutNaturalscrollingDialog
+ self.PreferencesDialog = PreferencesNaturalscrollingDialog
+
+ # Code for other initialization actions should be added here.
+
View
51 naturalscrolling/PreferencesNaturalscrollingDialog.py
@@ -0,0 +1,51 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+# This is your preferences dialog.
+#
+# Define your preferences dictionary in the __init__.main() function.
+# The widget names in the PreferencesTestProjectDialog.ui
+# file need to correspond to the keys in the preferences dictionary.
+#
+# Each preference also need to be defined in the 'widget_methods' map below
+# to show up in the dialog itself. Provide three bits of information:
+# 1) The first entry is the method on the widget that grabs a value from the
+# widget.
+# 2) The second entry is the method on the widget that sets the widgets value
+# from a stored preference.
+# 3) The third entry is a signal the widget will send when the contents have
+# been changed by the user. The preferences dictionary is always up to
+# date and will signal the rest of the application about these changes.
+# The values will be saved to desktopcouch when the application closes.
+#
+# TODO: replace widget_methods with your own values
+
+
+widget_methods = {
+ 'example_entry': ['get_text', 'set_text', 'changed'],
+}
+
+import gettext
+from gettext import gettext as _
+gettext.textdomain('naturalscrolling')
+
+import logging
+logger = logging.getLogger('naturalscrolling')
+
+from naturalscrolling_lib.PreferencesDialog import PreferencesDialog
+
+class PreferencesNaturalscrollingDialog(PreferencesDialog):
+ __gtype_name__ = "PreferencesNaturalscrollingDialog"
+
+ def finish_initializing(self, builder): # pylint: disable=E1002
+ """Set up the preferences dialog"""
+ super(PreferencesNaturalscrollingDialog, self).finish_initializing(builder)
+
+ # populate the dialog from the preferences dictionary
+ # using the methods from widget_methods
+ self.widget_methods = widget_methods
+ self.set_widgets_from_preferences() # pylint: disable=E1101
+
+ # Code for other initialization actions should be added here.
View
50 naturalscrolling/__init__.py
@@ -0,0 +1,50 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+import optparse
+
+import gettext
+from gettext import gettext as _
+gettext.textdomain('naturalscrolling')
+
+import gtk
+
+from naturalscrolling import NaturalscrollingIndicator
+
+#from naturalscrolling_lib import set_up_logging, preferences, get_version
+from naturalscrolling_lib import set_up_logging, get_version
+
+def parse_options():
+ """Support for command line options"""
+ parser = optparse.OptionParser(version="%%prog %s" % get_version())
+ parser.add_option(
+ "-v", "--verbose", action="count", dest="verbose",
+ help=_("Show debug messages (-vv debugs naturalscrolling_lib also)"))
+ (options, args) = parser.parse_args()
+
+ set_up_logging(options)
+
+def main():
+ 'constructor for your class instances'
+ parse_options()
+
+ # preferences
+ # set some values for our first session
+ # TODO: replace defaults with your own values
+ default_preferences = {
+ 'example_entry': 'I remember stuff',
+ }
+
+ #preferences.update(default_preferences)
+ # user's stored preferences are used for 2nd and subsequent sessions
+ #preferences.db_connect()
+ #preferences.load()
+
+ # Run the application.
+ indicator = NaturalscrollingIndicator.NaturalscrollingIndicator()
+ indicator.main()
+
+ #preferences.save()
+
View
37 naturalscrolling_lib/AboutDialog.py
@@ -0,0 +1,37 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+import gtk
+
+from . helpers import get_builder
+
+class AboutDialog(gtk.AboutDialog):
+ __gtype_name__ = "AboutDialog"
+
+ def __new__(cls):
+ """Special static method that's automatically called by Python when
+ constructing a new instance of this class.
+
+ Returns a fully instantiated AboutDialog object.
+ """
+ builder = get_builder('AboutNaturalscrollingDialog')
+ new_object = builder.get_object("about_naturalscrolling_dialog")
+ new_object.finish_initializing(builder)
+ return new_object
+
+ def finish_initializing(self, builder):
+ """Called while initializing this instance in __new__
+
+ finish_initalizing should be called after parsing the ui definition
+ and creating a AboutDialog object with it in order
+ to finish initializing the start of the new AboutNaturalscrollingDialog
+ instance.
+
+ Put your initialization code in here and leave __init__ undefined.
+ """
+ # Get a reference to the builder and set up the signals.
+ self.builder = builder
+ self.ui = builder.get_ui(self)
+
View
296 naturalscrolling_lib/Builder.py
@@ -0,0 +1,296 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+'''Enhances builder connections, provides object to access glade objects'''
+
+from gi.repository import GObject # pylint: disable=E0611
+
+import gtk
+import inspect
+import functools
+import logging
+logger = logging.getLogger('naturalscrolling_lib')
+
+from xml.etree.cElementTree import ElementTree
+
+# this module is big so uses some conventional prefixes and postfixes
+# *s list, except self.widgets is a dictionary
+# *_dict dictionary
+# *name string
+# ele_* element in a ElementTree
+
+
+# pylint: disable=R0904
+# the many public methods is a feature of gtk.Builder
+class Builder(gtk.Builder):
+ ''' extra features
+ connects glade defined handler to default_handler if necessary
+ auto connects widget to handler with matching name or alias
+ auto connects several widgets to a handler via multiple aliases
+ allow handlers to lookup widget name
+ logs every connection made, and any on_* not made
+ '''
+
+ def __init__(self):
+ gtk.Builder.__init__(self)
+ self.widgets = {}
+ self.glade_handler_dict = {}
+ self.connections = []
+ self._reverse_widget_dict = {}
+
+# pylint: disable=R0201
+# this is a method so that a subclass of Builder can redefine it
+ def default_handler(self,
+ handler_name, filename, *args, **kwargs):
+ '''helps the apprentice guru
+
+ glade defined handlers that do not exist come here instead.
+ An apprentice guru might wonder which signal does what he wants,
+ now he can define any likely candidates in glade and notice which
+ ones get triggered when he plays with the project.
+ this method does not appear in gtk.Builder'''
+ logger.debug('''tried to call non-existent function:%s()
+ expected in %s
+ args:%s
+ kwargs:%s''', handler_name, filename, args, kwargs)
+# pylint: enable=R0201
+
+ def get_name(self, widget):
+ ''' allows a handler to get the name (id) of a widget
+
+ this method does not appear in gtk.Builder'''
+ return self._reverse_widget_dict.get(widget)
+
+ def add_from_file(self, filename):
+ '''parses xml file and stores wanted details'''
+ gtk.Builder.add_from_file(self, filename)
+
+ # extract data for the extra interfaces
+ tree = ElementTree()
+ tree.parse(filename)
+
+ ele_widgets = tree.getiterator("object")
+ for ele_widget in ele_widgets:
+ name = ele_widget.attrib['id']
+ widget = self.get_object(name)
+
+ # populate indexes - a dictionary of widgets
+ self.widgets[name] = widget
+
+ # populate a reversed dictionary
+ self._reverse_widget_dict[widget] = name
+
+ # populate connections list
+ ele_signals = ele_widget.findall("signal")
+
+ connections = [
+ (name,
+ ele_signal.attrib['name'],
+ ele_signal.attrib['handler']) for ele_signal in ele_signals]
+
+ if connections:
+ self.connections.extend(connections)
+
+ ele_signals = tree.getiterator("signal")
+ for ele_signal in ele_signals:
+ self.glade_handler_dict.update(
+ {ele_signal.attrib["handler"]: None})
+
+ def connect_signals(self, callback_obj):
+ '''connect the handlers defined in glade
+
+ reports successful and failed connections
+ and logs call to missing handlers'''
+ filename = inspect.getfile(callback_obj.__class__)
+ callback_handler_dict = dict_from_callback_obj(callback_obj)
+ connection_dict = {}
+ connection_dict.update(self.glade_handler_dict)
+ connection_dict.update(callback_handler_dict)
+ for item in connection_dict.items():
+ if item[1] is None:
+ # the handler is missing so reroute to default_handler
+ handler = functools.partial(
+ self.default_handler, item[0], filename)
+
+ connection_dict[item[0]] = handler
+
+ # replace the run time warning
+ logger.warn("expected handler '%s' in %s",
+ item[0], filename)
+
+ # connect glade define handlers
+ gtk.Builder.connect_signals(self, connection_dict)
+
+ # let's tell the user how we applied the glade design
+ for connection in self.connections:
+ widget_name, signal_name, handler_name = connection
+ logger.debug("connect builder by design '%s', '%s', '%s'",
+ widget_name, signal_name, handler_name)
+
+ def get_ui(self, callback_obj=None, by_name=True):
+ '''Creates the ui object with widgets as attributes
+
+ connects signals by 2 methods
+ this method does not appear in gtk.Builder'''
+
+ result = UiFactory(self.widgets)
+
+ # Hook up any signals the user defined in glade
+ if callback_obj is not None:
+ # connect glade define handlers
+ self.connect_signals(callback_obj)
+
+ if by_name:
+ auto_connect_by_name(callback_obj, self)
+
+ return result
+
+
+# pylint: disable=R0903
+# this class deliberately does not provide any public interfaces
+# apart from the glade widgets
+class UiFactory():
+ ''' provides an object with attributes as glade widgets'''
+ def __init__(self, widget_dict):
+ self._widget_dict = widget_dict
+ for (widget_name, widget) in widget_dict.items():
+ setattr(self, widget_name, widget)
+
+ # Mangle any non-usable names (like with spaces or dashes)
+ # into pythonic ones
+ cannot_message = """cannot bind ui.%s, name already exists
+ consider using a pythonic name instead of design name '%s'"""
+ consider_message = """consider using a pythonic name instead of design name '%s'"""
+
+ for (widget_name, widget) in widget_dict.items():
+ pyname = make_pyname(widget_name)
+ if pyname != widget_name:
+ if hasattr(self, pyname):
+ logger.debug(cannot_message, pyname, widget_name)
+ else:
+ logger.debug(consider_message, widget_name)
+ setattr(self, pyname, widget)
+
+ def iterator():
+ '''Support 'for o in self' '''
+ return iter(widget_dict.values())
+ setattr(self, '__iter__', iterator)
+
+ def __getitem__(self, name):
+ 'access as dictionary where name might be non-pythonic'
+ return self._widget_dict[name]
+# pylint: enable=R0903
+
+
+def make_pyname(name):
+ ''' mangles non-pythonic names into pythonic ones'''
+ pyname = ''
+ for character in name:
+ if (character.isalpha() or character == '_' or
+ (pyname and character.isdigit())):
+ pyname += character
+ else:
+ pyname += '_'
+ return pyname
+
+
+def dict_from_callback_obj(callback_obj):
+ '''a dictionary interface to callback_obj'''
+ methods = inspect.getmembers(callback_obj, inspect.ismethod)
+
+ aliased_methods = [x[1] for x in methods if hasattr(x[1], 'aliases')]
+
+ # a method may have several aliases
+ #~ @alias('on_btn_foo_clicked')
+ #~ @alias('on_tool_foo_activate')
+ #~ on_menu_foo_activate():
+ #~ pass
+ alias_groups = [(x.aliases, x) for x in aliased_methods]
+
+ aliases = []
+ for item in alias_groups:
+ for alias in item[0]:
+ aliases.append((alias, item[1]))
+
+ dict_methods = dict(methods)
+ dict_aliases = dict(aliases)
+
+ results = {}
+ results.update(dict_methods)
+ results.update(dict_aliases)
+
+ return results
+
+
+def auto_connect_by_name(callback_obj, builder):
+ '''finds handlers like on_<widget_name>_<signal> and connects them
+
+ i.e. find widget,signal pair in builder and call
+ widget.connect(signal, on_<widget_name>_<signal>)'''
+
+ callback_handler_dict = dict_from_callback_obj(callback_obj)
+
+ for item in builder.widgets.items():
+ (widget_name, widget) = item
+ signal_ids = []
+ try:
+ widget_type = type(widget)
+ while widget_type:
+ signal_ids.extend(GObject.signal_list_ids(widget_type))
+ widget_type = GObject.type_parent(widget_type)
+ except RuntimeError: # pylint wants a specific error
+ pass
+ signal_names = [GObject.signal_name(sid) for sid in signal_ids]
+
+ # Now, automatically find any the user didn't specify in glade
+ for sig in signal_names:
+ # using convention suggested by glade
+ handler_names = ["on_%s_%s" % (widget_name, sig)]
+
+ # Using the convention that the top level window is not
+ # specified in the handler name. That is use
+ # on_destroy() instead of on_windowname_destroy()
+ if widget is callback_obj:
+ handler_names.append("on_%s" % sig)
+
+ do_connect(item, sig, handler_names,
+ callback_handler_dict, builder.connections)
+
+ log_unconnected_functions(callback_handler_dict, builder.connections)
+
+
+def do_connect(item, signal_name, handler_names,
+ callback_handler_dict, connections):
+ '''connect this signal to an unused handler'''
+ widget_name, widget = item
+
+ for handler_name in handler_names:
+ target = handler_name in callback_handler_dict.keys()
+ connection = (widget_name, signal_name, handler_name)
+ duplicate = connection in connections
+ if target and not duplicate:
+ widget.connect(signal_name, callback_handler_dict[handler_name])
+ connections.append(connection)
+
+ logger.debug("connect builder by name '%s','%s', '%s'",
+ widget_name, signal_name, handler_name)
+
+
+def log_unconnected_functions(callback_handler_dict, connections):
+ '''log functions like on_* that we could not connect'''
+
+ connected_functions = [x[2] for x in connections]
+
+ handler_names = callback_handler_dict.keys()
+ unconnected = [x for x in handler_names if x.startswith('on_')]
+
+ for handler_name in connected_functions:
+ try:
+ unconnected.remove(handler_name)
+ except ValueError:
+ pass
+
+ for handler_name in unconnected:
+ logger.debug("Not connected to builder '%s'", handler_name)
View
132 naturalscrolling_lib/PreferencesDialog.py
@@ -0,0 +1,132 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+"""this dialog adjusts values in the preferences dictionary
+
+requirements:
+in module preferences: defaults[key] has a value
+self.builder.get_object(key) is a suitable widget to adjust value
+widget_methods[key] provides method names for the widget
+each widget calls set_preference(...) when it has adjusted value
+"""
+
+import gtk
+import logging
+logger = logging.getLogger('naturalscrolling_lib')
+
+from . helpers import get_builder, show_uri, get_help_uri
+from . preferences import preferences
+
+class PreferencesDialog(gtk.Dialog):
+ __gtype_name__ = "PreferencesDialog"
+
+ def __new__(cls):
+ """Special static method that's automatically called by Python when
+ constructing a new instance of this class.
+
+ Returns a fully instantiated PreferencesDialog object.
+ """
+ builder = get_builder('PreferencesNaturalscrollingDialog')
+ new_object = builder.get_object("preferences_naturalscrolling_dialog")
+ new_object.finish_initializing(builder)
+ return new_object
+
+ def finish_initializing(self, builder):
+ """Called while initializing this instance in __new__
+
+ finish_initalizing should be called after parsing the ui definition
+ and creating a PreferencesDialog object with it in order to
+ finish initializing the start of the new PerferencesNaturalscrollingDialog
+ instance.
+
+ Put your initialization code in here and leave __init__ undefined.
+ """
+
+ # Get a reference to the builder and set up the signals.
+ self.builder = builder
+ self.ui = builder.get_ui(self, True)
+
+ # code for other initialization actions should be added here
+ self.widget_methods = []
+
+ def set_widgets_from_preferences(self):
+ ''' these widgets show values in the preferences dictionary '''
+ for key in preferences.keys():
+ self.set_widget_from_preference(key)
+
+ def set_widget_from_preference(self, key):
+ '''set widget value from item in preferences'''
+
+ value = preferences.get(key)
+ widget = self.builder.get_object(key)
+ if widget is None:
+ # this preference is not adjustable by this dialog
+ # for example: window and dialog geometries
+ logger.debug('no widget for preference: %s' % key)
+ return
+
+ logger.debug('set_widget_from_preference: %s' % key)
+ try:
+ write_method_name = self.widget_methods[key][1]
+ except KeyError:
+ logger.warn('%s not in widget_methods' % key)
+ return
+
+ try:
+ method = getattr(widget, write_method_name)
+ except AttributeError:
+ logger.warn("""'%s' does not have a '%s' method.
+Please edit 'widget_methods' in %s"""
+ % (key, write_method_name, self.__gtype_name__))
+ return
+
+ try:
+ widget.connect(self.widget_methods[key][2], self.set_preference)
+ except TypeError:
+ logger.warn("""'%s' unknown signal name '%s'
+Please edit 'widget_methods' in %s"""
+ % (key, self.widget_methods[key][2], self.__gtype_name__))
+
+ method(value)
+
+ def get_key_for_widget(self, widget):
+ key = None
+ for key_try in preferences.keys():
+ obj = self.builder.get_object(key_try)
+ if obj == widget:
+ key = key_try
+ return key
+
+ def set_preference(self, widget, data=None):
+ '''set a preference from a widget'''
+ key = self.get_key_for_widget(widget)
+ if key is None:
+ logger.warn('''This widget will not write to a preference.
+The preference must already exist so add this widget's name
+to default_preferences in your main function''')
+ return
+
+ # set_widget_from_preference is called first
+ # so no KeyError test is needed here
+ read_method_name = self.widget_methods[key][0]
+
+ try:
+ read_method = getattr(widget, read_method_name)
+ except AttributeError:
+ logger.warn("""'%s' does not have a '%s' method.
+Please edit 'widget_methods' in %s"""
+ % (key, read_method_name, self.__gtype_name__))
+ return
+
+ value=read_method()
+ logger.debug('set_preference: %s = %s' % (key, str(value)))
+ preferences[key] = value
+
+ def on_btn_close_clicked(self, widget, data=None):
+ self.destroy()
+
+ def on_btn_help_clicked(self, widget, data=None):
+ show_uri(self, "ghelp:%s" % get_help_uri('preferences'))
+
View
129 naturalscrolling_lib/Window.py
@@ -0,0 +1,129 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+import gtk
+import logging
+logger = logging.getLogger('naturalscrolling_lib')
+
+from . helpers import get_builder, show_uri, get_help_uri
+from . preferences import preferences
+
+# This class is meant to be subclassed by NaturalscrollingWindow. It provides
+# common functions and some boilerplate.
+class Window(gtk.Window):
+ __gtype_name__ = "Window"
+
+ # To construct a new instance of this method, the following notable
+ # methods are called in this order:
+ # __new__(cls)
+ # __init__(self)
+ # finish_initializing(self, builder)
+ # __init__(self)
+ #
+ # For this reason, it's recommended you leave __init__ empty and put
+ # your initialization code in finish_initializing
+
+ def __new__(cls):
+ """Special static method that's automatically called by Python when
+ constructing a new instance of this class.
+
+ Returns a fully instantiated BaseNaturalscrollingWindow object.
+ """
+ builder = get_builder('NaturalscrollingWindow')
+ new_object = builder.get_object("naturalscrolling_window")
+ new_object.finish_initializing(builder)
+ return new_object
+
+ def finish_initializing(self, builder):
+ """Called while initializing this instance in __new__
+
+ finish_initializing should be called after parsing the UI definition
+ and creating a NaturalscrollingWindow object with it in order to finish
+ initializing the start of the new NaturalscrollingWindow instance.
+ """
+ # Get a reference to the builder and set up the signals.
+ self.builder = builder
+ self.ui = builder.get_ui(self, True)
+ self.PreferencesDialog = None # class
+ self.preferences_dialog = None # instance
+ self.AboutDialog = None # class
+
+ preferences.connect('changed', self.on_preferences_changed)
+
+ # Optional Launchpad integration
+ # This shouldn't crash if not found as it is simply used for bug reporting.
+ # See https://wiki.ubuntu.com/UbuntuDevelopment/Internationalisation/Coding
+ # for more information about Launchpad integration.
+ try:
+ import LaunchpadIntegration
+ LaunchpadIntegration.add_items(self.ui.helpMenu, 1, True, True)
+ LaunchpadIntegration.set_sourcepackagename('naturalscrolling')
+ except ImportError:
+ pass
+
+ # Optional application indicator support
+ # Run 'quickly add indicator' to get started.
+ # More information:
+ # http://owaislone.org/quickly-add-indicator/
+ # https://wiki.ubuntu.com/DesktopExperienceTeam/ApplicationIndicators
+ try:
+ from naturalscrolling import indicator
+ # self is passed so methods of this class can be called from indicator.py
+ # Comment this next line out to disable appindicator
+ self.indicator = indicator.new_application_indicator(self)
+ except ImportError:
+ pass
+
+ def on_mnu_contents_activate(self, widget, data=None):
+ show_uri(self, "ghelp:%s" % get_help_uri())
+
+ def on_mnu_about_activate(self, widget, data=None):
+ """Display the about box for naturalscrolling."""
+ if self.AboutDialog is not None:
+ about = self.AboutDialog() # pylint: disable=E1102
+ response = about.run()
+ about.destroy()
+
+ def on_mnu_preferences_activate(self, widget, data=None):
+ """Display the preferences window for naturalscrolling."""
+
+ """ From the PyGTK Reference manual
+ Say for example the preferences dialog is currently open,
+ and the user chooses Preferences from the menu a second time;
+ use the present() method to move the already-open dialog
+ where the user can see it."""
+ if self.preferences_dialog is not None:
+ logger.debug('show existing preferences_dialog')
+ self.preferences_dialog.present()
+ elif self.PreferencesDialog is not None:
+ logger.debug('create new preferences_dialog')
+ self.preferences_dialog = self.PreferencesDialog() # pylint: disable=E1102
+ self.preferences_dialog.connect('destroy', self.on_preferences_dialog_destroyed)
+ self.preferences_dialog.show()
+ # destroy command moved into dialog to allow for a help button
+
+ def on_mnu_close_activate(self, widget, data=None):
+ """Signal handler for closing the NaturalscrollingWindow."""
+ self.destroy()
+
+ def on_destroy(self, widget, data=None):
+ """Called when the NaturalscrollingWindow is closed."""
+ # Clean up code for saving application state should be added here.
+ gtk.main_quit()
+
+ def on_preferences_changed(self, widget, data=None):
+ logger.debug('main window received preferences changed')
+ for key in data:
+ logger.debug('preference changed: %s = %s' % (key, preferences[key]))
+
+ def on_preferences_dialog_destroyed(self, widget, data=None):
+ '''only affects gui
+
+ logically there is no difference between the user closing,
+ minimising or ignoring the preferences dialog'''
+ logger.debug('on_preferences_dialog_destroyed')
+ # to determine whether to create or present preferences_dialog
+ self.preferences_dialog = None
+
View
13 naturalscrolling_lib/__init__.py
@@ -0,0 +1,13 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+'''facade - makes naturalscrolling_lib package easy to refactor
+
+while keeping its api constant'''
+from . helpers import set_up_logging
+from . preferences import preferences
+from . Window import Window
+from . naturalscrollingconfig import get_version
+
View
101 naturalscrolling_lib/helpers.py
@@ -0,0 +1,101 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+"""Helpers for an Ubuntu application."""
+import logging
+import os
+
+import gtk
+
+from . naturalscrollingconfig import get_data_file
+from . Builder import Builder
+
+import gettext
+from gettext import gettext as _
+gettext.textdomain('naturalscrolling')
+
+def get_builder(builder_file_name):
+ """Return a fully-instantiated gtk.Builder instance from specified ui
+ file
+
+ :param builder_file_name: The name of the builder file, without extension.
+ Assumed to be in the 'ui' directory under the data path.
+ """
+ # Look for the ui file that describes the user interface.
+ ui_filename = get_data_file('ui', '%s.ui' % (builder_file_name,))
+ if not os.path.exists(ui_filename):
+ ui_filename = None
+
+ builder = Builder()
+ builder.set_translation_domain('naturalscrolling')
+ builder.add_from_file(ui_filename)
+ return builder
+
+
+# Owais Lone : To get quick access to icons and stuff.
+def get_media_file(media_file_name):
+ media_filename = get_data_file('media', '%s' % (media_file_name,))
+ if not os.path.exists(media_filename):
+ media_filename = None
+
+ return "file:///"+media_filename
+
+class NullHandler(logging.Handler):
+ def emit(self, record):
+ pass
+
+def set_up_logging(opts):
+ # add a handler to prevent basicConfig
+ root = logging.getLogger()
+ null_handler = NullHandler()
+ root.addHandler(null_handler)
+
+ formatter = logging.Formatter("%(levelname)s:%(name)s: %(funcName)s() '%(message)s'")
+
+ logger = logging.getLogger('naturalscrolling')
+ logger_sh = logging.StreamHandler()
+ logger_sh.setFormatter(formatter)
+ logger.addHandler(logger_sh)
+
+ lib_logger = logging.getLogger('naturalscrolling_lib')
+ lib_logger_sh = logging.StreamHandler()
+ lib_logger_sh.setFormatter(formatter)
+ lib_logger.addHandler(lib_logger_sh)
+
+ # Set the logging level to show debug messages.
+ if opts.verbose:
+ logger.setLevel(logging.DEBUG)
+ logger.debug('logging enabled')
+ if opts.verbose > 1:
+ lib_logger.setLevel(logging.DEBUG)
+
+def get_help_uri(page=None):
+ # help_uri from source tree - default language
+ here = os.path.dirname(__file__)
+ help_uri = os.path.abspath(os.path.join(here, '..', 'help', 'C'))
+
+ if not os.path.exists(help_uri):
+ # installed so use gnome help tree - user's language
+ help_uri = 'naturalscrolling'
+
+ # unspecified page is the index.page
+ if page is not None:
+ help_uri = '%s#%s' % (help_uri, page)
+
+ return help_uri
+
+def show_uri(parent, link):
+ screen = parent.get_screen()
+ gtk.show_uri(screen, link, gtk.get_current_event_time())
+
+def alias(alternative_function_name):
+ '''see http://www.drdobbs.com/web-development/184406073#l9'''
+ def decorator(function):
+ '''attach alternative_function_name(s) to function'''
+ if not hasattr(function, 'aliases'):
+ function.aliases = []
+ function.aliases.append(alternative_function_name)
+ return function
+ return decorator
View
63 naturalscrolling_lib/naturalscrollingconfig.py
@@ -0,0 +1,63 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+# THIS IS Naturalscrolling CONFIGURATION FILE
+# YOU CAN PUT THERE SOME GLOBAL VALUE
+# Do not touch unless you know what you're doing.
+# you're warned :)
+
+__all__ = [
+ 'project_path_not_found',
+ 'get_data_file',
+ 'get_data_path',
+ ]
+
+# Where your project will look for your data (for instance, images and ui
+# files). By default, this is ../data, relative your trunk layout
+__naturalscrolling_data_directory__ = '../data/'
+__license__ = ''
+__version__ = 'VERSION'
+
+import os
+
+import gettext
+from gettext import gettext as _
+gettext.textdomain('naturalscrolling')
+
+class project_path_not_found(Exception):
+ """Raised when we can't find the project directory."""
+
+
+def get_data_file(*path_segments):
+ """Get the full path to a data file.
+
+ Returns the path to a file underneath the data directory (as defined by
+ `get_data_path`). Equivalent to os.path.join(get_data_path(),
+ *path_segments).
+ """
+ return os.path.join(get_data_path(), *path_segments)
+
+
+def get_data_path():
+ """Retrieve naturalscrolling data path
+
+ This path is by default <naturalscrolling_lib_path>/../data/ in trunk
+ and /usr/share/naturalscrolling in an installed version but this path
+ is specified at installation time.
+ """
+
+ # Get pathname absolute or relative.
+ path = os.path.join(
+ os.path.dirname(__file__), __naturalscrolling_data_directory__)
+
+ abs_data_path = os.path.abspath(path)
+ if not os.path.exists(abs_data_path):
+ raise project_path_not_found
+
+ return abs_data_path
+
+
+def get_version():
+ return __version__
View
100 naturalscrolling_lib/preferences.py
@@ -0,0 +1,100 @@
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+"""Provides a shared preferences dictionary"""
+
+from desktopcouch.records.server import CouchDatabase
+from desktopcouch.records.record import Record
+import gtk
+import gobject
+
+class User_dict(dict):
+ ''' a dictionary with extra methods:
+
+ persistence: load, save and db_connect
+ gobject signals: connect and emit.
+
+ Don't use this directly. Please use the preferences instance.'''
+
+ def __init__(self, *args, **kwds):
+ dict.__init__(self, *args, **kwds)
+ # Set up couchdb.
+ self._db_name = "naturalscrolling"
+ self._key = None
+ self._database = None
+
+ self._record_type = (
+ "http://wiki.ubuntu.com/Quickly/RecordTypes/Naturalscrolling/"
+ "Preferences")
+
+ class Publisher(gtk.Invisible): # pylint: disable=R0904
+ '''set up signals in a separate class
+
+ gtk.Invisible has 230 public methods'''
+ __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
+ 'loaded' : (gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))}
+
+ publisher = Publisher()
+ self.emit = publisher.emit
+ self.connect = publisher.connect
+
+ def db_connect(self):
+ '''connect to couchdb
+
+ create if necessary'''
+ # logging.basicConfig will be called now
+ self._database = CouchDatabase(self._db_name, create=True)
+
+ def save(self):
+ 'save to couchdb'
+ self._database.update_fields(self._key, self)
+
+
+ def load(self):
+ 'load from couchdb'
+ self.update({"record_type": self._record_type})
+
+ results = self._database.get_records(
+ record_type=self._record_type, create_view=True)
+
+ if len(results.rows) == 0:
+ # No preferences have ever been saved
+ # save them before returning.
+ self._key = self._database.put_record(Record(self))
+ else:
+ self.update(results.rows[0].value)
+ del self['_rev']
+ self._key = results.rows[0].value["_id"]
+ self.emit('loaded', None)
+
+ def update(self, *args, **kwds):
+ ''' interface for dictionary
+
+ send changed signal when appropriate '''
+
+ # parse args
+ new_data = {}
+ new_data.update(*args, **kwds)
+
+ changed_keys = []
+ for key in new_data.keys():
+ if new_data.get(key) != dict.get(self, key):
+ changed_keys.append(key)
+ dict.update(self, new_data)
+ if changed_keys:
+ self.emit('changed', tuple(changed_keys))
+
+ def __setitem__(self, key, value):
+ ''' interface for dictionary
+
+ send changed signal when appropriate '''
+ if value != dict.get(self, key):
+ dict.__setitem__(self, key, value)
+ self.emit('changed', (key,))
+
+preferences = User_dict()
+
View
97 po/naturalscrolling.pot
@@ -0,0 +1,97 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-07-26 08:54+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. natural scrolling
+#: ../naturalscrolling/NaturalscrollingIndicator.py:39
+msgid "Natural Scrolling"
+msgstr ""
+
+#: ../naturalscrolling/NaturalscrollingIndicator.py:51
+msgid "Preferences"
+msgstr ""
+
+#: ../naturalscrolling/NaturalscrollingIndicator.py:52
+msgid "Start at login"
+msgstr ""
+
+#. about
+#: ../naturalscrolling/NaturalscrollingIndicator.py:60
+msgid "About..."
+msgstr ""
+
+#. quit
+#: ../naturalscrolling/NaturalscrollingIndicator.py:69
+msgid "Quit Natural Srcolling"
+msgstr ""
+
+#. Code for other initialization actions should be added here.
+#: ../naturalscrolling.desktop.in.h:1 ../data/ui/NaturalscrollingWindow.ui.h:2
+msgid "Naturalscrolling"
+msgstr ""
+
+#: ../naturalscrolling.desktop.in.h:2
+msgid "Naturalscrolling application"
+msgstr ""
+
+#: ../naturalscrolling/__init__.py:24
+msgid "Show debug messages (-vv debugs naturalscrolling_lib also)"
+msgstr ""
+
+#: ../data/ui/NaturalscrollingWindow.ui.h:1
+msgid "Contents"
+msgstr ""
+
+#: ../data/ui/NaturalscrollingWindow.ui.h:3
+msgid "Status Area"
+msgstr ""
+
+#: ../data/ui/NaturalscrollingWindow.ui.h:4
+msgid ""
+"Your application has been created!\n"
+"\n"
+"To start changing this user interface, run 'quickly design', which will open "
+"Glade so you can edit the default windows and dialogs.\n"
+"\n"
+"To change the behavior and edit the python code, run 'quickly edit', which "
+"will bring up a text editor."
+msgstr ""
+
+#: ../data/ui/NaturalscrollingWindow.ui.h:9
+msgid "_Edit"
+msgstr ""
+
+#: ../data/ui/NaturalscrollingWindow.ui.h:10
+msgid "_File"
+msgstr ""
+
+#: ../data/ui/NaturalscrollingWindow.ui.h:11
+msgid "_Help"
+msgstr ""
+
+#: ../data/ui/NaturalscrollingWindow.ui.h:12
+msgid "_View"
+msgstr ""
+
+#: ../data/ui/PreferencesNaturalscrollingDialog.ui.h:1
+msgid "Naturalscrolling Preferences"
+msgstr ""
+
+#: ../data/ui/PreferencesNaturalscrollingDialog.ui.h:2
+msgid "_Example entry:"
+msgstr ""
View
88 setup.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+###################### DO NOT TOUCH THIS (HEAD TO THE SECOND PART) ######################
+
+import os
+import sys
+
+try:
+ import DistUtilsExtra.auto
+except ImportError:
+ print >> sys.stderr, 'To build naturalscrolling you need https://launchpad.net/python-distutils-extra'
+ sys.exit(1)
+assert DistUtilsExtra.auto.__version__ >= '2.18', 'needs DistUtilsExtra.auto >= 2.18'
+
+def update_config(values = {}):
+
+ oldvalues = {}
+ try:
+ fin = file('naturalscrolling_lib/naturalscrollingconfig.py', 'r')
+ fout = file(fin.name + '.new', 'w')
+
+ for line in fin:
+ fields = line.split(' = ') # Separate variable from value
+ if fields[0] in values:
+ oldvalues[fields[0]] = fields[1].strip()
+ line = "%s = %s\n" % (fields[0], values[fields[0]])
+ fout.write(line)
+
+ fout.flush()
+ fout.close()
+ fin.close()
+ os.rename(fout.name, fin.name)
+ except (OSError, IOError), e:
+ print ("ERROR: Can't find naturalscrolling_lib/naturalscrollingconfig.py")
+ sys.exit(1)
+ return oldvalues
+
+
+def update_desktop_file(datadir):
+
+ try:
+ fin = file('naturalscrolling.desktop.in', 'r')
+ fout = file(fin.name + '.new', 'w')
+
+ for line in fin:
+ if 'Icon=' in line:
+ line = "Icon=%s\n" % (datadir + 'media/naturalscrolling.svg')
+ fout.write(line)
+ fout.flush()
+ fout.close()
+ fin.close()
+ os.rename(fout.name, fin.name)
+ except (OSError, IOError), e:
+ print ("ERROR: Can't find naturalscrolling.desktop.in")
+ sys.exit(1)
+
+
+class InstallAndUpdateDataDirectory(DistUtilsExtra.auto.install_auto):
+ def run(self):
+ values = {'__naturalscrolling_data_directory__': "'%s'" % (self.prefix + '/share/naturalscrolling/'),
+ '__version__': "'%s'" % self.distribution.get_version()}
+ previous_values = update_config(values)
+ update_desktop_file(self.prefix + '/share/naturalscrolling/')
+ DistUtilsExtra.auto.install_auto.run(self)
+ update_config(previous_values)
+
+
+
+##################################################################################
+###################### YOU SHOULD MODIFY ONLY WHAT IS BELOW ######################
+##################################################################################
+
+DistUtilsExtra.auto.setup(
+ name='naturalscrolling',
+ version='0.3',
+ license='GPL-3',
+ author='Charalampos Emmanouilidis',
+ <