diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ccd8bd2..04179b5 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -2,7 +2,7 @@
name: CI
-# Controls when the action will run.
+# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
@@ -26,12 +26,15 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- - name: Meson Build
- uses: BSFishy/meson-build@v1.0.1
+ - name: Install dependencies
+ run: sudo apt-get -y install appstream appstream-util desktop-file-utils gettext
+
+ - name: Meson build
+ uses: BSFishy/meson-build@v1.0.3
with:
action: build
- - name: Meson Test
- uses: BSFishy/meson-build@v1.0.1
+ - name: Meson test
+ uses: BSFishy/meson-build@v1.0.3
with:
action: test
diff --git a/.gitignore b/.gitignore
index 8d6a672..04a5dc8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,8 +13,7 @@ __pycache__/
# Distribution / packaging
.Python
build/
-builddir/
-builddir-flatpak/
+build-flatpak/
develop-eggs/
dist/
downloads/
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..5a30cb8
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,28 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Python: Debug",
+ "type": "python",
+ "request": "launch",
+ "program": "${workspaceFolder}/main.py",
+ "console": "integratedTerminal",
+ // "env": {
+ // "PYTHONPATH": "."
+ // }
+ },
+ {
+ "name": "Python: Remote Attach",
+ "type": "python",
+ "request": "attach",
+ "port": 5678,
+ "host": "localhost",
+ "pathMappings": [
+ {
+ "localRoot": "${workspaceFolder}",
+ "remoteRoot": "."
+ }
+ ]
+ }
+ ]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6dc933a..57922e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added MetaInfo file
- New SVG icon (thanks @creepertron95, @jimmac and @freddii)
+- Started modularizing the code
### Changed
- Changed AppID to io.github.mightycreak.Diffuse (as explained in
@@ -21,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add .desktop translations in .po files
### Fixed
+- Fixed some GTK deprecation warnings
## [0.6.0] - 2020-11-29
diff --git a/README.md b/README.md
index 6e527cf..3a7b157 100644
--- a/README.md
+++ b/README.md
@@ -20,48 +20,71 @@ Some key features of Diffuse:
## Requirements
+Diffuse is implemented entirely in Python and should run on any platform with
+Python and PyGObject.
+
* Python >= 3.4
* PyGObject >= 3.18
-Diffuse is implemented entirely in Python and should run on any platform with
-Python and PyGTK. If you need to manually install PyGTK, please be aware its
-dependencies should be installed prior to installing PyGTK.
+## Users
-Diffuse can be run directly from an untared source distribution on any POSIX
-system or installed with the instructions described in the next section.
+### Installing using Flatpak
-The location of the personal preferences, state, and initialisation files have
-changed in the 0.4.1 release. Old settings may be migrated using the following
-commands:
+This is the easiest way to install Diffuse:
- $ mkdir -p ~/.config/diffuse
- $ mv ~/.diffuse/config ~/.config/diffuse/state
- $ mv ~/.diffuse/* ~/.config/diffuse
- $ rmdir ~/.diffuse
+```sh
+flatpak install io.github.mightycreak.Diffuse
+```
-The rules for parsing files in `~/.diffuse` changed in the 0.3.0 release.
-Non-fatal errors may be reported when parsing old files. These errors can be
-fixed by removing the offending lines (or the entire file) from
-`~/.config/diffuse/diffuserc`.
+## Developers
-## Installing on POSIX systems
+### Setup
+
+#### Run Diffuse from source
+
+To run Diffuse from the source code, type this:
+```sh
+python main.py
+```
+
+To debug with VS Code, open the directory in VS Code, place your breakpoints and hit F5.
+
+#### Build Diffuse
+
+To build Diffuse, type this:
+```sh
+python setup.py build
+```
+
+To run from the build, type this:
+```sh
+PYTHONPATH=build/lib ./build/scripts-3.7/diffuse
+```
+
+#### Install Diffuse locally
Diffuse build system is meson.
To install diffuse locally:
- meson builddir
- meson install -C builddir
+```sh
+meson setup build
+cd build
+meson compile
+meson install # requires admin privileges
+```
To uninstall diffuse afterwards:
- sudo ninja uninstall -C builddir
- sudo rm -v /usr/local/share/locale/*/LC_MESSAGES/diffuse.mo
+```sh
+sudo ninja uninstall -C build
+sudo rm -v /usr/local/share/locale/*/LC_MESSAGES/diffuse.mo
+```
Meson allows to change the default installation directories, see
[command-line documentation](https://mesonbuild.com/Commands.html#configure).
-## Installing on Windows
+### Installing on Windows
The `windows-installer` directory contains scripts for building an installable
package for Windows that includes all dependencies.
@@ -73,24 +96,26 @@ Diffuse. The `XDG_CONFIG_HOME` and `XDG_DATA_DIR` environment variables
indicate where Diffuse should store persistent settings (eg. the path to a
writable directory on the pen drive).
-## Installing the Flatpak package
-
- flatpak install io.github.mightycreak.Diffuse
-
## Building and testing the Flatpak package
To install Diffuse locally:
- flatpak install flatpak install runtime/org.gnome.Sdk/$(uname -p)/3.38
- flatpak-builder builddir-flatpak --user --install io.github.mightycreak.Diffuse.yml
+```sh
+flatpak install runtime/org.gnome.Sdk/$(uname -p)/3.38
+flatpak-builder build-flatpak --user --install io.github.mightycreak.Diffuse.yml
+```
To run Diffuse through Flatpak:
- flatpak run io.github.mightycreak.Diffuse
+```sh
+flatpak run io.github.mightycreak.Diffuse
+```
To uninstall Diffuse:
- flatpak remove io.github.mightycreak.Diffuse
+```sh
+flatpak remove io.github.mightycreak.Diffuse
+```
## Help Documentation
diff --git a/build-scripts/meson-postinstall.py b/build-aux/meson/postinstall.py
similarity index 62%
rename from build-scripts/meson-postinstall.py
rename to build-aux/meson/postinstall.py
index 6999cb4..2e07613 100644
--- a/build-scripts/meson-postinstall.py
+++ b/build-aux/meson/postinstall.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python3
-import sysconfig
-from compileall import compile_dir
from os import environ, path
from subprocess import call
@@ -13,3 +11,9 @@ destdir = environ.get('DESTDIR', '')
if not destdir:
print('Updating icon cache...')
call(['gtk-update-icon-cache', '-qtf', path.join(datadir, 'icons', 'hicolor')])
+
+ print('Updating desktop database...')
+ call(['update-desktop-database', '-q', path.join(datadir, 'applications')])
+
+ print('Compiling GSettings schemas...')
+ call(['glib-compile-schemas', path.join(datadir, 'glib-2.0', 'schemas')])
diff --git a/src/etc/diffuserc.py.in b/data/diffuserc.in
similarity index 100%
rename from src/etc/diffuserc.py.in
rename to data/diffuserc.in
diff --git a/src/usr/share/icons/hicolor/scalable/apps/io.github.mightycreak.Diffuse.Devel.svg b/data/icons/hicolor/scalable/apps/io.github.mightycreak.Diffuse.Devel.svg
similarity index 100%
rename from src/usr/share/icons/hicolor/scalable/apps/io.github.mightycreak.Diffuse.Devel.svg
rename to data/icons/hicolor/scalable/apps/io.github.mightycreak.Diffuse.Devel.svg
diff --git a/src/usr/share/icons/hicolor/scalable/apps/io.github.mightycreak.Diffuse.svg b/data/icons/hicolor/scalable/apps/io.github.mightycreak.Diffuse.svg
similarity index 100%
rename from src/usr/share/icons/hicolor/scalable/apps/io.github.mightycreak.Diffuse.svg
rename to data/icons/hicolor/scalable/apps/io.github.mightycreak.Diffuse.svg
diff --git a/src/usr/share/icons/hicolor/symbolic/apps/io.github.mightycreak.Diffuse-symbolic.svg b/data/icons/hicolor/symbolic/apps/io.github.mightycreak.Diffuse-symbolic.svg
similarity index 100%
rename from src/usr/share/icons/hicolor/symbolic/apps/io.github.mightycreak.Diffuse-symbolic.svg
rename to data/icons/hicolor/symbolic/apps/io.github.mightycreak.Diffuse-symbolic.svg
diff --git a/data/icons/meson.build b/data/icons/meson.build
new file mode 100644
index 0000000..2e3a308
--- /dev/null
+++ b/data/icons/meson.build
@@ -0,0 +1,13 @@
+application_id = 'io.github.mightycreak.Diffuse'
+
+scalable_dir = join_paths('hicolor', 'scalable', 'apps')
+install_data(
+ join_paths(scalable_dir, ('@0@.svg').format(application_id)),
+ install_dir: join_paths(get_option('datadir'), 'icons', scalable_dir)
+)
+
+symbolic_dir = join_paths('hicolor', 'symbolic', 'apps')
+install_data(
+ join_paths(symbolic_dir, ('@0@-symbolic.svg').format(application_id)),
+ install_dir: join_paths(get_option('datadir'), 'icons', symbolic_dir)
+)
diff --git a/data/diffuse.desktop.in b/data/io.github.mightycreak.Diffuse.desktop.in
similarity index 100%
rename from data/diffuse.desktop.in
rename to data/io.github.mightycreak.Diffuse.desktop.in
diff --git a/src/usr/share/metainfo/io.github.mightycreak.Diffuse.metainfo.xml b/data/io.github.mightycreak.Diffuse.metainfo.xml.in
similarity index 98%
rename from src/usr/share/metainfo/io.github.mightycreak.Diffuse.metainfo.xml
rename to data/io.github.mightycreak.Diffuse.metainfo.xml.in
index 86a598f..00e3ed9 100644
--- a/src/usr/share/metainfo/io.github.mightycreak.Diffuse.metainfo.xml
+++ b/data/io.github.mightycreak.Diffuse.metainfo.xml.in
@@ -1,7 +1,6 @@
io.github.mightycreak.Diffuse
-
Diffuse Merge Tool
Graphical tool for merging and comparing text files
@@ -9,23 +8,18 @@
Diffuse is a graphical tool for comparing and merging text files. It can retrieve files for comparison from Bazaar, CVS, Darcs, Git, Mercurial, Monotone, RCS, Subversion, and SVK repositories.
-
FSFAP
GPL-2.0-or-later
-
-
+
io.github.mightycreak.Diffuse.desktop
-
https://mightycreak.github.io/diffuse/
https://github.com/MightyCreak/diffuse/issues
-
- https://mightycreak.github.io/diffuse/images/screenshot_v0.5.0.png
Main window: diff between two files
+ https://mightycreak.github.io/diffuse/images/screenshot_v0.7.0.png
-
@@ -102,7 +96,6 @@
-
Romain Failliot
romain.failliot@foolstep.com
diff --git a/data/meson.build b/data/meson.build
index fa85471..19a354a 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -1,10 +1,49 @@
-desktop_file = 'diffuse.desktop'
-i18n.merge_file(
- desktop_file,
- input: desktop_file + '.in',
- output: desktop_file,
+pkgdatadir = join_paths(get_option('prefix'), get_option('datadir'), meson.project_name())
+
+desktop_file = i18n.merge_file(
+ input: 'io.github.mightycreak.Diffuse.desktop.in',
+ output: 'io.github.mightycreak.Diffuse.desktop',
+ type: 'desktop',
po_dir: '../po',
install: true,
- install_dir: join_paths(datadir, 'applications'),
- type: 'desktop'
+ install_dir: join_paths(get_option('datadir'), 'applications')
)
+
+desktop_utils = find_program('desktop-file-validate', required: false)
+if desktop_utils.found()
+ test('Validate desktop file', desktop_utils,
+ args: [desktop_file]
+ )
+endif
+
+appstream_file = i18n.merge_file(
+ input: 'io.github.mightycreak.Diffuse.metainfo.xml.in',
+ output: 'io.github.mightycreak.Diffuse.metainfo.xml',
+ po_dir: '../po',
+ install: true,
+ install_dir: join_paths(get_option('datadir'), 'appdata')
+)
+
+appstream_util = find_program('appstream-util', required: false)
+if appstream_util.found()
+ test('Validate appstream file', appstream_util,
+ args: ['validate', appstream_file]
+ )
+endif
+
+# Diffuse config file
+conf = configuration_data()
+conf.set('PKGDATADIR', pkgdatadir)
+
+configure_file(
+ input: 'diffuserc.in',
+ output: 'diffuserc',
+ configuration: conf,
+ install: true,
+ install_dir: get_option('sysconfdir')
+)
+
+# Data files
+install_subdir('usr/share', install_dir: get_option('datadir'), strip_directory: true)
+
+subdir('icons')
diff --git a/src/usr/share/diffuse/syntax/bash.syntax b/data/usr/share/diffuse/syntax/bash.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/bash.syntax
rename to data/usr/share/diffuse/syntax/bash.syntax
diff --git a/src/usr/share/diffuse/syntax/c++.syntax b/data/usr/share/diffuse/syntax/c++.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/c++.syntax
rename to data/usr/share/diffuse/syntax/c++.syntax
diff --git a/src/usr/share/diffuse/syntax/csh.syntax b/data/usr/share/diffuse/syntax/csh.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/csh.syntax
rename to data/usr/share/diffuse/syntax/csh.syntax
diff --git a/src/usr/share/diffuse/syntax/csharp.syntax b/data/usr/share/diffuse/syntax/csharp.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/csharp.syntax
rename to data/usr/share/diffuse/syntax/csharp.syntax
diff --git a/src/usr/share/diffuse/syntax/css.syntax b/data/usr/share/diffuse/syntax/css.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/css.syntax
rename to data/usr/share/diffuse/syntax/css.syntax
diff --git a/src/usr/share/diffuse/syntax/erlang.syntax b/data/usr/share/diffuse/syntax/erlang.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/erlang.syntax
rename to data/usr/share/diffuse/syntax/erlang.syntax
diff --git a/src/usr/share/diffuse/syntax/fortran.syntax b/data/usr/share/diffuse/syntax/fortran.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/fortran.syntax
rename to data/usr/share/diffuse/syntax/fortran.syntax
diff --git a/src/usr/share/diffuse/syntax/gettext.syntax b/data/usr/share/diffuse/syntax/gettext.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/gettext.syntax
rename to data/usr/share/diffuse/syntax/gettext.syntax
diff --git a/src/usr/share/diffuse/syntax/glsl.syntax b/data/usr/share/diffuse/syntax/glsl.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/glsl.syntax
rename to data/usr/share/diffuse/syntax/glsl.syntax
diff --git a/src/usr/share/diffuse/syntax/html.syntax b/data/usr/share/diffuse/syntax/html.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/html.syntax
rename to data/usr/share/diffuse/syntax/html.syntax
diff --git a/src/usr/share/diffuse/syntax/ini.syntax b/data/usr/share/diffuse/syntax/ini.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/ini.syntax
rename to data/usr/share/diffuse/syntax/ini.syntax
diff --git a/src/usr/share/diffuse/syntax/java.syntax b/data/usr/share/diffuse/syntax/java.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/java.syntax
rename to data/usr/share/diffuse/syntax/java.syntax
diff --git a/src/usr/share/diffuse/syntax/javascript.syntax b/data/usr/share/diffuse/syntax/javascript.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/javascript.syntax
rename to data/usr/share/diffuse/syntax/javascript.syntax
diff --git a/src/usr/share/diffuse/syntax/json.syntax b/data/usr/share/diffuse/syntax/json.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/json.syntax
rename to data/usr/share/diffuse/syntax/json.syntax
diff --git a/src/usr/share/diffuse/syntax/jsp.syntax b/data/usr/share/diffuse/syntax/jsp.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/jsp.syntax
rename to data/usr/share/diffuse/syntax/jsp.syntax
diff --git a/src/usr/share/diffuse/syntax/makefile.syntax b/data/usr/share/diffuse/syntax/makefile.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/makefile.syntax
rename to data/usr/share/diffuse/syntax/makefile.syntax
diff --git a/src/usr/share/diffuse/syntax/objective-c++.syntax b/data/usr/share/diffuse/syntax/objective-c++.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/objective-c++.syntax
rename to data/usr/share/diffuse/syntax/objective-c++.syntax
diff --git a/src/usr/share/diffuse/syntax/octave.syntax b/data/usr/share/diffuse/syntax/octave.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/octave.syntax
rename to data/usr/share/diffuse/syntax/octave.syntax
diff --git a/src/usr/share/diffuse/syntax/opencl.syntax b/data/usr/share/diffuse/syntax/opencl.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/opencl.syntax
rename to data/usr/share/diffuse/syntax/opencl.syntax
diff --git a/src/usr/share/diffuse/syntax/pascal.syntax b/data/usr/share/diffuse/syntax/pascal.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/pascal.syntax
rename to data/usr/share/diffuse/syntax/pascal.syntax
diff --git a/src/usr/share/diffuse/syntax/patch.syntax b/data/usr/share/diffuse/syntax/patch.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/patch.syntax
rename to data/usr/share/diffuse/syntax/patch.syntax
diff --git a/src/usr/share/diffuse/syntax/perl.syntax b/data/usr/share/diffuse/syntax/perl.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/perl.syntax
rename to data/usr/share/diffuse/syntax/perl.syntax
diff --git a/src/usr/share/diffuse/syntax/php.syntax b/data/usr/share/diffuse/syntax/php.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/php.syntax
rename to data/usr/share/diffuse/syntax/php.syntax
diff --git a/src/usr/share/diffuse/syntax/python.syntax b/data/usr/share/diffuse/syntax/python.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/python.syntax
rename to data/usr/share/diffuse/syntax/python.syntax
diff --git a/src/usr/share/diffuse/syntax/r.syntax b/data/usr/share/diffuse/syntax/r.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/r.syntax
rename to data/usr/share/diffuse/syntax/r.syntax
diff --git a/src/usr/share/diffuse/syntax/ruby.syntax b/data/usr/share/diffuse/syntax/ruby.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/ruby.syntax
rename to data/usr/share/diffuse/syntax/ruby.syntax
diff --git a/src/usr/share/diffuse/syntax/sql.syntax b/data/usr/share/diffuse/syntax/sql.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/sql.syntax
rename to data/usr/share/diffuse/syntax/sql.syntax
diff --git a/src/usr/share/diffuse/syntax/tcl.syntax b/data/usr/share/diffuse/syntax/tcl.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/tcl.syntax
rename to data/usr/share/diffuse/syntax/tcl.syntax
diff --git a/src/usr/share/diffuse/syntax/vb.syntax b/data/usr/share/diffuse/syntax/vb.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/vb.syntax
rename to data/usr/share/diffuse/syntax/vb.syntax
diff --git a/src/usr/share/diffuse/syntax/verilog.syntax b/data/usr/share/diffuse/syntax/verilog.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/verilog.syntax
rename to data/usr/share/diffuse/syntax/verilog.syntax
diff --git a/src/usr/share/diffuse/syntax/vhdl.syntax b/data/usr/share/diffuse/syntax/vhdl.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/vhdl.syntax
rename to data/usr/share/diffuse/syntax/vhdl.syntax
diff --git a/src/usr/share/diffuse/syntax/xml.syntax b/data/usr/share/diffuse/syntax/xml.syntax
similarity index 100%
rename from src/usr/share/diffuse/syntax/xml.syntax
rename to data/usr/share/diffuse/syntax/xml.syntax
diff --git a/src/usr/share/gnome/help/diffuse/C/diffuse.xml b/data/usr/share/gnome/help/diffuse/C/diffuse.xml
similarity index 100%
rename from src/usr/share/gnome/help/diffuse/C/diffuse.xml
rename to data/usr/share/gnome/help/diffuse/C/diffuse.xml
diff --git a/src/usr/share/gnome/help/diffuse/cs/diffuse.xml b/data/usr/share/gnome/help/diffuse/cs/diffuse.xml
similarity index 100%
rename from src/usr/share/gnome/help/diffuse/cs/diffuse.xml
rename to data/usr/share/gnome/help/diffuse/cs/diffuse.xml
diff --git a/src/usr/share/gnome/help/diffuse/it/diffuse.xml b/data/usr/share/gnome/help/diffuse/it/diffuse.xml
similarity index 100%
rename from src/usr/share/gnome/help/diffuse/it/diffuse.xml
rename to data/usr/share/gnome/help/diffuse/it/diffuse.xml
diff --git a/src/usr/share/gnome/help/diffuse/ru/diffuse.xml b/data/usr/share/gnome/help/diffuse/ru/diffuse.xml
similarity index 100%
rename from src/usr/share/gnome/help/diffuse/ru/diffuse.xml
rename to data/usr/share/gnome/help/diffuse/ru/diffuse.xml
diff --git a/src/usr/share/man/cs/man1/diffuse.1 b/data/usr/share/man/cs/man1/diffuse.1
similarity index 100%
rename from src/usr/share/man/cs/man1/diffuse.1
rename to data/usr/share/man/cs/man1/diffuse.1
diff --git a/src/usr/share/man/it/man1/diffuse.1 b/data/usr/share/man/it/man1/diffuse.1
similarity index 100%
rename from src/usr/share/man/it/man1/diffuse.1
rename to data/usr/share/man/it/man1/diffuse.1
diff --git a/src/usr/share/man/man1/diffuse.1 b/data/usr/share/man/man1/diffuse.1
similarity index 100%
rename from src/usr/share/man/man1/diffuse.1
rename to data/usr/share/man/man1/diffuse.1
diff --git a/src/usr/share/man/ru/man1/diffuse.1 b/data/usr/share/man/ru/man1/diffuse.1
similarity index 100%
rename from src/usr/share/man/ru/man1/diffuse.1
rename to data/usr/share/man/ru/man1/diffuse.1
diff --git a/src/usr/share/omf/diffuse/diffuse-C.omf b/data/usr/share/omf/diffuse/diffuse-C.omf
similarity index 100%
rename from src/usr/share/omf/diffuse/diffuse-C.omf
rename to data/usr/share/omf/diffuse/diffuse-C.omf
diff --git a/src/usr/share/omf/diffuse/diffuse-cs.omf b/data/usr/share/omf/diffuse/diffuse-cs.omf
similarity index 100%
rename from src/usr/share/omf/diffuse/diffuse-cs.omf
rename to data/usr/share/omf/diffuse/diffuse-cs.omf
diff --git a/src/usr/share/omf/diffuse/diffuse-it.omf b/data/usr/share/omf/diffuse/diffuse-it.omf
similarity index 100%
rename from src/usr/share/omf/diffuse/diffuse-it.omf
rename to data/usr/share/omf/diffuse/diffuse-it.omf
diff --git a/src/usr/share/omf/diffuse/diffuse-ru.omf b/data/usr/share/omf/diffuse/diffuse-ru.omf
similarity index 100%
rename from src/usr/share/omf/diffuse/diffuse-ru.omf
rename to data/usr/share/omf/diffuse/diffuse-ru.omf
diff --git a/io.github.mightycreak.Diffuse.yml b/io.github.mightycreak.Diffuse.yml
index a9cde15..288e50f 100644
--- a/io.github.mightycreak.Diffuse.yml
+++ b/io.github.mightycreak.Diffuse.yml
@@ -10,9 +10,14 @@ finish-args:
- --filesystem=home
modules:
- name: diffuse
+ builddir: true
buildsystem: meson
sources:
- - type: git
- url: https://github.com/MightyCreak/diffuse
- branch: v0.6.0
-rename-desktop-file: diffuse.desktop
+ - type: dir
+ path: .
+ # - type: git
+ # url: file:///home/creak/dev/diffuse
+ # branch: split-code-into-modules
+ # - type: git
+ # url: https://github.com/MightyCreak/diffuse
+ # branch: v0.6.0
diff --git a/meson.build b/meson.build
index 3a5589e..2545b38 100644
--- a/meson.build
+++ b/meson.build
@@ -1,29 +1,13 @@
project('diffuse',
- version: '0.6.0',
+ version: '0.7.0',
meson_version: '>= 0.50',
- license: 'GPL-2.0-or-later')
+ license: 'GPL-2.0-or-later',
+ default_options: [ 'warning_level=2' ])
i18n = import('i18n')
-python = import('python')
-py_installation = python.find_installation('python3')
-
-find_program('gtk-update-icon-cache', required: false)
-
-prefix = get_option('prefix')
-
-bindir = prefix / get_option('bindir')
-datadir = prefix / get_option('datadir')
-localedir = prefix / get_option('localedir')
-libexecdir = prefix / get_option('libexecdir')
-sysconfdir = prefix / get_option('sysconfdir')
-pythondir = py_installation.get_path('purelib')
-
-pkgdatadir = join_paths(datadir, meson.project_name())
-podir = join_paths(meson.source_root(), 'po')
-
-subdir('po')
subdir('data')
subdir('src')
+subdir('po')
-meson.add_install_script('build-scripts/meson-postinstall.py')
+meson.add_install_script('build-aux/meson/postinstall.py')
diff --git a/src/__init__.py b/src/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/diffuse.in b/src/diffuse.in
new file mode 100755
index 0000000..56da64c
--- /dev/null
+++ b/src/diffuse.in
@@ -0,0 +1,37 @@
+#!@PYTHON@
+
+# Diffuse: a graphical tool for merging and comparing text files.
+#
+# Copyright (C) 2019 Derrick Moser
+# Copyright (C) 2021 Romain Failliot
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import sys
+import gettext
+import locale
+
+VERSION = '@VERSION@'
+pkgdatadir = '@pkgdatadir@'
+localedir = '@localedir@'
+sysconfigdir = '@sysconfigdir@'
+
+sys.path.insert(1, pkgdatadir)
+gettext.install('diffuse', localedir)
+
+if __name__ == '__main__':
+ from diffuse import main
+
+ sys.exit(main.main(VERSION, sysconfigdir))
diff --git a/src/usr/bin/diffuse.py.in b/src/main.py
old mode 100755
new mode 100644
similarity index 97%
rename from src/usr/bin/diffuse.py.in
rename to src/main.py
index 96139b9..2c6b941
--- a/src/usr/bin/diffuse.py.in
+++ b/src/main.py
@@ -1,121 +1,34 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
+# Diffuse: a graphical tool for merging and comparing text files.
+#
+# Copyright (C) 2019 Derrick Moser
+# Copyright (C) 2021 Romain Failliot
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-# Copyright (C) 2006-2019 Derrick Moser
-# Copyright (C) 2015-2020 Romain Failliot
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the license, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program. You may also obtain a copy of the GNU General Public License
-# from the Free Software Foundation by visiting their web site
-# (http://www.fsf.org/) or by writing to the Free Software Foundation, Inc.,
-# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import codecs
-import gettext
-import locale
import os
import sys
-
-# use the program's location as a starting place to search for supporting files
-# such as icon and help documentation
-if hasattr(sys, 'frozen'):
- app_path = sys.executable
-else:
- app_path = os.path.realpath(sys.argv[0])
-bin_dir = os.path.dirname(app_path)
-
-# platform test
-def isWindows():
- return os.name == 'nt'
-
-# translation location: '../share/locale//LC_MESSAGES/diffuse.mo'
-# where '' is the language key
-lang = locale.getdefaultlocale()[0]
-if isWindows():
- # gettext looks for the language using environment variables which
- # are normally not set on Windows so we try setting it for them
- for v in 'LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG':
- if v in os.environ:
- lang = os.environ[v]
- # remove any additional languages, encodings, or modifications
- for v in ':.@':
- lang = lang.split(v)[0]
- break
- else:
- if lang is not None:
- os.environ['LANG'] = lang
- del v
- locale_dir = 'locale'
-else:
- locale_dir = '../share/locale'
-locale_dir = os.path.join(bin_dir, locale_dir)
-gettext.bindtextdomain('diffuse', locale_dir)
-
-gettext.textdomain('diffuse')
-_ = gettext.gettext
-
-APP_NAME = 'Diffuse'
-VERSION = '0.6.0'
-COPYRIGHT = '''{copyright} © 2006-2019 Derrick Moser
-{copyright} © 2015-2020 Romain Failliot'''.format(copyright=_("Copyright"))
-WEBSITE = 'https://github.com/MightyCreak/diffuse'
-
-# process help options
-if __name__ == '__main__':
- args = sys.argv
- argc = len(args)
- if argc == 2 and args[1] in [ '-v', '--version' ]:
- print(f'{APP_NAME} {VERSION}\n{COPYRIGHT}')
- sys.exit(0)
- if argc == 2 and args[1] in [ '-h', '-?', '--help' ]:
- print(_('''Usage:
- diffuse [ [OPTION...] [FILE...] ]...
- diffuse ( -h | -? | --help | -v | --version )
-
-Diffuse is a graphical tool for merging and comparing text files. Diffuse is
-able to compare an arbitrary number of files side-by-side and gives users the
-ability to manually adjust line matching and directly edit files. Diffuse can
-also retrieve revisions of files from Bazaar, CVS, Darcs, Git, Mercurial,
-Monotone, RCS, Subversion, and SVK repositories for comparison and merging.
-
-Help Options:
- ( -h | -? | --help ) Display this usage information
- ( -v | --version ) Display version and copyright information
-
-Configuration Options:
- --no-rcfile Do not read any resource files
- --rcfile Specify explicit resource file
-
-General Options:
- ( -c | --commit ) File revisions and
- ( -D | --close-if-same ) Close all tabs with no differences
- ( -e | --encoding ) Use to read and write files
- ( -L | --label ) Display instead of the file name
- ( -m | --modified ) Create a new tab for each modified file
- ( -r | --revision ) File revision
- ( -s | --separate ) Create a new tab for each file
- ( -t | --tab ) Start a new tab
- ( -V | --vcs ) Version control system search order
- --line Start with line selected
- --null-file Create a blank file comparison pane
-
-Display Options:
- ( -b | --ignore-space-change ) Ignore changes to white space
- ( -B | --ignore-blank-lines ) Ignore changes in blank lines
- ( -E | --ignore-end-of-line ) Ignore end of line differences
- ( -i | --ignore-case ) Ignore case differences
- ( -w | --ignore-all-space ) Ignore white space differences'''))
- sys.exit(0)
+import codecs
+import difflib
+import encodings
+import glob
+import re
+import shlex
+import stat
+import subprocess
+import unicodedata
+import webbrowser
import gi
@@ -137,27 +50,14 @@ from gi.repository import Pango
gi.require_version('PangoCairo', '1.0')
from gi.repository import PangoCairo
-import difflib
-import encodings
-import glob
-import re
-import shlex
-import stat
-import string
-import subprocess
-import unicodedata
-import webbrowser
-
from urllib.parse import urlparse
+from diffuse import utils
+
if not hasattr(__builtins__, 'WindowsError'):
# define 'WindowsError' so 'except' statements will work on all platforms
WindowsError = IOError
-# convenience function to display debug messages
-def logDebug(s):
- pass #sys.stderr.write(f'{APP_NAME}: {s}\n')
-
# avoid some dictionary lookups when string.whitespace is used in loops
# this is sorted based upon frequency to speed up code for stripping whitespace
whitespace = ' \t\n\r\x0b\x0c'
@@ -167,27 +67,6 @@ def globEscape(s):
m = dict([ (c, f'[{c}]') for c in '[]?*' ])
return ''.join([ m.get(c, c) for c in s ])
-# associate our icon with all of our windows
-if __name__ == '__main__':
- # this is not automatically set on some older version of PyGTK
- Gtk.Window.set_default_icon_name('diffuse')
-
-# convenience class for displaying a message dialogue
-class MessageDialog(Gtk.MessageDialog):
- def __init__(self, parent, type, s):
- if type == Gtk.MessageType.ERROR:
- buttons = Gtk.ButtonsType.OK
- else:
- buttons = Gtk.ButtonsType.OK_CANCEL
- Gtk.MessageDialog.__init__(self, parent = parent, destroy_with_parent = True, message_type = type, buttons = buttons, text = s)
- self.set_title(APP_NAME)
-
-# report error messages
-def logError(s):
- m = MessageDialog(None, Gtk.MessageType.ERROR, s)
- m.run()
- m.destroy()
-
# colour resources
class Colour:
def __init__(self, r, g, b, a=1.0):
@@ -544,7 +423,7 @@ class Resources:
try:
return self.colours[symbol]
except KeyError:
- logDebug(f'Warning: unknown colour "{symbol}"')
+ utils.logDebug(f'Warning: unknown colour "{symbol}"')
self.colours[symbol] = v = Colour(0.0, 0.0, 0.0)
return v
@@ -553,7 +432,7 @@ class Resources:
try:
return self.floats[symbol]
except KeyError:
- logDebug(f'Warning: unknown float "{symbol}"')
+ utils.logDebug(f'Warning: unknown float "{symbol}"')
self.floats[symbol] = v = 0.5
return v
@@ -562,7 +441,7 @@ class Resources:
try:
return self.strings[symbol]
except KeyError:
- logDebug(f'Warning: unknown string "{symbol}"')
+ utils.logDebug(f'Warning: unknown string "{symbol}"')
self.strings[symbol] = v = ''
return v
@@ -687,7 +566,7 @@ class Resources:
pass
else:
flags = 0
- if isWindows():
+ if utils.isWindows():
flags |= re.IGNORECASE
self.syntax_file_patterns[key] = re.compile(args[2], flags)
# eg. default to the Python syntax rules when viewing
@@ -713,7 +592,7 @@ class Resources:
raise ValueError()
except: # Grr... the 're' module throws weird errors
#except ValueError:
- logError(_('Error processing line %(line)d of %(file)s.') % { 'line': i + 1, 'file': file_name })
+ utils.logError(_('Error processing line %(line)d of %(file)s.') % { 'line': i + 1, 'file': file_name })
theResources = Resources()
@@ -799,7 +678,7 @@ class Preferences:
# find available encodings
self.encodings = sorted(set(encodings.aliases.aliases.values()))
- if isWindows():
+ if utils.isWindows():
svk_bin = 'svk.bat'
else:
svk_bin = 'svk'
@@ -860,7 +739,7 @@ class Preferences:
[ 'List',
[ 'Integer', 'tabs_default_panes', 2, _('Default panes'), 2, 16 ],
[ 'Boolean', 'tabs_always_show', False, _('Always show the tab bar') ],
- [ 'Boolean', 'tabs_warn_before_quit', True, _('Warn me when closing a tab will quit %s') % APP_NAME ]
+ [ 'Boolean', 'tabs_warn_before_quit', True, _('Warn me when closing a tab will quit %s') % utils.APP_NAME ]
],
_('Regional Settings'),
[ 'List',
@@ -878,7 +757,7 @@ class Preferences:
'align_ignore_blanklines': ('align_ignore_whitespace', True),
'align_ignore_endofline': ('align_ignore_whitespace', True)
}
- if isWindows():
+ if utils.isWindows():
root = os.environ.get('SYSTEMDRIVE', None)
if root is None:
root = 'C:\\'
@@ -914,7 +793,7 @@ class Preferences:
[ 'File', key + '_bin_rlog', 'rlog', _('"rlog" command') ] ])
else:
temp.extend([ [ 'File', key + '_bin', cmd, _('Command') ] ])
- if isWindows():
+ if utils.isWindows():
temp.append([ 'Boolean', key + '_bash', False, _('Launch from a Bash login shell') ])
if key != 'git':
temp.append([ 'Boolean', key + '_cygwin', False, _('Update paths for Cygwin') ])
@@ -949,10 +828,10 @@ class Preferences:
except ValueError:
# this may happen if the prefs were written by a
# different version -- don't bother the user
- logDebug(f'Error processing line {j + 1} of {self.path}.')
+ utils.logDebug(f'Error processing line {j + 1} of {self.path}.')
except IOError:
# bad $HOME value? -- don't bother the user
- logDebug(f'Error reading {self.path}.')
+ utils.logDebug(f'Error reading {self.path}.')
# recursively traverses 'template' to discover the preferences and
# initialise their default values in self.bool_prefs, self.int_prefs, and
@@ -1021,12 +900,12 @@ class Preferences:
ss.append(f'{k} "{v_escaped}"\n')
ss.sort()
f = open(self.path, 'w')
- f.write(f'# This prefs file was generated by {APP_NAME} {VERSION}.\n\n')
+ f.write(f'# This prefs file was generated by {utils.APP_NAME} {utils.VERSION}.\n\n')
for s in ss:
f.write(s)
f.close()
except IOError:
- m = MessageDialog(parent, Gtk.MessageType.ERROR, _('Error writing %s.') % (self.path, ))
+ m = utils.MessageDialog(parent, Gtk.MessageType.ERROR, _('Error writing %s.') % (self.path, ))
m.run()
m.destroy()
dialog.destroy()
@@ -1143,7 +1022,7 @@ class Preferences:
# cygwin and native applications can be used on windows, use this method
# to convert a path to the usual form expected on sys.platform
def convertToNativePath(self, s):
- if isWindows() and s.find('/') >= 0:
+ if utils.isWindows() and s.find('/') >= 0:
# treat as a cygwin path
s = s.replace(os.sep, '/')
# convert to a Windows native style path
@@ -1319,7 +1198,7 @@ def drive_from_path(s):
# constructs a relative path from 'a' to 'b', both should be absolute paths
def relpath(a, b):
- if isWindows():
+ if utils.isWindows():
if drive_from_path(a) != drive_from_path(b):
return b
c1 = [ c for c in a.split(os.sep) if c != '' ]
@@ -1335,7 +1214,7 @@ def relpath(a, b):
# by prepending './' to the basename
def safeRelativePath(abspath1, name, prefs, cygwin_pref):
s = os.path.join(os.curdir, relpath(abspath1, os.path.abspath(name)))
- if isWindows():
+ if utils.isWindows():
if prefs.getBool(cygwin_pref):
s = s.replace('\\', '/')
else:
@@ -1350,12 +1229,12 @@ def bashEscape(s):
def popenRead(dn, cmd, prefs, bash_pref, success_results=None):
if success_results is None:
success_results = [ 0 ]
- if isWindows() and prefs.getBool(bash_pref):
+ if utils.isWindows() and prefs.getBool(bash_pref):
# launch the command from a bash shell is requested
cmd = [ prefs.convertToNativePath('/bin/bash.exe'), '-l', '-c', 'cd {}; {}'.format(bashEscape(dn), ' '.join([ bashEscape(arg) for arg in cmd ])) ]
dn = None
# use subprocess.Popen to retrieve the file contents
- if isWindows():
+ if utils.isWindows():
info = subprocess.STARTUPINFO()
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
info.wShowWindow = subprocess.SW_HIDE
@@ -1653,7 +1532,7 @@ class _Cvs:
k0 = k
result.append([ (k0, prev), (k, rev) ])
except ValueError:
- logError(_('Error parsing revision %s.') % (rev, ))
+ utils.logError(_('Error parsing revision %s.') % (rev, ))
return result
def getFolderTemplate(self, prefs, names):
@@ -2270,7 +2149,7 @@ class _Rcs:
k0 = k
result.append([ (k0, prev), (k, rev) ])
except ValueError:
- logError(_('Error parsing revision %s.') % (rev, ))
+ utils.logError(_('Error parsing revision %s.') % (rev, ))
return result
def getFolderTemplate(self, prefs, names):
@@ -2414,7 +2293,7 @@ class _Svn:
try:
prev = self._getPreviousRevision(rev)
except ValueError:
- logError(_('Error parsing revision %s.') % (rev, ))
+ utils.logError(_('Error parsing revision %s.') % (rev, ))
return result
# build command
@@ -2599,7 +2478,7 @@ def _get_svk_repo(path, prefs):
name = path
# parse the ~/.svk/config file to discover which directories are part of
# SVK repositories
- if isWindows():
+ if utils.isWindows():
name = name.upper()
svkroot = os.environ.get('SVKROOT', None)
if svkroot is None:
@@ -2647,7 +2526,7 @@ def _get_svk_repo(path, prefs):
tt.append(key[j])
j += 1
key = ''.join(tt).replace(sep, os.sep)
- if isWindows():
+ if utils.isWindows():
key = key.upper()
projs.append(key)
break
@@ -2655,7 +2534,7 @@ def _get_svk_repo(path, prefs):
if _VcsFolderSet(projs).contains(name):
return _Svk(path)
except IOError:
- logError(_('Error parsing %s.') % (svkconfig, ))
+ utils.logError(_('Error parsing %s.') % (svkconfig, ))
class VCSs:
def __init__(self):
@@ -2912,7 +2791,7 @@ def path2url(path, proto='file'):
for c in s[i:]:
if c == os.sep:
c = '/'
- elif c == ':' and isWindows():
+ elif c == ':' and utils.isWindows():
c = '|'
else:
v = ord(c)
@@ -6669,7 +6548,7 @@ class SearchDialog(Gtk.Dialog):
# convenience method to request confirmation when closing the last tab
def confirmTabClose(parent):
- dialog = MessageDialog(parent, Gtk.MessageType.WARNING, _('Closing this tab will quit %s.') % (APP_NAME, ))
+ dialog = utils.MessageDialog(parent, Gtk.MessageType.WARNING, _('Closing this tab will quit %s.') % (utils.APP_NAME, ))
end = (dialog.run() == Gtk.ResponseType.OK)
dialog.destroy()
return end
@@ -6756,31 +6635,38 @@ class NumericDialog(Gtk.Dialog):
def url_hook(dialog, link, userdata):
webbrowser.open(link)
-# the about dialogue
+
+
+# the about dialog
class AboutDialog(Gtk.AboutDialog):
def __init__(self):
Gtk.AboutDialog.__init__(self)
- self.set_logo_icon_name('diffuse')
- if hasattr(self, 'set_program_name'):
- # only available in pygtk >= 2.12
- self.set_program_name(APP_NAME)
- self.set_version(VERSION)
+ self.set_logo_icon_name('io.github.mightycreak.Diffuse')
+ self.set_program_name(utils.APP_NAME)
+ self.set_version(utils.VERSION)
self.set_comments(_('Diffuse is a graphical tool for merging and comparing text files.'))
- self.set_copyright(COPYRIGHT)
- self.set_website(WEBSITE)
+ self.set_copyright(utils.COPYRIGHT)
+ self.set_website(utils.WEBSITE)
self.set_authors([ 'Derrick Moser ',
'Romain Failliot ' ])
self.set_translator_credits(_('translator-credits'))
- ss = [ APP_NAME + ' ' + VERSION + '\n',
- COPYRIGHT + '\n\n',
- _("""This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the licence, or (at your option) any later version.
+ license_text = [
+ utils.APP_NAME + ' ' + utils.VERSION + '\n\n',
+ utils.COPYRIGHT + '\n\n',
+ _('''This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
-This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
-You should have received a copy of the GNU General Public License along with this program. You may also obtain a copy of the GNU General Public License from the Free Software Foundation by visiting their web site (http://www.fsf.org/) or by writing to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-""") ]
- self.set_license(''.join(ss))
- self.set_wrap_license(True)
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.''') ]
+ self.set_license(''.join(license_text))
# widget classed to create notebook tabs with labels and a close button
# use notebooktab.button.connect() to be notified when the button is pressed
@@ -7012,7 +6898,7 @@ class Diffuse(Gtk.Window):
if self.headers[f].has_edits:
# warn users of any unsaved changes they might lose
dialog = Gtk.MessageDialog(self.get_toplevel(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.NONE, _('Save changes before loading the new file?'))
- dialog.set_title(APP_NAME)
+ dialog.set_title(utils.APP_NAME)
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
dialog.add_button(Gtk.STOCK_NO, Gtk.ResponseType.REJECT)
dialog.add_button(Gtk.STOCK_YES, Gtk.ResponseType.OK)
@@ -7124,7 +7010,7 @@ class Diffuse(Gtk.Window):
msg = _('Error reading revision %(rev)s of %(file)s.') % { 'rev': rev, 'file': name }
else:
msg = _('Error reading %s.') % (name, )
- dialog = MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, msg)
+ dialog = utils.MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, msg)
dialog.run()
dialog.destroy()
return
@@ -7197,7 +7083,7 @@ class Diffuse(Gtk.Window):
else:
s = info.name
msg = _('The file %s changed on disk. Do you want to reload the file?') % (s, )
- dialog = MessageDialog(self.get_toplevel(), Gtk.MessageType.QUESTION, msg)
+ dialog = utils.MessageDialog(self.get_toplevel(), Gtk.MessageType.QUESTION, msg)
ok = (dialog.run() == Gtk.ResponseType.OK)
dialog.destroy()
if ok:
@@ -7249,7 +7135,7 @@ class Diffuse(Gtk.Window):
if info.stat[stat.ST_MTIME] < os.stat(name)[stat.ST_MTIME]:
msg = _('The file %s has been modified by another process since reading it. If you save, all the external changes could be lost. Save anyways?') % (name, )
if msg is not None:
- dialog = MessageDialog(self.get_toplevel(), Gtk.MessageType.QUESTION, msg)
+ dialog = utils.MessageDialog(self.get_toplevel(), Gtk.MessageType.QUESTION, msg)
end = (dialog.run() != Gtk.ResponseType.OK)
dialog.destroy()
if end:
@@ -7288,11 +7174,11 @@ class Diffuse(Gtk.Window):
self.setSyntax(syntax)
return True
except (UnicodeEncodeError, LookupError):
- dialog = MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, _('Error encoding to %s.') % (encoding, ))
+ dialog = utils.MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, _('Error encoding to %s.') % (encoding, ))
dialog.run()
dialog.destroy()
except IOError:
- dialog = MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, _('Error writing %s.') % (name, ))
+ dialog = utils.MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, _('Error writing %s.') % (name, ))
dialog.run()
dialog.destroy()
return False
@@ -7558,7 +7444,7 @@ class Diffuse(Gtk.Window):
menuspecs.append([ _('_Help'), [
[_('_Help Contents...'), self.help_contents_cb, None, Gtk.STOCK_HELP, 'help_contents'],
[],
- [_('_About %s...') % (APP_NAME, ), self.about_cb, None, Gtk.STOCK_ABOUT, 'about'] ] ])
+ [_('_About %s...') % (utils.APP_NAME, ), self.about_cb, None, Gtk.STOCK_ABOUT, 'about'] ] ])
# used to disable menu events when switching tabs
self.menu_update_depth = 0
@@ -7655,10 +7541,10 @@ class Diffuse(Gtk.Window):
except ValueError:
# this may happen if the state was written by a
# different version -- don't bother the user
- logDebug(f'Error processing line {j + 1} of {statepath}.')
+ utils.logDebug(f'Error processing line {j + 1} of {statepath}.')
except IOError:
# bad $HOME value? -- don't bother the user
- logDebug(f'Error reading {statepath}.')
+ utils.logDebug(f'Error reading {statepath}.')
self.move(self.int_state['window_x'], self.int_state['window_y'])
self.resize(self.int_state['window_width'], self.int_state['window_height'])
@@ -7675,13 +7561,13 @@ class Diffuse(Gtk.Window):
ss.append(f'{k} {v}\n')
ss.sort()
f = open(statepath, 'w')
- f.write(f"# This state file was generated by {APP_NAME} {VERSION}.\n\n")
+ f.write(f"# This state file was generated by {utils.APP_NAME} {utils.VERSION}.\n\n")
for s in ss:
f.write(s)
f.close()
except IOError:
# bad $HOME value? -- don't bother the user
- logDebug(f'Error writing {statepath}.')
+ utils.logDebug(f'Error writing {statepath}.')
# select viewer for a newly selected file in the confirm close dialogue
def __confirmClose_row_activated_cb(self, tree, path, col, model):
@@ -7715,7 +7601,7 @@ class Diffuse(Gtk.Window):
buttons=Gtk.ButtonsType.NONE,
text=_('Some files have unsaved changes. Select the files to save before closing.'))
dialog.set_resizable(True)
- dialog.set_title(APP_NAME)
+ dialog.set_title(utils.APP_NAME)
# add list of files with unsaved changes
sw = Gtk.ScrolledWindow.new()
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
@@ -7802,7 +7688,7 @@ class Diffuse(Gtk.Window):
# update window's title
def updateTitle(self, viewer):
title = self.notebook.get_tab_label(viewer).get_text()
- self.set_title(f'{title} - {APP_NAME}')
+ self.set_title(f'{title} - {utils.APP_NAME}')
# update the message in the status bar
def setStatus(self, s):
@@ -7956,7 +7842,7 @@ class Diffuse(Gtk.Window):
viewer.load(i, FileInfo(name, encoding, vcs, rev))
viewer.setOptions(options)
except (IOError, OSError, WindowsError):
- dialog = MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, _('Error retrieving commits for %s.') % (dn, ))
+ dialog = utils.MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, _('Error retrieving commits for %s.') % (dn, ))
dialog.run()
dialog.destroy()
@@ -7987,7 +7873,7 @@ class Diffuse(Gtk.Window):
viewer.load(i, FileInfo(name, encoding, vcs, rev))
viewer.setOptions(options)
except (IOError, OSError, WindowsError):
- dialog = MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, _('Error retrieving modifications for %s.') % (dn, ))
+ dialog = utils.MessageDialog(self.get_toplevel(), Gtk.MessageType.ERROR, _('Error retrieving modifications for %s.') % (dn, ))
dialog.run()
dialog.destroy()
@@ -8048,7 +7934,7 @@ class Diffuse(Gtk.Window):
self.notebook.set_current_page(n)
self.getCurrentViewer().grab_focus()
else:
- m = MessageDialog(parent, Gtk.MessageType.ERROR, _('No modified files found.'))
+ m = utils.MessageDialog(parent, Gtk.MessageType.ERROR, _('No modified files found.'))
m.run()
m.destroy()
@@ -8068,7 +7954,7 @@ class Diffuse(Gtk.Window):
self.notebook.set_current_page(n)
self.getCurrentViewer().grab_focus()
else:
- m = MessageDialog(parent, Gtk.MessageType.ERROR, _('No committed files found.'))
+ m = utils.MessageDialog(parent, Gtk.MessageType.ERROR, _('No committed files found.'))
m.run()
m.destroy()
@@ -8172,7 +8058,7 @@ class Diffuse(Gtk.Window):
msg = _('Phrase not found. Continue from the end of the file?')
else:
msg = _('Phrase not found. Continue from the start of the file?')
- dialog = MessageDialog(self.get_toplevel(), Gtk.MessageType.QUESTION, msg)
+ dialog = utils.MessageDialog(self.get_toplevel(), Gtk.MessageType.QUESTION, msg)
dialog.set_default_response(Gtk.ResponseType.OK)
more = (dialog.run() == Gtk.ResponseType.OK)
dialog.destroy()
@@ -8238,15 +8124,15 @@ class Diffuse(Gtk.Window):
# display help documentation
def help_contents_cb(self, widget, data):
help_url = None
- if isWindows():
+ if utils.isWindows():
# help documentation is distributed as local HTML files
# search for localised manual first
parts = [ 'manual' ]
- if lang is not None:
+ if utils.lang is not None:
parts = [ 'manual' ]
- parts.extend(lang.split('_'))
+ parts.extend(utils.lang.split('_'))
while len(parts) > 0:
- help_file = os.path.join(bin_dir, '_'.join(parts) + '.html')
+ help_file = os.path.join(utils.bin_dir, '_'.join(parts) + '.html')
if os.path.isfile(help_file):
# we found a help file
help_url = path2url(help_file)
@@ -8264,11 +8150,11 @@ class Diffuse(Gtk.Window):
break
if browser is not None:
# find localised help file
- if lang is None:
+ if utils.lang is None:
parts = []
else:
- parts = lang.split('_')
- s = os.path.abspath(os.path.join(bin_dir, '../share/gnome/help/diffuse'))
+ parts = utils.lang.split('_')
+ s = os.path.abspath(os.path.join(utils.bin_dir, '../share/gnome/help/diffuse'))
while True:
if len(parts) > 0:
d = '_'.join(parts)
@@ -8286,10 +8172,10 @@ class Diffuse(Gtk.Window):
del parts[-1]
if help_url is None:
# no local help file is available, show on-line help
- help_url = WEBSITE + 'manual.html'
+ help_url = utils.WEBSITE + 'manual.html'
# ask for localised manual
- if lang is not None:
- help_url += '?lang=' + lang
+ if utils.lang is not None:
+ help_url += '?lang=' + utils.lang
# use a web browser to display the help documentation
webbrowser.open(help_url)
@@ -8307,31 +8193,70 @@ GObject.signal_new('reload', Diffuse.FileDiffViewer.PaneHeader, GObject.SignalFl
GObject.signal_new('save', Diffuse.FileDiffViewer.PaneHeader, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ())
GObject.signal_new('save-as', Diffuse.FileDiffViewer.PaneHeader, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ())
-# create nested subdirectories and return the complete path
-def make_subdirs(p, ss):
- for s in ss:
- p = os.path.join(p, s)
- if not os.path.exists(p):
- try:
- os.mkdir(p)
- except IOError:
- pass
- return p
+def main(version, sysconfigdir):
+ # app = Application()
+ # return app.run(sys.argv)
+
+ utils.VERSION = version
+
+ args = sys.argv
+ argc = len(args)
+
+ if argc == 2 and args[1] in [ '-v', '--version' ]:
+ print('%s %s\n%s' % (utils.APP_NAME, utils.VERSION, utils.COPYRIGHT))
+ sys.exit(0)
+ if argc == 2 and args[1] in [ '-h', '-?', '--help' ]:
+ print(_('''Usage:
+ diffuse [ [OPTION...] [FILE...] ]...
+ diffuse ( -h | -? | --help | -v | --version )
+
+Diffuse is a graphical tool for merging and comparing text files. Diffuse is
+able to compare an arbitrary number of files side-by-side and gives users the
+ability to manually adjust line matching and directly edit files. Diffuse can
+also retrieve revisions of files from Bazaar, CVS, Darcs, Git, Mercurial,
+Monotone, RCS, Subversion, and SVK repositories for comparison and merging.
+
+Help Options:
+ ( -h | -? | --help ) Display this usage information
+ ( -v | --version ) Display version and copyright information
+
+Configuration Options:
+ --no-rcfile Do not read any resource files
+ --rcfile Specify explicit resource file
+
+General Options:
+ ( -c | --commit ) File revisions and
+ ( -D | --close-if-same ) Close all tabs with no differences
+ ( -e | --encoding ) Use to read and write files
+ ( -L | --label ) Display instead of the file name
+ ( -m | --modified ) Create a new tab for each modified file
+ ( -r | --revision ) File revision
+ ( -s | --separate ) Create a new tab for each file
+ ( -t | --tab ) Start a new tab
+ ( -V | --vcs ) Version control system search order
+ --line Start with line selected
+ --null-file Create a blank file comparison pane
+
+Display Options:
+ ( -b | --ignore-space-change ) Ignore changes to white space
+ ( -B | --ignore-blank-lines ) Ignore changes in blank lines
+ ( -E | --ignore-end-of-line ) Ignore end of line differences
+ ( -i | --ignore-case ) Ignore case differences
+ ( -w | --ignore-all-space ) Ignore white space differences'''))
+ sys.exit(0)
-# process the command line arguments
-if __name__ == '__main__':
# find the config directory and create it if it didn't exist
rc_dir, subdirs = os.environ.get('XDG_CONFIG_HOME', None), ['diffuse']
if rc_dir is None:
rc_dir = os.path.expanduser('~')
subdirs.insert(0, '.config')
- rc_dir = make_subdirs(rc_dir, subdirs)
+ rc_dir = utils.make_subdirs(rc_dir, subdirs)
# find the local data directory and create it if it didn't exist
data_dir, subdirs = os.environ.get('XDG_DATA_HOME', None), ['diffuse']
if data_dir is None:
data_dir = os.path.expanduser('~')
subdirs[:0] = [ '.local', 'share' ]
- data_dir = make_subdirs(data_dir, subdirs)
+ data_dir = utils.make_subdirs(data_dir, subdirs)
# load resource files
i, rc_files = 1, []
if i < argc and args[i] == '--no-rcfile':
@@ -8342,10 +8267,10 @@ if __name__ == '__main__':
i += 1
else:
# parse system wide then personal initialisation files
- if isWindows():
- rc_file = os.path.join(bin_dir, 'diffuserc')
+ if utils.isWindows():
+ rc_file = os.path.join(utils.bin_dir, 'diffuserc')
else:
- rc_file = os.path.join(bin_dir, '@SYSCONFIGDIR@/diffuserc')
+ rc_file = os.path.join(utils.bin_dir, f'{sysconfigdir}/diffuserc')
for rc_file in rc_file, os.path.join(rc_dir, 'diffuserc'):
if os.path.isfile(rc_file):
rc_files.append(rc_file)
@@ -8354,11 +8279,14 @@ if __name__ == '__main__':
# reported with normalised file names
rc_file = os.path.abspath(rc_file)
try:
+ # diffuse.theResources.parse(rc_file) # Modularization
theResources.parse(rc_file)
except IOError:
- logError(_('Error reading %s.') % (rc_file, ))
+ utils.logError(_('Error reading %s.') % (rc_file, ))
+ # diff = diffuse.Diffuse(rc_dir) # Modularization
diff = Diffuse(rc_dir)
+
# load state
statepath = os.path.join(data_dir, 'state')
diff.loadState(statepath)
@@ -8437,7 +8365,7 @@ if __name__ == '__main__':
try:
options['line'] = int(args[i])
except ValueError:
- logError(_('Error parsing line number.'))
+ utils.logError(_('Error parsing line number.'))
elif arg == '--null-file':
# add a blank file pane
if mode == 'single' or mode == 'separate':
@@ -8447,14 +8375,14 @@ if __name__ == '__main__':
revs = []
had_specs = True
else:
- logError(_('Skipping unknown argument "%s".') % (args[i], ))
+ utils.logError(_('Skipping unknown argument "%s".') % (args[i], ))
else:
filename = diff.prefs.convertToNativePath(args[i])
if (mode == 'single' or mode == 'separate') and os.path.isdir(filename):
if len(specs) > 0:
filename = os.path.join(filename, os.path.basename(specs[-1][0]))
else:
- logError(_('Error processing argument "%s". Directory not expected.') % (args[i], ))
+ utils.logError(_('Error processing argument "%s". Directory not expected.') % (args[i], ))
filename = None
if filename is not None:
if len(revs) == 0:
@@ -8483,3 +8411,5 @@ if __name__ == '__main__':
Gtk.main()
# save state
diff.saveState(statepath)
+
+ return 0
diff --git a/src/meson.build b/src/meson.build
index 4657bc4..b93609c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,42 +1,28 @@
-# Diffuse binary file
-diffuse_conf = configuration_data()
-diffuse_conf.set('SYSCONFIGDIR', sysconfdir)
+pkgdatadir = join_paths(get_option('prefix'), get_option('datadir'), meson.project_name())
+moduledir = join_paths(pkgdatadir, meson.project_name())
+sysconfdir = join_paths(get_option('prefix'), get_option('sysconfdir'))
+
+python = import('python')
+
+conf = configuration_data()
+conf.set('PYTHON', python.find_installation('python3').path())
+conf.set('VERSION', meson.project_version())
+conf.set('localedir', join_paths(get_option('prefix'), get_option('localedir')))
+conf.set('pkgdatadir', pkgdatadir)
+conf.set('sysconfigdir', sysconfdir)
configure_file(
- input: 'usr/bin/diffuse.py.in',
+ input: 'diffuse.in',
output: 'diffuse',
- configuration: diffuse_conf,
+ configuration: conf,
install: true,
- install_dir: bindir
+ install_dir: get_option('bindir')
)
-# Diffuse config file
-diffuserc_conf = configuration_data()
-diffuserc_conf.set('PKGDATADIR', pkgdatadir)
+diffuse_sources = [
+ '__init__.py',
+ 'main.py',
+ 'utils.py',
+]
-configure_file(
- input: 'etc/diffuserc.py.in',
- output: 'diffuserc',
- configuration: diffuserc_conf,
- install: true,
- install_dir: sysconfdir
-)
-
-# Validate MetaInfo file
-metainfo_file = join_paths(meson.source_root(), 'src/usr/share/metainfo/io.github.mightycreak.Diffuse.metainfo.xml')
-ascli_exe = find_program('appstreamcli', required: false)
-if ascli_exe.found()
- test(
- 'validate metainfo file',
- ascli_exe,
- args: [
- 'validate',
- '--no-net',
- '--pedantic',
- metainfo_file
- ]
- )
-endif
-
-# Data files
-install_subdir('usr/share', install_dir: datadir, strip_directory: true)
+install_data(diffuse_sources, install_dir: moduledir)
diff --git a/src/utils.py b/src/utils.py
new file mode 100644
index 0000000..a6ffca9
--- /dev/null
+++ b/src/utils.py
@@ -0,0 +1,93 @@
+# Diffuse: a graphical tool for merging and comparing text files.
+#
+# Copyright (C) 2019 Derrick Moser
+# Copyright (C) 2021 Romain Failliot
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import os
+import sys
+import locale
+
+import gi
+
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# convenience class for displaying a message dialogue
+class MessageDialog(Gtk.MessageDialog):
+ def __init__(self, parent, type, s):
+ if type == Gtk.MessageType.ERROR:
+ buttons = Gtk.ButtonsType.OK
+ else:
+ buttons = Gtk.ButtonsType.OK_CANCEL
+ Gtk.MessageDialog.__init__(self, parent = parent, destroy_with_parent = True, message_type = type, buttons = buttons, text = s)
+ self.set_title(APP_NAME)
+
+# platform test
+def isWindows():
+ return os.name == 'nt'
+
+# convenience function to display debug messages
+def logDebug(s):
+ pass #sys.stderr.write(f'{APP_NAME}: {s}\n')
+
+# report error messages
+def logError(s):
+ m = MessageDialog(None, Gtk.MessageType.ERROR, s)
+ m.run()
+ m.destroy()
+
+# create nested subdirectories and return the complete path
+def make_subdirs(p, ss):
+ for s in ss:
+ p = os.path.join(p, s)
+ if not os.path.exists(p):
+ try:
+ os.mkdir(p)
+ except IOError:
+ pass
+ return p
+
+# use the program's location as a starting place to search for supporting files
+# such as icon and help documentation
+if hasattr(sys, 'frozen'):
+ app_path = sys.executable
+else:
+ app_path = os.path.realpath(sys.argv[0])
+bin_dir = os.path.dirname(app_path)
+
+# translation location: '../share/locale//LC_MESSAGES/diffuse.mo'
+# where '' is the language key
+lang = locale.getdefaultlocale()[0]
+if isWindows():
+ # gettext looks for the language using environment variables which
+ # are normally not set on Windows so we try setting it for them
+ for v in 'LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE':
+ if v in os.environ:
+ lang = os.environ[v]
+ # remove any additional languages, encodings, or modifications
+ for v in ':.@':
+ lang = lang.split(v)[0]
+ break
+ else:
+ if lang is not None:
+ os.environ['LANG'] = lang
+
+APP_NAME = 'Diffuse'
+VERSION = '0.0.0'
+COPYRIGHT = '''{copyright} © 2006-2019 Derrick Moser
+{copyright} © 2015-2021 Romain Failliot'''.format(copyright=_("Copyright"))
+WEBSITE = 'https://mightycreak.github.io/diffuse/'
diff --git a/windows-installer/build.py b/windows-installer/build.py
index d4c8b6b..9feece3 100755
--- a/windows-installer/build.py
+++ b/windows-installer/build.py
@@ -1,239 +1,239 @@
-# Copyright (C) 2006-2014 Derrick Moser
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the licence, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program. You may also obtain a copy of the GNU General Public License
-# from the Free Software Foundation by visiting their web site
-# (http://www.fsf.org/) or by writing to the Free Software Foundation, Inc.,
-# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-# This program builds a Windows installer for Diffuse.
-
-import codecs
-import glob
-import os
-import platform
-import subprocess
-import sys
-
-VERSION='0.6.0'
-PACKAGE='1'
-PLATFORM='win' + ''.join([ c for c in platform.architecture()[0] if c.isdigit() ])
-INSTALLER='diffuse-%s-%s.%s' % (VERSION, PACKAGE, PLATFORM)
-
-# makes a directory without complaining if it already exists
-def mkdir(s):
- if not os.path.isdir(s):
- os.mkdir(s)
-
-# copies a file to 'dest'
-def copyFile(src, dest, use_text_mode=False,enc=None):
- print 'copying "%s" to "%s"' % (src, dest)
- if use_text_mode:
- r, w = 'r', 'w'
- else:
- r, w = 'rb', 'wb'
- f = open(src, r)
- s = f.read()
- f.close()
- if enc is not None:
- s = codecs.encode(unicode(s, 'utf_8'), enc)
- f = open(dest, w)
- f.write(s)
- f.close()
-
-# recursively copies a directory to 'dest'
-def copyDir(src, dest):
- print 'copying "%s" to "%s"' % (src, dest)
- mkdir(dest)
- for f in os.listdir(src):
- s = os.path.join(src, f)
- d = os.path.join(dest, f)
- if os.path.isfile(s):
- copyFile(s, d)
- elif os.path.isdir(s):
- copyDir(s, d)
-
-# helper to clean up the resulting HTML
-def extract_tag(s, start, end):
- i = s.find(start)
- if i >= 0:
- pre = s[:i]
- i += len(start)
- j = s.find(end, i)
- if j >= 0:
- return pre, start, s[i:j], end, s[j+len(end):]
-
-#
-# Make sure we are in the correct directory.
-#
-
-path = os.path.dirname(sys.argv[0])
-if path != '':
- os.chdir(path)
-
-#
-# Build EXE versions of the Diffuse Python script.
-#
-
-# make a temp directory
-mkdir('temp')
-# copy script into temp directory under two names
-for p in 'temp\\diffuse.py', 'temp\\diffusew.pyw':
- copyFile('..\\src\\usr\\bin\\diffuse', p, True)
-
-# build executable in 'dist' from diffuse.py and diffusew.pyw
-args = [ sys.executable, 'setup.py', 'py2exe' ]
-if os.spawnv(os.P_WAIT, args[0], args) != 0:
- raise OSError('Could not run setup.py')
-
-# include Microsoft redistributables needed by Python 2.6 and above
-for f in 'msvcm90.dll', 'msvcp90.dll', 'msvcr90.dll':
- copyFile(os.path.join(os.environ['SYSTEMROOT'], 'WinSxS\\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375\\' + f), 'dist\\' + f)
-copyFile(os.path.join(os.environ['SYSTEMROOT'], 'WinSxS\\Manifests\\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375.manifest'), 'dist\\Microsoft.VC90.CRT.manifest')
-
-# include GTK dependencies
-gtk_dir = os.environ['GTK_BASEPATH']
-copyDir(os.path.join(gtk_dir, 'etc'), 'dist\\etc')
-copyDir(os.path.join(gtk_dir, 'lib'), 'dist\\lib')
-mkdir('dist\\share')
-copyDir(os.path.join(gtk_dir, 'share\\icons'), 'dist\\share\\icons')
-copyDir(os.path.join(gtk_dir, 'share\\themes'), 'dist\\share\\themes')
-
-#
-# Add all support files.
-#
-
-# syntax highlighting support
-mkdir('dist\\syntax')
-for p in glob.glob('..\\src\\usr\\share\\diffuse\\syntax\\*.syntax'):
- copyFile(p, os.path.join('dist\\syntax', os.path.basename(p)), True)
-copyFile('diffuserc', 'dist\\diffuserc')
-
-# application icon
-copyDir('..\\src\\usr\\share\\icons', 'dist\\share\\icons')
-
-# translations
-mkdir('dist\\share\\locale')
-locale_dir = os.path.join(gtk_dir, 'share\\locale')
-for s in glob.glob('..\\po\\*.po'):
- lang = s[16:-3]
- # Diffuse localisations
- print 'Compiling %s translation' % (lang, )
- d = 'dist'
- for p in [ 'locale', lang, 'LC_MESSAGES' ]:
- d = os.path.join(d, p)
- mkdir(d)
- d = os.path.join(d, 'diffuse.mo')
- if subprocess.Popen(['msgfmt', '-o', d, s]).wait() != 0:
- raise OSError('Failed to compile "%s" into "%s".' % (s, d))
- # GTK localisations
- d = os.path.join(locale_dir, lang)
- if os.path.isdir(d):
- copyDir(d, os.path.join('dist\\share\\locale', lang))
-
-#
-# Add all documentation.
-#
-
-# license and other documentation
-for p in 'AUTHORS', 'ChangeLog', 'COPYING', 'README':
- copyFile(os.path.join('..', p), os.path.join('dist', p + '.txt'), True)
-for p, enc in [ ('ChangeLog_ru', 'cp1251'), ('README_ru', 'cp1251') ]:
- copyFile(os.path.join('..', p), os.path.join('dist', p + '.txt'), True, enc)
-
-# fetch translations for English text hard coded in the stylesheets
-translations = {}
-f = open('translations.txt', 'rb')
-for v in f.read().split('\n'):
- v = v.split(':')
- if len(v) == 3:
- lang = v[0]
- if not translations.has_key(lang):
- translations[lang] = []
- translations[lang].append(v[1:])
-f.close()
-
-# convert the manual from DocBook to HTML
-d = '..\\src\\usr\\share\\gnome\\help\\diffuse'
-for lang in os.listdir(d):
- p = os.path.join(os.path.join(d, lang), 'diffuse.xml')
- if os.path.isfile(p):
- cmd = [ 'xsltproc', '/usr/share/sgml/docbook/xsl-stylesheets/html/docbook.xsl', p ]
- info = subprocess.STARTUPINFO()
- info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- info.wShowWindow = subprocess.SW_HIDE
- proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=info)
- proc.stdin.close()
- proc.stderr.close()
- fd = proc.stdout
- s = fd.read()
- fd.close()
- if proc.wait() != 0:
- raise OSError('Could not run xsltproc')
- # add link to style sheet
- s = s.replace('', ' ')
- s = s.replace('\n
', '')
- s = s.replace('\n
', '')
- # cleanup HTML to simpler UTF-8 form
- s = s.replace(' ', ' ')
- a, idx = [], 0
- while True:
- i = s.find('', idx)
- if i < 0:
- a.append(unicode(s[idx:], 'latin_1'))
- break
- a.append(unicode(s[idx:i], 'latin_1'))
- i += 2
- j = s.find(';', i)
- a.append(unichr(int(s[i:j])))
- idx = j + 1
- s = u''.join(a)
- s = codecs.encode(s, 'utf-8')
- # clean up translator credit portion
- div = extract_tag(s, '', '
')
- if div is not None:
- firstname = extract_tag(div[2], '', ' ')
- surname = extract_tag(div[2], '', ' ')
- contrib = extract_tag(div[2], '', ' ')
- email = extract_tag(div[2], '', '
')
- copyright = extract_tag(div[4], '', '
')
- if firstname is not None and surname is not None and contrib is not None and email is not None and copyright is not None:
- s = '%s%s%s: %s %s %s
%s' % (div[0], ''.join(copyright[:4]), contrib[2], firstname[2], surname[2], email[2], copyright[4])
- # translate extra text
- for k, v in translations.get(lang, []):
- s = s.replace(k, v)
- # save HTML version of the manual
- fn = 'manual'
- if lang != 'C':
- fn += '_' + lang
- # update the document language
- s = s.replace(' lang="en" ', ' lang="%s" ' % (lang,))
- f = open(os.path.join('dist', fn + '.html'), 'w')
- f.write(s)
- f.close()
-copyFile('style.css', 'dist\\style.css')
-
-#
-# Package everything into a single EXE installer.
-#
-
-# build binary installer
-copyFile(os.path.join(os.environ['ADD_PATH_HOME'], 'add_path.exe'), 'dist\\add_path.exe')
-if os.system('iscc diffuse.iss /F%s' % (INSTALLER, )) != 0:
- raise OSError('Could not run iscc')
-
-#
-# Declare success.
-#
-
-print 'Successfully created "%s".' % (INSTALLER, )
+# Copyright (C) 2006-2014 Derrick Moser
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the licence, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. You may also obtain a copy of the GNU General Public License
+# from the Free Software Foundation by visiting their web site
+# (http://www.fsf.org/) or by writing to the Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# This program builds a Windows installer for Diffuse.
+
+import codecs
+import glob
+import os
+import platform
+import subprocess
+import sys
+
+VERSION='0.6.0'
+PACKAGE='1'
+PLATFORM='win' + ''.join([ c for c in platform.architecture()[0] if c.isdigit() ])
+INSTALLER='diffuse-%s-%s.%s' % (VERSION, PACKAGE, PLATFORM)
+
+# makes a directory without complaining if it already exists
+def mkdir(s):
+ if not os.path.isdir(s):
+ os.mkdir(s)
+
+# copies a file to 'dest'
+def copyFile(src, dest, use_text_mode=False,enc=None):
+ print 'copying "%s" to "%s"' % (src, dest)
+ if use_text_mode:
+ r, w = 'r', 'w'
+ else:
+ r, w = 'rb', 'wb'
+ f = open(src, r)
+ s = f.read()
+ f.close()
+ if enc is not None:
+ s = codecs.encode(unicode(s, 'utf_8'), enc)
+ f = open(dest, w)
+ f.write(s)
+ f.close()
+
+# recursively copies a directory to 'dest'
+def copyDir(src, dest):
+ print 'copying "%s" to "%s"' % (src, dest)
+ mkdir(dest)
+ for f in os.listdir(src):
+ s = os.path.join(src, f)
+ d = os.path.join(dest, f)
+ if os.path.isfile(s):
+ copyFile(s, d)
+ elif os.path.isdir(s):
+ copyDir(s, d)
+
+# helper to clean up the resulting HTML
+def extract_tag(s, start, end):
+ i = s.find(start)
+ if i >= 0:
+ pre = s[:i]
+ i += len(start)
+ j = s.find(end, i)
+ if j >= 0:
+ return pre, start, s[i:j], end, s[j+len(end):]
+
+#
+# Make sure we are in the correct directory.
+#
+
+path = os.path.dirname(sys.argv[0])
+if path != '':
+ os.chdir(path)
+
+#
+# Build EXE versions of the Diffuse Python script.
+#
+
+# make a temp directory
+mkdir('temp')
+# copy script into temp directory under two names
+for p in 'temp\\diffuse.py', 'temp\\diffusew.pyw':
+ copyFile('..\\src\\usr\\bin\\diffuse', p, True)
+
+# build executable in 'dist' from diffuse.py and diffusew.pyw
+args = [ sys.executable, 'setup.py', 'py2exe' ]
+if os.spawnv(os.P_WAIT, args[0], args) != 0:
+ raise OSError('Could not run setup.py')
+
+# include Microsoft redistributables needed by Python 2.6 and above
+for f in 'msvcm90.dll', 'msvcp90.dll', 'msvcr90.dll':
+ copyFile(os.path.join(os.environ['SYSTEMROOT'], 'WinSxS\\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375\\' + f), 'dist\\' + f)
+copyFile(os.path.join(os.environ['SYSTEMROOT'], 'WinSxS\\Manifests\\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375.manifest'), 'dist\\Microsoft.VC90.CRT.manifest')
+
+# include GTK dependencies
+gtk_dir = os.environ['GTK_BASEPATH']
+copyDir(os.path.join(gtk_dir, 'etc'), 'dist\\etc')
+copyDir(os.path.join(gtk_dir, 'lib'), 'dist\\lib')
+mkdir('dist\\share')
+copyDir(os.path.join(gtk_dir, 'share\\icons'), 'dist\\share\\icons')
+copyDir(os.path.join(gtk_dir, 'share\\themes'), 'dist\\share\\themes')
+
+#
+# Add all support files.
+#
+
+# syntax highlighting support
+mkdir('dist\\syntax')
+for p in glob.glob('..\\src\\usr\\share\\diffuse\\syntax\\*.syntax'):
+ copyFile(p, os.path.join('dist\\syntax', os.path.basename(p)), True)
+copyFile('diffuserc', 'dist\\diffuserc')
+
+# application icon
+copyDir('..\\src\\usr\\share\\icons', 'dist\\share\\icons')
+
+# translations
+mkdir('dist\\share\\locale')
+locale_dir = os.path.join(gtk_dir, 'share\\locale')
+for s in glob.glob('..\\po\\*.po'):
+ lang = s[16:-3]
+ # Diffuse localisations
+ print 'Compiling %s translation' % (lang, )
+ d = 'dist'
+ for p in [ 'locale', lang, 'LC_MESSAGES' ]:
+ d = os.path.join(d, p)
+ mkdir(d)
+ d = os.path.join(d, 'diffuse.mo')
+ if subprocess.Popen(['msgfmt', '-o', d, s]).wait() != 0:
+ raise OSError('Failed to compile "%s" into "%s".' % (s, d))
+ # GTK localisations
+ d = os.path.join(locale_dir, lang)
+ if os.path.isdir(d):
+ copyDir(d, os.path.join('dist\\share\\locale', lang))
+
+#
+# Add all documentation.
+#
+
+# license and other documentation
+for p in 'AUTHORS', 'ChangeLog', 'COPYING', 'README':
+ copyFile(os.path.join('..', p), os.path.join('dist', p + '.txt'), True)
+for p, enc in [ ('ChangeLog_ru', 'cp1251'), ('README_ru', 'cp1251') ]:
+ copyFile(os.path.join('..', p), os.path.join('dist', p + '.txt'), True, enc)
+
+# fetch translations for English text hard coded in the stylesheets
+translations = {}
+f = open('translations.txt', 'rb')
+for v in f.read().split('\n'):
+ v = v.split(':')
+ if len(v) == 3:
+ lang = v[0]
+ if not translations.has_key(lang):
+ translations[lang] = []
+ translations[lang].append(v[1:])
+f.close()
+
+# convert the manual from DocBook to HTML
+d = '..\\src\\usr\\share\\gnome\\help\\diffuse'
+for lang in os.listdir(d):
+ p = os.path.join(os.path.join(d, lang), 'diffuse.xml')
+ if os.path.isfile(p):
+ cmd = [ 'xsltproc', '/usr/share/sgml/docbook/xsl-stylesheets/html/docbook.xsl', p ]
+ info = subprocess.STARTUPINFO()
+ info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+ info.wShowWindow = subprocess.SW_HIDE
+ proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=info)
+ proc.stdin.close()
+ proc.stderr.close()
+ fd = proc.stdout
+ s = fd.read()
+ fd.close()
+ if proc.wait() != 0:
+ raise OSError('Could not run xsltproc')
+ # add link to style sheet
+ s = s.replace('', ' ')
+ s = s.replace('\n
', '')
+ s = s.replace('\n
', '')
+ # cleanup HTML to simpler UTF-8 form
+ s = s.replace(' ', ' ')
+ a, idx = [], 0
+ while True:
+ i = s.find('', idx)
+ if i < 0:
+ a.append(unicode(s[idx:], 'latin_1'))
+ break
+ a.append(unicode(s[idx:i], 'latin_1'))
+ i += 2
+ j = s.find(';', i)
+ a.append(unichr(int(s[i:j])))
+ idx = j + 1
+ s = ''.join(a)
+ s = codecs.encode(s, 'utf-8')
+ # clean up translator credit portion
+ div = extract_tag(s, '', '
')
+ if div is not None:
+ firstname = extract_tag(div[2], '', ' ')
+ surname = extract_tag(div[2], '', ' ')
+ contrib = extract_tag(div[2], '', ' ')
+ email = extract_tag(div[2], '', '
')
+ copyright = extract_tag(div[4], '', '
')
+ if firstname is not None and surname is not None and contrib is not None and email is not None and copyright is not None:
+ s = '%s%s%s: %s %s %s
%s' % (div[0], ''.join(copyright[:4]), contrib[2], firstname[2], surname[2], email[2], copyright[4])
+ # translate extra text
+ for k, v in translations.get(lang, []):
+ s = s.replace(k, v)
+ # save HTML version of the manual
+ fn = 'manual'
+ if lang != 'C':
+ fn += '_' + lang
+ # update the document language
+ s = s.replace(' lang="en" ', ' lang="%s" ' % (lang,))
+ f = open(os.path.join('dist', fn + '.html'), 'w')
+ f.write(s)
+ f.close()
+copyFile('style.css', 'dist\\style.css')
+
+#
+# Package everything into a single EXE installer.
+#
+
+# build binary installer
+copyFile(os.path.join(os.environ['ADD_PATH_HOME'], 'add_path.exe'), 'dist\\add_path.exe')
+if os.system('iscc diffuse.iss /F%s' % (INSTALLER, )) != 0:
+ raise OSError('Could not run iscc')
+
+#
+# Declare success.
+#
+
+print 'Successfully created "%s".' % (INSTALLER, )