Use GTK3's GApplication/GtkApplication #178 (#181)

* Use GTK3's GApplication/GtkApplication #178
* fix: remove icons in menu
* doc: update CHANGELOG.md
* chore(i18n): update po/diffuse.pot

Co-authored-by: Yurii Zolotko <yurii.zolotko@pm.me>
Co-authored-by: Romain Failliot <romain.failliot@foolstep.com>
This commit is contained in:
Iurii Zolotko 2022-11-22 02:01:49 +07:00 committed by GitHub
parent c1cddb755b
commit 8e32f883ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 731 additions and 601 deletions

View File

@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Translation: updated Swedish translation (@eson57)
- Dialog: prompt only once if several files needs to be reloaded (@yuriiz)
- Use GTK3's Gtk.Application/Gtk.ApplicationWindow (@yuriiz)
### Fixed

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-23 20:00-0400\n"
"POT-Creation-Date: 2022-11-14 16:24-0500\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"
@ -42,604 +42,656 @@ msgstr ""
msgid "Copyright"
msgstr ""
#: src/diffuse/dialogs.py:40
#: src/diffuse/dialogs.py:41
msgid "Diffuse is a graphical tool for merging and comparing text files."
msgstr ""
#: src/diffuse/dialogs.py:45
#: src/diffuse/dialogs.py:46
msgid "translator-credits"
msgstr ""
#: src/diffuse/dialogs.py:82
#: src/diffuse/dialogs.py:83
msgid "Encoding: "
msgstr ""
#: src/diffuse/dialogs.py:94
#: src/diffuse/dialogs.py:95
msgid "Revision: "
msgstr ""
#: src/diffuse/dialogs.py:156
#: src/diffuse/dialogs.py:163
msgid "Find..."
msgstr ""
#: src/diffuse/dialogs.py:164
#: src/diffuse/dialogs.py:173
msgid "Search For: "
msgstr ""
#: src/diffuse/dialogs.py:189
#: src/diffuse/dialogs.py:198
msgid "Match Case"
msgstr ""
#: src/diffuse/dialogs.py:194
#: src/diffuse/dialogs.py:203
msgid "Search Backwards"
msgstr ""
#: src/diffuse/main.py:85
#: src/diffuse/main.py:84
msgid "Close Tab"
msgstr ""
#: src/diffuse/main.py:132
#: src/diffuse/main.py:131
msgid "Open File..."
msgstr ""
#: src/diffuse/main.py:133
#: src/diffuse/main.py:132
msgid "Reload File"
msgstr ""
#: src/diffuse/main.py:134
#: src/diffuse/main.py:133
msgid "Save File"
msgstr ""
#: src/diffuse/main.py:135
#: src/diffuse/main.py:134
msgid "Save File As..."
msgstr ""
#: src/diffuse/main.py:217
#: src/diffuse/main.py:229
#, python-format
msgid "Column %d"
msgstr ""
#: src/diffuse/main.py:291
#: src/diffuse/main.py:303
msgid "Save changes before loading the new file?"
msgstr ""
#: src/diffuse/main.py:402
#: src/diffuse/main.py:414
#, python-format
msgid "Error reading revision %(rev)s of %(file)s."
msgstr ""
#: src/diffuse/main.py:405 src/diffuse/main.py:1860
#: src/diffuse/main.py:417 src/diffuse/main.py:2004
#, python-format
msgid "Error reading %s."
msgstr ""
#: src/diffuse/main.py:427
#: src/diffuse/main.py:439
msgid "Open File"
msgstr ""
#: src/diffuse/main.py:484
#, python-format
msgid "The file %s changed on disk. Do you want to reload the file?"
msgstr ""
#: src/diffuse/main.py:510
#: src/diffuse/main.py:492
#, python-format
msgid "Save %(title)s Pane %(pane)d"
msgstr ""
#: src/diffuse/main.py:542
#: src/diffuse/main.py:524
#, python-format
msgid "A file named %s already exists. Do you want to overwrite it?"
msgstr ""
#: src/diffuse/main.py:549
#: src/diffuse/main.py:531
#, python-format
msgid ""
"The file %s has been modified by another process since reading it. If you save, "
"all the external changes could be lost. Save anyways?"
msgstr ""
#: src/diffuse/main.py:591
#: src/diffuse/main.py:573
#, python-format
msgid "Error encoding to %s."
msgstr ""
#: src/diffuse/main.py:595 src/diffuse/preferences.py:300
#: src/diffuse/main.py:577 src/diffuse/preferences.py:300
#, python-format
msgid "Error writing %s."
msgstr ""
#: src/diffuse/main.py:625
#: src/diffuse/main.py:607
msgid "Go To Line..."
msgstr ""
#: src/diffuse/main.py:626
#: src/diffuse/main.py:608
msgid "Line Number: "
msgstr ""
#: src/diffuse/main.py:657
#: src/diffuse/main.py:639
msgid ""
"Press the enter key or double click to edit. Press the space bar or use the RMB "
"menu to manually align."
msgstr ""
#: src/diffuse/main.py:661
#: src/diffuse/main.py:643
msgid "Press the escape key to finish editing."
msgstr ""
#: src/diffuse/main.py:664
#: src/diffuse/main.py:646
msgid ""
"Select target line and press the space bar to align. Press the escape key to "
"cancel."
msgstr ""
#: src/diffuse/main.py:787
#: src/diffuse/main.py:768
msgid "_File"
msgstr ""
#: src/diffuse/main.py:788
#: src/diffuse/main.py:770
msgid "_Open File..."
msgstr ""
#: src/diffuse/main.py:789
#: src/diffuse/main.py:771
msgid "Open File In New _Tab..."
msgstr ""
#: src/diffuse/main.py:790
#: src/diffuse/main.py:772
msgid "Open _Modified Files..."
msgstr ""
#: src/diffuse/main.py:791
#: src/diffuse/main.py:773
msgid "Open Commi_t..."
msgstr ""
#: src/diffuse/main.py:792
#: src/diffuse/main.py:774
msgid "_Reload File"
msgstr ""
#: src/diffuse/main.py:794
#: src/diffuse/main.py:776
msgid "_Save File"
msgstr ""
#: src/diffuse/main.py:795
#: src/diffuse/main.py:777
msgid "Save File _As..."
msgstr ""
#: src/diffuse/main.py:796
#: src/diffuse/main.py:778
msgid "Save A_ll"
msgstr ""
#: src/diffuse/main.py:798
#: src/diffuse/main.py:780
msgid "New _2-Way File Merge"
msgstr ""
#: src/diffuse/main.py:799
#: src/diffuse/main.py:781
msgid "New _3-Way File Merge"
msgstr ""
#: src/diffuse/main.py:800
#: src/diffuse/main.py:782
msgid "New _N-Way File Merge..."
msgstr ""
#: src/diffuse/main.py:802
#: src/diffuse/main.py:784
msgid "_Close Tab"
msgstr ""
#: src/diffuse/main.py:803
#: src/diffuse/main.py:785
msgid "_Undo Close Tab"
msgstr ""
#: src/diffuse/main.py:804
#: src/diffuse/main.py:786
msgid "_Quit"
msgstr ""
#: src/diffuse/main.py:807
#: src/diffuse/main.py:790
msgid "_Edit"
msgstr ""
#: src/diffuse/main.py:808
#: src/diffuse/main.py:792
msgid "_Undo"
msgstr ""
#: src/diffuse/main.py:809
#: src/diffuse/main.py:793
msgid "_Redo"
msgstr ""
#: src/diffuse/main.py:811
#: src/diffuse/main.py:795
msgid "Cu_t"
msgstr ""
#: src/diffuse/main.py:812
#: src/diffuse/main.py:796
msgid "_Copy"
msgstr ""
#: src/diffuse/main.py:813
#: src/diffuse/main.py:797
msgid "_Paste"
msgstr ""
#: src/diffuse/main.py:815
#: src/diffuse/main.py:799
msgid "Select _All"
msgstr ""
#: src/diffuse/main.py:816
#: src/diffuse/main.py:800
msgid "C_lear Edits"
msgstr ""
#: src/diffuse/main.py:817
#: src/diffuse/main.py:801
msgid "_Dismiss All Edits"
msgstr ""
#: src/diffuse/main.py:819
#: src/diffuse/main.py:803
msgid "_Find..."
msgstr ""
#: src/diffuse/main.py:820
#: src/diffuse/main.py:804
msgid "Find _Next"
msgstr ""
#: src/diffuse/main.py:821
#: src/diffuse/main.py:805
msgid "Find Pre_vious"
msgstr ""
#: src/diffuse/main.py:822
#: src/diffuse/main.py:806
msgid "_Go To Line..."
msgstr ""
#: src/diffuse/main.py:824
#: src/diffuse/main.py:808
msgid "Pr_eferences..."
msgstr ""
#: src/diffuse/main.py:828
#: src/diffuse/main.py:812
msgid "None"
msgstr ""
#: src/diffuse/main.py:846
#: src/diffuse/main.py:834
msgid "_View"
msgstr ""
#: src/diffuse/main.py:847
#: src/diffuse/main.py:836
msgid "_Syntax Highlighting"
msgstr ""
#: src/diffuse/main.py:849
#: src/diffuse/main.py:838
msgid "Re_align All"
msgstr ""
#: src/diffuse/main.py:850
#: src/diffuse/main.py:839
msgid "_Isolate"
msgstr ""
#: src/diffuse/main.py:852
#: src/diffuse/main.py:841
msgid "_First Difference"
msgstr ""
#: src/diffuse/main.py:853
#: src/diffuse/main.py:842
msgid "_Previous Difference"
msgstr ""
#: src/diffuse/main.py:854
#: src/diffuse/main.py:843
msgid "_Next Difference"
msgstr ""
#: src/diffuse/main.py:855
#: src/diffuse/main.py:844
msgid "_Last Difference"
msgstr ""
#: src/diffuse/main.py:857
#: src/diffuse/main.py:846
msgid "Fir_st Tab"
msgstr ""
#: src/diffuse/main.py:858
#: src/diffuse/main.py:847
msgid "Pre_vious Tab"
msgstr ""
#: src/diffuse/main.py:859
#: src/diffuse/main.py:848
msgid "Next _Tab"
msgstr ""
#: src/diffuse/main.py:860
#: src/diffuse/main.py:849
msgid "Las_t Tab"
msgstr ""
#: src/diffuse/main.py:862
#: src/diffuse/main.py:851
msgid "Shift Pane _Right"
msgstr ""
#: src/diffuse/main.py:863
#: src/diffuse/main.py:852
msgid "Shift Pane _Left"
msgstr ""
#: src/diffuse/main.py:866
#: src/diffuse/main.py:856
msgid "F_ormat"
msgstr ""
#: src/diffuse/main.py:867
#: src/diffuse/main.py:858
msgid "Convert To _Upper Case"
msgstr ""
#: src/diffuse/main.py:868
#: src/diffuse/main.py:859
msgid "Convert To _Lower Case"
msgstr ""
#: src/diffuse/main.py:870
#: src/diffuse/main.py:861
msgid "Sort Lines In _Ascending Order"
msgstr ""
#: src/diffuse/main.py:871
#: src/diffuse/main.py:862
msgid "Sort Lines In D_escending Order"
msgstr ""
#: src/diffuse/main.py:873
#: src/diffuse/main.py:864
msgid "Remove Trailing _White Space"
msgstr ""
#: src/diffuse/main.py:874
#: src/diffuse/main.py:865
msgid "Convert Tabs To _Spaces"
msgstr ""
#: src/diffuse/main.py:875
#: src/diffuse/main.py:866
msgid "Convert Leading Spaces To _Tabs"
msgstr ""
#: src/diffuse/main.py:877
#: src/diffuse/main.py:868
msgid "_Increase Indenting"
msgstr ""
#: src/diffuse/main.py:878
#: src/diffuse/main.py:869
msgid "De_crease Indenting"
msgstr ""
#: src/diffuse/main.py:880
#: src/diffuse/main.py:871
msgid "Convert To _DOS Format"
msgstr ""
#: src/diffuse/main.py:881
#: src/diffuse/main.py:872
msgid "Convert To _Mac Format"
msgstr ""
#: src/diffuse/main.py:882
#: src/diffuse/main.py:873
msgid "Convert To Uni_x Format"
msgstr ""
#: src/diffuse/main.py:885
#: src/diffuse/main.py:877
msgid "_Merge"
msgstr ""
#: src/diffuse/main.py:886
#: src/diffuse/main.py:879
msgid "Copy Selection _Right"
msgstr ""
#: src/diffuse/main.py:887
#: src/diffuse/main.py:880
msgid "Copy Selection _Left"
msgstr ""
#: src/diffuse/main.py:889
#: src/diffuse/main.py:882
msgid "Copy Left _Into Selection"
msgstr ""
#: src/diffuse/main.py:890
#: src/diffuse/main.py:883
msgid "Copy Right I_nto Selection"
msgstr ""
#: src/diffuse/main.py:891
#: src/diffuse/main.py:884
msgid "_Merge From Left Then Right"
msgstr ""
#: src/diffuse/main.py:892
#: src/diffuse/main.py:885
msgid "M_erge From Right Then Left"
msgstr ""
#: src/diffuse/main.py:895
#: src/diffuse/main.py:889
msgid "_Help"
msgstr ""
#: src/diffuse/main.py:896
#: src/diffuse/main.py:891
msgid "_Help Contents..."
msgstr ""
#: src/diffuse/main.py:898
#: src/diffuse/main.py:893
#, python-format
msgid "_About %s..."
msgstr ""
#: src/diffuse/main.py:913
#: src/diffuse/main.py:908
msgid "New 2-Way File Merge"
msgstr ""
#: src/diffuse/main.py:914
#: src/diffuse/main.py:909
msgid "New 3-Way File Merge"
msgstr ""
#: src/diffuse/main.py:916
#: src/diffuse/main.py:911
msgid "Realign All"
msgstr ""
#: src/diffuse/main.py:917
#: src/diffuse/main.py:912
msgid "First Difference"
msgstr ""
#: src/diffuse/main.py:918
#: src/diffuse/main.py:913
msgid "Previous Difference"
msgstr ""
#: src/diffuse/main.py:919
#: src/diffuse/main.py:914
msgid "Next Difference"
msgstr ""
#: src/diffuse/main.py:920
#: src/diffuse/main.py:915
msgid "Last Difference"
msgstr ""
#: src/diffuse/main.py:922
#: src/diffuse/main.py:917
msgid "Copy Selection Right"
msgstr ""
#: src/diffuse/main.py:923
#: src/diffuse/main.py:918
msgid "Copy Selection Left"
msgstr ""
#: src/diffuse/main.py:924
#: src/diffuse/main.py:919
msgid "Copy Left Into Selection"
msgstr ""
#: src/diffuse/main.py:925
#: src/diffuse/main.py:920
msgid "Copy Right Into Selection"
msgstr ""
#: src/diffuse/main.py:926
#: src/diffuse/main.py:921
msgid "Merge From Left Then Right"
msgstr ""
#: src/diffuse/main.py:927
#: src/diffuse/main.py:922
msgid "Merge From Right Then Left"
msgstr ""
#: src/diffuse/main.py:929
#: src/diffuse/main.py:924
msgid "Undo"
msgstr ""
#: src/diffuse/main.py:930
#: src/diffuse/main.py:925
msgid "Redo"
msgstr ""
#: src/diffuse/main.py:931 src/diffuse/widgets.py:1861
#: src/diffuse/main.py:926 src/diffuse/widgets.py:1861
msgid "Cut"
msgstr ""
#: src/diffuse/main.py:932 src/diffuse/widgets.py:1862
#: src/diffuse/main.py:927 src/diffuse/widgets.py:1862
msgid "Copy"
msgstr ""
#: src/diffuse/main.py:933 src/diffuse/widgets.py:1863
#: src/diffuse/main.py:928 src/diffuse/widgets.py:1863
msgid "Paste"
msgstr ""
#: src/diffuse/main.py:934 src/diffuse/widgets.py:1866
#: src/diffuse/main.py:929 src/diffuse/widgets.py:1866
msgid "Clear Edits"
msgstr ""
#: src/diffuse/main.py:1057
#: src/diffuse/main.py:996
msgid "Changes detected"
msgstr ""
#: src/diffuse/main.py:1000
#, python-format
msgid ""
"The file \"%s\" changed on disk.\n"
"\n"
"Do you want to reload the file?"
msgstr ""
#: src/diffuse/main.py:1005
#, python-format
msgid ""
"The following files changed on disk:\n"
"%s\n"
"\n"
"Do you want to reload these files?"
msgstr ""
#: src/diffuse/main.py:1120
msgid "Some files have unsaved changes. Select the files to save before closing."
msgstr ""
#: src/diffuse/main.py:1071
#: src/diffuse/main.py:1134
msgid "Tab"
msgstr ""
#: src/diffuse/main.py:1076
#: src/diffuse/main.py:1139
msgid "Pane"
msgstr ""
#: src/diffuse/main.py:1087
#: src/diffuse/main.py:1150
msgid "Close _Without Saving"
msgstr ""
#: src/diffuse/main.py:1126
#: src/diffuse/main.py:1189
#, python-format
msgid "Closing this tab will quit %s."
msgstr ""
#: src/diffuse/main.py:1209
#: src/diffuse/main.py:1266
#, python-format
msgid "File Merge %d"
msgstr ""
#: src/diffuse/main.py:1315
#: src/diffuse/main.py:1372
#, python-format
msgid "Error retrieving commits for %s."
msgstr ""
#: src/diffuse/main.py:1347
#: src/diffuse/main.py:1404
#, python-format
msgid "Error retrieving modifications for %s."
msgstr ""
#: src/diffuse/main.py:1380
#: src/diffuse/main.py:1437
msgid "Open File In New Tab"
msgstr ""
#: src/diffuse/main.py:1403
#: src/diffuse/main.py:1460
msgid "Choose Folder With Modified Files"
msgstr ""
#: src/diffuse/main.py:1421
#: src/diffuse/main.py:1478
msgid "No modified files found."
msgstr ""
#: src/diffuse/main.py:1427
#: src/diffuse/main.py:1484
msgid "Choose Folder With Commit"
msgstr ""
#: src/diffuse/main.py:1447
#: src/diffuse/main.py:1504
msgid "No committed files found."
msgstr ""
#: src/diffuse/main.py:1483
#: src/diffuse/main.py:1540
msgid "New N-Way File Merge..."
msgstr ""
#: src/diffuse/main.py:1484
#: src/diffuse/main.py:1541
msgid "Number of panes: "
msgstr ""
#: src/diffuse/main.py:1558
#: src/diffuse/main.py:1615
msgid "Phrase not found. Continue from the end of the file?"
msgstr ""
#: src/diffuse/main.py:1560
#: src/diffuse/main.py:1617
msgid "Phrase not found. Continue from the start of the file?"
msgstr ""
#: src/diffuse/main.py:1781
#: src/diffuse/main.py:1827
msgid "Do not read any resource files"
msgstr ""
#: src/diffuse/main.py:1835
msgid "Specify explicit resource file"
msgstr ""
#: src/diffuse/main.py:1843
msgid "File revisions <rev-1> and <rev>"
msgstr ""
#: src/diffuse/main.py:1851
msgid "Close all tabs with no differences"
msgstr ""
#: src/diffuse/main.py:1858
msgid "Use <codec> to read and write files"
msgstr ""
#: src/diffuse/main.py:1866
msgid "Display <label> instead of the file name"
msgstr ""
#: src/diffuse/main.py:1874
msgid "Create a new tab for each modified file"
msgstr ""
#: src/diffuse/main.py:1881
msgid "File revision <rev>"
msgstr ""
#: src/diffuse/main.py:1889
msgid "Create a new tab for each file"
msgstr ""
#: src/diffuse/main.py:1896
msgid "Start a new tab"
msgstr ""
#: src/diffuse/main.py:1903 src/diffuse/preferences.py:165
msgid "Version control system search order"
msgstr ""
#: src/diffuse/main.py:1911
msgid "Start with line <line> selected"
msgstr ""
#: src/diffuse/main.py:1919
msgid "Create a blank file comparison pane"
msgstr ""
#: src/diffuse/main.py:1926 src/diffuse/preferences.py:91
#: src/diffuse/preferences.py:100
msgid "Ignore changes to white space"
msgstr ""
#: src/diffuse/main.py:1933
msgid "Ignore changes in blank lines"
msgstr ""
#: src/diffuse/main.py:1940 src/diffuse/preferences.py:93
msgid "Ignore end of line differences"
msgstr ""
#: src/diffuse/main.py:1947 src/diffuse/preferences.py:89
msgid "Ignore case differences"
msgstr ""
#: src/diffuse/main.py:1954 src/diffuse/preferences.py:90
msgid "Ignore white space differences"
msgstr ""
#: src/diffuse/main.py:1957
msgid ""
"Usage:\n"
" diffuse [OPTION...] [FILE...]\n"
" diffuse ( -h | -? | --help | -v | --version )\n"
"\n"
"Diffuse is a graphical tool for merging and comparing text files. Diffuse is\n"
"able to compare an arbitrary number of files side-by-side and gives users the\n"
"ability to manually adjust line matching and directly edit files. Diffuse can\n"
"also retrieve revisions of files from several VCSs for comparison and merging.\n"
"\n"
"Help Options:\n"
" ( -h | -? | --help ) Display this usage information\n"
" ( -v | --version ) Display version and copyright information\n"
"\n"
"Configuration Options:\n"
" --no-rcfile Do not read any resource files\n"
" --rcfile <file> Specify explicit resource file\n"
"\n"
"General Options:\n"
" ( -c | --commit ) <rev> File revisions <rev-1> and <rev>\n"
" ( -D | --close-if-same ) Close all tabs with no differences\n"
" ( -e | --encoding ) <codec> Use <codec> to read and write files\n"
" ( -L | --label ) <label> Display <label> instead of the file name\n"
" ( -m | --modified ) Create a new tab for each modified file\n"
" ( -r | --revision ) <rev> File revision <rev>\n"
" ( -s | --separate ) Create a new tab for each file\n"
" ( -t | --tab ) Start a new tab\n"
" ( -V | --vcs ) <vcs-list> Version control system search order\n"
" --line <line> Start with line <line> selected\n"
" --null-file Create a blank file comparison pane\n"
"\n"
"Display Options:\n"
" ( -b | --ignore-space-change ) Ignore changes to white space\n"
" ( -B | --ignore-blank-lines ) Ignore changes in blank lines\n"
" ( -E | --ignore-end-of-line ) Ignore end of line differences\n"
" ( -i | --ignore-case ) Ignore case differences\n"
" ( -w | --ignore-all-space ) Ignore white space differences"
"also retrieve revisions of files from several VCSs for comparison and merging."
msgstr ""
#: src/diffuse/main.py:1948
#: src/diffuse/main.py:2080
msgid "Error parsing line number."
msgstr ""
#: src/diffuse/main.py:1958
#, python-format
msgid "Skipping unknown argument \"%s\"."
msgstr ""
#: src/diffuse/main.py:1966
#: src/diffuse/main.py:2096
#, python-format
msgid "Error processing argument \"%s\". Directory not expected."
msgstr ""
@ -672,26 +724,10 @@ msgstr ""
msgid "Show white space characters"
msgstr ""
#: src/diffuse/preferences.py:89
msgid "Ignore case differences"
msgstr ""
#: src/diffuse/preferences.py:90
msgid "Ignore white space differences"
msgstr ""
#: src/diffuse/preferences.py:91 src/diffuse/preferences.py:100
msgid "Ignore changes to white space"
msgstr ""
#: src/diffuse/preferences.py:92
msgid "Ignore blank line differences"
msgstr ""
#: src/diffuse/preferences.py:93
msgid "Ignore end of line differences"
msgstr ""
#: src/diffuse/preferences.py:95
msgid "Alignment"
msgstr ""
@ -769,10 +805,6 @@ msgstr ""
msgid "Cygdrive prefix"
msgstr ""
#: src/diffuse/preferences.py:165
msgid "Version control system search order"
msgstr ""
#: src/diffuse/preferences.py:173
msgid "\"co\" command"
msgstr ""
@ -882,7 +914,7 @@ msgstr ""
msgid "Unhandled error at line {line} of {file}."
msgstr ""
#: src/diffuse/utils.py:66
#: src/diffuse/utils.py:65
msgid "Auto Detect"
msgstr ""

