Merge pull request #207 from MightyCreak/modernize-for-gtk
Modernize some GTK code
This commit is contained in:
commit
be433eb2d7
|
@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Some signals weren't properly renamed from the previous GTK3 migration (@MightyCreak)
|
||||
- The syntax menu wasn't working anymore (@MightyCreak)
|
||||
- Properly handles SIGINT (i.e. Ctrl+C) now (@MightyCreak)
|
||||
- Add back `save_state()` to remember window's width and height (@MightyCreak)
|
||||
|
||||
## 0.8.1 - 2023-04-07
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ from gettext import gettext as _
|
|||
from typing import Final
|
||||
|
||||
APP_NAME: Final[str] = 'Diffuse'
|
||||
APP_ID: Final[str] = 'io.github.mightycreak.Diffuse'
|
||||
COPYRIGHT: Final[str] = '''{copyright} © 2006-2019 Derrick Moser
|
||||
{copyright} © 2015-2023 Romain Failliot'''.format(copyright=_("Copyright"))
|
||||
WEBSITE: Final[str] = 'https://mightycreak.github.io/diffuse/'
|
||||
|
|
|
@ -22,7 +22,6 @@ import os
|
|||
from gettext import gettext as _
|
||||
from typing import Optional
|
||||
|
||||
from diffuse import constants
|
||||
from diffuse import utils
|
||||
|
||||
import gi # type: ignore
|
||||
|
@ -31,39 +30,6 @@ gi.require_version('Gtk', '3.0')
|
|||
from gi.repository import GObject, Gtk # type: ignore # noqa: E402
|
||||
|
||||
|
||||
# the about dialog
|
||||
class AboutDialog(Gtk.AboutDialog):
|
||||
def __init__(self, parent: Gtk.Widget) -> None:
|
||||
Gtk.AboutDialog.__init__(self)
|
||||
self.set_transient_for(parent)
|
||||
self.set_logo_icon_name('io.github.mightycreak.Diffuse')
|
||||
self.set_program_name(constants.APP_NAME)
|
||||
self.set_version(constants.VERSION)
|
||||
self.set_comments(_('Diffuse is a graphical tool for merging and comparing text files.'))
|
||||
self.set_copyright(constants.COPYRIGHT)
|
||||
self.set_website(constants.WEBSITE)
|
||||
self.set_authors(['Derrick Moser <derrick_moser@yahoo.com>',
|
||||
'Romain Failliot <romain.failliot@foolstep.com>'])
|
||||
self.set_translator_credits(_('translator-credits'))
|
||||
license_text = [
|
||||
constants.APP_NAME + ' ' + constants.VERSION + '\n\n',
|
||||
constants.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.
|
||||
|
||||
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))
|
||||
|
||||
|
||||
# custom dialogue for picking files with widgets for specifying the encoding
|
||||
# and revision
|
||||
class FileChooserDialog(Gtk.FileChooserDialog):
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
import os
|
||||
import sys
|
||||
import gettext
|
||||
import signal
|
||||
|
||||
from gi.repository import Gio
|
||||
|
||||
|
@ -32,6 +33,10 @@ SYSCONFIGDIR = '@SYSCONFIGDIR@'
|
|||
|
||||
sys.path.insert(1, PKGDATADIR)
|
||||
|
||||
# Quietly handle SIGINT (i.e. Ctrl+C)
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
# Initialize i18n
|
||||
gettext.bindtextdomain('diffuse', localedir=LOCALEDIR)
|
||||
gettext.textdomain('diffuse')
|
||||
|
||||
|
|
|
@ -37,12 +37,14 @@ class DiffuseApplication(Gtk.Application):
|
|||
|
||||
def __init__(self, sysconfigdir):
|
||||
super().__init__(
|
||||
application_id='io.github.mightycreak.Diffuse',
|
||||
application_id=constants.APP_ID,
|
||||
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE | Gio.ApplicationFlags.NON_UNIQUE)
|
||||
|
||||
self.window = None
|
||||
self.sysconfigdir = sysconfigdir
|
||||
|
||||
self.connect('shutdown', self.on_shutdown)
|
||||
|
||||
self.add_main_option(
|
||||
'version',
|
||||
ord('v'),
|
||||
|
@ -249,7 +251,7 @@ also retrieve revisions of files from several VCSs for comparison and merging.''
|
|||
|
||||
# load state
|
||||
self.statepath = os.path.join(data_dir, 'state')
|
||||
diff_window.loadState(self.statepath)
|
||||
diff_window.load_state(self.statepath)
|
||||
|
||||
# process remaining command line arguments
|
||||
encoding = None
|
||||
|
@ -366,6 +368,9 @@ also retrieve revisions of files from several VCSs for comparison and merging.''
|
|||
self.activate()
|
||||
return 0
|
||||
|
||||
def on_shutdown(self, application: Gio.Application) -> None:
|
||||
self.window.save_state(self.statepath)
|
||||
|
||||
|
||||
def main(version: str, sysconfigdir: str) -> int:
|
||||
"""The application's entry point."""
|
||||
|
|
|
@ -28,7 +28,7 @@ from typing import List, Optional
|
|||
from urllib.parse import urlparse
|
||||
|
||||
from diffuse import constants, utils
|
||||
from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, SearchDialog
|
||||
from diffuse.dialogs import FileChooserDialog, NumericDialog, SearchDialog
|
||||
from diffuse.preferences import Preferences
|
||||
from diffuse.resources import theResources
|
||||
from diffuse.utils import LineEnding
|
||||
|
@ -682,24 +682,18 @@ class DiffuseWindow(Gtk.ApplicationWindow):
|
|||
# number of created viewers (used to label some tabs)
|
||||
self.viewer_count = 0
|
||||
|
||||
# get monitor resolution
|
||||
monitor_geometry = Gdk.Display.get_default().get_monitor(0).get_geometry()
|
||||
|
||||
# state information that should persist across sessions
|
||||
self.bool_state = {
|
||||
'window_maximized': False,
|
||||
'search_matchcase': False,
|
||||
'search_backwards': False
|
||||
}
|
||||
self.int_state = {'window_width': 1024, 'window_height': 768}
|
||||
self.int_state['window_x'] = max(
|
||||
0,
|
||||
(monitor_geometry.width - self.int_state['window_width']) / 2
|
||||
)
|
||||
self.int_state['window_y'] = max(
|
||||
0,
|
||||
(monitor_geometry.height - self.int_state['window_height']) / 2
|
||||
)
|
||||
self.int_state = {
|
||||
'window_width': 1024,
|
||||
'window_height': 768,
|
||||
}
|
||||
|
||||
# window state signals
|
||||
self.connect('configure-event', self.configure_cb)
|
||||
self.connect('window-state-event', self.window_state_cb)
|
||||
|
||||
|
@ -985,35 +979,39 @@ class DiffuseWindow(Gtk.ApplicationWindow):
|
|||
page.open_file(f, True)
|
||||
|
||||
# record the window's position and size
|
||||
def configure_cb(self, widget, event):
|
||||
def configure_cb(self, widget: Gtk.Widget, event: Gdk.EventConfigure) -> None:
|
||||
# read the state directly instead of using window_maximized as the order
|
||||
# of configure/window_state events is undefined
|
||||
if (widget.get_window().get_state() & Gdk.WindowState.MAXIMIZED) == 0:
|
||||
self.int_state['window_x'], self.int_state['window_y'] = widget.get_window().get_root_origin() # noqa: E501
|
||||
self.int_state['window_width'] = event.width
|
||||
self.int_state['window_height'] = event.height
|
||||
|
||||
# record the window's maximized state
|
||||
def window_state_cb(self, window, event):
|
||||
self.bool_state['window_maximized'] = (
|
||||
(event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0
|
||||
)
|
||||
def window_state_cb(self, widget: Gtk.Widget, event: Gdk.EventWindowState) -> None:
|
||||
is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0
|
||||
self.bool_state['window_maximized'] = is_maximized
|
||||
|
||||
# load state information that should persist across sessions
|
||||
def loadState(self, statepath: str) -> None:
|
||||
def load_state(self, statepath: str) -> None:
|
||||
if os.path.isfile(statepath):
|
||||
try:
|
||||
f = open(statepath, 'r')
|
||||
ss = utils.readlines(f)
|
||||
f.close()
|
||||
|
||||
for j, s in enumerate(ss):
|
||||
try:
|
||||
a = shlex.split(s, True)
|
||||
if len(a) > 0:
|
||||
if len(a) == 2 and a[0] in self.bool_state:
|
||||
self.bool_state[a[0]] = (a[1] == 'True')
|
||||
elif len(a) == 2 and a[0] in self.int_state:
|
||||
self.int_state[a[0]] = int(a[1])
|
||||
if len(a) == 0:
|
||||
continue
|
||||
if len(a) != 2:
|
||||
raise ValueError()
|
||||
|
||||
(key, value) = a
|
||||
if key in self.bool_state:
|
||||
self.bool_state[key] = (value == 'True')
|
||||
elif key in self.int_state:
|
||||
self.int_state[key] = int(value)
|
||||
else:
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
|
@ -1024,13 +1022,12 @@ class DiffuseWindow(Gtk.ApplicationWindow):
|
|||
# bad $HOME value? -- don't bother the user
|
||||
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'])
|
||||
if self.bool_state['window_maximized']:
|
||||
self.maximize()
|
||||
|
||||
# save state information that should persist across sessions
|
||||
def saveState(self, statepath: str) -> None:
|
||||
def save_state(self, statepath: str) -> None:
|
||||
try:
|
||||
ss = []
|
||||
for k, v in self.bool_state.items():
|
||||
|
@ -1038,6 +1035,7 @@ class DiffuseWindow(Gtk.ApplicationWindow):
|
|||
for k, v in self.int_state.items():
|
||||
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
|
||||
for s in ss:
|
||||
|
@ -1733,9 +1731,41 @@ class DiffuseWindow(Gtk.ApplicationWindow):
|
|||
|
||||
# callback for the about menu item
|
||||
def about_cb(self, widget, data):
|
||||
dialog = AboutDialog(self.get_toplevel())
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
authors = [
|
||||
'Derrick Moser <derrick_moser@yahoo.com>',
|
||||
'Romain Failliot <romain.failliot@foolstep.com>'
|
||||
]
|
||||
license = f'''{constants.APP_NAME} {constants.VERSION}
|
||||
|
||||
{constants.COPYRIGHT}
|
||||
|
||||
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.'''
|
||||
|
||||
dialog = Gtk.AboutDialog(
|
||||
transient_for=self.get_toplevel(),
|
||||
modal=True,
|
||||
program_name=constants.APP_NAME,
|
||||
logo_icon_name=constants.APP_ID,
|
||||
version=constants.VERSION,
|
||||
comments=_('Diffuse is a graphical tool for merging and comparing text files.'),
|
||||
copyright=constants.COPYRIGHT,
|
||||
website=constants.WEBSITE,
|
||||
authors=authors,
|
||||
translator_credits=_('translator-credits'),
|
||||
license=license)
|
||||
dialog.present()
|
||||
|
||||
|
||||
def _append_buttons(box, size, specs):
|
||||
|
|
Loading…
Reference in New Issue