Skip to content

Commit be433eb

Browse files
authored
Merge pull request #207 from MightyCreak/modernize-for-gtk
Modernize some GTK code
2 parents e23cdce + ef3d59d commit be433eb

File tree

6 files changed

+77
-68
lines changed

6 files changed

+77
-68
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222

2323
- Some signals weren't properly renamed from the previous GTK3 migration (@MightyCreak)
2424
- The syntax menu wasn't working anymore (@MightyCreak)
25+
- Properly handles SIGINT (i.e. Ctrl+C) now (@MightyCreak)
26+
- Add back `save_state()` to remember window's width and height (@MightyCreak)
2527

2628
## 0.8.1 - 2023-04-07
2729

src/diffuse/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from typing import Final
2222

2323
APP_NAME: Final[str] = 'Diffuse'
24+
APP_ID: Final[str] = 'io.github.mightycreak.Diffuse'
2425
COPYRIGHT: Final[str] = '''{copyright} © 2006-2019 Derrick Moser
2526
{copyright} © 2015-2023 Romain Failliot'''.format(copyright=_("Copyright"))
2627
WEBSITE: Final[str] = 'https://mightycreak.github.io/diffuse/'

src/diffuse/dialogs.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from gettext import gettext as _
2323
from typing import Optional
2424

25-
from diffuse import constants
2625
from diffuse import utils
2726

2827
import gi # type: ignore
@@ -31,39 +30,6 @@
3130
from gi.repository import GObject, Gtk # type: ignore # noqa: E402
3231

3332

34-
# the about dialog
35-
class AboutDialog(Gtk.AboutDialog):
36-
def __init__(self, parent: Gtk.Widget) -> None:
37-
Gtk.AboutDialog.__init__(self)
38-
self.set_transient_for(parent)
39-
self.set_logo_icon_name('io.github.mightycreak.Diffuse')
40-
self.set_program_name(constants.APP_NAME)
41-
self.set_version(constants.VERSION)
42-
self.set_comments(_('Diffuse is a graphical tool for merging and comparing text files.'))
43-
self.set_copyright(constants.COPYRIGHT)
44-
self.set_website(constants.WEBSITE)
45-
self.set_authors(['Derrick Moser <[email protected]>',
46-
'Romain Failliot <[email protected]>'])
47-
self.set_translator_credits(_('translator-credits'))
48-
license_text = [
49-
constants.APP_NAME + ' ' + constants.VERSION + '\n\n',
50-
constants.COPYRIGHT + '\n\n',
51-
'''This program is free software; you can redistribute it and/or modify
52-
it under the terms of the GNU General Public License as published by
53-
the Free Software Foundation; either version 2 of the License, or
54-
(at your option) any later version.
55-
56-
This program is distributed in the hope that it will be useful,
57-
but WITHOUT ANY WARRANTY; without even the implied warranty of
58-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59-
GNU General Public License for more details.
60-
61-
You should have received a copy of the GNU General Public License along
62-
with this program; if not, write to the Free Software Foundation, Inc.,
63-
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.''']
64-
self.set_license(''.join(license_text))
65-
66-
6733
# custom dialogue for picking files with widgets for specifying the encoding
6834
# and revision
6935
class FileChooserDialog(Gtk.FileChooserDialog):

src/diffuse/diffuse.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import os
2323
import sys
2424
import gettext
25+
import signal
2526

2627
from gi.repository import Gio
2728

@@ -32,6 +33,10 @@ SYSCONFIGDIR = '@SYSCONFIGDIR@'
3233

3334
sys.path.insert(1, PKGDATADIR)
3435

36+
# Quietly handle SIGINT (i.e. Ctrl+C)
37+
signal.signal(signal.SIGINT, signal.SIG_DFL)
38+
39+
# Initialize i18n
3540
gettext.bindtextdomain('diffuse', localedir=LOCALEDIR)
3641
gettext.textdomain('diffuse')
3742

src/diffuse/main.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ class DiffuseApplication(Gtk.Application):
3737

