Extract dialogs into their own module
This commit is contained in:
parent
74ef6650a0
commit
8b1b7cbe7e
|
@ -0,0 +1,194 @@
|
||||||
|
# Diffuse: a graphical tool for merging and comparing text files.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019 Derrick Moser <derrick_moser@yahoo.com>
|
||||||
|
# Copyright (C) 2021 Romain Failliot <romain.failliot@foolstep.com>
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# pylint: disable=wrong-import-position
|
||||||
|
import gi
|
||||||
|
gi.require_version('GObject', '2.0')
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import GObject, Gtk
|
||||||
|
# pylint: enable=wrong-import-position
|
||||||
|
|
||||||
|
from diffuse import constants
|
||||||
|
from diffuse import utils
|
||||||
|
|
||||||
|
# the about dialog
|
||||||
|
class AboutDialog(Gtk.AboutDialog):
|
||||||
|
def __init__(self):
|
||||||
|
Gtk.AboutDialog.__init__(self)
|
||||||
|
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):
|
||||||
|
# record last chosen folder so the file chooser can start at a more useful
|
||||||
|
# location for empty panes
|
||||||
|
last_chosen_folder = os.path.realpath(os.curdir)
|
||||||
|
|
||||||
|
def __current_folder_changed_cb(self, widget):
|
||||||
|
FileChooserDialog.last_chosen_folder = widget.get_current_folder()
|
||||||
|
|
||||||
|
def __init__(self, title, parent, prefs, action, accept, rev=False):
|
||||||
|
Gtk.FileChooserDialog.__init__(self, title=title, parent=parent, action=action)
|
||||||
|
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
|
||||||
|
self.add_button(accept, Gtk.ResponseType.OK)
|
||||||
|
self.prefs = prefs
|
||||||
|
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
||||||
|
hbox.set_border_width(5)
|
||||||
|
label = Gtk.Label.new(_('Encoding: '))
|
||||||
|
hbox.pack_start(label, False, False, 0)
|
||||||
|
label.show()
|
||||||
|
self.encoding = entry = utils.EncodingMenu(
|
||||||
|
prefs,
|
||||||
|
action in [ Gtk.FileChooserAction.OPEN, Gtk.FileChooserAction.SELECT_FOLDER ])
|
||||||
|
hbox.pack_start(entry, False, False, 5)
|
||||||
|
entry.show()
|
||||||
|
if rev:
|
||||||
|
self.revision = entry = Gtk.Entry.new()
|
||||||
|
hbox.pack_end(entry, False, False, 0)
|
||||||
|
entry.show()
|
||||||
|
label = Gtk.Label.new(_('Revision: '))
|
||||||
|
hbox.pack_end(label, False, False, 0)
|
||||||
|
label.show()
|
||||||
|
|
||||||
|
self.vbox.pack_start(hbox, False, False, 0) # pylint: disable=no-member
|
||||||
|
hbox.show()
|
||||||
|
self.set_current_folder(self.last_chosen_folder)
|
||||||
|
self.connect('current-folder-changed', self.__current_folder_changed_cb)
|
||||||
|
|
||||||
|
def set_encoding(self, encoding):
|
||||||
|
self.encoding.set_text(encoding)
|
||||||
|
|
||||||
|
def get_encoding(self):
|
||||||
|
return self.encoding.get_text()
|
||||||
|
|
||||||
|
def get_revision(self):
|
||||||
|
return self.revision.get_text()
|
||||||
|
|
||||||
|
def get_filename(self):
|
||||||
|
# convert from UTF-8 string to unicode
|
||||||
|
return Gtk.FileChooserDialog.get_filename(self) # pylint: disable=no-member
|
||||||
|
|
||||||
|
# dialogue used to search for text
|
||||||
|
class NumericDialog(Gtk.Dialog):
|
||||||
|
def __init__(self, parent, title, text, val, lower, upper, step=1, page=0):
|
||||||
|
Gtk.Dialog.__init__(self, title=title, parent=parent, destroy_with_parent=True)
|
||||||
|
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT)
|
||||||
|
self.add_button(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)
|
||||||
|
|
||||||
|
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
|
||||||
|
vbox.set_border_width(10)
|
||||||
|
|
||||||
|
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
||||||
|
label = Gtk.Label.new(text)
|
||||||
|
hbox.pack_start(label, False, False, 0)
|
||||||
|
label.show()
|
||||||
|
adj = Gtk.Adjustment.new(val, lower, upper, step, page, 0)
|
||||||
|
self.button = button = Gtk.SpinButton.new(adj, 1.0, 0)
|
||||||
|
button.connect('activate', self.button_cb)
|
||||||
|
hbox.pack_start(button, True, True, 0)
|
||||||
|
button.show()
|
||||||
|
|
||||||
|
vbox.pack_start(hbox, True, True, 0)
|
||||||
|
hbox.show()
|
||||||
|
|
||||||
|
self.vbox.pack_start(vbox, False, False, 0) # pylint: disable=no-member
|
||||||
|
vbox.show()
|
||||||
|
|
||||||
|
def button_cb(self, widget):
|
||||||
|
self.response(Gtk.ResponseType.ACCEPT)
|
||||||
|
|
||||||
|
# dialogue used to search for text
|
||||||
|
class SearchDialog(Gtk.Dialog):
|
||||||
|
def __init__(self, parent, pattern=None, history=None):
|
||||||
|
Gtk.Dialog.__init__(self, title=_('Find...'), parent=parent, destroy_with_parent=True)
|
||||||
|
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT)
|
||||||
|
self.add_button(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)
|
||||||
|
|
||||||
|
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
|
||||||
|
vbox.set_border_width(10)
|
||||||
|
|
||||||
|
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
||||||
|
label = Gtk.Label.new(_('Search For: '))
|
||||||
|
hbox.pack_start(label, False, False, 0)
|
||||||
|
label.show()
|
||||||
|
combo = Gtk.ComboBoxText.new_with_entry()
|
||||||
|
self.entry = combo.get_child()
|
||||||
|
self.entry.connect('activate', self.entry_cb)
|
||||||
|
|
||||||
|
if pattern is not None:
|
||||||
|
self.entry.set_text(pattern)
|
||||||
|
|
||||||
|
if history is not None:
|
||||||
|
completion = Gtk.EntryCompletion.new()
|
||||||
|
liststore = Gtk.ListStore(GObject.TYPE_STRING)
|
||||||
|
completion.set_model(liststore)
|
||||||
|
completion.set_text_column(0)
|
||||||
|
for h in history:
|
||||||
|
liststore.append([h])
|
||||||
|
combo.append_text(h)
|
||||||
|
self.entry.set_completion(completion)
|
||||||
|
|
||||||
|
hbox.pack_start(combo, True, True, 0)
|
||||||
|
combo.show()
|
||||||
|
vbox.pack_start(hbox, False, False, 0)
|
||||||
|
hbox.show()
|
||||||
|
|
||||||
|
button = Gtk.CheckButton.new_with_mnemonic(_('Match Case'))
|
||||||
|
self.match_case_button = button
|
||||||
|
vbox.pack_start(button, False, False, 0)
|
||||||
|
button.show()
|
||||||
|
|
||||||
|
button = Gtk.CheckButton.new_with_mnemonic(_('Search Backwards'))
|
||||||
|
self.backwards_button = button
|
||||||
|
vbox.pack_start(button, False, False, 0)
|
||||||
|
button.show()
|
||||||
|
|
||||||
|
self.vbox.pack_start(vbox, False, False, 0) # pylint: disable=no-member
|
||||||
|
vbox.show()
|
||||||
|
|
||||||
|
# callback used when the Enter key is pressed
|
||||||
|
def entry_cb(self, widget):
|
||||||
|
self.response(Gtk.ResponseType.ACCEPT)
|
197
src/main.py
197
src/main.py
|
@ -22,8 +22,6 @@ import sys
|
||||||
import codecs
|
import codecs
|
||||||
import difflib
|
import difflib
|
||||||
import encodings
|
import encodings
|
||||||
import glob
|
|
||||||
import re
|
|
||||||
import shlex
|
import shlex
|
||||||
import stat
|
import stat
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
@ -42,8 +40,9 @@ from gi.repository import GObject, Gtk, Gdk, GdkPixbuf, Pango, PangoCairo
|
||||||
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from diffuse import utils
|
|
||||||
from diffuse import constants
|
from diffuse import constants
|
||||||
|
from diffuse import utils
|
||||||
|
from diffuse.dialogs import AboutDialog, FileChooserDialog, NumericDialog, SearchDialog
|
||||||
from diffuse.preferences import Preferences
|
from diffuse.preferences import Preferences
|
||||||
from diffuse.resources import Resources
|
from diffuse.resources import Resources
|
||||||
from diffuse.vcs.vcs_registry import VcsRegistry
|
from diffuse.vcs.vcs_registry import VcsRegistry
|
||||||
|
@ -146,7 +145,7 @@ def has_unix_line_ending(s):
|
||||||
return s.endswith('\n') and not s.endswith('\r\n')
|
return s.endswith('\n') and not s.endswith('\r\n')
|
||||||
|
|
||||||
# returns the format mask for a list of strings
|
# returns the format mask for a list of strings
|
||||||
def getFormat(ss):
|
def get_format(ss):
|
||||||
flags = 0
|
flags = 0
|
||||||
for s in ss:
|
for s in ss:
|
||||||
if s is not None:
|
if s is not None:
|
||||||
|
@ -161,7 +160,7 @@ def getFormat(ss):
|
||||||
# convenience method to change the line ending of a string
|
# convenience method to change the line ending of a string
|
||||||
def convert_to_format(s, format):
|
def convert_to_format(s, format):
|
||||||
if s is not None and format != 0:
|
if s is not None and format != 0:
|
||||||
old_format = getFormat([ s ])
|
old_format = get_format([ s ])
|
||||||
if old_format != 0 and (old_format & format) == 0:
|
if old_format != 0 and (old_format & format) == 0:
|
||||||
s = utils.strip_eol(s)
|
s = utils.strip_eol(s)
|
||||||
# prefer the host line ending style
|
# prefer the host line ending style
|
||||||
|
@ -436,7 +435,7 @@ def getCharacterClass(c):
|
||||||
return OTHER_CLASS
|
return OTHER_CLASS
|
||||||
|
|
||||||
# longest common subsequence of unique elements common to 'a' and 'b'
|
# longest common subsequence of unique elements common to 'a' and 'b'
|
||||||
def __patience_subsequence(a, b):
|
def _patience_subsequence(a, b):
|
||||||
# value unique lines by their order in each list
|
# value unique lines by their order in each list
|
||||||
value_a, value_b = {}, {}
|
value_a, value_b = {}, {}
|
||||||
# find unique values in 'a'
|
# find unique values in 'a'
|
||||||
|
@ -491,7 +490,7 @@ def __patience_subsequence(a, b):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# difflib-style approximation of the longest common subsequence
|
# difflib-style approximation of the longest common subsequence
|
||||||
def __lcs_approx(a, b):
|
def _lcs_approx(a, b):
|
||||||
count1, lookup = {}, {}
|
count1, lookup = {}, {}
|
||||||
# count occurrences of each element in 'a'
|
# count occurrences of each element in 'a'
|
||||||
for s in a:
|
for s in a:
|
||||||
|
@ -562,8 +561,8 @@ def __lcs_approx(a, b):
|
||||||
aidx, bidx, nidx = ai, bi, n
|
aidx, bidx, nidx = ai, bi, n
|
||||||
return aidx, bidx, nidx
|
return aidx, bidx, nidx
|
||||||
|
|
||||||
# patinence diff with difflib-style fallback
|
# patience diff with difflib-style fallback
|
||||||
def patience_diff(a, b):
|
def _patience_diff(a, b):
|
||||||
matches, len_a, len_b = [], len(a), len(b)
|
matches, len_a, len_b = [], len(a), len(b)
|
||||||
if len_a and len_b:
|
if len_a and len_b:
|
||||||
blocks = [ (0, len_a, 0, len_b, 0) ]
|
blocks = [ (0, len_a, 0, len_b, 0) ]
|
||||||
|
@ -571,7 +570,7 @@ def patience_diff(a, b):
|
||||||
start_a, end_a, start_b, end_b, match_idx = blocks.pop()
|
start_a, end_a, start_b, end_b, match_idx = blocks.pop()
|
||||||
aa, bb = a[start_a:end_a], b[start_b:end_b]
|
aa, bb = a[start_a:end_a], b[start_b:end_b]
|
||||||
# try patience
|
# try patience
|
||||||
pivots = __patience_subsequence(aa, bb)
|
pivots = _patience_subsequence(aa, bb)
|
||||||
if pivots:
|
if pivots:
|
||||||
offset_a, offset_b = start_a, start_b
|
offset_a, offset_b = start_a, start_b
|
||||||
for pivot_a, pivot_b in pivots:
|
for pivot_a, pivot_b in pivots:
|
||||||
|
@ -599,7 +598,7 @@ def patience_diff(a, b):
|
||||||
blocks.append((start_a, end_a, start_b, end_b, match_idx))
|
blocks.append((start_a, end_a, start_b, end_b, match_idx))
|
||||||
else:
|
else:
|
||||||
# fallback if patience fails
|
# fallback if patience fails
|
||||||
pivots = __lcs_approx(aa, bb)
|
pivots = _lcs_approx(aa, bb)
|
||||||
if pivots:
|
if pivots:
|
||||||
idx_a, idx_b, n = pivots
|
idx_a, idx_b, n = pivots
|
||||||
idx_a += start_a
|
idx_a += start_a
|
||||||
|
@ -1600,7 +1599,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
# align s1 and s2 by inserting spacer lines
|
# align s1 and s2 by inserting spacer lines
|
||||||
# this will be used to determine which lines from the inner lists of
|
# this will be used to determine which lines from the inner lists of
|
||||||
# lines should be neighbours
|
# lines should be neighbours
|
||||||
for block in patience_diff(t1, t2):
|
for block in _patience_diff(t1, t2):
|
||||||
delta = (n1 + block[0]) - (n2 + block[1])
|
delta = (n1 + block[0]) - (n2 + block[1])
|
||||||
if delta < 0:
|
if delta < 0:
|
||||||
# insert spacer lines in s1
|
# insert spacer lines in s1
|
||||||
|
@ -1679,7 +1678,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
def replaceContents(self, f, ss):
|
def replaceContents(self, f, ss):
|
||||||
self.alignmentChange(False)
|
self.alignmentChange(False)
|
||||||
# determine the format for the text
|
# determine the format for the text
|
||||||
self.setFormat(f, getFormat(ss))
|
self.setFormat(f, get_format(ss))
|
||||||
|
|
||||||
# create an initial set of blocks for the lines
|
# create an initial set of blocks for the lines
|
||||||
blocks = []
|
blocks = []
|
||||||
|
@ -1786,7 +1785,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
ss.append('')
|
ss.append('')
|
||||||
# change the format to that of the target pane
|
# change the format to that of the target pane
|
||||||
if pane.format == 0:
|
if pane.format == 0:
|
||||||
self.setFormat(f, getFormat(ss))
|
self.setFormat(f, get_format(ss))
|
||||||
ss = [ convert_to_format(s, pane.format) for s in ss ]
|
ss = [ convert_to_format(s, pane.format) for s in ss ]
|
||||||
# prepend original text that was before the selection
|
# prepend original text that was before the selection
|
||||||
if col0 > 0:
|
if col0 > 0:
|
||||||
|
@ -3968,7 +3967,7 @@ class FileDiffViewer(Gtk.Grid):
|
||||||
if pane.format == 0:
|
if pane.format == 0:
|
||||||
# copy the format of the source pane if the format for the
|
# copy the format of the source pane if the format for the
|
||||||
# destination pane as not yet been determined
|
# destination pane as not yet been determined
|
||||||
self.setFormat(f_dst, getFormat(ss))
|
self.setFormat(f_dst, get_format(ss))
|
||||||
for i, s in enumerate(ss):
|
for i, s in enumerate(ss):
|
||||||
self.updateText(f_dst, start + i, convert_to_format(s, pane.format))
|
self.updateText(f_dst, start + i, convert_to_format(s, pane.format))
|
||||||
n = len(ss)
|
n = len(ss)
|
||||||
|
@ -4107,59 +4106,6 @@ GObject.signal_new('cursor-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAS
|
||||||
GObject.signal_new('syntax-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (str, ))
|
GObject.signal_new('syntax-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (str, ))
|
||||||
GObject.signal_new('format-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int))
|
GObject.signal_new('format-changed', FileDiffViewer, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (int, int))
|
||||||
|
|
||||||
# dialogue used to search for text
|
|
||||||
class SearchDialog(Gtk.Dialog):
|
|
||||||
def __init__(self, parent, pattern=None, history=None):
|
|
||||||
Gtk.Dialog.__init__(self, title=_('Find...'), parent=parent, destroy_with_parent=True)
|
|
||||||
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT)
|
|
||||||
self.add_button(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)
|
|
||||||
|
|
||||||
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
|
|
||||||
vbox.set_border_width(10)
|
|
||||||
|
|
||||||
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
|
||||||
label = Gtk.Label.new(_('Search For: '))
|
|
||||||
hbox.pack_start(label, False, False, 0)
|
|
||||||
label.show()
|
|
||||||
combo = Gtk.ComboBoxText.new_with_entry()
|
|
||||||
self.entry = combo.get_child()
|
|
||||||
self.entry.connect('activate', self.entry_cb)
|
|
||||||
|
|
||||||
if pattern is not None:
|
|
||||||
self.entry.set_text(pattern)
|
|
||||||
|
|
||||||
if history is not None:
|
|
||||||
completion = Gtk.EntryCompletion.new()
|
|
||||||
liststore = Gtk.ListStore(GObject.TYPE_STRING)
|
|
||||||
completion.set_model(liststore)
|
|
||||||
completion.set_text_column(0)
|
|
||||||
for h in history:
|
|
||||||
liststore.append([h])
|
|
||||||
combo.append_text(h)
|
|
||||||
self.entry.set_completion(completion)
|
|
||||||
|
|
||||||
hbox.pack_start(combo, True, True, 0)
|
|
||||||
combo.show()
|
|
||||||
vbox.pack_start(hbox, False, False, 0)
|
|
||||||
hbox.show()
|
|
||||||
|
|
||||||
button = Gtk.CheckButton.new_with_mnemonic(_('Match Case'))
|
|
||||||
self.match_case_button = button
|
|
||||||
vbox.pack_start(button, False, False, 0)
|
|
||||||
button.show()
|
|
||||||
|
|
||||||
button = Gtk.CheckButton.new_with_mnemonic(_('Search Backwards'))
|
|
||||||
self.backwards_button = button
|
|
||||||
vbox.pack_start(button, False, False, 0)
|
|
||||||
button.show()
|
|
||||||
|
|
||||||
self.vbox.pack_start(vbox, False, False, 0) # pylint: disable=no-member
|
|
||||||
vbox.show()
|
|
||||||
|
|
||||||
# callback used when the Enter key is pressed
|
|
||||||
def entry_cb(self, widget):
|
|
||||||
self.response(Gtk.ResponseType.ACCEPT)
|
|
||||||
|
|
||||||
# convenience method to request confirmation when closing the last tab
|
# convenience method to request confirmation when closing the last tab
|
||||||
def confirmTabClose(parent):
|
def confirmTabClose(parent):
|
||||||
dialog = utils.MessageDialog(parent, Gtk.MessageType.WARNING, _('Closing this tab will quit %s.') % (constants.APP_NAME, ))
|
dialog = utils.MessageDialog(parent, Gtk.MessageType.WARNING, _('Closing this tab will quit %s.') % (constants.APP_NAME, ))
|
||||||
|
@ -4167,121 +4113,6 @@ def confirmTabClose(parent):
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
return end
|
return end
|
||||||
|
|
||||||
# custom dialogue for picking files with widgets for specifying the encoding
|
|
||||||
# and revision
|
|
||||||
class FileChooserDialog(Gtk.FileChooserDialog):
|
|
||||||
# record last chosen folder so the file chooser can start at a more useful
|
|
||||||
# location for empty panes
|
|
||||||
last_chosen_folder = os.path.realpath(os.curdir)
|
|
||||||
|
|
||||||
def __current_folder_changed_cb(self, widget):
|
|
||||||
FileChooserDialog.last_chosen_folder = widget.get_current_folder()
|
|
||||||
|
|
||||||
def __init__(self, title, parent, prefs, action, accept, rev=False):
|
|
||||||
Gtk.FileChooserDialog.__init__(self, title=title, parent=parent, action=action)
|
|
||||||
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
|
|
||||||
self.add_button(accept, Gtk.ResponseType.OK)
|
|
||||||
self.prefs = prefs
|
|
||||||
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
|
||||||
hbox.set_border_width(5)
|
|
||||||
label = Gtk.Label.new(_('Encoding: '))
|
|
||||||
hbox.pack_start(label, False, False, 0)
|
|
||||||
label.show()
|
|
||||||
self.encoding = entry = utils.EncodingMenu(prefs, action in [ Gtk.FileChooserAction.OPEN, Gtk.FileChooserAction.SELECT_FOLDER ])
|
|
||||||
hbox.pack_start(entry, False, False, 5)
|
|
||||||
entry.show()
|
|
||||||
if rev:
|
|
||||||
self.revision = entry = Gtk.Entry.new()
|
|
||||||
hbox.pack_end(entry, False, False, 0)
|
|
||||||
entry.show()
|
|
||||||
label = Gtk.Label.new(_('Revision: '))
|
|
||||||
hbox.pack_end(label, False, False, 0)
|
|
||||||
label.show()
|
|
||||||
|
|
||||||
self.vbox.pack_start(hbox, False, False, 0) # pylint: disable=no-member
|
|
||||||
hbox.show()
|
|
||||||
self.set_current_folder(self.last_chosen_folder)
|
|
||||||
self.connect('current-folder-changed', self.__current_folder_changed_cb)
|
|
||||||
|
|
||||||
def set_encoding(self, encoding):
|
|
||||||
self.encoding.set_text(encoding)
|
|
||||||
|
|
||||||
def get_encoding(self):
|
|
||||||
return self.encoding.get_text()
|
|
||||||
|
|
||||||
def get_revision(self):
|
|
||||||
return self.revision.get_text()
|
|
||||||
|
|
||||||
def get_filename(self):
|
|
||||||
# convert from UTF-8 string to unicode
|
|
||||||
return Gtk.FileChooserDialog.get_filename(self)
|
|
||||||
|
|
||||||
# dialogue used to search for text
|
|
||||||
class NumericDialog(Gtk.Dialog):
|
|
||||||
def __init__(self, parent, title, text, val, lower, upper, step=1, page=0):
|
|
||||||
Gtk.Dialog.__init__(self, title=title, parent=parent, destroy_with_parent=True)
|
|
||||||
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT)
|
|
||||||
self.add_button(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)
|
|
||||||
|
|
||||||
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
|
|
||||||
vbox.set_border_width(10)
|
|
||||||
|
|
||||||
hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
|
|
||||||
label = Gtk.Label.new(text)
|
|
||||||
hbox.pack_start(label, False, False, 0)
|
|
||||||
label.show()
|
|
||||||
adj = Gtk.Adjustment.new(val, lower, upper, step, page, 0)
|
|
||||||
self.button = button = Gtk.SpinButton.new(adj, 1.0, 0)
|
|
||||||
button.connect('activate', self.button_cb)
|
|
||||||
hbox.pack_start(button, True, True, 0)
|
|
||||||
button.show()
|
|
||||||
|
|
||||||
vbox.pack_start(hbox, True, True, 0)
|
|
||||||
hbox.show()
|
|
||||||
|
|
||||||
self.vbox.pack_start(vbox, False, False, 0) # pylint: disable=no-member
|
|
||||||
vbox.show()
|
|
||||||
|
|
||||||
def button_cb(self, widget):
|
|
||||||
self.response(Gtk.ResponseType.ACCEPT)
|
|
||||||
|
|
||||||
# establish callback for the about dialog's link to Diffuse's web site
|
|
||||||
def url_hook(dialog, link, userdata):
|
|
||||||
webbrowser.open(link)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# the about dialog
|
|
||||||
class AboutDialog(Gtk.AboutDialog):
|
|
||||||
def __init__(self):
|
|
||||||
Gtk.AboutDialog.__init__(self)
|
|
||||||
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))
|
|
||||||
|
|
||||||
# widget classed to create notebook tabs with labels and a close button
|
# widget classed to create notebook tabs with labels and a close button
|
||||||
# use notebooktab.button.connect() to be notified when the button is pressed
|
# use notebooktab.button.connect() to be notified when the button is pressed
|
||||||
# make this a Gtk.EventBox so signals can be connected for MMB and RMB button
|
# make this a Gtk.EventBox so signals can be connected for MMB and RMB button
|
||||||
|
|
|
@ -32,6 +32,7 @@ configure_file(
|
||||||
|
|
||||||
diffuse_sources = [
|
diffuse_sources = [
|
||||||
'__init__.py',
|
'__init__.py',
|
||||||
|
'dialogs.py',
|
||||||
'main.py',
|
'main.py',
|
||||||
'preferences.py',
|
'preferences.py',
|
||||||
'resources.py',
|
'resources.py',
|
||||||
|
|
Loading…
Reference in New Issue