View File

@ -38,7 +38,6 @@ from diffuse.utils import LineEnding
from diffuse.vcs.vcs_interface import VcsInterface
from diffuse.vcs.vcs_registry import VcsRegistry
from diffuse.widgets import FileDiffViewerBase, EditMode
from diffuse.widgets import createMenu
import gi # type: ignore
gi.require_version('GObject', '2.0')
@ -47,8 +46,8 @@ gi.require_version('Gdk', '3.0')
gi.require_version('GdkPixbuf', '2.0')
gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import GObject, Gtk, Gdk, GdkPixbuf, Pango # type: ignore # noqa: E402
from gi.repository import (Gdk, GdkPixbuf, Gio, # type: ignore # noqa: E402
GLib, GObject, Gtk, Pango)
theVCSs = VcsRegistry()
@ -121,7 +120,7 @@ class FileInfo:
# the main application class containing a set of file viewers
# this class displays tab for switching between viewers and dispatches menu
# commands to the current viewer
class Diffuse(Gtk.Window):
class Diffuse(Gtk.ApplicationWindow):
# specialization of FileDiffViewerBase for Diffuse
class FileDiffViewer(FileDiffViewerBase):
# pane header
@ -665,8 +664,8 @@ class Diffuse(Gtk.Window):
def format_changed_cb(self, widget, f, fmt):
self.footers[f].setFormat(fmt)
def __init__(self, rc_dir):
super().__init__(type=Gtk.WindowType.TOPLEVEL)
def __init__(self, rc_dir, *args, **kwargs):
super().__init__(type=Gtk.WindowType.TOPLEVEL, *args, **kwargs)
self.prefs = Preferences(os.path.join(rc_dir, 'prefs'))
# number of created viewers (used to label some tabs)
@ -698,7 +697,6 @@ class Diffuse(Gtk.Window):
self.search_history: List[str] = []
self.connect('delete-event', self.delete_cb)
accel_group = Gtk.AccelGroup()
# create a Box for our contents
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
@ -717,7 +715,7 @@ class Diffuse(Gtk.Window):
factory = Gtk.IconFactory()
# render the base item used to indicate a new document
p0 = default_theme.load_icon_for_scale("document-new", icon_size, scale_factor, 0)
p0 = default_theme.load_icon_for_scale('document-new', icon_size, scale_factor, 0)
w, h = p0.get_width(), p0.get_height()
# render new 2-way merge icon
@ -743,8 +741,8 @@ class Diffuse(Gtk.Window):
factory.add(DIFFUSE_STOCK_NEW_3WAY_MERGE, Gtk.IconSet.new_from_pixbuf(p))
# render the left and right arrow we will use in our custom icons
p0 = default_theme.load_icon_for_scale("go-next", icon_size, scale_factor, 0)
p1 = default_theme.load_icon_for_scale("go-previous", icon_size, scale_factor, 0)
p0 = default_theme.load_icon_for_scale('go-next', icon_size, scale_factor, 0)
p1 = default_theme.load_icon_for_scale('go-previous', icon_size, scale_factor, 0)
w, h, s = p0.get_width(), p0.get_height(), 0.65
sw, sh = int(s * w), int(s * h)
w1, h1 = w - sw, h - sh
@ -768,127 +766,141 @@ class Diffuse(Gtk.Window):
menuspecs = []
menuspecs.append([_('_File'), [
[_('_Open File...'), self.open_file_cb, None, Gtk.STOCK_OPEN, 'open_file'],
[_('Open File In New _Tab...'), self.open_file_in_new_tab_cb, None, None, 'open_file_in_new_tab'], # noqa: E501
[_('Open _Modified Files...'), self.open_modified_files_cb, None, None, 'open_modified_files'], # noqa: E501
[_('Open Commi_t...'), self.open_commit_cb, None, None, 'open_commit'],
[_('_Reload File'), self.reload_file_cb, None, Gtk.STOCK_REFRESH, 'reload_file'],
[],
[_('_Save File'), self.save_file_cb, None, Gtk.STOCK_SAVE, 'save_file'],
[_('Save File _As...'), self.save_file_as_cb, None, Gtk.STOCK_SAVE_AS, 'save_file_as'],
[_('Save A_ll'), self.save_all_cb, None, None, 'save_all'],
[],
[_('New _2-Way File Merge'), self.new_2_way_file_merge_cb, None, DIFFUSE_STOCK_NEW_2WAY_MERGE, 'new_2_way_file_merge'], # noqa: E501
[_('New _3-Way File Merge'), self.new_3_way_file_merge_cb, None, DIFFUSE_STOCK_NEW_3WAY_MERGE, 'new_3_way_file_merge'], # noqa: E501
[_('New _N-Way File Merge...'), self.new_n_way_file_merge_cb, None, None, 'new_n_way_file_merge'], # noqa: E501
[],
[_('_Close Tab'), self.close_tab_cb, None, Gtk.STOCK_CLOSE, 'close_tab'],
[_('_Undo Close Tab'), self.undo_close_tab_cb, None, None, 'undo_close_tab'],
[_('_Quit'), self.quit_cb, None, Gtk.STOCK_QUIT, 'quit']
[
[_('_Open File...'), self.open_file_cb, None, 'open_file'],
[_('Open File In New _Tab...'), self.open_file_in_new_tab_cb, None, 'open_file_in_new_tab'], # noqa: E501
[_('Open _Modified Files...'), self.open_modified_files_cb, None, 'open_modified_files'], # noqa: E501
[_('Open Commi_t...'), self.open_commit_cb, None, 'open_commit'],
[_('_Reload File'), self.reload_file_cb, None, 'reload_file'],
], [
[_('_Save File'), self.save_file_cb, None, 'save_file'],
[_('Save File _As...'), self.save_file_as_cb, None, 'save_file_as'],
[_('Save A_ll'), self.save_all_cb, None, 'save_all'],
], [
[_('New _2-Way File Merge'), self.new_2_way_file_merge_cb, None, 'new_2_way_file_merge'], # noqa: E501
[_('New _3-Way File Merge'), self.new_3_way_file_merge_cb, None, 'new_3_way_file_merge'], # noqa: E501
[_('New _N-Way File Merge...'), self.new_n_way_file_merge_cb, None, 'new_n_way_file_merge'], # noqa: E501
], [
[_('_Close Tab'), self.close_tab_cb, None, 'close_tab'],
[_('_Undo Close Tab'), self.undo_close_tab_cb, None, 'undo_close_tab'],
[_('_Quit'), self.quit_cb, None, 'quit']
]
]])
menuspecs.append([_('_Edit'), [
[_('_Undo'), self.button_cb, 'undo', Gtk.STOCK_UNDO, 'undo'],
[_('_Redo'), self.button_cb, 'redo', Gtk.STOCK_REDO, 'redo'],
[],
[_('Cu_t'), self.button_cb, 'cut', Gtk.STOCK_CUT, 'cut'],
[_('_Copy'), self.button_cb, 'copy', Gtk.STOCK_COPY, 'copy'],
[_('_Paste'), self.button_cb, 'paste', Gtk.STOCK_PASTE, 'paste'],
[],
[_('Select _All'), self.button_cb, 'select_all', None, 'select_all'],
[_('C_lear Edits'), self.button_cb, 'clear_edits', Gtk.STOCK_CLEAR, 'clear_edits'],
[_('_Dismiss All Edits'), self.button_cb, 'dismiss_all_edits', None, 'dismiss_all_edits'], # noqa: E501
[],
[_('_Find...'), self.find_cb, None, Gtk.STOCK_FIND, 'find'],
[_('Find _Next'), self.find_next_cb, None, None, 'find_next'],
[_('Find Pre_vious'), self.find_previous_cb, None, None, 'find_previous'],
[_('_Go To Line...'), self.go_to_line_cb, None, Gtk.STOCK_JUMP_TO, 'go_to_line'],
[],
[_('Pr_eferences...'), self.preferences_cb, None, Gtk.STOCK_PREFERENCES, 'preferences']
[
[_('_Undo'), self.button_cb, 'undo', 'undo'],
[_('_Redo'), self.button_cb, 'redo', 'redo'],
], [
[_('Cu_t'), self.button_cb, 'cut', 'cut'],
[_('_Copy'), self.button_cb, 'copy', 'copy'],
[_('_Paste'), self.button_cb, 'paste', 'paste'],
], [
[_('Select _All'), self.button_cb, 'select_all', 'select_all'],
[_('C_lear Edits'), self.button_cb, 'clear_edits', 'clear_edits'],
[_('_Dismiss All Edits'), self.button_cb, 'dismiss_all_edits', 'dismiss_all_edits'],
], [
[_('_Find...'), self.find_cb, None, 'find'],
[_('Find _Next'), self.find_next_cb, None, 'find_next'],
[_('Find Pre_vious'), self.find_previous_cb, None, 'find_previous'],
[_('_Go To Line...'), self.go_to_line_cb, None, 'go_to_line'],
], [
[_('Pr_eferences...'), self.preferences_cb, None, 'preferences']
]
]])
submenudef = [
[_('None'), self.syntax_cb, None, None, 'no_syntax_highlighting', True, None, ('syntax', None)] # noqa: E501
]
syntax_menu = [[[_('None'), None, '', 'syntax_highlighting']]]
names = theResources.getSyntaxNames()
variant = GLib.Variant.new_string('')
self.syntax_action = Gio.SimpleAction.new_stateful(
'syntax_highlighting', variant.get_type(), variant
)
self.syntax_action.connect('change-state', self.syntax_cb)
self.add_action(self.syntax_action)
if len(names) > 0:
submenudef.append([])
syntax_section = []
names.sort(key=str.lower)
for name in names:
submenudef.append([
name,
self.syntax_cb,
name,
None,
'syntax_highlighting_' + name,
True,
None,
('syntax', name)
])
syntax_section.append(
[
name,
None,
name,
'syntax_highlighting',
]
)
syntax_menu.append(syntax_section)
menuspecs.append([_('_View'), [
[_('_Syntax Highlighting'), None, None, None, None, True, submenudef],
[],
[_('Re_align All'), self.button_cb, 'realign_all', Gtk.STOCK_EXECUTE, 'realign_all'],
[_('_Isolate'), self.button_cb, 'isolate', None, 'isolate'],
[],
[_('_First Difference'), self.button_cb, 'first_difference', Gtk.STOCK_GOTO_TOP, 'first_difference'], # noqa: E501
[_('_Previous Difference'), self.button_cb, 'previous_difference', Gtk.STOCK_GO_UP, 'previous_difference'], # noqa: E501
[_('_Next Difference'), self.button_cb, 'next_difference', Gtk.STOCK_GO_DOWN, 'next_difference'], # noqa: E501
[_('_Last Difference'), self.button_cb, 'last_difference', Gtk.STOCK_GOTO_BOTTOM, 'last_difference'], # noqa: E501
[],
[_('Fir_st Tab'), self.first_tab_cb, None, None, 'first_tab'],
[_('Pre_vious Tab'), self.previous_tab_cb, None, None, 'previous_tab'],
[_('Next _Tab'), self.next_tab_cb, None, None, 'next_tab'],
[_('Las_t Tab'), self.last_tab_cb, None, None, 'last_tab'],
[],
[_('Shift Pane _Right'), self.button_cb, 'shift_pane_right', None, 'shift_pane_right'],
[_('Shift Pane _Left'), self.button_cb, 'shift_pane_left', None, 'shift_pane_left']
[
[_('_Syntax Highlighting'), None, None, None, syntax_menu]
], [
[_('Re_align All'), self.button_cb, 'realign_all', 'realign_all'],
[_('_Isolate'), self.button_cb, 'isolate', 'isolate'],
], [
[_('_First Difference'), self.button_cb, 'first_difference', 'first_difference'],
[_('_Previous Difference'), self.button_cb, 'previous_difference', 'previous_difference'], # noqa: E501
[_('_Next Difference'), self.button_cb, 'next_difference', 'next_difference'],
[_('_Last Difference'), self.button_cb, 'last_difference', 'last_difference'],
], [
[_('Fir_st Tab'), self.first_tab_cb, None, 'first_tab'],
[_('Pre_vious Tab'), self.previous_tab_cb, None, 'previous_tab'],
[_('Next _Tab'), self.next_tab_cb, None, 'next_tab'],
[_('Las_t Tab'), self.last_tab_cb, None, 'last_tab'],
], [
[_('Shift Pane _Right'), self.button_cb, 'shift_pane_right', 'shift_pane_right'],
[_('Shift Pane _Left'), self.button_cb, 'shift_pane_left', 'shift_pane_left']
]
]])
menuspecs.append([_('F_ormat'), [
[_('Convert To _Upper Case'), self.button_cb, 'convert_to_upper_case', None, 'convert_to_upper_case'], # noqa: E501
[_('Convert To _Lower Case'), self.button_cb, 'convert_to_lower_case', None, 'convert_to_lower_case'], # noqa: E501
[],
[_('Sort Lines In _Ascending Order'), self.button_cb, 'sort_lines_in_ascending_order', Gtk.STOCK_SORT_ASCENDING, 'sort_lines_in_ascending_order'], # noqa: E501
[_('Sort Lines In D_escending Order'), self.button_cb, 'sort_lines_in_descending_order', Gtk.STOCK_SORT_DESCENDING, 'sort_lines_in_descending_order'], # noqa: E501
[],
[_('Remove Trailing _White Space'), self.button_cb, 'remove_trailing_white_space', None, 'remove_trailing_white_space'], # noqa: E501
[_('Convert Tabs To _Spaces'), self.button_cb, 'convert_tabs_to_spaces', None, 'convert_tabs_to_spaces'], # noqa: E501
[_('Convert Leading Spaces To _Tabs'), self.button_cb, 'convert_leading_spaces_to_tabs', None, 'convert_leading_spaces_to_tabs'], # noqa: E501
[],
[_('_Increase Indenting'), self.button_cb, 'increase_indenting', Gtk.STOCK_INDENT, 'increase_indenting'], # noqa: E501
[_('De_crease Indenting'), self.button_cb, 'decrease_indenting', Gtk.STOCK_UNINDENT, 'decrease_indenting'], # noqa: E501
[],
[_('Convert To _DOS Format'), self.button_cb, 'convert_to_dos', None, 'convert_to_dos'], # noqa: E501
[_('Convert To _Mac Format'), self.button_cb, 'convert_to_mac', None, 'convert_to_mac'], # noqa: E501
[_('Convert To Uni_x Format'), self.button_cb, 'convert_to_unix', None, 'convert_to_unix'] # noqa: E501
[
[_('Convert To _Upper Case'), self.button_cb, 'convert_to_upper_case', 'convert_to_upper_case'], # noqa: E501
[_('Convert To _Lower Case'), self.button_cb, 'convert_to_lower_case', 'convert_to_lower_case'], # noqa: E501
], [
[_('Sort Lines In _Ascending Order'), self.button_cb, 'sort_lines_in_ascending_order', 'sort_lines_in_ascending_order'], # noqa: E501
[_('Sort Lines In D_escending Order'), self.button_cb, 'sort_lines_in_descending_order', 'sort_lines_in_descending_order'], # noqa: E501
], [
[_('Remove Trailing _White Space'), self.button_cb, 'remove_trailing_white_space', 'remove_trailing_white_space'], # noqa: E501
[_('Convert Tabs To _Spaces'), self.button_cb, 'convert_tabs_to_spaces', 'convert_tabs_to_spaces'], # noqa: E501
[_('Convert Leading Spaces To _Tabs'), self.button_cb, 'convert_leading_spaces_to_tabs', 'convert_leading_spaces_to_tabs'], # noqa: E501
], [
[_('_Increase Indenting'), self.button_cb, 'increase_indenting', 'increase_indenting'], # noqa: E501
[_('De_crease Indenting'), self.button_cb, 'decrease_indenting', 'decrease_indenting'], # noqa: E501
], [
[_('Convert To _DOS Format'), self.button_cb, 'convert_to_dos', 'convert_to_dos'],
[_('Convert To _Mac Format'), self.button_cb, 'convert_to_mac', 'convert_to_mac'],
[_('Convert To Uni_x Format'), self.button_cb, 'convert_to_unix', 'convert_to_unix']
]
]])
menuspecs.append([_('_Merge'), [
[_('Copy Selection _Right'), self.button_cb, 'copy_selection_right', Gtk.STOCK_GOTO_LAST, 'copy_selection_right'], # noqa: E501
[_('Copy Selection _Left'), self.button_cb, 'copy_selection_left', Gtk.STOCK_GOTO_FIRST, 'copy_selection_left'], # noqa: E501
[],
[_('Copy Left _Into Selection'), self.button_cb, 'copy_left_into_selection', Gtk.STOCK_GO_FORWARD, 'copy_left_into_selection'], # noqa: E501
[_('Copy Right I_nto Selection'), self.button_cb, 'copy_right_into_selection', Gtk.STOCK_GO_BACK, 'copy_right_into_selection'], # noqa: E501
[_('_Merge From Left Then Right'), self.button_cb, 'merge_from_left_then_right', DIFFUSE_STOCK_LEFT_RIGHT, 'merge_from_left_then_right'], # noqa: E501
[_('M_erge From Right Then Left'), self.button_cb, 'merge_from_right_then_left', DIFFUSE_STOCK_RIGHT_LEFT, 'merge_from_right_then_left'] # noqa: E501
[
[_('Copy Selection _Right'), self.button_cb, 'copy_selection_right', 'copy_selection_right'], # noqa: E501
[_('Copy Selection _Left'), self.button_cb, 'copy_selection_left', 'copy_selection_left'], # noqa: E501
], [
[_('Copy Left _Into Selection'), self.button_cb, 'copy_left_into_selection', 'copy_left_into_selection'], # noqa: E501
[_('Copy Right I_nto Selection'), self.button_cb, 'copy_right_into_selection', 'copy_right_into_selection'], # noqa: E501
[_('_Merge From Left Then Right'), self.button_cb, 'merge_from_left_then_right', 'merge_from_left_then_right'], # noqa: E501
[_('M_erge From Right Then Left'), self.button_cb, 'merge_from_right_then_left', 'merge_from_right_then_left'] # noqa: E501
]
]])
menuspecs.append([_('_Help'), [
[_('_Help Contents...'), self.help_contents_cb, None, Gtk.STOCK_HELP, 'help_contents'],
[],
[_('_About %s...') % (constants.APP_NAME, ), self.about_cb, None, Gtk.STOCK_ABOUT, 'about'] # noqa: E501
[
[_('_Help Contents...'), self.help_contents_cb, None, 'help_contents'],
], [
[_('_About %s...') % (constants.APP_NAME, ), self.about_cb, None, 'about']
]
]])
# used to disable menu events when switching tabs
self.menu_update_depth = 0
# build list of radio menu items so we can update them to match the
# currently viewed pane
self.radio_menus = radio_menus = {}
menu_bar = _create_menu_bar(menuspecs, radio_menus, accel_group)
vbox.pack_start(menu_bar, False, False, 0)
menu_bar.show()
menubar = Gio.Menu()
for label, sections in menuspecs:
menubar.append_submenu(label, self._create_menu(sections))
self.get_application().set_menubar(menubar)
# create button bar
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
@ -933,11 +945,39 @@ class Diffuse(Gtk.Window):
vbox.pack_start(statusbar, False, False, 0)
statusbar.show()
self.add_accel_group(accel_group)
self.add(vbox)
vbox.show()
self.connect('focus-in-event', self.focus_in_cb)
def _create_menu(self, sections):
menu = Gio.Menu.new()
for section in sections:
section_menu = Gio.Menu.new()
for label, cb, cb_data, accel, *submenu in section:
if submenu:
(submenu,) = submenu
section_menu.append_submenu(label, self._create_menu(submenu))
else:
if cb_data is not None:
cb_data = GLib.Variant.new_string(cb_data)
accel = accel.replace('_', '-')
if cb is not None:
action = Gio.SimpleAction.new(accel, cb_data and cb_data.get_type())
action.connect('activate', cb)
self.add_action(action)
item = Gio.MenuItem.new(label)
item.set_action_and_target_value('win.' + accel, cb_data)
key_binding = theResources.getKeyBindings('menu', accel)
if len(key_binding) > 0:
key, modifier = key_binding[0]
self.get_application().set_accels_for_action(
Gio.Action.print_detailed_name('win.' + accel, cb_data),
[Gtk.accelerator_name(key, modifier)],
)
section_menu.append_item(item)
menu.append_section(None, section_menu)
return menu
# notifies all viewers on focus changes so they may check for external
# changes to files
def focus_in_cb(self, widget, event):
@ -955,18 +995,18 @@ class Diffuse(Gtk.Window):
filename = h.info.label if h.info.label is not None else h.info.name
filenames.append(filename)
primary_text = _("Changes detected")
secondary_text = ""
primary_text = _('Changes detected')
secondary_text = ''
if len(filenames) == 1:
secondary_text = _(
"The file \"%s\" changed on disk.\n\n"
"Do you want to reload the file?"
'The file "%s" changed on disk.\n\n'
'Do you want to reload the file?'
) % (filenames[0],)
else:
secondary_text = _(
"The following files changed on disk:\n%s\n\n"
"Do you want to reload these files?"
) % ("\n".join("- " + filename for filename in filenames),)
'The following files changed on disk:\n%s\n\n'
'Do you want to reload these files?'
) % ('\n'.join('- ' + filename for filename in filenames),)
dialog = Gtk.MessageDialog(
transient_for=self.get_toplevel(),
@ -1041,7 +1081,7 @@ 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 {constants.APP_NAME} {constants.VERSION}.\n\n") # noqa: E501
f.write(f'# This state file was generated by {constants.APP_NAME} {constants.VERSION}.\n\n') # noqa: E501
for s in ss:
f.write(s)
f.close()
@ -1194,13 +1234,7 @@ class Diffuse(Gtk.Window):
# update the label in the status bar
def setSyntax(self, s):
# update menu
t = self.radio_menus.get('syntax', None)
if t is not None:
t = t[1]
if s in t:
self.menu_update_depth += 1
t[s].set_active(True)
self.menu_update_depth -= 1
self.syntax_action.set_state(GLib.Variant.new_string(s or ''))
# callback used when switching notebook pages
def switch_page_cb(self, widget, ptr, page_num):
@ -1387,7 +1421,7 @@ class Diffuse(Gtk.Window):
# respond to close window request from the window manager
def delete_cb(self, widget, event):
if self.confirmQuit():
Gtk.main_quit()
self.get_application().quit()
return False
return True
@ -1534,7 +1568,7 @@ class Diffuse(Gtk.Window):
# callback for the quit menu item
def quit_cb(self, widget, data):
if self.confirmQuit():
Gtk.main_quit()
self.get_application().quit()
# request search parameters if force=True and then perform a search in the
# current viewer pane
@ -1644,7 +1678,7 @@ class Diffuse(Gtk.Window):
# callback for most menu items and buttons
def button_cb(self, widget, data):
self.getCurrentViewer().button_cb(widget, data)
self.getCurrentViewer().button_cb(widget, data.get_string())
# display help documentation
def help_contents_cb(self, widget, data):
@ -1711,18 +1745,6 @@ class Diffuse(Gtk.Window):
dialog.destroy()
# convenience method for creating a menu bar according to a template
def _create_menu_bar(specs, radio, accel_group):
menu_bar = Gtk.MenuBar()
for label, spec in specs:
menu = Gtk.MenuItem.new_with_mnemonic(label)
menu.set_submenu(createMenu(spec, radio, accel_group))
menu.set_use_underline(True)
menu.show()
menu_bar.append(menu)
return menu_bar
# convenience method for packing buttons into a container according to a
# template
def _append_buttons(box, size, specs):
@ -1789,207 +1811,305 @@ GObject.signal_new('save', Diffuse.FileDiffViewer.PaneHeader, GObject.SignalFlag
GObject.signal_new('save-as', Diffuse.FileDiffViewer.PaneHeader, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, ()) # noqa: E501
def main(version, sysconfigdir):
# app = Application()
# return app.run(sys.argv)
class Application(Gtk.Application):
def __init__(self, sysconfigdir, *args, **kwargs):
super().__init__(
*args,
application_id='io.github.mightycreak.Diffuse',
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE | Gio.ApplicationFlags.NON_UNIQUE,
**kwargs,
)
args = sys.argv
argc = len(args)
self.sysconfigdir = sysconfigdir
constants.VERSION = version
if argc == 2 and args[1] in ['-v', '--version']:
print('%s %s\n%s' % (constants.APP_NAME, constants.VERSION, constants.COPYRIGHT))
return 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
self.add_main_option(
'version',
ord('v'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Display version and copyright information'),
None,
)
self.add_main_option(
'no-rcfile',
0,
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Do not read any resource files'),
None,
)
self.add_main_option(
'rcfile',
0,
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
_('Specify explicit resource file'),
'file',
)
self.add_main_option(
'commit',
ord('c'),
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
_('File revisions <rev-1> and <rev>'),
'rev',
)
self.add_main_option(
'close-if-same',
ord('D'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Close all tabs with no differences'),
)
self.add_main_option(
'encoding',
ord('e'),
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
_('Use <codec> to read and write files'),
'codec',
)
self.add_main_option(
'label',
ord('L'),
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
_('Display <label> instead of the file name'),
'label',
)
self.add_main_option(
'modified',
ord('m'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Create a new tab for each modified file'),
)
self.add_main_option(
'revision',
ord('r'),
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
_('File revision <rev>'),
'rev',
)
self.add_main_option(
'separate',
ord('s'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Create a new tab for each file'),
)
self.add_main_option(
'tab',
ord('t'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Start a new tab'),
)
self.add_main_option(
'vcs',
ord('V'),
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
_('Version control system search order'),
'vcs-list',
)
self.add_main_option(
'line',
0,
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
_('Start with line <line> selected'),
'line',
)
self.add_main_option(
'null-file',
0,
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Create a blank file comparison pane'),
)
self.add_main_option(
'ignore-space-change',
ord('b'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Ignore changes to white space'),
)
self.add_main_option(
'ignore-blank-lines',
ord('B'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Ignore changes in blank lines'),
)
self.add_main_option(
'ignore-end-of-line',
ord('E'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Ignore end of line differences'),
)
self.add_main_option(
'ignore-case',
ord('i'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Ignore case differences'),
)
self.add_main_option(
'ignore-all-space',
ord('w'),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
_('Ignore white space differences'),
)
self.set_option_context_summary(_(
'''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 several VCSs for comparison and merging.
also retrieve revisions of files from several VCSs for comparison and merging.'''
))
Help Options:
( -h | -? | --help ) Display this usage information
( -v | --version ) Display version and copyright information
def do_activate(self):
self.window.present()
Configuration Options:
--no-rcfile Do not read any resource files
--rcfile <file> Specify explicit resource file
def do_command_line(self, command_line):
options = command_line.get_options_dict()
# convert GVariantDict -> GVariant -> dict
options = options.end().unpack()
General Options:
( -c | --commit ) <rev> File revisions <rev-1> and <rev>
( -D | --close-if-same ) Close all tabs with no differences
( -e | --encoding ) <codec> Use <codec> to read and write files
( -L | --label ) <label> Display <label> instead of the file name
( -m | --modified ) Create a new tab for each modified file
( -r | --revision ) <rev> File revision <rev>
( -s | --separate ) Create a new tab for each file
( -t | --tab ) Start a new tab
( -V | --vcs ) <vcs-list> Version control system search order
--line <line> Start with line <line> selected
--null-file Create a blank file comparison pane
if 'version' in options:
print('%s %s\n%s' % (constants.APP_NAME, constants.VERSION, constants.COPYRIGHT))
return 0
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'''))
return 0
# find the config directory and create it if it didn't exist
rc_dir = os.environ.get('XDG_CONFIG_HOME', None)
subdirs = ['diffuse']
if rc_dir is None:
rc_dir = os.path.expanduser('~')
subdirs.insert(0, '.config')
rc_dir = utils.make_subdirs(rc_dir, subdirs)
# find the local data directory and create it if it didn't exist
data_dir = os.environ.get('XDG_DATA_HOME', None)
subdirs = ['diffuse']
if data_dir is None:
data_dir = os.path.expanduser('~')
subdirs[:0] = ['.local', 'share']
data_dir = utils.make_subdirs(data_dir, subdirs)
# load resource files
i = 1
rc_files = []
if i < argc and args[i] == '--no-rcfile':
i += 1
elif i + 1 < argc and args[i] == '--rcfile':
i += 1
rc_files.append(args[i])
i += 1
else:
# parse system wide then personal initialization files
if utils.isWindows():
rc_file = os.path.join(utils.bin_dir, 'diffuserc')
else:
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)
for rc_file in rc_files:
# convert to absolute path so the location of any processing errors are
# reported with normalized file names
rc_file = os.path.abspath(rc_file)
try:
theResources.parse(rc_file)
except IOError:
utils.logError(_('Error reading %s.') % (rc_file, ))
diff = Diffuse(rc_dir)
# load state
statepath = os.path.join(data_dir, 'state')
diff.loadState(statepath)
# process remaining command line arguments
encoding = None
revs = []
close_on_same = False
specs = []
had_specs = False
labels = []
funcs = {
'modified': diff.createModifiedFileTabs,
'commit': diff.createCommitFileTabs,
'separate': diff.createSeparateTabs,
'single': diff.createSingleTab
}
mode = 'single'
options = {}
while i < argc:
arg = args[i]
if len(arg) > 0 and arg[0] == '-':
if i + 1 < argc and arg in ['-c', '--commit']:
# specified revision
funcs[mode](specs, labels, options)
i += 1
specs, labels, options = [], [], {'commit': args[i]}
mode = 'commit'
elif arg in ['-D', '--close-if-same']:
close_on_same = True
elif i + 1 < argc and arg in ['-e', '--encoding']:
i += 1
encoding = args[i]
encoding = encodings.aliases.aliases.get(encoding, encoding)
elif arg in ['-m', '--modified']:
funcs[mode](specs, labels, options)
specs, labels, options = [], [], {}
mode = 'modified'
elif i + 1 < argc and arg in ['-r', '--revision']:
# specified revision
i += 1
revs.append((args[i], encoding))
elif arg in ['-s', '--separate']:
funcs[mode](specs, labels, options)
specs, labels, options = [], [], {}
# open items in separate tabs
mode = 'separate'
elif arg in ['-t', '--tab']:
funcs[mode](specs, labels, options)
specs, labels, options = [], [], {}
# start a new tab
mode = 'single'
elif i + 1 < argc and arg in ['-V', '--vcs']:
i += 1
diff.prefs.setString('vcs_search_order', args[i])
diff.preferences_updated()
elif arg in ['-b', '--ignore-space-change']:
diff.prefs.setBool('display_ignore_whitespace_changes', True)
diff.prefs.setBool('align_ignore_whitespace_changes', True)
diff.preferences_updated()
elif arg in ['-B', '--ignore-blank-lines']:
diff.prefs.setBool('display_ignore_blanklines', True)
diff.prefs.setBool('align_ignore_blanklines', True)
diff.preferences_updated()
elif arg in ['-E', '--ignore-end-of-line']:
diff.prefs.setBool('display_ignore_endofline', True)
diff.prefs.setBool('align_ignore_endofline', True)
diff.preferences_updated()
elif arg in ['-i', '--ignore-case']:
diff.prefs.setBool('display_ignore_case', True)
diff.prefs.setBool('align_ignore_case', True)
diff.preferences_updated()
elif arg in ['-w', '--ignore-all-space']:
diff.prefs.setBool('display_ignore_whitespace', True)
diff.prefs.setBool('align_ignore_whitespace', True)
diff.preferences_updated()
elif i + 1 < argc and arg == '-L':
i += 1
labels.append(args[i])
elif i + 1 < argc and arg == '--line':
i += 1
try:
options['line'] = int(args[i])
except ValueError:
utils.logError(_('Error parsing line number.'))
elif arg == '--null-file':
# add a blank file pane
if mode == 'single' or mode == 'separate':
if len(revs) == 0:
revs.append((None, encoding))
specs.append((None, revs))
revs = []
had_specs = True
# find the config directory and create it if it didn't exist
rc_dir = os.environ.get('XDG_CONFIG_HOME', None)
subdirs = ['diffuse']
if rc_dir is None:
rc_dir = os.path.expanduser('~')
subdirs.insert(0, '.config')
rc_dir = utils.make_subdirs(rc_dir, subdirs)
# find the local data directory and create it if it didn't exist
data_dir = os.environ.get('XDG_DATA_HOME', None)
subdirs = ['diffuse']
if data_dir is None:
data_dir = os.path.expanduser('~')
subdirs[:0] = ['.local', 'share']
data_dir = utils.make_subdirs(data_dir, subdirs)
# load resource files
rc_files = []
if 'no-rcfile' not in options:
# parse system wide then personal initialization files
if utils.isWindows():
rc_file = os.path.join(utils.bin_dir, 'diffuserc')
else:
utils.logError(_('Skipping unknown argument "%s".') % (args[i], ))
else:
filename = diff.prefs.convertToNativePath(args[i])
rc_file = os.path.join(utils.bin_dir, self.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)
if 'rcfile' in options:
rc_files.append(options['rcfile'])
for rc_file in rc_files:
# convert to absolute path so the location of any processing errors are
# reported with normalized file names
rc_file = os.path.abspath(rc_file)
try:
theResources.parse(rc_file)
except IOError:
utils.logError(_('Error reading %s.') % (rc_file,))
self.window = diff = Diffuse(rc_dir, application=self)
# load state
self.statepath = os.path.join(data_dir, 'state')
self.window.loadState(self.statepath)
# process remaining command line arguments
encoding = None
revs = []
close_on_same = False
specs = []
had_specs = False
labels = []
funcs = {
'modified': diff.createModifiedFileTabs,
'commit': diff.createCommitFileTabs,
'separate': diff.createSeparateTabs,
'single': diff.createSingleTab,
}
mode = 'single'
opts = {}
if 'commit' in options:
# specified revision
funcs[mode](specs, labels, options)
specs, labels, opts = [], [], {'commit': options['commit']}
mode = 'commit'
if 'close-if-same' in options:
close_on_same = True
if 'encoding' in options:
encoding = options['encoding']
encoding = encodings.aliases.aliases.get(encoding, encoding)
if 'modified' in options:
funcs[mode](specs, labels, opts)
specs, labels, opts = [], [], {}
mode = 'modified'
if 'revision' in options:
# specified revision
revs.append((options['revision'], encoding))
if 'separate' in options:
funcs[mode](specs, labels, opts)
specs, labels, opts = [], [], {}
# open items in separate tabs
mode = 'separate'
if 'tab' in options:
funcs[mode](specs, labels, opts)
specs, labels, opts = [], [], {}
# start a new tab
mode = 'single'
if 'vcs' in options:
diff.prefs.setString('vcs_search_order', options['vcs'])
diff.preferences_updated()
if 'ignore-space-change' in options:
diff.prefs.setBool('display_ignore_whitespace_changes', True)
diff.prefs.setBool('align_ignore_whitespace_changes', True)
diff.preferences_updated()
if 'ignore-blank-lines' in options:
diff.prefs.setBool('display_ignore_blanklines', True)
diff.prefs.setBool('align_ignore_blanklines', True)
diff.preferences_updated()
if 'ignore-end-of-line' in options:
diff.prefs.setBool('display_ignore_endofline', True)
diff.prefs.setBool('align_ignore_endofline', True)
diff.preferences_updated()
if 'ignore-case' in options:
diff.prefs.setBool('display_ignore_case', True)
diff.prefs.setBool('align_ignore_case', True)
diff.preferences_updated()
if 'ignore-all-space' in options:
diff.prefs.setBool('display_ignore_whitespace', True)
diff.prefs.setBool('align_ignore_whitespace', True)
diff.preferences_updated()
if 'label' in options:
labels.append(options['label'])
if 'line' in options:
try:
opts['line'] = int(options['line'])
except ValueError:
utils.logError(_('Error parsing line number.'))
if 'null-file' in options:
# add a blank file pane
if mode == 'single' or mode == 'separate':
if len(revs) == 0:
revs.append((None, encoding))
specs.append((None, revs))
revs = []
had_specs = True
for arg in command_line.get_arguments()[1:]:
filename = diff.prefs.convertToNativePath(arg)
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:
utils.logError(_(
'Error processing argument "%s". Directory not expected.') % (args[i], )
)
utils.logError(
_('Error processing argument "%s". Directory not expected.') % (arg,))
filename = None
if filename is not None:
if len(revs) == 0:
@ -1997,26 +2117,27 @@ Display Options:
specs.append((filename, revs))
revs = []
had_specs = True
i += 1
if mode in ['modified', 'commit'] and len(specs) == 0:
specs.append((os.curdir, [(None, encoding)]))
had_specs = True
funcs[mode](specs, labels, options)
if mode in ['modified', 'commit'] and len(specs) == 0:
specs.append((os.curdir, [(None, encoding)]))
had_specs = True
funcs[mode](specs, labels, opts)
# create a file diff viewer if the command line arguments haven't
# implicitly created any
if not had_specs:
diff.newLoadedFileDiffViewer([])
elif close_on_same:
diff.closeOnSame()
nb = diff.notebook
n = nb.get_n_pages()
if n > 0:
nb.set_show_tabs(diff.prefs.getBool('tabs_always_show') or n > 1)
nb.get_nth_page(0).grab_focus()
self.activate()
return 0
# create a file diff viewer if the command line arguments haven't
# implicitly created any
if not had_specs:
diff.newLoadedFileDiffViewer([])
elif close_on_same:
diff.closeOnSame()
nb = diff.notebook
n = nb.get_n_pages()
if n > 0:
nb.set_show_tabs(diff.prefs.getBool('tabs_always_show') or n > 1)
nb.get_nth_page(0).grab_focus()
diff.show()
Gtk.main()
# save state
diff.saveState(statepath)
return 0
def main(version, sysconfigdir):
constants.VERSION = version
app = Application(sysconfigdir)
return app.run(sys.argv)

View File

@ -1853,23 +1853,44 @@ class FileDiffViewerBase(Gtk.Grid):
can_select = self.mode in (EditMode.LINE, EditMode.CHAR) and f == self.current_pane
can_swap = (f != self.current_pane)
menu = createMenu([
[_('Align with Selection'), self.align_with_selection_cb, [f, i], Gtk.STOCK_EXECUTE, None, can_align], # noqa: E501
[_('Isolate'), self.button_cb, 'isolate', None, None, can_isolate],
[_('Merge Selection'), self.merge_lines_cb, f, None, None, can_merge],
menu = self._create_menu([
[_('Align with Selection'), self.align_with_selection_cb, [f, i], Gtk.STOCK_EXECUTE, can_align], # noqa: E501
[_('Isolate'), self.button_cb, 'isolate', None, can_isolate],
[_('Merge Selection'), self.merge_lines_cb, f, None, can_merge],
[],
[_('Cut'), self.button_cb, 'cut', Gtk.STOCK_CUT, None, can_select],
[_('Copy'), self.button_cb, 'copy', Gtk.STOCK_COPY, None, can_select],
[_('Paste'), self.button_cb, 'paste', Gtk.STOCK_PASTE, None, can_select],
[_('Cut'), self.button_cb, 'cut', Gtk.STOCK_CUT, can_select],
[_('Copy'), self.button_cb, 'copy', Gtk.STOCK_COPY, can_select],
[_('Paste'), self.button_cb, 'paste', Gtk.STOCK_PASTE, can_select],
[],
[_('Select All'), self.button_cb, 'select_all', None, None, can_select],
[_('Clear Edits'), self.button_cb, 'clear_edits', Gtk.STOCK_CLEAR, None, can_isolate], # noqa: E501
[_('Select All'), self.button_cb, 'select_all', None, can_select],
[_('Clear Edits'), self.button_cb, 'clear_edits', Gtk.STOCK_CLEAR, can_isolate], # noqa: E501
[],
[_('Swap with Selected Pane'), self.swap_panes_cb, f, None, None, can_swap]
[_('Swap with Selected Pane'), self.swap_panes_cb, f, None, can_swap]
])
menu.attach_to_widget(self)
menu.popup(None, None, None, None, event.button, event.time)
@staticmethod
def _create_menu(specs):
menu = Gtk.Menu()
for spec in specs:
if len(spec) > 0:
(label, cb, cb_data, image_stock_name, sensitive) = spec
item = Gtk.ImageMenuItem.new_with_mnemonic(label)
item.set_use_underline(True)
item.set_sensitive(sensitive)
if image_stock_name is not None:
image = Gtk.Image()
image.set_from_stock(image_stock_name, Gtk.IconSize.MENU)
item.set_image(image)
if cb is not None:
item.connect('activate', cb, cb_data)
else:
item = Gtk.SeparatorMenuItem()
item.show()
menu.append(item)
return menu
# callback used to notify us about click and drag motion
def darea_motion_notify_cb(self, widget, event, f):
if event.state & Gdk.ModifierType.BUTTON1_MASK:
@ -3708,51 +3729,6 @@ class FileDiffViewerBase(Gtk.Grid):
self._mergeBoth(True)
# convenience method for creating a menu according to a template
def createMenu(specs, radio=None, accel_group=None):
menu = Gtk.Menu()
for spec in specs:
if len(spec) > 0:
if len(spec) > 7 and spec[7] is not None:
g, k = spec[7]
if g not in radio:
item = Gtk.RadioMenuItem.new_with_mnemonic_from_widget(None, spec[0])
radio[g] = (item, {})
else:
item = Gtk.RadioMenuItem.new_with_mnemonic_from_widget(radio[g][0], spec[0])
radio[g][1][k] = item
else:
item = Gtk.ImageMenuItem.new_with_mnemonic(spec[0])
cb = spec[1]
if cb is not None:
data = spec[2]
item.connect('activate', cb, data)
if len(spec) > 3 and spec[3] is not None:
image = Gtk.Image()
image.set_from_stock(spec[3], Gtk.IconSize.MENU)
item.set_image(image)
if accel_group is not None and len(spec) > 4:
a = theResources.getKeyBindings('menu', spec[4])
if len(a) > 0:
key, modifier = a[0]
item.add_accelerator(
'activate',
accel_group,
key,
modifier,
Gtk.AccelFlags.VISIBLE)
if len(spec) > 5:
item.set_sensitive(spec[5])
if len(spec) > 6 and spec[6] is not None:
item.set_submenu(createMenu(spec[6], radio, accel_group))
item.set_use_underline(True)
else:
item = Gtk.SeparatorMenuItem()
item.show()
menu.append(item)
return menu
class CharacterClass(IntFlag):
ALPHANUMERIC = 0
WHITESPACE = 1