3838
def __init__(self, sysconfigdir):
3939
super().__init__(
40-
application_id='io.github.mightycreak.Diffuse',
40+
application_id=constants.APP_ID,
4141
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE | Gio.ApplicationFlags.NON_UNIQUE)
4242

4343
self.window = None
4444
self.sysconfigdir = sysconfigdir
4545

46+
self.connect('shutdown', self.on_shutdown)
47+
4648
self.add_main_option(
4749
'version',
4850
ord('v'),
@@ -249,7 +251,7 @@ def do_command_line(self, command_line):
249251

250252
# load state
251253
self.statepath = os.path.join(data_dir, 'state')
252-
diff_window.loadState(self.statepath)
254+
diff_window.load_state(self.statepath)
253255

254256
# process remaining command line arguments
255257
encoding = None
@@ -366,6 +368,9 @@ def do_command_line(self, command_line):
366368
self.activate()
367369
return 0
368370

371+
def on_shutdown(self, application: Gio.Application) -> None:
372+
self.window.save_state(self.statepath)
373+
369374

370375
def main(version: str, sysconfigdir: str) -> int:
371376
"""The application's entry point."""

src/diffuse/window.py

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from urllib.parse import urlparse
2929

3030
from diffuse import constants, utils
31-
from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, SearchDialog
31+
from diffuse.dialogs import FileChooserDialog, NumericDialog, SearchDialog
3232
from diffuse.preferences import Preferences
3333
from diffuse.resources import theResources
3434
from diffuse.utils import LineEnding
@@ -682,24 +682,18 @@ def __init__(self, rc_dir, **kwargs):
682682
# number of created viewers (used to label some tabs)
683683
self.viewer_count = 0
684684

685-
# get monitor resolution
686-
monitor_geometry = Gdk.Display.get_default().get_monitor(0).get_geometry()
687-
688685
# state information that should persist across sessions
689686
self.bool_state = {
690687
'window_maximized': False,
691688
'search_matchcase': False,
692689
'search_backwards': False
693690
}
694-
self.int_state = {'window_width': 1024, 'window_height': 768}
695-
self.int_state['window_x'] = max(
696-
0,
697-
(monitor_geometry.width - self.int_state['window_width']) / 2
698-
)
699-
self.int_state['window_y'] = max(
700-
0,
701-
(monitor_geometry.height - self.int_state['window_height']) / 2
702-
)
691+
self.int_state = {
692+
'window_width': 1024,
693+
'window_height': 768,
694+
}
695+
696+
# window state signals
703697
self.connect('configure-event', self.configure_cb)
704698
self.connect('window-state-event', self.window_state_cb)
705699

@@ -985,37 +979,41 @@ def focus_in_cb(self, widget, event):
985979
page.open_file(f, True)
986980

987981
# record the window's position and size
988-
def configure_cb(self, widget, event):
982+
def configure_cb(self, widget: Gtk.Widget, event: Gdk.EventConfigure) -> None:
989983
# read the state directly instead of using window_maximized as the order
990984
# of configure/window_state events is undefined
991985
if (widget.get_window().get_state() & Gdk.WindowState.MAXIMIZED) == 0:
992-
self.int_state['window_x'], self.int_state['window_y'] = widget.get_window().get_root_origin() # noqa: E501
993986
self.int_state['window_width'] = event.width
994987
self.int_state['window_height'] = event.height
995988

996989
# record the window's maximized state
997-
def window_state_cb(self, window, event):
998-
self.bool_state['window_maximized'] = (
999-
(event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0
1000-
)
990+
def window_state_cb(self, widget: Gtk.Widget, event: Gdk.EventWindowState) -> None:
991+
is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0
992+
self.bool_state['window_maximized'] = is_maximized
1001993

1002994
# load state information that should persist across sessions
1003-
def loadState(self, statepath: str) -> None:
995+
def load_state(self, statepath: str) -> None:
1004996
if os.path.isfile(statepath):
1005997
try:
1006998
f = open(statepath, 'r')
1007999
ss = utils.readlines(f)
10081000
f.close()
1001+
10091002
for j, s in enumerate(ss):
10101003
try:
10111004
a = shlex.split(s, True)
1012-
if len(a) > 0:
1013-
if len(a) == 2 and a[0] in self.bool_state:
1014-
self.bool_state[a[0]] = (a[1] == 'True')
1015-
elif len(a) == 2 and a[0] in self.int_state:
1016-
self.int_state[a[0]] = int(a[1])
1017-
else:
1018-
raise ValueError()
1005+
if len(a) == 0:
1006+
continue
1007+
if len(a) != 2:
1008+
raise ValueError()
1009+
1010+
(key, value) = a
1011+
if key in self.bool_state:
1012+
self.bool_state[key] = (value == 'True')
1013+
elif key in self.int_state:
1014+
self.int_state[key] = int(value)
1015+
else:
1016+
raise ValueError()
10191017
except ValueError:
10201018
# this may happen if the state was written by a
10211019
# different version -- don't bother the user
@@ -1024,20 +1022,20 @@ def loadState(self, statepath: str) -> None:
10241022
# bad $HOME value? -- don't bother the user
10251023
utils.logDebug(f'Error reading {statepath}.')
10261024

1027-
self.move(self.int_state['window_x'], self.int_state['window_y'])
10281025
self.resize(self.int_state['window_width'], self.int_state['window_height'])
10291026
if self.bool_state['window_maximized']:
10301027
self.maximize()
10311028

10321029
# save state information that should persist across sessions
1033-
def saveState(self, statepath: str) -> None:
1030+
def save_state(self, statepath: str) -> None:
10341031
try:
10351032
ss = []
10361033
for k, v in self.bool_state.items():
10371034
ss.append(f'{k} {v}\n')
10381035
for k, v in self.int_state.items():
10391036
ss.append(f'{k} {v}\n')
10401037
ss.sort()
1038+
10411039
f = open(statepath, 'w')
10421040
f.write(f'# This state file was generated by {constants.APP_NAME} {constants.VERSION}.\n\n') # noqa: E501
10431041
for s in ss:
@@ -1733,9 +1731,41 @@ def _path_to_url(path: str, proto: str = 'file') -> str:
17331731

17341732
# callback for the about menu item
17351733
def about_cb(self, widget, data):
1736-
dialog = AboutDialog(self.get_toplevel())
1737-
dialog.run()
1738-
dialog.destroy()
1734+
authors = [
1735+
'Derrick Moser <[email protected]>',
1736+
'Romain Failliot <[email protected]>'
1737+
]
1738+
license = f'''{constants.APP_NAME} {constants.VERSION}
1739+
1740+
{constants.COPYRIGHT}
1741+
1742+
This program is free software; you can redistribute it and/or modify
1743+
it under the terms of the GNU General Public License as published by
1744+
the Free Software Foundation; either version 2 of the License, or
1745+
(at your option) any later version.
1746+
1747+
This program is distributed in the hope that it will be useful,
1748+
but WITHOUT ANY WARRANTY; without even the implied warranty of
1749+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1750+
GNU General Public License for more details.
1751+
1752+
You should have received a copy of the GNU General Public License along
1753+
with this program; if not, write to the Free Software Foundation, Inc.,
1754+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.'''
1755+
1756+
dialog = Gtk.AboutDialog(
1757+
transient_for=self.get_toplevel(),
1758+
modal=True,
1759+
program_name=constants.APP_NAME,
1760+
logo_icon_name=constants.APP_ID,
1761+
version=constants.VERSION,
1762+
comments=_('Diffuse is a graphical tool for merging and comparing text files.'),
1763+
copyright=constants.COPYRIGHT,
1764+
website=constants.WEBSITE,
1765+
authors=authors,
1766+
translator_credits=_('translator-credits'),
1767+
license=license)
1768+
dialog.present()
17391769

17401770

17411771
def _append_buttons(box, size, specs):

0 commit comments

Comments
 (0)